This commit is contained in:
2026-07-21 21:40:35 +02:00
parent 2fe13c1e76
commit ab80f8d909
6 changed files with 671 additions and 582 deletions

View File

@@ -775,10 +775,16 @@ class ShipmentIn(metaclass=PoolMeta):
fields.One2Many('valuation.valuation.line', '', "Pnl"),
'get_pnl_lines')
lotqt = fields.One2Many('lot.qt','lot_shipment_in',"Lots")
quantity = fields.Function(fields.Numeric("Quantity"),'get_quantity')
unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit')
bl_date = fields.Date("BL date")
bl_number = fields.Char("BL number")
quantity = fields.Function(fields.Numeric("Quantity"),'get_quantity')
unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit')
list_from_locations = fields.Function(
fields.Char("From"), 'get_list_from_locations')
list_to_locations = fields.Function(
fields.Char("To"), 'get_list_to_locations')
list_purchase_suppliers = fields.Function(
fields.Char("Supplier"), 'get_list_purchase_suppliers')
bl_date = fields.Date("BL date")
bl_number = fields.Char("BL number")
etl = fields.Date("Est. Loading")
eta = fields.Date("🏗ETA POL")
etad = fields.Date("⚓ETA")
@@ -2058,7 +2064,9 @@ class ShipmentIn(metaclass=PoolMeta):
self._report_number(purchase),
self._report_number(sale),
]))
return ' '.join(part for part in ['Linkage', numbers, reference] if part)
vessel = self._report_rec_name(getattr(self, 'vessel', None))
return ' '.join(part for part in [
'Linkage', numbers, reference, vessel] if part)
@property
def report_linkage_desk(self):
@@ -3064,6 +3072,54 @@ class ShipmentIn(metaclass=PoolMeta):
lines.append(line)
return lines
@staticmethod
def _list_rec_names(records):
names = []
seen = set()
for record in records:
name = (
getattr(record, 'rec_name', None)
or getattr(record, 'name', None)
or getattr(record, 'vessel_name', None))
if not name or name in seen:
continue
seen.add(name)
names.append(name)
return ', '.join(names)
def get_list_from_locations(self, name=None):
locations = []
for lot_qt in getattr(self, 'lotqt', []) or []:
location = (
getattr(lot_qt, 'planned_from_location', None)
or getattr(self, 'from_location', None))
if location:
locations.append(location)
if not locations and getattr(self, 'from_location', None):
locations.append(self.from_location)
return self._list_rec_names(locations)
def get_list_to_locations(self, name=None):
locations = []
for lot_qt in getattr(self, 'lotqt', []) or []:
location = (
getattr(lot_qt, 'planned_to_location', None)
or getattr(self, 'to_location', None))
if location:
locations.append(location)
if not locations and getattr(self, 'to_location', None):
locations.append(self.to_location)
return self._list_rec_names(locations)
def get_list_purchase_suppliers(self, name=None):
suppliers = []
for line in self._get_purchase_lines_from_lots():
purchase = getattr(line, 'purchase', None)
supplier = getattr(purchase, 'party', None)
if supplier:
suppliers.append(supplier)
return self._list_rec_names(suppliers)
def _get_sale_lines_from_lots(self):
lines = []
seen = set()
@@ -4917,6 +4973,11 @@ class LotReportLinkageRecord:
def _get_report_linkage_lots(self):
return [self.lot]
@property
def vessel(self):
shipment = getattr(self.lot, 'lot_shipment_in', None)
return getattr(shipment, 'vessel', None)
_get_report_linkage_purchase_lines = (
ShipmentIn._get_report_linkage_purchase_lines)
_get_report_linkage_sale_lines = ShipmentIn._get_report_linkage_sale_lines

View File

@@ -13,6 +13,22 @@ this repository contains the full copyright notices and license terms. -->
<field name="inherit" ref="stock.shipment_in_view_tree"/>
<field name="name">shipment_in_tree</field>
</record>
<record model="ir.action.act_window.domain" id="stock.act_shipment_in_form_domain_all">
<field name="sequence" eval="1"/>
</record>
<record model="ir.action.act_window.domain" id="stock.act_shipment_in_form_domain_draft">
<field name="sequence" eval="2"/>
</record>
<record model="ir.action.act_window.domain" id="act_shipment_in_form_domain_started">
<field name="name">Started</field>
<field name="sequence" eval="3"/>
<field name="domain" eval="[('state', '=', 'started')]" pyson="1"/>
<field name="count" eval="True"/>
<field name="act_window" ref="stock.act_shipment_in_form"/>
</record>
<record model="ir.action.act_window.domain" id="stock.act_shipment_in_form_domain_received">
<field name="sequence" eval="4"/>
</record>
<record model="ir.ui.view" id="lot_qt_planned_view_tree">
<field name="model">lot.qt</field>
<field name="type">tree</field>

View File

@@ -7497,9 +7497,11 @@ description</t></is></c>
line=purchase_line,
sale_line=sale_line)
shipment.incoming_moves = [SimpleNamespace(lot=lot)]
shipment.vessel = SimpleNamespace(vessel_name='MV ATLANTIC')
self.assertEqual(
shipment.report_linkage_title, 'Linkage P-10/S-20 26.0001')
shipment.report_linkage_title,
'Linkage P-10/S-20 26.0001 MV ATLANTIC')
self.assertEqual(shipment.report_linkage_desk, 'Sulfuric Acid')
self.assertEqual(shipment.report_linkage_book, 'H2SO4 Chile FY26')
self.assertEqual(
@@ -7702,6 +7704,7 @@ description</t></is></c>
sale_line=sale_line,
lot_shipment_in=SimpleNamespace(
bl_date=datetime.date(2026, 4, 30),
vessel=SimpleNamespace(vessel_name='MV ATLANTIC'),
fees=[]),
move=None)
lot_report = SimpleNamespace(
@@ -7715,12 +7718,12 @@ description</t></is></c>
self.assertEqual(
record.report_linkage_title,
'Linkage P-10/S-20 26.0001 - PROVISIONAL')
'Linkage P-10/S-20 26.0001 MV ATLANTIC - PROVISIONAL')
final_record = stock_module.LotReportLinkageRecord(
physical_lot, lot_report, linkage_status='FINAL')
self.assertEqual(
final_record.report_linkage_title,
'Linkage P-10/S-20 26.0001 - FINAL')
'Linkage P-10/S-20 26.0001 MV ATLANTIC - FINAL')
self.assertEqual(record.report_linkage_bl_date, 'Thursday, April 30, 2026')
self.assertIn('Purchases', record.report_linkage_summary_groups)
self.assertIn('Sales', record.report_linkage_summary_groups)
@@ -10530,6 +10533,44 @@ description</t></is></c>
)),
[])
def test_shipment_in_list_columns_use_planned_lots_and_purchase_supplier(self):
'shipment list helper columns aggregate lot plans and purchase supplier'
ShipmentIn = Pool().get('stock.shipment.in')
supplier = Mock(rec_name='SUMITOMO')
purchase = Mock(party=supplier)
purchase_line = Mock(id=10, purchase=purchase)
lot_p = Mock(line=purchase_line)
shipment = ShipmentIn()
shipment.from_location = Mock(rec_name='Fallback From')
shipment.to_location = Mock(rec_name='Fallback To')
shipment.lotqt = [
Mock(
lot_p=lot_p,
planned_from_location=Mock(rec_name='Aomori'),
planned_to_location=Mock(rec_name='Stockton')),
Mock(
lot_p=lot_p,
planned_from_location=Mock(rec_name='Sagano'),
planned_to_location=Mock(rec_name='Stockton')),
]
self.assertEqual(shipment.get_list_from_locations(), 'Aomori, Sagano')
self.assertEqual(shipment.get_list_to_locations(), 'Stockton')
self.assertEqual(shipment.get_list_purchase_suppliers(), 'SUMITOMO')
def test_shipment_in_list_locations_fallback_to_shipment_locations(self):
'shipment list location helpers fallback when no planned lots exist'
ShipmentIn = Pool().get('stock.shipment.in')
shipment = ShipmentIn()
shipment.from_location = Mock(rec_name='Kobe')
shipment.to_location = Mock(rec_name='Rotterdam')
shipment.lotqt = []
self.assertEqual(shipment.get_list_from_locations(), 'Kobe')
self.assertEqual(shipment.get_list_to_locations(), 'Rotterdam')
def test_invoice_report_transportation_uses_truck_label(self):
'invoice_melya Transportation displays By Truck for truck shipments'
Invoice = Pool().get('account.invoice')

View File

@@ -33,7 +33,7 @@
<field name="r_operator" width="130" optional="1"/>
<field name="r_bl_date" width="90" optional="1"/>
<field name="r_bl_number" width="110" optional="1"/>
<field name="r_shipping_status" widget="badge" string="Shipping status" badge_colors="shipped:#16a34a,unshipped:#ef4444,planned:#06b6d4,scheduled:#f59e0b" width="120"/>
<field name="r_shipping_status" widget="badge" string="Shipping status" badge_colors="shipped:#16a34a,unshipped:#ef4444,planned:#06b6d4,scheduled:#f59e0b,received:#cbd5d5" width="120"/>
<field name="r_lot_status"/>
<field name="r_lot_av" width="80" optional="1" tree_invisible="1"/>
<field name="r_purchase_currency_symbol" tree_invisible="1"/>

View File

@@ -4,5 +4,16 @@ this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='number']" position="after">
<field name="bl_number"/>
<field name="bl_date" optional="1"/>
</xpath>
<xpath expr="/tree/field[@name='reference']" position="after">
<field name="vessel" string="Vessel name" optional="1"/>
<field name="list_from_locations" optional="1"/>
<field name="list_to_locations" optional="1"/>
</xpath>
<xpath expr="/tree/field[@name='supplier']" position="replace">
<field name="carrier_" string="SSCO/Owner" expand="2" optional="0"/>
<field name="supplier" string="Broker" optional="1"/>
<field name="list_purchase_suppliers" optional="1"/>
</xpath>
</data>

File diff suppressed because it is too large Load Diff