This commit is contained in:
Deena 2025-08-11 16:26:29 +05:30
parent faa8f2ff3b
commit 611cb5e418
20 changed files with 489 additions and 163 deletions

View File

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

View File

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

View File

@ -17,12 +17,15 @@ class SOS_Dock_Audit(models.Model):
deliverables_boq_id = fields.Many2one('sos_deliverables_boq', string="Deliverables/BOQ Id") deliverables_boq_id = fields.Many2one('sos_deliverables_boq', string="Deliverables/BOQ Id")
fg_name = fields.Selection( fg_name = fields.Selection(
[ [
('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'),
('MC 250W', 'MC 250W'), ('SBMS 55A', 'SBMS 55A'),
('MC 250W', 'MC 250W'),
('HeartTarang', 'HeartTarang') ('HeartTarang', 'HeartTarang')
], ],
string="Product Type",required=True) string="Product Type",required=True)
@ -384,6 +387,7 @@ class sos_dock_audit_Line_Material(models.Model):
ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade") 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")

View File

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

View File

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

View File

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

View File

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

View File

@ -86,131 +86,192 @@
} }
</style> </style>
<div class="page"> <div class="page">
<t t-set="counter" t-value="0"/>
<t t-foreach="range(o.master_quantity)" t-as="line_items">
<t t-set="counter" t-value="counter + 1"/>
<div class="label-set">
<!-- Combine all tables into a single list -->
<t t-set="all_tables" t-value="[]"/>
<!-- Add FG tables -->
<t t-set="fg_counter" t-value="0"/>
<t t-foreach="o.line_ids_fg or []" t-as="fg_item">
<t t-set="fg_counter" t-value="fg_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_counter)]"/>
</t>
<!-- Add SFG tables -->
<t t-set="sfg_counter" t-value="0"/>
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item">
<t t-set="sfg_counter" t-value="sfg_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_counter)]"/>
</t>
<!-- Add Materials tables -->
<t t-set="material_counter" t-value="0"/>
<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><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-set="counter" t-value="0"/> <!-- Second table in the pair (if exists) -->
<t t-foreach="range(o.master_quantity)" t-as="line_items"> <div class="col-6">
<t t-set="counter" t-value="counter + 1"/> <t t-if="pair_index + 1 &lt; len(all_tables)">
<div class="label-set"> <t t-set="table_info" t-value="all_tables[pair_index + 1]"/>
<!-- Combine all tables into a single list --> <t t-set="category" t-value="table_info[0]"/>
<t t-set="all_tables" t-value="[]"/> <t t-set="item" t-value="table_info[1]"/>
<!-- Add FG tables --> <t t-set="index" t-value="table_info[2]"/>
<t t-foreach="o.line_ids_fg or []" t-as="fg_item"> <div class="table-container">
<t t-set="fg_index_safe" t-value="fg_index if fg_index is not None else 0"/> <table class="label-table">
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_index_safe + 1)]"/> <tbody>
</t> <tr>
<!-- Add SFG tables --> <td class="column">Set No</td>
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item"> <td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
<t t-set="sfg_index_safe" t-value="sfg_index if sfg_index is not None else 0"/> </tr>
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_index_safe + 1)]"/> <tr>
</t> <td class="column">Battery</td>
<!-- Add Materials tables --> <td><t t-esc="o.batter_or_cells"/></td>
<t t-foreach="o.line_ids_material or []" t-as="material_item"> </tr>
<t t-set="material_index_safe" t-value="material_index if material_index is not None else 0"/> <tr>
<t t-set="all_tables" t-value="all_tables + [(3, material_item, material_index_safe + 1)]"/> <td class="column">No</td>
</t> <td><t t-esc="category"/>.<t t-esc="index"/></td>
</tr>
<tr>
<td class="column">Name</td>
<!-- Process all tables in pairs --> <td>
<t t-foreach="range(0, len(all_tables), 2)" t-as="pair_index"> <t t-if="'component_id' in item">
<div class="row"> <t t-esc="item.component_id.name or item.component_id.part_no or item.name or 'N/A'"/>
<!-- First table in the pair --> </t>
<div class="col-6"> <t t-else="">
<t t-set="table_info" t-value="all_tables[pair_index]"/> <t t-esc="item.name or 'N/A'"/>
<t t-set="category" t-value="table_info[0]"/> </t>
<t t-set="item" t-value="table_info[1]"/> </td>
<t t-set="index" t-value="table_info[2]"/> </tr>
<div class="table-container"> <tr>
<table class="label-table"> <td class="column">UOM</td>
<tbody> <td>
<tr> <t t-if="'uom' in item">
<td class="column">Set No</td> <t t-esc="item.uom or 'N/A'"/>
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td> </t>
</tr> <t t-else="">
<tr> <t t-esc="'N/A'"/>
<td class="column">Battery</td> </t>
<td></td> </td>
</tr> </tr>
<tr> <tr>
<td class="column">No</td> <td class="column">Qty</td>
<td><t t-esc="category"/>.<t t-esc="index"/></td> <td>
</tr> <t t-if="'singet_set_qty' in item">
<tr> <t t-esc="item.singet_set_qty or 'N/A'"/>
<td class="column">Name</td> </t>
<td><t t-esc="item.component_id.name or 'N/A'"/></td> <t t-else="">
</tr> <t t-esc="item.quantity or 'N/A'"/>
<tr> </t>
<td class="column">UOM</td> </td>
<td><t t-esc="item.uom or 'N/A'"/></td> </tr>
</tr> <tr>
<tr> <td class="column">S.No</td>
<td class="column">Qty</td> <td></td>
<td><t t-esc="item.singet_set_qty or 'N/A'"/></td> </tr>
</tr> </tbody>
<tr> </table>
<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 &lt; 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></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>
</t>
<t t-else="">
<div class="empty-col"></div>
</t>
</div>
</div> <!-- Close row -->
</t>
</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>

View File

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

View File

@ -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 &lt; 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>

View File

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

View File

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

View File

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

View File

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