Risk Management - Physical Open Position
This commit is contained in:
@@ -318,6 +318,283 @@ class CTRMNetPosition(ModelSQL, ModelView):
|
||||
group_by=group_by)
|
||||
|
||||
|
||||
class CTRMOpenPositionContext(ModelView):
|
||||
"CTRM Open Position Context"
|
||||
__name__ = 'ctrm.reporting.risk.open_position.context'
|
||||
|
||||
valuation_date = fields.Date("Valuation Date")
|
||||
commodity = fields.Many2One('product.category', "Commodity")
|
||||
product = fields.Many2One('product.product', "Product")
|
||||
pricing_status = fields.Selection([
|
||||
(None, ''),
|
||||
('fixed', 'Fixed'),
|
||||
('unfixed', 'Unfixed'),
|
||||
('partly', 'Partly fix'),
|
||||
], "Pricing Status")
|
||||
shipment_period = fields.Many2One('product.month', "Shipment Period")
|
||||
strategy = fields.Many2One('mtm.strategy', "Strategy")
|
||||
counterparty = fields.Many2One('party.party', "Counterparty")
|
||||
direction = fields.Selection([
|
||||
(None, ''),
|
||||
('long', 'LONG'),
|
||||
('short', 'SHORT'),
|
||||
('flat', 'Flat'),
|
||||
], "Direction")
|
||||
|
||||
@classmethod
|
||||
def default_valuation_date(cls):
|
||||
return Pool().get('ir.date').today()
|
||||
|
||||
|
||||
class CTRMOpenPosition(ModelSQL, ModelView):
|
||||
"CTRM Open Position"
|
||||
__name__ = 'ctrm.reporting.risk.open_position'
|
||||
|
||||
product = fields.Many2One(
|
||||
'product.product', "Product", readonly=True)
|
||||
commodity = fields.Many2One(
|
||||
'product.category', "Commodity", readonly=True)
|
||||
pricing = fields.Selection([
|
||||
('fixed', 'Fixed'),
|
||||
('unfixed', 'Unfixed'),
|
||||
('partly', 'Partly fix'),
|
||||
], "Pricing", readonly=True)
|
||||
shipment_period = fields.Many2One(
|
||||
'product.month', "Shipment Pd", readonly=True)
|
||||
strategy = fields.Many2One(
|
||||
'mtm.strategy', "Strategy", readonly=True)
|
||||
bought = fields.Numeric("Bought", digits=(16, 3), readonly=True)
|
||||
sold = fields.Numeric("Sold", digits=(16, 3), readonly=True)
|
||||
net = fields.Numeric("Net", digits=(16, 3), readonly=True)
|
||||
direction = fields.Selection([
|
||||
('long', 'LONG'),
|
||||
('short', 'SHORT'),
|
||||
('flat', 'Flat'),
|
||||
], "Direction", readonly=True)
|
||||
unit = fields.Many2One('product.uom', "Unit", readonly=True)
|
||||
open_contracts = fields.Integer("Open Contracts", readonly=True)
|
||||
unfixed_qty = fields.Numeric(
|
||||
"Unfixed Qty", digits=(16, 3), readonly=True)
|
||||
|
||||
@classmethod
|
||||
def _physical_quantity_query(cls, lot, line_field):
|
||||
return lot.select(
|
||||
getattr(lot, line_field).as_('line'),
|
||||
Sum(Coalesce(lot.lot_quantity, 0)).as_('quantity'),
|
||||
where=((lot.lot_type == 'physic')
|
||||
& (getattr(lot, line_field) != Null)),
|
||||
group_by=[getattr(lot, line_field)])
|
||||
|
||||
@classmethod
|
||||
def table_query(cls):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
SaleLine = pool.get('sale.line')
|
||||
Sale = pool.get('sale.sale')
|
||||
Lot = pool.get('lot.lot')
|
||||
Product = pool.get('product.product')
|
||||
Template = pool.get('product.template')
|
||||
TemplateCategory = pool.get('product.template-product.category')
|
||||
PurchaseStrategy = pool.get('purchase.strategy')
|
||||
SaleStrategy = pool.get('sale.strategy')
|
||||
|
||||
purchase_line = PurchaseLine.__table__()
|
||||
purchase = Purchase.__table__()
|
||||
sale_line = SaleLine.__table__()
|
||||
sale = Sale.__table__()
|
||||
lot = Lot.__table__()
|
||||
product = Product.__table__()
|
||||
template = Template.__table__()
|
||||
template_category = TemplateCategory.__table__()
|
||||
purchase_strategy = PurchaseStrategy.__table__()
|
||||
sale_strategy = SaleStrategy.__table__()
|
||||
|
||||
context = Transaction().context
|
||||
|
||||
purchase_physical = cls._physical_quantity_query(lot, 'line')
|
||||
purchase_contract_qty = Coalesce(
|
||||
purchase_line.quantity_theorical, purchase_line.quantity, 0)
|
||||
purchase_physical_qty = Coalesce(Max(purchase_physical.quantity), 0)
|
||||
purchase_residual = purchase_contract_qty - purchase_physical_qty
|
||||
purchase_open_qty = Case(
|
||||
(purchase_line.finished == True, 0),
|
||||
(purchase_residual > 0, purchase_residual),
|
||||
else_=0)
|
||||
purchase_fixed = purchase_line.price_type.in_(['cash', 'priced'])
|
||||
purchase_where = (
|
||||
(purchase_line.product != Null)
|
||||
& purchase.state.in_(['confirmed', 'processing'])
|
||||
& (template.type != 'service'))
|
||||
if context.get('product'):
|
||||
purchase_where &= purchase_line.product == context['product']
|
||||
if context.get('commodity'):
|
||||
purchase_where &= (
|
||||
template_category.category == context['commodity'])
|
||||
if context.get('shipment_period'):
|
||||
purchase_where &= (
|
||||
purchase_line.del_period == context['shipment_period'])
|
||||
if context.get('strategy'):
|
||||
purchase_where &= (
|
||||
purchase_strategy.strategy == context['strategy'])
|
||||
if context.get('counterparty'):
|
||||
purchase_where &= purchase.party == context['counterparty']
|
||||
|
||||
purchase_query = (
|
||||
purchase_line
|
||||
.join(purchase, condition=purchase_line.purchase == purchase.id)
|
||||
.join(purchase_physical, 'LEFT',
|
||||
condition=purchase_physical.line == purchase_line.id)
|
||||
.join(product, condition=purchase_line.product == product.id)
|
||||
.join(template, condition=product.template == template.id)
|
||||
.join(template_category, 'LEFT',
|
||||
condition=template_category.template == template.id)
|
||||
.join(purchase_strategy, 'LEFT',
|
||||
condition=purchase_strategy.line == purchase_line.id)
|
||||
.select(
|
||||
Literal('purchase').as_('side'),
|
||||
purchase_line.id.as_('line_id'),
|
||||
purchase_line.product.as_('product'),
|
||||
Max(template_category.category).as_('commodity'),
|
||||
purchase_line.del_period.as_('shipment_period'),
|
||||
Max(purchase_strategy.strategy).as_('strategy'),
|
||||
purchase.party.as_('counterparty'),
|
||||
purchase_line.unit.as_('unit'),
|
||||
purchase_open_qty.as_('bought'),
|
||||
Literal(0).as_('sold'),
|
||||
Case((purchase_fixed, purchase_open_qty), else_=0).as_(
|
||||
'fixed_qty'),
|
||||
Case((purchase_fixed, 0), else_=purchase_open_qty).as_(
|
||||
'unfixed_qty'),
|
||||
where=purchase_where,
|
||||
group_by=[
|
||||
purchase_line.id,
|
||||
purchase_line.product,
|
||||
purchase_line.del_period,
|
||||
purchase.party,
|
||||
purchase_line.unit,
|
||||
purchase_line.finished,
|
||||
purchase_line.quantity_theorical,
|
||||
purchase_line.quantity,
|
||||
purchase_line.price_type,
|
||||
],
|
||||
having=purchase_open_qty > 0))
|
||||
|
||||
sale_physical = cls._physical_quantity_query(lot, 'sale_line')
|
||||
sale_contract_qty = Coalesce(
|
||||
sale_line.quantity_theorical, sale_line.quantity, 0)
|
||||
sale_physical_qty = Coalesce(Max(sale_physical.quantity), 0)
|
||||
sale_residual = sale_contract_qty - sale_physical_qty
|
||||
sale_open_qty = Case(
|
||||
(sale_line.finished == True, 0),
|
||||
(sale_residual > 0, sale_residual),
|
||||
else_=0)
|
||||
sale_fixed = sale_line.price_type.in_(['cash', 'priced'])
|
||||
sale_where = (
|
||||
(sale_line.product != Null)
|
||||
& sale.state.in_(['confirmed', 'processing'])
|
||||
& (template.type != 'service'))
|
||||
if context.get('product'):
|
||||
sale_where &= sale_line.product == context['product']
|
||||
if context.get('commodity'):
|
||||
sale_where &= template_category.category == context['commodity']
|
||||
if context.get('shipment_period'):
|
||||
sale_where &= sale_line.del_period == context['shipment_period']
|
||||
if context.get('strategy'):
|
||||
sale_where &= sale_strategy.strategy == context['strategy']
|
||||
if context.get('counterparty'):
|
||||
sale_where &= sale.party == context['counterparty']
|
||||
|
||||
sale_query = (
|
||||
sale_line
|
||||
.join(sale, condition=sale_line.sale == sale.id)
|
||||
.join(sale_physical, 'LEFT',
|
||||
condition=sale_physical.line == sale_line.id)
|
||||
.join(product, condition=sale_line.product == product.id)
|
||||
.join(template, condition=product.template == template.id)
|
||||
.join(template_category, 'LEFT',
|
||||
condition=template_category.template == template.id)
|
||||
.join(sale_strategy, 'LEFT',
|
||||
condition=sale_strategy.sale_line == sale_line.id)
|
||||
.select(
|
||||
Literal('sale').as_('side'),
|
||||
sale_line.id.as_('line_id'),
|
||||
sale_line.product.as_('product'),
|
||||
Max(template_category.category).as_('commodity'),
|
||||
sale_line.del_period.as_('shipment_period'),
|
||||
Max(sale_strategy.strategy).as_('strategy'),
|
||||
sale.party.as_('counterparty'),
|
||||
sale_line.unit.as_('unit'),
|
||||
Literal(0).as_('bought'),
|
||||
sale_open_qty.as_('sold'),
|
||||
Case((sale_fixed, sale_open_qty), else_=0).as_(
|
||||
'fixed_qty'),
|
||||
Case((sale_fixed, 0), else_=sale_open_qty).as_(
|
||||
'unfixed_qty'),
|
||||
where=sale_where,
|
||||
group_by=[
|
||||
sale_line.id,
|
||||
sale_line.product,
|
||||
sale_line.del_period,
|
||||
sale.party,
|
||||
sale_line.unit,
|
||||
sale_line.finished,
|
||||
sale_line.quantity_theorical,
|
||||
sale_line.quantity,
|
||||
sale_line.price_type,
|
||||
],
|
||||
having=sale_open_qty > 0))
|
||||
|
||||
lines = Union(purchase_query, sale_query, all_=True)
|
||||
bought = Sum(Coalesce(lines.bought, 0))
|
||||
sold = Sum(Coalesce(lines.sold, 0))
|
||||
net = bought - sold
|
||||
fixed_qty = Sum(Coalesce(lines.fixed_qty, 0))
|
||||
unfixed_qty = Sum(Coalesce(lines.unfixed_qty, 0))
|
||||
pricing = Case(
|
||||
((fixed_qty > 0) & (unfixed_qty > 0), 'partly'),
|
||||
(unfixed_qty > 0, 'unfixed'),
|
||||
else_='fixed')
|
||||
direction = Case(
|
||||
(net > 0, 'long'),
|
||||
(net < 0, 'short'),
|
||||
else_='flat')
|
||||
|
||||
having = (bought + sold) > 0
|
||||
if context.get('pricing_status'):
|
||||
having &= pricing == context['pricing_status']
|
||||
if context.get('direction') == 'long':
|
||||
having &= net > 0
|
||||
elif context.get('direction') == 'short':
|
||||
having &= net < 0
|
||||
elif context.get('direction') == 'flat':
|
||||
having &= net == 0
|
||||
|
||||
report_id = RowNumber(window=Window([],
|
||||
order_by=[lines.product, lines.unit]))
|
||||
|
||||
return lines.select(
|
||||
Literal(0).as_('create_uid'),
|
||||
CurrentTimestamp().as_('create_date'),
|
||||
Literal(None).as_('write_uid'),
|
||||
Literal(None).as_('write_date'),
|
||||
report_id.as_('id'),
|
||||
lines.product.as_('product'),
|
||||
Max(lines.commodity).as_('commodity'),
|
||||
pricing.as_('pricing'),
|
||||
Max(lines.shipment_period).as_('shipment_period'),
|
||||
Max(lines.strategy).as_('strategy'),
|
||||
bought.as_('bought'),
|
||||
sold.as_('sold'),
|
||||
net.as_('net'),
|
||||
direction.as_('direction'),
|
||||
lines.unit.as_('unit'),
|
||||
Count(lines.line_id).as_('open_contracts'),
|
||||
unfixed_qty.as_('unfixed_qty'),
|
||||
group_by=[lines.product, lines.unit],
|
||||
having=having)
|
||||
|
||||
|
||||
class CTRMPnlContextMixin:
|
||||
date = fields.Date("Valuation Date")
|
||||
product = fields.Many2One('product.product', "Product")
|
||||
|
||||
Reference in New Issue
Block a user