04.01.26
This commit is contained in:
@@ -3,7 +3,32 @@
|
|||||||
|
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
|
|
||||||
from . import purchase,sale,global_reporting,stock,derivative,lot,pricing,workflow,lc,dashboard,fee,payment_term,purchase_prepayment,cron,party,forex,outgoing,incoming,optional,association_tables, document_tracking, open_position, credit_risk
|
from . import (
|
||||||
|
purchase,
|
||||||
|
sale,
|
||||||
|
global_reporting,
|
||||||
|
stock,
|
||||||
|
derivative,
|
||||||
|
lot,
|
||||||
|
pricing,
|
||||||
|
workflow,
|
||||||
|
lc,
|
||||||
|
dashboard,
|
||||||
|
fee,
|
||||||
|
payment_term,
|
||||||
|
purchase_prepayment,
|
||||||
|
cron,
|
||||||
|
party,
|
||||||
|
forex,
|
||||||
|
outgoing,
|
||||||
|
incoming,
|
||||||
|
optional,
|
||||||
|
association_tables,
|
||||||
|
document_tracking,
|
||||||
|
open_position,
|
||||||
|
credit_risk,
|
||||||
|
valuation,
|
||||||
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
Pool.register(
|
Pool.register(
|
||||||
@@ -69,8 +94,8 @@ def register():
|
|||||||
fee.Fee,
|
fee.Fee,
|
||||||
fee.FeeLots,
|
fee.FeeLots,
|
||||||
purchase.FeeLots,
|
purchase.FeeLots,
|
||||||
fee.Valuation,
|
valuation.Valuation,
|
||||||
fee.ValuationDyn,
|
valuation.ValuationDyn,
|
||||||
derivative.Derivative,
|
derivative.Derivative,
|
||||||
derivative.DerivativeMatch,
|
derivative.DerivativeMatch,
|
||||||
derivative.MatchWizardStart,
|
derivative.MatchWizardStart,
|
||||||
|
|||||||
@@ -21,117 +21,6 @@ from trytond.exceptions import UserWarning, UserError
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
VALTYPE = [
|
|
||||||
('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'),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Valuation(ModelSQL,ModelView):
|
|
||||||
"Valuation"
|
|
||||||
__name__ = 'valuation.valuation'
|
|
||||||
|
|
||||||
purchase = fields.Many2One('purchase.purchase',"Purchase")
|
|
||||||
line = fields.Many2One('purchase.line',"Purch. Line")
|
|
||||||
date = fields.Date("Date")
|
|
||||||
type = fields.Selection(VALTYPE, "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='unit')
|
|
||||||
currency = fields.Many2One('currency.currency',"Cur")
|
|
||||||
quantity = fields.Numeric("Quantity",digits='unit')
|
|
||||||
unit = fields.Many2One('product.uom',"Unit")
|
|
||||||
amount = fields.Numeric("Amount",digits='unit')
|
|
||||||
mtm = fields.Numeric("Mtm",digits='unit')
|
|
||||||
lot = fields.Many2One('lot.lot',"Lot")
|
|
||||||
|
|
||||||
class ValuationDyn(ModelSQL,ModelView):
|
|
||||||
"Valuation"
|
|
||||||
__name__ = 'valuation.valuation.dyn'
|
|
||||||
|
|
||||||
r_purchase = fields.Many2One('purchase.purchase',"Purchase")
|
|
||||||
r_line = fields.Many2One('purchase.line',"Line")
|
|
||||||
r_date = fields.Date("Date")
|
|
||||||
r_type = fields.Selection(VALTYPE, "Type")
|
|
||||||
r_reference = fields.Char("Reference")
|
|
||||||
r_counterparty = fields.Many2One('party.party',"Counterparty")
|
|
||||||
r_product = fields.Many2One('product.product',"Product")
|
|
||||||
r_state = fields.Char("State")
|
|
||||||
r_price = fields.Numeric("Price",digits='r_unit')
|
|
||||||
r_currency = fields.Many2One('currency.currency',"Cur")
|
|
||||||
r_quantity = fields.Numeric("Quantity",digits='r_unit')
|
|
||||||
r_unit = fields.Many2One('product.uom',"Unit")
|
|
||||||
r_amount = fields.Numeric("Amount",digits='r_unit')
|
|
||||||
r_mtm = fields.Numeric("Mtm",digits='r_unit')
|
|
||||||
r_lot = fields.Many2One('lot.lot',"Lot")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def table_query(cls):
|
|
||||||
Valuation = Pool().get('valuation.valuation')
|
|
||||||
val = Valuation.__table__()
|
|
||||||
context = Transaction().context
|
|
||||||
group_pnl = context.get('group_pnl')
|
|
||||||
wh = (val.id > 0)
|
|
||||||
# query = 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.purchase.as_('r_purchase'),
|
|
||||||
# val.line.as_('r_line'),
|
|
||||||
# val.date.as_('r_date'),
|
|
||||||
# val.type.as_('r_type'),
|
|
||||||
# val.reference.as_('r_reference'),
|
|
||||||
# val.counterparty.as_('r_counterparty'),
|
|
||||||
# val.product.as_('r_product'),
|
|
||||||
# val.state.as_('r_state'),
|
|
||||||
# val.price.as_('r_price'),
|
|
||||||
# val.currency.as_('r_currency'),
|
|
||||||
# val.quantity.as_('r_quantity'),
|
|
||||||
# val.unit.as_('r_unit'),
|
|
||||||
# val.amount.as_('r_amount'),
|
|
||||||
# val.mtm.as_('r_mtm'),
|
|
||||||
# val.lot.as_('r_lot'),
|
|
||||||
# where=wh)
|
|
||||||
|
|
||||||
#if group_pnl==True:
|
|
||||||
query = val.select(
|
|
||||||
Literal(0).as_('create_uid'),
|
|
||||||
CurrentTimestamp().as_('create_date'),
|
|
||||||
Literal(None).as_('write_uid'),
|
|
||||||
Literal(None).as_('write_date'),
|
|
||||||
Max(val.id).as_('id'),
|
|
||||||
Max(val.purchase).as_('r_purchase'),
|
|
||||||
Max(val.line).as_('r_line'),
|
|
||||||
Max(val.date).as_('r_date'),
|
|
||||||
val.type.as_('r_type'),
|
|
||||||
Max(val.reference).as_('r_reference'),
|
|
||||||
val.counterparty.as_('r_counterparty'),
|
|
||||||
Max(val.product).as_('r_product'),
|
|
||||||
val.state.as_('r_state'),
|
|
||||||
Avg(val.price).as_('r_price'),
|
|
||||||
Max(val.currency).as_('r_currency'),
|
|
||||||
Sum(val.quantity).as_('r_quantity'),
|
|
||||||
Max(val.unit).as_('r_unit'),
|
|
||||||
Sum(val.amount).as_('r_amount'),
|
|
||||||
Sum(val.mtm).as_('r_mtm'),
|
|
||||||
Max(val.lot).as_('r_lot'),
|
|
||||||
where=wh,
|
|
||||||
group_by=[val.type,val.counterparty,val.state])
|
|
||||||
|
|
||||||
return query
|
|
||||||
|
|
||||||
def filter_state(state):
|
def filter_state(state):
|
||||||
def filter(func):
|
def filter(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
|
|||||||
@@ -19,26 +19,6 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="name">fee_tree_sequence</field>
|
<field name="name">fee_tree_sequence</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="valuation_view_tree_sequence3">
|
|
||||||
<field name="model">valuation.valuation</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="name">valuation_tree_sequence3</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="valuation_view_graph">
|
|
||||||
<field name="model">valuation.valuation</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="name">valuation_graph</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="valuation_view_graph2">
|
|
||||||
<field name="model">valuation.valuation</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="name">valuation_graph2</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="valuation_view_tree_sequence4">
|
|
||||||
<field name="model">valuation.valuation.dyn</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="name">valuation_tree_sequence4</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="fee_view_tree_sequence2">
|
<record model="ir.ui.view" id="fee_view_tree_sequence2">
|
||||||
<field name="model">fee.fee</field>
|
<field name="model">fee.fee</field>
|
||||||
<field name="type">tree</field>
|
<field name="type">tree</field>
|
||||||
|
|||||||
@@ -380,19 +380,11 @@ class Purchase(metaclass=PoolMeta):
|
|||||||
Decimal(str(line.quantity))
|
Decimal(str(line.quantity))
|
||||||
.quantize(Decimal("0.00001"))
|
.quantize(Decimal("0.00001"))
|
||||||
)
|
)
|
||||||
|
|
||||||
Line.save([line])
|
Line.save([line])
|
||||||
|
|
||||||
#compute pnl
|
#compute pnl
|
||||||
Pnl = Pool().get('valuation.valuation')
|
Pnl = Pool().get('valuation.valuation')
|
||||||
pnl = Pnl.search([('line','=',line.id)])
|
Pnl.generate(line)
|
||||||
if pnl:
|
|
||||||
Pnl.delete(pnl)
|
|
||||||
pnl_lines = []
|
|
||||||
pnl_lines.extend(line.get_pnl_fee_lines())
|
|
||||||
pnl_lines.extend(line.get_pnl_price_lines())
|
|
||||||
pnl_lines.extend(line.get_pnl_der_lines())
|
|
||||||
Pnl.save(pnl_lines)
|
|
||||||
|
|
||||||
if line.quantity_theorical:
|
if line.quantity_theorical:
|
||||||
OpenPosition = Pool().get('open.position')
|
OpenPosition = Pool().get('open.position')
|
||||||
|
|||||||
@@ -29,3 +29,4 @@ xml:
|
|||||||
forex.xml
|
forex.xml
|
||||||
global_reporting.xml
|
global_reporting.xml
|
||||||
derivative.xml
|
derivative.xml
|
||||||
|
valuation.xml
|
||||||
383
modules/purchase_trade/valuation.py
Normal file
383
modules/purchase_trade/valuation.py
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
from trytond.model import fields
|
||||||
|
from trytond.report import Report
|
||||||
|
from trytond.pool import Pool, PoolMeta
|
||||||
|
from trytond.pyson import Bool, Eval, Id, If
|
||||||
|
from trytond.model import (ModelSQL, ModelView)
|
||||||
|
from trytond.tools import is_full_text, lstrip_wildcard
|
||||||
|
from trytond.transaction import Transaction, inactive_records
|
||||||
|
from decimal import getcontext, Decimal, ROUND_HALF_UP
|
||||||
|
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
|
||||||
|
from sql.conditionals import Case
|
||||||
|
from sql import Column, Literal
|
||||||
|
from sql.functions import CurrentTimestamp, DateTrunc
|
||||||
|
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||||
|
from itertools import chain, groupby
|
||||||
|
from operator import itemgetter
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
|
from trytond.exceptions import UserWarning, UserError
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
VALTYPE = [
|
||||||
|
('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'),
|
||||||
|
]
|
||||||
|
|
||||||
|
class Valuation(ModelSQL,ModelView):
|
||||||
|
"Valuation"
|
||||||
|
__name__ = 'valuation.valuation'
|
||||||
|
|
||||||
|
purchase = fields.Many2One('purchase.purchase',"Purchase")
|
||||||
|
line = fields.Many2One('purchase.line',"Purch. Line")
|
||||||
|
date = fields.Date("Date")
|
||||||
|
type = fields.Selection(VALTYPE, "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='unit')
|
||||||
|
currency = fields.Many2One('currency.currency',"Cur")
|
||||||
|
quantity = fields.Numeric("Quantity",digits='unit')
|
||||||
|
unit = fields.Many2One('product.uom',"Unit")
|
||||||
|
amount = fields.Numeric("Amount",digits='unit')
|
||||||
|
mtm = fields.Numeric("Mtm",digits='unit')
|
||||||
|
lot = fields.Many2One('lot.lot',"Lot")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _base_pnl(cls, *, line, lot, pnl_type, sale=None):
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
pnl = Pool().get('valuation.valuation')()
|
||||||
|
|
||||||
|
pnl.purchase = line.purchase.id
|
||||||
|
pnl.line = line.id
|
||||||
|
pnl.type = pnl_type
|
||||||
|
pnl.date = Date.today()
|
||||||
|
pnl.lot = lot.id
|
||||||
|
|
||||||
|
if sale:
|
||||||
|
pnl.sale = sale.id
|
||||||
|
|
||||||
|
return pnl
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_basis_pnl(cls, *, line, lot, sale_line, pc, sign):
|
||||||
|
pnl = cls._base_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=lot,
|
||||||
|
sale=sale_line.sale if sale_line else None,
|
||||||
|
pnl_type='sale priced' if sale_line else 'pur. priced'
|
||||||
|
)
|
||||||
|
|
||||||
|
qty = lot.get_current_quantity_converted()
|
||||||
|
|
||||||
|
pnl.reference = f"{pc.get_name()} / {pc.ratio}%"
|
||||||
|
pnl.price = round(pc.price, 4)
|
||||||
|
pnl.counterparty = sale_line.sale.party if sale_line else line.purchase.party
|
||||||
|
pnl.product = sale_line.product if sale_line else line.product
|
||||||
|
|
||||||
|
# State
|
||||||
|
if pc.unfixed_qt == 0:
|
||||||
|
pnl.state = 'fixed'
|
||||||
|
elif pc.fixed_qt == 0:
|
||||||
|
pnl.state = 'unfixed'
|
||||||
|
else:
|
||||||
|
base = sale_line.quantity_theorical if sale_line else line.quantity_theorical
|
||||||
|
pnl.state = f"part. fixed {round(pc.fixed_qt / Decimal(base) * 100, 0)}%"
|
||||||
|
|
||||||
|
if pc.price and pc.ratio:
|
||||||
|
pnl.quantity = round(qty, 5)
|
||||||
|
pnl.amount = round(pc.price * qty * Decimal(sign) * pc.ratio / 100, 4)
|
||||||
|
|
||||||
|
mtm = Decimal(0)
|
||||||
|
last_price = pc.get_last_price()
|
||||||
|
if last_price:
|
||||||
|
mtm = round(Decimal(last_price) * qty * Decimal(sign), 4)
|
||||||
|
|
||||||
|
pnl.mtm = round(pnl.amount - (mtm * pc.ratio / 100), 4)
|
||||||
|
pnl.unit = sale_line.unit if sale_line else line.unit
|
||||||
|
pnl.currency = sale_line.sale.currency if sale_line else line.purchase.currency
|
||||||
|
|
||||||
|
return pnl
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_simple_pnl(cls, *, line, lot, sale_line, price, state, sign, pnl_type):
|
||||||
|
pnl = cls._base_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=lot,
|
||||||
|
sale=sale_line.sale if sale_line else None,
|
||||||
|
pnl_type=pnl_type
|
||||||
|
)
|
||||||
|
|
||||||
|
qty = lot.get_current_quantity_converted()
|
||||||
|
pnl.price = round(price, 4)
|
||||||
|
pnl.quantity = round(qty, 5)
|
||||||
|
pnl.amount = round(pnl.price * qty * Decimal(sign), 4)
|
||||||
|
pnl.mtm = Decimal(0)
|
||||||
|
pnl.state = state
|
||||||
|
pnl.unit = sale_line.unit if sale_line else line.unit
|
||||||
|
pnl.currency = sale_line.sale.currency if sale_line else line.purchase.currency
|
||||||
|
pnl.counterparty = sale_line.sale.party if sale_line else line.purchase.party
|
||||||
|
pnl.product = sale_line.product if sale_line else line.product
|
||||||
|
|
||||||
|
pnl.reference = 'Sale/Physic' if lot.lot_type == 'physic' else 'Sale/Open' if sale_line else 'Purchase/Physic'
|
||||||
|
|
||||||
|
return pnl
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_pnl_price_from_line(cls, line):
|
||||||
|
price_lines = []
|
||||||
|
LotQt = Pool().get('lot.qt')
|
||||||
|
|
||||||
|
for lot in line.lots:
|
||||||
|
|
||||||
|
# --- PURCHASE SIDE ---
|
||||||
|
if line.price_type == 'basis':
|
||||||
|
for pc in line.price_summary or []:
|
||||||
|
pnl = cls._build_basis_pnl(line=line, lot=lot, sale_line=None, pc=pc, sign=-1)
|
||||||
|
if pnl:
|
||||||
|
price_lines.append(pnl)
|
||||||
|
|
||||||
|
elif line.price_type in ('priced', 'efp') and lot.lot_price:
|
||||||
|
state = 'fixed' if line.price_type == 'priced' else 'not fixed'
|
||||||
|
price_lines.append(
|
||||||
|
cls._build_simple_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=lot,
|
||||||
|
sale_line=None,
|
||||||
|
price=lot.lot_price,
|
||||||
|
state=state,
|
||||||
|
sign=-1,
|
||||||
|
pnl_type=f'pur. {line.price_type}'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- SALE SIDE ---
|
||||||
|
sale_lots = [lot] if lot.sale_line else [
|
||||||
|
lqt.lot_s for lqt in LotQt.search([('lot_p','=',lot.id),('lot_s','>',0),('lot_quantity','>',0)])
|
||||||
|
]
|
||||||
|
|
||||||
|
for sl in sale_lots:
|
||||||
|
sl_line = sl.sale_line
|
||||||
|
if not sl_line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if sl_line.price_type == 'basis':
|
||||||
|
for pc in sl_line.price_summary or []:
|
||||||
|
pnl = cls._build_basis_pnl(line=line, lot=sl, sale_line=sl_line, pc=pc, sign=+1)
|
||||||
|
if pnl:
|
||||||
|
price_lines.append(pnl)
|
||||||
|
|
||||||
|
elif sl_line.price_type in ('priced', 'efp'):
|
||||||
|
state = 'fixed' if sl_line.price_type == 'priced' else 'not fixed'
|
||||||
|
price = sl.lot_price_sale
|
||||||
|
price_lines.append(
|
||||||
|
cls._build_simple_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=sl,
|
||||||
|
sale_line=sl_line,
|
||||||
|
price=price,
|
||||||
|
state=state,
|
||||||
|
sign=+1,
|
||||||
|
pnl_type=f'sale {sl_line.price_type}'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return price_lines
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def group_fees_by_type_supplier(cls,line,fees):
|
||||||
|
grouped = defaultdict(list)
|
||||||
|
|
||||||
|
# Regrouper par (type, supplier)
|
||||||
|
for fee in fees:
|
||||||
|
key = (fee.product, fee.supplier)
|
||||||
|
grouped[key].append(fee)
|
||||||
|
result = []
|
||||||
|
for key, fee_list in grouped.items():
|
||||||
|
ordered_fees = [f for f in fee_list if f.type == 'ordered']
|
||||||
|
if ordered_fees:
|
||||||
|
result.extend(ordered_fees)
|
||||||
|
else:
|
||||||
|
budgeted_fees = [f for f in fee_list if f.type == 'budgeted']
|
||||||
|
result.extend(budgeted_fees)
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_pnl_fee_from_line(cls,line):
|
||||||
|
fee_lines = []
|
||||||
|
#pnl management
|
||||||
|
Pnl = Pool().get('valuation.valuation')
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
Currency = Pool().get('currency.currency')
|
||||||
|
FeeLots = Pool().get('fee.lots')
|
||||||
|
if line.lots:
|
||||||
|
for lot in line.lots:
|
||||||
|
fl = FeeLots.search(['lot','=',lot.id])
|
||||||
|
if fl:
|
||||||
|
fees = [e.fee for e in fl]
|
||||||
|
sorted_fees = cls.group_fees_by_type_supplier(line,fees)
|
||||||
|
if sorted_fees:
|
||||||
|
for sf in sorted_fees:
|
||||||
|
pnl = Pnl()
|
||||||
|
pnl.lot = lot.id
|
||||||
|
if lot.sale_line:
|
||||||
|
pnl.sale = lot.sale_line.sale.id
|
||||||
|
pnl.purchase = line.purchase.id
|
||||||
|
pnl.line = line.id
|
||||||
|
if sf.line:
|
||||||
|
pnl.type = 'pur. fee'
|
||||||
|
if sf.sale_line:
|
||||||
|
pnl.type = 'sale fee'
|
||||||
|
if sf.shipment_in:
|
||||||
|
pnl.type = 'shipment fee'
|
||||||
|
pnl.date = Date.today()
|
||||||
|
pnl.price = Decimal(sf.get_price_per_qt())
|
||||||
|
if sf.currency != line.purchase.currency:
|
||||||
|
with Transaction().set_context(date=Date.today()):
|
||||||
|
pnl.price = Currency.compute(sf.currency,pnl.price, line.purchase.currency)
|
||||||
|
pnl.counterparty = sf.supplier
|
||||||
|
str_op = ''
|
||||||
|
if lot.lot_type == 'physic':
|
||||||
|
str_op = '/Physic'
|
||||||
|
else:
|
||||||
|
str_op = '/Open'
|
||||||
|
pnl.reference = sf.product.name + str_op
|
||||||
|
pnl.product = sf.product
|
||||||
|
pnl.state = sf.type
|
||||||
|
if sf.p_r == 'pay':
|
||||||
|
sign = -1
|
||||||
|
pnl.amount = round(pnl.price * lot.get_current_quantity_converted() * sign,2)
|
||||||
|
pnl.mtm = 0
|
||||||
|
pnl.quantity = round(lot.get_current_quantity_converted(),5)
|
||||||
|
pnl.unit = sf.unit if sf.unit else line.unit
|
||||||
|
pnl.currency = sf.currency
|
||||||
|
fee_lines.append(pnl)
|
||||||
|
|
||||||
|
return fee_lines
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_pnl_der_from_line(cls,line):
|
||||||
|
der_lines = []
|
||||||
|
if line.derivatives:
|
||||||
|
Pnl = Pool().get('valuation.valuation')
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
for d in line.derivatives:
|
||||||
|
pnl = Pnl()
|
||||||
|
pnl.purchase = line.purchase.id
|
||||||
|
pnl.line = line.id
|
||||||
|
pnl.type = 'derivative'
|
||||||
|
pnl.date = Date.today()
|
||||||
|
pnl.reference = d.price_index.price_index
|
||||||
|
pnl.price = round(Decimal(d.price_index.get_price_per_qt(d.price,line.unit,line.purchase.currency)),4)
|
||||||
|
pnl.counterparty = d.party
|
||||||
|
pnl.product = d.product
|
||||||
|
pnl.state = 'fixed'
|
||||||
|
pnl.amount = round(pnl.price * (d.quantity) * Decimal(-1),4)
|
||||||
|
mtm = round(Decimal(d.price_index.get_price(Date.today(),line.unit,line.purchase.currency,True)) * d.quantity * Decimal(-1),4)
|
||||||
|
pnl.mtm = pnl.amount - mtm
|
||||||
|
pnl.quantity = round(d.quantity,5)
|
||||||
|
pnl.unit = line.unit
|
||||||
|
pnl.currency = line.purchase.currency
|
||||||
|
der_lines.append(pnl)
|
||||||
|
return der_lines
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate(cls, line):
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
Pnl = Pool().get('valuation.valuation')
|
||||||
|
pnl = Pnl.search([('line','=',line.id),('date','=',Date.today())])
|
||||||
|
if pnl:
|
||||||
|
Pnl.delete(pnl)
|
||||||
|
pnl_lines = []
|
||||||
|
pnl_lines.extend(cls.create_pnl_fee_from_line(line))
|
||||||
|
pnl_lines.extend(cls.create_pnl_price_from_line(line))
|
||||||
|
pnl_lines.extend(cls.create_pnl_der_from_line(line))
|
||||||
|
Pnl.save(pnl_lines)
|
||||||
|
|
||||||
|
class ValuationDyn(ModelSQL,ModelView):
|
||||||
|
"Valuation"
|
||||||
|
__name__ = 'valuation.valuation.dyn'
|
||||||
|
|
||||||
|
r_purchase = fields.Many2One('purchase.purchase',"Purchase")
|
||||||
|
r_line = fields.Many2One('purchase.line',"Line")
|
||||||
|
r_date = fields.Date("Date")
|
||||||
|
r_type = fields.Selection(VALTYPE, "Type")
|
||||||
|
r_reference = fields.Char("Reference")
|
||||||
|
r_counterparty = fields.Many2One('party.party',"Counterparty")
|
||||||
|
r_product = fields.Many2One('product.product',"Product")
|
||||||
|
r_state = fields.Char("State")
|
||||||
|
r_price = fields.Numeric("Price",digits='r_unit')
|
||||||
|
r_currency = fields.Many2One('currency.currency',"Cur")
|
||||||
|
r_quantity = fields.Numeric("Quantity",digits='r_unit')
|
||||||
|
r_unit = fields.Many2One('product.uom',"Unit")
|
||||||
|
r_amount = fields.Numeric("Amount",digits='r_unit')
|
||||||
|
r_mtm = fields.Numeric("Mtm",digits='r_unit')
|
||||||
|
r_lot = fields.Many2One('lot.lot',"Lot")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def table_query(cls):
|
||||||
|
Valuation = Pool().get('valuation.valuation')
|
||||||
|
val = Valuation.__table__()
|
||||||
|
context = Transaction().context
|
||||||
|
group_pnl = context.get('group_pnl')
|
||||||
|
wh = (val.id > 0)
|
||||||
|
# query = 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.purchase.as_('r_purchase'),
|
||||||
|
# val.line.as_('r_line'),
|
||||||
|
# val.date.as_('r_date'),
|
||||||
|
# val.type.as_('r_type'),
|
||||||
|
# val.reference.as_('r_reference'),
|
||||||
|
# val.counterparty.as_('r_counterparty'),
|
||||||
|
# val.product.as_('r_product'),
|
||||||
|
# val.state.as_('r_state'),
|
||||||
|
# val.price.as_('r_price'),
|
||||||
|
# val.currency.as_('r_currency'),
|
||||||
|
# val.quantity.as_('r_quantity'),
|
||||||
|
# val.unit.as_('r_unit'),
|
||||||
|
# val.amount.as_('r_amount'),
|
||||||
|
# val.mtm.as_('r_mtm'),
|
||||||
|
# val.lot.as_('r_lot'),
|
||||||
|
# where=wh)
|
||||||
|
|
||||||
|
#if group_pnl==True:
|
||||||
|
query = val.select(
|
||||||
|
Literal(0).as_('create_uid'),
|
||||||
|
CurrentTimestamp().as_('create_date'),
|
||||||
|
Literal(None).as_('write_uid'),
|
||||||
|
Literal(None).as_('write_date'),
|
||||||
|
Max(val.id).as_('id'),
|
||||||
|
Max(val.purchase).as_('r_purchase'),
|
||||||
|
Max(val.line).as_('r_line'),
|
||||||
|
Max(val.date).as_('r_date'),
|
||||||
|
val.type.as_('r_type'),
|
||||||
|
Max(val.reference).as_('r_reference'),
|
||||||
|
val.counterparty.as_('r_counterparty'),
|
||||||
|
Max(val.product).as_('r_product'),
|
||||||
|
val.state.as_('r_state'),
|
||||||
|
Avg(val.price).as_('r_price'),
|
||||||
|
Max(val.currency).as_('r_currency'),
|
||||||
|
Sum(val.quantity).as_('r_quantity'),
|
||||||
|
Max(val.unit).as_('r_unit'),
|
||||||
|
Sum(val.amount).as_('r_amount'),
|
||||||
|
Sum(val.mtm).as_('r_mtm'),
|
||||||
|
Max(val.lot).as_('r_lot'),
|
||||||
|
where=wh,
|
||||||
|
group_by=[val.type,val.counterparty,val.state])
|
||||||
|
|
||||||
|
return query
|
||||||
24
modules/purchase_trade/valuation.xml
Normal file
24
modules/purchase_trade/valuation.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<tryton>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_tree_sequence3">
|
||||||
|
<field name="model">valuation.valuation</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="name">valuation_tree_sequence3</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_graph">
|
||||||
|
<field name="model">valuation.valuation</field>
|
||||||
|
<field name="type">graph</field>
|
||||||
|
<field name="name">valuation_graph</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_graph2">
|
||||||
|
<field name="model">valuation.valuation</field>
|
||||||
|
<field name="type">graph</field>
|
||||||
|
<field name="name">valuation_graph2</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_tree_sequence4">
|
||||||
|
<field name="model">valuation.valuation.dyn</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="name">valuation_tree_sequence4</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</tryton>
|
||||||
Reference in New Issue
Block a user