From 1dc8803287559f030e18f349bdea58c914b590a0 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Sun, 24 May 2026 17:53:33 +0200 Subject: [PATCH] GR --- modules/purchase_trade/__init__.py | 2 + modules/purchase_trade/ctrm_reporting.py | 236 +++++++++++++++--- modules/purchase_trade/ctrm_reporting.xml | 21 ++ .../ctrm_position_financial_context_form.xml | 24 ++ .../view/ctrm_position_financial_list.xml | 17 ++ 5 files changed, 261 insertions(+), 39 deletions(-) create mode 100644 modules/purchase_trade/view/ctrm_position_financial_context_form.xml create mode 100644 modules/purchase_trade/view/ctrm_position_financial_list.xml diff --git a/modules/purchase_trade/__init__.py b/modules/purchase_trade/__init__.py index 5a9b8b2..76207e6 100755 --- a/modules/purchase_trade/__init__.py +++ b/modules/purchase_trade/__init__.py @@ -60,6 +60,8 @@ def register(): global_reporting.GRConfiguration, ctrm_reporting.CTRMPhysicalPosition, ctrm_reporting.CTRMPhysicalPositionContext, + ctrm_reporting.CTRMFinancialPosition, + ctrm_reporting.CTRMFinancialPositionContext, configuration.Configuration, pricing.ImportPricesStart, pricing.ImportPricesResult, diff --git a/modules/purchase_trade/ctrm_reporting.py b/modules/purchase_trade/ctrm_reporting.py index 7ff4ee3..9c51a75 100644 --- a/modules/purchase_trade/ctrm_reporting.py +++ b/modules/purchase_trade/ctrm_reporting.py @@ -1,5 +1,6 @@ from sql import Literal, Null from sql.aggregate import Max, Min, Sum +from sql.conditionals import Case, Coalesce from sql.functions import CurrentTimestamp from trytond.model import ModelSQL, ModelView, fields @@ -21,8 +22,6 @@ class CTRMPhysicalPositionContext(ModelView): ('open', 'Open'), ('physic', 'Physic'), ('shipped', 'Shipped'), - ('hedge', 'Hedge'), - ('priced', 'Priced'), ], "Position Type") @classmethod @@ -44,8 +43,6 @@ class CTRMPhysicalPosition(ModelSQL, ModelView): ('open', 'Open'), ('physic', 'Physic'), ('shipped', 'Shipped'), - ('hedge', 'Hedge'), - ('priced', 'Priced'), ], "Position Type") physical_qty = fields.Numeric("Physical Quantity", digits=(16, 5)) hedged_qty = fields.Numeric("Hedged Quantity", digits=(16, 5)) @@ -58,8 +55,11 @@ class CTRMPhysicalPosition(ModelSQL, ModelView): @classmethod def table_query(cls): - OpenPosition = Pool().get('open.position') - op = OpenPosition.__table__() + LotReport = Pool().get('lot.report') + PurchaseLine = Pool().get('purchase.line') + Purchase = Pool().get('purchase.purchase') + SaleLine = Pool().get('sale.line') + Sale = Pool().get('sale.sale') context = Transaction().context as_of = context.get('as_of') @@ -69,51 +69,209 @@ class CTRMPhysicalPosition(ModelSQL, ModelView): currency = context.get('currency') position_type = context.get('position_type') - where = Literal(True) + lot_context = { + 'purchase': None, + 'sale': None, + 'shipment': None, + 'type': 'all', + 'state': 'all', + 'wh': 'all', + 'group': 'by_physic', + 'origin': 'all', + 'ps': 'all', + 'shipping_status': 'all', + } if as_of: - where &= ((op.period_start == Null) | (op.period_start <= as_of)) - where &= ((op.period_end == Null) | (op.period_end >= as_of)) + lot_context['todate'] = as_of if product: - where &= op.product == product + lot_context['product'] = product if supplier: - where &= op.supplier == supplier + lot_context['supplier'] = supplier if client: - where &= op.client == client + lot_context['client'] = client + + lr = LotReport.table_query(lot_context) + pl = PurchaseLine.__table__() + pu = Purchase.__table__() + sl = SaleLine.__table__() + sa = Sale.__table__() + + position_type_expr = Case( + (lr.r_lot_type == 'virtual', 'open'), + (lr.r_shipping_status.in_(['scheduled', 'shipped', 'received']), + 'shipped'), + else_='physic') + currency_expr = Coalesce(sa.currency, pu.currency) + price_expr = Coalesce(sl.unit_price, pl.unit_price, 0) + + where = Literal(True) if currency: - where &= op.currency == currency + where &= currency_expr == currency if position_type: - where &= op.type == position_type + where &= position_type_expr == position_type group_by = [ - op.product, - op.supplier, - op.client, - op.currency, - op.uom, - op.type, - op.period_start, - op.period_end, + lr.r_lot_product, + lr.r_supplier, + lr.r_client, + currency_expr, + lr.r_lot_unit, + position_type_expr, ] - return op.select( + return ( + lr + .join(pl, 'LEFT', condition=pl.id == lr.r_line) + .join(pu, 'LEFT', condition=pu.id == lr.r_purchase) + .join(sl, 'LEFT', condition=sl.id == lr.r_sale_line) + .join(sa, 'LEFT', condition=sa.id == lr.r_sale) + .select( Literal(0).as_('create_uid'), CurrentTimestamp().as_('create_date'), Literal(None).as_('write_uid'), Literal(None).as_('write_date'), - Min(op.id).as_('id'), - op.product.as_('product'), - op.supplier.as_('supplier'), - op.client.as_('client'), - op.currency.as_('currency'), - op.uom.as_('uom'), - op.type.as_('position_type'), - Sum(op.physical_qty).as_('physical_qty'), - Sum(op.hedged_qty).as_('hedged_qty'), - Sum(op.net_exposure).as_('net_exposure'), - Sum(op.amount).as_('amount'), - Sum(op.mtm).as_('mtm'), - Sum(op.amount - op.mtm).as_('pnl'), - Max(op.period_start).as_('period_start'), - Max(op.period_end).as_('period_end'), + Min(lr.id).as_('id'), + lr.r_lot_product.as_('product'), + lr.r_supplier.as_('supplier'), + lr.r_client.as_('client'), + currency_expr.as_('currency'), + lr.r_lot_unit.as_('uom'), + position_type_expr.as_('position_type'), + Sum(lr.r_lot_quantity).as_('physical_qty'), + Literal(0).as_('hedged_qty'), + Sum(lr.r_lot_quantity).as_('net_exposure'), + Sum(lr.r_lot_quantity * price_expr).as_('amount'), + Literal(None).as_('mtm'), + Literal(None).as_('pnl'), + Literal(None).as_('period_start'), + Literal(None).as_('period_end'), where=where, - group_by=group_by) + group_by=group_by)) + + +class CTRMFinancialPositionContext(ModelView): + "CTRM Financial Position Context" + __name__ = 'ctrm.reporting.position.financial.context' + + trade_from = fields.Date("Trade Date From") + trade_to = fields.Date("Trade Date To") + maturity_from = fields.Date("Maturity From") + maturity_to = fields.Date("Maturity To") + product = fields.Many2One('product.product', "Product") + party = fields.Many2One('party.party', "Counterparty") + purchase = fields.Many2One('purchase.purchase', "Purchase") + sale = fields.Many2One('sale.sale', "Sale") + direction = fields.Selection([ + (None, ''), + ('long', 'Long'), + ('short', 'Short'), + ], 'Direction') + state = fields.Selection([ + (None, ''), + ('open', 'Open'), + ('closed', 'Closed'), + ], 'State') + open_only = fields.Boolean("Open Positions Only") + + @classmethod + def default_trade_to(cls): + Date = Pool().get('ir.date') + return Date.today() + + @classmethod + def default_open_only(cls): + return True + + +class CTRMFinancialPosition(ModelSQL, ModelView): + "CTRM Financial Position" + __name__ = 'ctrm.reporting.position.financial' + + derivative = fields.Many2One('derivative.derivative', "Derivative") + trade_date = fields.Date("Trade Date") + maturity_date = fields.Date("Maturity") + product = fields.Many2One('product.product', "Product") + party = fields.Many2One('party.party', "Counterparty") + purchase = fields.Many2One('purchase.purchase', "Purchase") + purchase_line = fields.Many2One('purchase.line', "Purchase Line") + sale = fields.Many2One('sale.sale', "Sale") + sale_line = fields.Many2One('sale.line', "Sale Line") + price_index = fields.Many2One('price.price', "Curve") + direction = fields.Selection([ + ('long', 'Long'), + ('short', 'Short'), + ], 'Direction') + state = fields.Selection([ + ('open', 'Open'), + ('closed', 'Closed'), + ], 'State') + contract_count = fields.Integer("Nb ct") + open_qty = fields.Numeric("Open Quantity", digits='unit') + entry_price = fields.Numeric("Entry Price", digits='currency') + exit_price = fields.Numeric("Exit Price", digits='currency') + + @classmethod + def table_query(cls): + Derivative = Pool().get('derivative.derivative') + d = Derivative.__table__() + + context = Transaction().context + trade_from = context.get('trade_from') + trade_to = context.get('trade_to') + maturity_from = context.get('maturity_from') + maturity_to = context.get('maturity_to') + product = context.get('product') + party = context.get('party') + purchase = context.get('purchase') + sale = context.get('sale') + direction = context.get('direction') + state = context.get('state') + open_only = context.get('open_only') + + where = Literal(True) + if trade_from: + where &= d.trade_date >= trade_from + if trade_to: + where &= d.trade_date <= trade_to + if maturity_from: + where &= d.maturity_date >= maturity_from + if maturity_to: + where &= d.maturity_date <= maturity_to + if product: + where &= d.product == product + if party: + where &= d.party == party + if purchase: + where &= d.purchase == purchase + if sale: + where &= d.sale == sale + if direction: + where &= d.direction == direction + if state: + where &= d.state == state + if open_only: + where &= d.open_qty > 0 + + return d.select( + Literal(0).as_('create_uid'), + CurrentTimestamp().as_('create_date'), + Literal(None).as_('write_uid'), + Literal(None).as_('write_date'), + d.id.as_('id'), + d.id.as_('derivative'), + d.trade_date.as_('trade_date'), + d.maturity_date.as_('maturity_date'), + d.product.as_('product'), + d.party.as_('party'), + d.purchase.as_('purchase'), + d.line.as_('purchase_line'), + d.sale.as_('sale'), + d.sale_line.as_('sale_line'), + d.price_index.as_('price_index'), + d.direction.as_('direction'), + d.state.as_('state'), + d.nb_ct.as_('contract_count'), + d.open_qty.as_('open_qty'), + d.price.as_('entry_price'), + d.exit_price.as_('exit_price'), + where=where) diff --git a/modules/purchase_trade/ctrm_reporting.xml b/modules/purchase_trade/ctrm_reporting.xml index 6f6f3a3..983ea1f 100644 --- a/modules/purchase_trade/ctrm_reporting.xml +++ b/modules/purchase_trade/ctrm_reporting.xml @@ -20,6 +20,26 @@ + + ctrm.reporting.position.financial.context + form + ctrm_position_financial_context_form + + + ctrm.reporting.position.financial + tree + ctrm_position_financial_list + + + 1.2 Financial Paper Position + ctrm.reporting.position.financial + ctrm.reporting.position.financial.context + + + + + + +