This commit is contained in:
2026-06-17 12:25:21 +02:00
parent ee183e23c5
commit a754e15e32
6 changed files with 97 additions and 63 deletions

View File

@@ -8,7 +8,7 @@ from trytond.tools import is_full_text, lstrip_wildcard
from trytond.transaction import Transaction, inactive_records
from decimal import getcontext, Decimal, ROUND_UP, ROUND_HALF_UP
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
from sql.conditionals import Case
from sql.conditionals import Case, Coalesce
from sql import Column, Literal, Null
from sql.functions import CurrentTimestamp, DateTrunc
from trytond.wizard import Button, StateTransition, StateView, Wizard
@@ -1515,10 +1515,10 @@ class FeeLots(ModelSQL,ModelView):
class FeeReport(
ModelSQL, ModelView):
"Fee Report"
__name__ = 'fee.report'
r_purchase_line = fields.Many2One('purchase.line', "Purchase line")
r_sale_line = fields.Many2One('sale.line', "Sale line")
"Fee Report"
__name__ = 'fee.report'
r_purchase_line = fields.Many2One('purchase.line', "Purchase line")
r_sale_line = fields.Many2One('sale.line', "Sale line")
r_shipment_in = fields.Many2One('stock.shipment.in', "Shipment in")
r_shipment_out = fields.Many2One('stock.shipment.out', "Shipment out")
r_shipment_internal = fields.Many2One('stock.shipment.internal', "Shipment internal")
@@ -1540,9 +1540,10 @@ class FeeReport(
('pcost', '% cost price'),
], 'Mode', required=True)
r_fee_quantity = fields.Function(fields.Numeric("Qt",digits=(1,4)),'get_quantity')
r_fee_unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit')
r_purchase = fields.Many2One('purchase.purchase',"Purchase", ondelete='CASCADE')
r_fee_quantity = fields.Function(fields.Numeric("Qt",digits=(1,4)),'get_quantity')
r_fee_unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit')
r_purchase = fields.Many2One('purchase.purchase',"Purchase", ondelete='CASCADE')
r_sale = fields.Many2One('sale.sale',"Sale", ondelete='CASCADE')
r_fee_amount = fields.Function(fields.Numeric("Amount", digits=(1,4)),'get_amount')
r_inv = fields.Function(fields.Many2One('account.invoice',"Invoice"),'get_invoice')
r_dn_cn = fields.Function(fields.Many2One('account.invoice',"DN/CN"),
@@ -1617,16 +1618,26 @@ class FeeReport(
@classmethod
def table_query(cls):
FeeReport = Pool().get('fee.fee')
fr = FeeReport.__table__()
Purchase = Pool().get('purchase.purchase')
pu = Purchase.__table__()
PurchaseLine = Pool().get('purchase.line')
pl = PurchaseLine.__table__()
Sale = Pool().get('sale.sale')
sa = Sale.__table__()
SaleLine = Pool().get('sale.line')
sl = SaleLine.__table__()
FeeReport = Pool().get('fee.fee')
fr = FeeReport.__table__()
FeeLots = Pool().get('fee.lots')
fl = FeeLots.__table__()
PurchaseLine = Pool().get('purchase.line')
pl = PurchaseLine.__table__()
SaleLine = Pool().get('sale.line')
sl = SaleLine.__table__()
fee_lot_lines = (
fl
.join(pl, 'LEFT', condition=fl.line == pl.id)
.join(sl, 'LEFT', condition=fl.sale_line == sl.id)
.select(
fl.fee.as_('fee'),
Min(fl.line).as_('purchase_line'),
Min(pl.purchase).as_('purchase'),
Min(fl.sale_line).as_('sale_line'),
Min(sl.sale).as_('sale'),
group_by=[fl.fee]))
context = Transaction().context
party = context.get('party')
@@ -1644,10 +1655,13 @@ class FeeReport(
wh &= (fr.fee_counterparty == party)
if fee_type:
wh &= (fr.fee_type == fee_type)
if purchase:
wh &= (pu.id == purchase)
if sale:
wh &= (sa.id == sale)
purchase_expr = Coalesce(fee_lot_lines.purchase, fr.purchase)
purchase_line_expr = Coalesce(fee_lot_lines.purchase_line, fr.line)
sale_expr = fee_lot_lines.sale
if purchase:
wh &= (purchase_expr == purchase)
if sale:
wh &= (sale_expr == sale)
if shipment_in:
wh &= (fr.shipment_in == shipment_in)
if invoice_status == 'not_invoiced':
@@ -1656,30 +1670,33 @@ class FeeReport(
wh &= ((fr.state == 'invoiced') & (fr.dn_cn == Null))
elif invoice_status == 'invoiced_with_dn_cn':
wh &= ((fr.state == 'invoiced') & (fr.dn_cn != Null))
# if shipment_out:
# wh &= (fr.shipment_out == shipment_out)
query = fr.join(pl,'LEFT',condition=fr.line == pl.id).join(pu,'LEFT', condition=pl.purchase == pu.id).select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
fr.id.as_('id'),
fr.line.as_('r_purchase_line'),
Literal(None).as_('r_sale_line'),
fr.shipment_in.as_('r_shipment_in'),
Literal(None).as_('r_shipment_out'),
fr.shipment_internal.as_('r_shipment_internal'),
fr.product.as_('r_fee_type'),
fr.supplier.as_('r_fee_counterparty'),
# if shipment_out:
# wh &= (fr.shipment_out == shipment_out)
query = fr.join(
fee_lot_lines, 'LEFT', condition=fr.id == fee_lot_lines.fee
).select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
fr.id.as_('id'),
purchase_line_expr.as_('r_purchase_line'),
fee_lot_lines.sale_line.as_('r_sale_line'),
fr.shipment_in.as_('r_shipment_in'),
Literal(None).as_('r_shipment_out'),
fr.shipment_internal.as_('r_shipment_internal'),
fr.product.as_('r_fee_type'),
fr.supplier.as_('r_fee_counterparty'),
fr.type.as_('r_type'),
fr.p_r.as_('r_fee_paystatus'),
fr.mode.as_('r_mode'),
fr.state.as_('r_state'),
fr.purchase.as_('r_purchase'),
#fr.amount.as_('r_fee_amount'),
fr.price.as_('r_fee_price'),
fr.currency.as_('r_fee_currency'),
fr.p_r.as_('r_fee_paystatus'),
fr.mode.as_('r_mode'),
fr.state.as_('r_state'),
purchase_expr.as_('r_purchase'),
sale_expr.as_('r_sale'),
#fr.amount.as_('r_fee_amount'),
fr.price.as_('r_fee_price'),
fr.currency.as_('r_fee_currency'),
#fr.fee_lots.as_('r_fee_lots'),
#fr.lots.as_('r_lots'),
where=wh)

View File

@@ -159,5 +159,9 @@ this repository contains the full copyright notices and license terms. -->
action="act_fee_report_form"
sequence="10"
id="menu_fee_report_form"/>
<record model="ir.ui.menu" id="menu_fee_report_form">
<field name="parent" ref="menu_fees"/>
<field name="sequence" eval="10"/>
</record>
</data>
</tryton>

View File

@@ -612,24 +612,24 @@ class MtmStrategy(ModelSQL, ModelView):
if comp.price_source_type == 'curve' and comp.price_index:
value = Decimal(
comp.price_index.get_price(
dt,
line.unit,
self.currency,
last=scenario.use_last_price
)
)
comp.price_index.get_price(
dt,
line.unit,
self.currency,
relative_last=scenario.use_last_price
)
)
elif comp.price_source_type == 'matrix' and comp.price_matrix:
value = self._get_matrix_price(comp, line, dt)
elif comp.price_source_type == 'fixed':
value = Decimal(comp.get_price(
dt,
line.unit,
self.currency,
last=scenario.use_last_price
))
value = Decimal(comp.get_price(
dt,
line.unit,
self.currency,
relative_last=scenario.use_last_price
))
if comp.ratio:
value *= Decimal(comp.ratio) / Decimal(100)

View File

@@ -709,6 +709,11 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(
strategy.get_mtm(line, Decimal('10')),
Decimal('250.00'))
strategy.components[0].price_index.get_price.assert_called_once_with(
'2026-03-29',
line.unit,
strategy.currency,
relative_last=True)
@with_transaction()
def test_get_mtm_uses_fixed_pricing_component(self):
@@ -1205,6 +1210,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
def test_get_strategy_mtm_price_applies_component_ratio(self):
'strategy mtm price applies component ratios'
price_index = Mock(get_price=Mock(return_value=Decimal('100')))
strategy = Mock(
scenario=Mock(
valuation_date='2026-03-29',
@@ -1214,7 +1220,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
)
strategy.components = [Mock(
price_source_type='curve',
price_index=Mock(get_price=Mock(return_value=Decimal('100'))),
price_index=price_index,
price_matrix=None,
ratio=Decimal('25'),
)]
@@ -1223,6 +1229,11 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(
valuation_module.Valuation._get_strategy_mtm_price(strategy, line),
Decimal('25.0000'))
price_index.get_price.assert_called_once_with(
'2026-03-29',
line.unit,
strategy.currency,
relative_last=True)
def test_get_strategy_mtm_price_keeps_signed_component_spread(self):
'strategy mtm price keeps signed spread between different components'

View File

@@ -522,7 +522,7 @@ class ValuationBase(ModelSQL):
scenario.valuation_date,
line.unit,
strategy.currency,
last=scenario.use_last_price
relative_last=scenario.use_last_price
))
elif comp.price_source_type == 'matrix' and comp.price_matrix:
value = Decimal(strategy._get_matrix_price(
@@ -533,7 +533,7 @@ class ValuationBase(ModelSQL):
scenario.valuation_date,
line.unit,
strategy.currency,
last=scenario.use_last_price
relative_last=scenario.use_last_price
))
if comp.ratio:
@@ -583,7 +583,7 @@ class ValuationBase(ModelSQL):
scenario.valuation_date,
line.unit,
strategy.currency,
last=scenario.use_last_price))
relative_last=scenario.use_last_price))
previous = cls._previous_curve_price(
component.price_index,
scenario.valuation_date,

View File

@@ -1,6 +1,8 @@
<tree>
<button name="invoice" string="Action"/>
<field name="r_purchase_line" width="90"/>
<field name="r_purchase_line" width="90" tree_invisible="1"/>
<field name="r_purchase" width="100"/>
<field name="r_sale" width="100"/>
<field name="r_shipment_origin" width="120"/>
<field name="r_fee_type" width="120"/>
<field name="r_fee_counterparty" width="100"/>