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 @@
| Expected Order Value (In Lakhs) |
+ 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 :
+
+
@@ -44,7 +47,7 @@
- |
+ |
|
@@ -116,9 +119,11 @@
-
- | Sales Executive : |
-
+
+ |
+ Sales Executive :
+ |
+
| Month |
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 @@
+
+
+
+ | 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 @@
- | Final Costing |
+ Final Costing |
| ₹ |
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
- | Department In-Charge
+ | Top Management Sign
|
@@ -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"/>