diff --git a/sos_sales/__manifest__.py b/sos_sales/__manifest__.py index 9bcc107..aa22a60 100755 --- a/sos_sales/__manifest__.py +++ b/sos_sales/__manifest__.py @@ -49,7 +49,8 @@ 'wizard/week_summary_wizard.xml', 'wizard/action_plan_summary_wizard.xml', 'wizard/sos_sales_achievement_wizard_view.xml', - 'wizard/sos_business_performance_wizard_view.xml' + 'wizard/sos_business_performance_wizard_view.xml', + 'wizard/yet_to_bill_wizard_view.xml' ], diff --git a/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc b/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc index 6c983af..55ae0e8 100644 Binary files a/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_customers.cpython-310.pyc b/sos_sales/models/__pycache__/sos_customers.cpython-310.pyc index 6b2b563..dbd5d0d 100644 Binary files a/sos_sales/models/__pycache__/sos_customers.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_customers.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc index bc5e6a9..e6066ec 100644 Binary files a/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc index 0d45c1a..35c6b3e 100755 Binary files a/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc index 5a0b441..0595162 100644 Binary files a/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc b/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc index 8e69670..a1a7cdf 100644 Binary files a/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc b/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc index 48fe35c..b3bc2a5 100644 Binary files a/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc differ diff --git a/sos_sales/models/__pycache__/sos_sales_leads.cpython-310.pyc b/sos_sales/models/__pycache__/sos_sales_leads.cpython-310.pyc index 7218164..103c3ff 100644 Binary files a/sos_sales/models/__pycache__/sos_sales_leads.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_sales_leads.cpython-310.pyc differ diff --git a/sos_sales/models/sos_case_diary.py b/sos_sales/models/sos_case_diary.py index 900ee2c..9ce4bc2 100755 --- a/sos_sales/models/sos_case_diary.py +++ b/sos_sales/models/sos_case_diary.py @@ -28,7 +28,7 @@ class sos_case_diary(models.Model): 'res.users', string='Sales Executive', 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)]) + domain=lambda self: [('groups_id', 'in', self.env.ref('sos_inventory.sos_sales_user').ids + self.env.ref('sos_inventory.sos_ce_head').ids + self.env.ref('sos_inventory.sos_sales_sapl_user').ids)]) currency_id = fields.Many2one( 'res.currency', string='Currency', @@ -43,7 +43,6 @@ class sos_case_diary(models.Model): ('projects', 'Projects') ], string="Interested In",required=True,default="products") - project_name = fields.Char(string="Project Name") products = fields.Selection( [ ('BHMS 1.2V', 'BHMS 1.2V'), @@ -80,7 +79,32 @@ class sos_case_diary(models.Model): po_copy = fields.Binary(string="PO Copy") po_copy_filename=fields.Char(string="PO DocumentFile Name") order_expected_on = fields.Date(string="Order Expected On") - + sales_type = fields.Selection( + [ + ('Domestic', 'Domestic'), + ('International', 'International') + ], + string="Sales Type",default="Domestic") + project_name= fields.Many2one('sos_projects',string="Project Name") + country = fields.Many2one( + 'res.country', + string='Country', + default=lambda self: self.env['res.country'].search([('code', '=', 'IN')], limit=1) +) + is_ce_user_created = fields.Boolean( + compute='_compute_is_ce_user_created', + store=True, + string='Created by Sales User' + ) + @api.depends('create_uid') + def _compute_is_ce_user_created(self): + ce_groups = [ + self.env.ref('sos_inventory.sos_ce_user').id + ] + for record in self: + record.is_ce_user_created = any( + gid in record.create_uid.groups_id.ids for gid in ce_groups + ) @api.depends('end_customer_name', 'quote_no') def _compute_display_name(self): for rec in self: @@ -148,8 +172,13 @@ class sos_case_diary(models.Model): proposal_value = last_record.current_state_value # Log brief entry + current_date = last_record.status_changed_on + start_year = current_date.year if current_date.month >= 4 else current_date.year - 1 + end_year = start_year + 1 + current_fy = f"FY {start_year}-{end_year}" self.env['sos_sales_achievement_report_brief'].create({ 'ref_id': ref_id, + 'financial_year':current_fy, 'customer_name': self.customer_name.id, 'action_date': last_record.status_changed_on, 'proposal_value': proposal_value, @@ -235,7 +264,8 @@ class sos_case_diary(models.Model): }) totals_by_month[order_month_year] += record.proposal_value or 0.0 action = self.env.ref("sos_sales.action_report_pipeline").with_context(landscape=True).report_action( - self, data={'data_by_month': data_by_month,'totals_by_month': totals_by_month} + self, data={'data_by_month': data_by_month,'totals_by_month': totals_by_month, + 'report_generated_on':date.today(),'from_date':from_date,'to_date':to_date} ) return action else: diff --git a/sos_sales/models/sos_customers.py b/sos_sales/models/sos_customers.py index 0c7e6b9..e4259cf 100755 --- a/sos_sales/models/sos_customers.py +++ b/sos_sales/models/sos_customers.py @@ -39,6 +39,20 @@ class SOS_Customers(models.Model): line_ids_contacts = fields.One2many('sos_customers_line', 'ref_id', string="Contact Details",copy=True) reporting_to = fields.Many2one('res.users', string='Reporting To') responsible = fields.Many2one('res.users', string='Sales Executive',default=lambda self: self.env.user) + is_ce_user_created = fields.Boolean( + compute='_compute_is_ce_user_created', + store=True, + string='Created by Sales User' + ) + @api.depends('create_uid') + def _compute_is_ce_user_created(self): + ce_groups = [ + self.env.ref('sos_inventory.sos_ce_user').id + ] + for record in self: + record.is_ce_user_created = any( + gid in record.create_uid.groups_id.ids for gid in ce_groups + ) @api.model def create(self, vals): create_uid = vals.get('create_uid', self.env.uid) diff --git a/sos_sales/models/sos_proposal_boq.py b/sos_sales/models/sos_proposal_boq.py index bb898ef..7a59ac8 100755 --- a/sos_sales/models/sos_proposal_boq.py +++ b/sos_sales/models/sos_proposal_boq.py @@ -7,12 +7,13 @@ class Battery_Installation_Requirement(models.Model): _name = 'sos_proposal_boq' _description = 'Battery Installation Details' _rec_name="proposal_id" - _order = 'proposal_id asc' + _order = 'id desc' _sql_constraints = [ ('unique_proposal_id', 'unique(proposal_id)', 'Proposal ID must be unique!') ] proposal_id = fields.Many2one('sos_proposal_customer_requirement',string="Proposal ID", required= True, domain=lambda self: [('id', 'not in', self.env['sos_proposal_boq'].search([]).mapped('proposal_id').ids)]) + sales_executive = fields.Many2one('res.users', string='Requirement Submitted By',related="proposal_id.requirement_submitted_by_name") customer_name = fields.Char(string="Customer Name") location = fields.Char(string="Location") number_of_batteries = fields.Integer(string="Number of Batteries") @@ -63,10 +64,17 @@ class Battery_Installation_Requirement(models.Model): warranty_percentage = fields.Float(string="Warranty(%)") final_cost = fields.Monetary(string="Final Cost", compute='_compute_final_cost', store=True, currency_field='currency_id') final_cost_per_battery = fields.Monetary(string="Final Cost", compute='_compute_final_cost_per_battery', store=True, currency_field='currency_id') + final_cost_by_acc = fields.Monetary(string="Final Cost By Accounts", currency_field='currency_id') + final_total = fields.Monetary(string="Final Cost", compute='_compute_final_cost_acc', store=True, currency_field='currency_id') communication_type = fields.Selection([ ('wired', 'Wired'), ('wireless', 'Wireless') ], string="Communication Type", default='wired') + slave_type = fields.Selection([ + ('15S Individual Slave', '15S Individual Slave'), + ('90S Electrical Panel', '90S Electrical Panel'), + ('60S Electrical Panel', '60S Electrical Panel') + ], string="Slave Type", default='15S Individual Slave') specific_requirements = fields.Html(string="Specific Requirements") #CE Team Fields @@ -85,7 +93,8 @@ class Battery_Installation_Requirement(models.Model): man_month_1to2_yrs_cost = fields.Monetary(string="Cost", currency_field='currency_id') man_month_2to3_yrs_cost = fields.Monetary(string="Cost", currency_field='currency_id') man_month_manager_cost = fields.Monetary(string="Cost", currency_field='currency_id') - iandc_costing=fields.Monetary(readonly=False,string="I & C Costing",currency_field='currency_id',compute='_compute_iandc_expense') + iandc_costing=fields.Monetary(readonly=False,string="I & C Man Month Cost",currency_field='currency_id',compute='_compute_iandc_expense') + iandc_travel_cost = fields.Monetary(string="Travel Cost", currency_field='currency_id') #FG Fields line_ids_fg_ups1 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 1",compute='_compute_line_ids_by_ups',store=False) line_ids_fg_ups2 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 2",compute='_compute_line_ids_by_ups',store=False) @@ -148,21 +157,21 @@ class Battery_Installation_Requirement(models.Model): #Installation Kit Fields - line_ids_installation_kit_ups1 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 1",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups2 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 2",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups3 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 3",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups4 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 4",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups5 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 5",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups6 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 6",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups7 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 7",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups8 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 8",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups9 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 9",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups10 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 10",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups11 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 11",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups12 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 12",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups13 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 13",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups14 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 14",compute='_compute_line_ids_by_ups',store=False) - line_ids_installation_kit_ups15 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 15",compute='_compute_line_ids_by_ups',store=False) + line_ids_installation_kit_ups1 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 1",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups2 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 2",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups3 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 3",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups4 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 4",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups5 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 5",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups6 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 6",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups7 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 7",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups8 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 8",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups9 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 9",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups10 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 10",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups11 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 11",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups12 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 12",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups13 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 13",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups14 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 14",compute='_compute_line_ids_by_ups',store=False,readonly=False) + line_ids_installation_kit_ups15 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 15",compute='_compute_line_ids_by_ups',store=False,readonly=False) #Miscellaneous Kit Fields line_ids_miscellaneous_ups1 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 1",compute='_compute_line_ids_by_ups',store=False) @@ -199,7 +208,7 @@ class Battery_Installation_Requirement(models.Model): ups15_total = fields.Float(string="UPS 15 Total Cost", compute='_compute_ups_total', store=True) extra_lines_cost = fields.Monetary(string="Cost", compute='_compute_extra_lines_cost', store=True, currency_field='currency_id') - packing_and_forwarding = fields.Monetary(string="Packing & Forwarding", currency_field='currency_id',store=True) + packing_and_forwarding = fields.Monetary(string="Packing & Forwarding", currency_field='currency_id',store=True,compute="_compute_packing_and_forwarding",compute_sudo=True) additional_warranty = fields.Monetary(string="Additional Warranty", currency_field='currency_id',store=True) line_ids_spare_ups1 = fields.One2many('sos_proposal_line_spare_ups1','ref_id', string="Spare UPS 1") line_ids_spare_ups2 = fields.One2many('sos_proposal_line_spare_ups2', 'ref_id',string="Spare UPS 2") @@ -216,6 +225,33 @@ class Battery_Installation_Requirement(models.Model): line_ids_spare_ups13 = fields.One2many('sos_proposal_line_spare_ups13','ref_id', string="Spare UPS 13") line_ids_spare_ups14 = fields.One2many('sos_proposal_line_spare_ups14','ref_id', string="Spare UPS 14") line_ids_spare_ups15 = fields.One2many('sos_proposal_line_spare_ups15','ref_id', string="Spare UPS 15") + + @api.depends( + 'final_cost_by_acc','number_of_batteries' + ) + def _compute_final_cost_acc(self): + for rec in self: + rec.final_total = rec.final_cost_by_acc * rec.number_of_batteries + + @api.depends( + 'proposal_id.number_of_batteries', + 'proposal_id.number_of_ups', + 'proposal_id.direction', + ) + def _compute_packing_and_forwarding(self): + for rec in self: + val = 0.0 + p = rec.proposal_id + if p: + packing_calculation = 1200 + packing_kg_12V = 100 * (p.number_of_ups or 0) + direction = (p.direction or '').strip() + rate = 20 if direction == "North" else 19 if direction == "West" \ + else 23 if direction == "East" else 13 + forwarding_calculation = rate * packing_kg_12V + base_amount = 1.20 * (packing_calculation + forwarding_calculation) + val = base_amount * 1.18 + rec.packing_and_forwarding = val def _compute_merged_spare_html(self): for rec in self: # Collect lines from ups1..ups15 (skip models that don't exist) @@ -228,16 +264,17 @@ class Battery_Installation_Requirement(models.Model): if model not in self.env: continue for l in self.env[model].search([('ref_id', '=', rec.id)]): - unit_price = l.unit_price or 0.0 - curr_id = l.currency_id.id if l.currency_id else False - key = (l.component_id.id, l.uom, curr_id, unit_price) + if l.production_cost: + unit_price = l.unit_price or 0.0 + curr_id = l.currency_id.id if l.currency_id else False + key = (l.component_id.id, l.uom, curr_id, unit_price) - agg[key] += float(l.quantity or 0) - if key not in name_map: - # Use part number (fallback to component name if needed) - name_map[key] = (getattr(l.component_id, 'part_no', False) or l.component_id.name or '') - curr_map[key] = l.currency_id if l.currency_id else False - price_map[key] = unit_price + agg[key] += float(l.quantity or 0) + if key not in name_map: + # Use part number (fallback to component name if needed) + name_map[key] = (getattr(l.component_id, 'part_no', False) or l.component_id.name or '') + curr_map[key] = l.currency_id if l.currency_id else False + price_map[key] = unit_price rows_sorted = sorted(agg.items(), key=lambda it: name_map[it[0]].lower()) if not rows_sorted: @@ -374,15 +411,16 @@ class Battery_Installation_Requirement(models.Model): price_map = {} for l in lines: - unit_price = l.unit_price or 0.0 - curr_id = l.currency_id.id if l.currency_id else False - key = (l.component_id.id, l.uom, curr_id, unit_price) + if l.production_cost: + unit_price = l.unit_price or 0.0 + curr_id = l.currency_id.id if l.currency_id else False + key = (l.component_id.id, l.uom, curr_id, unit_price) - agg[key] += float(l.quantity or 0) - if key not in name_map: - name_map[key] = l.component_id.part_no or '' - curr_map[key] = l.currency_id if l.currency_id else False - price_map[key] = unit_price + agg[key] += float(l.quantity or 0) + if key not in name_map: + name_map[key] = l.component_id.part_no or '' + curr_map[key] = l.currency_id if l.currency_id else False + price_map[key] = unit_price rows_sorted = sorted(agg.items(), key=lambda it: name_map[it[0]].lower()) if not rows_sorted: @@ -524,15 +562,16 @@ class Battery_Installation_Requirement(models.Model): price_map = {} for l in lines: - unit_price = l.unit_price or 0.0 - curr_id = l.currency_id.id if l.currency_id else False - key = (l.component_id.id, l.uom, curr_id, unit_price) + if l.production_cost: + unit_price = l.unit_price or 0.0 + curr_id = l.currency_id.id if l.currency_id else False + key = (l.component_id.id, l.uom, curr_id, unit_price) - agg[key] += float(l.quantity or 0) - if key not in name_map: - name_map[key] = l.component_id.name or '' - curr_map[key] = l.currency_id if l.currency_id else False - price_map[key] = unit_price + agg[key] += float(l.quantity or 0) + if key not in name_map: + name_map[key] = l.component_id.name or '' + curr_map[key] = l.currency_id if l.currency_id else False + price_map[key] = unit_price rows_sorted = sorted(agg.items(), key=lambda it: name_map[it[0]].lower()) if not rows_sorted: @@ -599,15 +638,16 @@ class Battery_Installation_Requirement(models.Model): price_map = {} for l in lines: - unit_price = l.unit_price or 0.0 - curr_id = l.currency_id.id if l.currency_id else False - key = (l.component_id.id, l.uom, curr_id, unit_price) + if l.production_cost: + unit_price = l.unit_price or 0.0 + curr_id = l.currency_id.id if l.currency_id else False + key = (l.component_id.id, l.uom, curr_id, unit_price) - agg[key] += float(l.quantity or 0) - if key not in name_map: - name_map[key] = l.component_id.name or '' - curr_map[key] = l.currency_id if l.currency_id else False - price_map[key] = unit_price + agg[key] += float(l.quantity or 0) + if key not in name_map: + name_map[key] = l.component_id.name or '' + curr_map[key] = l.currency_id if l.currency_id else False + price_map[key] = unit_price rows_sorted = sorted(agg.items(), key=lambda it: name_map[it[0]].lower()) if not rows_sorted: @@ -707,10 +747,10 @@ class Battery_Installation_Requirement(models.Model): # for rec in self: # rec.transport_expense = 5000 - @api.depends('no_of_days','man_month_1to2_yrs_persons','man_month_2to3_yrs_persons','man_month_manager_persons','man_month_1to2_yrs_cost','man_month_2to3_yrs_cost','man_month_manager_cost') + @api.depends('iandc_travel_cost','no_of_days','man_month_1to2_yrs_persons','man_month_2to3_yrs_persons','man_month_manager_persons','man_month_1to2_yrs_cost','man_month_2to3_yrs_cost','man_month_manager_cost') def _compute_iandc_expense(self): for rec in self: - rec.iandc_costing = ((rec.man_month_1to2_yrs_persons * rec.man_month_1to2_yrs_cost) + + rec.iandc_costing = rec.iandc_travel_cost + ((rec.man_month_1to2_yrs_persons * rec.man_month_1to2_yrs_cost) + (rec.man_month_2to3_yrs_persons * rec.man_month_2to3_yrs_cost) + (rec.man_month_manager_persons * rec.man_month_manager_cost)) * rec.no_of_days @@ -719,10 +759,10 @@ class Battery_Installation_Requirement(models.Model): for record in self: record.extra_lines_cost = sum(line.cost for line in record.extra_cost_line_ids) - @api.depends('iandc_costing','warranty_cost', 'total_cost','margin','extra_lines_cost','packing_and_forwarding','additional_warranty') + @api.depends('warranty_cost', 'total_cost','margin','extra_lines_cost','packing_and_forwarding','additional_warranty','iandc_costing') def _compute_final_cost(self): for rec in self: - rec.final_cost = rec.warranty_cost + rec.total_cost + rec.margin + rec.extra_lines_cost + rec.packing_and_forwarding + rec.additional_warranty + rec.iandc_costing + rec.final_cost = rec.warranty_cost + rec.total_cost + rec.margin + rec.extra_lines_cost + rec.packing_and_forwarding + rec.additional_warranty + rec.iandc_costing @api.depends('total_cost', 'warranty_percentage') def _compute_warranty(self): @@ -879,8 +919,10 @@ class Battery_Installation_Requirement(models.Model): @api.model def create(self, vals): record = super().create(vals) - if vals.get('proposal_id'): - record._generate_boq_lines_from_proposal() + if vals.get('proposal_id') and vals.get('products') == "BHMS 12V": + record._generate_boq_lines_from_proposal_12v() + elif vals.get('proposal_id') and vals.get('products') == "BHMS 1.2V" and vals.get('slave_type') == "15S Individual Slave": + record._generate_boq_lines_from_proposal_15s() return record @api.onchange('proposal_id') @@ -898,37 +940,186 @@ class Battery_Installation_Requirement(models.Model): self.number_of_ups = self.proposal_id.number_of_ups self.products = self.proposal_id.products self.communication_type = self.proposal_id.communication_type + self.slave_type = self.proposal_id.slave_type self.specific_requirements = self.proposal_id.specific_requirements - multiplier = max(1, math.ceil(self.proposal_id.number_of_batteries / 20)) - if self.proposal_id.number_of_batteries < 100: - self.engineers_nos = 1 - self.no_of_days = multiplier - else: - self.engineers_nos = 2 - self.no_of_days = multiplier / 2 - packing_calculation = 1200 - packing_kg_12V = 100 * self.proposal_id.number_of_ups - direction = self.proposal_id.direction - if direction == "North": - forwarding_calculation = 20 * packing_kg_12V - elif direction == "West": - forwarding_calculation = 19 * packing_kg_12V - elif direction == "East": - forwarding_calculation = 23 * packing_kg_12V - else: - forwarding_calculation = 13 * packing_kg_12V - base_amount = 1.20 * (packing_calculation + forwarding_calculation) - self.packing_and_forwarding = base_amount * 1.18 - - # Clear all lines + self.line_ids_fg = [(5, 0, 0)] self.line_ids_sfg = [(5, 0, 0)] self.line_ids_material = [(5, 0, 0)] self.line_ids_installation_kit = [(5, 0, 0)] self.line_ids_miscellaneous = [(5, 0, 0)] - self._generate_boq_lines_from_proposal() + if self.products == 'BHMS 12V': + self._generate_boq_lines_from_proposal_12v() + elif self.products == 'BHMS 1.2V': + self._generate_boq_lines_from_proposal_15s() + def _generate_boq_lines_from_proposal_15s(self): + if not self.proposal_id: + return + record = self.env['sos_deliverables_config'].search([ + ('fg_name', '=', self.products), + ('slave_type', '=', self.slave_type) + ], limit=1) + if record and self.number_of_ups: + fg_lines = [] + sfg_lines = [] + material_lines = [] + install_kit_lines = [] + misc_lines = [] - def _generate_boq_lines_from_proposal(self): + for ups_index in range(1, self.number_of_ups + 1): + for line in record.fg_ids: + single_set_qty = 1 + if line.item_type == 'Master Panel': + single_set_qty = 1 + elif line.item_type == 'CT Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + number_of_strings_per_battery = matching_ups_line.number_of_strings_per_battery + single_set_qty = number_of_strings_per_battery + ct_module = ((matching_ups_line.ups_capacity_kva * 1000) / (12 * matching_ups_line.number_of_batteries)) / 10 + if 0 <= ct_module < 100: + material_code="MDS070" + elif 100 <= ct_module < 200: + material_code="MDS071" + elif 200 <= ct_module < 300: + material_code="MDS069" + else: + material_code="MDS040" + material_record=self.env['sos_material'].search([('material_code', '=', material_code)],limit=1) + material_lines.append((0, 0, { + 'component_id': material_record.id, + 'uom': material_record.uom, + 'singet_set_qty':single_set_qty, + 'unit_price': material_record.unit_price, + 'description': material_record.description, + 'ups_index': ups_index, + 'production_cost':True + })) + elif line.item_type == 'Slave Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + single_set_qty = math.ceil((matching_ups_line.number_of_batteries / 15) + * matching_ups_line.number_of_strings_per_battery) + name_lower = (line.component_id.name or "").lower() + is_electrical_panel = "electrical panel" in name_lower + + # Find the UPS line for this index (use first match if multiple) + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index)[:1] + num_strings = (matching_ups_line.number_of_strings_per_battery + if matching_ups_line and matching_ups_line.number_of_strings_per_battery + else 0) + + fg_lines.append((0, 0, { + 'component_id': line.component_id.id, + 'uom': line.uom, + 'unit_price': line.component_id.unit_price, # no panel-based zeroing + 'description': line.description, + 'item_type': line.item_type, + 'total_set': 1, # stays 1 + 'singet_set_qty': num_strings if is_electrical_panel else single_set_qty, + 'production_cost': line.add_production_cost, + 'ups_index': ups_index, + })) + + for line in record.sfg_ids: + if line.item_type == 'Master Panel': + single_set_qty = 1 + elif line.item_type == 'Battery Count': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + single_set_qty = matching_ups_line.number_of_batteries + elif line.item_type == 'CT Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + number_of_strings_per_battery = matching_ups_line.number_of_strings_per_battery + single_set_qty = number_of_strings_per_battery # example logic + elif line.item_type == 'Slave Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + single_set_qty = math.ceil((matching_ups_line.number_of_batteries)/4) * matching_ups_line.number_of_strings_per_battery + + sfg_lines.append((0, 0, { + 'component_id': line.component_id.id, + 'uom': line.uom, + 'unit_price': line.component_id.unit_price, + 'description': line.description, + 'item_type': line.item_type, + 'singet_set_qty': single_set_qty, + 'production_cost': line.add_production_cost, + 'ups_index': ups_index + })) + + + for line in record.material_ids: + if line.item_type == 'Internet Module' and self.proposal_id.internet_connectivity != 'yes': + continue + if line.item_type == 'Master Panel': + single_set_qty = 1 * line.quantity + elif line.item_type == 'Battery Count': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + single_set_qty = matching_ups_line.number_of_batteries + elif line.item_type == 'CT Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + number_of_strings_per_battery = matching_ups_line.number_of_strings_per_battery + single_set_qty = number_of_strings_per_battery * line.quantity + elif line.item_type == 'Slave Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + single_set_qty = (math.ceil((matching_ups_line.number_of_batteries)/4) * matching_ups_line.number_of_strings_per_battery) * line.quantity + + material_lines.append((0, 0, { + 'component_id': line.component_id.id, + 'uom': line.uom, + 'unit_price': line.component_id.unit_price, + 'description': line.description, + 'item_type': line.item_type, + 'singet_set_qty': single_set_qty, + 'production_cost': line.add_production_cost, + 'ups_index': ups_index + })) + + for line in record.installation_kit_ids: + if line.item_type == 'Master Panel': + single_set_qty = 1 * line.quantity + elif line.item_type == 'CT Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + number_of_strings_per_battery = matching_ups_line.number_of_strings_per_battery + single_set_qty = number_of_strings_per_battery * line.quantity + elif line.item_type == 'Slave Module': + matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index) + if matching_ups_line: + single_set_qty = (math.ceil((matching_ups_line.number_of_batteries)/4) * matching_ups_line.number_of_strings_per_battery) * line.quantity + + install_kit_lines.append((0, 0, { + 'component_id': line.component_id.id, + 'uom': line.uom, + 'unit_price': line.component_id.unit_price, + 'description': line.description, + 'item_type': line.item_type, + 'singet_set_qty': single_set_qty, + 'production_cost': line.add_production_cost, + 'ups_index': ups_index + })) + + misc_lines += [(0, 0, { + 'name': line.name, + 'cost': line.cost, + 'quantity': line.quantity, + 'ups_index': ups_index + }) for line in record.miscellaneous_ids] + + self.write({ + 'line_ids_fg': fg_lines, + 'line_ids_sfg': sfg_lines, + 'line_ids_material': material_lines, + 'line_ids_installation_kit': install_kit_lines, + 'line_ids_miscellaneous': misc_lines + }) + + def _generate_boq_lines_from_proposal_12v(self): if not self.proposal_id: return @@ -1142,33 +1333,81 @@ class Battery_Installation_Requirement(models.Model): 'boq_submitted_by_approved_on' ) def action_acc_esign_btn(self): + self.ensure_one() sequence_util = self.env['sos_common_scripts'] - body_html = """ -

Below Proposal is waiting for your Updation

+ # ---- Pretty values with fallbacks ---- + customer = self.customer_name or '—' + location = self.location or '—' + batteries = int(self.number_of_batteries or 0) + currency_symbol = self.env.company.currency_id.symbol or '' + final_cost = float(self.final_cost_by_acc or 0.0) + final_cost_disp = f"{currency_symbol} {final_cost:,.2f}" + warranty = self.proposal_id.warranty + insta_location = self.proposal_id.installation_location or '—' + + # ---- HTML body ---- + body_html = f""" +
+

+ Below Proposal Costing was completed. You can proceed with the proposal builder. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer{customer}
Delivery Location{location}
Installation Location{insta_location}
Number of Batteries{batteries:,}
Warranty (In Months){warranty}
Final MSP per Battery{final_cost_disp}
+ + +


""" + + # Who submitted requirements? req_submitted_by = self.env['sos_proposal_customer_requirement'].search([ ('proposal_id', '=', self.proposal_id.proposal_id) ], limit=1) - # Send email only if login exists if req_submitted_by and req_submitted_by.requirement_submitted_by_name and req_submitted_by.requirement_submitted_by_name.login: sequence_util.send_direct_email( self.env, "sos_proposal_boq", self.id, req_submitted_by.requirement_submitted_by_name.login, - f"Costing Done - {self.proposal_id.proposal_id}", + f"Costing Done - {customer}", body_html ) - # Assign signature + # Assign signature (account approval) return sequence_util.action_assign_signature( - self, + self, 'acc_approved_by_name', 'acc_approved_on' ) + class SOS_Proposal_BOQ_Material(models.Model): _name = 'sos_proposal_boq_material' _description = 'Proposal BOQ Material Lines' diff --git a/sos_sales/models/sos_proposal_builder.py b/sos_sales/models/sos_proposal_builder.py index ab345ed..118e1d6 100755 --- a/sos_sales/models/sos_proposal_builder.py +++ b/sos_sales/models/sos_proposal_builder.py @@ -8,6 +8,7 @@ class SOS_Proposal_Builder(models.Model): _rec_name="proposal_id" proposal_id = fields.Many2one('sos_proposal_customer_requirement',string="Proposal ID", required= True) + sales_executive = fields.Many2one('res.users', string='Requirement Submitted By',related="proposal_id.requirement_submitted_by_name") customer_name = fields.Char(string="Customer Name") customer_logo=fields.Binary(string="Customer Logo") doc_attachement = fields.Binary(string="Attachments",help="Any attachments to be attached with Proposal") @@ -190,7 +191,7 @@ class SOS_Proposal_Builder(models.Model): if self.proposal_id: self.customer_name = self.proposal_id.customer_name boq = self.env['sos_proposal_boq'].search([('proposal_id', '=', self.proposal_id.id)], limit=1, order='id desc') - self.total_cost = boq.final_cost + self.total_cost = boq.final_cost_by_acc @api.depends('buffer_in_percentage', 'total_cost') def _compute_total_with_buffer(self): @@ -204,7 +205,7 @@ class SOS_Proposal_Builder(models.Model): [('proposal_id', '=', rec.proposal_id.id)], limit=1, order='id desc' ) - rec.total_cost = boq.final_cost if boq else 0.0 + rec.total_cost = boq.final_cost_by_acc if boq else 0.0 @api.model def create(self, vals): if vals.get('proposal_id') and not vals.get('total_cost'): @@ -212,7 +213,7 @@ class SOS_Proposal_Builder(models.Model): [('proposal_id', '=', vals['proposal_id'])], limit=1, order='id desc' ) if boq: - vals['total_cost'] = boq.final_cost + vals['total_cost'] = boq.final_cost_by_acc return super().create(vals) def write(self, vals): @@ -221,7 +222,7 @@ class SOS_Proposal_Builder(models.Model): [('proposal_id', '=', vals['proposal_id'])], limit=1, order='id desc' ) if boq: - vals['total_cost'] = boq.final_cost + vals['total_cost'] = boq.final_cost_by_acc return super().write(vals) class SOS_Technical_Diagrams(models.Model): diff --git a/sos_sales/models/sos_proposal_customer_requirement.py b/sos_sales/models/sos_proposal_customer_requirement.py index 4980c47..89b1fc2 100755 --- a/sos_sales/models/sos_proposal_customer_requirement.py +++ b/sos_sales/models/sos_proposal_customer_requirement.py @@ -5,6 +5,7 @@ class Battery_Installation_Requirement(models.Model): _name = 'sos_proposal_customer_requirement' _description = 'Battery Installation Details' _rec_name="proposal_id" + _order = 'id desc' proposal_id = fields.Char(string="Proposal ID", readonly= True, required= True, default=lambda self: self._generate_id()) customer_name = fields.Char(string="Customer Name", required=True) location = fields.Char(string="Delivery Location") @@ -58,7 +59,7 @@ class Battery_Installation_Requirement(models.Model): duct_type = fields.Selection([ ('pvc', 'PVC'), ('metal', 'Metal') - ], string="Duct Type", default='metal') + ], string="Duct Type", default='pvc') communication_type = fields.Selection([ ('wired', 'Wired'), ('wireless', 'Wireless') @@ -70,6 +71,7 @@ class Battery_Installation_Requirement(models.Model): ups_line_ids = fields.One2many('dynamic_ups_line', 'parent_id', string="UPS Details") warranty=fields.Char(string="Warranty (In Months)") # Fields for 1.2V + spare_count = fields.Integer(string="Spare Count") master_type = fields.Selection([ ('AC 110V', 'AC 110V'), ('AC 220V', 'AC 220V'), @@ -78,7 +80,8 @@ class Battery_Installation_Requirement(models.Model): ], string="Master Type", default='AC 110V') slave_type = fields.Selection([ ('15S Individual Slave', '15S Individual Slave'), - ('90S Electrical Panel', '90S Electrical Panel') + ('60S Electrical Panel', '60S Electrical Panel'), + ('90S Electrical Panel', '90S Electrical Panel'), ], string="Slave Type", default='15S Individual Slave') ntc = fields.Selection([ ('Individual', 'Individual'), diff --git a/sos_sales/models/sos_sales_achievement_report.py b/sos_sales/models/sos_sales_achievement_report.py index 7bb5410..f982cfd 100755 --- a/sos_sales/models/sos_sales_achievement_report.py +++ b/sos_sales/models/sos_sales_achievement_report.py @@ -14,8 +14,12 @@ class SOS_Sales_Achievement_Report(models.Model): ('unique_financial_year_sales_person', 'UNIQUE(financial_year, sales_person)', 'The combination of Financial Year and Sales Person must be unique.') ] display_name = fields.Char(string="Name", compute='_compute_display_name', store=True) + category = fields.Selection([ + ('sales', 'Sales'), + ('export', 'Export') + ], string="Category", required=True, default='sales') financial_year = fields.Char(string="Financial Year", default=lambda self: self._get_financial_year()) - sales_person = fields.Many2one('res.users', string='Sales person',required=True) + sales_person = fields.Many2one('res.users', string='Sales person') overall_target = fields.Float(string="Overall Target") opening_balance = fields.Float(string="Opening Balance") planned_target_april = fields.Float(string="Planned For April") @@ -105,7 +109,7 @@ class SOS_Sales_Achievement_Report(models.Model): ytd_target = fields.Float(string="YTD Total", store=True, compute="_compute_ytd_target") ytd_sales = fields.Float(string="YTD Sales", store=True, compute="_compute_ytd_sales_target") ytd_sales_percentage = fields.Float(string="YTD Sales Percentage", store=True, compute="_compute_ytd_sales_percentage") - ytd_yet_to_billed = fields.Float(string="YTD Billed", store=True,compute="_compute_ytd_yet_to_billed") + ytd_yet_to_billed = fields.Float(string="YTD Billed") ytd_billed = fields.Float(string="YTD Billed", store=True,compute="_compute_ytd_billed") ytd_collected = fields.Float(string="YTD Collected", store=True,compute="_compute_ytd_collected") line_ids = fields.One2many('sos_sales_achievement_report_brief', 'ref_id',copy=True) @@ -114,10 +118,26 @@ class SOS_Sales_Achievement_Report(models.Model): 'ref_id', string="Billing Collection Lines" ) - @api.depends('financial_year', 'sales_person') + def action_yet_to_bill(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Yet To Bill', + 'res_model': 'yet_to_bill_wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'brief_ref_id': self.id, # scopes wizard to this report's brief lines + } + } + @api.depends('category','financial_year', 'sales_person') def _compute_display_name(self): for record in self: - record.display_name = f"{record.financial_year or ''} / {record.sales_person.name or ''}" + if record.category == "sales": + record.display_name = f"{record.financial_year or ''} / {record.sales_person.name or ''}" + else: + record.display_name = f"{record.financial_year or ''} / Export" + @api.depends('opening_balance', 'actual_target_april', 'billed_target_april') def _compute_yet_to_billed_april(self): @@ -158,31 +178,7 @@ class SOS_Sales_Achievement_Report(models.Model): - @api.depends( - 'yet_to_billed_target_april', 'yet_to_billed_target_may', 'yet_to_billed_target_june', - 'yet_to_billed_target_july', 'yet_to_billed_target_august', 'yet_to_billed_target_september', - 'yet_to_billed_target_october', 'yet_to_billed_target_november', 'yet_to_billed_target_december', - 'yet_to_billed_target_january', 'yet_to_billed_target_february', 'yet_to_billed_target_march' - ) - def _compute_ytd_yet_to_billed(self): - month_map = [ - 'april', 'may', 'june', 'july', 'august', 'september', - 'october', 'november', 'december', - 'january', 'february', 'march' - ] - current_month = date.today().month - # In FY April to March, January=10th index - if current_month >= 4: - months_to_include = month_map[:current_month - 4 + 1] - else: - months_to_include = month_map[:current_month + 9 + 1] - - for rec in self: - total = 0.0 - for m in months_to_include: - total += getattr(rec, f'yet_to_billed_target_{m}', 0.0) - rec.ytd_yet_to_billed = total def action_view_billed_lines(self): self.ensure_one() @@ -193,29 +189,31 @@ class SOS_Sales_Achievement_Report(models.Model): else: view_id = self.env.ref('sos_sales.view_sos_billed_collection_wizard_form_collected').id - # Extract year from financial_year string like 'FY 2025-2026' match = re.search(r'FY\s*(\d{4})', self.financial_year or '') year = int(match.group(1)) if match else date.today().year - # Get month date range last_day = calendar.monthrange(year, month)[1] from_date = date(year, month, 1) to_date = date(year, month, last_day) + if self.category == "sales": + sales_person_id = self.sales_person.id or self.ref('sales_person').id - # Use the actual related record's value (not the related field) - sales_person_id = self.sales_person.id or self.ref('sales_person').id + if not sales_person_id: + raise UserError("Sales person is missing in this record.") - # If sales_person is still None, raise a warning - if not sales_person_id: - raise UserError("Sales person is missing in this record.") - - # Search filtered billing lines - billed_lines = self.env['sos_billing_collection'].search([ - ('sales_person', '=', sales_person_id), - ('action_status', '=', action), - ('date_of_action', '>=', from_date), - ('date_of_action', '<=', to_date), - ]) + billed_lines = self.env['sos_billing_collection'].search([ + ('sales_person', '=', sales_person_id), + ('action_status', '=', action), + ('date_of_action', '>=', from_date), + ('date_of_action', '<=', to_date), + ]) + else: + billed_lines = self.env['sos_billing_collection'].search([ + ('category', '=', "export"), + ('action_status', '=', action), + ('date_of_action', '>=', from_date), + ('date_of_action', '<=', to_date), + ]) wizard = self.env['sos_billed_collection_wizard'].create({ 'main_parent_id': self.id, 'billed_line_ids': [(6, 0, billed_lines.ids)], @@ -246,9 +244,14 @@ class SOS_Sales_Achievement_Report(models.Model): # } def action_view_brief_lines_acc(self): self.ensure_one() - domain = [('ref_record_id', '=', self.id)] - - + current_date = date.today() + start_year = current_date.year if current_date.month >= 4 else current_date.year - 1 + end_year = start_year + 1 + current_fy = f"FY {start_year}-{end_year}" + domain = [ + ('ref_record_id', '=', self.id), + ('financial_year', '!=', current_fy), + ] brief_lines = self.env['sos_sales_achievement_report_brief'].search(domain) @@ -266,7 +269,6 @@ class SOS_Sales_Achievement_Report(models.Model): def action_view_brief_lines(self): self.ensure_one() domain = [('ref_id', '=', self.id)] - # Extract starting year from 'FY 2025-2026' match = re.search(r'FY\s*(\d{4})', self.financial_year or '') if match: @@ -289,6 +291,14 @@ class SOS_Sales_Achievement_Report(models.Model): domain.append(('action_date', '<=', end_date)) brief_lines = self.env['sos_sales_achievement_report_brief'].search(domain) + total_proposal_value=0 + for eachrecord in brief_lines: + total_proposal_value += eachrecord.proposal_value + + if month: + month_name = calendar.month_name[month].lower() + field_name = f"actual_target_{month_name}" + self[field_name] = total_proposal_value return { 'name': 'Achievement Brief Lines', @@ -497,27 +507,22 @@ class SOS_Sales_Achievement_Report(models.Model): record.achievement_percentage_total = "0.00%" @api.depends( - 'yet_to_billed_target_april','yet_to_billed_target_may','yet_to_billed_target_june', - 'yet_to_billed_target_july','yet_to_billed_target_august','yet_to_billed_target_september', - 'yet_to_billed_target_october','yet_to_billed_target_november','yet_to_billed_target_december', - 'yet_to_billed_target_january','yet_to_billed_target_february','yet_to_billed_target_march' + 'yet_to_billed_target_april','yet_to_billed_target_may','yet_to_billed_target_june', + 'yet_to_billed_target_july','yet_to_billed_target_august','yet_to_billed_target_september', + 'yet_to_billed_target_october','yet_to_billed_target_november','yet_to_billed_target_december', + 'yet_to_billed_target_january','yet_to_billed_target_february','yet_to_billed_target_march' ) def _compute_yet_to_billed_total_target(self): for record in self: - record.yet_to_billed_target_total = round(sum([ - record.yet_to_billed_target_april or 0, - record.yet_to_billed_target_may or 0, - record.yet_to_billed_target_june or 0, - record.yet_to_billed_target_july or 0, - record.yet_to_billed_target_august or 0, - record.yet_to_billed_target_september or 0, - record.yet_to_billed_target_october or 0, - record.yet_to_billed_target_november or 0, - record.yet_to_billed_target_december or 0, - record.yet_to_billed_target_january or 0, - record.yet_to_billed_target_february or 0, - record.yet_to_billed_target_march or 0 - ]), 2) + # Get current month name in lowercase + month_num = date.today().month + month_name = calendar.month_name[month_num].lower() # e.g., "august" + + # Build the dynamic field name + field_name = f"yet_to_billed_target_{month_name}" + record.ytd_yet_to_billed = round(getattr(record, field_name) or 0, 2) + record.yet_to_billed_target_total = round(getattr(record, field_name) or 0, 2) + @api.depends( 'planned_target_april','planned_target_may','planned_target_june', 'planned_target_july','planned_target_august','planned_target_september', @@ -599,6 +604,7 @@ class SOS_Sales_Achievement_Report_Brief(models.Model): _name = 'sos_sales_achievement_report_brief' _description = 'Achievement Brief' _order = 'action_date desc' + _rec_name = 'po_no' def get_financial_year_selection(self): current_date = date.today() start_year = 2024 # starting from FY 2024-2025 @@ -624,33 +630,77 @@ class SOS_Sales_Achievement_Report_Brief(models.Model): string="Financial Year", required=True ) + category = fields.Selection([ + ('sales', 'Sales'), + ('export', 'Export') + ], string="Category", required=True,related="ref_id.category") sales_person = fields.Many2one('res.users', string='Sales person',related="ref_id.sales_person",store=True) po_no = fields.Char(string="PO No") invoice_no = fields.Char(string="Invoice No") proposal_value = fields.Monetary(currency_field='currency_id',string="PO Value(In Lakhs)") billed_date = fields.Date(string="Billed Date") billed_amount = fields.Monetary(currency_field='currency_id',string="Billed Value(In Lakhs)") - # def view_lines_acc(self): - # self.ensure_one() - # return { - # 'name': 'Achievement Brief Lines', - # 'type': 'ir.actions.act_window', - # 'res_model': 'sos_sales_achievement_report_brief', - # 'view_mode': 'tree', - # 'domain': [('ref_record_id', '=', self.id)], - # 'context': { - # 'default_ref_record_id': self.id - # }, - # 'target': 'current', - # } + def delete_achievement_line(self): - self.unlink() - def unlink(self): - for record in self: - report = self.env['sos_sales_achievement_report'].browse(record.ref_id.id) - if report and hasattr(report, 'opening_balance'): - report.write({'opening_balance': report.opening_balance - record.proposal_value}) - return super(SOS_Sales_Achievement_Report_Brief, self).unlink() + + Env = self.env + Line = Env['sos_sales_achievement_report_brief'] + Parent = Env['sos_sales_achievement_report'] + + # 1) Collect affected month windows per parent (no sums yet) + # { parent_id: set((cal_year, month_num)) } + affected = {} + + for rec in self: + # resolve parent id (prefer Many2one 'ref_id'; fallback to 'ref_record_id') + parent_id = False + if hasattr(rec, 'ref_id') and rec.ref_id: + parent_id = rec.ref_id.id + elif hasattr(rec, 'ref_record_id') and rec.ref_record_id: + parent_id = getattr(rec.ref_record_id, 'id', rec.ref_record_id) + if not parent_id: + continue + + d = fields.Date.to_date(rec.action_date) or fields.Date.context_today(self) + month_num = d.month # 1..12 + + fy_text = (rec.financial_year or '').strip() + m = re.search(r'FY\s*(\d{4})', fy_text) + if not m: + # skip this line if FY is malformed + continue + fy_start = int(m.group(1)) + + # Apr–Dec => start year; Jan–Mar => start year+1 + cal_year = fy_start if month_num >= 4 else fy_start + 1 + + affected.setdefault(parent_id, set()).add((cal_year, month_num)) + + # 2) Delete the lines first + super(type(self), self).unlink() + + # 3) Recompute totals from remaining lines and write to parent + for parent_id, months in affected.items(): + write_vals = {} + for cal_year, month_num in months: + last_day = calendar.monthrange(cal_year, month_num)[1] + start_str = f"{cal_year}-{month_num:02d}-01" + end_str = f"{cal_year}-{month_num:02d}-{last_day:02d}" + + total = sum(Line.search([ + ('ref_id', '=', parent_id), + ('action_date', '>=', start_str), + ('action_date', '<=', end_str), + ]).mapped('proposal_value')) or 0.0 + + field_name = f"actual_target_{calendar.month_name[month_num].lower()}" + if field_name in Parent._fields: + write_vals[field_name] = total + + if write_vals: + Parent.browse(parent_id).write(write_vals) + + return True def open_line_form(self): self.ensure_one() @@ -668,60 +718,58 @@ class SOS_Sales_Achievement_Report_Brief(models.Model): return f"FY {start_year}-{end_year}" def write(self, vals): + res = super().write(vals) + + # avoid infinite loop when we write our computed monthly field + if self.env.context.get('_skip_month_compute'): + return res + for rec in self: - # Previous values - old_billed_date = rec.billed_date - old_billed_amount = rec.billed_amount - report = rec.ref_id - # New values - new_billed_date = vals.get('billed_date', old_billed_date) - new_billed_amount = vals.get('billed_amount', old_billed_amount) - if isinstance(new_billed_date, str): - new_billed_date = datetime.strptime(new_billed_date, '%Y-%m-%d').date() + # take incoming values if present, else current record values + action_date_any = vals.get('action_date', rec.action_date) + fy_text = (vals.get('financial_year', rec.financial_year) or '').strip() - # Adjust billed target - if report and old_billed_date and new_billed_date: - old_month_billed = old_billed_date.strftime('%B').lower() - new_month_billed = new_billed_date.strftime('%B').lower() - if old_month_billed != new_month_billed or old_billed_amount != new_billed_amount: - old_field_billed = f"billed_target_{old_month_billed}" - new_field_billed = f"billed_target_{new_month_billed}" - if hasattr(report, old_field_billed): - report.write({ - old_field_billed: max((getattr(report, old_field_billed, 0.0) or 0.0) - old_billed_amount, 0.0) - }) - if hasattr(report, new_field_billed): - report.write({ - new_field_billed: (getattr(report, new_field_billed, 0.0) or 0.0) + new_billed_amount - }) - # Optionally create billing collection entry (if needed + # normalize to date + d = fields.Date.to_date(action_date_any) or fields.Date.context_today(self) + month_num = d.month # 1..12 + + # FY like "FY 2025-2026" -> start year 2025 + m = re.search(r'FY\s*(\d{4})', fy_text) + if not m: + raise ValueError(f"Unable to extract year from financial year: {fy_text}") + fy_start = int(m.group(1)) + + # Apr–Dec belong to start year; Jan–Mar to start year+1 + cal_year = fy_start if month_num >= 4 else fy_start + 1 + + # monthly date window + last_day = calendar.monthrange(cal_year, month_num)[1] + start_str = f"{cal_year}-{month_num:02d}-01" + end_str = f"{cal_year}-{month_num:02d}-{last_day:02d}" + + # sum for this record in that month domain = [ - ('ref_id', '=', report.id), - ('sales_person', '=', report.sales_person.id), - ('customer_name', '=', vals.get('customer_name', rec.customer_name.id)) + ('ref_id', '=', self.ref_record_id), + ('action_date', '>=', start_str), + ('action_date', '<=', end_str), ] + lines = rec.env['sos_sales_achievement_report_brief'].search(domain) + total_proposal_value=0 + for eachrecord in lines: + total_proposal_value += eachrecord.proposal_value + record = self.env['sos_sales_achievement_report'].search( + [('id', '=', self.ref_record_id)], + limit=1 + ) + month_name = calendar.month_name[month_num].lower() + field_name = f"actual_target_{month_name}" + field_name = f"actual_target_{month_name}" + record.write({ + field_name : total_proposal_value + }) - existing = self.env['sos_billing_collection'].search(domain, limit=1) - - if not existing: - self.env['sos_billing_collection'].create({ - 'ref_id': report.id, - 'customer_name': vals.get('customer_name', rec.customer_name.id), - 'sales_person': report.sales_person.id, - 'action_status': 'Billed', - 'date_of_action': new_billed_date, - 'po_no':vals.get('po_no'), - 'value': new_billed_amount - }) - else: - existing.write({ - 'value': new_billed_amount, - 'po_no':vals.get('po_no'), - 'date_of_action': new_billed_date - }) - - return super(SOS_Sales_Achievement_Report_Brief, self).write(vals) + return res @api.model def create(self, vals): ref_id = vals.get('ref_id') @@ -822,7 +870,7 @@ class SosBilledCollectionWizard(models.TransientModel): billing_lines = self.env['sos_billing_collection'].search([('ref_id', '=', main_parent_id)]) res['billed_line_ids'] = [(6, 0, billing_lines.ids)] return res - + def action_add_new_collection_line(self): self.ensure_one() if not self.main_parent_id: @@ -851,7 +899,12 @@ class SosBillingCollection(models.Model): _name = 'sos_billing_collection' _description = 'Billing & Collection Details' + ref_id = fields.Many2one('sos_sales_achievement_report', ondelete="cascade", required=True) + category = fields.Selection([ + ('sales', 'Sales'), + ('export', 'Export') + ], string="Category", required=True,related="ref_id.category") customer_name = fields.Many2one('sos_customers', string="Customer Name") sales_person = fields.Many2one( 'res.users', @@ -871,7 +924,12 @@ class SosBillingCollection(models.Model): ) value = fields.Monetary(currency_field='currency_id', string="Value (In Lakhs)") customer_name = fields.Many2one('sos_customers',string="Customer Name") - po_no = fields.Char(string="PO No") + po_id = fields.Many2one( + 'sos_sales_achievement_report_brief', # or 'sos_sales_achievement_report' if PO lives there + string="PO No", + domain="[('po_no','!=', False), ('customer_name', '=', customer_name)]", + ) + po_no = fields.Char(string="PO No", related='po_id.po_no', store=True, readonly=True) invoice_no = fields.Char(string="Invoice No") products = fields.Selection( [ @@ -894,7 +952,16 @@ class SosBillingCollection(models.Model): return f"FY {start_year}-{end_year}" def delete_form_line(self): self.unlink() - + def write_line_form(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Edit Line', + 'res_model': self._name, + 'res_id': self.id, + 'view_mode': 'form', + 'target': 'new', # Opens as a popup + } def unlink(self): for record in self: @@ -919,6 +986,53 @@ class SosBillingCollection(models.Model): setattr(report, actual_field_name, current_val - record.value) return super(SosBillingCollection, self).unlink() + def write(self, vals): + for record in self: + # Capture old values + old_date = record.date_of_action + old_status = record.action_status + old_value = record.value + + # Determine if any key field is changing + new_date = vals.get('date_of_action', old_date) + new_status = vals.get('action_status', old_status) + new_value = vals.get('value', old_value) + + # Convert date if needed + if isinstance(new_date, str): + new_date = datetime.strptime(new_date, '%Y-%m-%d').date() + + # Compute old and new financial year/month + old_fy = self._get_financial_year(old_date) if old_date else None + old_month = old_date.strftime('%B').lower() if old_date else None + new_fy = self._get_financial_year(new_date) if new_date else None + new_month = new_date.strftime('%B').lower() if new_date else None + + # Build field names + old_field = f"{old_status.lower()}_target_{old_month}" if old_month else None + new_field = f"{new_status.lower()}_target_{new_month}" if new_month else None + + # Fetch related report + years = [fy for fy in [old_fy, new_fy] if fy] + report = self.env['sos_sales_achievement_report'].search([ + ('sales_person', '=', record.sales_person.id), + ('financial_year', 'in', years) + ], limit=1) + + + if report: + # Subtract old value + if old_field and hasattr(report, old_field): + current_old = getattr(report, old_field, 0.0) or 0.0 + setattr(report, old_field, current_old - old_value) + + # Add new value + if new_field and hasattr(report, new_field): + current_new = getattr(report, new_field, 0.0) or 0.0 + setattr(report, new_field, current_new + new_value) + + # Proceed with actual write + return super(SosBillingCollection, self).write(vals) @api.model def create(self, vals): diff --git a/sos_sales/models/sos_sales_action_plan.py b/sos_sales/models/sos_sales_action_plan.py index 4772bb8..db726ec 100755 --- a/sos_sales/models/sos_sales_action_plan.py +++ b/sos_sales/models/sos_sales_action_plan.py @@ -24,6 +24,24 @@ class sos_sales_action_plan(models.Model): string="End Customer/Quote No", domain="[('customer_name', '=', customer_name), ('quote_no', '!=', False)]" ) + interested_in = fields.Selection( + [ + ('Product', 'Products'), + ('Project', 'Projects') + ], + string="Interested In",required=True,default="Product") + sales_type = fields.Selection( + [ + ('Domestic', 'Domestic'), + ('International', 'International') + ], + string="Sales Type",default="Domestic") + project_name= fields.Many2one('sos_projects',string="Project Name") + country = fields.Many2one( + 'res.country', + string='Country', + default=lambda self: self.env['res.country'].search([('code', '=', 'IN')], limit=1) +) ce_product_type = fields.Selection( [ ('Sales', 'Sales'), @@ -45,7 +63,7 @@ class sos_sales_action_plan(models.Model): ('MC 250W', 'MC 250W'), ('HeartTarang', 'HeartTarang') ], - string="Products",required=True) + string="Products") location = fields.Char(string="Location") quantity = fields.Char(string="Quantity") sales_executive = fields.Many2one( @@ -58,7 +76,7 @@ class sos_sales_action_plan(models.Model): '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)] + domain=lambda self: [('groups_id', 'in', self.env.ref('sos_inventory.sos_sales_user').ids + self.env.ref('sos_inventory.sos_ce_head').ids + self.env.ref('sos_inventory.sos_sales_sapl_user').ids)] ) action_type = fields.Selection([ @@ -107,7 +125,21 @@ class sos_sales_action_plan(models.Model): ('draft', 'Draft'), ('confirmed', 'Confirmed'), ], string='State', default='draft', readonly=True) - + is_ce_user_created = fields.Boolean( + compute='_compute_is_ce_user_created', + store=True, + string='Created by CE User' + ) + reporting_to = fields.Many2one('res.users',related="sales_executive.reporting_to", string='Reporting To') + @api.depends('create_uid') + def _compute_is_ce_user_created(self): + ce_groups = [ + self.env.ref('sos_inventory.sos_ce_user').id + ] + for record in self: + record.is_ce_user_created = any( + gid in record.create_uid.groups_id.ids for gid in ce_groups + ) def _get_customer_domain(self): if ( self.env.user.has_group('sos_inventory.sos_management_user') or @@ -450,6 +482,7 @@ class sos_sales_action_plan(models.Model): } } else: + interested_in=self.interested_in.lower()+'s' vals = { 'customer_name': self.customer_name.id, 'end_customer_name': self.end_customer_name, @@ -462,7 +495,11 @@ class sos_sales_action_plan(models.Model): 'quantity': self.quantity, 'po_no': self.po_no, 'po_copy': self.po_copy, - 'po_copy_filename': self.po_copy_filename + 'po_copy_filename': self.po_copy_filename, + 'sales_type':self.sales_type, + 'project_name':self.project_name.id, + 'interested_in':interested_in, + 'country':self.country.id } if self.ce_product_type: vals['ce_product_type'] = self.ce_product_type diff --git a/sos_sales/models/sos_sales_leads.py b/sos_sales/models/sos_sales_leads.py index 4503859..a623596 100755 --- a/sos_sales/models/sos_sales_leads.py +++ b/sos_sales/models/sos_sales_leads.py @@ -61,6 +61,8 @@ class sos_sales_leads(models.Model): ) line_ids_contacts = fields.One2many('sos_leads_customer_contact_lines', 'ref_id', string="Contact Details",copy=True) convert_to_customer_btn_display = fields.Boolean(default=True) + responsible_sales_persons = fields.Many2many('res.users',string="Responsible") + def action_convert_to_customer(self): self.convert_to_customer_btn_display = False self.status = "Moved to Customer" @@ -73,14 +75,18 @@ class sos_sales_leads(models.Model): 'mobile_number': line.mobile_number, 'set_as_primary': line.set_as_primary, }) for line in self.line_ids_contacts] + if self.interested_in == "Product": + interest = "products" + else: + interest = "projects" customer_record = customer_model.create({ 'customer_name': self.company_name, 'customer_city':self.location, 'vertical_domain':self.vertical_domain.id, 'correspondence_address': self.correspondence_address, - 'products':self.products, - 'interested_in':self.interested_in, - 'project_name':self.project_name, + 'products':self.products_interested.name, + 'interested_in':interest, + 'project_name':self.project_name.name, 'line_ids_contacts': line_contacts_data, 'leads_ref_no':self.id diff --git a/sos_sales/report/__pycache__/business_performance_report_abstract.cpython-310.pyc b/sos_sales/report/__pycache__/business_performance_report_abstract.cpython-310.pyc index fb800eb..bac9597 100644 Binary files a/sos_sales/report/__pycache__/business_performance_report_abstract.cpython-310.pyc and b/sos_sales/report/__pycache__/business_performance_report_abstract.cpython-310.pyc differ diff --git a/sos_sales/report/business_performance_report_abstract.py b/sos_sales/report/business_performance_report_abstract.py index 313e789..8cffc6f 100755 --- a/sos_sales/report/business_performance_report_abstract.py +++ b/sos_sales/report/business_performance_report_abstract.py @@ -15,12 +15,17 @@ class ReportBusinessPerformance(models.AbstractModel): def _get_report_values(self, docids, data=None): sales_person_id = data.get('sales_person_id') if data else False financial_year = self._get_financial_year() + category = data.get('category') domain = [('financial_year', '=', financial_year)] - if sales_person_id: - domain.append(('sales_person', '=', sales_person_id)) + if category == "sales": + if sales_person_id: + domain.append(('sales_person', '=', sales_person_id)) - records = self.env['sos_sales_achievement_report'].search(domain, order="sales_person ASC") + records = self.env['sos_sales_achievement_report'].search(domain, order="sales_person ASC") + else: + domain.append(('category', '=', 'export')) + records = self.env['sos_sales_achievement_report'].search(domain) months = [ 'april', 'may', 'june', 'july', 'august', 'september', @@ -121,6 +126,7 @@ class ReportBusinessPerformance(models.AbstractModel): 'summary': summary, 'ytd_months': ytd_months, 'current_month_index': int(current_month_index), - 'is_filtered_by_sales_person': bool(sales_person_id) + 'is_filtered_by_sales_person': bool(sales_person_id), + 'report_generated_on':date.today() } diff --git a/sos_sales/report/pipeline_report_result.xml b/sos_sales/report/pipeline_report_result.xml index 73a65e0..1f7fa1c 100755 --- a/sos_sales/report/pipeline_report_result.xml +++ b/sos_sales/report/pipeline_report_result.xml @@ -21,6 +21,15 @@ border: solid 1px #ccc; border-radius: 20px;">
PIPELINE REPORT
+
+ +
+

Report Period : From To .

+
+
+ Report generated on : +
+ @@ -41,7 +50,7 @@ + font-size: smaller;" t-att-colspan="month_count">Expected Order Value(In Lacs) diff --git a/sos_sales/report/report_business_performance.xml b/sos_sales/report/report_business_performance.xml index f1eb40d..045f769 100755 --- a/sos_sales/report/report_business_performance.xml +++ b/sos_sales/report/report_business_performance.xml @@ -9,8 +9,11 @@


Business Performance Report-

- Note : Rupees(In Lakhs) - + Note : Rupees(In Lakhs) +

+ Report generated on : +
+
Expected Order Value (In Lakhs)
@@ -44,7 +47,7 @@ - + @@ -116,9 +119,11 @@
- - - + + + diff --git a/sos_sales/report/sos_proposal_boq_report_view.xml b/sos_sales/report/sos_proposal_boq_report_view.xml index 832824a..9de901f 100755 --- a/sos_sales/report/sos_proposal_boq_report_view.xml +++ b/sos_sales/report/sos_proposal_boq_report_view.xml @@ -99,6 +99,26 @@
Sales Executive :
+ Sales Executive : +
Month
+

+ + + + + + + + + + + + + + + + + + +
Costing
Product Cost
Opreational Cost per Battery
Opreational Cost
Packing and Forwarding
I and C Cost
Warranty (%)
Warranty
MSP Per Battery
Additional Warranty
Proposal Value
Accounts Costing
MSP(Per Battery)
Final MSP
diff --git a/sos_sales/report/sos_proposal_report_view.xml b/sos_sales/report/sos_proposal_report_view.xml index c902d37..5d6194a 100755 --- a/sos_sales/report/sos_proposal_report_view.xml +++ b/sos_sales/report/sos_proposal_report_view.xml @@ -68,7 +68,7 @@


- + diff --git a/sos_sales/security/ir.model.access.csv b/sos_sales/security/ir.model.access.csv index e8bf7b4..fd88c89 100755 --- a/sos_sales/security/ir.model.access.csv +++ b/sos_sales/security/ir.model.access.csv @@ -63,5 +63,6 @@ access_sos_billed_collection_wizard,sos_billed_collection_wizard access,model_so access_sos_business_performance_wizard,sos_business_performance_wizard access,model_sos_business_performance_wizard,base.group_user,1,1,1,1 access_sos_products,sos_products access,model_sos_products,base.group_user,1,1,1,1 access_sos_projects,sos_projects access,model_sos_projects,base.group_user,1,1,1,1 - +access_yet_to_bill_wizard,yet_to_bill_wizard access,model_yet_to_bill_wizard,base.group_user,1,1,1,1 +access_yet_to_bill_line,yet_to_bill_line access,model_yet_to_bill_line,base.group_user,1,1,1,1 diff --git a/sos_sales/security/record_rules.xml b/sos_sales/security/record_rules.xml index 77969c5..96ff255 100755 --- a/sos_sales/security/record_rules.xml +++ b/sos_sales/security/record_rules.xml @@ -7,7 +7,8 @@ [(1, '=', 1)] @@ -18,7 +19,9 @@ Sos Achievement Report: All Records - Read Access [ - ('create_uid', '=', user.id) + '|', + ('create_uid', '=', user.id), + ('sales_person', '=', user.id) ] @@ -33,7 +36,8 @@ [(1, '=', 1)] @@ -55,44 +59,64 @@ - - Sos sales action plan: Own Records - Read Access + + + Sos Action Plan: Full Access (Mgmt/Finance/Reviewer) - [(1, '=', 1)] + + [(1,'=',1)] - - Sales Action Plan: CE Head Reads CE User Records - - - - - - - - - - Sos sales action plan: All Records - Read Access + + + Sos Action Plan: Own and Team (Internal Users) - [ - '|', - ('create_uid', '=', user.id), - ('sales_executive', '=', user.id) -] + + [ + '|','|', + ('create_uid', '=', user.id), + ('sales_executive', '=', user.id), + ('reporting_to', '=', user.id) + ] + + - + + + + Sales Action Plan: CE Team View CE Records + + [('is_ce_user_created', '=', True)] + + + + + + + + Sales Action Plan: CE Head View CE Records + + [('is_ce_user_created', '=', True)] + + + + + + + Sos customers: Own Records - Read Access @@ -101,14 +125,24 @@ - + + Customers: CE Team View CE customers Records + + [('is_ce_user_created', '=', True)] + + + + + + Sos customers: All Records - Read Access @@ -125,55 +159,39 @@ - - - Sos Case Diary: Own Records - Read Access + + Case Diary: CE Team View CE Records - [(1, '=', 1)] - + [('is_ce_user_created', '=', True)] + - - + + - - Case Diary: CE Head Reads CE User Records + + + Case Diary: CE Head View CE Records - + [('is_ce_user_created', '=', True)] - - Sos Case Diary: All Records - Read Access - - - [ - '|', '|', - ('create_uid', '=', user.id), - ('reporting_to', '=', user.id), - ('sales_person', '=', user.id) - ] - - - - - - - - - Sos Case Diary Report: Own Records - Read Access - - [(1, '=', 1)] + + + Sos Case Diary: Full Access (Mgmt/Finance/Reviewer) + + + [(1,'=',1)] @@ -181,20 +199,26 @@ - - Sos Case Diary Report: All Records - Read Access - - [ - '|', + + + Sos Case Diary: Own and Team (Internal Users) + + + [ + '|','|', ('create_uid', '=', user.id), - ('reporting_to', '=', user.id) - ] + ('reporting_to', '=', user.id), + ('sales_person', '=', user.id) + ] + + - + - + + Sos Leads: Own Records - Read Access @@ -214,14 +238,57 @@ Sos Leads: All Records - Read Access [ - '|', + '|','|', ('create_uid', '=', user.id), - ('lead_generated_by', '=', user.id) + ('lead_generated_by', '=', user.id), + ('responsible_sales_persons', 'in', [user.id]) ] + + + Sos Proposal BOQ: All Records - Read Access + + [ + '|', + ('create_uid', '=', user.id), + ('sales_executive', '=', user.id) + ] + + + + + + + + + Sos Proposal Builder: All Records - Read Access + + [ + '|', + ('create_uid', '=', user.id), + ('sales_executive', '=', user.id) + ] + + + + + + + + + Sos Proposal Requirement: All Records - Read Access + + [ + ('create_uid', '=', user.id) + ] + + + + + diff --git a/sos_sales/views/menu.xml b/sos_sales/views/menu.xml index 335ec9f..7f613b5 100755 --- a/sos_sales/views/menu.xml +++ b/sos_sales/views/menu.xml @@ -1,7 +1,7 @@ - - - - + + + + \ No newline at end of file diff --git a/sos_sales/views/sos_case_diary_view.xml b/sos_sales/views/sos_case_diary_view.xml index 15c0924..ba99ecd 100755 --- a/sos_sales/views/sos_case_diary_view.xml +++ b/sos_sales/views/sos_case_diary_view.xml @@ -62,6 +62,8 @@ + + @@ -133,7 +135,7 @@
Witness For Commercial Order
Final Costing
Final Costing
- @@ -281,7 +283,7 @@ - + + sequence="8" groups="sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_ce_user,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/> + sequence="9" groups="sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_ce_user,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/> + sequence="10" groups="sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_ce_user,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/> + @@ -89,5 +90,5 @@ - + diff --git a/sos_sales/views/sos_proposal_boq_view.xml b/sos_sales/views/sos_proposal_boq_view.xml index f70bcc1..55881aa 100755 --- a/sos_sales/views/sos_proposal_boq_view.xml +++ b/sos_sales/views/sos_proposal_boq_view.xml @@ -19,6 +19,7 @@ +

@@ -1655,6 +1656,35 @@ + + + + + + + + +

+
Department In-Charge + Top Management Sign



+ + + + + + + +
No of PersonsCost per Day
Man Month( 2 to 3 Yrs)
Man Month( 3 to 5 Yrs)
Man Month(Manager)
Total Cost(Man Month+Travel)
+ + + + + + + + + + +

Finished Goods

@@ -1676,27 +1706,24 @@ text-decoration: underline;">Miscellaneous -
- + +


-
+

Product Cost

@@ -1721,20 +1748,8 @@
-
- -

Miscellaneous Cost

- - - - - - -
-
-
+

Opreational Cost

- + + - + + +
Packing & Forwarding
Installation & Commissioning
I & C Cost
Warranty (%)
Warranty Cost
Additional Warranty
MSP Per Battery
MSP Per Battery
Proposal Value
+ + + + + + + + + -
Accounts Costing
MSP(Per Battery)
Final MSP
Final MSP
@@ -1788,7 +1816,7 @@ To Be Filled By Finance Team Approved By - + diff --git a/sos_sales/views/sos_proposal_customer_requirement_view.xml b/sos_sales/views/sos_proposal_customer_requirement_view.xml index 6160b9a..7644590 100755 --- a/sos_sales/views/sos_proposal_customer_requirement_view.xml +++ b/sos_sales/views/sos_proposal_customer_requirement_view.xml @@ -28,6 +28,8 @@ + + @@ -37,7 +39,7 @@ - + diff --git a/sos_sales/views/sos_sales_achievement_report_view.xml b/sos_sales/views/sos_sales_achievement_report_view.xml index fe3e009..e2ac99f 100755 --- a/sos_sales/views/sos_sales_achievement_report_view.xml +++ b/sos_sales/views/sos_sales_achievement_report_view.xml @@ -15,10 +15,11 @@ name="action_report_achievement_btn"> Print Report - + - - +
+ + + -
+
- -

+