leads of sales and inside sales matched

This commit is contained in:
Deena 2025-08-01 22:14:43 +05:30
parent 590d716eec
commit 0565053d7a
43 changed files with 646 additions and 199 deletions

View File

@ -15,6 +15,7 @@
'data': [
'security/ir.model.access.csv',
'security/record_rules.xml',
'data/lead_source_data.xml',
'views/menu.xml',
'views/sos_inside_sales_leads_view.xml',
'views/sos_target_tracker_view.xml',

View File

@ -0,0 +1,16 @@
<odoo>
<data noupdate="1">
<record id="lead_source_website" model="lead_source">
<field name="name">Website</field>
</record>
<record id="lead_source_expo" model="lead_source">
<field name="name">Expo</field>
</record>
<record id="lead_source_cold_calls" model="lead_source">
<field name="name">Cold Calls</field>
</record>
<record id="lead_source_linkedin" model="lead_source">
<field name="name">Linkedin</field>
</record>
</data>
</odoo>

View File

@ -20,31 +20,44 @@ class sos_inside_sales_leads(models.Model):
moved_on = fields.Date(string="Moved On")
location = fields.Char(string="Location")
website_url = fields.Char(string="Website URL")
vertical_domain = fields.Many2one('sos_vertical_domain',string="Domain / Industry", ondelete="cascade")
vertical_domain = fields.Many2many('sos_vertical_domain',string="Domain / Industry", ondelete="cascade")
line_ids_contacts = fields.One2many('sos_leads_contact_lines', 'ref_id', string="Contact Details",copy=True)
remarks=fields.Text(string="Remarks")
linkedin_profile = fields.Char(string="Linkedin profile")
products = fields.Selection(
# products = fields.Selection(
# [
# ('BHMS 1.2V', 'BHMS 1.2V'),
# ('BHMS 2V', 'BHMS 2V'),
# ('BHMS 12V', 'BHMS 12V'),
# ('BMS-HV', 'BMS-HV'),
# ('BMS-LV 100A', 'BMS-LV 100A'),
# ('BMS-LV 40A', 'BMS-LV 40A'),
# ('SBMS 55A', 'SBMS 55A'),
# ('MC 250W', 'MC 250W'),
# ('HeartTarang', 'HeartTarang')
# ],
# string="Product Interested In")
products_interested = fields.Many2many('sos_products',string="Product Interested In")
service = fields.Selection(
[
('BHMS 1.2V', 'BHMS 1.2V'),
('BHMS 2V', 'BHMS 2V'),
('BHMS 12V', 'BHMS 12V'),
('BMS-HV', 'BMS-HV'),
('BMS-LV 100A', 'BMS-LV 100A'),
('BMS-LV 40A', 'BMS-LV 40A'),
('SBMS 55A', 'SBMS 55A'),
('MC 250W', 'MC 250W'),
('HeartTarang', 'HeartTarang')
('Product', 'Products'),
('Project', 'Projects')
],
string="Product Interested In")
source = fields.Selection(
string="Service Type",default="Product")
project_name= fields.Many2one('sos_projects',string="Project Name")
status = fields.Selection(
[
('website', 'Website'),
('expo', 'Expo'),
('cold_calls', 'Cold Calls'),
('linkedin', 'Linkedin')
('Open', 'Open'),
('Close', 'Close'),
('New', 'New'),
('In-Progress', 'In-Progress'),
('Qualified', 'Qualified'),
('Unqualified', 'Unqualified')
],
string="Source")
string="Status",default="New")
new_source = fields.Many2one('lead_source', string="Source")
meeting_scheduled = fields.Selection(
[
('Online', 'Online'),
@ -52,12 +65,24 @@ class sos_inside_sales_leads(models.Model):
],
string="Meeting Schedule")
expo_name = fields.Char(string="Expo Name")
move_to_sales = fields.Many2many('res.users',string="Move Lead to")
cc_mail = fields.Many2one('res.users',string="CC to")
move_to_sales = fields.Many2many('res.users',string="Move Lead to", domain=lambda self: [('groups_id', 'in', self.env.ref('sos_inventory.sos_sales_user').ids + self.env.ref('sos_inventory.sos_ce_head').ids)])
cc_mail = fields.Many2one('res.users',string="CC to", domain=lambda self: [('groups_id', 'in', self.env.ref('sos_inventory.sos_sales_user').ids + self.env.ref('sos_inventory.sos_ce_head').ids)])
move_div_display = fields.Boolean(default=False,string="Moved to Sales")
line_ids = fields.One2many('sos_inside_sales_activities_line', 'ref_id', string="Action")
week = fields.Char(string="Week")
reporting_to = fields.Many2one('res.users', string='Reporting To')
sales_type = fields.Selection(
[
('Domestic', 'Domestic'),
('International', 'International')
],
string="Sales Type",default="Domestic")
country = fields.Many2one(
'res.country',
string='Country',
default=lambda self: self.env['res.country'].search([('code', '=', 'IN')], limit=1)
)
@api.model
def create(self, vals):
create_uid = vals.get('create_uid', self.env.uid)
@ -122,14 +147,29 @@ class sos_inside_sales_leads(models.Model):
'transferred_on': fields.Date.today(),
'location':self.location,
'website_url':self.website_url,
'vertical_domain':self.vertical_domain.id,
'vertical_domain': [(6, 0, self.vertical_domain.ids)],
'linkedin_profile':self.linkedin_profile,
'products':self.products,
'products_interested':[(6, 0,self.products_interested.ids)],
'source':'inside_sales',
'expo_name':self.expo_name,
'lead_generated_by':self.env.user.id
'lead_generated_by':self.env.user.id,
'sales_type':self.sales_type,
'interested_in':self.service,
'project_name':self.project_name.id
})
if new_record:
if self.line_ids_contacts:
for contact in self.line_ids_contacts:
self.env['sos_leads_customer_contact_lines'].create({
'ref_id': new_record.id,
'name': contact.name,
'dept': contact.dept,
'email': contact.email,
'mobile_number': contact.mobile_number,
'set_as_primary': contact.set_as_primary,
'linkedin_profile': contact.linkedin_profile,
'remarks': contact.remarks
})
self.move_div_display = True
return {
'type': 'ir.actions.client',
@ -151,6 +191,8 @@ class SOS_Leads_Line(models.Model):
email = fields.Char(string="Email Id")
mobile_number = fields.Char(string="Contact Number")
set_as_primary = fields.Boolean(string="Set Primary")
linkedin_profile = fields.Char(string="LinkedIn Profile")
remarks = fields.Text(string="Remarks")
@api.model
def create(self, vals):
record = super(SOS_Leads_Line, self).create(vals)
@ -176,4 +218,11 @@ class sos_inside_sales_activities_lines(models.Model):
ref_id = fields.Many2one('sos_inside_sales_leads', ondelete="cascade")
action_date = fields.Date(string="Action Date")
remarks = fields.Text(string="Remarks")
next_action_on = fields.Date(string="Next Action On")
remarks = fields.Text(string="Remarks")
class LeadSource(models.Model):
_name = 'lead_source'
_description = 'Lead Source Master'
name = fields.Char(string="Source Name", required=True)

View File

@ -4,3 +4,5 @@ access_sos_inside_sales_activities_line,sos_inside_sales_activities_line access,
access_sos_target_tracker,sos_target_tracker access,model_sos_target_tracker,base.group_user,1,1,1,1
access_sos_leads_contact_lines,sos_leads_contact_lines access,model_sos_leads_contact_lines,base.group_user,1,1,1,1
access_action_leads_report_wizard,action_leads_report_wizard access,model_action_leads_report_wizard,base.group_user,1,1,1,1
access_lead_source,lead_source access,model_lead_source,base.group_user,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
4 access_sos_target_tracker sos_target_tracker access model_sos_target_tracker base.group_user 1 1 1 1
5 access_sos_leads_contact_lines sos_leads_contact_lines access model_sos_leads_contact_lines base.group_user 1 1 1 1
6 access_action_leads_report_wizard action_leads_report_wizard access model_action_leads_report_wizard base.group_user 1 1 1 1
7 access_lead_source lead_source access model_lead_source base.group_user 1 1 1 1
8

View File

@ -7,10 +7,12 @@
<field name="arch" type="xml">
<tree>
<field name="company_name"/>
<field name="status"/>
<field name="location"/>
<field name="vertical_domain"/>
<field name="products_interested" string="Products" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="project_name"/>
<field name="lead_generated_by"/>
<field name="entry_date" string="Lead Generated On"/>
<!-- <field name="entry_date" string="Lead Generated On"/> -->
<field name="move_div_display" widget="boolean_toggle" readonly="1"/>
</tree>
</field>
@ -20,17 +22,21 @@
<field name="model">sos_inside_sales_leads</field>
<field name="arch" type="xml">
<search>
<searchpanel>
<field name="products" string="Products Interested In" icon="fa-list-ul" enable_counters="1"/>
<field name="vertical_domain" string="Vertical/Domain" icon="fa-pie-chart" enable_counters="1"/>
<searchpanel>
</searchpanel>
<field name="sales_type" string="Type" icon="fa-list-ul" enable_counters="1"/>
</searchpanel>
<!-- Add fields to search on -->
<field name="company_name" string="Company Name"/>
<field name="vertical_domain" string="Domain/Vertical"/>
<field name="lead_generated_by" string="Lead Generated By"/>
<field name="products_interested"/>
<filter name="product_filter"
string="Product"
domain="[('products_interested', 'in', products_interested)]"/>
</search>
@ -50,18 +56,24 @@
<group>
<group>
<field name="move_div_display" invisible="1"/>
<field name="sales_type"/>
<field name="company_name"/>
<field name="entry_date"/>
<field name="location"/>
<field name="vertical_domain"/>
<field name="country" invisible="sales_type != 'International'"/>
<field name="location" string="City/State"/>
<field name="vertical_domain" widget="many2many_tags"/>
<field name="status"/>
</group>
<group>
<field name="website_url"/>
<field name="linkedin_profile"/>
<field name="products"/>
<field name="source"/>
<field name="expo_name" invisible="source != 'expo'"/>
<field name="service"/>
<field name="products_interested"
options="{'no_create': True, 'no_edit': True, 'no_create_edit': True}"
widget="many2many_tags" invisible="service != 'Product'"/>
<field name="project_name" invisible="service != 'Project'"/>
<field name="new_source"/>
<field name="expo_name" invisible="new_source != 2"/>
<field name="meeting_scheduled"/>
<field name="remarks"/>
</group>
@ -77,6 +89,8 @@
<field name="dept"/>
<field name="email"/>
<field name="mobile_number"/>
<field name="linkedin_profile"/>
<field name="remarks"/>
</tree>
</field>
@ -87,6 +101,8 @@
<tree editable="bottom">
<field name="action_date"/>
<field name="remarks"/>
<field name="next_action_on"/>
</tree>

View File

@ -63,6 +63,9 @@ class FIR_Only(models.Model):
cloud_data = fields.Binary("Cloud Data", required=False, attachment=True)
cloud_data_filename = fields.Char("Cloud Data Filename")
firmware_data = fields.Binary("Firmware Data", required=False, attachment=True)
firmware_data_filename = fields.Char("Firmware Data Filename")
@api.onchange('batch_size')
def _onchange_batch_size(self):
if self._origin and self.batch_size is not False:
@ -75,11 +78,11 @@ class FIR_Only(models.Model):
]
self.testing_parameter_line_ids = line_values
@api.constrains('test_log_filename', 'sd_card_data_filename', 'cloud_data_filename')
@api.constrains('test_log_filename', 'sd_card_data_filename', 'cloud_data_filename', 'firmware_data_filename')
def _check_file_extension(self):
allowed_extensions = ['xlsx', 'xls', 'csv', 'zip', 'pdf']
for record in self:
for field_name in ['test_log_filename', 'sd_card_data_filename', 'cloud_data_filename']:
for field_name in ['test_log_filename', 'sd_card_data_filename', 'cloud_data_filename','firmware_data_filename']:
file_name = getattr(record, field_name)
if file_name:
ext = file_name.split('.')[-1].lower()

View File

@ -13,3 +13,23 @@ class sos_inventory_customers(models.Model):
customer_name = fields.Char(string="Customer Name")
customer_location = fields.Char(string="Location")
@api.model
def create(self, vals):
new_name = (vals.get('customer_name') or '').lower()
if new_name and len(new_name) >= 5:
existing_customers = self.search([])
for customer in existing_customers:
existing_name = (customer.customer_name or '').lower()
# Check all substrings of length 5 or more
for i in range(len(new_name) - 4):
substring = new_name[i:i+5]
if substring in existing_name:
raise UserError(
f"A customer with a similar name already exists: '{customer.customer_name}' "
f"(matched substring: '{substring}')"
)
return super(sos_inventory_customers, self).create(vals)

View File

@ -154,7 +154,7 @@ class SOS_IR(models.Model):
self.line_ids_material = lines
except Exception as e:
_logger.error("Error processing po_no: %s", str(e))
print("Error processing po_no: %s", str(e))
@api.onchange('wo_no')
def _onchange_wo_no(self):
if self.wo_no:
@ -170,7 +170,7 @@ class SOS_IR(models.Model):
self.line_ids_sfg = lines
except Exception as e:
_logger.error("Error processing po_no: %s", str(e))
print("Error processing po_no: %s", str(e))
@api.onchange('wo_no_inhouse')
def _onchange_wo_no_inhouse(self):
if self.wo_no_inhouse:
@ -186,7 +186,7 @@ class SOS_IR(models.Model):
self.line_ids_sfg = lines
except Exception as e:
_logger.error("Error processing po_no: %s", str(e))
print("Error processing po_no: %s", str(e))
def action_report_ir_btn(self):
@ -409,6 +409,7 @@ class SOS_IR_Line_Material(models.Model):
unit_price = fields.Monetary(string="Unit Price", currency_field='currency_id')
test_report = fields.Binary(string="Test Report")
test_report_filename=fields.Char(string="Test Report File Name")
expiry_date = fields.Date(string="Expiry Date")
@api.onchange('component_id')
def _onchange_component_id(self):
if self.component_id:

View File

@ -48,6 +48,8 @@ class SOS_Material(models.Model):
string="Materials History - In",
domain=[('action', '=', 'in')]
)
shelf_life = fields.Selection([('yes', 'Yes'), ('no', 'No')],default="no", string="Shelf Life",required=True)
line_ids_out = fields.One2many(
'sos_material_transaction_history', 'ref_id',

View File

@ -14,7 +14,7 @@ class sos__mon(models.Model):
mon_date = fields.Date(string="MON/SON/FON Date", required=True, default=fields.Date.today)
min_no = fields.Many2one('sos_min',string="MIN/SIN/FIN No")
filled_by = fields.Many2one('res.users', string='Filled By', readonly=True,required=True,default=lambda self: self.env.user)
logged_inuser_group=fields.Boolean(string='Group Name',compute='compute_user_grp')
logged_inuser_group=fields.Boolean(string='Group Name',compute='compute_user_grp',store=True)
dept = fields.Many2one('sos_departments',string="Department")
purpose = fields.Char(string="Purpose")
product_name = fields.Many2one('sos_fg', string='Material/Product Name & No')
@ -61,7 +61,7 @@ class sos__mon(models.Model):
auto_load_fg_item_ids = fields.Many2many(
'sos_fg',
compute='_compute_fg_items',
store=False
store=True
)
company_id = fields.Many2one('res.company', store=True, copy=False,
string="Company",
@ -70,7 +70,7 @@ class sos__mon(models.Model):
related='company_id.currency_id',
default=lambda
self: self.env.user.company_id.currency_id.id)
approx_value = fields.Monetary(compute='_compute_approx_value', string="Approximate Value", currency_field='currency_id', readonly=True)
approx_value = fields.Monetary(compute='_compute_approx_value', string="Approximate Value", currency_field='currency_id', readonly=True,store=True)
status = fields.Selection([ ('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
active = fields.Boolean(default=True)
@api.constrains('indent_ref_no', 'auto_load_fg_items')
@ -189,6 +189,8 @@ class sos__mon(models.Model):
@api.onchange('auto_load_fg_items')
def _onchange_auto_load_fg_items(self):
if not self.auto_load_fg_items:
return
if self.auto_load_fg_items:
self.material_option = True
self.sfg_option = True

View File

@ -46,7 +46,51 @@ class sos__mrn(models.Model):
default=lambda
self: self.env.user.company_id.currency_id.id)
approx_value = fields.Monetary(compute='_compute_approx_value', string="Approximate Value", currency_field='currency_id', readonly=True)
@api.onchange('min_no')
def _onchange_min_no(self):
if self.min_no:
self.material_option = self.min_no.material_option
self.sfg_option = self.min_no.sfg_option
self.fg_option = self.min_no.fg_option
#materials
self.line_ids_material = [(5, 0, 0)]
material_lines = []
for material in self.min_no.line_ids_material:
line_vals = {
'mrn_id': self.id,
'component_id': material.component_id,
'qp_no': material.qp_no,
'uom': material.uom,
'quantity': material.quantity
}
material_lines.append((0, 0, line_vals))
self.line_ids_material = material_lines
#sfg
self.line_ids_sfg = [(5, 0, 0)]
sfg_lines = []
for sfg in self.min_no.line_ids_sfg:
line_vals_sfg = {
'mrn_id': self.id,
'component_id': sfg.component_id,
'qp_no': sfg.qp_no,
'quantity': sfg.quantity
}
sfg_lines.append((0, 0, line_vals_sfg))
self.line_ids_sfg = sfg_lines
#sfg
self.line_ids_fg = [(5, 0, 0)]
fg_lines = []
for fg in self.min_no.line_ids_fg:
line_vals_fg = {
'mrn_id': self.id,
'component_id': fg.component_id,
'quantity': fg.quantity
}
fg_lines.append((0, 0, line_vals_fg))
self.line_ids_fg = fg_lines
@api.depends('line_ids_material.total_cost')
def _compute_approx_value(self):
for record in self:
@ -115,6 +159,7 @@ class sos__mrn(models.Model):
def action_report_esign_btn1(self):
sequence_util = self.env['sos_common_scripts']
splitted=self.order_type.split(",")
customer_return_record = False
if self.return_type == "customer":
sequence_util = self.env['sos_common_scripts']
tc_no = sequence_util.generate_sequence('sos_transfer_challan_return_from_customer','TC', 'tc_no')

View File

@ -56,6 +56,7 @@ class NCMR_Model(models.Model):
comments_on_capa = fields.Html(string="Comments on Corrective / Preventive Action ")
qa_comments=fields.Text(string="QA Comments")
qa_done_on = fields.Date(string="Verified On")
rework_responsible_rd_user = fields.Many2one('res.users',string='R&D User')
rd_user = fields.Many2many('res.users',string='R&D User', relation='sos_ncmr_rd_user_rel')
qa_by = fields.Many2one('res.users',string='QC In-Charge',readonly=True)
qa_sign = fields.Image(related="qa_by.signature_image",string='QC In-Charge',readonly=True)
@ -94,10 +95,15 @@ class NCMR_Model(models.Model):
string="Rework Action",
default="inhouse"
)
rework_action_by_qc = fields.Boolean(string="Rework")
rd_comments=fields.Text(string="Comments")
rd_approval_by = fields.Many2one('res.users',string='R&D In-Charge',readonly=True)
rd_approval_sign = fields.Image(related="rd_approval_by.signature_image",string='R&D In-Charge',readonly=True)
rd_approval_on = fields.Datetime(string="Approved On")
rework_rd_approval_by = fields.Many2one('res.users',string='Rework - R&D In-Charge',readonly=True)
rework_rd_approval_sign = fields.Image(related="rework_rd_approval_by.signature_image",string='Rework - R&D In-Charge',readonly=True)
rework_rd_approval_on = fields.Datetime(string="Approved On")
combined_incoming_doc_ref = fields.Reference(
selection=[
('sos_iqi', 'IQI Ref No'),
@ -244,7 +250,8 @@ class NCMR_Model(models.Model):
open_count = sum(1 for rec in line.defective_status_ids if rec.status == 'open')
closed_count = sum(1 for rec in line.defective_status_ids if rec.status == 'closed')
closure_rate = ((closed_count/required_ncmr)*100)
closure_rate = (closed_count / required_ncmr) * 100 if required_ncmr else 0.0
ppm = None
@ -668,6 +675,13 @@ class NCMR_Model(models.Model):
'rd_approval_by',
'rd_approval_on'
)
def action_rework_rd_esign_btn(self):
send_email = self.env['sos_common_scripts']
send_email.action_assign_signature(
self,
'rework_rd_approval_by',
'rework_rd_approval_on'
)
def action_qa_esign_btn(self):
if self.action_group:
@ -678,6 +692,20 @@ class NCMR_Model(models.Model):
'qa_tested_on',
'sos_inventory.sos_qc_user'
)
if self.rework_action_by_qc:
# Email part
body_html = f"""
<p>Below <b>NCMR</b> is waiting for your Action</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(
self.env,
"sos_ncmr",
self.id,
self.rework_responsible_rd_user.login,
"NCMR Rework Action Request",
body_html
)
if self.action_group == "rd":
# Email part
body_html = f"""

View File

@ -6,7 +6,7 @@ class SosTestingParameters(models.Model):
_rec_name='fg_name'
fg_name = fields.Many2one('sos_fg',string="FG Name")
parameter_ids = fields.One2many('sos_parameter', 'testing_id', string= 'BRR Testing Parameters',copy=True)
parameter_ids = fields.One2many('sos_parameter', 'testing_id', string= 'In-Process/BRR Testing Parameters',copy=True)
fir_parameter_ids = fields.One2many('sos_parameter_fir', 'testing_id', string= 'FIR Testing Parameters',copy=True)
specification_ids = fields.One2many('sos_specification', 'spec_id', string='Specifications',copy=True)
deliverables_ids = fields.One2many('sos_deliverables', 'accessory_id', string='Deliverables',copy=True)

View File

@ -44,11 +44,10 @@
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_ncmr_all_records_rule" model="ir.rule">
<record id="sos_ncmr_all_records_rule" model="ir.rule">
<field name="name">Sos NCMR: All Records - Read Access</field>
<field name="model_id" ref="model_sos_ncmr"/>
<field name="domain_force">[('rd_user', '=', user.id)]</field>
<field name="domain_force">[('rd_user', '=', user.id), ('rework_responsible_rd_user', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>

View File

@ -192,7 +192,7 @@
</record>
<!-- Menuitem -->
<menuitem id="menu_sos_fir_brr" sequence="3" name="Batch Realease Report (BRR)" parent="mop_forms_menu_root" action="action_sos_fir_brr" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user"/>
<menuitem id="menu_sos_fir_brr" sequence="3" name="In-Process Inspection / Batch Realease Report (BRR)" parent="mop_forms_menu_root" action="action_sos_fir_brr" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user"/>
</data>
</odoo>

View File

@ -119,6 +119,12 @@
<br></br>
<hr></hr>
</page>
<page string="Firmware Data">
<field name="firmware_data" filename="firmware_data_filename"/>
<field name="firmware_data_filename" invisible="1"/>
<br></br>
<hr></hr>
</page>
</notebook>
<templates>

View File

@ -99,6 +99,7 @@
<field name="order_qty"/>
<field name="qty"/>
<field name="unit_price"/>
<field name="expiry_date"/>
<field name="test_report" widget="binary" filename="test_report_filename"/>
<field name="test_report_filename" column_invisible="1"/>
<button name="action_view_pdf" invisible="not test_report" type="object" string="View" class="btn-primary" icon="fa-eye"/>

View File

@ -118,6 +118,7 @@
<field name="description"/>
<field name="category"/>
<field name="uom"/>
<field name="shelf_life"/>
<field name="inspection_method" widget="color_picker"/>
<field name="qp_no"/>
<field name="hsn_code"/>

View File

@ -29,7 +29,7 @@
<field name="forward_by" string="QA Approval" widget="many2one_avatar_user"/>
<field name="status" widget="badge" decoration-success="status == 'closed'" decoration-danger="status == 'open'"/>
<field name="rework_action_by_qc" widget="boolean_toggle"/>
<field name="write_uid" string="Last Edited By" optional="hide"/>
<field name="write_date" string="Last Edited On" optional="hide"/>
@ -242,19 +242,27 @@
<templates>
<div class="container">
<div class="row" style="margin-top:100px">
<div class="col-6">
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr><td colspan="2" class="table_custom_header">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">QC</span> Team</td></tr>
<div class="col-6" style="padding: 0px;box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<table class="table_custom" >
<tr><td colspan="2" class="table_custom_header" style="color:#fff">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">QC</span> Team</td></tr>
<tr>
<td style="padding: 8px;" class="column"><b>QC Comments</b></td>
<td><field name="comments"/></td>
</tr>
<tr>
<td style="padding: 8px;" class="column"><b>Action to be taken By</b></td>
<td><field name="action_group"/><field name="rd_user" widget="many2many_tags" invisible="action_group != 'rd'"/></td>
</tr>
<tr>
<td style="padding: 8px;" class="column"><b>Rework Decision</b></td>
<td><field name="rework_action_by_qc" widget="boolean_toggle"/>
<field name="rework_responsible_rd_user" invisible="rework_action_by_qc != True"/>
</td>
</tr>
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>QC Tested By </b></td>
<td>
@ -270,8 +278,8 @@
<td><field name="qa_by" readonly="1"/></td>
</tr>
</table></div>
<div class="col-6"><table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr><td colspan="2" class="table_custom_header">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">SCG</span> Team</td></tr>
<div class="col-6" style="padding: 0px;box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;"><table class="table_custom" >
<tr><td colspan="2" class="table_custom_header" style="color:#fff">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">SCG</span> Team</td></tr>
<tr>
<td style="padding: 8px;" class="column"><b>Comments</b></td>
@ -301,8 +309,8 @@
<div class="container">
<div class="row">
<div class="col-6" invisible="rework_action == 'outsourcing_vendor'"><table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr><td colspan="2" class="table_custom_header">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">Production</span> Team</td></tr>
<div class="col-6" style="padding: 0px;box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;" invisible="rework_action == 'outsourcing_vendor'"><table class="table_custom" >
<tr><td colspan="2" class="table_custom_header" style="color:#fff">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">Production</span> Team</td></tr>
<tr>
<td style="padding: 8px;" class="column"><b>Comments</b></td>
@ -323,9 +331,9 @@
<td><field name="production_approval_by" readonly="1"/></td>
</tr>
</table></div>
<div class="col-6"> <table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<div class="col-6" style="padding: 0px;box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;"> <table class="table_custom" >
<tr><td colspan="2" class="table_custom_header">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">QA</span> Team</td></tr>
<tr><td colspan="2" class="table_custom_header" style="color:#fff">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">QA</span> Team</td></tr>
<tr>
<td style="padding: 8px;" class="column"><b>Comments</b></td>
@ -362,8 +370,8 @@
<div class="container">
<div class="row">
<div class="col-6" invisible="action_group != 'rd'"><table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr><td colspan="2" class="table_custom_header">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">R&amp;D</span> Team</td></tr>
<div class="col-6" style="padding: 0px;box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;" invisible="action_group != 'rd'"><table class="table_custom" >
<tr><td colspan="2" class="table_custom_header" style="color:#fff">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">R&amp;D</span> Team</td></tr>
<tr>
<td style="padding: 8px;" class="column"><b>Comments</b></td>
@ -383,7 +391,27 @@
<td style="padding: 8px;" class="column"><b>Approved By</b></td>
<td><field name="rd_approval_by" readonly="1"/></td>
</tr>
</table></div></div>
</table></div>
<div class="col-6" style="padding: 0px;box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;" invisible="rework_action_by_qc != True"><table class="table_custom" >
<tr><td colspan="2" class="table_custom_header" style="color:#fff">To Be Filled By <span style="font-size: 18px;padding: 0px; color: #ffcc00;">R&amp;D</span> Team(Rework)</td></tr>
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Approved By </b></td>
<td>
<button string="Approve" invisible="rework_rd_approval_sign" class="btn-primary custom_btn" type="object" name="action_rework_rd_esign_btn"></button>
<field name="rework_rd_approval_sign" widget="image"/></td>
</tr>
<tr invisible="rework_rd_approval_sign == False">
<td style="padding: 8px;" class="column"><b>Approved On</b></td>
<td><field name="rework_rd_approval_on" readonly="1"/></td>
</tr>
<tr invisible="rework_rd_approval_sign == False">
<td style="padding: 8px;" class="column"><b>Approved By</b></td>
<td><field name="rework_rd_approval_by" readonly="1"/></td>
</tr>
</table></div>
</div>
</div>
</templates>

View File

@ -38,7 +38,7 @@
<field name="fg_name"/>
</group>
<notebook>
<page string="BRR Testing Parameters" groups="sos_inventory.sos_qc_user,sos_inventory.sos_qa_user">
<page string="In-Process/BRR Testing Parameters" groups="sos_inventory.sos_qc_user,sos_inventory.sos_qa_user">
<field name="parameter_ids">
<tree string="Parameters" editable="bottom">
<field name="sequence" widget="handle"/>
@ -115,7 +115,7 @@
</record>
<!-- Action to open sos_testing_parameters -->
<record id="action_sos_testing_parameters" model="ir.actions.act_window">
<field name="name">FG Configurations</field>
<field name="name">In-Process Configurations</field>
<field name="res_model">sos_testing_parameters</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
@ -136,7 +136,7 @@
</record>
<!-- Menu item to access sos_testing_parameters -->
<menuitem id="menu_sos_testing_parameters"
name="FG Configurations"
name="In-Process Configurations"
parent="settings_root_menu" action="action_sos_testing_parameters" groups="sos_inventory.sos_qc_user,sos_inventory.sos_qa_user,sos_inventory.sos_ce_user,sos_inventory.sos_production_user,sos_inventory.sos_management_user"/>
<menuitem id="menu_sos_fir_testing_parameters"
name="FIR Parameters Configurations"

View File

@ -152,7 +152,8 @@ class sos_case_diary(models.Model):
'ref_id': ref_id,
'customer_name': self.customer_name.id,
'action_date': last_record.status_changed_on,
'proposal_value': proposal_value
'proposal_value': proposal_value,
'po_no':self.po_no
})
# Assign signature

View File

@ -13,7 +13,7 @@ class SOS_Sales_Achievement_Report(models.Model):
_sql_constraints = [
('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)
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)
overall_target = fields.Float(string="Overall Target")
@ -114,6 +114,11 @@ class SOS_Sales_Achievement_Report(models.Model):
'ref_id',
string="Billing Collection Lines"
)
@api.depends('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 ''}"
@api.depends('opening_balance', 'actual_target_april', 'billed_target_april')
def _compute_yet_to_billed_april(self):
for rec in self:
@ -226,24 +231,24 @@ class SOS_Sales_Achievement_Report(models.Model):
'target': 'new',
'res_id': wizard.id,
}
def action_view_brief_lines(self):
# def action_view_brief_lines_acc(self):
# return {
# 'name': "Action",
# 'type': 'ir.actions.act_window',
# 'res_model': 'sos_sales_achievement_report_brief',
# 'view_mode': 'form',
# 'view_id': self.env.ref('sos_sales.view_form_sos_sales_achievement_acc').id,
# 'target': 'new',
# 'context': {
# 'ref_record_id': self.id
# }
# }
def action_view_brief_lines_acc(self):
self.ensure_one()
domain = [('ref_id', '=', self.id)]
domain = [('ref_record_id', '=', self.id)]
# Extract year from 'FY 2025-2026'
match = re.search(r'FY\s*(\d{4})', self.financial_year or '')
if match:
year = int(match.group(1))
else:
raise ValueError(f"Unable to extract year from financial year: {self.financial_year}")
# Filter by month if present
month = int(self.env.context.get('month', 0))
if month:
domain.append(('action_date', '>=', f'{year}-{month:02d}-01'))
last_day = calendar.monthrange(year, month)[1]
domain.append(('action_date', '<=', f'{year}-{month:02d}-{last_day:02d}'))
brief_lines = self.env['sos_sales_achievement_report_brief'].search(domain)
@ -258,6 +263,45 @@ class SOS_Sales_Achievement_Report(models.Model):
'default_parent_id': self.id,
}
}
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:
year = int(match.group(1))
else:
raise ValueError(f"Unable to extract year from financial year: {self.financial_year}")
month = int(self.env.context.get('month', 0))
if month:
# Filter specific month
domain.append(('action_date', '>=', f'{year}-{month:02d}-01'))
last_day = calendar.monthrange(year, month)[1]
domain.append(('action_date', '<=', f'{year}-{month:02d}-{last_day:02d}'))
else:
# Filter for entire financial year (April to March)
start_date = f'{year}-04-01'
end_date = f'{year + 1}-03-31'
domain.append(('action_date', '>=', start_date))
domain.append(('action_date', '<=', end_date))
brief_lines = self.env['sos_sales_achievement_report_brief'].search(domain)
return {
'name': 'Achievement Brief Lines',
'type': 'ir.actions.act_window',
'res_model': 'sos_sales_achievement_report_brief_wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_brief_line_ids': [(6, 0, brief_lines.ids)],
'default_parent_id': self.id,
}
}
@api.depends(
@ -554,17 +598,59 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
_name = 'sos_sales_achievement_report_brief'
_description = 'Achievement Brief'
_order = 'action_date desc'
def get_financial_year_selection(self):
current_date = date.today()
start_year = 2024 # starting from FY 2024-2025
current_year = current_date.year if current_date.month >= 4 else current_date.year - 1
end_year = current_year + 1
selection = []
for y in range(start_year, current_year + 1):
fy = f"FY {y}-{y+1}"
selection.append((fy, fy))
return selection
ref_id = fields.Many2one('sos_sales_achievement_report', string="Financial Year", ondelete="cascade")
ref_record_id = fields.Integer(string="Reference")
customer_name = fields.Many2one('sos_customers',string="Customer Name", required=True)
action_date = fields.Date(string="Date")
action_date = fields.Date(string="PO Date")
currency_id = fields.Many2one(
'res.currency',
string='Currency',
default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
)
proposal_value = fields.Monetary(currency_field='currency_id',string="PO Received(In Lakhs)")
financial_year = fields.Selection(
selection=get_financial_year_selection,
string="Financial Year",
required=True
)
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()
def open_line_form(self):
self.ensure_one()
return {
@ -589,7 +675,6 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
# 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()
@ -609,53 +694,63 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
new_field_billed: (getattr(report, new_field_billed, 0.0) or 0.0) + new_billed_amount
})
# Optionally create billing collection entry (if needed)
if new_billed_amount > 0:
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,
'value': new_billed_amount
})
if new_billed_amount > 0:
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,
'value': new_billed_amount
})
return super(SOS_Sales_Achievement_Report_Brief, self).write(vals)
@api.model
def create(self, vals):
ref_id = vals.get('ref_id')
vals['ref_record_id'] = ref_id
action_date = vals.get('action_date')
billed_date = vals.get('billed_date')
customer_name = vals.get('customer_name')
value = vals.get('proposal_value', 0.0)
billed_value = vals.get('billed_amount', 0.0)
ref_id = vals.get('ref_id')
if isinstance(action_date, str):
action_date = datetime.strptime(action_date, '%Y-%m-%d').date()
if isinstance(billed_date, str):
billed_date = datetime.strptime(billed_date, '%Y-%m-%d').date()
month_name = action_date.strftime('%B').lower()
value = vals.get('proposal_value', 0.0)
actual_field = f"actual_target_{month_name}"
ref_id = vals.get('ref_id')
if ref_id:
if ref_id and action_date:
report = self.env['sos_sales_achievement_report'].browse(ref_id)
sales_person = report.sales_person
if sales_person and hasattr(report, actual_field):
current_value = getattr(report, actual_field, 0.0) or 0.0
report.write({actual_field: current_value + value})
month_name = action_date.strftime('%B').lower()
actual_field = f"actual_target_{month_name}"
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}"
if vals.get('financial_year') != current_fy:
report.write({'opening_balance': report.opening_balance + value})
# Instead of also summing billed_value here, just create billing line
if billed_date:
self.env['sos_billing_collection'].create({
'ref_id': report.id,
'customer_name': customer_name,
'sales_person': report.sales_person.id,
'action_status': 'Billed',
'date_of_action': billed_date,
'value': vals.get('billed_amount', 0.0)
})
if vals.get('financial_year') == current_fy:
if hasattr(report, actual_field):
current_val = getattr(report, actual_field) or 0.0
report.write({actual_field: current_val + value})
return super(SOS_Sales_Achievement_Report_Brief, self).create(vals)
if billed_date:
self.env['sos_billing_collection'].create({
'ref_id': report.id,
'customer_name': customer_name,
'sales_person': report.sales_person.id,
'action_status': 'Billed',
'date_of_action': billed_date,
'value': billed_value
})
new_record = super(SOS_Sales_Achievement_Report_Brief, self).create(vals)
return new_record
class SOS_Sales_Achievement_Report_Brief_Wizard(models.TransientModel):
@ -780,6 +875,33 @@ class SosBillingCollection(models.Model):
start_year = current_date.year if current_date.month >= 4 else current_date.year - 1
end_year = start_year + 1
return f"FY {start_year}-{end_year}"
def delete_form_line(self):
self.unlink()
def unlink(self):
for record in self:
if record.date_of_action:
fy = self._get_financial_year(record.date_of_action)
month_name = record.date_of_action.strftime('%B').lower()
else:
fy = False
month_name = False
if fy and month_name:
actual_field_name = f"{record.action_status.lower()}_target_{month_name}"
# Fetch related record where field update should happen (example logic)
report = self.env['sos_sales_achievement_report'].search([
('financial_year', '=', fy),
('sales_person', '=', record.sales_person.id)
], limit=1)
if report and hasattr(report, actual_field_name):
current_val = getattr(report, actual_field_name, 0.0)
setattr(report, actual_field_name, current_val - record.value)
return super(SosBillingCollection, self).unlink()
@api.model
def create(self, vals):

View File

@ -1,4 +1,5 @@
from odoo import models, fields, api
import random
class sos_sales_leads(models.Model):
_name = 'sos_sales_leads'
@ -9,31 +10,43 @@ class sos_sales_leads(models.Model):
transferred_on = fields.Date(string="Transferred On")
location = fields.Char(string="Location")
website_url = fields.Char(string="Website URL")
vertical_domain = fields.Many2one('sos_vertical_domain',string="Domain / Industry", ondelete="cascade")
vertical_domain = fields.Many2many('sos_vertical_domain',string="Domain / Industry", ondelete="cascade")
linkedin_profile = fields.Char(string="Linkedin profile")
status = fields.Char(string="Status")
correspondence_address = fields.Text(string="Correspondence Address")
interested_in = fields.Selection(
[
('products', 'Products'),
('projects', 'Projects')
('Product', 'Products'),
('Project', 'Projects')
],
string="Interested In",required=True,default="products")
project_name = fields.Char(string="Project Name")
products = fields.Selection(
string="Service Type",required=True,default="Product")
sales_type = fields.Selection(
[
('BHMS 1.2V', 'BHMS 1.2V'),
('BHMS 2V', 'BHMS 2V'),
('BHMS 12V', 'BHMS 12V'),
('BHMS 48V', 'BHMS 48V'),
('BMS-HV', 'BMS-HV'),
('BMS-LV 100A', 'BMS-LV 100A'),
('BMS-LV 40A', 'BMS-LV 40A'),
('SBMS 55A', 'SBMS 55A'),
('MC 250W', 'MC 250W'),
('HeartTarang', 'HeartTarang')
('Domestic', 'Domestic'),
('International', 'International')
],
string="Products")
string="Sales Type",default="Domestic")
project_name= fields.Many2one('sos_projects',string="Project Name")
products_interested = fields.Many2many('sos_products',string="Product Interested In")
# products = fields.Selection(
# [
# ('BHMS 1.2V', 'BHMS 1.2V'),
# ('BHMS 2V', 'BHMS 2V'),
# ('BHMS 12V', 'BHMS 12V'),
# ('BHMS 48V', 'BHMS 48V'),
# ('BMS-HV', 'BMS-HV'),
# ('BMS-LV 100A', 'BMS-LV 100A'),
# ('BMS-LV 40A', 'BMS-LV 40A'),
# ('SBMS 55A', 'SBMS 55A'),
# ('MC 250W', 'MC 250W'),
# ('HeartTarang', 'HeartTarang')
# ],
# string="Products")
country = fields.Many2one(
'res.country',
string='Country',
default=lambda self: self.env['res.country'].search([('code', '=', 'IN')], limit=1)
)
source = fields.Selection(
[
('self', 'Self'),
@ -91,16 +104,18 @@ class SOS_Leads_Lines(models.Model):
email = fields.Char(string="Email Id")
mobile_number = fields.Char(string="Contact Number")
set_as_primary = fields.Boolean(string="Set Primary")
@api.model
def create(self, vals):
record = super(SOS_Leads_Lines, self).create(vals)
if not self.env['sos_leads_customer_contact_lines'].search([
('ref_id', '=', record.ref_id.id),
('set_as_primary', '=', True)
]):
# Set the created record as primary
record.set_as_primary = True
return record
linkedin_profile = fields.Char(string="LinkedIn Profile")
remarks = fields.Text(string="Remarks")
# @api.model
# def create(self, vals):
# record = super(SOS_Leads_Lines, self).create(vals)
# if not self.env['sos_leads_customer_contact_lines'].search([
# ('ref_id', '=', record.ref_id.id),
# ('set_as_primary', '=', True)
# ]):
# # Set the created record as primary
# record.set_as_primary = True
# return record
def write(self, vals):
res = super(SOS_Leads_Lines, self).write(vals)
if 'set_as_primary' in vals and vals['set_as_primary']:
@ -109,4 +124,22 @@ class SOS_Leads_Lines(models.Model):
('ref_id', '=', record.ref_id.id),
('id', '!=', record.id)
]).write({'set_as_primary': False})
return res
return res
class ProductInterest(models.Model):
_name = 'sos_products'
_description = 'Product Interest'
name = fields.Char(string="Product Name", required=True)
color = fields.Integer(string="Colour")
@api.model
def create(self, vals):
if 'color' not in vals:
vals['color'] = random.randint(1, 11)
return super(ProductInterest, self).create(vals)
class ProjectMaster(models.Model):
_name = 'sos_projects'
_description = 'Projects'
name = fields.Char(string="Project Name", required=True)

View File

@ -17,13 +17,11 @@ class ReportBusinessPerformance(models.AbstractModel):
financial_year = self._get_financial_year()
domain = [('financial_year', '=', financial_year)]
if sales_person_id:
domain.append(('sales_person', '=', sales_person_id))
records = self.env['sos_sales_achievement_report'].search(domain, order="sales_person ASC")
months = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
@ -39,8 +37,10 @@ class ReportBusinessPerformance(models.AbstractModel):
current_month = date.today().month
if current_month >= 4:
ytd_months = months[:current_month - 4 + 1]
current_month_key = months[current_month - 4]
else:
ytd_months = months[:current_month + 9 + 1]
current_month_key = months[current_month + 9]
records = list(records)
@ -54,7 +54,7 @@ class ReportBusinessPerformance(models.AbstractModel):
'target': 0.0,
'sales': 0.0,
'sales_percentage': 0.0,
'yet_to_billed':0.0,
'yet_to_billed': 0.0,
'billed': 0.0,
'collected': 0.0,
}
@ -77,16 +77,18 @@ class ReportBusinessPerformance(models.AbstractModel):
for m in ytd_months:
ytd_target += round(getattr(doc, f'planned_target_{m}', 0.0), 2)
ytd_sales += round(getattr(doc, f'actual_target_{m}', 0.0), 2)
ytd_yet_to_billed += round(getattr(doc, f'yet_to_billed_target_{m}', 0.0), 2)
ytd_billed += round(getattr(doc, f'billed_target_{m}', 0.0), 2)
ytd_collected += round(getattr(doc, f'collected_target_{m}', 0.0), 2)
summary['ytd']['target'] += round(getattr(doc, f'planned_target_{m}', 0.0), 2)
summary['ytd']['sales'] += round(getattr(doc, f'actual_target_{m}', 0.0), 2)
summary['ytd']['yet_to_billed'] += round(getattr(doc, f'yet_to_billed_target_{m}', 0.0), 2)
summary['ytd']['billed'] += round(getattr(doc, f'billed_target_{m}', 0.0), 2)
summary['ytd']['collected'] += round(getattr(doc, f'collected_target_{m}', 0.0), 2)
# Override YTD "yet_to_billed" with only current month value
ytd_yet_to_billed = round(getattr(doc, f'yet_to_billed_target_{current_month_key}', 0.0), 2)
summary['ytd']['yet_to_billed'] += ytd_yet_to_billed
# Assign as formatted string with 2 decimal places
doc.ytd_target = "%.2f" % ytd_target
doc.ytd_sales = "%.2f" % ytd_sales
@ -104,18 +106,21 @@ class ReportBusinessPerformance(models.AbstractModel):
else:
summary['ytd']['sales_percentage'] = "0.00"
# Also convert YTD values in summary to formatted strings
for key in ['target', 'sales', 'billed', 'collected']:
summary['ytd'][key] = "%.2f" % summary['ytd'][key]
summary['ytd']['yet_to_billed'] = "%.2f" % summary['ytd']['yet_to_billed']
current_month_index = months.index(current_month_key) if current_month_key in months else 0
return {
'doc_ids': [rec.id for rec in records],
'doc_model': 'sos_sales_achievement_report',
'docs': records,
'financial_year': financial_year,
'enumerated_months': list(enumerate(months)),
'months': months,
'month_names': month_names,
'summary': summary,
'ytd_months': ytd_months,
'current_month_index': int(current_month_index),
'is_filtered_by_sales_person': bool(sales_person_id)
}

View File

@ -9,7 +9,7 @@
<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 -->
<t t-if="not is_filtered_by_sales_person">
@ -64,25 +64,38 @@
<td><t t-esc="summary['ytd']['billed']"/></td>
</tr>
<tr>
<td style="background-color:#e4e0f9"><strong>Yet to Bill</strong></td>
<t t-set="total_yet_billed" t-value="0"/>
<t t-foreach="months" t-as="m">
<t t-set="val" t-value="summary['yet_to_billed'][m]"/>
<td><t t-esc="'%.2f' % val"/></td>
<t t-set="total_yet_billed" t-value="total_billed + val"/>
</t>
<tr>
<td style="background-color:#e4e0f9"><strong>Yet to Bill</strong></td>
<t t-set="total_yet_billed" t-value="0"/>
<t t-foreach="enumerated_months" t-as="item">
<t t-set="i" t-value="item[0]"/>
<t t-set="m" t-value="item[1]"/>
<t t-if="i &lt;= current_month_index">
<t t-set="val" t-value="summary['yet_to_billed'][m] or 0.0"/>
<td><t t-esc="'%.2f' % val"/></td>
<t t-set="total_yet_billed" t-value="total_yet_billed + val"/>
</t>
<t t-else="">
<td>0.00</td>
</t>
</t>
<td><t t-esc="summary['ytd']['yet_to_billed']"/></td>
<td><t t-esc="summary['ytd']['yet_to_billed']"/></td>
</tr>
<td><t t-esc="'%.2f' % float(total_yet_billed)"/></td>
<td><t t-esc="summary['ytd']['yet_to_billed']"/></td>
</tr>
<tr>
<td style="background-color:#e4e0f9"><strong>Collected</strong></td>
<t t-set="total_collected" t-value="0"/>
<t t-foreach="months" t-as="m">
<t t-set="val" t-value="summary['collected'][m]"/>
<td><t t-esc="val"/></td>
<td><t t-esc="'%.2f' % val"/></td>
<t t-set="total_collected" t-value="total_collected + val"/>
</t>
<td><t t-esc="'%.2f' % float(total_collected)"/></td>
@ -186,22 +199,25 @@
<td><t t-esc="'%.2f' % float(doc.ytd_billed)"/></td>
</tr>
<tr>
<td style="background-color:#e4e0f9"><strong>Yet to Bill</strong></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_april)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_may)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_june)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_july)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_august)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_september)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_october)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_november)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_december)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_january)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_february)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_march)"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_total)"/></td>
<td><t t-esc="'%.2f' % float(doc.ytd_yet_to_billed)"/></td>
</tr>
<td style="background-color:#e4e0f9"><strong>Yet to Bill</strong></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_april) if current_month_index >= 0 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_may) if current_month_index >= 1 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_june) if current_month_index >= 2 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_july) if current_month_index >= 3 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_august) if current_month_index >= 4 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_september) if current_month_index >= 5 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_october) if current_month_index >= 6 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_november) if current_month_index >= 7 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_december) if current_month_index >= 8 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_january) if current_month_index >= 9 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_february) if current_month_index >= 10 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.yet_to_billed_target_march) if current_month_index >= 11 else '0.00'"/></td>
<td><t t-esc="'%.2f' % float(doc.ytd_yet_to_billed)"/></td>
<td><t t-esc="'%.2f' % float(doc.ytd_yet_to_billed)"/></td>
</tr>
<tr>
<td style="background-color:#e4e0f9"><strong>Collected</strong></td>
<td><t t-esc="'%.2f' % float(doc.collected_target_april)"/></td>

View File

@ -61,5 +61,7 @@ access_sos_sales_achievement_report_brief_wizard,sos_sales_achievement_report_br
access_sos_billing_collection,sos_billing_collection access,model_sos_billing_collection,base.group_user,1,1,1,1
access_sos_billed_collection_wizard,sos_billed_collection_wizard access,model_sos_billed_collection_wizard,base.group_user,1,1,1,1
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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
61 access_sos_billing_collection sos_billing_collection access model_sos_billing_collection base.group_user 1 1 1 1
62 access_sos_billed_collection_wizard sos_billed_collection_wizard access model_sos_billed_collection_wizard base.group_user 1 1 1 1
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
67

View File

@ -13,13 +13,23 @@ name="action_report_achievement_btn"><i class="fa fa-print"></i> Print Report</b
<sheet>
<h2 style="text-align: center;text-transform: uppercase;text-shadow: 1px 1p 1px #140718;color: #65407c;padding:5px;">Sales Achievements Report</h2><hr></hr><br></br>
<group>
<field name="financial_year" style="font-size: 20px; pointer-events: none; color: #793595;"/>
<field name="sales_person" />
<field name="overall_target" />
<field name="opening_balance" groups="sos_inventory.sos_finance_user,sos_inventory.sos_management_user"/>
<group><field name="financial_year" style="font-size: 20px; pointer-events: none; color: #793595;"/>
</group>
<group><field name="sales_person" /></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></td>
<td><button name="action_view_brief_lines_acc"
type="object"
string="📊"
class="btn-link"
context="{'month': 0}"
style="padding: 0; border: none; background: none; font-size: 18px; box-shadow: 4px 5px 5px #e3e2e2;"/>
</td>
</tr></table>
</group>
<br></br>
<table class="table_custom">
<thead>

View File

@ -22,11 +22,11 @@
<field name="model">sos_sales_leads</field>
<field name="arch" type="xml">
<search>
<searchpanel>
<field name="products" string="Products Interested In" icon="fa-list-ul" enable_counters="1"/>
<field name="vertical_domain" string="Vertical/Domain" icon="fa-pie-chart" enable_counters="1"/>
<searchpanel>
</searchpanel>
<field name="sales_type" string="Type" icon="fa-list-ul" enable_counters="1"/>
</searchpanel>
<!-- Add fields to search on -->
<field name="company_name" string="Company Name"/>
@ -53,20 +53,25 @@
<group>
<group>
<field name="sales_type"/>
<field name="lead_generated_by"/>
<field name="company_name"/>
<field name="convert_to_customer_btn_display" invisible="1"/>
<field name="transferred_on" invisible="1"/>
<field name="country" invisible="sales_type != 'International'"/>
<field name="website_url"/>
<field name="linkedin_profile"/>
<field name="correspondence_address"/>
</group>
<group>
<field name="interested_in"/>
<field name="project_name" invisible="interested_in == 'products'"/>
<field name="products" invisible="interested_in == 'projects'"/>
<field name="project_name" invisible="interested_in == 'Product'"/>
<field name="products_interested"
options="{'no_create': True, 'no_edit': True, 'no_create_edit': True}"
widget="many2many_tags" invisible="interested_in != 'Product'"/>
<field name="location"/>
<field name="vertical_domain"/>
<field name="vertical_domain" widget="many2many_tags"/>
<field name="source"/>
<field name="expo_name" invisible="source != 'expo'"/>
</group>
@ -82,6 +87,8 @@
<field name="dept"/>
<field name="email"/>
<field name="mobile_number"/>
<field name="linkedin_profile"/>
<field name="remarks"/>
</tree>
</field>

View File

@ -1,5 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_form_sos_sales_achievement_acc" model="ir.ui.view">
<field name="name">sos.sales.achievement.report.brief.form</field>
<field name="model">sos_sales_achievement_report_brief</field>
<field name="arch" type="xml">
<form string="Achievement Brief">
<sheet>
<group>
<field name="ref_id" invisible="1"/>
<field name="financial_year"/>
<field name="sales_person"/>
<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="currency_id" invisible="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_sos_sales_achievement_brief_wizard_form" model="ir.ui.view">
<field name="name">sos_sales_achievement_report_brief_wizard.form</field>
@ -19,13 +43,18 @@
<field name="brief_line_ids" nolabel="1">
<tree>
<field name="financial_year"/>
<field name="action_date" string="Date"/>
<field name="customer_name"/>
<field name="po_no"/>
<field name="invoice_no"/>
<field name="proposal_value"/>
<field name="billed_date"/>
<field name="billed_amount"/>
<button name="open_line_form" string="Edit"
type="object" icon="fa-pencil" invisible="billed_date != False and billed_amount != False"/>
<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"
type="object" icon="fa-pencil"/>
</tree>
</field>
@ -139,6 +168,8 @@
<field name="customer_name"/>
<field name="value"/>
<field name="invoice_no"/>
<button name="delete_form_line" class="btn btn-primary" string="Delete"
type="object" icon="fa-times"/>
</tree>
</field>
<footer>