Linkage
This commit is contained in:
@@ -1489,6 +1489,559 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
if not today:
|
||||
return ''
|
||||
return today.strftime('%d-%m-%Y')
|
||||
|
||||
@staticmethod
|
||||
def _report_record_key(record):
|
||||
record_id = getattr(record, 'id', None)
|
||||
return record_id if record_id is not None else id(record)
|
||||
|
||||
@classmethod
|
||||
def _report_unique_records(cls, records):
|
||||
unique = []
|
||||
seen = set()
|
||||
for record in records:
|
||||
if not record:
|
||||
continue
|
||||
key = cls._report_record_key(record)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
unique.append(record)
|
||||
return unique
|
||||
|
||||
def _get_report_linkage_lots(self):
|
||||
lots = self._get_report_physical_lots()
|
||||
if lots:
|
||||
return lots
|
||||
return self._report_unique_records(
|
||||
getattr(move, 'lot', None)
|
||||
for move in (self.incoming_moves or self.moves or []))
|
||||
|
||||
def _get_report_linkage_purchase_lines(self):
|
||||
return self._report_unique_records(
|
||||
getattr(lot, 'line', None)
|
||||
for lot in self._get_report_linkage_lots())
|
||||
|
||||
def _get_report_linkage_sale_lines(self):
|
||||
return self._report_unique_records(
|
||||
getattr(lot, 'sale_line', None)
|
||||
for lot in self._get_report_linkage_lots())
|
||||
|
||||
def _get_report_linkage_purchase(self):
|
||||
purchases = self._report_unique_records(
|
||||
getattr(line, 'purchase', None)
|
||||
for line in self._get_report_linkage_purchase_lines())
|
||||
return purchases[0] if purchases else None
|
||||
|
||||
def _get_report_linkage_sale(self):
|
||||
sales = self._report_unique_records(
|
||||
getattr(line, 'sale', None)
|
||||
for line in self._get_report_linkage_sale_lines())
|
||||
return sales[0] if sales else None
|
||||
|
||||
@staticmethod
|
||||
def _report_rec_name(record):
|
||||
return (
|
||||
getattr(record, 'rec_name', None)
|
||||
or getattr(record, 'name', None)
|
||||
or getattr(record, 'number', None)
|
||||
or '')
|
||||
|
||||
@classmethod
|
||||
def _report_number(cls, record):
|
||||
return (
|
||||
getattr(record, 'number', None)
|
||||
or getattr(record, 'full_number', None)
|
||||
or cls._report_rec_name(record)
|
||||
or '')
|
||||
|
||||
@staticmethod
|
||||
def _report_date(value):
|
||||
if not value:
|
||||
return ''
|
||||
if hasattr(value, 'strftime'):
|
||||
return value.strftime('%d %b %y')
|
||||
return str(value)
|
||||
|
||||
@staticmethod
|
||||
def _report_amount(value):
|
||||
if value in (None, ''):
|
||||
return ''
|
||||
return format(Decimal(str(value or 0)).quantize(Decimal('0.001')), ',f')
|
||||
|
||||
@staticmethod
|
||||
def _report_price(value):
|
||||
if value in (None, ''):
|
||||
return ''
|
||||
return format(Decimal(str(value or 0)).quantize(Decimal('0.001')), 'f')
|
||||
|
||||
@classmethod
|
||||
def _report_currency_code(cls, record):
|
||||
currency = getattr(record, 'currency', None)
|
||||
return (
|
||||
getattr(currency, 'code', None)
|
||||
or getattr(currency, 'name', None)
|
||||
or getattr(currency, 'symbol', None)
|
||||
or '')
|
||||
|
||||
@classmethod
|
||||
def _report_unit_symbol(cls, line):
|
||||
unit = getattr(line, 'unit', None)
|
||||
return (
|
||||
getattr(unit, 'symbol', None)
|
||||
or getattr(unit, 'rec_name', None)
|
||||
or getattr(unit, 'name', None)
|
||||
or '')
|
||||
|
||||
@classmethod
|
||||
def _report_line_quantity(cls, line):
|
||||
return Decimal(str(
|
||||
getattr(line, 'quantity_theorical', None)
|
||||
or getattr(line, 'quantity', 0)
|
||||
or 0))
|
||||
|
||||
@classmethod
|
||||
def _report_line_amount(cls, line):
|
||||
quantity = cls._report_line_quantity(line)
|
||||
price = Decimal(str(getattr(line, 'unit_price', 0) or 0))
|
||||
return quantity * price
|
||||
|
||||
def _get_report_linkage_product(self):
|
||||
lines = (
|
||||
self._get_report_linkage_purchase_lines()
|
||||
or self._get_report_linkage_sale_lines())
|
||||
product = getattr(lines[0], 'product', None) if lines else None
|
||||
if product:
|
||||
return product
|
||||
move = self._get_report_primary_move()
|
||||
return getattr(move, 'product', None) if move else None
|
||||
|
||||
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)
|
||||
|
||||
for fee in getattr(self, '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
|
||||
|
||||
@classmethod
|
||||
def _report_fee_amount(cls, fee):
|
||||
get_amount = getattr(fee, 'get_amount', None)
|
||||
if callable(get_amount):
|
||||
amount = get_amount()
|
||||
else:
|
||||
amount = None
|
||||
if amount in (None, ''):
|
||||
price = Decimal(str(getattr(fee, 'price', 0) or 0))
|
||||
quantity = Decimal(str(getattr(fee, 'quantity', 0) or 0))
|
||||
mode = getattr(fee, 'mode', None)
|
||||
amount = price if mode == 'lumpsum' else price * quantity
|
||||
amount = Decimal(str(amount or 0))
|
||||
return -amount if getattr(fee, 'p_r', None) == 'pay' else amount
|
||||
|
||||
def _get_report_linkage_summary_rows(self):
|
||||
purchase_lines = self._get_report_linkage_purchase_lines()
|
||||
sale_lines = self._get_report_linkage_sale_lines()
|
||||
purchase_total = -sum(
|
||||
self._report_line_amount(line) for line in purchase_lines)
|
||||
sale_total = sum(self._report_line_amount(line) for line in sale_lines)
|
||||
|
||||
rows = []
|
||||
if purchase_lines:
|
||||
rows.append(('Purchases', '', purchase_total, purchase_total))
|
||||
if sale_lines:
|
||||
rows.append(('Sales', '', sale_total, sale_total))
|
||||
|
||||
fee_total = Decimal('0')
|
||||
for fee in self._get_report_linkage_fees():
|
||||
product = getattr(fee, 'product', None)
|
||||
label = self._report_rec_name(product) or 'Fee'
|
||||
amount = self._report_fee_amount(fee)
|
||||
fee_total += amount
|
||||
rows.append(('Costs', label, amount, amount))
|
||||
|
||||
if rows:
|
||||
rows.append(('Costs', 'Total', fee_total, fee_total))
|
||||
pnl = purchase_total + sale_total + fee_total
|
||||
rows.append(('P&L', '', pnl, pnl))
|
||||
return rows
|
||||
|
||||
def _get_report_linkage_movement_rows(self):
|
||||
rows = []
|
||||
purchase_lines = self._get_report_linkage_purchase_lines()
|
||||
sale_lines = self._get_report_linkage_sale_lines()
|
||||
if purchase_lines:
|
||||
rows.append(('Purchase', '', '', '', '', '', '', '', '', ''))
|
||||
for line in purchase_lines:
|
||||
purchase = getattr(line, 'purchase', None)
|
||||
rows.append(self._get_report_linkage_movement_row(
|
||||
'Deal', purchase, line))
|
||||
if sale_lines:
|
||||
rows.append(('Sale', '', '', '', '', '', '', '', '', ''))
|
||||
for line in sale_lines:
|
||||
sale = getattr(line, 'sale', None)
|
||||
rows.append(self._get_report_linkage_movement_row(
|
||||
'Deal', sale, line))
|
||||
if purchase_lines or sale_lines:
|
||||
balance = (
|
||||
sum(self._report_line_quantity(line) for line in purchase_lines)
|
||||
- sum(self._report_line_quantity(line) for line in sale_lines))
|
||||
unit = self._report_unit_symbol(
|
||||
purchase_lines[0] if purchase_lines else sale_lines[0])
|
||||
rows.append((
|
||||
'Balance (Gain/Loss)', '', '', '',
|
||||
' '.join(part for part in [
|
||||
self._format_report_quantity(balance),
|
||||
unit.upper() if unit else ''] if part),
|
||||
'', '', '', '', ''))
|
||||
return rows
|
||||
|
||||
def _get_report_linkage_movement_row(self, kind, trade, line):
|
||||
product = getattr(line, 'product', None)
|
||||
quantity = ' '.join(part for part in [
|
||||
self._format_report_quantity(self._report_line_quantity(line)),
|
||||
(self._report_unit_symbol(line) or '').upper()] if part)
|
||||
incoterm = getattr(trade, 'incoterm', None)
|
||||
period = getattr(line, 'period_at', None) or ''
|
||||
if period == 'laycan':
|
||||
period = 'Laycan'
|
||||
return (
|
||||
kind,
|
||||
self._report_number(trade),
|
||||
self._report_rec_name(getattr(trade, 'party', None)),
|
||||
self._report_rec_name(product),
|
||||
quantity,
|
||||
getattr(incoterm, 'code', None) or '',
|
||||
self._report_rec_name(getattr(trade, 'to_location', None)),
|
||||
period,
|
||||
self._report_date(getattr(line, 'from_del', None)),
|
||||
self._report_date(getattr(line, 'to_del', None)),
|
||||
)
|
||||
|
||||
def _get_report_linkage_pricing_rows(self):
|
||||
rows = []
|
||||
for label, lines in (
|
||||
('Buy', self._get_report_linkage_purchase_lines()),
|
||||
('Sell', self._get_report_linkage_sale_lines())):
|
||||
for line in lines:
|
||||
trade = (
|
||||
getattr(line, 'purchase', None)
|
||||
or getattr(line, 'sale', None))
|
||||
currency = self._report_currency_code(trade)
|
||||
unit = self._report_unit_symbol(line).upper()
|
||||
price = self._report_price(getattr(line, 'unit_price', 0))
|
||||
rows.append((
|
||||
self._report_number(trade),
|
||||
label,
|
||||
(getattr(line, 'price_type', None) or 'Fixed').title(),
|
||||
'',
|
||||
self._report_rec_name(getattr(line, 'del_period', None)),
|
||||
' '.join(part for part in [
|
||||
price,
|
||||
'/'.join(part for part in [currency, unit] if part)]
|
||||
if part),
|
||||
))
|
||||
return rows
|
||||
|
||||
def _get_report_linkage_detail_rows(self):
|
||||
rows = []
|
||||
for line in self._get_report_linkage_sale_lines():
|
||||
sale = getattr(line, 'sale', None)
|
||||
rows.append(self._get_report_linkage_detail_row(
|
||||
'A', 'Deal revenue', sale, line, self._report_line_amount(line)))
|
||||
for line in self._get_report_linkage_purchase_lines():
|
||||
purchase = getattr(line, 'purchase', None)
|
||||
rows.append(self._get_report_linkage_detail_row(
|
||||
'A', 'Deal expense', purchase, line,
|
||||
-self._report_line_amount(line)))
|
||||
for fee in self._get_report_linkage_fees():
|
||||
rows.append(self._get_report_linkage_fee_detail_row(fee))
|
||||
total = sum((row[-1] for row in rows), Decimal('0'))
|
||||
if rows:
|
||||
rows.append(('A', 'Total', '', '', '', '', total, total))
|
||||
rows.append(('P&L', '', '', '', '', '', total, total))
|
||||
return rows
|
||||
|
||||
def _get_report_linkage_detail_row(self, group, label, trade, line, amount):
|
||||
currency = self._report_currency_code(trade)
|
||||
unit = self._report_unit_symbol(line).upper()
|
||||
unit_price = self._report_price(getattr(line, 'unit_price', 0))
|
||||
quantity = self._format_report_quantity(self._report_line_quantity(line))
|
||||
return (
|
||||
group,
|
||||
label,
|
||||
self._report_rec_name(getattr(trade, 'party', None)),
|
||||
self._report_number(trade),
|
||||
' '.join(part for part in [
|
||||
unit_price,
|
||||
'/'.join(part for part in [currency, unit] if part)]
|
||||
if part),
|
||||
quantity,
|
||||
amount,
|
||||
amount,
|
||||
)
|
||||
|
||||
def _get_report_linkage_fee_detail_row(self, fee):
|
||||
product = getattr(fee, 'product', None)
|
||||
trade_line = (
|
||||
getattr(fee, 'line', None)
|
||||
or getattr(fee, 'sale_line', None)
|
||||
or (self._get_report_linkage_purchase_lines()
|
||||
or self._get_report_linkage_sale_lines()
|
||||
or [None])[0])
|
||||
trade = (
|
||||
getattr(trade_line, 'purchase', None)
|
||||
or getattr(trade_line, 'sale', None))
|
||||
currency = self._report_currency_code(fee) or self._report_currency_code(trade)
|
||||
unit = self._get_report_unit_text(getattr(fee, 'unit', None))
|
||||
amount = self._report_fee_amount(fee)
|
||||
quantity = getattr(fee, 'quantity', None)
|
||||
if quantity in (None, '') and trade_line:
|
||||
quantity = self._report_line_quantity(trade_line)
|
||||
return (
|
||||
'A',
|
||||
self._report_rec_name(product) or 'Fee',
|
||||
self._report_rec_name(getattr(fee, 'supplier', None)),
|
||||
self._report_number(trade),
|
||||
' '.join(part for part in [
|
||||
self._report_price(getattr(fee, 'price', 0)),
|
||||
'/'.join(part for part in [currency, unit.upper() if unit else ''] if part)]
|
||||
if part),
|
||||
self._format_report_quantity(quantity or 0),
|
||||
amount,
|
||||
amount,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _report_column(cls, rows, index, amount=False):
|
||||
values = []
|
||||
for row in rows:
|
||||
value = row[index]
|
||||
if amount:
|
||||
value = cls._report_amount(value)
|
||||
values.append(str(value or ''))
|
||||
return '\n'.join(values)
|
||||
|
||||
@property
|
||||
def report_linkage_title(self):
|
||||
purchase = self._get_report_linkage_purchase()
|
||||
sale = self._get_report_linkage_sale()
|
||||
reference = (
|
||||
getattr(purchase, 'reference', None)
|
||||
or getattr(sale, 'reference', None)
|
||||
or getattr(self, 'reference', None)
|
||||
or '')
|
||||
numbers = '/'.join(filter(None, [
|
||||
self._report_number(purchase),
|
||||
self._report_number(sale),
|
||||
]))
|
||||
return ' '.join(part for part in ['Linkage', numbers, reference] if part)
|
||||
|
||||
@property
|
||||
def report_linkage_desk(self):
|
||||
product = self._get_report_linkage_product()
|
||||
return self._report_rec_name(product)
|
||||
|
||||
@property
|
||||
def report_linkage_book(self):
|
||||
product = self._get_report_linkage_product()
|
||||
code = getattr(product, 'code', None) if product else ''
|
||||
period = self.report_linkage_strategy
|
||||
return ' '.join(part for part in [code, period] if part)
|
||||
|
||||
@property
|
||||
def report_linkage_strategy(self):
|
||||
for line in (
|
||||
self._get_report_linkage_purchase_lines()
|
||||
+ self._get_report_linkage_sale_lines()):
|
||||
strategies = list(getattr(line, 'mtm', []) or [])
|
||||
if strategies:
|
||||
strategy = getattr(strategies[0], 'strategy', None) or strategies[0]
|
||||
return self._report_rec_name(strategy)
|
||||
period = getattr(line, 'del_period', None)
|
||||
if period:
|
||||
return self._report_rec_name(period)
|
||||
return ''
|
||||
|
||||
@property
|
||||
def report_linkage_bl_date(self):
|
||||
return self.bl_date and self.bl_date.strftime('%A, %B %d, %Y') or ''
|
||||
|
||||
@property
|
||||
def report_linkage_trading_unit(self):
|
||||
sale = self._get_report_linkage_sale()
|
||||
party = getattr(sale, 'party', None) if sale else None
|
||||
return self._report_rec_name(party)
|
||||
|
||||
@property
|
||||
def report_linkage_finance_user(self):
|
||||
trade = self._get_report_linkage_purchase() or self._get_report_linkage_sale()
|
||||
operator = getattr(trade, 'operator', None) if trade else None
|
||||
return self._report_rec_name(operator)
|
||||
|
||||
@property
|
||||
def report_linkage_summary_groups(self):
|
||||
return self._report_column(self._get_report_linkage_summary_rows(), 0)
|
||||
|
||||
@property
|
||||
def report_linkage_summary_cost_types(self):
|
||||
return self._report_column(self._get_report_linkage_summary_rows(), 1)
|
||||
|
||||
@property
|
||||
def report_linkage_summary_estimated(self):
|
||||
return self._report_column(
|
||||
self._get_report_linkage_summary_rows(), 2, amount=True)
|
||||
|
||||
@property
|
||||
def report_linkage_summary_validated(self):
|
||||
return self._report_column(
|
||||
self._get_report_linkage_summary_rows(), 3, amount=True)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_types(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 0)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_references(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 1)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_counterparts(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 2)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_commodities(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 3)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_quantities(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 4)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_deliveries(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 5)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_basis(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 6)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_periods(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 7)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_from_dates(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 8)
|
||||
|
||||
@property
|
||||
def report_linkage_movement_to_dates(self):
|
||||
return self._report_column(self._get_report_linkage_movement_rows(), 9)
|
||||
|
||||
@property
|
||||
def report_linkage_bank_deal(self):
|
||||
purchase = self._get_report_linkage_purchase()
|
||||
return self._report_number(purchase)
|
||||
|
||||
@property
|
||||
def report_linkage_bank_type(self):
|
||||
return 'Buy' if self._get_report_linkage_purchase() else ''
|
||||
|
||||
@property
|
||||
def report_linkage_bank_name(self):
|
||||
sale = self._get_report_linkage_sale()
|
||||
bank = getattr(sale, 'our_bank_account', None) if sale else None
|
||||
bank_party = getattr(bank, 'bank', None) if bank else None
|
||||
return self._report_rec_name(bank_party)
|
||||
|
||||
@property
|
||||
def report_linkage_lc_type(self):
|
||||
return 'N/A'
|
||||
|
||||
@property
|
||||
def report_linkage_lc_number(self):
|
||||
sale = self._get_report_linkage_sale()
|
||||
return getattr(sale, 'lc_number', '') or ''
|
||||
|
||||
@property
|
||||
def report_linkage_lc_amount(self):
|
||||
return ''
|
||||
|
||||
@property
|
||||
def report_linkage_lc_expiry_date(self):
|
||||
sale = self._get_report_linkage_sale()
|
||||
return self._report_date(getattr(sale, 'lc_date', None))
|
||||
|
||||
@property
|
||||
def report_linkage_pricing_deals(self):
|
||||
return self._report_column(self._get_report_linkage_pricing_rows(), 0)
|
||||
|
||||
@property
|
||||
def report_linkage_pricing_sides(self):
|
||||
return self._report_column(self._get_report_linkage_pricing_rows(), 1)
|
||||
|
||||
@property
|
||||
def report_linkage_pricing_types(self):
|
||||
return self._report_column(self._get_report_linkage_pricing_rows(), 2)
|
||||
|
||||
@property
|
||||
def report_linkage_pricing_symbols(self):
|
||||
return self._report_column(self._get_report_linkage_pricing_rows(), 3)
|
||||
|
||||
@property
|
||||
def report_linkage_pricing_periods(self):
|
||||
return self._report_column(self._get_report_linkage_pricing_rows(), 4)
|
||||
|
||||
@property
|
||||
def report_linkage_pricing_input_prices(self):
|
||||
return self._report_column(self._get_report_linkage_pricing_rows(), 5)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_groups(self):
|
||||
return self._report_column(self._get_report_linkage_detail_rows(), 0)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_cost_types(self):
|
||||
return self._report_column(self._get_report_linkage_detail_rows(), 1)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_counterparts(self):
|
||||
return self._report_column(self._get_report_linkage_detail_rows(), 2)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_sources(self):
|
||||
return self._report_column(self._get_report_linkage_detail_rows(), 3)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_unit_prices(self):
|
||||
return self._report_column(self._get_report_linkage_detail_rows(), 4)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_quantities(self):
|
||||
return self._report_column(self._get_report_linkage_detail_rows(), 5)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_estimated(self):
|
||||
return self._report_column(
|
||||
self._get_report_linkage_detail_rows(), 6, amount=True)
|
||||
|
||||
@property
|
||||
def report_linkage_detail_validated(self):
|
||||
return self._report_column(
|
||||
self._get_report_linkage_detail_rows(), 7, amount=True)
|
||||
|
||||
def get_rec_name(self, name=None):
|
||||
if self.number:
|
||||
|
||||
@@ -6407,6 +6407,92 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(
|
||||
shipment.report_insurance_amount, 'USD 550.00')
|
||||
|
||||
def test_shipment_linkage_helpers_use_matched_purchase_sale_lot(self):
|
||||
'linkage helpers expose matched purchase and sale values'
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
shipment = ShipmentIn()
|
||||
shipment.bl_date = datetime.date(2026, 4, 30)
|
||||
shipment.fees = []
|
||||
shipment.moves = []
|
||||
shipment.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=[])
|
||||
lot = SimpleNamespace(
|
||||
id=101,
|
||||
lot_type='physic',
|
||||
line=purchase_line,
|
||||
sale_line=sale_line)
|
||||
shipment.incoming_moves = [SimpleNamespace(lot=lot)]
|
||||
|
||||
self.assertEqual(
|
||||
shipment.report_linkage_title, 'Linkage P-10/S-20 26.0001')
|
||||
self.assertEqual(shipment.report_linkage_desk, 'Sulphuric Acid')
|
||||
self.assertEqual(shipment.report_linkage_book, 'H2SO4 Chile FY26')
|
||||
self.assertEqual(
|
||||
shipment.report_linkage_bl_date, 'Thursday, April 30, 2026')
|
||||
self.assertIn('Purchases', shipment.report_linkage_summary_groups)
|
||||
self.assertIn('Sales', shipment.report_linkage_summary_groups)
|
||||
self.assertIn('-8,000.000', shipment.report_linkage_summary_estimated)
|
||||
self.assertIn('10,000.000', shipment.report_linkage_summary_estimated)
|
||||
self.assertIn('P-10', shipment.report_linkage_movement_references)
|
||||
self.assertIn('S-20', shipment.report_linkage_movement_references)
|
||||
self.assertIn('100 MT', shipment.report_linkage_movement_quantities)
|
||||
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_shipment_insurance_contact_surveyor_prefers_shipment_surveyor(self):
|
||||
'insurance contact surveyor property uses shipment surveyor first'
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
|
||||
1553
modules/stock/linkage.fodt
Normal file
1553
modules/stock/linkage.fodt
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user