Files
tradon/modules/purchase_trade/ctrm_reporting.py
2026-05-24 18:47:19 +02:00

899 lines
35 KiB
Python

from sql import Literal, Null, Window
from sql.aggregate import Max, Min, Sum
from sql.conditionals import Case, Coalesce
from sql.functions import CurrentTimestamp, RowNumber
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import Pool
from trytond.transaction import Transaction
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']
ALL_VALUATION_TYPES = PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES
class CTRMValuationContextMixin:
date = fields.Date("Valuation Date")
product = fields.Many2One('product.product', "Product")
counterparty = fields.Many2One('party.party', "Counterparty")
currency = fields.Many2One('currency.currency', "Currency")
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_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")
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):
ValuationLine = Pool().get('valuation.valuation.line')
val = ValuationLine.__table__()
context = Transaction().context
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']
return val.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
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(
CTRMValuationContextMixin, ModelView):
"CTRM Financial Position Context"
__name__ = 'ctrm.reporting.position.financial.context'
class CTRMFinancialPosition(ModelSQL, ModelView):
"CTRM Financial Position"
__name__ = 'ctrm.reporting.position.financial'
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")
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):
ValuationLine = Pool().get('valuation.valuation.line')
val = ValuationLine.__table__()
context = Transaction().context
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']
return val.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
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)
class CTRMPnlContextMixin:
date = fields.Date("Valuation Date")
product = fields.Many2One('product.product', "Product")
counterparty = fields.Many2One('party.party', "Counterparty")
currency = fields.Many2One('currency.currency', "Currency")
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_date(cls):
Date = Pool().get('ir.date')
return Date.today()
class CTRMRealizedPnlContext(CTRMPnlContextMixin, ModelView):
"CTRM Realized P&L Context"
__name__ = 'ctrm.reporting.pnl.realized.context'
realization = fields.Selection([
('purchase_and_sale_invoiced', 'Purchase and Sale Invoiced'),
('sale_invoiced', 'Sale Invoiced'),
('purchase_invoiced', 'Purchase Invoiced'),
('all_valued', 'All Valued Lines'),
], "Realization")
@classmethod
def default_realization(cls):
return 'purchase_and_sale_invoiced'
class CTRMRealizedPnl(ModelSQL, ModelView):
"CTRM Realized P&L"
__name__ = 'ctrm.reporting.pnl.realized'
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")
purchase_invoice = fields.Many2One('account.invoice', "Purchase Invoice")
sale = fields.Many2One('sale.sale', "Sale")
sale_line = fields.Many2One('sale.line', "Sale Line")
sale_invoice = fields.Many2One('account.invoice', "Sale Invoice")
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'),
('derivative', 'Derivative'),
], "Type")
reference = fields.Char("Reference")
counterparty = fields.Many2One('party.party', "Counterparty")
product = fields.Many2One('product.product', "Product")
state = fields.Char("State")
quantity = fields.Numeric("Quantity", digits=(16, 5))
unit = fields.Many2One('product.uom', "Unit")
currency = fields.Many2One('currency.currency', "Currency")
amount = fields.Numeric("Amount", digits=(16, 2))
base_amount = fields.Numeric("Base Amount", digits=(16, 2))
strategy = fields.Many2One('mtm.strategy', "Strategy")
@classmethod
def table_query(cls):
ValuationLine = Pool().get('valuation.valuation.line')
Lot = Pool().get('lot.lot')
InvoiceLine = Pool().get('account.invoice.line')
Invoice = Pool().get('account.invoice')
val = ValuationLine.__table__()
lot = Lot.__table__()
purchase_invoice_line = InvoiceLine.__table__()
purchase_invoice = Invoice.__table__()
sale_invoice_line = InvoiceLine.__table__()
sale_invoice = Invoice.__table__()
context = Transaction().context
realization = context.get(
'realization', 'purchase_and_sale_invoiced')
where = val.type.in_(ALL_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']
if realization == 'purchase_and_sale_invoiced':
where &= lot.invoice_line != Null
where &= lot.sale_invoice_line != Null
elif realization == 'sale_invoiced':
where &= lot.sale_invoice_line != Null
elif realization == 'purchase_invoiced':
where &= lot.invoice_line != Null
return (
val
.join(lot, 'LEFT', condition=lot.id == val.lot)
.join(purchase_invoice_line, 'LEFT',
condition=purchase_invoice_line.id == lot.invoice_line)
.join(purchase_invoice, 'LEFT',
condition=purchase_invoice.id == purchase_invoice_line.invoice)
.join(sale_invoice_line, 'LEFT',
condition=sale_invoice_line.id == lot.sale_invoice_line)
.join(sale_invoice, 'LEFT',
condition=sale_invoice.id == sale_invoice_line.invoice)
.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
val.id.as_('id'),
val.date.as_('valuation_date'),
val.lot.as_('lot'),
val.purchase.as_('purchase'),
val.line.as_('purchase_line'),
purchase_invoice.id.as_('purchase_invoice'),
val.sale.as_('sale'),
val.sale_line.as_('sale_line'),
sale_invoice.id.as_('sale_invoice'),
val.type.as_('type'),
val.reference.as_('reference'),
val.counterparty.as_('counterparty'),
val.product.as_('product'),
val.state.as_('state'),
val.quantity.as_('quantity'),
val.unit.as_('unit'),
val.currency.as_('currency'),
val.amount.as_('amount'),
val.base_amount.as_('base_amount'),
val.strategy.as_('strategy'),
where=where))
class CTRMMtmPnlContext(CTRMPnlContextMixin, ModelView):
"CTRM Mark-to-Market Context"
__name__ = 'ctrm.reporting.pnl.mtm.context'
class CTRMMtmPnl(ModelSQL, ModelView):
"CTRM Mark-to-Market"
__name__ = 'ctrm.reporting.pnl.mtm'
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'),
('derivative', 'Derivative'),
], "Type")
reference = fields.Char("Reference")
counterparty = fields.Many2One('party.party', "Counterparty")
product = fields.Many2One('product.product', "Product")
state = fields.Char("State")
quantity = fields.Numeric("Quantity", digits=(16, 5))
unit = fields.Many2One('product.uom', "Unit")
price = fields.Numeric("Price", digits=(16, 4))
mtm_price = fields.Numeric("MTM Price", digits=(16, 4))
currency = fields.Many2One('currency.currency', "Currency")
amount = fields.Numeric("Amount", digits=(16, 2))
mtm = fields.Numeric("MTM", digits=(16, 2))
mtm_delta = fields.Numeric("MTM Delta", digits=(16, 2))
strategy = fields.Many2One('mtm.strategy', "Strategy")
@classmethod
def table_query(cls):
ValuationLine = Pool().get('valuation.valuation.line')
val = ValuationLine.__table__()
context = Transaction().context
where = val.type.in_(ALL_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']
return val.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
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.quantity.as_('quantity'),
val.unit.as_('unit'),
val.price.as_('price'),
val.mtm_price.as_('mtm_price'),
val.currency.as_('currency'),
val.amount.as_('amount'),
val.mtm.as_('mtm'),
(Coalesce(val.mtm, 0) - Coalesce(val.amount, 0)).as_(
'mtm_delta'),
val.strategy.as_('strategy'),
where=where)
class CTRMPnlExplainContext(CTRMPnlContextMixin, ModelView):
"CTRM P&L Explain Context"
__name__ = 'ctrm.reporting.pnl.explain.context'
class CTRMPnlExplain(ModelSQL, ModelView):
"CTRM P&L Explain"
__name__ = 'ctrm.reporting.pnl.explain'
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_pnl = fields.Numeric("Physical P&L", digits=(16, 2))
fee_pnl = fields.Numeric("Fee P&L", digits=(16, 2))
derivative_pnl = fields.Numeric("Derivative P&L", digits=(16, 2))
mtm_delta = fields.Numeric("MTM Delta", digits=(16, 2))
total_pnl = fields.Numeric("Total P&L", digits=(16, 2))
@classmethod
def table_query(cls):
ValuationLine = Pool().get('valuation.valuation.line')
val = ValuationLine.__table__()
context = Transaction().context
where = val.type.in_(ALL_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_fee = val.type.in_([
'line fee',
'pur. fee',
'sale fee',
'shipment fee',
])
is_derivative = val.type.in_(DERIVATIVE_VALUATION_TYPES)
physical_pnl = Case(
(is_fee | is_derivative, 0), else_=Coalesce(val.amount, 0))
fee_pnl = Case((is_fee, Coalesce(val.amount, 0)), else_=0)
derivative_pnl = Case(
(is_derivative, Coalesce(val.amount, 0)), else_=0)
mtm_delta = Coalesce(val.mtm, 0) - Coalesce(val.amount, 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_pnl).as_('physical_pnl'),
Sum(fee_pnl).as_('fee_pnl'),
Sum(derivative_pnl).as_('derivative_pnl'),
Sum(mtm_delta).as_('mtm_delta'),
Sum(Coalesce(val.amount, 0)).as_('total_pnl'),
where=where,
group_by=group_by)
class CTRMPnlDimensionContext(CTRMPnlContextMixin, ModelView):
"CTRM P&L by Dimension Context"
__name__ = 'ctrm.reporting.pnl.dimension.context'
dimension = fields.Many2One('analytic.dimension', "Dimension")
value = fields.Many2One('analytic.dimension.value', "Value")
class CTRMPnlDimension(ModelSQL, ModelView):
"CTRM P&L by Dimension"
__name__ = 'ctrm.reporting.pnl.dimension'
valuation_date = fields.Date("Valuation Date")
dimension = fields.Many2One('analytic.dimension', "Dimension")
value = fields.Many2One('analytic.dimension.value', "Value")
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")
quantity = fields.Numeric("Quantity", digits=(16, 5))
amount = fields.Numeric("Amount", digits=(16, 2))
mtm = fields.Numeric("MTM", digits=(16, 2))
mtm_delta = fields.Numeric("MTM Delta", digits=(16, 2))
@classmethod
def table_query(cls):
ValuationLine = Pool().get('valuation.valuation.line')
Assignment = Pool().get('analytic.dimension.assignment')
val = ValuationLine.__table__()
purchase_assignment = Assignment.__table__()
sale_assignment = Assignment.__table__()
context = Transaction().context
dimension_expr = Coalesce(
sale_assignment.dimension, purchase_assignment.dimension)
value_expr = Coalesce(sale_assignment.value, purchase_assignment.value)
where = val.type.in_(ALL_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']
if context.get('dimension'):
where &= dimension_expr == context['dimension']
if context.get('value'):
where &= value_expr == context['value']
group_by = [
val.date,
dimension_expr,
value_expr,
val.product,
val.counterparty,
val.currency,
val.unit,
val.state,
val.strategy,
]
grouped = (
val
.join(purchase_assignment, 'LEFT',
condition=purchase_assignment.purchase == val.purchase)
.join(sale_assignment, 'LEFT',
condition=sale_assignment.sale == val.sale)
.select(
val.date.as_('valuation_date'),
dimension_expr.as_('dimension'),
value_expr.as_('value'),
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(Coalesce(val.quantity, 0)).as_('quantity'),
Sum(Coalesce(val.amount, 0)).as_('amount'),
Sum(Coalesce(val.mtm, 0)).as_('mtm'),
Sum(Coalesce(val.mtm, 0) - Coalesce(val.amount, 0)).as_(
'mtm_delta'),
where=where,
group_by=group_by))
row_number = RowNumber(window=Window([],
order_by=[
grouped.valuation_date.asc,
grouped.dimension.asc,
grouped.value.asc,
grouped.product.asc,
grouped.counterparty.asc,
grouped.currency.asc,
grouped.unit.asc,
grouped.state.asc,
grouped.strategy.asc,
]))
return grouped.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
row_number.as_('id'),
grouped.valuation_date.as_('valuation_date'),
grouped.dimension.as_('dimension'),
grouped.value.as_('value'),
grouped.product.as_('product'),
grouped.counterparty.as_('counterparty'),
grouped.currency.as_('currency'),
grouped.unit.as_('unit'),
grouped.state.as_('state'),
grouped.strategy.as_('strategy'),
grouped.quantity.as_('quantity'),
grouped.amount.as_('amount'),
grouped.mtm.as_('mtm'),
grouped.mtm_delta.as_('mtm_delta'))
class CTRMCreditRiskContext(ModelView):
"CTRM Credit Risk Context"
__name__ = 'ctrm.reporting.risk.credit.context'
party = fields.Many2One('party.party', "Party")
currency = fields.Many2One('currency.currency', "Credit Currency")
risk_level = fields.Selection([
(None, ''),
('low', 'Low'),
('medium', 'Medium'),
('high', 'High'),
], "Risk Level")
over_limit_only = fields.Boolean("Over Limit Only")
class CTRMCreditRisk(ModelSQL, ModelView):
"CTRM Credit Risk"
__name__ = 'ctrm.reporting.risk.credit'
party = fields.Many2One('party.party', "Party")
credit_limit = fields.Numeric("Credit Limit", digits=(16, 2))
credit_currency = fields.Many2One(
'currency.currency', "Credit Currency")
open_exposure = fields.Numeric("Open Exposure", digits=(16, 2))
internal_limit = fields.Numeric("Internal Limit", digits=(16, 2))
insurance_limit = fields.Numeric("Insurance Limit", digits=(16, 2))
available_credit = fields.Numeric("Available Credit", digits=(16, 2))
utilization = fields.Numeric("Utilization %", digits=(16, 2))
risk_level = fields.Selection([
(None, ''),
('low', 'Low'),
('medium', 'Medium'),
('high', 'High'),
], "Risk Level")
status = fields.Char("Status")
@classmethod
def table_query(cls):
Party = Pool().get('party.party')
MoveLine = Pool().get('account.move.line')
InternalLimit = Pool().get('party.internal.limit')
InsuranceLimit = Pool().get('party.insurance.limit')
party = Party.__table__()
move_line = MoveLine.__table__()
internal_limit = InternalLimit.__table__()
insurance_limit = InsuranceLimit.__table__()
exposure_query = move_line.select(
move_line.party.as_('party'),
Sum(Coalesce(move_line.debit, 0)
- Coalesce(move_line.credit, 0)).as_('open_exposure'),
where=((move_line.party != Null)
& (move_line.reconciliation == Null)),
group_by=[move_line.party])
internal_query = internal_limit.select(
internal_limit.party.as_('party'),
Sum(Coalesce(internal_limit.amount, 0)).as_('internal_limit'),
group_by=[internal_limit.party])
insurance_query = insurance_limit.select(
insurance_limit.party.as_('party'),
Sum(Coalesce(insurance_limit.amount, 0)).as_('insurance_limit'),
group_by=[insurance_limit.party])
exposure = Coalesce(exposure_query.open_exposure, 0)
limit = Coalesce(party.credit_limit, 0)
utilization = Case(
(limit != 0, (exposure / limit) * 100),
else_=0)
available_credit = limit - exposure
status = Case(
(exposure > limit, 'Over limit'),
(exposure > limit * 0.8, 'Warning'),
else_='OK')
context = Transaction().context
where = (
(party.credit_limit != Null)
| (exposure_query.open_exposure != Null)
| (internal_query.internal_limit != Null)
| (insurance_query.insurance_limit != Null))
if context.get('party'):
where &= party.id == context['party']
if context.get('currency'):
where &= party.credit_currency == context['currency']
if context.get('risk_level'):
where &= party.risk_level == context['risk_level']
if context.get('over_limit_only'):
where &= exposure > limit
return (
party
.join(exposure_query, 'LEFT',
condition=exposure_query.party == party.id)
.join(internal_query, 'LEFT',
condition=internal_query.party == party.id)
.join(insurance_query, 'LEFT',
condition=insurance_query.party == party.id)
.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
party.id.as_('id'),
party.id.as_('party'),
party.credit_limit.as_('credit_limit'),
party.credit_currency.as_('credit_currency'),
exposure.as_('open_exposure'),
Coalesce(internal_query.internal_limit, 0).as_(
'internal_limit'),
Coalesce(insurance_query.insurance_limit, 0).as_(
'insurance_limit'),
available_credit.as_('available_credit'),
utilization.as_('utilization'),
party.risk_level.as_('risk_level'),
status.as_('status'),
where=where))