Linkage report
This commit is contained in:
@@ -206,8 +206,9 @@ def register():
|
||||
stock.AccountMove,
|
||||
stock.ContainerType,
|
||||
stock.ShipmentContainer,
|
||||
lot.Lot,
|
||||
lot.LotQt,
|
||||
lot.Lot,
|
||||
lot.LotQt,
|
||||
stock.LotQt,
|
||||
lot.LotReport,
|
||||
lot.LotContext,
|
||||
lot.LotShippingStart,
|
||||
@@ -363,5 +364,6 @@ def register():
|
||||
stock.ShipmentCOOReport,
|
||||
stock.ShipmentPackingListReport,
|
||||
stock.ShipmentLinkageReport,
|
||||
stock.LotQtLinkageReport,
|
||||
module='purchase_trade', type_='report')
|
||||
|
||||
|
||||
@@ -1927,6 +1927,13 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
|
||||
@property
|
||||
def report_linkage_trading_unit(self):
|
||||
purchase = self._get_report_linkage_purchase()
|
||||
company = getattr(purchase, 'company', None) if purchase else None
|
||||
if not company:
|
||||
company = getattr(self, 'company', None)
|
||||
party = getattr(company, 'party', None) if company else None
|
||||
if party:
|
||||
return self._report_rec_name(party)
|
||||
sale = self._get_report_linkage_sale()
|
||||
party = getattr(sale, 'party', None) if sale else None
|
||||
return self._report_rec_name(party)
|
||||
@@ -2827,17 +2834,222 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
]
|
||||
)
|
||||
|
||||
class FindVessel(Wizard):
|
||||
__name__ = 'stock.shipment.in.vf'
|
||||
start_state = 'vf'
|
||||
vf = StateAction('purchase_trade.url_vessel_finder')
|
||||
|
||||
def do_vf(self, action):
|
||||
action['url'] = self.record.get_vessel_url()
|
||||
return action, {}
|
||||
|
||||
class ShipmentOut(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out'
|
||||
class FindVessel(Wizard):
|
||||
__name__ = 'stock.shipment.in.vf'
|
||||
start_state = 'vf'
|
||||
vf = StateAction('purchase_trade.url_vessel_finder')
|
||||
|
||||
def do_vf(self, action):
|
||||
action['url'] = self.record.get_vessel_url()
|
||||
return action, {}
|
||||
|
||||
|
||||
class LotQt(metaclass=PoolMeta):
|
||||
__name__ = 'lot.qt'
|
||||
|
||||
@staticmethod
|
||||
def _report_record_key(record):
|
||||
return ShipmentIn._report_record_key(record)
|
||||
|
||||
@classmethod
|
||||
def _report_unique_records(cls, records):
|
||||
return ShipmentIn._report_unique_records(records)
|
||||
|
||||
@staticmethod
|
||||
def _report_linkage_lot_purchase_line(lot):
|
||||
return ShipmentIn._report_linkage_lot_purchase_line(lot)
|
||||
|
||||
@staticmethod
|
||||
def _report_linkage_lot_sale_line(lot):
|
||||
return ShipmentIn._report_linkage_lot_sale_line(lot)
|
||||
|
||||
@staticmethod
|
||||
def _report_rec_name(record):
|
||||
return ShipmentIn._report_rec_name(record)
|
||||
|
||||
@classmethod
|
||||
def _report_number(cls, record):
|
||||
return ShipmentIn._report_number(record)
|
||||
|
||||
@staticmethod
|
||||
def _report_date(value):
|
||||
return ShipmentIn._report_date(value)
|
||||
|
||||
@staticmethod
|
||||
def _report_amount(value):
|
||||
return ShipmentIn._report_amount(value)
|
||||
|
||||
@staticmethod
|
||||
def _report_price(value):
|
||||
return ShipmentIn._report_price(value)
|
||||
|
||||
@classmethod
|
||||
def _report_currency_code(cls, record):
|
||||
return ShipmentIn._report_currency_code(record)
|
||||
|
||||
@classmethod
|
||||
def _report_unit_symbol(cls, line):
|
||||
return ShipmentIn._report_unit_symbol(line)
|
||||
|
||||
@classmethod
|
||||
def _report_line_quantity(cls, line):
|
||||
return ShipmentIn._report_line_quantity(line)
|
||||
|
||||
@classmethod
|
||||
def _report_line_amount(cls, line):
|
||||
return ShipmentIn._report_line_amount(line)
|
||||
|
||||
@classmethod
|
||||
def _report_fee_amount(cls, fee):
|
||||
return ShipmentIn._report_fee_amount(fee)
|
||||
|
||||
@classmethod
|
||||
def _report_column(cls, rows, index, amount=False):
|
||||
return ShipmentIn._report_column(rows, index, amount=amount)
|
||||
|
||||
@classmethod
|
||||
def _report_table_rows(cls, rows, names, amount_names=()):
|
||||
return ShipmentIn._report_table_rows(rows, names, amount_names)
|
||||
|
||||
@staticmethod
|
||||
def _format_report_quantity(value, digits='0.001'):
|
||||
return ShipmentIn._format_report_quantity(value, digits=digits)
|
||||
|
||||
@staticmethod
|
||||
def _get_report_unit_text(unit):
|
||||
return ShipmentIn._get_report_unit_text(unit)
|
||||
|
||||
def _get_report_primary_move(self):
|
||||
return getattr(self, 'lot_move', None)
|
||||
|
||||
def _get_report_linkage_lots(self):
|
||||
return self._report_unique_records((
|
||||
getattr(self, 'lot_p', None),
|
||||
getattr(self, 'lot_s', None),
|
||||
))
|
||||
|
||||
_get_report_linkage_purchase_lines = (
|
||||
ShipmentIn._get_report_linkage_purchase_lines)
|
||||
_get_report_linkage_sale_lines = ShipmentIn._get_report_linkage_sale_lines
|
||||
_get_report_linkage_purchase = ShipmentIn._get_report_linkage_purchase
|
||||
_get_report_linkage_sale = ShipmentIn._get_report_linkage_sale
|
||||
_get_report_linkage_product = ShipmentIn._get_report_linkage_product
|
||||
|
||||
def _get_report_linkage_fees(self):
|
||||
fees = []
|
||||
seen = set()
|
||||
|
||||
def add_fee(fee):
|
||||
if not fee:
|
||||
return
|
||||
key = self._report_record_key(fee)
|
||||
if key in seen:
|
||||
return
|
||||
seen.add(key)
|
||||
fees.append(fee)
|
||||
|
||||
shipment = getattr(self, 'lot_shipment_in', None)
|
||||
for fee in getattr(shipment, 'fees', []) or []:
|
||||
add_fee(fee)
|
||||
for line in (
|
||||
self._get_report_linkage_purchase_lines()
|
||||
+ self._get_report_linkage_sale_lines()):
|
||||
for fee in getattr(line, 'fees', []) or []:
|
||||
add_fee(fee)
|
||||
return fees
|
||||
|
||||
_get_report_linkage_summary_rows = (
|
||||
ShipmentIn._get_report_linkage_summary_rows)
|
||||
_get_report_linkage_movement_rows = (
|
||||
ShipmentIn._get_report_linkage_movement_rows)
|
||||
_get_report_linkage_movement_row = (
|
||||
ShipmentIn._get_report_linkage_movement_row)
|
||||
_get_report_linkage_pricing_rows = (
|
||||
ShipmentIn._get_report_linkage_pricing_rows)
|
||||
_get_report_linkage_detail_rows = (
|
||||
ShipmentIn._get_report_linkage_detail_rows)
|
||||
_get_report_linkage_detail_row = (
|
||||
ShipmentIn._get_report_linkage_detail_row)
|
||||
_get_report_linkage_fee_detail_row = (
|
||||
ShipmentIn._get_report_linkage_fee_detail_row)
|
||||
|
||||
report_linkage_title = ShipmentIn.report_linkage_title
|
||||
report_linkage_desk = ShipmentIn.report_linkage_desk
|
||||
report_linkage_book = ShipmentIn.report_linkage_book
|
||||
report_linkage_strategy = ShipmentIn.report_linkage_strategy
|
||||
|
||||
@property
|
||||
def report_linkage_bl_date(self):
|
||||
shipment = getattr(self, 'lot_shipment_in', None)
|
||||
bl_date = getattr(shipment, 'bl_date', None)
|
||||
if not bl_date:
|
||||
move = getattr(self, 'lot_move', None)
|
||||
bl_date = getattr(move, 'bldate', None)
|
||||
return bl_date and bl_date.strftime('%A, %B %d, %Y') or ''
|
||||
|
||||
report_linkage_trading_unit = ShipmentIn.report_linkage_trading_unit
|
||||
report_linkage_finance_user = ShipmentIn.report_linkage_finance_user
|
||||
report_linkage_summary_groups = ShipmentIn.report_linkage_summary_groups
|
||||
report_linkage_summary_cost_types = (
|
||||
ShipmentIn.report_linkage_summary_cost_types)
|
||||
report_linkage_summary_estimated = (
|
||||
ShipmentIn.report_linkage_summary_estimated)
|
||||
report_linkage_summary_validated = (
|
||||
ShipmentIn.report_linkage_summary_validated)
|
||||
report_linkage_summary_rows = ShipmentIn.report_linkage_summary_rows
|
||||
report_linkage_movement_types = ShipmentIn.report_linkage_movement_types
|
||||
report_linkage_movement_references = (
|
||||
ShipmentIn.report_linkage_movement_references)
|
||||
report_linkage_movement_counterparts = (
|
||||
ShipmentIn.report_linkage_movement_counterparts)
|
||||
report_linkage_movement_commodities = (
|
||||
ShipmentIn.report_linkage_movement_commodities)
|
||||
report_linkage_movement_quantities = (
|
||||
ShipmentIn.report_linkage_movement_quantities)
|
||||
report_linkage_movement_deliveries = (
|
||||
ShipmentIn.report_linkage_movement_deliveries)
|
||||
report_linkage_movement_basis = ShipmentIn.report_linkage_movement_basis
|
||||
report_linkage_movement_periods = (
|
||||
ShipmentIn.report_linkage_movement_periods)
|
||||
report_linkage_movement_from_dates = (
|
||||
ShipmentIn.report_linkage_movement_from_dates)
|
||||
report_linkage_movement_to_dates = (
|
||||
ShipmentIn.report_linkage_movement_to_dates)
|
||||
report_linkage_movement_rows = ShipmentIn.report_linkage_movement_rows
|
||||
report_linkage_bank_deal = ShipmentIn.report_linkage_bank_deal
|
||||
report_linkage_bank_type = ShipmentIn.report_linkage_bank_type
|
||||
report_linkage_bank_name = ShipmentIn.report_linkage_bank_name
|
||||
report_linkage_lc_type = ShipmentIn.report_linkage_lc_type
|
||||
report_linkage_lc_number = ShipmentIn.report_linkage_lc_number
|
||||
report_linkage_lc_amount = ShipmentIn.report_linkage_lc_amount
|
||||
report_linkage_lc_expiry_date = ShipmentIn.report_linkage_lc_expiry_date
|
||||
report_linkage_pricing_deals = ShipmentIn.report_linkage_pricing_deals
|
||||
report_linkage_pricing_sides = ShipmentIn.report_linkage_pricing_sides
|
||||
report_linkage_pricing_types = ShipmentIn.report_linkage_pricing_types
|
||||
report_linkage_pricing_symbols = ShipmentIn.report_linkage_pricing_symbols
|
||||
report_linkage_pricing_periods = ShipmentIn.report_linkage_pricing_periods
|
||||
report_linkage_pricing_input_prices = (
|
||||
ShipmentIn.report_linkage_pricing_input_prices)
|
||||
report_linkage_pricing_rows = ShipmentIn.report_linkage_pricing_rows
|
||||
report_linkage_detail_groups = ShipmentIn.report_linkage_detail_groups
|
||||
report_linkage_detail_cost_types = (
|
||||
ShipmentIn.report_linkage_detail_cost_types)
|
||||
report_linkage_detail_counterparts = (
|
||||
ShipmentIn.report_linkage_detail_counterparts)
|
||||
report_linkage_detail_sources = ShipmentIn.report_linkage_detail_sources
|
||||
report_linkage_detail_unit_prices = (
|
||||
ShipmentIn.report_linkage_detail_unit_prices)
|
||||
report_linkage_detail_quantities = (
|
||||
ShipmentIn.report_linkage_detail_quantities)
|
||||
report_linkage_detail_estimated = (
|
||||
ShipmentIn.report_linkage_detail_estimated)
|
||||
report_linkage_detail_validated = (
|
||||
ShipmentIn.report_linkage_detail_validated)
|
||||
report_linkage_detail_rows = ShipmentIn.report_linkage_detail_rows
|
||||
|
||||
|
||||
class ShipmentOut(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out'
|
||||
|
||||
from_location = fields.Many2One('stock.location', 'From location')
|
||||
to_location = fields.Many2One('stock.location', 'To location')
|
||||
@@ -4249,6 +4461,15 @@ class ShipmentLinkageReport(ShipmentTemplateReportMixin, BaseSupplierShipping):
|
||||
'shipment_linkage_report_template', 'stock')
|
||||
|
||||
|
||||
class LotQtLinkageReport(ShipmentTemplateReportMixin, BaseSupplierShipping):
|
||||
__name__ = 'lot.qt.linkage'
|
||||
|
||||
@classmethod
|
||||
def _resolve_configured_report_path(cls, action):
|
||||
return cls._resolve_template_path(
|
||||
'shipment_linkage_report_template', 'stock')
|
||||
|
||||
|
||||
class ShipmentCOOReport(ShipmentTemplateReportMixin, BaseSupplierShipping):
|
||||
__name__ = 'stock.shipment.in.coo'
|
||||
|
||||
|
||||
@@ -250,13 +250,13 @@ this repository contains the full copyright notices and license terms. -->
|
||||
|
||||
<record model="ir.action.report" id="report_shipment_in_linkage">
|
||||
<field name="name">Linkage</field>
|
||||
<field name="model">stock.shipment.in</field>
|
||||
<field name="report_name">stock.shipment.in.linkage</field>
|
||||
<field name="model">lot.qt</field>
|
||||
<field name="report_name">lot.qt.linkage</field>
|
||||
<field name="report">stock/linkage.fodt</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_shipment_in_linkage_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">stock.shipment.in,-1</field>
|
||||
<field name="model">lot.qt,-1</field>
|
||||
<field name="action" ref="report_shipment_in_linkage"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -6506,6 +6506,84 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertIn('80.000 USD/MT', shipment.report_linkage_pricing_input_prices)
|
||||
self.assertIn('100.000 USD/MT', shipment.report_linkage_pricing_input_prices)
|
||||
|
||||
def test_lotqt_linkage_helpers_use_single_matching(self):
|
||||
'lot.qt linkage helpers expose only the selected matching'
|
||||
LotQt = Pool().get('lot.qt')
|
||||
lotqt = LotQt()
|
||||
|
||||
currency = SimpleNamespace(code='USD')
|
||||
unit = SimpleNamespace(symbol='MT')
|
||||
product = SimpleNamespace(
|
||||
rec_name='Sulphuric Acid',
|
||||
name='Sulphuric Acid',
|
||||
code='H2SO4')
|
||||
purchase = SimpleNamespace(
|
||||
id=1,
|
||||
number='P-10',
|
||||
reference='26.0001',
|
||||
currency=currency,
|
||||
party=SimpleNamespace(rec_name='SUPPLIER SA'),
|
||||
to_location=SimpleNamespace(rec_name='Odda'),
|
||||
incoterm=SimpleNamespace(code='FOB'),
|
||||
operator=SimpleNamespace(rec_name='Operator A'))
|
||||
sale = SimpleNamespace(
|
||||
id=2,
|
||||
number='S-20',
|
||||
reference='26.0001',
|
||||
currency=currency,
|
||||
party=SimpleNamespace(rec_name='CLIENT SA'),
|
||||
to_location=SimpleNamespace(rec_name='Mejillones'),
|
||||
incoterm=SimpleNamespace(code='CFR'))
|
||||
period = SimpleNamespace(rec_name='Chile FY26')
|
||||
purchase_line = SimpleNamespace(
|
||||
id=11,
|
||||
product=product,
|
||||
purchase=purchase,
|
||||
quantity_theorical=Decimal('100'),
|
||||
quantity=Decimal('100'),
|
||||
unit_price=Decimal('80'),
|
||||
unit=unit,
|
||||
price_type='fixed',
|
||||
del_period=period,
|
||||
period_at='laycan',
|
||||
from_del=datetime.date(2026, 4, 30),
|
||||
to_del=datetime.date(2026, 5, 8),
|
||||
fees=[],
|
||||
mtm=[])
|
||||
sale_line = SimpleNamespace(
|
||||
id=12,
|
||||
product=product,
|
||||
sale=sale,
|
||||
quantity_theorical=Decimal('100'),
|
||||
quantity=Decimal('100'),
|
||||
unit_price=Decimal('100'),
|
||||
unit=unit,
|
||||
price_type='fixed',
|
||||
del_period=period,
|
||||
period_at='laycan',
|
||||
from_del=datetime.date(2026, 4, 30),
|
||||
to_del=datetime.date(2026, 4, 30),
|
||||
fees=[],
|
||||
mtm=[])
|
||||
lotqt.lot_p = SimpleNamespace(id=101, line=purchase_line)
|
||||
lotqt.lot_s = SimpleNamespace(id=102, sale_line=sale_line)
|
||||
lotqt.lot_shipment_in = SimpleNamespace(
|
||||
bl_date=datetime.date(2026, 4, 30),
|
||||
fees=[])
|
||||
|
||||
self.assertEqual(
|
||||
lotqt.report_linkage_title, 'Linkage P-10/S-20 26.0001')
|
||||
self.assertEqual(lotqt.report_linkage_desk, 'Sulphuric Acid')
|
||||
self.assertEqual(lotqt.report_linkage_book, 'H2SO4 Chile FY26')
|
||||
self.assertEqual(
|
||||
lotqt.report_linkage_bl_date, 'Thursday, April 30, 2026')
|
||||
self.assertIn('Purchases', lotqt.report_linkage_summary_groups)
|
||||
self.assertIn('Sales', lotqt.report_linkage_summary_groups)
|
||||
self.assertIn('P-10', lotqt.report_linkage_movement_references)
|
||||
self.assertIn('S-20', lotqt.report_linkage_movement_references)
|
||||
self.assertIn('80.000 USD/MT', lotqt.report_linkage_pricing_input_prices)
|
||||
self.assertIn('100.000 USD/MT', lotqt.report_linkage_pricing_input_prices)
|
||||
|
||||
def test_shipment_insurance_contact_surveyor_prefers_shipment_surveyor(self):
|
||||
'insurance contact surveyor property uses shipment surveyor first'
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
|
||||
Reference in New Issue
Block a user