Compare commits
4 Commits
e608da1fc9
...
838086034f
| Author | SHA1 | Date |
|---|---|---|
|
|
838086034f | |
|
|
611cb5e418 | |
|
|
faa8f2ff3b | |
|
|
f7db2aebc9 |
Binary file not shown.
|
|
@ -11,7 +11,7 @@
|
|||
<field name="location"/>
|
||||
<field name="products_interested" string="Products" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||
<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="move_div_display" widget="boolean_toggle" readonly="1"/>
|
||||
</tree>
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@
|
|||
'report/mme_history_card_report.xml',
|
||||
'report/sos_boq_labels.xml',
|
||||
'report/shelflife_report.xml',
|
||||
'report/sos_boq_report.xml',
|
||||
'data/send_indent_plan_email_template.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_image = fields.Image(related="verified_by.signature_image",string='Prepared by Sign',readonly=True)
|
||||
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):
|
||||
try:
|
||||
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):
|
||||
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(
|
||||
self,
|
||||
'prepared_by',
|
||||
'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):
|
||||
sequence_util = self.env['sos_common_scripts']
|
||||
return sequence_util.action_assign_signature(
|
||||
|
|
@ -185,6 +231,7 @@ class sos_deliverables_boq_Line_Material(models.Model):
|
|||
|
||||
ref_id = fields.Many2one('sos_deliverables_boq', string="Materials", ondelete="cascade")
|
||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
||||
display_name = fields.Char(string="Display Name", related="component_id.name", store=True)
|
||||
uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], default="Nos",string="Uom")
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
||||
|
|
@ -306,6 +353,7 @@ class sos_deliverables_Material_installationkit(models.Model):
|
|||
|
||||
ref_id = fields.Many2one('sos_deliverables_boq', string="Materials", ondelete="cascade")
|
||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
||||
display_name = fields.Char(string="Display Name", related="component_id.name", store=True)
|
||||
uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@ class SOS_Dock_Audit(models.Model):
|
|||
deliverables_boq_id = fields.Many2one('sos_deliverables_boq', string="Deliverables/BOQ Id")
|
||||
fg_name = fields.Selection(
|
||||
[
|
||||
('BHMS 1.2V', 'BHMS 1.2V'),
|
||||
('BHMS 2V', 'BHMS 2V'),
|
||||
('BHMS 12V', 'BHMS 12V'),
|
||||
('BHMS 1.2V', 'BHMS 1.2V'),
|
||||
('BHMS 2V', 'BHMS 2V'),
|
||||
('BHMS 12V', 'BHMS 12V'),
|
||||
('BHMS 48V', 'BHMS 48V'),
|
||||
('BMS-HV', 'BMS-HV'),
|
||||
('BMS-LV 100A', 'BMS-LV 100A'),
|
||||
('BMS-LV 40A', 'BMS-LV 40A'),
|
||||
('MC 250W', 'MC 250W'),
|
||||
('BMS-LV 40A', 'BMS-LV 40A'),
|
||||
('SBMS 55A', 'SBMS 55A'),
|
||||
('MC 250W', 'MC 250W'),
|
||||
('HeartTarang', 'HeartTarang')
|
||||
],
|
||||
string="Product Type",required=True)
|
||||
|
|
@ -384,6 +387,7 @@ class sos_dock_audit_Line_Material(models.Model):
|
|||
|
||||
ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade")
|
||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
||||
display_name = fields.Char(string="Display Name", related="component_id.name", store=True)
|
||||
uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], default="Nos",string="Uom")
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
||||
|
|
@ -689,6 +693,7 @@ class sos_dock_audit_Material_installationkit(models.Model):
|
|||
|
||||
ref_id = fields.Many2one('sos_dock_audit', string="Materials", ondelete="cascade")
|
||||
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
||||
display_name = fields.Char(string="Display Name", related="component_id.name", store=True)
|
||||
uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], default="Nos",string="Uom")
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
material_code = fields.Char(related="component_id.material_code",string="Material Code")
|
||||
|
|
|
|||
|
|
@ -51,21 +51,45 @@ class FIR_Only(models.Model):
|
|||
stores_received_on = fields.Date(string="Stores Received On")
|
||||
ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Reference (If any Rejected)")
|
||||
customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name", required=True)
|
||||
|
||||
remarks = fields.Text(string="Remarks")
|
||||
test_log = fields.Binary("Test Log", required=False, attachment=True)
|
||||
test_log_filename = fields.Char("Test Log Filename")
|
||||
rejected_line_ids = fields.One2many('sos_fir_rejected_lines', 'ref_id', string="Finished Goods",copy=True, ondelete='cascade')
|
||||
serial_no_line_ids = fields.One2many('sos_fir_serial_no_lines', 'ref_id',copy=True)
|
||||
|
||||
sd_card_data = fields.Binary("SD Card Data", required=False, attachment=True)
|
||||
sd_card_data_filename = fields.Char("SD Card Data Filename")
|
||||
|
||||
cloud_data = fields.Binary("Cloud Data", required=False, attachment=True)
|
||||
cloud_data_filename = fields.Char("Cloud Data Filename")
|
||||
|
||||
firmware_data = fields.Binary("Firmware Data", required=False, attachment=True)
|
||||
firmware_data_filename = fields.Char("Firmware Data Filename")
|
||||
serial_no_parse = fields.Text(string="Serial no's to parse")
|
||||
def parse_serial_nos(self):
|
||||
self.ensure_one()
|
||||
if not self.serial_no_parse:
|
||||
return
|
||||
|
||||
# Split into clean serial numbers
|
||||
serial_numbers = [
|
||||
line.strip() for line in self.serial_no_parse.splitlines() if line.strip()
|
||||
]
|
||||
|
||||
SerialLine = self.env['sos_fir_serial_no_lines']
|
||||
|
||||
for serial in serial_numbers:
|
||||
# Check if already exists for this ref_id
|
||||
exists = SerialLine.search([
|
||||
('ref_id', '=', self.id),
|
||||
('serial_no', '=', serial)
|
||||
], limit=1)
|
||||
|
||||
if not exists:
|
||||
SerialLine.create({
|
||||
'ref_id': self.id,
|
||||
'serial_no': serial,
|
||||
'inspection_decision': 'PASS', # default
|
||||
})
|
||||
|
||||
|
||||
@api.onchange('batch_size')
|
||||
def _onchange_batch_size(self):
|
||||
if self._origin and self.batch_size is not False:
|
||||
|
|
|
|||
|
|
@ -18,18 +18,30 @@ class sos_inventory_customers(models.Model):
|
|||
new_name = (vals.get('customer_name') or '').lower()
|
||||
|
||||
if new_name and len(new_name) >= 5:
|
||||
existing_customers = self.search([])
|
||||
# Words/phrases to exclude
|
||||
blacklist = [
|
||||
'private limited', 'pvt ltd', 'pvt. ltd.', 'ltd', 'llp', 'inc', 'co', 'company', 'corporation'
|
||||
]
|
||||
|
||||
def clean_name(name):
|
||||
for word in blacklist:
|
||||
name = name.replace(word, '')
|
||||
return name.strip()
|
||||
|
||||
new_name_clean = clean_name(new_name)
|
||||
|
||||
existing_customers = self.search([])
|
||||
for customer in existing_customers:
|
||||
existing_name = (customer.customer_name or '').lower()
|
||||
existing_name_clean = clean_name(existing_name)
|
||||
|
||||
# Check all substrings of length 5 or more
|
||||
for i in range(len(new_name) - 4):
|
||||
substring = new_name[i:i+5]
|
||||
if substring in existing_name:
|
||||
# Check all substrings of length 7 or more
|
||||
for i in range(len(new_name_clean) - 6):
|
||||
substring = new_name_clean[i:i+7]
|
||||
if substring in existing_name_clean:
|
||||
raise UserError(
|
||||
f"A customer with a similar name already exists: '{customer.customer_name}' "
|
||||
f"(matched substring: '{substring}')"
|
||||
)
|
||||
|
||||
return super(sos_inventory_customers, self).create(vals)
|
||||
return super().create(vals)
|
||||
|
|
@ -217,6 +217,11 @@ class SOS_IR(models.Model):
|
|||
for item in self.line_ids_material:
|
||||
# Fetch the component related to the current item
|
||||
component = self.env['sos_material'].browse(item.component_id.id)
|
||||
if self.supplier_name and self.supplier_name.id not in component.suppliers.ids:
|
||||
component.write({
|
||||
'suppliers': [(4, self.supplier_name.id)]
|
||||
})
|
||||
|
||||
if component.shelf_life == "yes":
|
||||
shelflife_line_values = {
|
||||
'ir_ref_no':self.id,
|
||||
|
|
@ -301,6 +306,10 @@ class SOS_IR(models.Model):
|
|||
for item in self.line_ids_sfg:
|
||||
# PO update part
|
||||
sfg_component = self.env['sos_sfg'].browse(item.component_id.id)
|
||||
if self.service_provider_name and self.service_provider_name.id not in sfg_component.service_providers.ids:
|
||||
sfg_component.write({
|
||||
'service_providers': [(4, self.service_provider_name.id)]
|
||||
})
|
||||
if self.wo_planned_at == "outsource":
|
||||
if self.wo_no:
|
||||
if not self.orr_no:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class SOS_SalesOrder(models.Model):
|
|||
],
|
||||
string="Product Name",required=True)
|
||||
line_ids = fields.One2many('sos_sales_order_line', 'ref_id',copy=True)
|
||||
customer_name = fields.Char(string="Customer Name")
|
||||
customer_name = fields.Many2one('sos_inventory_customers',string="Customer Name")
|
||||
lead_time = fields.Datetime(string="Lead Time")
|
||||
customer_po_no = fields.Char(string="PO No")
|
||||
customer_po_date = fields.Datetime(string="PO Date")
|
||||
|
|
@ -58,7 +58,7 @@ class SOS_SalesOrder(models.Model):
|
|||
'sales_id':self.id,
|
||||
'fg_name':self.fg_name,
|
||||
'quantity':self.qty,
|
||||
'customer_name':self.customer_name,
|
||||
'customer_name':self.customer_name.customer_name,
|
||||
'lead_time':self.lead_time,
|
||||
'customer_po_no':self.customer_po_no,
|
||||
'customer_po_date':self.customer_po_date
|
||||
|
|
@ -109,15 +109,15 @@ class SOS_SalesOrder(models.Model):
|
|||
sequence_util = self.env['sos_common_scripts']
|
||||
return sequence_util.generate_sequence('sos_sales_order','SALES', 'order_id')
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
customer_name = vals.get('customer_name')
|
||||
if customer_name:
|
||||
existing = self.env['sos_inventory_customers'].search([('customer_name', '=', customer_name)], limit=1)
|
||||
if not existing:
|
||||
self.env['sos_inventory_customers'].create({'customer_name': customer_name})
|
||||
# @api.model
|
||||
# def create(self, vals):
|
||||
# customer_name = vals.get('customer_name')
|
||||
# if customer_name:
|
||||
# existing = self.env['sos_inventory_customers'].search([('customer_name', '=', customer_name)], limit=1)
|
||||
# if not existing:
|
||||
# self.env['sos_inventory_customers'].create({'customer_name': customer_name})
|
||||
|
||||
return super(SOS_SalesOrder, self).create(vals)
|
||||
# return super(SOS_SalesOrder, self).create(vals)
|
||||
|
||||
class SOS_SalesOrder_Line(models.Model):
|
||||
_name = 'sos_sales_order_line'
|
||||
|
|
|
|||
|
|
@ -86,131 +86,192 @@
|
|||
}
|
||||
</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"/>
|
||||
<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-foreach="o.line_ids_fg or []" t-as="fg_item">
|
||||
<t t-set="fg_index_safe" t-value="fg_index if fg_index is not None else 0"/>
|
||||
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_index_safe + 1)]"/>
|
||||
</t>
|
||||
<!-- Add SFG tables -->
|
||||
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item">
|
||||
<t t-set="sfg_index_safe" t-value="sfg_index if sfg_index is not None else 0"/>
|
||||
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_index_safe + 1)]"/>
|
||||
</t>
|
||||
<!-- Add Materials tables -->
|
||||
<t t-foreach="o.line_ids_material or []" t-as="material_item">
|
||||
<t t-set="material_index_safe" t-value="material_index if material_index is not None else 0"/>
|
||||
<t t-set="all_tables" t-value="all_tables + [(3, material_item, material_index_safe + 1)]"/>
|
||||
</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) -->
|
||||
<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></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>
|
||||
<!-- 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>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="empty-col"></div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</div> <!-- Close row -->
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</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>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="column">Serial No</td><td colspan="3"><t t-esc="o.serial_no"/></td>
|
||||
|
||||
</tr>
|
||||
|
||||
</t>
|
||||
<t t-else="">
|
||||
<tr>
|
||||
|
|
@ -56,10 +53,7 @@
|
|||
<td class="column">Sampling Size</td><td><t t-esc="o.sampling_size"/></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="column">Serial No</td><td colspan="3"><t t-esc="o.serial_no"/></td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
</t>
|
||||
<tr>
|
||||
|
|
@ -74,7 +68,21 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</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">
|
||||
<tbody>
|
||||
<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_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_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_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
|
||||
|
|
|
|||
|
|
|
@ -43,10 +43,12 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Model Form">
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
<button name="action_report_boq" string="Report"
|
||||
type="object" class="btn-primary"/>
|
||||
</header>
|
||||
<sheet>
|
||||
|
||||
|
|
@ -71,7 +73,7 @@
|
|||
<field name="show_ev_fields" invisible="1"/>
|
||||
<field name="master_quantity" invisible="show_ev_fields == False"/>
|
||||
<field name="slave_quantity" invisible="show_ev_fields == False"/>
|
||||
|
||||
<field name="batter_or_cells"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
|
|
@ -108,6 +110,7 @@
|
|||
<field name="line_ids_material">
|
||||
<tree editable="bottom">
|
||||
<field name="component_id"/>
|
||||
<field name="display_name"/>
|
||||
<field name="add_production_cost" widget="boolean_toggle"/>
|
||||
<field name="is_spare" widget="boolean_toggle"/>
|
||||
|
||||
|
|
@ -122,6 +125,7 @@
|
|||
<field name="line_ids_installation_kit">
|
||||
<tree editable="bottom">
|
||||
<field name="component_id"/>
|
||||
<field name="display_name"/>
|
||||
<field name="uom"/>
|
||||
<field name="singet_set_qty"/>
|
||||
<field name="total_set"/>
|
||||
|
|
@ -285,13 +289,55 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-4"></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>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 -->
|
||||
<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>Verified By</b>
|
||||
<td style="padding: 8px;" class="column"><b>QA Verified By</b>
|
||||
<br></br><br></br>
|
||||
<button string="Approve" invisible="verified_by_image" class="btn-primary custom_btn" type="object" name="action_verified_esign_btn"></button>
|
||||
</td>
|
||||
|
|
@ -307,6 +353,8 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-3"></div>
|
||||
|
||||
</div>
|
||||
|
||||
</sheet>
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@
|
|||
<field name="line_ids_material" >
|
||||
<tree editable="bottom">
|
||||
<field name="component_id"/>
|
||||
<field name="display_name"/>
|
||||
<field name="is_spare" widget="boolean_toggle"/>
|
||||
|
||||
<field name="uom"/>
|
||||
|
|
@ -122,6 +123,7 @@
|
|||
<field name="line_ids_installation_kit" >
|
||||
<tree editable="bottom">
|
||||
<field name="component_id"/>
|
||||
<field name="display_name"/>
|
||||
<field name="uom"/>
|
||||
<field name="singet_set_qty"/>
|
||||
<field name="total_set"/>
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@
|
|||
<field name="fg_name" widget="many2many_tags"/>
|
||||
<field name="product_type"/>
|
||||
|
||||
<field name="customer_name"/>
|
||||
|
||||
</group>
|
||||
<group>
|
||||
<field name="customer_name"/>
|
||||
<field name="batch_No"/>
|
||||
<field name="serial_no"/>
|
||||
<!-- <field name="serial_no"/> -->
|
||||
<field name="mfg_date"/>
|
||||
<field name="sampling_size"/>
|
||||
<field name="remarks"/>
|
||||
|
|
@ -61,6 +62,13 @@
|
|||
</field>
|
||||
</page>
|
||||
<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">
|
||||
<tree editable="bottom">
|
||||
<field name="serial_no"/>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Accessories">
|
||||
<page string="Product / Accessories">
|
||||
|
||||
<div class="new_header">Finished Goods</div>
|
||||
<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)])
|
||||
move_div_display = fields.Boolean(default=False,string="Moved to Sales")
|
||||
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')
|
||||
def _onchange_expo(self):
|
||||
if self.expo_option:
|
||||
self.source = "Expo"
|
||||
self.conference_option = False
|
||||
self.linkedin_groups_option = False
|
||||
self.linkedin_poll_option = False
|
||||
|
|
@ -53,6 +65,7 @@ class sos_marketing(models.Model):
|
|||
@api.onchange('conference_option')
|
||||
def _onchange_conference(self):
|
||||
if self.conference_option:
|
||||
self.source = "Conference"
|
||||
self.expo_option = False
|
||||
self.linkedin_groups_option = False
|
||||
self.linkedin_poll_option = False
|
||||
|
|
@ -62,6 +75,7 @@ class sos_marketing(models.Model):
|
|||
@api.onchange('linkedin_groups_option')
|
||||
def _onchange_linkedin_groups(self):
|
||||
if self.linkedin_groups_option:
|
||||
self.source = "LinkedIn Groups"
|
||||
self.expo_option = False
|
||||
self.conference_option = False
|
||||
self.linkedin_poll_option = False
|
||||
|
|
@ -71,6 +85,7 @@ class sos_marketing(models.Model):
|
|||
@api.onchange('linkedin_poll_option')
|
||||
def _onchange_linkedin_poll(self):
|
||||
if self.linkedin_poll_option:
|
||||
self.source = "LinkedIn Poll"
|
||||
self.expo_option = False
|
||||
self.conference_option = False
|
||||
self.linkedin_groups_option = False
|
||||
|
|
@ -80,6 +95,7 @@ class sos_marketing(models.Model):
|
|||
@api.onchange('direct_company')
|
||||
def _onchange_direct_company(self):
|
||||
if self.direct_company:
|
||||
self.source = "Direct Company"
|
||||
self.expo_option = False
|
||||
self.conference_option = False
|
||||
self.linkedin_groups_option = False
|
||||
|
|
@ -89,6 +105,7 @@ class sos_marketing(models.Model):
|
|||
@api.onchange('expo_participated_option')
|
||||
def _onchange_expo_participated(self):
|
||||
if self.expo_participated_option:
|
||||
self.source = "Expo [Paricipated]"
|
||||
self.expo_option = False
|
||||
self.conference_option = False
|
||||
self.linkedin_groups_option = False
|
||||
|
|
|
|||
|
|
@ -7,7 +7,25 @@
|
|||
<field name="view_mode">tree,form,kanban</field>
|
||||
</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">
|
||||
<field name="name">sos_marketing_leads.view.tree</field>
|
||||
|
|
@ -16,6 +34,7 @@
|
|||
<tree>
|
||||
|
||||
<field name="company_name"/>
|
||||
<field name="source"/>
|
||||
|
||||
|
||||
|
||||
|
|
@ -55,6 +74,7 @@
|
|||
</group>
|
||||
<!-- Second Column -->
|
||||
<group>
|
||||
<field name="source" invisible="1"/>
|
||||
<field name="website_url"/>
|
||||
<field name="location"/>
|
||||
</group>
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +1,7 @@
|
|||
from odoo import models, fields, api
|
||||
import math
|
||||
from collections import defaultdict
|
||||
from odoo.tools.misc import format_amount
|
||||
|
||||
class Battery_Installation_Requirement(models.Model):
|
||||
_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_ups = fields.Integer(string="Number of UPS",store=True)
|
||||
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)")
|
||||
products = fields.Selection([
|
||||
('BHMS 1.2V', 'BHMS 1.2V'),
|
||||
|
|
@ -69,13 +72,19 @@ class Battery_Installation_Requirement(models.Model):
|
|||
#CE Team Fields
|
||||
engineers_nos=fields.Integer(string="No of engineers required")
|
||||
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)
|
||||
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')
|
||||
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')
|
||||
|
||||
# mode_of_transport = fields.Selection([('car','🚗 Car'),('bus','🚌 Bus'),('train','🚂 Train'),('flight','🛩️ Flight')],string='Mode of Transport',default="train")
|
||||
# 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')
|
||||
# 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')
|
||||
#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)
|
||||
|
|
@ -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_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)
|
||||
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
|
||||
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
|
||||
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_ups2 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 2",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)
|
||||
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_ups5 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 5",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)
|
||||
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_ups8 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 8",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)
|
||||
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_ups11 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 11",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)
|
||||
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_ups14 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 14",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)
|
||||
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,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,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,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,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,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,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,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,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,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,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,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,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,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,readonly=False)
|
||||
|
||||
|
||||
#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_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")
|
||||
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
|
||||
def create(self, vals):
|
||||
res = super().create(vals)
|
||||
|
|
@ -225,26 +690,29 @@ class Battery_Installation_Requirement(models.Model):
|
|||
for rec in related_records:
|
||||
rec._compute_acc_costing_submitted_by()
|
||||
|
||||
@api.depends('no_of_days')
|
||||
def _compute_local_expense(self):
|
||||
for rec in self:
|
||||
rec.local_expense = rec.no_of_days * 500
|
||||
@api.depends('no_of_days')
|
||||
def _compute_stay_expense(self):
|
||||
for rec in self:
|
||||
rec.stay_expense = rec.no_of_days * 2500
|
||||
@api.depends('no_of_days')
|
||||
def _compute_food_expense(self):
|
||||
for rec in self:
|
||||
rec.food_expense = rec.no_of_days * 600
|
||||
@api.depends('no_of_days')
|
||||
def _compute_transport_expense(self):
|
||||
for rec in self:
|
||||
rec.transport_expense = 5000
|
||||
@api.depends('no_of_days','transport_expense','food_expense','stay_expense','local_expense')
|
||||
# @api.depends('no_of_days')
|
||||
# def _compute_local_expense(self):
|
||||
# for rec in self:
|
||||
# rec.local_expense = rec.no_of_days * 500
|
||||
# @api.depends('no_of_days')
|
||||
# def _compute_stay_expense(self):
|
||||
# for rec in self:
|
||||
# rec.stay_expense = rec.no_of_days * 2500
|
||||
# @api.depends('no_of_days')
|
||||
# def _compute_food_expense(self):
|
||||
# for rec in self:
|
||||
# rec.food_expense = rec.no_of_days * 600
|
||||
# @api.depends('no_of_days')
|
||||
# def _compute_transport_expense(self):
|
||||
# for rec in self:
|
||||
# rec.transport_expense = 5000
|
||||
|
||||
@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):
|
||||
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')
|
||||
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.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_ups = self.proposal_id.number_of_ups
|
||||
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)
|
||||
if matching_ups_line:
|
||||
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, {
|
||||
'component_id': line.component_id.id,
|
||||
'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,
|
||||
'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,
|
||||
'ups_index': ups_index
|
||||
}))
|
||||
|
||||
for line in record.sfg_ids:
|
||||
if line.item_type == 'Master Panel':
|
||||
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)
|
||||
def action_ce_esign_btn(self):
|
||||
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.send_group_email(self.env,'sos_proposal_boq',self.id,"deenalaura.m@sosaley.in","Proposal System - BOQ Submitted",body_html,'sos_inventory.sos_finance_user')
|
||||
sequence_util.send_group_email(
|
||||
self.env,
|
||||
'sos_proposal_boq',
|
||||
self.id,
|
||||
"deenalaura.m@sosaley.in",
|
||||
f"Proposal System - BOQ Submitted for {self.customer_name}",
|
||||
body_html,
|
||||
'sos_inventory.sos_finance_user'
|
||||
)
|
||||
return sequence_util.action_assign_signature(
|
||||
self,
|
||||
'boq_submitted_by_name',
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class Battery_Installation_Requirement(models.Model):
|
|||
number_of_strings = fields.Integer(string="Number of Strings")
|
||||
number_of_ups = fields.Integer(string="Number of UPS")
|
||||
ups_rating_kva = fields.Integer(string="UPS Rating (KVA)")
|
||||
no_of_electrical_panel = fields.Integer(string="No of Electrical Panel")
|
||||
battery_capacity_ah = fields.Integer(string="Battery Capacity (AH)")
|
||||
specific_requirements = fields.Html(string="Specific Requirements")
|
||||
products = fields.Selection(
|
||||
|
|
@ -147,6 +148,7 @@ class Battery_Installation_Requirement(models.Model):
|
|||
@api.onchange('number_of_ups')
|
||||
def _onchange_number_of_ups(self):
|
||||
if self.number_of_ups is not None:
|
||||
self.no_of_electrical_panel = self.number_of_ups
|
||||
if self.number_of_ups > 15:
|
||||
raise ValidationError("Number of UPS cannot be more than 15.")
|
||||
|
||||
|
|
|
|||
|
|
@ -694,16 +694,31 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
|
|||
report.write({
|
||||
new_field_billed: (getattr(report, new_field_billed, 0.0) or 0.0) + new_billed_amount
|
||||
})
|
||||
# Optionally create billing collection entry (if needed)
|
||||
if new_billed_amount > 0:
|
||||
# Optionally create billing collection entry (if needed
|
||||
domain = [
|
||||
('ref_id', '=', report.id),
|
||||
('sales_person', '=', report.sales_person.id),
|
||||
('customer_name', '=', vals.get('customer_name', rec.customer_name.id))
|
||||
]
|
||||
|
||||
existing = self.env['sos_billing_collection'].search(domain, limit=1)
|
||||
|
||||
if not existing:
|
||||
self.env['sos_billing_collection'].create({
|
||||
'ref_id': report.id,
|
||||
'customer_name': vals.get('customer_name', rec.customer_name.id),
|
||||
'sales_person': report.sales_person.id,
|
||||
'action_status': 'Billed',
|
||||
'date_of_action': new_billed_date,
|
||||
'po_no':vals.get('po_no'),
|
||||
'value': new_billed_amount
|
||||
})
|
||||
else:
|
||||
existing.write({
|
||||
'value': new_billed_amount,
|
||||
'po_no':vals.get('po_no'),
|
||||
'date_of_action': new_billed_date
|
||||
})
|
||||
|
||||
return super(SOS_Sales_Achievement_Report_Brief, self).write(vals)
|
||||
|
||||
|
|
@ -748,7 +763,8 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
|
|||
'sales_person': report.sales_person.id,
|
||||
'action_status': 'Billed',
|
||||
'date_of_action': billed_date,
|
||||
'value': billed_value
|
||||
'value': billed_value,
|
||||
'po_no':vals.get('po_no')
|
||||
})
|
||||
new_record = super(SOS_Sales_Achievement_Report_Brief, self).create(vals)
|
||||
return new_record
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@
|
|||
|
||||
<group>
|
||||
<group>
|
||||
<field name="proposal_id"/>
|
||||
<field name="customer_name"/>
|
||||
<field name="proposal_id" readonly="acc_approved_by_name"/>
|
||||
<field name="customer_name" readonly="acc_approved_by_name"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="location"/>
|
||||
<field name="products"/>
|
||||
<field name="location" readonly="acc_approved_by_name"/>
|
||||
<field name="products" readonly="acc_approved_by_name"/>
|
||||
</group>
|
||||
</group>
|
||||
<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>
|
||||
<group>
|
||||
<group>
|
||||
<field name="communication_type"/>
|
||||
<field name="number_of_batteries"/>
|
||||
<field name="number_of_ups"/>
|
||||
<field name="communication_type" readonly="acc_approved_by_name"/>
|
||||
<field name="number_of_batteries" readonly="acc_approved_by_name"/>
|
||||
<field name="number_of_ups" readonly="acc_approved_by_name"/>
|
||||
|
||||
</group>
|
||||
<group>
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
<notebook>
|
||||
<!-- Page 1 -->
|
||||
|
||||
<page string="UPS 1" invisible="number_of_ups < 1">
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Finished Goods</h3>
|
||||
|
|
@ -111,7 +112,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -219,7 +220,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -328,7 +329,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -436,7 +437,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -544,7 +545,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -651,7 +652,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -759,7 +760,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -867,7 +868,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -975,7 +976,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1083,7 +1084,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1190,7 +1191,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1297,7 +1298,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1404,7 +1405,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1511,7 +1512,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1618,7 +1619,7 @@
|
|||
</field>
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
<field name="production_cost" widget="boolean_toggle"/>
|
||||
|
|
@ -1654,116 +1655,42 @@
|
|||
</div>
|
||||
</div>
|
||||
</page>
|
||||
<!-- <page string="Finished Goods">
|
||||
<field name="line_ids_fg">
|
||||
<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_fg_cost"/>
|
||||
</group>
|
||||
</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 string="Summary" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user">
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Finished Goods</h3>
|
||||
<field name="merged_fg_html" nolabel="1"/>
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Semi-Finished Goods</h3>
|
||||
<field name="merged_sfg_html" nolabel="1"/>
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Materials</h3>
|
||||
<field name="merged_material_html" nolabel="1"/>
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Installation Kit</h3>
|
||||
<field name="merged_installation_kit_html" nolabel="1"/>
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Spare</h3>
|
||||
<field name="merged_spare_html" nolabel="1"/>
|
||||
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Miscellaneous</h3>
|
||||
<field name="merged_miscellaneous_html" nolabel="1"/>
|
||||
|
||||
</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">
|
||||
<group>
|
||||
<field name="engineers_nos"/>
|
||||
<field name="no_of_days"/>
|
||||
<field name="mode_of_transport"/>
|
||||
<field name="transport_expense" 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"/>
|
||||
<field name="stay_expense" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
||||
<field name="local_expense" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
||||
<field name="iandc_costing" groups="sos_inventory.sos_management_user,sos_inventory.sos_finance_user"/>
|
||||
</group>
|
||||
|
||||
<group><field name="engineers_nos"/></group>
|
||||
<group><field name="no_of_days"/></group>
|
||||
<br></br>
|
||||
<table class="table_custom" 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>
|
||||
<tbody>
|
||||
<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>
|
||||
<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>
|
||||
<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 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">
|
||||
<h3 style="text-transform: uppercase;
|
||||
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">
|
||||
<field name="field_name"/>
|
||||
<field name="cost"/>
|
||||
|
|
@ -1812,20 +1739,20 @@
|
|||
<table class="table">
|
||||
<h3 style="text-transform: uppercase;
|
||||
text-decoration: underline;">Final Cost</h3>
|
||||
<tr><td class="column">Product Cost</td><td><field name="total_cost"/></td></tr>
|
||||
<tr><td class="column">Opreational Cost per Battery</td><td><field name="margin_per_battery"/></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" readonly="acc_approved_by_name"/></td></tr>
|
||||
|
||||
<tr><td class="column">Opreational Cost</td><td><field name="margin"/></td></tr>
|
||||
<tr><td class="column">Packing & Forwarding</td><td><field name="packing_and_forwarding"/></td></tr>
|
||||
<tr><td class="column">Installation & Commissioning</td><td><field name="iandc_costing"/></td></tr>
|
||||
<tr><td class="column">Warranty (%)</td><td><field name="warranty_percentage"/></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" readonly="acc_approved_by_name"/></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" 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 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><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" 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>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
<group>
|
||||
<field name="type_of_source"/>
|
||||
<field name="number_of_ups" string="Number of UPS"/>
|
||||
<field name="no_of_electrical_panel"/>
|
||||
<field name="ups_line_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="ups_index" column_invisible="1"/>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<tree>
|
||||
<field name="company_name"/>
|
||||
<field name="location"/>
|
||||
<field name="vertical_domain"/>
|
||||
<field name="vertical_domain" widget="many2many_tags"/>
|
||||
<field name="interested_in"/>
|
||||
<field name="lead_generated_by"/>
|
||||
<field name="status" decoration-success="status == 'Moved to Customer'"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue