Merge pull request 'Feature' (#8) from Feature into main
Reviewed-on: #8
This commit is contained in:
commit
838086034f
Binary file not shown.
|
|
@ -11,7 +11,7 @@
|
||||||
<field name="location"/>
|
<field name="location"/>
|
||||||
<field name="products_interested" string="Products" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
<field name="products_interested" string="Products" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||||
<field name="project_name"/>
|
<field name="project_name"/>
|
||||||
<field name="lead_generated_by"/>
|
<field name="lead_generated_by" string="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"/>
|
<field name="move_div_display" widget="boolean_toggle" readonly="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@
|
||||||
'report/mme_history_card_report.xml',
|
'report/mme_history_card_report.xml',
|
||||||
'report/sos_boq_labels.xml',
|
'report/sos_boq_labels.xml',
|
||||||
'report/shelflife_report.xml',
|
'report/shelflife_report.xml',
|
||||||
|
'report/sos_boq_report.xml',
|
||||||
'data/send_indent_plan_email_template.xml',
|
'data/send_indent_plan_email_template.xml',
|
||||||
'data/selection_item.xml'
|
'data/selection_item.xml'
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -65,6 +65,19 @@ class sos_deliverables_boq(models.Model):
|
||||||
verified_by = fields.Many2one('res.users', string='Prepared by')
|
verified_by = fields.Many2one('res.users', string='Prepared by')
|
||||||
verified_by_image = fields.Image(related="verified_by.signature_image",string='Prepared by Sign',readonly=True)
|
verified_by_image = fields.Image(related="verified_by.signature_image",string='Prepared by Sign',readonly=True)
|
||||||
verified_on = fields.Datetime(string="Prepared On")
|
verified_on = fields.Datetime(string="Prepared On")
|
||||||
|
ce_verified_by = fields.Many2one('res.users', string='CE Verified by')
|
||||||
|
ce_verified_by_image = fields.Image(related="ce_verified_by.signature_image",string='CE Verified by Sign',readonly=True)
|
||||||
|
ce_verified_on = fields.Datetime(string="CE Head Verified On")
|
||||||
|
scg_head_verified_by = fields.Many2one('res.users', string='SCG Verified by')
|
||||||
|
scg_head_verified_by_image = fields.Image(related="scg_head_verified_by.signature_image",string='SCG Verified by Sign',readonly=True)
|
||||||
|
scg_head_verified_on = fields.Datetime(string="SCG Head Verified On")
|
||||||
|
batter_or_cells=fields.Integer(string="Battery/Cells")
|
||||||
|
def action_report_boq(self):
|
||||||
|
try:
|
||||||
|
action = self.env.ref("sos_inventory.action_sos_deliverables_boq").report_action(self)
|
||||||
|
return action
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Failed to find report action: {e}")
|
||||||
def action_print_labels_btn(self):
|
def action_print_labels_btn(self):
|
||||||
try:
|
try:
|
||||||
action = self.env.ref("sos_inventory.action_report_labels").report_action(self)
|
action = self.env.ref("sos_inventory.action_report_labels").report_action(self)
|
||||||
|
|
@ -127,11 +140,44 @@ class sos_deliverables_boq(models.Model):
|
||||||
)
|
)
|
||||||
def action_prepared_esign_btn(self):
|
def action_prepared_esign_btn(self):
|
||||||
sequence_util = self.env['sos_common_scripts']
|
sequence_util = self.env['sos_common_scripts']
|
||||||
|
# Email part
|
||||||
|
body_html = f"""
|
||||||
|
<p>Below <b>Deliverables/BOQ</b> is waiting for your Approval</p>
|
||||||
|
"""
|
||||||
|
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(
|
return sequence_util.action_assign_signature(
|
||||||
self,
|
self,
|
||||||
'prepared_by',
|
'prepared_by',
|
||||||
'prepared_on'
|
'prepared_on'
|
||||||
)
|
)
|
||||||
|
def action_ce_verified_esign_btn(self):
|
||||||
|
sequence_util = self.env['sos_common_scripts']
|
||||||
|
# Email part
|
||||||
|
body_html = f"""
|
||||||
|
<p>Below <b>Deliverables/BOQ</b> is waiting for your Approval</p>
|
||||||
|
"""
|
||||||
|
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"""
|
||||||
|
<p>Below <b>Deliverables/BOQ</b> is waiting for your Approval</p>
|
||||||
|
"""
|
||||||
|
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):
|
def action_scg_esign_btn(self):
|
||||||
sequence_util = self.env['sos_common_scripts']
|
sequence_util = self.env['sos_common_scripts']
|
||||||
return sequence_util.action_assign_signature(
|
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")
|
ref_id = fields.Many2one('sos_deliverables_boq', string="Materials", ondelete="cascade")
|
||||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
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")
|
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')
|
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
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")
|
ref_id = fields.Many2one('sos_deliverables_boq', string="Materials", ondelete="cascade")
|
||||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
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")
|
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')
|
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,11 @@ class SOS_Dock_Audit(models.Model):
|
||||||
('BHMS 1.2V', 'BHMS 1.2V'),
|
('BHMS 1.2V', 'BHMS 1.2V'),
|
||||||
('BHMS 2V', 'BHMS 2V'),
|
('BHMS 2V', 'BHMS 2V'),
|
||||||
('BHMS 12V', 'BHMS 12V'),
|
('BHMS 12V', 'BHMS 12V'),
|
||||||
|
('BHMS 48V', 'BHMS 48V'),
|
||||||
|
('BMS-HV', 'BMS-HV'),
|
||||||
('BMS-LV 100A', 'BMS-LV 100A'),
|
('BMS-LV 100A', 'BMS-LV 100A'),
|
||||||
('BMS-LV 40A', 'BMS-LV 40A'),
|
('BMS-LV 40A', 'BMS-LV 40A'),
|
||||||
|
('SBMS 55A', 'SBMS 55A'),
|
||||||
('MC 250W', 'MC 250W'),
|
('MC 250W', 'MC 250W'),
|
||||||
('HeartTarang', 'HeartTarang')
|
('HeartTarang', 'HeartTarang')
|
||||||
],
|
],
|
||||||
|
|
@ -384,6 +387,7 @@ class sos_dock_audit_Line_Material(models.Model):
|
||||||
|
|
||||||
ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade")
|
ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade")
|
||||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
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")
|
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')
|
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
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")
|
ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade")
|
||||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
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")
|
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')
|
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
||||||
|
|
|
||||||
|
|
@ -51,21 +51,45 @@ class FIR_Only(models.Model):
|
||||||
stores_received_on = fields.Date(string="Stores Received On")
|
stores_received_on = fields.Date(string="Stores Received On")
|
||||||
ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Reference (If any Rejected)")
|
ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Reference (If any Rejected)")
|
||||||
customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name", required=True)
|
customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name", required=True)
|
||||||
|
|
||||||
remarks = fields.Text(string="Remarks")
|
remarks = fields.Text(string="Remarks")
|
||||||
test_log = fields.Binary("Test Log", required=False, attachment=True)
|
test_log = fields.Binary("Test Log", required=False, attachment=True)
|
||||||
test_log_filename = fields.Char("Test Log Filename")
|
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')
|
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)
|
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 = fields.Binary("SD Card Data", required=False, attachment=True)
|
||||||
sd_card_data_filename = fields.Char("SD Card Data Filename")
|
sd_card_data_filename = fields.Char("SD Card Data Filename")
|
||||||
|
|
||||||
cloud_data = fields.Binary("Cloud Data", required=False, attachment=True)
|
cloud_data = fields.Binary("Cloud Data", required=False, attachment=True)
|
||||||
cloud_data_filename = fields.Char("Cloud Data Filename")
|
cloud_data_filename = fields.Char("Cloud Data Filename")
|
||||||
|
|
||||||
firmware_data = fields.Binary("Firmware Data", required=False, attachment=True)
|
firmware_data = fields.Binary("Firmware Data", required=False, attachment=True)
|
||||||
firmware_data_filename = fields.Char("Firmware Data Filename")
|
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')
|
@api.onchange('batch_size')
|
||||||
def _onchange_batch_size(self):
|
def _onchange_batch_size(self):
|
||||||
if self._origin and self.batch_size is not False:
|
if self._origin and self.batch_size is not False:
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,30 @@ class sos_inventory_customers(models.Model):
|
||||||
new_name = (vals.get('customer_name') or '').lower()
|
new_name = (vals.get('customer_name') or '').lower()
|
||||||
|
|
||||||
if new_name and len(new_name) >= 5:
|
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:
|
for customer in existing_customers:
|
||||||
existing_name = (customer.customer_name or '').lower()
|
existing_name = (customer.customer_name or '').lower()
|
||||||
|
existing_name_clean = clean_name(existing_name)
|
||||||
|
|
||||||
# Check all substrings of length 5 or more
|
# Check all substrings of length 7 or more
|
||||||
for i in range(len(new_name) - 4):
|
for i in range(len(new_name_clean) - 6):
|
||||||
substring = new_name[i:i+5]
|
substring = new_name_clean[i:i+7]
|
||||||
if substring in existing_name:
|
if substring in existing_name_clean:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
f"A customer with a similar name already exists: '{customer.customer_name}' "
|
f"A customer with a similar name already exists: '{customer.customer_name}' "
|
||||||
f"(matched substring: '{substring}')"
|
f"(matched substring: '{substring}')"
|
||||||
)
|
)
|
||||||
|
|
||||||
return super(sos_inventory_customers, self).create(vals)
|
return super().create(vals)
|
||||||
|
|
@ -217,6 +217,11 @@ class SOS_IR(models.Model):
|
||||||
for item in self.line_ids_material:
|
for item in self.line_ids_material:
|
||||||
# Fetch the component related to the current item
|
# Fetch the component related to the current item
|
||||||
component = self.env['sos_material'].browse(item.component_id.id)
|
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":
|
if component.shelf_life == "yes":
|
||||||
shelflife_line_values = {
|
shelflife_line_values = {
|
||||||
'ir_ref_no':self.id,
|
'ir_ref_no':self.id,
|
||||||
|
|
@ -301,6 +306,10 @@ class SOS_IR(models.Model):
|
||||||
for item in self.line_ids_sfg:
|
for item in self.line_ids_sfg:
|
||||||
# PO update part
|
# PO update part
|
||||||
sfg_component = self.env['sos_sfg'].browse(item.component_id.id)
|
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_planned_at == "outsource":
|
||||||
if self.wo_no:
|
if self.wo_no:
|
||||||
if not self.orr_no:
|
if not self.orr_no:
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class SOS_SalesOrder(models.Model):
|
||||||
],
|
],
|
||||||
string="Product Name",required=True)
|
string="Product Name",required=True)
|
||||||
line_ids = fields.One2many('sos_sales_order_line', 'ref_id',copy=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")
|
lead_time = fields.Datetime(string="Lead Time")
|
||||||
customer_po_no = fields.Char(string="PO No")
|
customer_po_no = fields.Char(string="PO No")
|
||||||
customer_po_date = fields.Datetime(string="PO Date")
|
customer_po_date = fields.Datetime(string="PO Date")
|
||||||
|
|
@ -58,7 +58,7 @@ class SOS_SalesOrder(models.Model):
|
||||||
'sales_id':self.id,
|
'sales_id':self.id,
|
||||||
'fg_name':self.fg_name,
|
'fg_name':self.fg_name,
|
||||||
'quantity':self.qty,
|
'quantity':self.qty,
|
||||||
'customer_name':self.customer_name,
|
'customer_name':self.customer_name.customer_name,
|
||||||
'lead_time':self.lead_time,
|
'lead_time':self.lead_time,
|
||||||
'customer_po_no':self.customer_po_no,
|
'customer_po_no':self.customer_po_no,
|
||||||
'customer_po_date':self.customer_po_date
|
'customer_po_date':self.customer_po_date
|
||||||
|
|
@ -109,15 +109,15 @@ class SOS_SalesOrder(models.Model):
|
||||||
sequence_util = self.env['sos_common_scripts']
|
sequence_util = self.env['sos_common_scripts']
|
||||||
return sequence_util.generate_sequence('sos_sales_order','SALES', 'order_id')
|
return sequence_util.generate_sequence('sos_sales_order','SALES', 'order_id')
|
||||||
|
|
||||||
@api.model
|
# @api.model
|
||||||
def create(self, vals):
|
# def create(self, vals):
|
||||||
customer_name = vals.get('customer_name')
|
# customer_name = vals.get('customer_name')
|
||||||
if customer_name:
|
# if customer_name:
|
||||||
existing = self.env['sos_inventory_customers'].search([('customer_name', '=', customer_name)], limit=1)
|
# existing = self.env['sos_inventory_customers'].search([('customer_name', '=', customer_name)], limit=1)
|
||||||
if not existing:
|
# if not existing:
|
||||||
self.env['sos_inventory_customers'].create({'customer_name': customer_name})
|
# 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):
|
class SOS_SalesOrder_Line(models.Model):
|
||||||
_name = 'sos_sales_order_line'
|
_name = 'sos_sales_order_line'
|
||||||
|
|
|
||||||
|
|
@ -86,131 +86,192 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
||||||
|
|
||||||
<t t-set="counter" t-value="0"/>
|
<t t-set="counter" t-value="0"/>
|
||||||
<t t-foreach="range(o.master_quantity)" t-as="line_items">
|
<t t-foreach="range(o.master_quantity)" t-as="line_items">
|
||||||
<t t-set="counter" t-value="counter + 1"/>
|
<t t-set="counter" t-value="counter + 1"/>
|
||||||
<div class="label-set">
|
<div class="label-set">
|
||||||
<!-- Combine all tables into a single list -->
|
<!-- Combine all tables into a single list -->
|
||||||
<t t-set="all_tables" t-value="[]"/>
|
<t t-set="all_tables" t-value="[]"/>
|
||||||
<!-- Add FG tables -->
|
<!-- Add FG tables -->
|
||||||
<t t-foreach="o.line_ids_fg or []" t-as="fg_item">
|
<t t-set="fg_counter" t-value="0"/>
|
||||||
<t t-set="fg_index_safe" t-value="fg_index if fg_index is not None else 0"/>
|
<t t-foreach="o.line_ids_fg or []" t-as="fg_item">
|
||||||
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_index_safe + 1)]"/>
|
<t t-set="fg_counter" t-value="fg_counter + 1"/>
|
||||||
</t>
|
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_counter)]"/>
|
||||||
<!-- Add SFG tables -->
|
</t>
|
||||||
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item">
|
<!-- Add SFG tables -->
|
||||||
<t t-set="sfg_index_safe" t-value="sfg_index if sfg_index is not None else 0"/>
|
<t t-set="sfg_counter" t-value="0"/>
|
||||||
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_index_safe + 1)]"/>
|
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item">
|
||||||
</t>
|
<t t-set="sfg_counter" t-value="sfg_counter + 1"/>
|
||||||
<!-- Add Materials tables -->
|
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_counter)]"/>
|
||||||
<t t-foreach="o.line_ids_material or []" t-as="material_item">
|
</t>
|
||||||
<t t-set="material_index_safe" t-value="material_index if material_index is not None else 0"/>
|
<!-- Add Materials tables -->
|
||||||
<t t-set="all_tables" t-value="all_tables + [(3, material_item, material_index_safe + 1)]"/>
|
<t t-set="material_counter" t-value="0"/>
|
||||||
</t>
|
<t t-foreach="o.line_ids_material or []" t-as="material_item">
|
||||||
|
<t t-set="material_counter" t-value="material_counter + 1"/>
|
||||||
|
<t t-set="all_tables" t-value="all_tables + [(3, material_item, material_counter)]"/>
|
||||||
|
</t>
|
||||||
|
<!-- Add Installation Kit tables -->
|
||||||
|
<t t-set="installation_kit_counter" t-value="0"/>
|
||||||
|
<t t-foreach="o.line_ids_installation_kit or []" t-as="installation_kit_item">
|
||||||
|
<t t-set="installation_kit_counter" t-value="installation_kit_counter + 1"/>
|
||||||
|
<t t-set="all_tables" t-value="all_tables + [(4, installation_kit_item, installation_kit_counter)]"/>
|
||||||
|
</t>
|
||||||
|
<!-- Add Miscelleneous tables -->
|
||||||
|
<t t-set="miscellaneous_counter" t-value="0"/>
|
||||||
|
<t t-foreach="o.line_ids_miscellaneous or []" t-as="miscellaneous_item">
|
||||||
|
<t t-set="miscellaneous_counter" t-value="miscellaneous_counter + 1"/>
|
||||||
|
<t t-set="all_tables" t-value="all_tables + [(5, miscellaneous_item, miscellaneous_counter)]"/>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Process all tables in pairs -->
|
|
||||||
<t t-foreach="range(0, len(all_tables), 2)" t-as="pair_index">
|
|
||||||
<div class="row">
|
|
||||||
<!-- First table in the pair -->
|
|
||||||
<div class="col-6">
|
|
||||||
<t t-set="table_info" t-value="all_tables[pair_index]"/>
|
|
||||||
<t t-set="category" t-value="table_info[0]"/>
|
|
||||||
<t t-set="item" t-value="table_info[1]"/>
|
|
||||||
<t t-set="index" t-value="table_info[2]"/>
|
|
||||||
<div class="table-container">
|
|
||||||
<table class="label-table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="column">Set No</td>
|
|
||||||
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="column">Battery</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="column">No</td>
|
|
||||||
<td><t t-esc="category"/>.<t t-esc="index"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="column">Name</td>
|
|
||||||
<td><t t-esc="item.component_id.name or 'N/A'"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="column">UOM</td>
|
|
||||||
<td><t t-esc="item.uom or 'N/A'"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="column">Qty</td>
|
|
||||||
<td><t t-esc="item.singet_set_qty or 'N/A'"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="column">S.No</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Second table in the pair (if exists) -->
|
<!-- Process all tables in pairs -->
|
||||||
<div class="col-6">
|
<t t-foreach="range(0, len(all_tables), 2)" t-as="pair_index">
|
||||||
<t t-if="pair_index + 1 < len(all_tables)">
|
<div class="row">
|
||||||
<t t-set="table_info" t-value="all_tables[pair_index + 1]"/>
|
<!-- First table in the pair -->
|
||||||
<t t-set="category" t-value="table_info[0]"/>
|
<div class="col-6">
|
||||||
<t t-set="item" t-value="table_info[1]"/>
|
<t t-set="table_info" t-value="all_tables[pair_index]"/>
|
||||||
<t t-set="index" t-value="table_info[2]"/>
|
<t t-set="category" t-value="table_info[0]"/>
|
||||||
<div class="table-container">
|
<t t-set="item" t-value="table_info[1]"/>
|
||||||
<table class="label-table">
|
<t t-set="index" t-value="table_info[2]"/>
|
||||||
<tbody>
|
|
||||||
<tr>
|
<div class="table-container">
|
||||||
<td class="column">Set No</td>
|
<table class="label-table">
|
||||||
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
|
<tbody>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td class="column">Set No</td>
|
||||||
<td class="column">Battery</td>
|
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
|
||||||
<td></td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td class="column">Battery</td>
|
||||||
<td class="column">No</td>
|
<td><t t-esc="o.batter_or_cells"/></td>
|
||||||
<td><t t-esc="category"/>.<t t-esc="index"/></td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td class="column">No</td>
|
||||||
<td class="column">Name</td>
|
<td><t t-esc="category"/>.<t t-esc="index"/></td>
|
||||||
<td><t t-esc="item.component_id.name or 'N/A'"/></td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td class="column">Name</td>
|
||||||
<td class="column">UOM</td>
|
<td>
|
||||||
<td><t t-esc="item.uom or 'N/A'"/></td>
|
<t t-if="'component_id' in item">
|
||||||
</tr>
|
<t t-esc="item.component_id.name or item.component_id.part_no or item.name or 'N/A'"/>
|
||||||
<tr>
|
</t>
|
||||||
<td class="column">Qty</td>
|
<t t-else="">
|
||||||
<td><t t-esc="item.singet_set_qty or 'N/A'"/></td>
|
<t t-esc="item.name or 'N/A'"/>
|
||||||
</tr>
|
</t>
|
||||||
<tr>
|
</td>
|
||||||
<td class="column">S.No</td>
|
</tr>
|
||||||
<td></td>
|
<tr>
|
||||||
</tr>
|
<td class="column">UOM</td>
|
||||||
</tbody>
|
<td>
|
||||||
</table>
|
<t t-if="'uom' in item">
|
||||||
</div>
|
<t t-esc="item.uom or 'N/A'"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<div class="empty-col"></div>
|
<t t-esc="'N/A'"/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</td>
|
||||||
</div> <!-- Close row -->
|
</tr>
|
||||||
</t>
|
<tr>
|
||||||
|
<td class="column">Qty</td>
|
||||||
|
<td>
|
||||||
|
<t t-if="'singet_set_qty' in item">
|
||||||
|
<t t-esc="item.singet_set_qty or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<t t-esc="item.quantity or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="column">S.No</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Second table in the pair (if exists) -->
|
||||||
|
<div class="col-6">
|
||||||
|
<t t-if="pair_index + 1 < len(all_tables)">
|
||||||
|
<t t-set="table_info" t-value="all_tables[pair_index + 1]"/>
|
||||||
|
<t t-set="category" t-value="table_info[0]"/>
|
||||||
|
<t t-set="item" t-value="table_info[1]"/>
|
||||||
|
<t t-set="index" t-value="table_info[2]"/>
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="label-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="column">Set No</td>
|
||||||
|
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column">Battery</td>
|
||||||
|
<td><t t-esc="o.batter_or_cells"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column">No</td>
|
||||||
|
<td><t t-esc="category"/>.<t t-esc="index"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column">Name</td>
|
||||||
|
<td>
|
||||||
|
<t t-if="'component_id' in item">
|
||||||
|
<t t-esc="item.component_id.name or item.component_id.part_no or item.name or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<t t-esc="item.name or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column">UOM</td>
|
||||||
|
<td>
|
||||||
|
<t t-if="'uom' in item">
|
||||||
|
<t t-esc="item.uom or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<t t-esc="'N/A'"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column">Qty</td>
|
||||||
|
<td>
|
||||||
|
<t t-if="'singet_set_qty' in item">
|
||||||
|
<t t-esc="item.singet_set_qty or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<t t-esc="item.quantity or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="column">S.No</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<div class="empty-col"></div>
|
||||||
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</div> <!-- Close row -->
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</div>
|
||||||
</template>
|
</t>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="report_boq">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<div class="page">
|
||||||
|
<h2>Deliverables/BOQ Report</h2>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
|
||||||
|
<tr><td class="column">Customer Name</td><td><t t-esc="o.customer_name"/></td><td class="column">Location</td><td><t t-esc="o.customer_location"/></td></tr>
|
||||||
|
|
||||||
|
<tr><td class="column">Product Name</td><td><t t-esc="o.fg_name"/></td><td class="column">Quantity</td><td><t t-esc="o.quantity"/></td></tr>
|
||||||
|
</table>
|
||||||
|
<br></br>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="6" class="column" style="background-color:#ccc">Finished Goods</td></tr>
|
||||||
|
<tr class="column">
|
||||||
|
<td>S.No</td>
|
||||||
|
<td>Name</td>
|
||||||
|
<td>UOM</td>
|
||||||
|
<td>Single set Qty</td>
|
||||||
|
<td>Total Set</td>
|
||||||
|
<td>Qty</td>
|
||||||
|
</tr>
|
||||||
|
<t t-foreach="enumerate(o.line_ids_fg)" t-as="it">
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="'1.%s' % (it[0] + 1)"/></td>
|
||||||
|
<td><t t-esc="it[1].component_id.name"/></td>
|
||||||
|
<td><t t-esc="it[1].uom"/></td>
|
||||||
|
<td><t t-esc="it[1].singet_set_qty"/></td>
|
||||||
|
<td><t t-esc="it[1].total_set"/></td>
|
||||||
|
<td><t t-esc="it[1].quantity"/></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
<tr><td colspan="6" class="column" style="background-color:#ccc">Semi-Finished Goods</td></tr>
|
||||||
|
<t t-foreach="enumerate(o.line_ids_sfg)" t-as="it">
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="'2.%s' % (it[0] + 1)"/></td>
|
||||||
|
<td><t t-esc="it[1].component_id.name"/></td>
|
||||||
|
<td><t t-esc="it[1].uom"/></td>
|
||||||
|
<td><t t-esc="it[1].singet_set_qty"/></td>
|
||||||
|
<td><t t-esc="it[1].total_set"/></td>
|
||||||
|
<td><t t-esc="it[1].quantity"/></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
<tr><td colspan="6" class="column" style="background-color:#ccc">Materials</td></tr>
|
||||||
|
<t t-foreach="enumerate(o.line_ids_material)" t-as="it">
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="'3.%s' % (it[0] + 1)"/></td>
|
||||||
|
<td><t t-esc="it[1].component_id.part_no"/></td>
|
||||||
|
<td><t t-esc="it[1].uom"/></td>
|
||||||
|
<td><t t-esc="it[1].singet_set_qty"/></td>
|
||||||
|
<td><t t-esc="it[1].total_set"/></td>
|
||||||
|
<td><t t-esc="it[1].quantity"/></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
<tr><td colspan="6" class="column" style="background-color:#ccc">Installation Kit</td></tr>
|
||||||
|
<t t-foreach="enumerate(o.line_ids_installation_kit)" t-as="it">
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="'4.%s' % (it[0] + 1)"/></td>
|
||||||
|
<td><t t-esc="it[1].component_id.part_no"/></td>
|
||||||
|
<td><t t-esc="it[1].uom"/></td>
|
||||||
|
<td><t t-esc="it[1].singet_set_qty"/></td>
|
||||||
|
<td><t t-esc="it[1].total_set"/></td>
|
||||||
|
<td><t t-esc="it[1].quantity"/></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
<tr><td colspan="6" class="column" style="background-color:#ccc">Miscellaneous</td></tr>
|
||||||
|
<tr class="column">
|
||||||
|
<td>S.No</td>
|
||||||
|
<td>Name</td>
|
||||||
|
<td colspan="4">Qty</td>
|
||||||
|
</tr>
|
||||||
|
<t t-foreach="enumerate(o.line_ids_miscellaneous)" t-as="it">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="'5.%s' % (it[0] + 1)"/></td>
|
||||||
|
<td><t t-esc="it[1].name"/></td>
|
||||||
|
<td colspan="4"><t t-esc="it[1].quantity"/></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<record id="action_sos_deliverables_boq" model="ir.actions.report">
|
||||||
|
<field name="name">BOQ Report</field>
|
||||||
|
<field name="model">sos_deliverables_boq</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">sos_inventory.report_boq</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
@ -45,10 +45,7 @@
|
||||||
<td class="column">Sampling Size</td><td><t t-esc="o.approved_qty"/></td>
|
<td class="column">Sampling Size</td><td><t t-esc="o.approved_qty"/></td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td class="column">Serial No</td><td colspan="3"><t t-esc="o.serial_no"/></td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -56,10 +53,7 @@
|
||||||
<td class="column">Sampling Size</td><td><t t-esc="o.sampling_size"/></td>
|
<td class="column">Sampling Size</td><td><t t-esc="o.sampling_size"/></td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td class="column">Serial No</td><td colspan="3"><t t-esc="o.serial_no"/></td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</t>
|
</t>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -74,7 +68,21 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br></br>
|
<table class="table table-bordered" style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr style="background-color:#ccc"><td colspan="7" class="column"><h5>Serial No's</h5></td></tr>
|
||||||
|
|
||||||
|
|
||||||
|
<t t-set="lines" t-value="o.serial_no_line_ids"/>
|
||||||
|
<t t-foreach="range(0, len(lines), 2)" t-as="i">
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="lines[i].serial_no or ''"/></td>
|
||||||
|
<td><t t-esc="(lines[i+1].serial_no if i+1 < len(lines) else '')"/></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<!-- <table class="table table-bordered">
|
<!-- <table class="table table-bordered">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr style="background-color:#ccc"><td colspan="7" class="column"><h5>Summary Report</h5></td></tr>
|
<tr style="background-color:#ccc"><td colspan="7" class="column"><h5>Summary Report</h5></td></tr>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ access_ir_act_window_sales_user,Access Action Window for Sales Users,base.model_
|
||||||
access_ir_act_window_healthcare_user,Access Action Window for Health Care Users,base.model_ir_actions_act_window,sos_inventory.sos_healthcare_user,1,0,0,0
|
access_ir_act_window_healthcare_user,Access Action Window for Health Care Users,base.model_ir_actions_act_window,sos_inventory.sos_healthcare_user,1,0,0,0
|
||||||
access_ir_act_window_sos_inside_sales_user,Access Action Window for Inside sales Users,base.model_ir_actions_act_window,sos_inventory.sos_inside_sales_user,1,0,0,0
|
access_ir_act_window_sos_inside_sales_user,Access Action Window for Inside sales Users,base.model_ir_actions_act_window,sos_inventory.sos_inside_sales_user,1,0,0,0
|
||||||
access_ir_act_window_hr_user,Access Action Window for Human Care Resources Users,base.model_ir_actions_act_window,sos_inventory.sos_hr_user,1,0,0,0
|
access_ir_act_window_hr_user,Access Action Window for Human Care Resources Users,base.model_ir_actions_act_window,sos_inventory.sos_hr_user,1,0,0,0
|
||||||
|
access_ir_act_window_sos_marketing_user,Access Action Window for Marketing Users,base.model_ir_actions_act_window,sos_inventory.sos_marketing_user,1,0,0,0
|
||||||
access_sos_return_fir,sos_return_fir access,model_sos_return_fir,base.group_user,1,1,1,1
|
access_sos_return_fir,sos_return_fir access,model_sos_return_fir,base.group_user,1,1,1,1
|
||||||
access_sos_return_fir_line,sos_return_fir_line access,model_sos_return_fir_line,base.group_user,1,1,1,1
|
access_sos_return_fir_line,sos_return_fir_line access,model_sos_return_fir_line,base.group_user,1,1,1,1
|
||||||
access_sos_return_calibration_devices_fir,sos_return_calibration_devices_fir access,model_sos_return_calibration_devices_fir,base.group_user,1,1,1,1
|
access_sos_return_calibration_devices_fir,sos_return_calibration_devices_fir access,model_sos_return_calibration_devices_fir,base.group_user,1,1,1,1
|
||||||
|
|
|
||||||
|
|
|
@ -43,10 +43,12 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Model Form">
|
<form string="Model Form">
|
||||||
<header>
|
<header>
|
||||||
<button class="btn btn-primary" type="object"
|
<button class="btn btn-primary" type="object"
|
||||||
name="action_print_labels_btn"><i class="fa fa-print"></i> Print Labels</button>
|
name="action_print_labels_btn"><i class="fa fa-print"></i> Print Labels</button>
|
||||||
|
|
||||||
|
|
||||||
|
<button name="action_report_boq" string="Report"
|
||||||
|
type="object" class="btn-primary"/>
|
||||||
</header>
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
|
|
||||||
|
|
@ -71,7 +73,7 @@
|
||||||
<field name="show_ev_fields" invisible="1"/>
|
<field name="show_ev_fields" invisible="1"/>
|
||||||
<field name="master_quantity" invisible="show_ev_fields == False"/>
|
<field name="master_quantity" invisible="show_ev_fields == False"/>
|
||||||
<field name="slave_quantity" invisible="show_ev_fields == False"/>
|
<field name="slave_quantity" invisible="show_ev_fields == False"/>
|
||||||
|
<field name="batter_or_cells"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
|
|
@ -108,6 +110,7 @@
|
||||||
<field name="line_ids_material">
|
<field name="line_ids_material">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="component_id"/>
|
<field name="component_id"/>
|
||||||
|
<field name="display_name"/>
|
||||||
<field name="add_production_cost" widget="boolean_toggle"/>
|
<field name="add_production_cost" widget="boolean_toggle"/>
|
||||||
<field name="is_spare" widget="boolean_toggle"/>
|
<field name="is_spare" widget="boolean_toggle"/>
|
||||||
|
|
||||||
|
|
@ -122,6 +125,7 @@
|
||||||
<field name="line_ids_installation_kit">
|
<field name="line_ids_installation_kit">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="component_id"/>
|
<field name="component_id"/>
|
||||||
|
<field name="display_name"/>
|
||||||
<field name="uom"/>
|
<field name="uom"/>
|
||||||
<field name="singet_set_qty"/>
|
<field name="singet_set_qty"/>
|
||||||
<field name="total_set"/>
|
<field name="total_set"/>
|
||||||
|
|
@ -285,13 +289,55 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4"></div>
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
|
<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>CE Head Verified By</b>
|
||||||
|
<br></br><br></br>
|
||||||
|
<button string="Approve" invisible="ce_verified_by_image" class="btn-primary custom_btn" type="object" name="action_ce_verified_esign_btn"></button>
|
||||||
|
</td>
|
||||||
|
<td><field name="ce_verified_by_image" widget="image"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr invisible="ce_verified_by_image == False">
|
||||||
|
<td style="padding: 8px;" class="column"><b>Verified On</b></td>
|
||||||
|
<td><field name="ce_verified_on" readonly="1"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr invisible="ce_verified_by_image == False">
|
||||||
|
<td style="padding: 8px;" class="column"><b>Verified By</b></td>
|
||||||
|
<td><field name="ce_verified_by" readonly="1"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<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>SCG Head Verified By</b>
|
||||||
|
<br></br><br></br>
|
||||||
|
<button string="Approve" invisible="scg_head_verified_by_image" class="btn-primary custom_btn" type="object" name="action_scg_head_verified_esign_btn"></button>
|
||||||
|
</td>
|
||||||
|
<td><field name="scg_head_verified_by_image" widget="image"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr invisible="scg_head_verified_by_image == False">
|
||||||
|
<td style="padding: 8px;" class="column"><b>Verified On</b></td>
|
||||||
|
<td><field name="scg_head_verified_on" readonly="1"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr invisible="scg_head_verified_by_image == False">
|
||||||
|
<td style="padding: 8px;" class="column"><b>Verified By</b></td>
|
||||||
|
<td><field name="scg_head_verified_by" readonly="1"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3"></div>
|
||||||
|
<div class="col-6">
|
||||||
<!-- Second Table -->
|
<!-- Second Table -->
|
||||||
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
|
<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;">
|
<tr style="border-bottom: solid 1px #ccc;">
|
||||||
<td style="padding: 8px;" class="column"><b>Verified By</b>
|
<td style="padding: 8px;" class="column"><b>QA Verified By</b>
|
||||||
<br></br><br></br>
|
<br></br><br></br>
|
||||||
<button string="Approve" invisible="verified_by_image" class="btn-primary custom_btn" type="object" name="action_verified_esign_btn"></button>
|
<button string="Approve" invisible="verified_by_image" class="btn-primary custom_btn" type="object" name="action_verified_esign_btn"></button>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -307,6 +353,8 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-3"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</sheet>
|
</sheet>
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@
|
||||||
<field name="line_ids_material" >
|
<field name="line_ids_material" >
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="component_id"/>
|
<field name="component_id"/>
|
||||||
|
<field name="display_name"/>
|
||||||
<field name="is_spare" widget="boolean_toggle"/>
|
<field name="is_spare" widget="boolean_toggle"/>
|
||||||
|
|
||||||
<field name="uom"/>
|
<field name="uom"/>
|
||||||
|
|
@ -122,6 +123,7 @@
|
||||||
<field name="line_ids_installation_kit" >
|
<field name="line_ids_installation_kit" >
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="component_id"/>
|
<field name="component_id"/>
|
||||||
|
<field name="display_name"/>
|
||||||
<field name="uom"/>
|
<field name="uom"/>
|
||||||
<field name="singet_set_qty"/>
|
<field name="singet_set_qty"/>
|
||||||
<field name="total_set"/>
|
<field name="total_set"/>
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,12 @@
|
||||||
<field name="fg_name" widget="many2many_tags"/>
|
<field name="fg_name" widget="many2many_tags"/>
|
||||||
<field name="product_type"/>
|
<field name="product_type"/>
|
||||||
|
|
||||||
<field name="customer_name"/>
|
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
|
<field name="customer_name"/>
|
||||||
<field name="batch_No"/>
|
<field name="batch_No"/>
|
||||||
<field name="serial_no"/>
|
<!-- <field name="serial_no"/> -->
|
||||||
<field name="mfg_date"/>
|
<field name="mfg_date"/>
|
||||||
<field name="sampling_size"/>
|
<field name="sampling_size"/>
|
||||||
<field name="remarks"/>
|
<field name="remarks"/>
|
||||||
|
|
@ -61,6 +62,13 @@
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
<page string="Serial No's">
|
<page string="Serial No's">
|
||||||
|
<table class="table_custom">
|
||||||
|
<thead><tr><td colspan="3"><b>Parse Serial No's</b></td></tr></thead>
|
||||||
|
<tr><td colspan="3"><field name="serial_no_parse"/></td><td><button type="object" name="parse_serial_nos" class="btn btn-primary" string="Parse"></button></td></tr>
|
||||||
|
</table>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
<field name="serial_no_line_ids">
|
<field name="serial_no_line_ids">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="serial_no"/>
|
<field name="serial_no"/>
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
<page string="Accessories">
|
<page string="Product / Accessories">
|
||||||
|
|
||||||
<div class="new_header">Finished Goods</div>
|
<div class="new_header">Finished Goods</div>
|
||||||
<field name="line_ids_fg">
|
<field name="line_ids_fg">
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,21 @@ class sos_marketing(models.Model):
|
||||||
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_inside_sales_user').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_inside_sales_user').ids)])
|
||||||
move_div_display = fields.Boolean(default=False,string="Moved to Sales")
|
move_div_display = fields.Boolean(default=False,string="Moved to Sales")
|
||||||
moved_on = fields.Date(string="Moved On")
|
moved_on = fields.Date(string="Moved On")
|
||||||
|
source = fields.Selection(
|
||||||
|
[
|
||||||
|
('Expo', 'Expo'),
|
||||||
|
('Conference', 'Conference'),
|
||||||
|
('LinkedIn Groups', 'LinkedIn Groups'),
|
||||||
|
('LinkedIn Poll', 'LinkedIn Poll'),
|
||||||
|
('Direct Company', 'Direct Company'),
|
||||||
|
('Expo [Paricipated]', 'Expo [Paricipated]')
|
||||||
|
|
||||||
|
],
|
||||||
|
string="Source",default="Expo")
|
||||||
@api.onchange('expo_option')
|
@api.onchange('expo_option')
|
||||||
def _onchange_expo(self):
|
def _onchange_expo(self):
|
||||||
if self.expo_option:
|
if self.expo_option:
|
||||||
|
self.source = "Expo"
|
||||||
self.conference_option = False
|
self.conference_option = False
|
||||||
self.linkedin_groups_option = False
|
self.linkedin_groups_option = False
|
||||||
self.linkedin_poll_option = False
|
self.linkedin_poll_option = False
|
||||||
|
|
@ -53,6 +65,7 @@ class sos_marketing(models.Model):
|
||||||
@api.onchange('conference_option')
|
@api.onchange('conference_option')
|
||||||
def _onchange_conference(self):
|
def _onchange_conference(self):
|
||||||
if self.conference_option:
|
if self.conference_option:
|
||||||
|
self.source = "Conference"
|
||||||
self.expo_option = False
|
self.expo_option = False
|
||||||
self.linkedin_groups_option = False
|
self.linkedin_groups_option = False
|
||||||
self.linkedin_poll_option = False
|
self.linkedin_poll_option = False
|
||||||
|
|
@ -62,6 +75,7 @@ class sos_marketing(models.Model):
|
||||||
@api.onchange('linkedin_groups_option')
|
@api.onchange('linkedin_groups_option')
|
||||||
def _onchange_linkedin_groups(self):
|
def _onchange_linkedin_groups(self):
|
||||||
if self.linkedin_groups_option:
|
if self.linkedin_groups_option:
|
||||||
|
self.source = "LinkedIn Groups"
|
||||||
self.expo_option = False
|
self.expo_option = False
|
||||||
self.conference_option = False
|
self.conference_option = False
|
||||||
self.linkedin_poll_option = False
|
self.linkedin_poll_option = False
|
||||||
|
|
@ -71,6 +85,7 @@ class sos_marketing(models.Model):
|
||||||
@api.onchange('linkedin_poll_option')
|
@api.onchange('linkedin_poll_option')
|
||||||
def _onchange_linkedin_poll(self):
|
def _onchange_linkedin_poll(self):
|
||||||
if self.linkedin_poll_option:
|
if self.linkedin_poll_option:
|
||||||
|
self.source = "LinkedIn Poll"
|
||||||
self.expo_option = False
|
self.expo_option = False
|
||||||
self.conference_option = False
|
self.conference_option = False
|
||||||
self.linkedin_groups_option = False
|
self.linkedin_groups_option = False
|
||||||
|
|
@ -80,6 +95,7 @@ class sos_marketing(models.Model):
|
||||||
@api.onchange('direct_company')
|
@api.onchange('direct_company')
|
||||||
def _onchange_direct_company(self):
|
def _onchange_direct_company(self):
|
||||||
if self.direct_company:
|
if self.direct_company:
|
||||||
|
self.source = "Direct Company"
|
||||||
self.expo_option = False
|
self.expo_option = False
|
||||||
self.conference_option = False
|
self.conference_option = False
|
||||||
self.linkedin_groups_option = False
|
self.linkedin_groups_option = False
|
||||||
|
|
@ -89,6 +105,7 @@ class sos_marketing(models.Model):
|
||||||
@api.onchange('expo_participated_option')
|
@api.onchange('expo_participated_option')
|
||||||
def _onchange_expo_participated(self):
|
def _onchange_expo_participated(self):
|
||||||
if self.expo_participated_option:
|
if self.expo_participated_option:
|
||||||
|
self.source = "Expo [Paricipated]"
|
||||||
self.expo_option = False
|
self.expo_option = False
|
||||||
self.conference_option = False
|
self.conference_option = False
|
||||||
self.linkedin_groups_option = False
|
self.linkedin_groups_option = False
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,25 @@
|
||||||
<field name="view_mode">tree,form,kanban</field>
|
<field name="view_mode">tree,form,kanban</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="sos_marketing_leads_view_panel" model="ir.ui.view">
|
||||||
|
<field name="name">sos_marketing_leads.search</field>
|
||||||
|
<field name="model">sos_marketing_leads</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
|
||||||
|
<searchpanel>
|
||||||
|
|
||||||
|
<field name="source" string="Source" icon="fa-list-ul" enable_counters="1"/>
|
||||||
|
|
||||||
|
</searchpanel>
|
||||||
|
<!-- Add fields to search on -->
|
||||||
|
<field name="company_name" string="Company Name"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="sos_marketing_leads_view_tree" model="ir.ui.view">
|
<record id="sos_marketing_leads_view_tree" model="ir.ui.view">
|
||||||
<field name="name">sos_marketing_leads.view.tree</field>
|
<field name="name">sos_marketing_leads.view.tree</field>
|
||||||
|
|
@ -16,6 +34,7 @@
|
||||||
<tree>
|
<tree>
|
||||||
|
|
||||||
<field name="company_name"/>
|
<field name="company_name"/>
|
||||||
|
<field name="source"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,6 +74,7 @@
|
||||||
</group>
|
</group>
|
||||||
<!-- Second Column -->
|
<!-- Second Column -->
|
||||||
<group>
|
<group>
|
||||||
|
<field name="source" invisible="1"/>
|
||||||
<field name="website_url"/>
|
<field name="website_url"/>
|
||||||
<field name="location"/>
|
<field name="location"/>
|
||||||
</group>
|
</group>
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +1,7 @@
|
||||||
from odoo import models, fields, api
|
from odoo import models, fields, api
|
||||||
import math
|
import math
|
||||||
|
from collections import defaultdict
|
||||||
|
from odoo.tools.misc import format_amount
|
||||||
|
|
||||||
class Battery_Installation_Requirement(models.Model):
|
class Battery_Installation_Requirement(models.Model):
|
||||||
_name = 'sos_proposal_boq'
|
_name = 'sos_proposal_boq'
|
||||||
|
|
@ -17,6 +19,7 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
number_of_strings = fields.Integer(string="Number of Strings")
|
number_of_strings = fields.Integer(string="Number of Strings")
|
||||||
number_of_ups = fields.Integer(string="Number of UPS",store=True)
|
number_of_ups = fields.Integer(string="Number of UPS",store=True)
|
||||||
ups_rating_kva = fields.Integer(string="UPS Rating (KVA)")
|
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)")
|
battery_capacity_ah = fields.Integer(string="Battery Capacity (AH)")
|
||||||
products = fields.Selection([
|
products = fields.Selection([
|
||||||
('BHMS 1.2V', 'BHMS 1.2V'),
|
('BHMS 1.2V', 'BHMS 1.2V'),
|
||||||
|
|
@ -69,13 +72,19 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
#CE Team Fields
|
#CE Team Fields
|
||||||
engineers_nos=fields.Integer(string="No of engineers required")
|
engineers_nos=fields.Integer(string="No of engineers required")
|
||||||
no_of_days=fields.Integer(string="No of days")
|
no_of_days=fields.Integer(string="No of days")
|
||||||
mode_of_transport = fields.Selection([('car','🚗 Car'),('bus','🚌 Bus'),('train','🚂 Train'),('flight','🛩️ Flight')],string='Mode of Transport',default="train")
|
|
||||||
|
|
||||||
extra_cost_line_ids = fields.One2many('sos_extra_cost_lines', 'ref_id',copy=True)
|
extra_cost_line_ids = fields.One2many('sos_extra_cost_lines', 'ref_id',copy=True)
|
||||||
transport_expense = fields.Monetary(readonly=False,string="Transport", compute='_compute_transport_expense', store=True, currency_field='currency_id')
|
|
||||||
food_expense = fields.Monetary(readonly=False,string="Food", compute='_compute_food_expense', store=True, currency_field='currency_id')
|
# mode_of_transport = fields.Selection([('car','🚗 Car'),('bus','🚌 Bus'),('train','🚂 Train'),('flight','🛩️ Flight')],string='Mode of Transport',default="train")
|
||||||
stay_expense = fields.Monetary(readonly=False,string="Room Rent", compute='_compute_stay_expense', store=True, currency_field='currency_id')
|
# transport_expense = fields.Monetary(readonly=False,string="Transport", compute='_compute_transport_expense', store=True, currency_field='currency_id')
|
||||||
local_expense = fields.Monetary(readonly=False,string="Local Conveyance", compute='_compute_local_expense', store=True, currency_field='currency_id')
|
# food_expense = fields.Monetary(readonly=False,string="Food", compute='_compute_food_expense', store=True, currency_field='currency_id')
|
||||||
|
# stay_expense = fields.Monetary(readonly=False,string="Room Rent", compute='_compute_stay_expense', store=True, currency_field='currency_id')
|
||||||
|
# local_expense = fields.Monetary(readonly=False,string="Local Conveyance", compute='_compute_local_expense', store=True, currency_field='currency_id')
|
||||||
|
man_month_1to2_yrs_persons = fields.Integer(string="Man Month (Persons) 1 to 2 Yrs")
|
||||||
|
man_month_2to3_yrs_persons = fields.Integer(string="Man Month (Persons) 2 to 3 Yrs")
|
||||||
|
man_month_manager_persons = fields.Integer(string="Man Month (Persons) Manager Yrs")
|
||||||
|
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 Costing",currency_field='currency_id',compute='_compute_iandc_expense')
|
||||||
#FG Fields
|
#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_ups1 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 1",compute='_compute_line_ids_by_ups',store=False)
|
||||||
|
|
@ -93,6 +102,14 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
line_ids_fg_ups13 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 13",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_fg_ups13 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 13",compute='_compute_line_ids_by_ups',store=False)
|
||||||
line_ids_fg_ups14 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 14",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_fg_ups14 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 14",compute='_compute_line_ids_by_ups',store=False)
|
||||||
line_ids_fg_ups15 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 15",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_fg_ups15 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 15",compute='_compute_line_ids_by_ups',store=False)
|
||||||
|
merged_fg_html = fields.Html(string="FG (Merged)", compute="_compute_merged_fg_html", sanitize=False)
|
||||||
|
merged_sfg_html = fields.Html(string="SFG (Merged)", compute="_compute_merged_sfg_html", sanitize=False)
|
||||||
|
merged_material_html = fields.Html(string="Materials (Merged)", compute="_compute_merged_material_html", sanitize=False)
|
||||||
|
merged_installation_kit_html = fields.Html(string="InstallationKit (Merged)", compute="_compute_merged_installation_kit_html", sanitize=False)
|
||||||
|
merged_miscellaneous_html = fields.Html(string="Miscellaneous (Merged)", compute="_compute_merged_miscellaneous_html", sanitize=False)
|
||||||
|
merged_spare_html = fields.Html(string="Spare (Merged)", compute="_compute_merged_spare_html", sanitize=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#SFG Fields
|
#SFG Fields
|
||||||
line_ids_sfg_ups1 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 1",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_sfg_ups1 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 1",compute='_compute_line_ids_by_ups',store=False)
|
||||||
|
|
@ -113,21 +130,21 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
|
|
||||||
|
|
||||||
#Material Fields
|
#Material Fields
|
||||||
line_ids_material_ups1 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 1",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups1 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 1",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups2 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 2",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups2 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 2",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups3 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 3",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups3 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 3",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups4 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 4",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups4 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 4",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups5 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 5",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups5 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 5",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups6 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 6",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups6 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 6",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups7 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 7",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups7 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 7",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups8 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 8",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups8 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 8",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups9 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 9",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups9 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 9",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups10 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 10",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups10 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 10",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups11 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 11",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups11 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 11",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups12 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 12",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups12 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 12",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups13 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 13",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups13 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 13",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups14 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 14",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups14 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 14",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
line_ids_material_ups15 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 15",compute='_compute_line_ids_by_ups',store=False)
|
line_ids_material_ups15 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 15",compute='_compute_line_ids_by_ups',store=False,readonly=False)
|
||||||
|
|
||||||
|
|
||||||
#Installation Kit Fields
|
#Installation Kit Fields
|
||||||
|
|
@ -199,6 +216,454 @@ 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_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_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")
|
line_ids_spare_ups15 = fields.One2many('sos_proposal_line_spare_ups15','ref_id', string="Spare UPS 15")
|
||||||
|
def _compute_merged_spare_html(self):
|
||||||
|
for rec in self:
|
||||||
|
# Collect lines from ups1..ups15 (skip models that don't exist)
|
||||||
|
models = [f'sos_proposal_line_spare_ups{i}' for i in range(1, 16)]
|
||||||
|
|
||||||
|
agg = defaultdict(float) # (component_id, uom, currency_id, unit_price) -> 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 = "<em>No Material items.</em>"
|
||||||
|
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"<tr>"
|
||||||
|
f"<td>{name_map[key]}</td>"
|
||||||
|
f"<td>{key[1] or ''}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(price_map[key], curr_map[key])}</td>"
|
||||||
|
f"<td style='text-align:right'>{qty:.2f}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_price, curr_map[key])}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows_html:
|
||||||
|
rec.merged_spare_html = "<em>No Material items.</em>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False
|
||||||
|
rows_html += (
|
||||||
|
f"<tr style='font-weight:bold;'>"
|
||||||
|
f"<td colspan='4' style='text-align:right'>Grand Total:</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_sum, currency_for_total)}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
rec.merged_spare_html = f"""
|
||||||
|
<table class="table table-sm" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><b>Material Name</b></th>
|
||||||
|
<th><b>UoM</b></th>
|
||||||
|
<th style="text-align:right"><b>Unit Price</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Qty</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Price</b></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows_html}</tbody>
|
||||||
|
</table>
|
||||||
|
"""
|
||||||
|
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 = "<em>No Miscellaneous items.</em>"
|
||||||
|
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"<tr>"
|
||||||
|
f"<td>{name_map[key]}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(price_map[key], curr_map[key])}</td>"
|
||||||
|
f"<td style='text-align:right'>{qty:.2f}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_price, curr_map[key])}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows_html: # If all rows were skipped
|
||||||
|
rec.merged_miscellaneous_html = "<em>No Miscellaneous items.</em>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add grand total row
|
||||||
|
currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False
|
||||||
|
rows_html += (
|
||||||
|
f"<tr style='font-weight:bold;'>"
|
||||||
|
f"<td colspan='3' style='text-align:right'>Grand Total:</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_sum, currency_for_total)}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
rec.merged_miscellaneous_html = f"""
|
||||||
|
<table class="table table-sm" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><b>Name</b></th>
|
||||||
|
<th style="text-align:right"><b>Unit Price</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Qty</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Price</b></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows_html}</tbody>
|
||||||
|
</table>
|
||||||
|
"""
|
||||||
|
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 = "<em>No Material items.</em>"
|
||||||
|
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"<tr>"
|
||||||
|
f"<td>{name_map[key]}</td>"
|
||||||
|
f"<td>{key[1] or ''}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(price_map[key], curr_map[key])}</td>"
|
||||||
|
f"<td style='text-align:right'>{qty:.2f}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_price, curr_map[key])}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows_html: # If all rows were skipped
|
||||||
|
rec.merged_installation_kit_html = "<em>No Material items.</em>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add grand total row
|
||||||
|
currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False
|
||||||
|
rows_html += (
|
||||||
|
f"<tr style='font-weight:bold;'>"
|
||||||
|
f"<td colspan='4' style='text-align:right'>Grand Total:</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_sum, currency_for_total)}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
rec.merged_installation_kit_html = f"""
|
||||||
|
<table class="table table-sm" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><b>Material Name</b></th>
|
||||||
|
<th><b>UoM</b></th>
|
||||||
|
<th style="text-align:right"><b>Unit Price</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Qty</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Price</b></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows_html}</tbody>
|
||||||
|
</table>
|
||||||
|
"""
|
||||||
|
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 = "<em>No Material items.</em>"
|
||||||
|
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"<tr>"
|
||||||
|
f"<td>{name_map[key]}</td>"
|
||||||
|
f"<td>{key[1] or ''}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(price_map[key], curr_map[key])}</td>"
|
||||||
|
f"<td style='text-align:right'>{qty:.2f}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_price, curr_map[key])}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows_html: # If all rows were skipped
|
||||||
|
rec.merged_material_html = "<em>No Material items.</em>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add grand total row
|
||||||
|
currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False
|
||||||
|
rows_html += (
|
||||||
|
f"<tr style='font-weight:bold;'>"
|
||||||
|
f"<td colspan='4' style='text-align:right'>Grand Total:</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_sum, currency_for_total)}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
rec.merged_material_html = f"""
|
||||||
|
<table class="table table-sm" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><b>Material Name</b></th>
|
||||||
|
<th><b>UoM</b></th>
|
||||||
|
<th style="text-align:right"><b>Unit Price</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Qty</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Price</b></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows_html}</tbody>
|
||||||
|
</table>
|
||||||
|
"""
|
||||||
|
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 = "<em>No SFG items.</em>"
|
||||||
|
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"<tr>"
|
||||||
|
f"<td>{name_map[key]}</td>"
|
||||||
|
f"<td>{key[1] or ''}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(price_map[key], curr_map[key])}</td>"
|
||||||
|
f"<td style='text-align:right'>{qty:.2f}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_price, curr_map[key])}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows_html: # If all rows were skipped
|
||||||
|
rec.merged_sfg_html = "<em>No SFG items.</em>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add grand total row
|
||||||
|
currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False
|
||||||
|
rows_html += (
|
||||||
|
f"<tr style='font-weight:bold;'>"
|
||||||
|
f"<td colspan='4' style='text-align:right'>Grand Total:</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_sum, currency_for_total)}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
rec.merged_sfg_html = f"""
|
||||||
|
<table class="table table-sm" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><b>SFG Name</b></th>
|
||||||
|
<th><b>UoM</b></th>
|
||||||
|
<th style="text-align:right"><b>Unit Price</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Qty</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Price</b></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows_html}</tbody>
|
||||||
|
</table>
|
||||||
|
"""
|
||||||
|
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 = "<em>No FG items.</em>"
|
||||||
|
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"<tr>"
|
||||||
|
f"<td>{name_map[key]}</td>"
|
||||||
|
f"<td>{key[1] or ''}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(price_map[key], curr_map[key])}</td>"
|
||||||
|
f"<td style='text-align:right'>{qty:.2f}</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_price, curr_map[key])}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows_html: # If all rows were skipped
|
||||||
|
rec.merged_fg_html = "<em>No FG items.</em>"
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add grand total row
|
||||||
|
currency_for_total = curr_map[rows_sorted[0][0]] if rows_sorted else False
|
||||||
|
rows_html += (
|
||||||
|
f"<tr style='font-weight:bold;'>"
|
||||||
|
f"<td colspan='4' style='text-align:right'>Grand Total:</td>"
|
||||||
|
f"<td style='text-align:right'>{fmt_price(total_sum, currency_for_total)}</td>"
|
||||||
|
f"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
rec.merged_fg_html = f"""
|
||||||
|
<table class="table table-sm" style="width:100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><b>FG Name</b></th>
|
||||||
|
<th><b>UoM</b></th>
|
||||||
|
<th style="text-align:right"><b>Unit Price</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Qty</b></th>
|
||||||
|
<th style="text-align:right"><b>Total Price</b></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows_html}</tbody>
|
||||||
|
</table>
|
||||||
|
"""
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
res = super().create(vals)
|
res = super().create(vals)
|
||||||
|
|
@ -225,26 +690,29 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
for rec in related_records:
|
for rec in related_records:
|
||||||
rec._compute_acc_costing_submitted_by()
|
rec._compute_acc_costing_submitted_by()
|
||||||
|
|
||||||
@api.depends('no_of_days')
|
# @api.depends('no_of_days')
|
||||||
def _compute_local_expense(self):
|
# def _compute_local_expense(self):
|
||||||
for rec in self:
|
# for rec in self:
|
||||||
rec.local_expense = rec.no_of_days * 500
|
# rec.local_expense = rec.no_of_days * 500
|
||||||
@api.depends('no_of_days')
|
# @api.depends('no_of_days')
|
||||||
def _compute_stay_expense(self):
|
# def _compute_stay_expense(self):
|
||||||
for rec in self:
|
# for rec in self:
|
||||||
rec.stay_expense = rec.no_of_days * 2500
|
# rec.stay_expense = rec.no_of_days * 2500
|
||||||
@api.depends('no_of_days')
|
# @api.depends('no_of_days')
|
||||||
def _compute_food_expense(self):
|
# def _compute_food_expense(self):
|
||||||
for rec in self:
|
# for rec in self:
|
||||||
rec.food_expense = rec.no_of_days * 600
|
# rec.food_expense = rec.no_of_days * 600
|
||||||
@api.depends('no_of_days')
|
# @api.depends('no_of_days')
|
||||||
def _compute_transport_expense(self):
|
# def _compute_transport_expense(self):
|
||||||
for rec in self:
|
# for rec in self:
|
||||||
rec.transport_expense = 5000
|
# rec.transport_expense = 5000
|
||||||
@api.depends('no_of_days','transport_expense','food_expense','stay_expense','local_expense')
|
|
||||||
|
@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')
|
||||||
def _compute_iandc_expense(self):
|
def _compute_iandc_expense(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
rec.iandc_costing = rec.transport_expense + rec.food_expense + rec.stay_expense + rec.local_expense
|
rec.iandc_costing = ((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
|
||||||
|
|
||||||
@api.depends('extra_cost_line_ids.cost')
|
@api.depends('extra_cost_line_ids.cost')
|
||||||
def _compute_extra_lines_cost(self):
|
def _compute_extra_lines_cost(self):
|
||||||
|
|
@ -425,6 +893,7 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
|
|
||||||
self.customer_name = self.proposal_id.customer_name
|
self.customer_name = self.proposal_id.customer_name
|
||||||
self.location = self.proposal_id.location
|
self.location = self.proposal_id.location
|
||||||
|
self.no_of_electrical_panel = self.proposal_id.no_of_electrical_panel
|
||||||
self.number_of_batteries = self.proposal_id.number_of_batteries
|
self.number_of_batteries = self.proposal_id.number_of_batteries
|
||||||
self.number_of_ups = self.proposal_id.number_of_ups
|
self.number_of_ups = self.proposal_id.number_of_ups
|
||||||
self.products = self.proposal_id.products
|
self.products = self.proposal_id.products
|
||||||
|
|
@ -508,17 +977,22 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index)
|
matching_ups_line = self.proposal_id.ups_line_ids.filtered(lambda l: l.ups_index == ups_index)
|
||||||
if matching_ups_line:
|
if matching_ups_line:
|
||||||
single_set_qty = math.ceil((matching_ups_line.number_of_batteries)/4) * matching_ups_line.number_of_strings_per_battery
|
single_set_qty = math.ceil((matching_ups_line.number_of_batteries)/4) * matching_ups_line.number_of_strings_per_battery
|
||||||
|
name_lower = (line.component_id.name or "").lower()
|
||||||
|
is_electrical_panel = "electrical panel" in name_lower
|
||||||
|
is_panel_exceeding = self.proposal_id.no_of_electrical_panel < ups_index
|
||||||
|
|
||||||
fg_lines.append((0, 0, {
|
fg_lines.append((0, 0, {
|
||||||
'component_id': line.component_id.id,
|
'component_id': line.component_id.id,
|
||||||
'uom': line.uom,
|
'uom': line.uom,
|
||||||
'unit_price': line.component_id.unit_price,
|
'unit_price': 0.00 if is_panel_exceeding and is_electrical_panel else line.component_id.unit_price,
|
||||||
'description': line.description,
|
'description': line.description,
|
||||||
'item_type': line.item_type,
|
'item_type': line.item_type,
|
||||||
'singet_set_qty': single_set_qty,
|
'total_set': 0 if is_panel_exceeding and is_electrical_panel else 1,
|
||||||
|
'singet_set_qty': 0 if is_panel_exceeding and is_electrical_panel else single_set_qty,
|
||||||
'production_cost': line.add_production_cost,
|
'production_cost': line.add_production_cost,
|
||||||
'ups_index': ups_index
|
'ups_index': ups_index
|
||||||
}))
|
}))
|
||||||
|
|
||||||
for line in record.sfg_ids:
|
for line in record.sfg_ids:
|
||||||
if line.item_type == 'Master Panel':
|
if line.item_type == 'Master Panel':
|
||||||
single_set_qty = 1
|
single_set_qty = 1
|
||||||
|
|
@ -646,10 +1120,22 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
record.total_fg_cost = sum(line.total_price for line in record.line_ids_fg)
|
record.total_fg_cost = sum(line.total_price for line in record.line_ids_fg)
|
||||||
def action_ce_esign_btn(self):
|
def action_ce_esign_btn(self):
|
||||||
body_html = f"""
|
body_html = f"""
|
||||||
<p>Below <b>Proposal</b> is waiting for your Updation</p>
|
<p>Below <b>BOQ</b> is waiting for your updation.</p>
|
||||||
|
<p><b>Customer Name:</b> {self.customer_name or ''}</p>
|
||||||
|
<p><b>Location:</b> {self.location or ''}</p>
|
||||||
|
<p><b>Number of Batteries:</b> {self.number_of_batteries or ''}</p>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sequence_util = self.env['sos_common_scripts']
|
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(
|
return sequence_util.action_assign_signature(
|
||||||
self,
|
self,
|
||||||
'boq_submitted_by_name',
|
'boq_submitted_by_name',
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
number_of_strings = fields.Integer(string="Number of Strings")
|
number_of_strings = fields.Integer(string="Number of Strings")
|
||||||
number_of_ups = fields.Integer(string="Number of UPS")
|
number_of_ups = fields.Integer(string="Number of UPS")
|
||||||
ups_rating_kva = fields.Integer(string="UPS Rating (KVA)")
|
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)")
|
battery_capacity_ah = fields.Integer(string="Battery Capacity (AH)")
|
||||||
specific_requirements = fields.Html(string="Specific Requirements")
|
specific_requirements = fields.Html(string="Specific Requirements")
|
||||||
products = fields.Selection(
|
products = fields.Selection(
|
||||||
|
|
@ -147,6 +148,7 @@ class Battery_Installation_Requirement(models.Model):
|
||||||
@api.onchange('number_of_ups')
|
@api.onchange('number_of_ups')
|
||||||
def _onchange_number_of_ups(self):
|
def _onchange_number_of_ups(self):
|
||||||
if self.number_of_ups is not None:
|
if self.number_of_ups is not None:
|
||||||
|
self.no_of_electrical_panel = self.number_of_ups
|
||||||
if self.number_of_ups > 15:
|
if self.number_of_ups > 15:
|
||||||
raise ValidationError("Number of UPS cannot be more than 15.")
|
raise ValidationError("Number of UPS cannot be more than 15.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -694,16 +694,31 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
|
||||||
report.write({
|
report.write({
|
||||||
new_field_billed: (getattr(report, new_field_billed, 0.0) or 0.0) + new_billed_amount
|
new_field_billed: (getattr(report, new_field_billed, 0.0) or 0.0) + new_billed_amount
|
||||||
})
|
})
|
||||||
# Optionally create billing collection entry (if needed)
|
# Optionally create billing collection entry (if needed
|
||||||
if new_billed_amount > 0:
|
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({
|
self.env['sos_billing_collection'].create({
|
||||||
'ref_id': report.id,
|
'ref_id': report.id,
|
||||||
'customer_name': vals.get('customer_name', rec.customer_name.id),
|
'customer_name': vals.get('customer_name', rec.customer_name.id),
|
||||||
'sales_person': report.sales_person.id,
|
'sales_person': report.sales_person.id,
|
||||||
'action_status': 'Billed',
|
'action_status': 'Billed',
|
||||||
'date_of_action': new_billed_date,
|
'date_of_action': new_billed_date,
|
||||||
|
'po_no':vals.get('po_no'),
|
||||||
'value': new_billed_amount
|
'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 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,
|
'sales_person': report.sales_person.id,
|
||||||
'action_status': 'Billed',
|
'action_status': 'Billed',
|
||||||
'date_of_action': billed_date,
|
'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)
|
new_record = super(SOS_Sales_Achievement_Report_Brief, self).create(vals)
|
||||||
return new_record
|
return new_record
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@
|
||||||
|
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="proposal_id"/>
|
<field name="proposal_id" readonly="acc_approved_by_name"/>
|
||||||
<field name="customer_name"/>
|
<field name="customer_name" readonly="acc_approved_by_name"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="location"/>
|
<field name="location" readonly="acc_approved_by_name"/>
|
||||||
<field name="products"/>
|
<field name="products" readonly="acc_approved_by_name"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
<h2 style="text-align: center;text-transform: uppercase;text-shadow: 1px 1p 1px #140718;color: #65407c;padding:5px;">Detailed Specifications</h2><hr></hr><br></br>
|
<h2 style="text-align: center;text-transform: uppercase;text-shadow: 1px 1p 1px #140718;color: #65407c;padding:5px;">Detailed Specifications</h2><hr></hr><br></br>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="communication_type"/>
|
<field name="communication_type" readonly="acc_approved_by_name"/>
|
||||||
<field name="number_of_batteries"/>
|
<field name="number_of_batteries" readonly="acc_approved_by_name"/>
|
||||||
<field name="number_of_ups"/>
|
<field name="number_of_ups" readonly="acc_approved_by_name"/>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
<notebook>
|
<notebook>
|
||||||
<!-- Page 1 -->
|
<!-- Page 1 -->
|
||||||
|
|
||||||
<page string="UPS 1" invisible="number_of_ups < 1">
|
<page string="UPS 1" invisible="number_of_ups < 1">
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Finished Goods</h3>
|
text-decoration: underline;">Finished Goods</h3>
|
||||||
|
|
@ -111,7 +112,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups1" context="{'default_ups_index': 1}">
|
<field name="line_ids_spare_ups1" context="{'default_ups_index': 1}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -219,7 +220,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups2" context="{'default_ups_index': 2}">
|
<field name="line_ids_spare_ups2" context="{'default_ups_index': 2}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -328,7 +329,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups3" context="{'default_ups_index': 3}">
|
<field name="line_ids_spare_ups3" context="{'default_ups_index': 3}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -436,7 +437,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups4" context="{'default_ups_index': 4}">
|
<field name="line_ids_spare_ups4" context="{'default_ups_index': 4}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -544,7 +545,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups5" context="{'default_ups_index': 5}">
|
<field name="line_ids_spare_ups5" context="{'default_ups_index': 5}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -651,7 +652,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups6" context="{'default_ups_index': 6}">
|
<field name="line_ids_spare_ups6" context="{'default_ups_index': 6}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -759,7 +760,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups7" context="{'default_ups_index': 7}">
|
<field name="line_ids_spare_ups7" context="{'default_ups_index': 7}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -867,7 +868,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups8" context="{'default_ups_index': 8}">
|
<field name="line_ids_spare_ups8" context="{'default_ups_index': 8}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -975,7 +976,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups9" context="{'default_ups_index': 9}">
|
<field name="line_ids_spare_ups9" context="{'default_ups_index': 9}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1083,7 +1084,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups10" context="{'default_ups_index': 10}">
|
<field name="line_ids_spare_ups10" context="{'default_ups_index': 10}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1190,7 +1191,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups11" context="{'default_ups_index': 11}">
|
<field name="line_ids_spare_ups11" context="{'default_ups_index': 11}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1297,7 +1298,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups12" context="{'default_ups_index': 12}">
|
<field name="line_ids_spare_ups12" context="{'default_ups_index': 12}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1404,7 +1405,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups13" context="{'default_ups_index': 13}">
|
<field name="line_ids_spare_ups13" context="{'default_ups_index': 13}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1511,7 +1512,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups14" context="{'default_ups_index': 14}">
|
<field name="line_ids_spare_ups14" context="{'default_ups_index': 14}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1618,7 +1619,7 @@
|
||||||
</field>
|
</field>
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Spare/Additional Materials</h3>
|
text-decoration: underline;">Spare/Additional Materials</h3>
|
||||||
<field name="line_ids_spare_ups15" context="{'default_ups_index': 15}">
|
<field name="line_ids_spare_ups15" context="{'default_ups_index': 15}" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="production_cost" widget="boolean_toggle"/>
|
||||||
|
|
@ -1654,116 +1655,42 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</page>
|
</page>
|
||||||
<!-- <page string="Finished Goods">
|
<page string="Summary" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
|
||||||
<field name="line_ids_fg">
|
<h3 style="text-transform: uppercase;
|
||||||
<tree editable="bottom">
|
text-decoration: underline;">Finished Goods</h3>
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
<field name="merged_fg_html" nolabel="1"/>
|
||||||
<field name="component_id"/>
|
<h3 style="text-transform: uppercase;
|
||||||
<field name="description"/>
|
text-decoration: underline;">Semi-Finished Goods</h3>
|
||||||
<field name="item_type"/>
|
<field name="merged_sfg_html" nolabel="1"/>
|
||||||
<field name="uom"/>
|
<h3 style="text-transform: uppercase;
|
||||||
<field name="unit_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
text-decoration: underline;">Materials</h3>
|
||||||
<field name="singet_set_qty"/>
|
<field name="merged_material_html" nolabel="1"/>
|
||||||
<field name="total_set"/>
|
<h3 style="text-transform: uppercase;
|
||||||
<field name="quantity"/>
|
text-decoration: underline;">Installation Kit</h3>
|
||||||
<field name="total_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
<field name="merged_installation_kit_html" nolabel="1"/>
|
||||||
</tree>
|
<h3 style="text-transform: uppercase;
|
||||||
</field>
|
text-decoration: underline;">Spare</h3>
|
||||||
<group style="float: right;
|
<field name="merged_spare_html" nolabel="1"/>
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;">
|
<h3 style="text-transform: uppercase;
|
||||||
<field name="total_fg_cost"/>
|
text-decoration: underline;">Miscellaneous</h3>
|
||||||
</group>
|
<field name="merged_miscellaneous_html" nolabel="1"/>
|
||||||
</page> -->
|
|
||||||
<!-- <page string="Semi Finished Goods">
|
|
||||||
<field name="line_ids_sfg">
|
|
||||||
<tree editable="bottom">
|
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
|
||||||
<field name="component_id"/>
|
|
||||||
<field name="description"/>
|
|
||||||
<field name="item_type"/>
|
|
||||||
<field name="uom"/>
|
|
||||||
<field name="unit_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
<field name="singet_set_qty"/>
|
|
||||||
<field name="total_set"/>
|
|
||||||
<field name="quantity"/>
|
|
||||||
<field name="total_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
<group style="float: right;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;">
|
|
||||||
<field name="total_sfg_cost"/>
|
|
||||||
</group>
|
|
||||||
</page> -->
|
|
||||||
<!-- <page string="Materials">
|
|
||||||
<field name="line_ids_material">
|
|
||||||
<tree editable="bottom">
|
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
|
||||||
<field name="component_id"/>
|
|
||||||
<field name="item_type"/>
|
|
||||||
<field name="description"/>
|
|
||||||
<field name="uom"/>
|
|
||||||
<field name="unit_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
<field name="singet_set_qty"/>
|
|
||||||
<field name="total_set"/>
|
|
||||||
<field name="quantity"/>
|
|
||||||
<field name="total_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
<group style="float: right;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;">
|
|
||||||
<field name="total_material_cost"/>
|
|
||||||
</group>
|
|
||||||
</page> -->
|
|
||||||
<!-- <page string="Installation Kit">
|
|
||||||
<field name="line_ids_installation_kit">
|
|
||||||
<tree editable="bottom">
|
|
||||||
<field name="production_cost" widget="boolean_toggle"/>
|
|
||||||
<field name="component_id"/>
|
|
||||||
<field name="description"/>
|
|
||||||
<field name="item_type"/>
|
|
||||||
<field name="uom"/>
|
|
||||||
<field name="unit_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
<field name="singet_set_qty"/>
|
|
||||||
<field name="total_set"/>
|
|
||||||
<field name="quantity"/>
|
|
||||||
<field name="total_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
<group style="float: right;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;">
|
|
||||||
<field name="total_installation_material_cost"/>
|
|
||||||
</group>
|
|
||||||
</page>
|
</page>
|
||||||
<page string="Miscellaneous">
|
|
||||||
<field name="line_ids_miscellaneous">
|
|
||||||
<tree editable="bottom">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="cost"/>
|
|
||||||
<field name="quantity"/>
|
|
||||||
<field name="total_price" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_user"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
<group style="float: right;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;">
|
|
||||||
<field name="total_miscellaneous_cost"/>
|
|
||||||
</group>
|
|
||||||
</page> -->
|
|
||||||
<page string="Installation & Commissioning" invisible="proposal_id == False">
|
<page string="Installation & Commissioning" invisible="proposal_id == False">
|
||||||
<group>
|
|
||||||
<field name="engineers_nos"/>
|
<group><field name="engineers_nos"/></group>
|
||||||
<field name="no_of_days"/>
|
<group><field name="no_of_days"/></group>
|
||||||
<field name="mode_of_transport"/>
|
<br></br>
|
||||||
<field name="transport_expense" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
<table class="table_custom" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
|
||||||
<field name="food_expense" 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>
|
||||||
<field name="stay_expense" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
<tbody>
|
||||||
<field name="local_expense" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
<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>
|
||||||
<field name="iandc_costing" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
<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>
|
||||||
</group>
|
<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>
|
||||||
|
|
||||||
</page>
|
</page>
|
||||||
<page string="Costing By Accounts" invisible="proposal_id == False" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
|
<page string="Costing By Accounts" invisible="proposal_id == False" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
|
||||||
|
|
@ -1798,7 +1725,7 @@
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Miscellaneous Cost</h3>
|
text-decoration: underline;">Miscellaneous Cost</h3>
|
||||||
<field name="extra_cost_line_ids">
|
<field name="extra_cost_line_ids" readonly="acc_approved_by_name">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="field_name"/>
|
<field name="field_name"/>
|
||||||
<field name="cost"/>
|
<field name="cost"/>
|
||||||
|
|
@ -1812,20 +1739,20 @@
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<h3 style="text-transform: uppercase;
|
<h3 style="text-transform: uppercase;
|
||||||
text-decoration: underline;">Final Cost</h3>
|
text-decoration: underline;">Final Cost</h3>
|
||||||
<tr><td class="column">Product Cost</td><td><field name="total_cost"/></td></tr>
|
<tr><td class="column">Product Cost</td><td><field name="total_cost" readonly="acc_approved_by_name"/></td></tr>
|
||||||
<tr><td class="column">Opreational Cost per Battery</td><td><field name="margin_per_battery"/></td></tr>
|
<tr><td class="column">Opreational Cost per Battery</td><td><field name="margin_per_battery" readonly="acc_approved_by_name"/></td></tr>
|
||||||
|
|
||||||
<tr><td class="column">Opreational Cost</td><td><field name="margin"/></td></tr>
|
<tr><td class="column">Opreational Cost</td><td><field name="margin" readonly="acc_approved_by_name"/></td></tr>
|
||||||
<tr><td class="column">Packing & Forwarding</td><td><field name="packing_and_forwarding"/></td></tr>
|
<tr><td class="column">Packing & Forwarding</td><td><field name="packing_and_forwarding" readonly="acc_approved_by_name"/></td></tr>
|
||||||
<tr><td class="column">Installation & Commissioning</td><td><field name="iandc_costing"/></td></tr>
|
<tr><td class="column">Installation & Commissioning</td><td><field name="iandc_costing" readonly="acc_approved_by_name"/></td></tr>
|
||||||
<tr><td class="column">Warranty (%)</td><td><field name="warranty_percentage"/></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"/></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"/></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"/></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 style="background-color: aliceblue;"><td class="column">Final MSP</td><td><field style="font-weight: bold;font-size: 18px;" name="final_cost"/></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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
<group>
|
<group>
|
||||||
<field name="type_of_source"/>
|
<field name="type_of_source"/>
|
||||||
<field name="number_of_ups" string="Number of UPS"/>
|
<field name="number_of_ups" string="Number of UPS"/>
|
||||||
|
<field name="no_of_electrical_panel"/>
|
||||||
<field name="ups_line_ids">
|
<field name="ups_line_ids">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="ups_index" column_invisible="1"/>
|
<field name="ups_index" column_invisible="1"/>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<tree>
|
<tree>
|
||||||
<field name="company_name"/>
|
<field name="company_name"/>
|
||||||
<field name="location"/>
|
<field name="location"/>
|
||||||
<field name="vertical_domain"/>
|
<field name="vertical_domain" widget="many2many_tags"/>
|
||||||
<field name="interested_in"/>
|
<field name="interested_in"/>
|
||||||
<field name="lead_generated_by"/>
|
<field name="lead_generated_by"/>
|
||||||
<field name="status" decoration-success="status == 'Moved to Customer'"/>
|
<field name="status" decoration-success="status == 'Moved to Customer'"/>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue