ICT template

This commit is contained in:
2026-06-16 20:38:57 +02:00
parent 0833baa2ab
commit d0dd8965cd
4 changed files with 374 additions and 30 deletions

View File

@@ -319,7 +319,71 @@ class Invoice(metaclass=PoolMeta):
]
return lines or list(self.lines or [])
@staticmethod
def _report_record_name(record):
if not record:
return ''
return (
getattr(record, 'rec_name', None)
or getattr(record, 'name', None)
or getattr(record, 'code', None)
or '')
def _get_report_commission_fee(self):
for line in self._get_report_invoice_lines():
fee = getattr(line, 'fee', None)
if fee:
return fee
origin = getattr(line, 'origin', None)
fee = getattr(origin, 'fee_', None)
if fee:
return fee
def _get_report_commission_lots(self):
lots = []
seen = set()
def add_lot(lot):
if not lot:
return
lot_id = getattr(lot, 'id', None)
key = ('id', lot_id) if lot_id is not None else ('obj', id(lot))
if key in seen:
return
seen.add(key)
lots.append(lot)
for line in self._get_report_invoice_lines():
add_lot(getattr(line, 'lot', None))
fee = self._get_report_commission_fee()
if fee and hasattr(fee, '_get_effective_fee_lots'):
for lot in fee._get_effective_fee_lots():
add_lot(lot)
for lot in getattr(fee, 'lots', []) or []:
add_lot(lot)
return lots
def _get_report_commission_trade_for(self, kind):
fee = self._get_report_commission_fee()
if fee:
if kind == 'sale' and getattr(fee, 'sale_line', None):
return fee.sale_line.sale
if kind == 'purchase' and getattr(fee, 'line', None):
return fee.line.purchase
for lot in self._get_report_commission_lots():
if kind == 'sale' and getattr(lot, 'sale_line', None):
return lot.sale_line.sale
if kind == 'purchase' and getattr(lot, 'line', None):
return lot.line.purchase
records = getattr(self, kind + 's', None)
if records:
return records[0]
def _get_report_commission_trade(self):
for kind in ('sale', 'purchase'):
trade = self._get_report_commission_trade_for(kind)
if trade:
return trade
for line in self._get_report_invoice_lines():
origin = getattr(line, 'origin', None)
if not origin:
@@ -345,6 +409,10 @@ class Invoice(metaclass=PoolMeta):
trade = self._get_report_commission_trade()
return getattr(trade, name, '') if trade else ''
def _get_report_commission_value_for(self, kind, name):
trade = self._get_report_commission_trade_for(kind)
return getattr(trade, name, '') if trade else ''
@property
def report_commission_invoice_number(self):
return self._get_report_commission_value(
@@ -400,6 +468,76 @@ class Invoice(metaclass=PoolMeta):
return self._get_report_commission_value(
'report_commission_secondary_rate_line')
def _get_report_commission_base_amount_display_for(self, kind):
trade = self._get_report_commission_trade_for(kind)
if trade:
base = getattr(trade, 'report_commission_base_amount_display', None)
if base:
return base
amount = (
getattr(trade, 'untaxed_amount', None)
or getattr(trade, 'total_amount', None)
or 0)
return self._format_report_number(
amount, digits='0.01', strip_trailing_zeros=False)
return self.report_commission_total_display
def _get_report_commission_currency_name_for(self, kind):
trade = self._get_report_commission_trade_for(kind)
currency = getattr(trade, 'currency', None) if trade else self.currency
return self._report_record_name(currency)
def _get_report_commission_rate_line_for(self, kind):
fee = self._get_report_commission_fee()
if not fee:
return self._get_report_commission_value(
'report_commission_rate_line')
mode = getattr(fee, 'mode', None)
price = getattr(fee, 'price', None) or 0
if mode in {'pprice', 'rate', 'pcost'}:
return ' '.join(part for part in [
self._format_report_number(price, digits='0.0000'),
'% on',
kind,
'amount',
self._get_report_commission_base_amount_display_for(kind),
self._get_report_commission_currency_name_for(kind),
] if part)
if (getattr(fee, 'enable_linked_currency', False)
and getattr(fee, 'linked_price', None) is not None):
price = fee.linked_price
currency = getattr(fee, 'linked_currency', None)
unit = getattr(fee, 'linked_unit', None)
else:
currency = getattr(fee, 'currency', None)
unit = getattr(fee, 'unit', None)
price_text = self._format_report_number(price, digits='0.0000')
currency_text = self._report_record_name(currency)
unit_text = self._report_record_name(unit)
if currency_text and unit_text:
return '%s %s/%s' % (price_text, currency_text, unit_text)
return ' '.join(part for part in [
price_text, currency_text or unit_text,
] if part)
@property
def report_sale_commission_rate_line(self):
return self._get_report_commission_rate_line_for('sale')
@property
def report_purchase_commission_rate_line(self):
return self._get_report_commission_rate_line_for('purchase')
@property
def report_sale_commission_secondary_rate_line(self):
return ''
@property
def report_purchase_commission_secondary_rate_line(self):
return ''
@property
def report_commission_currency_name(self):
return self._get_report_commission_value(
@@ -410,6 +548,149 @@ class Invoice(metaclass=PoolMeta):
return self._get_report_commission_value(
'report_commission_total_display')
def _get_report_commission_address_for(self, kind):
trade = self._get_report_commission_trade_for(kind)
return getattr(trade, 'report_address', '') or ''
def _get_report_commission_date_for(self, kind):
trade = self._get_report_commission_trade_for(kind)
return (
getattr(trade, 'sale_date', None)
or getattr(trade, 'purchase_date', None)
or self.invoice_date)
def _get_report_commission_total_display_for(self, kind):
fee = self._get_report_commission_fee()
if fee and hasattr(fee, 'get_amount'):
amount = fee.get_amount()
else:
amount = self.total_amount or 0
return self._format_report_number(
abs(Decimal(str(amount or 0))), digits='0.01',
strip_trailing_zeros=False)
@property
def report_sale_commission_address(self):
return self._get_report_commission_address_for('sale')
@property
def report_purchase_commission_address(self):
return self._get_report_commission_address_for('purchase')
@property
def report_sale_commission_date(self):
return self._get_report_commission_date_for('sale')
@property
def report_purchase_commission_date(self):
return self._get_report_commission_date_for('purchase')
@property
def report_sale_commission_invoice_number(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_invoice_number')
@property
def report_purchase_commission_invoice_number(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_invoice_number')
@property
def report_sale_commission_contract_number(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_contract_number')
@property
def report_purchase_commission_contract_number(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_contract_number')
@property
def report_sale_commission_bl_number(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_bl_number')
@property
def report_purchase_commission_bl_number(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_bl_number')
@property
def report_sale_commission_bl_date(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_bl_date')
@property
def report_purchase_commission_bl_date(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_bl_date')
@property
def report_sale_commission_vessel(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_vessel')
@property
def report_purchase_commission_vessel(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_vessel')
@property
def report_sale_commission_invoice_line(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_invoice_line')
@property
def report_purchase_commission_invoice_line(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_invoice_line')
@property
def report_sale_commission_quantity_unit_upper(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_quantity_unit_upper')
@property
def report_purchase_commission_quantity_unit_upper(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_quantity_unit_upper')
@property
def report_sale_commission_quantity_display(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_quantity_display')
@property
def report_purchase_commission_quantity_display(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_quantity_display')
@property
def report_sale_commission_lbs_display(self):
return self._get_report_commission_value_for(
'sale', 'report_commission_lbs_display')
@property
def report_purchase_commission_lbs_display(self):
return self._get_report_commission_value_for(
'purchase', 'report_commission_lbs_display')
@property
def report_sale_commission_currency_name(self):
return self._report_record_name(self.currency)
@property
def report_purchase_commission_currency_name(self):
return self._report_record_name(self.currency)
@property
def report_sale_commission_total_display(self):
return self._get_report_commission_total_display_for('sale')
@property
def report_purchase_commission_total_display(self):
return self._get_report_commission_total_display_for('purchase')
@staticmethod
def _get_report_related_lots(line):
lots = []

View File

@@ -4,6 +4,7 @@
import datetime
from contextlib import nullcontext
from decimal import Decimal
from types import SimpleNamespace
from unittest.mock import ANY, Mock, patch
from xml.etree import ElementTree
@@ -5210,6 +5211,68 @@ class PurchaseTradeTestCase(ModuleTestCase):
'amount': None,
}])
def test_sale_commission_invoice_uses_linked_perqt_fee_rate(self):
'sale commission invoice shows linked fee price per sale quantity'
Invoice = Pool().get('account.invoice')
sale = SimpleNamespace(
currency=SimpleNamespace(rec_name='USD'),
untaxed_amount=Decimal('7960'),
total_amount=Decimal('7960'))
sale_line = SimpleNamespace(sale=sale)
lot = SimpleNamespace(id=1, sale_line=sale_line, line=None)
fee = SimpleNamespace(
mode='perqt',
price=Decimal('0.02'),
enable_linked_currency=True,
linked_price=Decimal('2'),
linked_currency=SimpleNamespace(rec_name='USC'),
linked_unit=SimpleNamespace(rec_name='Kilogram'),
currency=SimpleNamespace(rec_name='USD'),
unit=SimpleNamespace(rec_name='MT'),
sale_line=sale_line,
line=None,
lots=[lot],
_get_effective_fee_lots=lambda: [lot])
invoice = Invoice()
invoice.currency = SimpleNamespace(rec_name='USD')
invoice.lines = [
SimpleNamespace(type='line', fee=fee, lot=lot, origin=None)]
self.assertEqual(
invoice.report_sale_commission_rate_line,
'2 USC/Kilogram')
self.assertEqual(invoice.report_sale_commission_secondary_rate_line, '')
def test_purchase_commission_invoice_uses_purchase_amount_for_percent_fee(self):
'purchase commission invoice shows percent fee on purchase amount'
Invoice = Pool().get('account.invoice')
purchase = SimpleNamespace(
currency=SimpleNamespace(rec_name='USD'),
untaxed_amount=Decimal('7960'),
total_amount=Decimal('7960'))
purchase_line = SimpleNamespace(purchase=purchase)
lot = SimpleNamespace(id=1, line=purchase_line, sale_line=None)
fee = SimpleNamespace(
mode='pprice',
price=Decimal('1.5'),
enable_linked_currency=False,
currency=SimpleNamespace(rec_name='USD'),
unit=SimpleNamespace(rec_name='MT'),
sale_line=None,
line=purchase_line,
lots=[lot],
_get_effective_fee_lots=lambda: [lot])
invoice = Invoice()
invoice.currency = SimpleNamespace(rec_name='USD')
invoice.lines = [
SimpleNamespace(type='line', fee=fee, lot=lot, origin=None)]
self.assertEqual(
invoice.report_purchase_commission_rate_line,
'1.5 % on purchase amount 7960.00 USD')
self.assertEqual(
invoice.report_purchase_commission_secondary_rate_line, '')
def test_fee_delete_detects_generated_ordered_purchase(self):
'ordered fee deletion targets only its generated service purchase'
Fee = Pool().get('fee.fee')