diff --git a/modules/purchase_trade/__init__.py b/modules/purchase_trade/__init__.py
index 76207e6..ec54115 100755
--- a/modules/purchase_trade/__init__.py
+++ b/modules/purchase_trade/__init__.py
@@ -62,6 +62,8 @@ def register():
ctrm_reporting.CTRMPhysicalPositionContext,
ctrm_reporting.CTRMFinancialPosition,
ctrm_reporting.CTRMFinancialPositionContext,
+ ctrm_reporting.CTRMNetPosition,
+ ctrm_reporting.CTRMNetPositionContext,
configuration.Configuration,
pricing.ImportPricesStart,
pricing.ImportPricesResult,
diff --git a/modules/purchase_trade/ctrm_reporting.py b/modules/purchase_trade/ctrm_reporting.py
index 9c51a75..58a344f 100644
--- a/modules/purchase_trade/ctrm_reporting.py
+++ b/modules/purchase_trade/ctrm_reporting.py
@@ -1,4 +1,4 @@
-from sql import Literal, Null
+from sql import Literal
from sql.aggregate import Max, Min, Sum
from sql.conditionals import Case, Coalesce
from sql.functions import CurrentTimestamp
@@ -8,270 +8,303 @@ from trytond.pool import Pool
from trytond.transaction import Transaction
-class CTRMPhysicalPositionContext(ModelView):
- "CTRM Physical Position Context"
- __name__ = 'ctrm.reporting.position.physical.context'
+PHYSICAL_VALUATION_TYPES = [
+ 'priced',
+ 'pur. priced',
+ 'pur. efp',
+ 'sale priced',
+ 'sale efp',
+ 'line fee',
+ 'pur. fee',
+ 'sale fee',
+ 'shipment fee',
+ 'market',
+]
+DERIVATIVE_VALUATION_TYPES = ['derivative']
- as_of = fields.Date("As of")
+
+class CTRMValuationContextMixin:
+ date = fields.Date("Valuation Date")
product = fields.Many2One('product.product', "Product")
- supplier = fields.Many2One('party.party', "Supplier")
- client = fields.Many2One('party.party', "Client")
+ counterparty = fields.Many2One('party.party', "Counterparty")
currency = fields.Many2One('currency.currency', "Currency")
- position_type = fields.Selection([
- (None, ""),
- ('open', 'Open'),
- ('physic', 'Physic'),
- ('shipped', 'Shipped'),
- ], "Position Type")
+ purchase = fields.Many2One('purchase.purchase', "Purchase")
+ sale = fields.Many2One('sale.sale', "Sale")
+ strategy = fields.Many2One('mtm.strategy', "Strategy")
+ state = fields.Char("State")
@classmethod
- def default_as_of(cls):
+ def default_date(cls):
Date = Pool().get('ir.date')
return Date.today()
+class CTRMPhysicalPositionContext(
+ CTRMValuationContextMixin, ModelView):
+ "CTRM Physical Position Context"
+ __name__ = 'ctrm.reporting.position.physical.context'
+
+
class CTRMPhysicalPosition(ModelSQL, ModelView):
"CTRM Physical Position"
__name__ = 'ctrm.reporting.position.physical'
+ valuation_date = fields.Date("Valuation Date")
+ lot = fields.Many2One('lot.lot', "Lot")
+ 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")
+ type = fields.Selection([
+ ('priced', 'Price'),
+ ('pur. priced', 'Pur. price'),
+ ('pur. efp', 'Pur. efp'),
+ ('sale priced', 'Sale price'),
+ ('sale efp', 'Sale efp'),
+ ('line fee', 'Line fee'),
+ ('pur. fee', 'Pur. fee'),
+ ('sale fee', 'Sale fee'),
+ ('shipment fee', 'Shipment fee'),
+ ('market', 'Market'),
+ ], "Type")
+ reference = fields.Char("Reference")
+ counterparty = fields.Many2One('party.party', "Counterparty")
product = fields.Many2One('product.product', "Product")
- supplier = fields.Many2One('party.party', "Supplier")
- client = fields.Many2One('party.party', "Client")
+ state = fields.Char("State")
+ price = fields.Numeric("Price", digits=(16, 4))
currency = fields.Many2One('currency.currency', "Currency")
- uom = fields.Many2One('product.uom', "Unit")
- position_type = fields.Selection([
- ('open', 'Open'),
- ('physic', 'Physic'),
- ('shipped', 'Shipped'),
- ], "Position Type")
- physical_qty = fields.Numeric("Physical Quantity", digits=(16, 5))
- hedged_qty = fields.Numeric("Hedged Quantity", digits=(16, 5))
- net_exposure = fields.Numeric("Net Exposure", digits=(16, 5))
+ quantity = fields.Numeric("Quantity", digits=(16, 5))
+ unit = fields.Many2One('product.uom', "Unit")
amount = fields.Numeric("Amount", digits=(16, 2))
+ base_amount = fields.Numeric("Base Amount", digits=(16, 2))
+ mtm_price = fields.Numeric("MTM Price", digits=(16, 4))
mtm = fields.Numeric("MTM", digits=(16, 2))
- pnl = fields.Numeric("P&L", digits=(16, 2))
- period_start = fields.Date("Period Start")
- period_end = fields.Date("Period End")
+ strategy = fields.Many2One('mtm.strategy', "Strategy")
@classmethod
def table_query(cls):
- 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')
+ ValuationLine = Pool().get('valuation.valuation.line')
+ val = ValuationLine.__table__()
context = Transaction().context
- as_of = context.get('as_of')
- product = context.get('product')
- supplier = context.get('supplier')
- client = context.get('client')
- currency = context.get('currency')
- position_type = context.get('position_type')
+ where = val.type.in_(PHYSICAL_VALUATION_TYPES)
+ if context.get('date'):
+ where &= val.date == context['date']
+ if context.get('product'):
+ where &= val.product == context['product']
+ if context.get('counterparty'):
+ where &= val.counterparty == context['counterparty']
+ if context.get('currency'):
+ where &= val.currency == context['currency']
+ if context.get('purchase'):
+ where &= val.purchase == context['purchase']
+ if context.get('sale'):
+ where &= val.sale == context['sale']
+ if context.get('strategy'):
+ where &= val.strategy == context['strategy']
+ if context.get('state'):
+ where &= val.state == context['state']
- 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:
- lot_context['todate'] = as_of
- if product:
- lot_context['product'] = product
- if supplier:
- lot_context['supplier'] = supplier
- if 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 &= currency_expr == currency
- if position_type:
- where &= position_type_expr == position_type
-
- group_by = [
- lr.r_lot_product,
- lr.r_supplier,
- lr.r_client,
- currency_expr,
- lr.r_lot_unit,
- position_type_expr,
- ]
-
- 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(
+ return val.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
- 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))
+ val.id.as_('id'),
+ val.date.as_('valuation_date'),
+ val.lot.as_('lot'),
+ val.purchase.as_('purchase'),
+ val.line.as_('purchase_line'),
+ val.sale.as_('sale'),
+ val.sale_line.as_('sale_line'),
+ val.type.as_('type'),
+ val.reference.as_('reference'),
+ val.counterparty.as_('counterparty'),
+ val.product.as_('product'),
+ val.state.as_('state'),
+ val.price.as_('price'),
+ val.currency.as_('currency'),
+ val.quantity.as_('quantity'),
+ val.unit.as_('unit'),
+ val.amount.as_('amount'),
+ val.base_amount.as_('base_amount'),
+ val.mtm_price.as_('mtm_price'),
+ val.mtm.as_('mtm'),
+ val.strategy.as_('strategy'),
+ where=where)
-class CTRMFinancialPositionContext(ModelView):
+class CTRMFinancialPositionContext(
+ CTRMValuationContextMixin, 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")
+ valuation_date = fields.Date("Valuation Date")
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')
+ reference = fields.Char("Reference")
+ counterparty = fields.Many2One('party.party', "Counterparty")
+ product = fields.Many2One('product.product', "Product")
+ state = fields.Char("State")
+ price = fields.Numeric("Price", digits=(16, 4))
+ currency = fields.Many2One('currency.currency', "Currency")
+ quantity = fields.Numeric("Quantity", digits=(16, 5))
+ unit = fields.Many2One('product.uom', "Unit")
+ amount = fields.Numeric("Amount", digits=(16, 2))
+ base_amount = fields.Numeric("Base Amount", digits=(16, 2))
+ mtm_price = fields.Numeric("MTM Price", digits=(16, 4))
+ mtm = fields.Numeric("MTM", digits=(16, 2))
+ strategy = fields.Many2One('mtm.strategy', "Strategy")
@classmethod
def table_query(cls):
- Derivative = Pool().get('derivative.derivative')
- d = Derivative.__table__()
+ ValuationLine = Pool().get('valuation.valuation.line')
+ val = ValuationLine.__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 = val.type.in_(DERIVATIVE_VALUATION_TYPES)
+ if context.get('date'):
+ where &= val.date == context['date']
+ if context.get('product'):
+ where &= val.product == context['product']
+ if context.get('counterparty'):
+ where &= val.counterparty == context['counterparty']
+ if context.get('currency'):
+ where &= val.currency == context['currency']
+ if context.get('purchase'):
+ where &= val.purchase == context['purchase']
+ if context.get('sale'):
+ where &= val.sale == context['sale']
+ if context.get('strategy'):
+ where &= val.strategy == context['strategy']
+ if context.get('state'):
+ where &= val.state == context['state']
- 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(
+ return val.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'),
+ val.id.as_('id'),
+ val.date.as_('valuation_date'),
+ val.purchase.as_('purchase'),
+ val.line.as_('purchase_line'),
+ val.sale.as_('sale'),
+ val.sale_line.as_('sale_line'),
+ val.reference.as_('reference'),
+ val.counterparty.as_('counterparty'),
+ val.product.as_('product'),
+ val.state.as_('state'),
+ val.price.as_('price'),
+ val.currency.as_('currency'),
+ val.quantity.as_('quantity'),
+ val.unit.as_('unit'),
+ val.amount.as_('amount'),
+ val.base_amount.as_('base_amount'),
+ val.mtm_price.as_('mtm_price'),
+ val.mtm.as_('mtm'),
+ val.strategy.as_('strategy'),
where=where)
+
+
+class CTRMNetPositionContext(
+ CTRMValuationContextMixin, ModelView):
+ "CTRM Net Position Context"
+ __name__ = 'ctrm.reporting.position.net.context'
+
+
+class CTRMNetPosition(ModelSQL, ModelView):
+ "CTRM Net Position"
+ __name__ = 'ctrm.reporting.position.net'
+
+ valuation_date = fields.Date("Valuation Date")
+ product = fields.Many2One('product.product', "Product")
+ counterparty = fields.Many2One('party.party', "Counterparty")
+ currency = fields.Many2One('currency.currency', "Currency")
+ unit = fields.Many2One('product.uom', "Unit")
+ state = fields.Char("State")
+ strategy = fields.Many2One('mtm.strategy', "Strategy")
+ physical_quantity = fields.Numeric(
+ "Physical Quantity", digits=(16, 5))
+ derivative_quantity = fields.Numeric(
+ "Derivative Quantity", digits=(16, 5))
+ net_quantity = fields.Numeric("Net Quantity", digits=(16, 5))
+ physical_amount = fields.Numeric("Physical Amount", digits=(16, 2))
+ derivative_amount = fields.Numeric("Derivative Amount", digits=(16, 2))
+ net_amount = fields.Numeric("Net Amount", digits=(16, 2))
+ mtm = fields.Numeric("MTM", digits=(16, 2))
+
+ @classmethod
+ def table_query(cls):
+ ValuationLine = Pool().get('valuation.valuation.line')
+ val = ValuationLine.__table__()
+
+ context = Transaction().context
+ where = val.type.in_(
+ PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES)
+ if context.get('date'):
+ where &= val.date == context['date']
+ if context.get('product'):
+ where &= val.product == context['product']
+ if context.get('counterparty'):
+ where &= val.counterparty == context['counterparty']
+ if context.get('currency'):
+ where &= val.currency == context['currency']
+ if context.get('purchase'):
+ where &= val.purchase == context['purchase']
+ if context.get('sale'):
+ where &= val.sale == context['sale']
+ if context.get('strategy'):
+ where &= val.strategy == context['strategy']
+ if context.get('state'):
+ where &= val.state == context['state']
+
+ is_derivative = val.type.in_(DERIVATIVE_VALUATION_TYPES)
+ physical_quantity = Case(
+ (is_derivative, 0), else_=Coalesce(val.quantity, 0))
+ derivative_quantity = Case(
+ (is_derivative, Coalesce(val.quantity, 0)), else_=0)
+ physical_amount = Case(
+ (is_derivative, 0), else_=Coalesce(val.amount, 0))
+ derivative_amount = Case(
+ (is_derivative, Coalesce(val.amount, 0)), else_=0)
+
+ group_by = [
+ val.date,
+ val.product,
+ val.counterparty,
+ val.currency,
+ val.unit,
+ val.state,
+ val.strategy,
+ ]
+
+ return val.select(
+ Literal(0).as_('create_uid'),
+ CurrentTimestamp().as_('create_date'),
+ Literal(None).as_('write_uid'),
+ Literal(None).as_('write_date'),
+ Min(val.id).as_('id'),
+ val.date.as_('valuation_date'),
+ val.product.as_('product'),
+ val.counterparty.as_('counterparty'),
+ val.currency.as_('currency'),
+ val.unit.as_('unit'),
+ val.state.as_('state'),
+ val.strategy.as_('strategy'),
+ Sum(physical_quantity).as_('physical_quantity'),
+ Sum(derivative_quantity).as_('derivative_quantity'),
+ Sum(Coalesce(val.quantity, 0)).as_('net_quantity'),
+ Sum(physical_amount).as_('physical_amount'),
+ Sum(derivative_amount).as_('derivative_amount'),
+ Sum(Coalesce(val.amount, 0)).as_('net_amount'),
+ Sum(Coalesce(val.mtm, 0)).as_('mtm'),
+ where=where,
+ group_by=group_by)
diff --git a/modules/purchase_trade/ctrm_reporting.xml b/modules/purchase_trade/ctrm_reporting.xml
index 983ea1f..865491b 100644
--- a/modules/purchase_trade/ctrm_reporting.xml
+++ b/modules/purchase_trade/ctrm_reporting.xml
@@ -11,7 +11,7 @@