[IMP] stock, stock_*: adapt reports to studio report editor

Includes several modifications to better work with the new editor:
- add placeholders on t-field/t-out nodes
- add several div.oe_structure containers in key places for easier user
edition
- add branching options for several t-if directives without a t-else to
display for users (e.g. displaying the payment QR code if the value is
set vs. when the value is not set). These branching options are set to a
default value (the t-if node) which is what users see by default in the
report editor; the t-else node is a version they can manually toggle if
they wish)
- avoiding 'naked' t nodes as child of table/thead/tbody/tr elements, as
these break the table layout in chomium-based engines (that we know of);
instead, try to have foreach loops on tr or td nodes directly, or hoist
t-set directives above the table node
- replace several div or p (block) nodes by span or other inline nodes
to make it easier for users to insert content in their vicinity

X-original-commit: 567b8d676b3b6dc747df4d9bf10e7a91b4cb61bb
Part-of: odoo/odoo#129492
This commit is contained in:
Damien Bouvy
2023-07-25 00:54:52 +02:00
parent 1b072d55c6
commit 86d3b4f51f
4 changed files with 186 additions and 149 deletions
+36 -40
View File
@@ -43,23 +43,21 @@
</t>
<div class="page">
<h2>
<span t-field="o.name"/>
<span t-field="o.name">WH/OUT/0001</span>
</h2>
<div class="oe_structure"></div>
<div class="row mt32 mb32">
<div t-if="o.origin" class="col-auto" name="div_origin">
<strong>Order:</strong>
<p t-field="o.origin"/>
<p t-field="o.origin">S0001</p>
</div>
<div t-if="o.state" class="col-auto" name="div_sched_date">
<strong>Shipping Date:</strong>
<t t-if="o.state == 'done'">
<p t-field="o.date_done"/>
</t>
<t t-if="o.state != 'done'">
<p t-field="o.scheduled_date"/>
</t>
<p t-if="o.state == 'done'" t-field="o.date_done"/>
<p t-else="" t-field="o.scheduled_date"/>
</div>
</div>
<div class="oe_structure"></div>
<table class="table table-sm" t-if="o.state!='done'" name="stock_move_table">
<thead>
<tr>
@@ -72,36 +70,34 @@
<t t-set="lines" t-value="o.move_ids.filtered(lambda x: x.product_uom_qty)"/>
<tr t-foreach="lines" t-as="move">
<td>
<span t-field="move.product_id"/>
<span t-field="move.product_id">Customizable Desk</span>
<p t-if="move.description_picking != move.product_id.name and move.description_picking != move.product_id.display_name">
<span t-field="move.description_picking"/>
<span t-field="move.description_picking">Description on transfer</span>
</p>
</td>
<td>
<span t-field="move.product_uom_qty"/>
<span t-field="move.product_uom"/>
<span t-field="move.product_uom_qty">3.00</span>
<span t-field="move.product_uom">units</span>
</td>
<td>
<span t-field="move.quantity_done"/>
<span t-field="move.product_uom"/>
<span t-field="move.quantity_done">3.00</span>
<span t-field="move.product_uom">units</span>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm mt48" t-if="o.move_line_ids and o.state=='done'" name="stock_move_line_table">
<table class="table table-sm mt48" t-elif="o.move_line_ids and o.state=='done'" name="stock_move_line_table">
<t t-set="has_serial_number" t-value="False"/>
<t t-set="has_serial_number" t-value="o.move_line_ids.mapped('lot_id')" groups="stock.group_lot_on_delivery_slip"/>
<thead>
<tr>
<th name="th_sml_product"><strong>Product</strong></th>
<t name="lot_serial" t-if="has_serial_number">
<th>
Lot/Serial Number
</th>
</t>
<th name="th_sml_qty_ordered" class="text-center" t-if="not has_serial_number">
<strong>Ordered</strong>
</th>
<th name="lot_serial" t-else="">
Lot/Serial Number
</th>
<th name="th_sml_quantity" class="text-center"><strong>Delivered</strong></th>
</tr>
</thead>
@@ -159,8 +155,10 @@
</t>
</tbody>
</table>
<div class="oe_structure"></div>
<t t-set="backorders" t-value="o.backorder_ids.filtered(lambda x: x.state not in ('done', 'cancel'))"/>
<t t-if="o.backorder_ids and backorders">
<div t-if="not (o.backorder_ids and backorders)" class="oe_structure"></div>
<div t-else="">
<p class="mt-5">
<span>Remaining quantities not yet delivered:</span>
</p>
@@ -173,27 +171,25 @@
</tr>
</thead>
<tbody>
<t t-foreach="backorders" t-as="backorder">
<t t-set="bo_lines" t-value="backorder.move_ids.filtered(lambda x: x.product_uom_qty)"/>
<tr t-foreach="bo_lines" t-as="bo_line">
<td class="w-auto">
<span t-field="bo_line.product_id"/>
<p t-if="bo_line.description_picking != bo_line.product_id.name and bo_line.description_picking != bo_line.product_id.display_name">
<span t-field="bo_line.description_picking"/>
</p>
</td>
<td/>
<td class="text-center w-auto">
<span t-field="bo_line.product_uom_qty"/>
<span t-field="bo_line.product_uom"/>
</td>
</tr>
</t>
<tr t-foreach="backorders.mapped('move_ids').filtered(lambda x: x.product_uom_qty)" t-as="bo_line">
<td class="w-auto">
<span t-field="bo_line.product_id">Office Chair</span>
<p t-if="bo_line.description_picking != bo_line.product_id.name and bo_line.description_picking != bo_line.product_id.display_name">
<span t-field="bo_line.description_picking">Description on transfer</span>
</p>
</td>
<td/>
<td class="text-center w-auto">
<span t-field="bo_line.product_uom_qty">3.00</span>
<span t-field="bo_line.product_uom">units</span>
</td>
</tr>
</tbody>
</table>
</t>
</div>
<div t-if="o.signature" class="mt32 ml64 mr4" name="signature">
<div t-if="not o.signature" class="oe_structure"></div>
<div t-else="" class="mt32 ml64 mr4" name="signature">
<div class="offset-8">
<strong>Signature</strong>
</div>
@@ -201,7 +197,7 @@
<img t-att-src="image_data_uri(o.signature)" style="max-height: 4cm; max-width: 8cm;"/>
</div>
<div class="offset-8 text-center">
<p t-field="o.partner_id.name"/>
<p t-field="o.partner_id.name">John Doe</p>
</div>
</div>
</div>
+38 -30
View File
@@ -10,39 +10,43 @@
<t>
<div class="page">
<div class="oe_structure"/>
<table class="table table-condensed" style="border-bottom: 0px solid white !important;">
<tr>
<th>
<h1 t-field="o.name" class="mt0 float-start"/>
</th>
<th name="td_pk_barcode" style="text-align: center">
<t t-if="o.valid_sscc">
<div class="row text-start">
<t t-set="barcode" t-value="'00' + o.name"/>
<t t-if="o.pack_date" t-set="barcode" t-value="barcode + '13' + o.pack_date.strftime('%y%m%d')"/>
<div class="col-3" name="datamatrix_barcode" style="margin: 0px 20px 40px 60px" t-out="barcode" t-options="{'widget': 'barcode', 'symbology': 'ECC200DataMatrix', 'width': 100, 'height': 100}"/>
</div>
</t>
<t t-else="">
<div t-field="o.name" t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;'}"/>
<p t-field="o.name"/>
</t>
</th>
</tr>
</table>
<div class="row mb-5">
<div class="col-6">
<h1><span t-field="o.name" class="mt0 float-start">Package Reference</span></h1>
</div>
<div class="col-6 d-flex flex-column justify-content-center align-item-center" t-if="o.valid_sscc">
<t t-set="barcode" t-value="'00' + o.name"/>
<t t-if="o.pack_date" t-set="barcode" t-value="barcode + '13' + o.pack_date.strftime('%y%m%d')"/>
<div name="datamatrix_barcode" t-out="barcode" t-options="{'widget': 'barcode', 'symbology': 'ECC200DataMatrix', 'width': 100, 'height': 100}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center px-1 py-2 opacity-75 text-muted text-center">
(package barcode)
</div>
</div>
</div>
<div t-else="" class="col-6 d-flex flex-column justify-content-center align-item-center text-center">
<div t-field="o.name" t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;'}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center px-1 py-2 opacity-75 text-muted text-center">
(package barcode)
</div>
</div>
<p t-field="o.name">Package Reference</p>
</div>
</div>
<div class="oe_structure"></div>
<div class="row mt32 mb32">
<div t-if="o.pack_date" class="col-auto">
<strong>Pack Date:</strong>
<p t-field="o.pack_date"/>
<p t-field="o.pack_date">2021-9-01</p>
</div>
<div t-if="o.package_type_id" class="o_packaging_type col-auto">
<strong>Package Type:</strong>
<p t-field="o.package_type_id.name"/>
<p t-field="o.package_type_id.name">Pallet</p>
</div>
</div>
<table class="table table-sm table-striped" style="border-bottom: 0px solid white !important;">
<t t-set="has_serial_number" t-value="o.quant_ids.mapped('lot_id')" />
<t t-set="has_ean_barcode" t-value="any(valid_ean for valid_ean in o.quant_ids.product_id.mapped('valid_ean'))" />
<div class="oe_structure"></div>
<t t-set="has_serial_number" t-value="o.quant_ids.mapped('lot_id')" />
<t t-set="has_ean_barcode" t-value="any(valid_ean for valid_ean in o.quant_ids.product_id.mapped('valid_ean'))" />
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Product</th>
@@ -55,16 +59,16 @@
<tbody>
<tr t-foreach="o.quant_ids" t-as="l">
<td>
<span t-field="l.product_id.name"/>
<span t-field="l.product_id.name">Customizable Desk</span>
</td>
<td class="text-end">
<span t-field="l.quantity"/>
<span t-field="l.quantity">3.00</span>
</td>
<td groups="uom.group_uom">
<span t-field="l.product_id.uom_id.name"/>
<span t-field="l.product_id.uom_id.name">units</span>
</td>
<td class="text-end" t-if="has_serial_number">
<t t-if="l.lot_id"><span t-field="l.lot_id.name"/></t>
<t t-if="l.lot_id"><span t-field="l.lot_id.name">54326786758</span></t>
</td>
<td class="text-end" t-if="has_ean_barcode">
<t t-if="l.product_id.valid_ean">
@@ -84,7 +88,11 @@
<!-- TODO: lot/sn ALWAYS has to be last part of barcode since left padding '0's = different sn/lot name match when scanning => fix when FNC1 can be used, -->
<t name="product_barcode_lot_datamatrix" t-if="l.product_id.tracking == 'lot' and l.lot_id" t-set="product_barcode" t-value="product_barcode + '10' + l.lot_id.name"/>
<t t-elif="l.product_id.tracking == 'serial' and l.lot_id" t-set="product_barcode" t-value="product_barcode + '21' + l.lot_id.name"/>
<span t-out="product_barcode" t-options="{'widget': 'barcode', 'symbology': 'ECC200DataMatrix', 'img_style': 'width:17mm;height:17mm'}"/>
<span t-out="product_barcode" t-options="{'widget': 'barcode', 'symbology': 'ECC200DataMatrix', 'img_style': 'width:17mm;height:17mm'}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center px-1 py-2 opacity-75 text-muted text-center">
(product barcode)
</div>
</span>
</t>
</td>
</tr>
@@ -6,10 +6,15 @@
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.external_layout">
<t t-set="address" t-value="None"/>
<div class="page">
<div class="row justify-content-end mb16">
<div class="col-4" name="right_box">
<div t-field="o.name" t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;'}"/>
<div t-field="o.name" t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;'}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center p-3 opacity-75 text-muted text-center">
(document barcode)
</div>
</div>
</div>
</div>
<div class="row">
@@ -17,12 +22,22 @@
<div t-if="o.should_print_delivery_address()">
<span><strong>Delivery Address:</strong></span>
<div t-field="o.move_ids[0].partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'>
<div class="bg-light border-1 rounded h-100 d-flex flex-column align-items-center justify-content-center p-4 opacity-75 text-muted text-center">
<strong>Delivery address</strong>
<div>Presence depends on the type of operation.</div>
</div>
</div>
</div>
<div t-elif="o.picking_type_id.code != 'internal' and o.picking_type_id.warehouse_id.partner_id">
<span><strong>Warehouse Address:</strong></span>
<div t-field="o.picking_type_id.warehouse_id.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'>
<div class="bg-light border-1 rounded h-100 d-flex flex-column align-items-center justify-content-center p-4 opacity-75 text-muted text-center">
<strong>Warehouse address</strong>
<div>Presence depends on the type of operation.</div>
</div>
</div>
</div>
</div>
<div class="col-5 offset-1" name="div_incoming_address">
@@ -31,34 +46,40 @@
<span><strong>Vendor Address:</strong></span>
<t t-set="show_partner" t-value="True" />
</div>
<div t-if="o.picking_type_id.code=='internal' and o.partner_id">
<div t-elif="o.picking_type_id.code=='internal' and o.partner_id">
<span><strong>Warehouse Address:</strong></span>
<t t-set="show_partner" t-value="True" />
</div>
<div t-if="o.picking_type_id.code=='outgoing' and o.partner_id and o.partner_id != o.partner_id.commercial_partner_id">
<div t-elif="o.picking_type_id.code=='outgoing' and o.partner_id and o.partner_id != o.partner_id.commercial_partner_id">
<span><strong>Customer Address:</strong></span>
<t t-set="show_partner" t-value="True" />
</div>
<div t-if="show_partner" name="partner_header">
<div t-field="o.partner_id.commercial_partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "vat"], "no_marker": True, "phone_icons": True}'/>
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "vat"], "no_marker": True, "phone_icons": True}'>
<div class="bg-light border-1 rounded h-100 d-flex flex-column align-items-center justify-content-center p-4 opacity-75 text-muted text-center">
<strong>Recipient address</strong>
<div>Presence depends on the type of operation.</div>
</div>
</div>
</div>
</div>
<div class="oe_structure"></div>
</div>
<br/>
<h1 t-field="o.name" class="mt0"/>
<div class="row mt48 mb32">
<h1 t-field="o.name" class="mt0">WH/OUT/00001</h1>
<div class="oe_structure"></div>
<div class="row mt32 mb32">
<div t-if="o.origin" class="col-auto" name="div_origin">
<strong>Order:</strong>
<p t-field="o.origin"/>
<p t-field="o.origin">S0001</p>
</div>
<div class="col-auto" name="div_state">
<strong>Status:</strong>
<p t-field="o.state"/>
<p t-field="o.state">Draft</p>
</div>
<div class="col-auto" name="div_sched_date">
<strong>Scheduled Date:</strong>
<p t-field="o.scheduled_date"/>
<p t-field="o.scheduled_date">2023-09-24</p>
</div>
</div>
<table class="table table-sm" t-if="o.move_line_ids and o.move_ids_without_package">
@@ -75,7 +96,7 @@
<th name="th_from" t-if="o.picking_type_id.code != 'incoming'" align="left" groups="stock.group_stock_multi_locations">
<strong>From</strong>
</th>
<th name="th_to" t-if="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">
<th name="th_to" t-elif="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">
<strong>To</strong>
</th>
<th name="th_serial_number" class="text-center" t-if="has_serial_number">
@@ -88,74 +109,90 @@
</thead>
<tbody>
<!-- In case you come across duplicated lines, ask NIM or LAP -->
<t t-foreach="o.move_line_ids_without_package" t-as="ml">
<tr>
<td>
<span t-field="ml.product_id.display_name"/><br/>
<span t-field="ml.product_id.description_picking"/>
</td>
<td>
<span t-if="o.state != 'done'" t-field="ml.reserved_uom_qty"/>
<span t-if="o.state == 'done'" t-field="ml.qty_done"/>
<span t-field="ml.product_uom_id" groups="uom.group_uom"/>
</td>
<td t-if="o.picking_type_id.code != 'incoming'" groups="stock.group_stock_multi_locations">
<span t-esc="ml.location_id.display_name"/>
<t t-if="ml.package_id">
<span t-field="ml.package_id"/>
</t>
</td>
<td t-if="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">
<div>
<span t-field="ml.location_dest_id"/>
<t t-if="ml.result_package_id">
<span t-field="ml.result_package_id"/>
</t>
</div>
</td>
<td class=" text-center h6" t-if="has_serial_number">
<div t-if="has_serial_number and (ml.lot_id or ml.lot_name)" t-esc="ml.lot_id.name or ml.lot_name" t-options="{'widget': 'barcode', 'humanreadable': 1, 'width': 400, 'height': 100, 'img_style': 'width:100%;height:35px;'}"/>
</td>
<td class="text-center" t-if="has_barcode">
<t t-if="product_barcode != ml.product_id.barcode">
<span t-if="ml.product_id and ml.product_id.barcode">
<div t-field="ml.product_id.barcode" t-options="{'widget': 'barcode', 'symbology': 'auto', 'width': 400, 'height': 100, 'quiet': 0, 'img_style': 'height:35px;'}"/>
</span>
<t t-set="product_barcode" t-value="ml.product_id.barcode"/>
<tr t-foreach="o.move_line_ids_without_package" t-as="ml">
<td>
<span t-field="ml.product_id.display_name">Customizable Desk</span><br/>
<span t-field="ml.product_id.description_picking">Description on transfer</span>
</td>
<td>
<span t-if="o.state != 'done'" t-field="ml.reserved_uom_qty">3.00</span>
<span t-else="" t-field="ml.qty_done">3.00</span>
<span t-field="ml.product_uom_id" groups="uom.group_uom">units</span>
</td>
<td t-if="o.picking_type_id.code != 'incoming'" groups="stock.group_stock_multi_locations">
<span t-esc="ml.location_id.display_name">WH/Stock</span>
<t t-if="ml.package_id">
<span t-field="ml.package_id">Package A</span>
</t>
</td>
</tr>
</t>
</td>
<td t-elif="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">
<div>
<span t-field="ml.location_dest_id">WH/Outgoing</span>
<t t-if="ml.result_package_id">
<span t-field="ml.result_package_id">Shelf 1</span>
</t>
</div>
</td>
<td class=" text-center h6" t-if="has_serial_number">
<span t-if="has_serial_number and (ml.lot_id or ml.lot_name)" t-esc="ml.lot_id.name or ml.lot_name" t-options="{'widget': 'barcode', 'humanreadable': 1, 'width': 400, 'height': 100, 'img_style': 'width:100%;height:35px;'}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center px-1 py-2 opacity-75 text-muted text-center">
(serial barcode)
</div>
</span>
</td>
<td class="text-center" t-if="has_barcode">
<t t-if="product_barcode != ml.product_id.barcode">
<span t-if="ml.product_id and ml.product_id.barcode">
<div t-field="ml.product_id.barcode" t-options="{'widget': 'barcode', 'symbology': 'auto', 'width': 400, 'height': 100, 'quiet': 0, 'img_style': 'height:35px;'}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center px-1 py-2 opacity-75 text-muted text-center">
(product barcode)
</div>
</div>
</span>
<t t-set="product_barcode" t-value="ml.product_id.barcode"/>
</t>
</td>
</tr>
</tbody>
</table>
<table class="table table-sm" t-if="o.package_level_ids and o.picking_type_entire_packs and o.state in ['assigned', 'done']">
<div t-if="not (o.package_level_ids and o.picking_type_entire_packs and o.state in ['assigned', 'done'])" class="oe_structure"></div>
<table t-else="" class="table table-sm">
<thead>
<tr>
<th name="th_package">Package</th>
<th name="th_pko_from" t-if="o.picking_type_id.code != 'incoming'" groups="stock.group_stock_multi_locations">From</th>
<th name="th_pki_from" t-if="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">To</th>
<th name="th_pki_from" t-elif="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">To</th>
</tr>
</thead>
<tbody>
<tr t-foreach="o.package_level_ids.sorted(key=lambda p: p.package_id.name)" t-as="package">
<t t-set="package" t-value="package.with_context(picking_id=o.id)" />
<td name="td_pk_barcode">
<div t-field="package.package_id.name" t-options="{'widget': 'barcode', 'humanreadable': 1, 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;margin-left: -50px;'}"/><br/>
<t t-set="package" t-value="package.with_context(picking_id=o.id)" />
<div t-field="package.package_id.name" t-options="{'widget': 'barcode', 'humanreadable': 1, 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;margin-left: -50px;'}">
<div class="bg-light border-1 rounded d-flex flex-column align-items-center justify-content-center px-1 py-2 opacity-75 text-muted text-center">
(package barcode)
</div>
</div>
<br/>
</td>
<td t-if="o.picking_type_id.code != 'incoming'" groups="stock.group_stock_multi_locations">
<span t-field="package.location_id"/>
</td>
<td t-if="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">
<td t-elif="o.picking_type_id.code != 'outgoing'" groups="stock.group_stock_multi_locations">
<span t-field="package.location_dest_id"/>
</td>
</tr>
</tbody>
</table>
<t t-set="no_reserved_product" t-value="o.move_ids.filtered(lambda x: x.product_uom_qty != x.reserved_availability and x.move_line_ids and x.state!='done')"/>
<p t-if="o.state in ['draft', 'waiting', 'confirmed'] or no_reserved_product"><i class="fa fa-exclamation-triangle" />
All products could not be reserved. Click on the "Check Availability" button to try to reserve products.
</p>
<p t-else=""/>
<div class="oe_structure"></div>
<p t-field="o.note"/>
<div class="oe_structure"></div>
</div>
</t>
</t>
+20 -24
View File
@@ -4,30 +4,26 @@
<xpath expr="//div[@id='right-elements']" position="after">
<t groups="stock_account.group_lot_on_invoice">
<t t-set="lot_values" t-value="o._get_invoiced_lot_values()"/>
<t t-if="lot_values">
<br/>
<table class="table table-sm" style="width: 50%;" name="invoice_snln_table">
<thead>
<tr>
<th><span>Product</span></th>
<th class="text-end"><span>Quantity</span></th>
<th class="text-end"><span>SN/LN</span></th>
</tr>
</thead>
<tbody>
<t t-foreach="lot_values" t-as="snln_line">
<tr>
<td><t t-esc="snln_line['product_name']"/></td>
<td class="text-end">
<t t-esc="snln_line['quantity']"/>
<t t-esc="snln_line['uom_name']" groups="uom.group_uom"/>
</td>
<td class="text-end"><t t-esc="snln_line['lot_name']"/></td>
</tr>
</t>
</tbody>
</table>
</t>
<div t-if="not lot_values" class="oe_structure">&#8203;</div>
<table t-else="" class="table table-sm mt-2" style="width: 50%;" name="invoice_snln_table">
<thead>
<tr>
<th><span>Product</span></th>
<th class="text-end"><span>Quantity</span></th>
<th class="text-end"><span>SN/LN</span></th>
</tr>
</thead>
<tbody>
<tr t-foreach="lot_values" t-as="snln_line">
<td><t t-esc="snln_line['product_name']">Bacon</t></td>
<td class="text-end">
<t t-esc="snln_line['quantity']">6.00</t>
<t t-esc="snln_line['uom_name']" groups="uom.group_uom">units</t>
</td>
<td><t class="text-end" t-esc="snln_line['lot_name']">BC46282798</t></td>
</tr>
</tbody>
</table>
</t>
</xpath>
</template>