from odoo import models, fields, api from datetime import date from odoo.exceptions import UserError from datetime import date, timedelta from calendar import month_name class sos_sales_action_plan(models.Model): _name = 'sos_sales_action_plan' _description = 'Sosaley Sales Action Plan' _rec_name="customer_name" date = fields.Date(string="Action Date",required=True) customer_name = fields.Many2one( 'sos_customers', string="Customer Name", required=True, domain=lambda self: self._get_customer_domain() ) end_customer_name = fields.Char(string="End Customer Name") quote_no = fields.Char(string="Quote/W.O No",help="Fill this if the same customer has different work orders.") quote_no_selector = fields.Many2one( 'sos_case_diary', string="End Customer/Quote No", domain="[('customer_name', '=', customer_name), ('quote_no', '!=', False)]" ) ce_product_type = fields.Selection( [ ('Sales', 'Sales'), ('Service', 'Service'), ('Spare', 'Spare'), ('Cloud', 'Cloud') ], string="Service Type") product = fields.Selection( [ ('BHMS 1.2V', 'BHMS 1.2V'), ('BHMS 2V', 'BHMS 2V'), ('BHMS 12V', 'BHMS 12V'), ('BHMS 48V', 'BHMS 48V'), ('BMS-HV', 'BMS-HV'), ('BMS-LV 100A', 'BMS-LV 100A'), ('BMS-LV 40A', 'BMS-LV 40A'), ('SBMS 55A', 'SBMS 55A'), ('MC 250W', 'MC 250W'), ('HeartTarang', 'HeartTarang') ], string="Products",required=True) location = fields.Char(string="Location") quantity = fields.Char(string="Quantity") sales_executive = fields.Many2one( 'res.users', string='Sales Executive', default=lambda self: self.env.user, readonly=True ) sales_head = fields.Many2one( 'res.users', string='Sales Head', default=lambda self: self.env.user, domain=lambda self: [('groups_id', 'in', self.env.ref('sos_inventory.sos_sales_user').ids + self.env.ref('sos_inventory.sos_ce_head').ids)] ) action_type = fields.Selection([ ('Meeting', 'Meeting'), ('Demo', 'Demo'), ('Discussion', 'Discussion'), ('Visit', 'Direct Visit'), ('Negotiation', 'Negotiation'), ('Validation', 'Validation'), ('Proposal', 'Proposal'), ('Email/Call/Video Call', 'Email/Call/Video Call') ], string='Action Type',requierd=True) current_spenco_status = fields.Selection([ ('Suspects', 'Suspects'), ('Prospects', 'Prospects'), ('Engaged', 'Engaged'), ('Negotiation', 'Negotiation'), ('Commercial Order', 'Commercial Order') ], string='Current SPENCO Status') po_copy = fields.Binary(string='PO Copy', attachment=True) po_copy_filename = fields.Char(string='PO Copy Filename') action = fields.Selection( [ ('Generate More Suspects', 'Generate More Suspects'), ('Suspect to Prospects', 'Suspect to Prospects'), ('Prospect to Engaged', 'Prospect to Engaged'), ('Engaged to Negotiation', 'Engaged to Negotiation'), ('Negotiation to Order', 'Negotiation to Order') ], string="Action Category",required=True) action_plan = fields.Text(string="Action Plan") result = fields.Text(string="Result") status = fields.Selection( [ ('open', 'Open'), ('close', 'Closed without Order'), ('close_order', 'Closed with Order') ], string="Status",default='open',required=True) currency_id = fields.Many2one('res.currency', string='Currency') po_no= fields.Char(string="PO No") value = fields.Monetary(currency_field='currency_id', string="Value (In Lakhs)") next_action_date = fields.Date(string="Next Action On") update_done = fields.Boolean(string="Update Done", default=False) state = fields.Selection([ ('draft', 'Draft'), ('confirmed', 'Confirmed'), ], string='State', default='draft', readonly=True) def _get_customer_domain(self): if ( self.env.user.has_group('sos_inventory.sos_management_user') or self.env.user.has_group('sos_inventory.sos_ce_head') or self.env.user.has_group('sos_inventory.sos_ce_user') ): return [] # no filter, show all return [('responsible', '=', self.env.uid)] @api.onchange('quote_no_selector') def _onchange_quote_no_selector(self): if self.quote_no_selector: self.quote_no = self.quote_no_selector.quote_no self.end_customer_name = self.quote_no_selector.end_customer_name def action_prev_action_plans(self): domain = [('customer_name', '=', self.customer_name.id)] if self.quote_no: domain.append(('quote_no', '=', self.quote_no)) if self.quote_no: domain.append(('product', '=', self.product)) prev_action_plans = self.env['sos_sales_action_plan'].search(domain) return { 'type': 'ir.actions.act_window', 'name': 'Previous Action Plans', 'res_model': 'sos_sales_action_plan', 'view_mode': 'tree', 'views': [(self.env.ref('sos_sales.view_prev_action_plan_tree').id, 'tree')], 'domain': [('id', 'in', prev_action_plans.ids)], 'target': 'new', } @api.onchange('quote_no') def _onchange_quote_no(self): """Set products from sos_case_diary if quote_no and customer match""" if self.customer_name and self.quote_no: case = self.env['sos_case_diary'].search([ ('customer_name', '=', self.customer_name.id), ('quote_no', '=', self.quote_no) ], limit=1) if case: self.product = case.products # assuming it's Char or same type self.location = case.customer_city self.quantity = case.quantity else: self.product = False self.location = False self.quantity = False def write(self, vals): if 'status' in vals: for record in self: domain = [ ('customer_name', '=', record.customer_name.customer_name), ('products', '=', record.product), ('sales_person', '=', record.sales_executive.id) ] if record.quote_no: domain.append(('quote_no', '=', record.quote_no)) case_diary_records = self.env['sos_case_diary'].search(domain, limit=1) if case_diary_records: case_diary_records.write({"status": vals['status']}) return super().write(vals) def action_view_po(self): # Check if there is a test report if not self.po_copy: raise UserError("No po available for this record.") # Use `ir.actions.act_url` to open the binary file as a PDF return { 'type': 'ir.actions.act_url', 'url': '/web/content/{}/{}/{}?download=false'.format( self._name, self.id, 'po_copy'), 'target': 'new', # This opens the PDF in a new tab } @api.onchange('current_spenco_status') def _onchange_current_spenco_status(self): if self.current_spenco_status != 'Commercial Order': self.po_copy = False def unlink(self): for record in self: if record.update_done: raise UserError("You can't delete the action plan after case diary updation!") action_plan_date = record.date sales_executive = record.sales_executive # Week-wise week_number = record._compute_week_number(action_plan_date) record.decrement_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='week_number', field_value=week_number, model_name='sos_action_plan_summary_week_wise' ) # Month-wise month_number = record._compute_month_number(action_plan_date) record.decrement_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='month_number', field_value=month_number, model_name='sos_action_plan_summary_month_wise' ) # Quarter-wise quarter_number = record._compute_quarter_number(action_plan_date) record.decrement_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='quarter_number', field_value=quarter_number, model_name='sos_action_plan_summary_quarter_wise' ) # Year-wise year_number = record._compute_year_number(action_plan_date) record.decrement_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='year', field_value=year_number, model_name='sos_action_plan_summary_year_wise' ) return super(sos_sales_action_plan, self).unlink() def decrement_summary(self, action_plan_date, sales_executive, field_name, field_value, model_name): SummaryModel = self.env[model_name] record = SummaryModel.search([ ('sales_executive', '=', sales_executive.id), (field_name, '=', field_value), ('action', '=', self.action), ('action_type', '=', self.action_type) ], limit=1) if record and record.count > 1: record.write({'count': record.count - 1}) elif record: record.unlink() @api.model def create(self, vals): record = super(sos_sales_action_plan, self).create(vals) record.state = 'confirmed' # Now access fields from the created record sales_executive = record.sales_executive action_plan_date = record.date #week wise week_number = record._compute_week_number(action_plan_date) record.update_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='week_number', field_value=week_number, model_name='sos_action_plan_summary_week_wise' ) #month wise month_number = record._compute_month_number(action_plan_date) record.update_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='month_number', field_value=month_number, model_name='sos_action_plan_summary_month_wise' ) #quarter wise quarter_number = record._compute_quarter_number(action_plan_date) record.update_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='quarter_number', field_value=quarter_number, model_name='sos_action_plan_summary_quarter_wise' ) #year wise year_number = record._compute_year_number(action_plan_date) record.update_summary( action_plan_date=action_plan_date, sales_executive=sales_executive, field_name='year', field_value=year_number, model_name='sos_action_plan_summary_year_wise' ) return record def _compute_year_number(self,action_plan_date): month_number = action_plan_date.strftime('%B') month = action_plan_date.month year = action_plan_date.year year_number = f"{year}-{year + 1}" if month >= 4 else f"{year - 1}-{year}" return year_number def _compute_quarter_number(self,action_plan_date): month_number = action_plan_date.strftime('%B') month = action_plan_date.month quarter_number = ( "Q1" if 4 <= month <= 6 else "Q2" if 7 <= month <= 9 else "Q3" if 10 <= month <= 12 else "Q4" ) return quarter_number def _compute_month_number(self,action_plan_date): month_number = action_plan_date.strftime('%B') return month_number def _compute_week_number(self,action_plan_date): start_of_week = action_plan_date - timedelta(days=action_plan_date.weekday()) end_of_week = start_of_week + timedelta(days=5) return f"{start_of_week.strftime('%b %d')} - {end_of_week.strftime('%b %d')}" def update_summary(self, action_plan_date, sales_executive, field_name, field_value, model_name): SummaryModel = self.env[model_name] record = SummaryModel.search([ ('sales_executive', '=', sales_executive.id), (field_name, '=', field_value), ('action', '=', self.action), ('action_type', '=', self.action_type) ], limit=1) if record: record.write({ 'count': record.count + 1 }) else: record = SummaryModel.create({ 'sales_executive': sales_executive.id, field_name: field_value, 'action': self.action, 'action_type': self.action_type, 'entered_date': action_plan_date, 'count': 1 }) return record def view_casediary(self): domain = [ ('customer_name', '=', self.customer_name.id), ('products', '=', self.product), ('sales_person', '=', self.sales_executive.id) ] if self.quote_no: domain.append(('quote_no', '=', self.quote_no)) case_diary_records = self.env['sos_case_diary'].search(domain, limit=1) if case_diary_records: return { 'type': 'ir.actions.act_window', 'name': 'Case Diary', 'res_model': 'sos_case_diary', 'view_mode': 'form', 'res_id': case_diary_records.id, 'target': 'current', } else: return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Not Found", 'type': 'danger', 'sticky': False } } def update_casediary(self): if self.update_done: return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Already updated!", 'type': 'warning', 'sticky': False } } # Validate mandatory fields missing_fields = [] if not self.current_spenco_status: missing_fields.append("Current SPENCO Status") if not self.next_action_date: missing_fields.append("Next Action Date") if not self.result: missing_fields.append("Result") if self.value <= 0.00: if self.current_spenco_status != "Suspects": missing_fields.append("Value") if missing_fields: raise UserError( f"The following fields are required before updating the Case Diary: {', '.join(missing_fields)}" ) self.update_done = True domain = [ ('customer_name', '=', self.customer_name.id), ('products', '=', self.product), ('sales_person', '=', self.env.user.id) ] if self.quote_no: domain.append(('quote_no', '=', self.quote_no)) if self.end_customer_name: domain.append(('end_customer_name', '=', self.end_customer_name)) case_diary_records = self.env['sos_case_diary'].search(domain, limit=1) if case_diary_records: case_diary_records.write({ 'status': self.status, 'po_copy':self.po_copy, 'po_no':self.po_no, 'quantity':self.quantity, 'po_copy_filename':self.po_copy_filename }) case_diary_records.line_ids.create({ 'ref_id': case_diary_records.id, 'action_plan_date': self.date, 'action_plan': self.action_plan, 'action_type': self.action_type, 'notes': self.result, 'next_action_date': self.next_action_date, 'status_changed_on': date.today(), 'spenco_status': self.current_spenco_status, 'current_state_value': self.value, }) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Updated", 'type': 'success', 'sticky': False } } else: vals = { 'customer_name': self.customer_name.id, 'end_customer_name': self.end_customer_name, 'customer_city': self.location, 'sales_person': self.sales_executive.id, 'products': self.product, 'proposal_value': self.value, 'status': self.status, 'quote_no': self.quote_no, 'quantity': self.quantity, 'po_no': self.po_no, 'po_copy': self.po_copy, 'po_copy_filename': self.po_copy_filename } if self.ce_product_type: vals['ce_product_type'] = self.ce_product_type casediary_record = self.env['sos_case_diary'].create(vals) self.env['sos_case_diary_line'].create({ 'ref_id': casediary_record.id, 'action_plan_date': self.date, 'action_plan': self.action_plan, 'action_type': self.action_type, 'notes': self.result, 'next_action_date': self.next_action_date, 'status_changed_on': date.today(), 'spenco_status': self.current_spenco_status, 'current_state_value': self.value }) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Created New Case Diary", 'type': 'success', 'sticky': False } }