bemade_fsm: Work order report printing relevant SO lines summary. Re Dur-Pro/bemade-addons#94

This commit is contained in:
Marc Durepos 2023-08-03 15:15:22 -04:00
parent f157ba3d9b
commit c4fdcd94b3
5 changed files with 227 additions and 8 deletions

View file

@ -37,6 +37,7 @@
'worksheet',
'industry_fsm_stock',
'industry_fsm_report',
'industry_fsm_sale_report',
'bemade_partner_root_ancestor',
'mail',
],
@ -50,7 +51,8 @@
'views/menus.xml',
'views/task_views.xml',
'views/sale_order_views.xml',
'reports/worksheet_templates.xml',
'reports/worksheet_custom_report_templates.xml',
'reports/worksheet_custom_reports.xml',
],
'assets': {
'web.assets_tests': [

View file

@ -36,6 +36,16 @@ class SaleOrder(models.Model):
inverse_name="sale_order_id",
readonly=False)
@api.depends('order_line.task_id')
def get_relevant_order_lines(self, task_id):
self.ensure_one()
linked_lines = self.order_line.filtered(lambda l: l.task_id == task_id
or l == task_id.visit_id.so_section_id)
visit_lines = linked_lines.filtered(lambda l: l.visit_id)
for line in visit_lines:
linked_lines |= line.get_section_line_ids()
return linked_lines
@api.depends('order_line.equipment_ids')
def _compute_summary_equipment_ids(self):
for rec in self:
@ -212,7 +222,7 @@ class SaleOrderLine(models.Model):
'visit_id': self.visit_id.id,
'date_deadline': self.visit_id.approx_date,
'planned_hours': self.task_duration,
'user_ids': False, # Force to empty or it uses the current user
'user_ids': False, # Force to empty or it uses the current user
})
return task

View file

@ -49,12 +49,14 @@ class Task(models.Model):
visit_id = fields.Many2one(comodel_name='bemade_fsm.visit')
# user_id = fields.Many2one('res.users', compute='_compute_user_id')
#
# @api.depends('user_ids')
# def _compute_user_id(self):
# for rec in self:
# rec.user_id = rec.user_ids and rec.user_ids[0] or 0
relevant_order_lines = fields.Many2many(comodel_name='sale.order.line',
store=False,
compute='_compute_relevant_order_lines',)
@api.depends('sale_order_id')
def _compute_relevant_order_lines(self):
for rec in self:
rec.relevant_order_lines = rec.sale_order_id \
and rec.sale_order_id.get_relevant_order_lines(rec)
def _get_closed_stage_by_project(self):
""" Gets the stage representing completed tasks for each project in
@ -176,3 +178,7 @@ class Task(models.Model):
rec.name += f" ({template.name})"
else:
rec.name = f"{template.name or rec.sale_line_id.name or rec.name}"
@property
def root_ancestor(self):
return self.parent_id and self.parent_id.root_ancestor or self

View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<template id="work_order_page">
<div class="page">
<div class="row">
<div t-attf-class="{{'col-6' if report_type == 'pdf' else 'col-md-6 col-12'}}">
<div t-if="doc.user_ids"><strong>Technicians: </strong></div>
<t t-foreach="doc.user_ids" t-as="user">
<div class="mb-3">
<div t-esc="user" t-options='{
"widget": "contact",
"fields": ["name", "address", "phone", "email"]
}'/>
</div>
</t>
</div>
<div t-attf-class="{{('col-6' if report_type == 'pdf' else 'col-md-6 col-12') + ' mb-3'}}">
<t t-if="doc.partner_id">
<div><strong>Customer: </strong></div>
<div t-esc="doc.partner_id" t-options='{
"widget": "contact",
"fields": ["name", "address",]
}'/>
</t>
</div>
<div t-attf-class="{{('col-6' if report_type == 'pdf' else 'col-md-6 col-12') + ' mb-3'}}">
<span>Company: <span t-out="company"/></span>
</div>
</div>
<t t-set="order_lines" t-value="doc.relevant_order_lines"/>
<t t-if="order_lines">
<t t-set="visit_lines" t-value="order_lines.mapped('visit_id')"/>
<t t-if="visit_lines" t-set="root_tasks"
t-value="visit_lines.mapped('task_id')"/>
<t t-else="" t-set="root_tasks"
t-value="order_lines.mapped('task_id').filtered(lambda l: not l.parent_id)"/>
<t t-set="final_subtotal" t-value="0"/>
<t t-set="final_tax" t-value="0"/>
<t t-set="final_total" t-value="0"/>
<t t-set="is_any_total_discount_line" t-value="False"/>
<t t-set="section_lines" t-value="order_lines.filtered(lambda l: l.display_type == 'line_section')"/>
<t t-foreach="order_lines" t-as="line">
<!-- <t t-set="is_task" t-value="line == doc.sale_line_id"/>-->
<t t-set="final_subtotal"
t-value="final_subtotal + line.delivered_price_subtotal"/>
<t t-set="final_total"
t-value="final_total + line.delivered_price_total"/>
<t t-set="final_tax"
t-value="final_tax + line.delivered_price_tax"/>
<t t-set="is_any_total_discount_line"
t-value="is_any_total_discount_line or (line.discount and line.price_unit != 0 and line.delivered_price_total == 0)"/>
</t>
<t t-set="display_discount"
t-value="any(line.discount for line in order_lines)"/>
</t>
<t t-else="" t-set="root_tasks"
t-value="doc.root_ancestor"/>
<h2 t-if="order_lines">Time &amp; Material</h2>
<div t-if="order_lines" class="table-responsive-sm">
<table class="table table-sm o_main_table">
<thead>
<tr>
<th class="text-left">Description</th>
<th class="text-right">Ordered</th>
<th class="text-right">Delivered</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right"
groups="product.group_discount_per_so_line">
<span>Disc.%</span>
</th>
<th class="text-right">
<span groups="account.group_show_line_subtotals_tax_excluded">
Amount</span>
<span groups="account.group_show_line_subtotals_tax_included">
Total Price</span>
</th>
</tr>
</thead>
<tbody class="sale_tbody">
<t t-set="current_subtotal" t-value="0"/>
<t t-foreach="order_lines" t-as="line">
<t t-set="is_task" t-value="line == doc.sale_line_id"/>
<t t-set="is_total_discount"
t-value="line.discount and line.price_unit != 0 and line.delivered_price_total == 0"/>
<t t-set="current_subtotal"
t-value="current_subtotal + line.delivered_price_subtotal"
groups="account.group_show_line_subtotals_tax_excluded"/>
<t t-set="current_total"
t-value="current_subtotal + line.delivered_price_total"
groups="account.group_show_line_subtotals_tax_included"/>
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
<t t-if="not line.display_type and not line.is_downpayment">
<td><span t-field="line.name"/></td>
<td class="text-right">
<span t-field="line.product_uom_qty"/>
<span t-field="line.product_uom"
groups="uom.group_uom"/>
</td>
<td class="text-right">
<span t-field="line.qty_delivered"/>
<span t-field="line.product_uom"
groups="uom.group_uom"/>
</td>
<td class="text-right">
<span t-field="line.price_unit"/>
</td>
<td t-if="display_discount"
class="text-right"
groups="product.group_discount_per_so_line">
<span t-field="line.discount"/>
</td>
<td class="text-right o_price_total">
<span t-field="line.delivered_price_subtotal"/>
</td>
</t>
<t t-if="line.display_type == 'line_section'">
<td colspan="99">
<span t-field="line.name"/>
</td>
<t t-set="current_section" t-value="line"/>
<t t-set="current_subtotal" t-value="0"/>
</t>
<t t-if="line.display_type == 'line_note'">
<td colspan="99">
<span t-field="line.name"/>
</td>
</t>
</tr>
<t t-if="current_section and len(section_lines) > 1 and (line_last or order_lines[line_index+1].display_type == 'line_section')">
<tr class="is-subtotal text-right">
<td colspan="99">
<strong class="mr16">Section Subtotal</strong>
<span
t-esc="current_subtotal"
t-options='{"widget": "monetary", "display_currency": doc.sale_order_id.pricelist_id.currency_id}'
/>
</td>
</tr>
</t>
</t>
</tbody>
</table>
</div>
<div class="container_subtotal" name="so_total_summary">
<div id="total" class="row justify-content-end" name="total">
<div t-attf-class="#{'col-auto' if report_type != 'html' else 'col-sm-2'}">
<table class="table table-sm">
<tr t-if="final_tax" class="border-black o_subtotal" style="">
<td><strong>Untaxed amount</strong></td>
<td class="text-right">
<span t-esc="final_subtotal" t-options='{"widget": "monetary", "display_currency": doc.sale_order_id.pricelist_id.currency_id}'/>
</td>
</tr>
<tr t-if="final_tax">
<td><strong>Taxes</strong></td>
<td class="text-right">
<span t-esc="final_tax" t-options='{"widget": "monetary", "display_currency": doc.sale_order_id.pricelist_id.currency_id}'/>
</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td class="text-right">
<span t-esc="final_total" t-options='{"widget": "monetary", "display_currency": doc.sale_order_id.pricelist_id.currency_id}'/>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</template>
<template id="work_order">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<t t-call="bemade_fsm.work_order_page"
t-lang="doc.partner_id.lang"/>
</t>
</t>
</t>
</template>
</data>
</odoo>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="industry_fsm_report.task_custom_report" model="ir.actions.report">
<field name="name">Work Order Report (PDF)</field>
<field name="report_name">bemade_fsm.work_order</field>
<field name="report_file">bemade_fsm.work_order</field>
<field name="print_report_name">'%s Work Order %s' % (time.strftime('%Y-%m-%d'), object.name)</field>
<field name="report_type">qweb-html</field>
</record>
</data>
</odoo>