From faa8f2ff3b3a420399e2cb714dd3a7651b9251c5 Mon Sep 17 00:00:00 2001 From: Deena Date: Mon, 11 Aug 2025 11:14:47 +0530 Subject: [PATCH] Reference #7 --- .../sos_proposal_boq.cpython-310.pyc | Bin 51648 -> 61066 bytes ...s_sales_achievement_report.cpython-310.pyc | Bin 26278 -> 26508 bytes sos_sales/models/sos_proposal_boq.py | 504 +++++++++++++++++- .../models/sos_sales_achievement_report.py | 22 +- sos_sales/views/sos_proposal_boq_view.xml | 126 +---- 5 files changed, 531 insertions(+), 121 deletions(-) diff --git a/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc index 9b8f470df6faf3f43c12290fbab649f31fbaa0ab..bc5e6a94c8593a085e7f4cd51eb2957f537ebc32 100644 GIT binary patch literal 61066 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 literal 51648 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 SZ(h}0k0<|sWi$V`ul#?IMnS6p diff --git a/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc b/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc index 7b98b7b4a0e64ba59602e88f5fe30e96aa334464..8e696706b0aae3f04da2c4a3b2f6c21772ad873d 100644 GIT binary patch delta 811 zcmaJ<&rcIk5Z-y)ZOgXX7TQvXkQfh!O*Azq67dKTC8bz?D5k-t@wJs=C~k{Uc(wTB zLM&c}gAo)OBT+8~_h6zn#)C)Y#HANwyyzdG2?x~oRwdDcyO}RD-+XVf@0(qF2jva$ zSm6emYvow%T|jNf`vvV-5|UV6&RBA1`I|HyW06Jj9UUaFc$F- z9}+1O(8I50%=4_KOyv@jIo)Ac;*{V!grY29K|q>wHRV$B!gNZPjf_wT%4+H%EJqUh z1EI5ySp!x?`B1r8+YZ=mL&U+^39(hX?+59%LP9SVP8qE{v!j||wn8O$GF4%2; zjeh_r;pSM>mF7R7(aW8fX~iGOpxJ%)2*4~B6LU~9zbBReuHfBdOY^Mo?jHjOId|RM zNKOzgI!gOt3Fqle*PM`ATP!vkE*yK=?thzz`ZyJui=XmwAcT<<fb;%z?Lln%s qty + name_map, curr_map, price_map = {}, {}, {} + + for model in models: + 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) + + 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: + rec.merged_spare_html = "No Material items." + continue + + def fmt_price(amount, currency): + return format_amount(self.env, amount, currency) if currency else f"{amount:.2f}" + + total_sum = 0.0 + rows_html = "" + for key, qty in rows_sorted: + if not qty: + continue + total_price = qty * price_map[key] + total_sum += total_price + rows_html += ( + f"" + f"{name_map[key]}" + f"{key[1] or ''}" + f"{fmt_price(price_map[key], curr_map[key])}" + f"{qty:.2f}" + f"{fmt_price(total_price, curr_map[key])}" + f"" + ) + + if not rows_html: + rec.merged_spare_html = "No Material items." + continue + + currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False + rows_html += ( + f"" + f"Grand Total:" + f"{fmt_price(total_sum, currency_for_total)}" + f"" + ) + + rec.merged_spare_html = f""" + + + + + + + + + + + {rows_html} +
Material NameUoMUnit PriceTotal QtyTotal Price
+ """ + def _compute_merged_miscellaneous_html(self): + for rec in self: + lines = self.env['sos_proposal_miscellaneous_items'].search([('ref_id', '=', rec.id)]) + + # Group by (component, currency, cost) + agg = defaultdict(float) + name_map = {} + curr_map = {} + price_map = {} + + for l in lines: + cost = l.cost or 0.0 + curr_id = l.currency_id.id if l.currency_id else False + key = (l.name, curr_id, cost) + + agg[key] += float(l.quantity or 0) + if key not in name_map: + name_map[key] = l.name or '' + curr_map[key] = l.currency_id if l.currency_id else False + price_map[key] = cost + + rows_sorted = sorted(agg.items(), key=lambda it: name_map[it[0]].lower()) + if not rows_sorted: + rec.merged_miscellaneous_html = "No Miscellaneous items." + continue + + def fmt_price(amount, currency): + if currency: + return format_amount(self.env, amount, currency) + return f"{amount:.2f}" + + total_sum = 0.0 + rows_html = "" + for key, qty in rows_sorted: + if not qty: # Skip rows with qty = 0 + continue + total_price = qty * price_map[key] + total_sum += total_price + rows_html += ( + f"" + f"{name_map[key]}" + f"{fmt_price(price_map[key], curr_map[key])}" + f"{qty:.2f}" + f"{fmt_price(total_price, curr_map[key])}" + f"" + ) + + if not rows_html: # If all rows were skipped + rec.merged_miscellaneous_html = "No Miscellaneous items." + continue + + # Add grand total row + currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False + rows_html += ( + f"" + f"Grand Total:" + f"{fmt_price(total_sum, currency_for_total)}" + f"" + ) + + rec.merged_miscellaneous_html = f""" + + + + + + + + + + {rows_html} +
NameUnit PriceTotal QtyTotal Price
+ """ + def _compute_merged_installation_kit_html(self): + for rec in self: + lines = self.env['sos_proposal_line_material_installation'].search([('ref_id', '=', rec.id)]) + + # Group by (component, uom, currency, unit_price) + agg = defaultdict(float) + name_map = {} + curr_map = {} + 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) + + 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: + rec.merged_installation_kit_html = "No Material items." + continue + + def fmt_price(amount, currency): + if currency: + return format_amount(self.env, amount, currency) + return f"{amount:.2f}" + + total_sum = 0.0 + rows_html = "" + for key, qty in rows_sorted: + if not qty: # Skip rows with qty = 0 + continue + total_price = qty * price_map[key] + total_sum += total_price + rows_html += ( + f"" + f"{name_map[key]}" + f"{key[1] or ''}" + f"{fmt_price(price_map[key], curr_map[key])}" + f"{qty:.2f}" + f"{fmt_price(total_price, curr_map[key])}" + f"" + ) + + if not rows_html: # If all rows were skipped + rec.merged_installation_kit_html = "No Material items." + continue + + # Add grand total row + currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False + rows_html += ( + f"" + f"Grand Total:" + f"{fmt_price(total_sum, currency_for_total)}" + f"" + ) + + rec.merged_installation_kit_html = f""" + + + + + + + + + + + {rows_html} +
Material NameUoMUnit PriceTotal QtyTotal Price
+ """ + def _compute_merged_material_html(self): + for rec in self: + lines = self.env['sos_proposal_boq_material'].search([('ref_id', '=', rec.id)]) + + # Group by (component, uom, currency, unit_price) + agg = defaultdict(float) + name_map = {} + curr_map = {} + 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) + + 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: + rec.merged_material_html = "No Material items." + continue + + def fmt_price(amount, currency): + if currency: + return format_amount(self.env, amount, currency) + return f"{amount:.2f}" + + total_sum = 0.0 + rows_html = "" + for key, qty in rows_sorted: + if not qty: # Skip rows with qty = 0 + continue + total_price = qty * price_map[key] + total_sum += total_price + rows_html += ( + f"" + f"{name_map[key]}" + f"{key[1] or ''}" + f"{fmt_price(price_map[key], curr_map[key])}" + f"{qty:.2f}" + f"{fmt_price(total_price, curr_map[key])}" + f"" + ) + + if not rows_html: # If all rows were skipped + rec.merged_material_html = "No Material items." + continue + + # Add grand total row + currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False + rows_html += ( + f"" + f"Grand Total:" + f"{fmt_price(total_sum, currency_for_total)}" + f"" + ) + + rec.merged_material_html = f""" + + + + + + + + + + + {rows_html} +
Material NameUoMUnit PriceTotal QtyTotal Price
+ """ + def _compute_merged_sfg_html(self): + for rec in self: + lines = self.env['sos_proposal_boq_sfg'].search([('ref_id', '=', rec.id)]) + + # Group by (component, uom, currency, unit_price) + agg = defaultdict(float) + name_map = {} + curr_map = {} + 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) + + 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: + rec.merged_sfg_html = "No SFG items." + continue + + def fmt_price(amount, currency): + if currency: + return format_amount(self.env, amount, currency) + return f"{amount:.2f}" + + total_sum = 0.0 + rows_html = "" + for key, qty in rows_sorted: + if not qty: # Skip rows with qty = 0 + continue + total_price = qty * price_map[key] + total_sum += total_price + rows_html += ( + f"" + f"{name_map[key]}" + f"{key[1] or ''}" + f"{fmt_price(price_map[key], curr_map[key])}" + f"{qty:.2f}" + f"{fmt_price(total_price, curr_map[key])}" + f"" + ) + + if not rows_html: # If all rows were skipped + rec.merged_sfg_html = "No SFG items." + continue + + # Add grand total row + currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False + rows_html += ( + f"" + f"Grand Total:" + f"{fmt_price(total_sum, currency_for_total)}" + f"" + ) + + rec.merged_sfg_html = f""" + + + + + + + + + + + {rows_html} +
SFG NameUoMUnit PriceTotal QtyTotal Price
+ """ + def _compute_merged_fg_html(self): + for rec in self: + lines = self.env['sos_proposal_boq_fg'].search([('ref_id', '=', rec.id)]) + + # Group by (component, uom, currency, unit_price) + agg = defaultdict(float) + name_map = {} + curr_map = {} + 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) + + 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: + rec.merged_fg_html = "No FG items." + continue + + def fmt_price(amount, currency): + if currency: + return format_amount(self.env, amount, currency) + return f"{amount:.2f}" + + total_sum = 0.0 + rows_html = "" + for key, qty in rows_sorted: + if not qty: # Skip rows with qty = 0 + continue + total_price = qty * price_map[key] + total_sum += total_price + rows_html += ( + f"" + f"{name_map[key]}" + f"{key[1] or ''}" + f"{fmt_price(price_map[key], curr_map[key])}" + f"{qty:.2f}" + f"{fmt_price(total_price, curr_map[key])}" + f"" + ) + + if not rows_html: # If all rows were skipped + rec.merged_fg_html = "No FG items." + continue + + # Add grand total row + currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False + rows_html += ( + f"" + f"Grand Total:" + f"{fmt_price(total_sum, currency_for_total)}" + f"" + ) + + rec.merged_fg_html = f""" + + + + + + + + + + + {rows_html} +
FG NameUoMUnit PriceTotal QtyTotal Price
+ """ + @api.model def create(self, vals): res = super().create(vals) @@ -662,10 +1120,22 @@ class Battery_Installation_Requirement(models.Model): record.total_fg_cost = sum(line.total_price for line in record.line_ids_fg) def action_ce_esign_btn(self): body_html = f""" -

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_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 28e00a0..f70bcc1 100755 --- a/sos_sales/views/sos_proposal_boq_view.xml +++ b/sos_sales/views/sos_proposal_boq_view.xml @@ -40,6 +40,7 @@ +

Finished Goods

@@ -1654,115 +1655,38 @@
- - - -

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