diff --git a/sos_inside_sales/models/__pycache__/sos_inside_sales_leads.cpython-310.pyc b/sos_inside_sales/models/__pycache__/sos_inside_sales_leads.cpython-310.pyc
index b37697b..900f941 100644
Binary files a/sos_inside_sales/models/__pycache__/sos_inside_sales_leads.cpython-310.pyc and b/sos_inside_sales/models/__pycache__/sos_inside_sales_leads.cpython-310.pyc differ
diff --git a/sos_inside_sales/views/sos_inside_sales_leads_view.xml b/sos_inside_sales/views/sos_inside_sales_leads_view.xml
index 0e8622e..19d706a 100755
--- a/sos_inside_sales/views/sos_inside_sales_leads_view.xml
+++ b/sos_inside_sales/views/sos_inside_sales_leads_view.xml
@@ -11,7 +11,7 @@
Below Deliverables/BOQ is waiting for your Approval
+ """ + sequence_util.send_group_email(self.env,'sos_deliverables_boq',self.id,"deenalaura.m@sosaley.in","Deliverables/BOQ Approval Request",body_html,'sos_inventory.sos_ce_head') + # Email part ends return sequence_util.action_assign_signature( self, 'prepared_by', 'prepared_on' ) + def action_ce_verified_esign_btn(self): + sequence_util = self.env['sos_common_scripts'] + # Email part + body_html = f""" +Below Deliverables/BOQ is waiting for your Approval
+ """ + sequence_util.send_group_email(self.env,'sos_deliverables_boq',self.id,"deenalaura.m@sosaley.in","Deliverables/BOQ Approval Request",body_html,'sos_inventory.sos_scg_group_manager') + # Email part ends + return sequence_util.action_assign_signature( + self, + 'ce_verified_by', + 'ce_verified_on' + ) + def action_scg_head_verified_esign_btn(self): + sequence_util = self.env['sos_common_scripts'] + # Email part + body_html = f""" +Below Deliverables/BOQ is waiting for your Approval
+ """ + sequence_util.send_group_email(self.env,'sos_deliverables_boq',self.id,"deenalaura.m@sosaley.in","Deliverables/BOQ Approval Request",body_html,'sos_inventory.sos_qa_user') + # Email part ends + return sequence_util.action_assign_signature( + self, + 'scg_head_verified_by', + 'scg_head_verified_on' + ) + def action_scg_esign_btn(self): sequence_util = self.env['sos_common_scripts'] return sequence_util.action_assign_signature( @@ -185,6 +231,7 @@ class sos_deliverables_boq_Line_Material(models.Model): ref_id = fields.Many2one('sos_deliverables_boq', string="Materials", ondelete="cascade") component_id = fields.Many2one('sos_material', string="Material Name", required=True) + display_name = fields.Char(string="Display Name", related="component_id.name", store=True) uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], default="Nos",string="Uom") currency_id = fields.Many2one('res.currency', string='Currency') material_code = fields.Char(related="component_id.material_code",string="Material Code") @@ -306,6 +353,7 @@ class sos_deliverables_Material_installationkit(models.Model): ref_id = fields.Many2one('sos_deliverables_boq', string="Materials", ondelete="cascade") component_id = fields.Many2one('sos_material', string="Material Name", required=True) + display_name = fields.Char(string="Display Name", related="component_id.name", store=True) uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom") currency_id = fields.Many2one('res.currency', string='Currency') material_code = fields.Char(related="component_id.material_code",string="Material Code") diff --git a/sos_inventory/models/sos_dock_audit.py b/sos_inventory/models/sos_dock_audit.py index 1e55618..83d6754 100755 --- a/sos_inventory/models/sos_dock_audit.py +++ b/sos_inventory/models/sos_dock_audit.py @@ -17,12 +17,15 @@ class SOS_Dock_Audit(models.Model): deliverables_boq_id = fields.Many2one('sos_deliverables_boq', string="Deliverables/BOQ Id") fg_name = fields.Selection( [ - ('BHMS 1.2V', 'BHMS 1.2V'), - ('BHMS 2V', 'BHMS 2V'), - ('BHMS 12V', 'BHMS 12V'), + ('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'), - ('MC 250W', 'MC 250W'), + ('BMS-LV 40A', 'BMS-LV 40A'), + ('SBMS 55A', 'SBMS 55A'), + ('MC 250W', 'MC 250W'), ('HeartTarang', 'HeartTarang') ], string="Product Type",required=True) @@ -384,6 +387,7 @@ class sos_dock_audit_Line_Material(models.Model): ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade") component_id = fields.Many2one('sos_material', string="Material Name", required=True) + display_name = fields.Char(string="Display Name", related="component_id.name", store=True) uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], default="Nos",string="Uom") currency_id = fields.Many2one('res.currency', string='Currency') material_code = fields.Char(related="component_id.material_code",string="Material Code") @@ -689,6 +693,7 @@ class sos_dock_audit_Material_installationkit(models.Model): ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade") component_id = fields.Many2one('sos_material', string="Material Name", required=True) + display_name = fields.Char(string="Display Name", related="component_id.name", store=True) uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], default="Nos",string="Uom") currency_id = fields.Many2one('res.currency', string='Currency') material_code = fields.Char(related="component_id.material_code",string="Material Code") diff --git a/sos_inventory/models/sos_fir.py b/sos_inventory/models/sos_fir.py index e2302b6..9ab45da 100755 --- a/sos_inventory/models/sos_fir.py +++ b/sos_inventory/models/sos_fir.py @@ -51,21 +51,45 @@ class FIR_Only(models.Model): stores_received_on = fields.Date(string="Stores Received On") ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Reference (If any Rejected)") customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name", required=True) - remarks = fields.Text(string="Remarks") test_log = fields.Binary("Test Log", required=False, attachment=True) test_log_filename = fields.Char("Test Log Filename") rejected_line_ids = fields.One2many('sos_fir_rejected_lines', 'ref_id', string="Finished Goods",copy=True, ondelete='cascade') serial_no_line_ids = fields.One2many('sos_fir_serial_no_lines', 'ref_id',copy=True) - sd_card_data = fields.Binary("SD Card Data", required=False, attachment=True) sd_card_data_filename = fields.Char("SD Card Data Filename") - 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") + serial_no_parse = fields.Text(string="Serial no's to parse") + def parse_serial_nos(self): + self.ensure_one() + if not self.serial_no_parse: + return + + # Split into clean serial numbers + serial_numbers = [ + line.strip() for line in self.serial_no_parse.splitlines() if line.strip() + ] + + SerialLine = self.env['sos_fir_serial_no_lines'] + + for serial in serial_numbers: + # Check if already exists for this ref_id + exists = SerialLine.search([ + ('ref_id', '=', self.id), + ('serial_no', '=', serial) + ], limit=1) + + if not exists: + SerialLine.create({ + 'ref_id': self.id, + 'serial_no': serial, + 'inspection_decision': 'PASS', # default + }) + + @api.onchange('batch_size') def _onchange_batch_size(self): if self._origin and self.batch_size is not False: diff --git a/sos_inventory/models/sos_inventory_customers.py b/sos_inventory/models/sos_inventory_customers.py index b9c0615..b54768a 100755 --- a/sos_inventory/models/sos_inventory_customers.py +++ b/sos_inventory/models/sos_inventory_customers.py @@ -18,18 +18,30 @@ class sos_inventory_customers(models.Model): new_name = (vals.get('customer_name') or '').lower() if new_name and len(new_name) >= 5: - existing_customers = self.search([]) + # Words/phrases to exclude + blacklist = [ + 'private limited', 'pvt ltd', 'pvt. ltd.', 'ltd', 'llp', 'inc', 'co', 'company', 'corporation' + ] + def clean_name(name): + for word in blacklist: + name = name.replace(word, '') + return name.strip() + + new_name_clean = clean_name(new_name) + + existing_customers = self.search([]) for customer in existing_customers: existing_name = (customer.customer_name or '').lower() + existing_name_clean = clean_name(existing_name) - # 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: + # Check all substrings of length 7 or more + for i in range(len(new_name_clean) - 6): + substring = new_name_clean[i:i+7] + if substring in existing_name_clean: 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) \ No newline at end of file + return super().create(vals) \ No newline at end of file diff --git a/sos_inventory/models/sos_ir.py b/sos_inventory/models/sos_ir.py index ed8d164..6a19daa 100755 --- a/sos_inventory/models/sos_ir.py +++ b/sos_inventory/models/sos_ir.py @@ -217,6 +217,11 @@ class SOS_IR(models.Model): for item in self.line_ids_material: # Fetch the component related to the current item component = self.env['sos_material'].browse(item.component_id.id) + if self.supplier_name and self.supplier_name.id not in component.suppliers.ids: + component.write({ + 'suppliers': [(4, self.supplier_name.id)] + }) + if component.shelf_life == "yes": shelflife_line_values = { 'ir_ref_no':self.id, @@ -301,6 +306,10 @@ class SOS_IR(models.Model): for item in self.line_ids_sfg: # PO update part sfg_component = self.env['sos_sfg'].browse(item.component_id.id) + if self.service_provider_name and self.service_provider_name.id not in sfg_component.service_providers.ids: + sfg_component.write({ + 'service_providers': [(4, self.service_provider_name.id)] + }) if self.wo_planned_at == "outsource": if self.wo_no: if not self.orr_no: diff --git a/sos_inventory/models/sos_sales_order.py b/sos_inventory/models/sos_sales_order.py index d7a1ab4..3ee5d5b 100755 --- a/sos_inventory/models/sos_sales_order.py +++ b/sos_inventory/models/sos_sales_order.py @@ -25,7 +25,7 @@ class SOS_SalesOrder(models.Model): ], string="Product Name",required=True) line_ids = fields.One2many('sos_sales_order_line', 'ref_id',copy=True) - customer_name = fields.Char(string="Customer Name") + customer_name = fields.Many2one('sos_inventory_customers',string="Customer Name") lead_time = fields.Datetime(string="Lead Time") customer_po_no = fields.Char(string="PO No") customer_po_date = fields.Datetime(string="PO Date") @@ -58,7 +58,7 @@ class SOS_SalesOrder(models.Model): 'sales_id':self.id, 'fg_name':self.fg_name, 'quantity':self.qty, - 'customer_name':self.customer_name, + 'customer_name':self.customer_name.customer_name, 'lead_time':self.lead_time, 'customer_po_no':self.customer_po_no, 'customer_po_date':self.customer_po_date @@ -109,15 +109,15 @@ class SOS_SalesOrder(models.Model): sequence_util = self.env['sos_common_scripts'] return sequence_util.generate_sequence('sos_sales_order','SALES', 'order_id') - @api.model - def create(self, vals): - customer_name = vals.get('customer_name') - if customer_name: - existing = self.env['sos_inventory_customers'].search([('customer_name', '=', customer_name)], limit=1) - if not existing: - self.env['sos_inventory_customers'].create({'customer_name': customer_name}) + # @api.model + # def create(self, vals): + # customer_name = vals.get('customer_name') + # if customer_name: + # existing = self.env['sos_inventory_customers'].search([('customer_name', '=', customer_name)], limit=1) + # if not existing: + # self.env['sos_inventory_customers'].create({'customer_name': customer_name}) - return super(SOS_SalesOrder, self).create(vals) + # return super(SOS_SalesOrder, self).create(vals) class SOS_SalesOrder_Line(models.Model): _name = 'sos_sales_order_line' diff --git a/sos_inventory/report/sos_boq_labels.xml b/sos_inventory/report/sos_boq_labels.xml index 86ae22f..c7a8ea7 100755 --- a/sos_inventory/report/sos_boq_labels.xml +++ b/sos_inventory/report/sos_boq_labels.xml @@ -86,131 +86,192 @@ } -| Set No | +# |
+
| Battery | +|
| No | +|
| Name | +
+ |
+
| UOM | +
+ |
+
| Qty | +
+ |
+
| S.No | ++ |
| Set No | -# |
-
| Battery | -- |
| No | -|
| Name | -|
| UOM | -|
| Qty | -|
| S.No | -- |
| Set No | -# |
-
| Battery | -- |
| No | -|
| Name | -|
| UOM | -|
| Qty | -|
| S.No | -- |
| Set No | +# |
+
| Battery | +|
| No | +|
| Name | +
+ |
+
| UOM | +
+ |
+
| Qty | +
+ |
+
| S.No | ++ |
| Customer Name | Location | ||
| Product Name | Quantity |
| Finished Goods | |||||
| S.No | +Name | +UOM | +Single set Qty | +Total Set | +Qty | +
| Semi-Finished Goods | |||||
| Materials | |||||
| Installation Kit | |||||
| Miscellaneous | |||||
| S.No | +Name | +Qty | +|||
Serial No's | ||||||
| Verified By + | QA Verified By
|
@@ -307,6 +353,8 @@
| Parse Serial No's | |||
| Material Name | +UoM | +Unit Price | +Total Qty | +Total Price | +
|---|
| Name | +Unit Price | +Total Qty | +Total Price | +
|---|
| Material Name | +UoM | +Unit Price | +Total Qty | +Total Price | +
|---|
| Material Name | +UoM | +Unit Price | +Total Qty | +Total Price | +
|---|
| SFG Name | +UoM | +Unit Price | +Total Qty | +Total Price | +
|---|
| FG Name | +UoM | +Unit Price | +Total Qty | +Total Price | +
|---|
Below Proposal is waiting for your Updation
+Below BOQ is waiting for your updation.
+Customer Name: {self.customer_name or ''}
+Location: {self.location or ''}
+Number of Batteries: {self.number_of_batteries or ''}
""" + sequence_util = self.env['sos_common_scripts'] - sequence_util.send_group_email(self.env,'sos_proposal_boq',self.id,"deenalaura.m@sosaley.in","Proposal System - BOQ Submitted",body_html,'sos_inventory.sos_finance_user') + sequence_util.send_group_email( + self.env, + 'sos_proposal_boq', + self.id, + "deenalaura.m@sosaley.in", + f"Proposal System - BOQ Submitted for {self.customer_name}", + body_html, + 'sos_inventory.sos_finance_user' + ) return sequence_util.action_assign_signature( self, 'boq_submitted_by_name', diff --git a/sos_sales/models/sos_proposal_customer_requirement.py b/sos_sales/models/sos_proposal_customer_requirement.py index b921509..4980c47 100755 --- a/sos_sales/models/sos_proposal_customer_requirement.py +++ b/sos_sales/models/sos_proposal_customer_requirement.py @@ -30,6 +30,7 @@ class Battery_Installation_Requirement(models.Model): number_of_strings = fields.Integer(string="Number of Strings") number_of_ups = fields.Integer(string="Number of UPS") ups_rating_kva = fields.Integer(string="UPS Rating (KVA)") + no_of_electrical_panel = fields.Integer(string="No of Electrical Panel") battery_capacity_ah = fields.Integer(string="Battery Capacity (AH)") specific_requirements = fields.Html(string="Specific Requirements") products = fields.Selection( @@ -147,6 +148,7 @@ class Battery_Installation_Requirement(models.Model): @api.onchange('number_of_ups') def _onchange_number_of_ups(self): if self.number_of_ups is not None: + self.no_of_electrical_panel = self.number_of_ups if self.number_of_ups > 15: raise ValidationError("Number of UPS cannot be more than 15.") diff --git a/sos_sales/models/sos_sales_achievement_report.py b/sos_sales/models/sos_sales_achievement_report.py index 25b26ca..7bb5410 100755 --- a/sos_sales/models/sos_sales_achievement_report.py +++ b/sos_sales/models/sos_sales_achievement_report.py @@ -694,16 +694,31 @@ class SOS_Sales_Achievement_Report_Brief(models.Model): 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) - if new_billed_amount > 0: + # Optionally create billing collection entry (if needed + domain = [ + ('ref_id', '=', report.id), + ('sales_person', '=', report.sales_person.id), + ('customer_name', '=', vals.get('customer_name', rec.customer_name.id)) + ] + + 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) @@ -748,7 +763,8 @@ class SOS_Sales_Achievement_Report_Brief(models.Model): 'sales_person': report.sales_person.id, 'action_status': 'Billed', 'date_of_action': billed_date, - 'value': billed_value + 'value': billed_value, + 'po_no':vals.get('po_no') }) new_record = super(SOS_Sales_Achievement_Report_Brief, self).create(vals) return new_record diff --git a/sos_sales/views/sos_proposal_boq_view.xml b/sos_sales/views/sos_proposal_boq_view.xml index 5d11f3a..f70bcc1 100755 --- a/sos_sales/views/sos_proposal_boq_view.xml +++ b/sos_sales/views/sos_proposal_boq_view.xml @@ -13,12 +13,12 @@| No of Persons | Cost per Day | + +|
| Man Month( 2 to 3 Yrs) | ||
| Man Month( 3 to 5 Yrs) | ||
| Man Month(Manager) | ||
| Total | ||
| Product Cost | |
| Opreational Cost per Battery | |
| Product Cost | |
| Opreational Cost per Battery | |
| Opreational Cost | |
| Packing & Forwarding | |
| Installation & Commissioning | |
| Warranty (%) | |
| Opreational Cost | |
| Packing & Forwarding | |
| Installation & Commissioning | |
| Warranty (%) | |
| Warranty Cost | |
| Warranty Cost | |
| Additional Warranty | |
| MSP Per Battery | |
| Additional Warranty | |
| MSP Per Battery | |
| Final MSP | |
| Final MSP |