Feature #13

Merged
Deena merged 3 commits from Feature into main 2025-09-17 15:02:23 +05:30
45 changed files with 1127 additions and 401 deletions
Showing only changes of commit 574bec5c2b - Show all commits

View File

@ -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'
],

View File

@ -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:

View File

@ -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)

View File

@ -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 = """
<p>Below <b>Proposal</b> is waiting for your Updation</p>
# ---- 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"""
<div style="font-family:Inter, Arial, sans-serif; color:#111827;">
<p style="margin:0 0 10px 0;">
Below <b>Proposal Costing</b> was completed. You can proceed with the proposal builder.
</p>
<table style="border-collapse:separate;border-spacing:0; width:100%; max-width:560px; background:#fff; border:1px solid #e5e7eb; border-radius:8px; overflow:hidden;">
<tbody>
<tr style="background:#f9fafb;">
<td style="padding:10px 12px; width:40%; color:#303236;">Customer</td>
<td style="padding:10px 12px;"><b>{customer}</b></td>
</tr>
<tr>
<td style="padding:10px 12px; color:#303236;">Delivery Location</td>
<td style="padding:10px 12px;">{location}</td>
</tr>
<tr>
<td style="padding:10px 12px; color:#303236;">Installation Location</td>
<td style="padding:10px 12px;">{insta_location}</td>
</tr>
<tr style="background:#f9fafb;">
<td style="padding:10px 12px; color:#303236;">Number of Batteries</td>
<td style="padding:10px 12px;">{batteries:,}</td>
</tr>
<tr style="background:#f9fafb;">
<td style="padding:10px 12px; color:#303236;">Warranty (In Months)</td>
<td style="padding:10px 12px;">{warranty}</td>
</tr>
<tr>
<td style="padding:10px 12px; color:#303236;"> Final MSP per Battery</td>
<td style="padding:10px 12px;"><b>{final_cost_disp}</b></td>
</tr>
</tbody>
</table>
</div><br></br>
"""
# 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'

View File

@ -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):

View File

@ -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'),

View File

@ -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))
# AprDec => start year; JanMar => 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))
# AprDec belong to start year; JanMar 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):

View File

@ -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

View File

@ -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

View File

@ -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()
}

View File

@ -21,6 +21,15 @@
border: solid 1px #ccc;
border-radius: 20px;">
<h6 style="text-align:center">PIPELINE REPORT</h6>
<div style="text-align:right; font-size:14px; margin-bottom:5px;">
<t t-if="from_date or to_date">
<div>
<p><b>Report Period : From</b> <t t-esc="from_date or 'N/A'"/> <b>To</b> <t t-esc="to_date or 'N/A'"/>.</p>
</div>
</t>
<b>Report generated on : </b> <t t-esc="report_generated_on"/>
</div>
<table class="table_custom display">
<thead style="background-color: lavender;">
<tr>
@ -41,7 +50,7 @@
<th style=" line-height: 2px; text-align: center;
font-weight: normal;
font-size: smaller;" t-att-colspan="month_count">Expected Order Value (In Lakhs)</th>
font-size: smaller;" t-att-colspan="month_count">Expected Order Value(In Lacs)</th>
</tr>
</thead>
<tbody>

View File

@ -9,8 +9,11 @@
<div class="page">
<br></br>
<h4 style="text-align: center;">Business Performance Report- <t t-esc="financial_year"/></h4>
<span style="float: right;font-weight: bold;">Note : Rupees(In Lakhs)</span>
<!-- ✅ SUMMARY TABLE -->
<span style="float: right;"><b>Note :</b> Rupees(In Lakhs)</span>
<br></br><div style="text-align:right; font-size:14px; margin-bottom:5px;">
<b>Report generated on : </b> <t t-esc="report_generated_on"/>
</div>
<t t-if="not is_filtered_by_sales_person">
<table class="table_custom" style="width:100%; font-size:14px;">
@ -44,7 +47,7 @@
<t t-set="total_sales" t-value="0"/>
<t t-foreach="months" t-as="m">
<t t-set="val" t-value="summary['sales'][m]"/>
<td><t t-esc="val"/></td>
<td><t t-esc="'%.2f' % float(val)"/></td>
<t t-set="total_sales" t-value="total_sales + val"/>
</t>
<td><t t-esc="'%.2f' % float(total_sales)"/></td>
@ -116,9 +119,11 @@
<t t-foreach="docs" t-as="doc">
<table class="table_custom" style="width:100%; font-size:14px;">
<thead>
<tr style="background-color: #c4c4ff;">
<td colspan="15"><b>Sales Executive : <t t-esc="doc.sales_person.name"/></b></td>
</tr>
<tr t-if="doc.category == 'sales'">
<td colspan="15">
<b>Sales Executive : <t t-esc="doc.sales_person.name"/></b>
</td>
</tr>
<tr style="background-color:#e4e0f9">
<th>Month</th>
<t t-foreach="months" t-as="m">

View File

@ -99,6 +99,26 @@
</t>
</tbody>
</table>
<br></br>
<table class="table">
<tbody>
<tr><td colspan="2" style="background-color:#ddd;font-weight:bold">Costing</td></tr>
<tr><td>Product Cost</td><td><t t-esc="o.total_cost"/></td></tr>
<tr><td>Opreational Cost per Battery</td><td><t t-esc="o.margin_per_battery"/></td></tr>
<tr><td>Opreational Cost</td><td><t t-esc="o.margin"/></td></tr>
<tr><td>Packing and Forwarding</td><td><t t-esc="o.packing_and_forwarding"/></td></tr>
<tr><td>I and C Cost</td><td><t t-esc="o.iandc_costing"/></td></tr>
<tr><td>Warranty (%)</td><td><t t-esc="o.warranty_percentage"/></td></tr>
<tr><td>Warranty</td><td><t t-esc="o.warranty_cost"/></td></tr>
<tr><td>MSP Per Battery</td><td><t t-esc="o.additional_warranty"/></td></tr>
<tr><td>Additional Warranty</td><td><t t-esc="o.final_cost_per_battery"/></td></tr>
<tr><td>Proposal Value</td><td><t t-esc="o.final_cost"/></td></tr>
<tr><td colspan="2" style="background-color:#ddd;font-weight:bold">Accounts Costing</td></tr>
<tr><td>MSP(Per Battery)</td><td><t t-esc="o.final_cost_by_acc"/></td></tr>
<tr><td>Final MSP</td><td><t t-esc="o.final_total"/></td></tr>
</tbody>
</table>
<div style="width: 100%; display: flex; justify-content: space-between; margin-top: 50px;">
<table class="table table-bordered">

View File

@ -68,7 +68,7 @@
</table>
<br></br>
<table class="table_custom">
<thead><td>Final Costing</td></thead>
<thead><td><b>Final Costing</b></td></thead>
<tr>
<td><t t-esc="o.total_with_buffer"/></td>
</tr>

View File

@ -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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
63 access_sos_business_performance_wizard sos_business_performance_wizard access model_sos_business_performance_wizard base.group_user 1 1 1 1
64 access_sos_products sos_products access model_sos_products base.group_user 1 1 1 1
65 access_sos_projects sos_projects access model_sos_projects base.group_user 1 1 1 1
66 access_yet_to_bill_wizard yet_to_bill_wizard access model_yet_to_bill_wizard base.group_user 1 1 1 1
67 access_yet_to_bill_line yet_to_bill_line access model_yet_to_bill_line base.group_user 1 1 1 1
68

View File

@ -7,7 +7,8 @@
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[
(4, ref('sos_inventory.sos_management_user')),
(4, ref('sos_inventory.sos_finance_user'))
(4, ref('sos_inventory.sos_finance_user')),
(4, ref('sos_inventory.sos_sales_reviewer'))
]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
@ -18,7 +19,9 @@
<field name="name">Sos Achievement Report: All Records - Read Access</field>
<field name="model_id" ref="model_sos_sales_achievement_report"/>
<field name="domain_force">[
('create_uid', '=', user.id)
'|',
('create_uid', '=', user.id),
('sales_person', '=', user.id)
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
@ -33,7 +36,8 @@
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[
(4, ref('sos_inventory.sos_management_user')),
(4, ref('sos_inventory.sos_finance_user'))
(4, ref('sos_inventory.sos_finance_user')),
(4, ref('sos_inventory.sos_sales_reviewer'))
]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
@ -55,44 +59,64 @@
<field name="perm_unlink" eval="0"/>
</record>
<!-- sales_action RECORD RULE-->
<record id="sos_sales_action_plan_own_records_rule" model="ir.rule">
<field name="name">Sos sales action plan: Own Records - Read Access</field>
<!-- 1) Full visibility for management/finance/sales reviewer -->
<record id="sos_sales_action_plan_full_access_rule" model="ir.rule">
<field name="name">Sos Action Plan: Full Access (Mgmt/Finance/Reviewer)</field>
<field name="model_id" ref="model_sos_sales_action_plan"/>
<field name="domain_force">[(1, '=', 1)]</field>
<!-- Always true -->
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[
(4, ref('sos_inventory.sos_management_user')),
(4, ref('sos_inventory.sos_finance_user'))
(4, ref('sos_inventory.sos_finance_user')),
(4, ref('sos_inventory.sos_sales_reviewer'))
]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_sales_action_plan_ce_head_read_ce_user_rule" model="ir.rule">
<field name="name">Sales Action Plan: CE Head Reads CE User Records</field>
<field name="model_id" ref="model_sos_sales_action_plan"/>
<field name="domain_force" eval="[('create_uid.groups_id', 'in', [ref('sos_inventory.sos_ce_user')])]"/>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_head'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_sales_action_plan_all_records_rule" model="ir.rule">
<field name="name">Sos sales action plan: All Records - Read Access</field>
<!-- 2) Own / Team for all internal users -->
<record id="sos_sales_action_plan_restricted_rule" model="ir.rule">
<field name="name">Sos Action Plan: Own and Team (Internal Users)</field>
<field name="model_id" ref="model_sos_sales_action_plan"/>
<field name="domain_force">[
'|',
('create_uid', '=', user.id),
('sales_executive', '=', user.id)
]</field>
<field name="domain_force">
[
'|','|',
('create_uid', '=', user.id),
('sales_executive', '=', user.id),
('reporting_to', '=', user.id)
]
</field>
<!-- Apply to all internal users -->
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_sales_action_plan_ce_team_rule" model="ir.rule">
<field name="name">Sales Action Plan: CE Team View CE Records</field>
<field name="model_id" ref="model_sos_sales_action_plan"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_sales_action_plan_ce_head_rule" model="ir.rule">
<field name="name">Sales Action Plan: CE Head View CE Records</field>
<field name="model_id" ref="model_sos_sales_action_plan"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_head'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<!-- customers RECORD RULE-->
<record id="sos_customers_own_records_rule" model="ir.rule">
<field name="name">Sos customers: Own Records - Read Access</field>
@ -101,14 +125,24 @@
<field name="groups" eval="[
(4, ref('sos_inventory.sos_management_user')),
(4, ref('sos_inventory.sos_finance_user')),
(4, ref('sos_inventory.sos_ce_head'))
(4, ref('sos_inventory.sos_ce_head')),
(4, ref('sos_inventory.sos_sales_reviewer'))
]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_customers_ce_team_rule" model="ir.rule">
<field name="name">Customers: CE Team View CE customers Records</field>
<field name="model_id" ref="model_sos_customers"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_customers_all_records_rule" model="ir.rule">
<field name="name">Sos customers: All Records - Read Access</field>
<field name="model_id" ref="model_sos_customers"/>
@ -125,55 +159,39 @@
<field name="perm_unlink" eval="0"/>
</record>
<!-- case_diary RECORD RULE-->
<record id="sos_case_diary_own_records_rule" model="ir.rule">
<field name="name">Sos Case Diary: Own Records - Read Access</field>
<record id="sos_case_diary_ce_team_rule" model="ir.rule">
<field name="name">Case Diary: CE Team View CE Records</field>
<field name="model_id" ref="model_sos_case_diary"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[
(4, ref('sos_inventory.sos_management_user')),
(4, ref('sos_inventory.sos_finance_user'))
]"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_case_diary_ce_head_read_ce_user_rule" model="ir.rule">
<field name="name">Case Diary: CE Head Reads CE User Records</field>
<record id="sos_case_diary_ce_head_rule" model="ir.rule">
<field name="name">Case Diary: CE Head View CE Records</field>
<field name="model_id" ref="model_sos_case_diary"/>
<field name="domain_force" eval="[('create_uid.groups_id', 'in', [ref('sos_inventory.sos_ce_user')])]"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_head'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="0"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_case_diary_all_records_rule" model="ir.rule">
<field name="name">Sos Case Diary: All Records - Read Access</field>
<field name="model_id" ref="model_sos_case_diary"/>
<field name="domain_force">
[
'|', '|',
('create_uid', '=', user.id),
('reporting_to', '=', user.id),
('sales_person', '=', user.id)
]
</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<!-- Case Diary Report RECORD RULE-->
<record id="sos_case_diary_report_own_records_rule" model="ir.rule">
<field name="name">Sos Case Diary Report: Own Records - Read Access</field>
<field name="model_id" ref="model_sos_case_diary_report"/>
<field name="domain_force">[(1, '=', 1)]</field>
<!-- 1) Full visibility for management/finance/sales reviewer -->
<record id="sos_case_diary_full_access_rule" model="ir.rule">
<field name="name">Sos Case Diary: Full Access (Mgmt/Finance/Reviewer)</field>
<field name="model_id" ref="model_sos_case_diary"/>
<!-- Always true -->
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[
(4, ref('sos_inventory.sos_management_user'))
(4, ref('sos_inventory.sos_management_user')),
(4, ref('sos_inventory.sos_finance_user')),
(4, ref('sos_inventory.sos_sales_reviewer'))
]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
@ -181,20 +199,26 @@
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_case_diary_report_all_records_rule" model="ir.rule">
<field name="name">Sos Case Diary Report: All Records - Read Access</field>
<field name="model_id" ref="model_sos_case_diary_report"/>
<field name="domain_force">[
'|',
<!-- 2) Own / Team for all internal users -->
<record id="sos_case_diary_restricted_rule" model="ir.rule">
<field name="name">Sos Case Diary: Own and Team (Internal Users)</field>
<field name="model_id" ref="model_sos_case_diary"/>
<field name="domain_force">
[
'|','|',
('create_uid', '=', user.id),
('reporting_to', '=', user.id)
]</field>
('reporting_to', '=', user.id),
('sales_person', '=', user.id)
]
</field>
<!-- Apply to all internal users -->
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
</record>
<!-- Leads RECORD RULE-->
<record id="sos_sales_leads_own_records_rule" model="ir.rule">
<field name="name">Sos Leads: Own Records - Read Access</field>
@ -214,14 +238,57 @@
<field name="name">Sos Leads: All Records - Read Access</field>
<field name="model_id" ref="model_sos_sales_leads"/>
<field name="domain_force">[
'|',
'|','|',
('create_uid', '=', user.id),
('lead_generated_by', '=', user.id)
('lead_generated_by', '=', user.id),
('responsible_sales_persons', 'in', [user.id])
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_proposal_boq_all_records_rule" model="ir.rule">
<field name="name">Sos Proposal BOQ: All Records - Read Access</field>
<field name="model_id" ref="model_sos_proposal_boq"/>
<field name="domain_force">[
'|',
('create_uid', '=', user.id),
('sales_executive', '=', user.id)
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="1"/>
</record>
<record id="sos_proposal_builder_all_records_rule" model="ir.rule">
<field name="name">Sos Proposal Builder: All Records - Read Access</field>
<field name="model_id" ref="model_sos_proposal_builder"/>
<field name="domain_force">[
'|',
('create_uid', '=', user.id),
('sales_executive', '=', user.id)
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="1"/>
</record>
<record id="sos_proposal_customer_requirement_all_records_rule" model="ir.rule">
<field name="name">Sos Proposal Requirement: All Records - Read Access</field>
<field name="model_id" ref="model_sos_proposal_customer_requirement"/>
<field name="domain_force">[
('create_uid', '=', user.id)
]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="1"/>
</record>
</odoo>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<menuitem id="sos_sales_menu_root" name="Sales" groups="sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_ce_user"/>
<menuitem id="sos_sales_report" name="REPORTS" parent="sos_sales_menu_root" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head"/>
<menuitem id="sos_top_sales_report" name="MANAGEMENT REPORTS" parent="sos_sales_menu_root" groups="sos_inventory.sos_management_user"/>
<menuitem id="sos_proposal_system" name="PROPOSAL SYSTEM" parent="sos_sales_menu_root" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user,sos_inventory.sos_inside_sales_user"/>
<menuitem id="sos_sales_menu_root" name="Sales" 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"/>
<menuitem id="sos_sales_report" name="REPORTS" parent="sos_sales_menu_root" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
<menuitem id="sos_top_sales_report" name="MANAGEMENT REPORTS" parent="sos_sales_menu_root" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_sales_reviewer"/>
<menuitem id="sos_proposal_system" name="PROPOSAL SYSTEM" parent="sos_sales_menu_root" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user,sos_inventory.sos_inside_sales_user,sos_inventory.sos_sales_sapl_user"/>
</odoo>

View File

@ -62,6 +62,8 @@
<group>
<group>
<field name="status"/>
<field name="sales_type"/>
<field name="country" invisible="sales_type != 'International'"/>
<field name="quote_no"/>
<field name="customer_name"/>
<field name="end_customer_name"/>
@ -133,7 +135,7 @@
<div class="col-4">
<h5>Witness For Commercial Order</h5>
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;"> <tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Department In-Charge</b>
<td style="padding: 8px;" class="column"><b>Top Management Sign</b>
<br></br><br></br>
<button string="Approve" invisible="dept_incharge_approval_image" class="btn-primary custom_btn" type="object" name="action_deptincharge_esign_btn"></button>
</td>
@ -281,7 +283,7 @@
<!-- Menu -->
<menuitem id="menu_sos_case_diary" name="CASE DIARY" parent="sos_sales_menu_root" action="action_sos_case_diary" sequence="6" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user"/>
<menuitem id="menu_sos_case_diary" name="CASE DIARY" parent="sos_sales_menu_root" action="action_sos_case_diary" sequence="6" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
<menuitem id="menu_sos_spenco_report"
name=" SPENCO Report"
parent="sos_sales_report"
@ -307,17 +309,17 @@
name="Action Plan Summary Report"
parent="sos_top_sales_report"
action="action_plan_summary_wizard"
sequence="8"/>
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"/>
<menuitem id="menu_billing_target_report"
name="Billing Target Report"
parent="sos_top_sales_report"
action="action_billing_target_report"
sequence="9"/>
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"/>
<menuitem id="menu_business_performance_report"
name="Business Performance Report"
parent="sos_top_sales_report"
action="action_open_business_performance_wizard"
sequence="10"/>
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"/>
<menuitem id="menu_sos_all_suspects" name="Suspects Report" parent="sos_sales_report" action="action_sos_suspects_report_wizard"/>
<menuitem id="sos_case_diary_report" name="Case Diary Report"
parent="sos_sales_report"

View File

@ -81,6 +81,7 @@
</searchpanel>
<!-- Add fields to search on -->
<field name="responsible" string="Sales Executive"/>
<field name="customer_name" string="Customer Name"/>
<field name="vertical_domain" string="Domain/Vertical"/>
@ -89,5 +90,5 @@
</field>
</record>
<!-- Menu -->
<menuitem id="menu_sos_customers" name="CUSTOMERS" parent="sos_sales_menu_root" action="action_sos_customers" sequence="2" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user"/>
<menuitem id="menu_sos_customers" name="CUSTOMERS" parent="sos_sales_menu_root" action="action_sos_customers" sequence="2" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
</odoo>

View File

@ -19,6 +19,7 @@
<group>
<field name="location" readonly="acc_approved_by_name"/>
<field name="products" readonly="acc_approved_by_name"/>
<field name="slave_type" invisible="products != 'BHMS 1.2V'"/>
</group>
</group>
<br></br>
@ -1655,6 +1656,35 @@
</div>
</div>
</page>
<page string="Installation &amp; Commissioning" invisible="proposal_id == False">
<group><field name="engineers_nos"/></group>
<group><field name="no_of_days"/></group>
<group><field name="iandc_travel_cost"/></group>
<br></br>
<table class="table_custom" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
<thead><td></td><td><b>No of Persons</b></td><td><b>Cost per Day</b></td></thead>
<tbody>
<tr><td><b>Man Month( 2 to 3 Yrs)</b></td><td><field name="man_month_1to2_yrs_persons" readonly="acc_approved_by_name"/></td><td><field name="man_month_1to2_yrs_cost" readonly="acc_approved_by_name"/></td></tr>
<tr><td><b>Man Month( 3 to 5 Yrs)</b></td><td><field name="man_month_2to3_yrs_persons" readonly="acc_approved_by_name"/></td><td><field name="man_month_2to3_yrs_cost" readonly="acc_approved_by_name"/></td></tr>
<tr><td><b>Man Month(Manager)</b></td><td><field name="man_month_manager_persons" readonly="acc_approved_by_name"/></td><td><field name="man_month_manager_cost" readonly="acc_approved_by_name"/></td></tr>
<tr><td><b>Total Cost(Man Month+Travel)</b></td><td colspan="2"><field name="iandc_costing" readonly="acc_approved_by_name"/></td></tr>
</tbody>
</table>
</page>
<page string="Miscellaneous">
<field name="extra_cost_line_ids" readonly="acc_approved_by_name">
<tree editable="bottom">
<field name="field_name"/>
<field name="cost"/>
</tree>
</field>
</page>
<page string="Summary" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
<h3 style="text-transform: uppercase;
text-decoration: underline;">Finished Goods</h3>
@ -1676,27 +1706,24 @@
text-decoration: underline;">Miscellaneous</h3>
<field name="merged_miscellaneous_html" nolabel="1"/>
</page>
<page string="Installation &amp; Commissioning" invisible="proposal_id == False">
<div class="oe_subtotal_footer" style="float: right;
padding: 20px;
border: solid 1px #ccc;
font-weight: bold;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;">
<group><field name="engineers_nos"/></group>
<group><field name="no_of_days"/></group>
<br></br>
<table class="table_custom" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
<thead><td></td><td><b>No of Persons</b></td><td><b>Cost per Day</b></td></thead>
<tbody>
<tr><td><b>Man Month( 2 to 3 Yrs)</b></td><td><field name="man_month_1to2_yrs_persons"/></td><td><field name="man_month_1to2_yrs_cost"/></td></tr>
<tr><td><b>Man Month( 3 to 5 Yrs)</b></td><td><field name="man_month_2to3_yrs_persons"/></td><td><field name="man_month_2to3_yrs_cost"/></td></tr>
<tr><td><b>Man Month(Manager)</b></td><td><field name="man_month_manager_persons"/></td><td><field name="man_month_manager_cost"/></td></tr>
<tr><td><b>Total</b></td><td colspan="2"><field name="iandc_costing"/></td></tr>
</tbody>
</table>
<!-- Gross Value Field -->
<div style="margin-bottom: 5px;">
<label for="total_cost" style="font-weight: bold; margin-right: 10px;">Product Cost</label>
<field name="total_cost" widget="monetary" options="{'currency_field': 'currency_id'}" class="oe_inline"/>
</div></div>
</page>
<page string="Costing By Accounts" invisible="proposal_id == False" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
<div class="row">
<br></br>
<div class="col-md-3" style="border-right: solid 1px #ccc;">
<div class="col-md-4" style="border-right: solid 1px #ccc;">
<table class="table">
<h3 style="text-transform: uppercase;
text-decoration: underline;">Product Cost</h3>
@ -1721,20 +1748,8 @@
</table>
</div>
<div class="col-md-3" style="border-right: solid 1px #ccc;">
<table class="table">
<h3 style="text-transform: uppercase;
text-decoration: underline;">Miscellaneous Cost</h3>
<field name="extra_cost_line_ids" readonly="acc_approved_by_name">
<tree editable="bottom">
<field name="field_name"/>
<field name="cost"/>
</tree>
</field>
</table>
</div>
<div class="col-md-6">
<div class="col-md-8">
<table class="table">
<h3 style="text-transform: uppercase;
@ -1744,15 +1759,28 @@
<tr><td class="column">Opreational Cost</td><td><field name="margin" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">Packing &amp; Forwarding</td><td><field name="packing_and_forwarding" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">Installation &amp; Commissioning</td><td><field name="iandc_costing" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">I &amp; C Cost</td><td><field name="iandc_costing" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">Warranty (%)</td><td><field name="warranty_percentage" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">Warranty Cost</td><td><field name="warranty_cost" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">Additional Warranty</td><td><field name="additional_warranty" readonly="acc_approved_by_name"/></td></tr>
<tr style="background-color: aliceblue;"><td class="column">MSP Per Battery</td><td><field style="font-weight: bold;font-size: 16px;" name="final_cost_per_battery" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">MSP Per Battery</td><td><field style="font-weight: bold;font-size: 16px;" name="final_cost_per_battery" readonly="acc_approved_by_name"/></td></tr>
<tr><td class="column">Proposal Value</td><td><field style="font-weight: bold;font-size: 18px;" name="final_cost" readonly="acc_approved_by_name"/></td></tr>
</table>
<table class="table_custom">
<thead>
<tr>
<th colspan="2" style="background-color: #504e4e;
color: #fff;
font-weight: bold;">Accounts Costing</th>
</tr>
</thead>
<tr style="background-color: #f2f9ff;"><td class="column">MSP(Per Battery)</td><td><field style="font-weight: bold;font-size: 18px;" name="final_cost_by_acc" readonly="acc_approved_by_name"/></td></tr>
<tr style="background-color: #f2f9ff;"><td class="column">Final MSP</td><td><field style="font-weight: bold;font-size: 18px;" name="final_total" readonly="acc_approved_by_name"/></td></tr>
<tr style="background-color: aliceblue;"><td class="column">Final MSP</td><td><field style="font-weight: bold;font-size: 18px;" name="final_cost" readonly="acc_approved_by_name"/></td></tr>
</table>
</div>
@ -1788,7 +1816,7 @@
<tr><td colspan="2" class="table_custom_header">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">Finance Team</span></td></tr>
<tr>
<td style="padding: 8px;" class="column"><b>Approved By</b>
<button string="Approve" invisible="acc_approved_by_image" class="btn-primary custom_btn" type="object" name="action_acc_esign_btn"></button>
<button string="Approve" confirm="Approve this costing?" invisible="acc_approved_by_image" class="btn-primary custom_btn" type="object" name="action_acc_esign_btn"></button>
</td>
<td><field name="acc_approved_by_image" widget="image"/></td>
</tr>

View File

@ -28,6 +28,8 @@
<field invisible="products != 'BHMS 1.2V'" name="master_type"/>
<field invisible="products != 'BHMS 1.2V'" name="slave_type"/>
<field invisible="products != 'BHMS 1.2V'" name="ntc"/>
<field invisible="products != 'BHMS 1.2V'" name="spare_count"/>
</group>
</group>
@ -37,7 +39,7 @@
<group>
<field name="type_of_source"/>
<field name="number_of_ups" string="Number of UPS"/>
<field name="no_of_electrical_panel"/>
<field name="no_of_electrical_panel" invisible="products != 'BHMS 12V'"/>
<field name="ups_line_ids">
<tree editable="bottom">
<field name="ups_index" column_invisible="1"/>

View File

@ -15,10 +15,11 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
<group>
<group><field name="financial_year" style="font-size: 20px; pointer-events: none; color: #793595;"/>
</group>
<group><field name="sales_person" /></group>
<group><field name="category" /></group>
<group><field name="overall_target" /></group>
<table style="margin-left: 15px;"><tr>
<td><group><field name="opening_balance" groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user"/>
<group><field name="sales_person" invisible="category == 'export'"/></group>
<table groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user" style="margin-left: 15px;"><tr>
<td><group><field name="opening_balance"/>
</group></td>
<td><button name="action_view_brief_lines_acc"
type="object"
@ -27,10 +28,15 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
context="{'month': 0}"
style="padding: 0; border: none; background: none; font-size: 18px; box-shadow: 4px 5px 5px #e3e2e2;"/>
</td>
</tr></table>
</tr>
</table>
</group>
<br></br>
<button groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user" name="action_yet_to_bill"
type="object"
string="📝 Yet To Bill"
class="btn-primary"
style="padding: 0; border: none; background: none; font-size: 18px; box-shadow: 4px 5px 5px #e3e2e2;"/>
<br></br><br></br>
<table class="table_custom">
<thead>
<tr style="background-color: lavender;">
@ -227,9 +233,9 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
<td><field name="achievement_percentage_total" /></td>
<td><field name="ytd_sales_percentage" /></td>
</tr>
<tr style="background-color: lavender;"><td class="column" colspan="15" style="text-align: center;
<tr groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user" style="background-color: lavender;"><td class="column" colspan="15" style="text-align: center;
color: #000;text-transform: capitalize;">To be filled by Accounts team</td></tr>
<tr>
<tr groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user">
<field name="can_edit_billed_target" invisible="1"/>
<td class="column" style="background-color: lavender;">Billed</td>
<td>
@ -368,7 +374,7 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
<td><field readonly="not can_edit_billed_target" name="ytd_billed" /></td>
</tr>
<tr>
<tr groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user">
<td class="column" style="background-color: lavender;">Collected</td>
<td>
<div style="display: flex; align-items: center;">
@ -505,7 +511,7 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
<td><field readonly="not can_edit_billed_target" name="collected_target_total" /></td>
<td><field readonly="not can_edit_billed_target" name="ytd_collected" /></td>
</tr>
<tr>
<tr groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user">
<field name="can_edit_billed_target" invisible="1"/>
<td class="column" style="background-color: lavender;">Yet to Bill</td>
<td><field readonly="not can_edit_billed_target" name="yet_to_billed_target_april" /></td>
@ -539,6 +545,7 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
<field name="arch" type="xml">
<tree string="Sales Achievement Reports">
<field name="financial_year" />
<field name="category"/>
<field name="sales_person" />
<field name="overall_target" />
</tree>

View File

@ -63,16 +63,21 @@
<group>
<field name="update_done" invisible="1"/>
<field name="status"/>
<field name="sales_type"/>
<field name="country" invisible="sales_type != 'International'"/>
<field name="sales_executive" readonly="state == 'confirmed'"/>
<field name="date" readonly="state == 'confirmed'"/>
<field name="action_type" readonly="state == 'confirmed'"/>
<field name="interested_in"/>
<field name="project_name" invisible="interested_in == 'Product'"/>
<field name="product" required="interested_in == 'Product'" invisible="interested_in == 'Project'" readonly="state == 'confirmed'"/>
<field name="customer_name" readonly="state == 'confirmed'"/>
<field name="quote_no_selector" readonly="state == 'confirmed'" options="{'no_create': True}"/>
<field name="quote_no" readonly="state == 'confirmed'" placeholder="Or type new Quote/W.O No"/>
<field name="end_customer_name" readonly="state == 'confirmed'" placeholder="Or type new End Customer Name"/>
</group>
<group>
<field name="product" readonly="state == 'confirmed'"/>
<field name="date" readonly="state == 'confirmed'"/>
<field name="action_type" readonly="state == 'confirmed'"/>
<field name="ce_product_type" groups="sos_inventory.sos_ce_user,sos_inventory.sos_ce_head,sos_inventory.sos_finance_user,sos_inventory.sos_management_user"/>
<field name="location" readonly="state == 'confirmed'"/>
@ -130,5 +135,5 @@
</record>
<!-- Menu -->
<menuitem id="menu_sos_sales_action_plan" name="ACTION PLAN" sequence="6" parent="sos_sales_menu_root" action="action_sos_sales_action_plan" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user"/>
<menuitem id="menu_sos_sales_action_plan" name="ACTION PLAN" sequence="6" parent="sos_sales_menu_root" action="action_sos_sales_action_plan" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_ce_user,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
</odoo>

View File

@ -54,7 +54,7 @@
<group>
<group>
<field name="sales_type"/>
<field name="lead_generated_by"/>
<field name="lead_generated_by" readonly="source == 'inside_sales'"/>
<field name="company_name"/>
<field name="convert_to_customer_btn_display" invisible="1"/>
<field name="transferred_on" invisible="1"/>
@ -106,5 +106,5 @@
</record>
<!-- Menu -->
<menuitem id="menu_sos_sales_leads" name="LEADS" parent="sos_sales_menu_root" action="action_sos_sales_leads" sequence="1" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head"/>
<menuitem id="menu_sos_sales_leads" name="LEADS" parent="sos_sales_menu_root" action="action_sos_sales_leads" sequence="1" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
</odoo>

View File

@ -257,5 +257,5 @@ name="action_report_sales_plan_btn"><i class="fa fa-print"></i> Print Report</bu
</record>
<!-- Menu -->
<menuitem id="menu_sos_sales_plan_target" name="SALES TARGET" sequence="7" parent="sos_sales_menu_root" action="action_sos_sales_plan_target" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head"/>
<menuitem id="menu_sos_sales_plan_target" name="SALES TARGET" sequence="7" parent="sos_sales_menu_root" action="action_sos_sales_plan_target" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
</odoo>

View File

@ -39,5 +39,5 @@
</record>
<!-- Menu -->
<menuitem id="menu_sos_vertical_domain" name="CONFIGURATION" parent="sos_sales_menu_root" action="action_sos_vertical_domain" sequence="4" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head"/>
<menuitem id="menu_sos_vertical_domain" name="CONFIGURATION" parent="sos_sales_menu_root" action="action_sos_vertical_domain" sequence="4" groups="sos_inventory.sos_finance_user,sos_inventory.sos_sales_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_head,sos_inventory.sos_sales_reviewer,sos_inventory.sos_sales_sapl_user"/>
</odoo>

View File

@ -7,4 +7,5 @@ from .import action_plan_wizard
from .import sales_action_plan_report_wizard
from .import week_summary_wizard
from .import action_plan_summary_wizard
from .import sos_business_performance_wizard
from .import sos_business_performance_wizard
from .import yet_to_bill_wizard

View File

@ -4,6 +4,10 @@ class Business_performanceWizard(models.TransientModel):
_name = 'sos_business_performance_wizard'
_description = 'Business Performance Wizard'
category = fields.Selection([
('sales', 'Sales'),
('export', 'Export')
], string="Category", required=True, default='sales')
sales_person_id = fields.Many2one(
'res.users',
string='Sales Executive',
@ -13,6 +17,7 @@ class Business_performanceWizard(models.TransientModel):
def print_report(self):
return self.env.ref('sos_sales.action_business_performance_report_custom').report_action(self, data={
'category':self.category,
'sales_person_id': self.sales_person_id.id if self.sales_person_id else False
})

View File

@ -14,7 +14,8 @@
<field name="arch" type="xml">
<form string="Business Performance Report">
<group>
<field name="sales_person_id"/>
<field name="category"/>
<field name="sales_person_id" invisible="category == 'export'"/>
</group>
<footer>
<button name="print_report" type="object" string="Generate Report" class="btn-primary"/>

View File

@ -8,16 +8,15 @@
<sheet>
<group>
<field name="ref_id" invisible="1"/>
<field name="category"/>
<field name="financial_year"/>
<field name="sales_person"/>
<field name="sales_person" invisible="category == 'export'"/>
<field name="customer_name"/>
<field name="invoice_no"/>
<field name="action_date"/>
<field name="po_no"/>
<field name="proposal_value"/>
<field name="billed_date"/>
<field name="billed_amount"/>
<field name="invoice_no"/>
<field name="currency_id" invisible="1"/>
</group>
</sheet>
@ -49,8 +48,7 @@
<field name="po_no"/>
<field name="invoice_no"/>
<field name="proposal_value"/>
<field name="billed_date"/>
<field name="billed_amount"/>
<button name="delete_achievement_line" class="btn btn-primary" string="Delete"
type="object" icon="fa-times"/>
<button name="open_line_form" class="btn btn-primary" string="Edit"
@ -91,7 +89,8 @@
<field name="customer_name"/>
<field name="value"/>
<field name="products"/>
<field name="po_no"/>
<field name="po_id" create="false"/>
<field name="invoice_no"/>
<!-- <field name="quantity"/> -->
</group>
</sheet>
@ -112,6 +111,8 @@
<field name="date_of_action"/>
<field name="customer_name"/>
<field name="value"/>
<field name="products"/>
<field name="po_id" create="false"/>
<field name="invoice_no"/>
</group>
@ -139,6 +140,10 @@
<field name="value"/>
<field name="products"/>
<field name="po_no"/>
<button name="delete_form_line" class="btn btn-primary" string="Delete"
type="object" icon="fa-times"/>
<button name="write_line_form" class="btn btn-primary" string="Edit"
type="object" icon="fa-pencil"/>
<!-- <field name="quantity"/> -->
</tree>
</field>
@ -170,6 +175,8 @@
<field name="invoice_no"/>
<button name="delete_form_line" class="btn btn-primary" string="Delete"
type="object" icon="fa-times"/>
<button name="write_line_form" class="btn btn-primary" string="Edit"
type="object" icon="fa-pencil"/>
</tree>
</field>
<footer>

View File

@ -1,4 +1,5 @@
from odoo import models, fields, api
from datetime import date
class SpencoReportSalesPersonWizard(models.TransientModel):
_name = 'spenco_salespersonwise_wizard'
@ -11,9 +12,9 @@ class SpencoReportSalesPersonWizard(models.TransientModel):
domain=lambda self: [('groups_id', 'in', self.env.ref('sos_inventory.sos_sales_user').ids + self.env.ref('sos_inventory.sos_ce_head').ids)]
)
from_date = fields.Date(
string="From Date",
default=fields.Date.today # Default to today's date
)
string="From Date",
default=lambda self: date(date.today().year if date.today().month >= 4 else date.today().year - 1, 4, 1)
)
to_date = fields.Date(
string="To Date",
default=fields.Date.today # Default to today's date

View File

@ -0,0 +1,82 @@
# models/yet_to_bill_wizard.py
from odoo import models, fields, api
from collections import defaultdict
def _norm(s):
return (s or '').strip().lower()
class SosYetToBillWizard(models.TransientModel):
_name = 'yet_to_bill_wizard'
_description = 'Yet To Bill'
line_ids = fields.One2many('yet_to_bill_line', 'wizard_id', string="Lines")
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
brief_ref_id = self.env.context.get('brief_ref_id')
brief_domain = [('ref_id', '=', brief_ref_id)] if brief_ref_id else []
Brief = self.env['sos_sales_achievement_report_brief']
Billing = self.env['sos_billing_collection']
line_cmds = []
# Search all brief records
brief_records = Brief.search(brief_domain)
for br in brief_records:
proposal = br.proposal_value or 0.0
customer = br.customer_name
po_no = br.po_no
if not customer or not po_no:
continue
# Get billed amount for same customer & po_no
billed_lines = Billing.search([
('customer_name', '=', customer.id),
('po_no', '=', po_no),
('action_status', '=', 'Billed')
])
billed_amount = sum(billed_lines.mapped('value'))
remaining = proposal - billed_amount
if remaining > 0:
line_cmds.append((0, 0, {
'customer_id': customer.id,
'customer_name_display': customer.display_name,
'po_no': po_no,
'proposal_value': proposal,
'billed_value': billed_amount,
'remaining_value': remaining,
}))
res['line_ids'] = line_cmds
return res
def action_open(self):
return {
'type': 'ir.actions.act_window',
'name': 'Yet To Bill',
'res_model': 'yet_to_bill_wizard', # FIXED
'view_mode': 'form',
'res_id': self.id,
'target': 'new',
}
class SosYetToBillLine(models.TransientModel):
_name = 'yet_to_bill_line'
_description = 'Yet To Bill Line'
_order = 'remaining_value desc'
wizard_id = fields.Many2one('yet_to_bill_wizard', required=True, ondelete='cascade')
customer_id = fields.Many2one('sos_customers', string="Customer", readonly=True)
customer_name_display = fields.Char(string="Customer Name", readonly=True)
po_no = fields.Char(string="PO No", readonly=True)
proposal_value = fields.Float(string="Proposal Value", readonly=True)
billed_value = fields.Float(string="Billed Value", readonly=True)
remaining_value = fields.Float(string="Yet to Bill", readonly=True)

View File

@ -0,0 +1,31 @@
<!-- views/yet_to_bill_wizard_views.xml -->
<odoo>
<record id="view_sos_yet_to_bill_wizard_form" model="ir.ui.view">
<field name="name">yet_to_bill_wizard.form</field>
<field name="model">yet_to_bill_wizard</field>
<field name="arch" type="xml">
<form string="Yet To Bill">
<field name="line_ids">
<tree editable="bottom">
<field name="customer_id"/>
<field name="po_no"/>
<field name="proposal_value"/>
<field name="billed_value"/>
<field name="remaining_value"/>
</tree>
</field>
<footer>
<button string="Close" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_sos_yet_to_bill_wizard" model="ir.actions.act_window">
<field name="name">Yet To Bill</field>
<field name="res_model">yet_to_bill_wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<!-- The wizard populates itself in default_get -->
</record>
</odoo>