1145 lines
46 KiB
Python
Executable File
1145 lines
46 KiB
Python
Executable File
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
from functools import wraps
|
|
from trytond.model import ModelSingleton, ModelSQL, ModelView, fields
|
|
from trytond.i18n import gettext
|
|
from trytond.report import Report
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.pyson import Bool, Eval, Id, If, PYSONEncoder
|
|
from trytond.model import (ModelSQL, ModelView)
|
|
from trytond.tools import (cursor_dict, 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, Abs
|
|
from trytond.wizard import Button, StateTransition, StateView, Wizard, StateAction
|
|
from itertools import chain, groupby
|
|
from operator import itemgetter
|
|
import datetime
|
|
import logging
|
|
import json
|
|
import jwt
|
|
from collections import defaultdict
|
|
from trytond.exceptions import UserWarning, UserError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
TRIGGERS = [
|
|
('bldate', 'BL date'),
|
|
('invdate', 'Invoice date'),
|
|
('ctdate', 'Ct. date'),
|
|
('prdate', 'Pur. date'),
|
|
('cod', 'COD date'),
|
|
('border', 'Border crossing date'),
|
|
('pump', 'Pump date'),
|
|
('discharge', 'Discharge NOR'),
|
|
('arrival', 'Arrival date'),
|
|
('delmonth', 'Delivery month'),
|
|
('deldate', 'Delivery date'),
|
|
]
|
|
|
|
class DocType(ModelSQL,ModelView):
|
|
"Document Type"
|
|
__name__ = 'document.type'
|
|
name = fields.Char('Name')
|
|
|
|
class ContractDocumentType(ModelSQL):
|
|
"Contract - Document Type"
|
|
__name__ = 'contract.document.type'
|
|
doc_type = fields.Many2One('document.type', 'Document Type')
|
|
purchase = fields.Many2One('purchase.purchase', "Purchase")
|
|
|
|
class DocTemplate(ModelSQL,ModelView):
|
|
"Documents Template"
|
|
__name__ = 'doc.template'
|
|
name = fields.Char('Name')
|
|
type = fields.Many2Many('doc.type.template','template','type',"Document Type")
|
|
|
|
class DocTypeTemplate(ModelSQL):
|
|
"Template - Document Type"
|
|
__name__ = 'doc.type.template'
|
|
template = fields.Many2One('doc.template')
|
|
type = fields.Many2One('document.type')
|
|
|
|
class PurchaseStrategy(ModelSQL):
|
|
"Purchase - Document Type"
|
|
__name__ = 'purchase.strategy'
|
|
line = fields.Many2One('purchase.line', 'Purchase Line')
|
|
strategy = fields.Many2One('mtm.strategy', "Strategy")
|
|
|
|
class Estimated(metaclass=PoolMeta):
|
|
"Estimated date"
|
|
__name__ = 'pricing.estimated'
|
|
|
|
shipment_in = fields.Many2One('stock.shipment.in')
|
|
shipment_out = fields.Many2One('stock.shipment.out')
|
|
shipment_internal = fields.Many2One('stock.shipment.internal')
|
|
purchase = fields.Many2One('purchase.purchase',"Purchase")
|
|
line = fields.Many2One('purchase.line',"Line")
|
|
|
|
class Currency(metaclass=PoolMeta):
|
|
"Currency"
|
|
__name__ = 'currency.currency'
|
|
|
|
concatenate = fields.Boolean("Concatenate")
|
|
|
|
@classmethod
|
|
def default_concatenate(cls):
|
|
return False
|
|
|
|
class Unit(metaclass=PoolMeta):
|
|
"Unit"
|
|
__name__ = 'product.uom'
|
|
|
|
concatenate = fields.Boolean("Concatenate")
|
|
|
|
@classmethod
|
|
def default_concatenate(cls):
|
|
return False
|
|
|
|
class FeeLots(metaclass=PoolMeta):
|
|
|
|
"Fee lots"
|
|
__name__ = 'fee.lots'
|
|
|
|
line = fields.Many2One('purchase.line',"Line")
|
|
|
|
class Component(metaclass=PoolMeta):
|
|
"Component"
|
|
__name__ = 'pricing.component'
|
|
|
|
line = fields.Many2One('purchase.line',"Line")
|
|
quota = fields.Function(fields.Numeric("Quota",digits='unit'),'get_quota_purchase')
|
|
unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit_purchase')
|
|
|
|
def getDelMonthDatePurchase(self):
|
|
PM = Pool().get('product.month')
|
|
if self.line.del_period:
|
|
pm = PM(self.line.del_period)
|
|
if pm:
|
|
logger.info("DELMONTHDATE:%s",pm.beg_date)
|
|
return pm.beg_date
|
|
|
|
def getEstimatedTriggerPurchase(self,t):
|
|
logger.info("GETTRIGGER:%s",t)
|
|
if t == 'delmonth':
|
|
return self.getDelMonthDatePurchase()
|
|
PE = Pool().get('pricing.estimated')
|
|
Date = Pool().get('ir.date')
|
|
pe = PE.search([('line','=',self.line),('trigger','=',t)])
|
|
if pe:
|
|
return pe[0].estimated_date
|
|
else:
|
|
return Date.today()
|
|
|
|
def get_unit_purchase(self, name):
|
|
if self.line:
|
|
return self.line.unit
|
|
|
|
def get_quota_purchase(self, name):
|
|
if self.line:
|
|
if self.line.quantity:
|
|
return round(self.line.quantity_theorical / (self.nbdays if self.nbdays > 0 else 1),5)
|
|
|
|
class Pricing(metaclass=PoolMeta):
|
|
"Pricing"
|
|
__name__ = 'pricing.pricing'
|
|
|
|
line = fields.Many2One('purchase.line',"Lines")
|
|
unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit_purchase')
|
|
|
|
def get_unit_purchase(self,name):
|
|
if self.line:
|
|
return self.line.unit
|
|
|
|
def get_eod_price_purchase(self):
|
|
if self.line:
|
|
return round((self.fixed_qt * self.fixed_qt_price + self.unfixed_qt * self.unfixed_qt_price)/Decimal(self.line.quantity_theorical),4)
|
|
return Decimal(0)
|
|
|
|
class Summary(ModelSQL,ModelView):
|
|
"Pricing summary"
|
|
__name__ = 'purchase.pricing.summary'
|
|
|
|
line = fields.Many2One('purchase.line',"Lines")
|
|
price_component = fields.Many2One('pricing.component',"Component")
|
|
quantity = fields.Numeric("Qt",digits=(1,5))
|
|
fixed_qt = fields.Numeric("Fixed qt",digits=(1,5))
|
|
unfixed_qt = fields.Numeric("Unfixed qt",digits=(1,5))
|
|
price = fields.Numeric("Price",digits=(1,4))
|
|
progress = fields.Float("Fix. progress")
|
|
ratio = fields.Numeric("Ratio")
|
|
|
|
def get_name(self):
|
|
if self.price_component:
|
|
return self.price_component.get_rec_name()
|
|
return ""
|
|
|
|
def get_last_price(self):
|
|
Date = Pool().get('ir.date')
|
|
if self.price_component:
|
|
pc = Pool().get('pricing.component')(self.price_component)
|
|
if pc.price_index:
|
|
PI = Pool().get('price.price')
|
|
pi = PI(pc.price_index)
|
|
logger.info("PURCHASECURRENCY:%s",self.line.purchase.currency)
|
|
return pi.get_price(Date.today(),self.line.unit,self.line.purchase.currency,True)
|
|
|
|
@classmethod
|
|
def table_query(cls):
|
|
PurchasePricing = Pool().get('pricing.pricing')
|
|
pp = PurchasePricing.__table__()
|
|
PurchaseComponent = Pool().get('pricing.component')
|
|
pc = PurchaseComponent.__table__()
|
|
#wh = Literal(True)
|
|
context = Transaction().context
|
|
group_pnl = context.get('group_pnl')
|
|
|
|
if group_pnl:
|
|
return None
|
|
|
|
query = pp.join(pc,'LEFT',condition=pp.price_component == pc.id).select(
|
|
Literal(0).as_('create_uid'),
|
|
CurrentTimestamp().as_('create_date'),
|
|
Literal(None).as_('write_uid'),
|
|
Literal(None).as_('write_date'),
|
|
Literal(None).as_('sale_line'),
|
|
Max(pp.id).as_('id'),
|
|
pp.line.as_('line'),
|
|
pp.price_component.as_('price_component'),
|
|
Max(pp.fixed_qt+pp.unfixed_qt).as_('quantity'),
|
|
Max(pp.fixed_qt).as_('fixed_qt'),
|
|
(Min(pp.unfixed_qt)).as_('unfixed_qt'),
|
|
Max(Case((pp.last, pp.eod_price),else_=0)).as_('price'),
|
|
(Max(pp.fixed_qt)/Max(pp.fixed_qt+pp.unfixed_qt)).as_('progress'),
|
|
Max(pc.ratio).as_('ratio'),
|
|
#where=wh,
|
|
group_by=[pp.line,pp.price_component])
|
|
|
|
return query
|
|
|
|
class StockLocation(metaclass=PoolMeta):
|
|
__name__ = 'stock.location'
|
|
lat = fields.Numeric("Latitude")
|
|
lon = fields.Numeric("Longitude")
|
|
|
|
class PurchaseCertification(ModelSQL,ModelView):
|
|
"Certification"
|
|
__name__ = 'purchase.certification'
|
|
name = fields.Char("Name")
|
|
|
|
class PurchaseCertificationWeightBasis(ModelSQL,ModelView):
|
|
"Weight basis"
|
|
__name__ = 'purchase.weight.basis'
|
|
name = fields.Char("Name")
|
|
qt_type = fields.Many2One('lot.qt.type',"Associated type to final invoice")
|
|
description = fields.Char("Description")
|
|
|
|
class PurchaseAssociation(ModelSQL,ModelView):
|
|
"Association"
|
|
__name__ = 'purchase.association'
|
|
name = fields.Char("Name")
|
|
party = fields.Many2One('party.party',"Party")
|
|
description = fields.Char("Description")
|
|
|
|
class PurchaseCrop(ModelSQL,ModelView):
|
|
"Crop"
|
|
__name__ = 'purchase.crop'
|
|
name = fields.Char("Name")
|
|
|
|
class Purchase(metaclass=PoolMeta):
|
|
__name__ = 'purchase.purchase'
|
|
|
|
btb = fields.Many2One('back.to.back',"Back to back")
|
|
from_location = fields.Many2One('stock.location', 'From location',domain=[('type', "!=", 'customer')])
|
|
to_location = fields.Many2One('stock.location', 'To location',domain=[('type', "!=", 'supplier')])
|
|
shipment_in = fields.Many2One('stock.shipment.in','Purchases')
|
|
broker = fields.Many2One('party.party',"Broker",domain=[('categories.parent', 'child_of', [4])])
|
|
tol_min = fields.Numeric("Tol - in %")
|
|
tol_max = fields.Numeric("Tol + in %")
|
|
tol_min_qt = fields.Numeric("Tol -")
|
|
tol_max_qt = fields.Numeric("Tol +")
|
|
certif = fields.Many2One('purchase.certification',"Certification",states={'invisible': Eval('company_visible'),})
|
|
wb = fields.Many2One('purchase.weight.basis',"Weight basis")
|
|
association = fields.Many2One('purchase.association',"Association",states={'invisible': Eval('company_visible'),})
|
|
crop = fields.Many2One('purchase.crop',"Crop",states={'invisible': Eval('company_visible'),})
|
|
pnl = fields.One2Many('valuation.valuation.dyn', 'r_purchase', 'Pnl',states={'invisible': ~Eval('group_pnl'),})
|
|
pnl_ = fields.One2Many('valuation.valuation.line', 'purchase', 'Pnl',states={'invisible': Eval('group_pnl'),})
|
|
derivatives = fields.One2Many('derivative.derivative', 'purchase', 'Derivative')
|
|
plans = fields.One2Many('workflow.plan','purchase',"Execution plans")
|
|
forex = fields.One2Many('forex.cover.physical.contract','contract',"Forex",readonly=True)
|
|
plan = fields.Many2One('workflow.plan',"Name")
|
|
estimated_date = fields.One2Many('pricing.estimated','purchase',"Estimated date")
|
|
group_pnl = fields.Boolean("Group Pnl")
|
|
viewer = fields.Function(fields.Text(""),'get_viewer')
|
|
doc_template = fields.Many2One('doc.template',"Template")
|
|
required_documents = fields.Many2Many(
|
|
'contract.document.type', 'purchase', 'doc_type', 'Required Documents')
|
|
analytic_dimensions = fields.One2Many(
|
|
'analytic.dimension.assignment',
|
|
'purchase',
|
|
'Analytic Dimensions'
|
|
)
|
|
trader = fields.Many2One('party.party',"Trader")
|
|
operator = fields.Many2One('party.party',"Operator")
|
|
our_reference = fields.Char("Our Reference")
|
|
company_visible = fields.Function(fields.Boolean("Visible"),'get_company_info')
|
|
lc_date = fields.Date("LC date")
|
|
|
|
def get_company_info(self,name):
|
|
return (self.company.party.name == 'MELYA')
|
|
|
|
@classmethod
|
|
def default_viewer(cls):
|
|
country_start = "Zobiland"
|
|
data = {
|
|
"highlightedCountryName": country_start
|
|
}
|
|
return "d3:" + json.dumps(data)
|
|
|
|
def get_viewer(self, name=None):
|
|
country_start = ''
|
|
dep_name = ''
|
|
arr_name = ''
|
|
departure = ''
|
|
arrival = ''
|
|
if self.party and self.party.addresses:
|
|
if self.party.addresses[0].country:
|
|
country_start = self.party.addresses[0].country.name
|
|
if self.from_location:
|
|
lat_from = self.from_location.lat
|
|
lon_from = self.from_location.lon
|
|
dep_name = self.from_location.name
|
|
departure = { "name":dep_name,"lat": str(lat_from), "lon": str(lon_from) }
|
|
if self.to_location:
|
|
lat_to = self.to_location.lat
|
|
lon_to = self.to_location.lon
|
|
arr_name = self.to_location.name
|
|
arrival = { "name":arr_name,"lat": str(lat_to), "lon": str(lon_to) }
|
|
data = {
|
|
"highlightedCountryNames": [{"name":country_start}],
|
|
"routePoints": [
|
|
{ "lon": -46.3, "lat": -23.9 },
|
|
{ "lon": -30.0, "lat": -20.0 },
|
|
{ "lon": -30.0, "lat": 0.0 },
|
|
{ "lon": -6.0, "lat": 35.9 },
|
|
{ "lon": 15.0, "lat": 38.0 },
|
|
{ "lon": 29.0, "lat": 41.0 }
|
|
],
|
|
"boats": [
|
|
# {"name": "CARIBBEAN 1",
|
|
# "imo": "1234567",
|
|
# "lon": -30.0,
|
|
# "lat": 0.0,
|
|
# "status": "En route",
|
|
# "links": [
|
|
# { "text": "Voir sur VesselFinder", "url": "https://www.vesselfinder.com" },
|
|
# { "text": "Détails techniques", "url": "https://example.com/tech" }
|
|
# ],
|
|
# "actions": [
|
|
# { "type": "track", "id": "123", "label": "Suivre ce bateau" },
|
|
# { "type": "details", "id": "123", "label": "Voir détails" }
|
|
# ]}
|
|
],
|
|
"cottonStocks": [
|
|
# { "name":"Mali","lat": 12.65, "lon": -8.0, "amount": 300 },
|
|
# { "name":"Egypte","lat": 30.05, "lon": 31.25, "amount": 500 },
|
|
# { "name":"Irak","lat": 33.0, "lon": 44.0, "amount": 150 }
|
|
],
|
|
"departures": [departure],
|
|
"arrivals": [arrival]
|
|
}
|
|
|
|
return "d3:" + json.dumps(data)
|
|
|
|
def getLots(self):
|
|
if self.lines:
|
|
if self.lines.lots:
|
|
return [l for l in self.lines.lots]
|
|
|
|
|
|
@fields.depends('party','from_location','to_location')
|
|
def on_change_with_viewer(self):
|
|
return self.get_viewer()
|
|
|
|
@fields.depends('doc_template','required_documents')
|
|
def on_change_with_required_documents(self):
|
|
if self.doc_template:
|
|
return self.doc_template.type
|
|
|
|
@classmethod
|
|
def copy(cls, purchases, default=None):
|
|
if default is None:
|
|
default = {}
|
|
else:
|
|
default = default.copy()
|
|
default.setdefault('pnl', None)
|
|
default.setdefault('derivatives', None)
|
|
default.setdefault('pnl_', None)
|
|
default.setdefault('plans', None)
|
|
return super().copy(purchases, default=default)
|
|
|
|
@classmethod
|
|
def validate(cls, purchases):
|
|
super(Purchase, cls).validate(purchases)
|
|
Line = Pool().get('purchase.line')
|
|
Date = Pool().get('ir.date')
|
|
for purchase in purchases:
|
|
for line in purchase.lines:
|
|
if not line.quantity_theorical and line.quantity > 0:
|
|
line.quantity_theorical = (
|
|
Decimal(str(line.quantity))
|
|
.quantize(Decimal("0.00001"))
|
|
)
|
|
Line.save([line])
|
|
|
|
#compute pnl
|
|
Pnl = Pool().get('valuation.valuation')
|
|
Pnl.generate(line)
|
|
|
|
if line.quantity_theorical:
|
|
OpenPosition = Pool().get('open.position')
|
|
# OpenPosition.create_from_purchase_line(line)
|
|
|
|
#line unit_price calculation
|
|
if line.price_type == 'basis' and line.lots: #line.price_pricing and line.price_components and
|
|
unit_price = line.get_basis_price()
|
|
logger.info("VALIDATEPURCHASE:%s",unit_price)
|
|
if unit_price != line.unit_price:
|
|
line.unit_price = unit_price
|
|
logger.info("VALIDATEPURCHASE2:%s",line.unit_price)
|
|
Line.save([line])
|
|
if line.price_type == 'efp':
|
|
if line.derivatives:
|
|
for d in line.derivatives:
|
|
line.unit_price = round(Decimal(d.price_index.get_price(Date.today(),line.unit,line.currency,True)),4)
|
|
logger.info("EFP_PRICE:%s",line.unit_price)
|
|
Line.save([line])
|
|
|
|
class Line(metaclass=PoolMeta):
|
|
__name__ = 'purchase.line'
|
|
|
|
quantity_theorical = fields.Numeric("Th. quantity", digits='unit', readonly=True)
|
|
price_type = fields.Selection([
|
|
('cash', 'Cash Price'),
|
|
('priced', 'Priced'),
|
|
('basis', 'Basis'),
|
|
('efp', 'EFP'),
|
|
], 'Price type')
|
|
progress = fields.Function(fields.Float("Fix. progress",
|
|
states={
|
|
'invisible': Eval('price_type') != 'basis',
|
|
}),'get_progress')
|
|
del_period = fields.Many2One('product.month',"Delivery Period")
|
|
from_del = fields.Date("From")
|
|
to_del = fields.Date("To")
|
|
period_at = fields.Selection([
|
|
(None, ''),
|
|
('laycan', 'Laycan'),
|
|
('loading', 'Loading'),
|
|
('discharge', 'Discharge'),
|
|
('crossing_border', 'Crossing Border'),
|
|
('title_transfer', 'Title transfer'),
|
|
('arrival', 'Arrival'),
|
|
],"Period at")
|
|
concentration = fields.Numeric("Concentration")
|
|
price_components = fields.One2Many('pricing.component','line',"Components")
|
|
price_pricing = fields.One2Many('pricing.pricing','line',"Pricing")
|
|
price_summary = fields.One2Many('purchase.pricing.summary','line',"Summary")
|
|
estimated_date = fields.One2Many('pricing.estimated','line',"Estimated date")
|
|
optional = fields.One2Many('optional.scenario','line',"Optionals Scenarios")
|
|
lots = fields.One2Many('lot.lot','line',"Lots",readonly=True)
|
|
purchase_line = fields.Many2One('purchase.line',"Lines")
|
|
fees = fields.One2Many('fee.fee', 'line', 'Fees')#, filter=[('product.type', '=', 'service')])
|
|
derivatives = fields.One2Many('derivative.derivative','line',"Derivatives")
|
|
mtm = fields.Many2Many('purchase.strategy', 'line', 'strategy', 'Mtm Strategy')
|
|
tol_min = fields.Numeric("Tol - in %",states={
|
|
'readonly': (Eval('inherit_tol')),
|
|
})
|
|
tol_max = fields.Numeric("Tol + in %",states={
|
|
'readonly': (Eval('inherit_tol')),
|
|
})
|
|
tol_min_qt = fields.Numeric("Tol -",states={
|
|
'readonly': (Eval('inherit_tol')),
|
|
})
|
|
tol_max_qt = fields.Numeric("Tol +",states={
|
|
'readonly': (Eval('inherit_tol')),
|
|
})
|
|
inherit_tol = fields.Boolean("Inherit tolerance")
|
|
tol_min_v = fields.Function(fields.Numeric("Qt min"),'get_tol_min')
|
|
tol_max_v = fields.Function(fields.Numeric("Qt max"),'get_tol_max')
|
|
# certification = fields.Selection([
|
|
# (None, ''),
|
|
# ('bci', 'BCI'),
|
|
# ],"Certification",states={'readonly': (Eval('inherit_cer')),})
|
|
certif = fields.Many2One('purchase.certification',"Certification",states={'readonly': (Eval('inherit_cer')),})
|
|
inherit_cer = fields.Boolean("Inherit certification")
|
|
enable_linked_currency = fields.Boolean("Linked currencies")
|
|
linked_price = fields.Numeric("Price", digits='unit',states={
|
|
'invisible': (~Eval('enable_linked_currency')),
|
|
})
|
|
linked_currency = fields.Many2One('currency.linked',"Currency",states={
|
|
'invisible': (~Eval('enable_linked_currency')),
|
|
})
|
|
linked_unit = fields.Many2One('product.uom', 'Unit',states={
|
|
'invisible': (~Eval('enable_linked_currency')),
|
|
})
|
|
premium = fields.Numeric("Premium/Discount",digits='unit')
|
|
fee_ = fields.Many2One('fee.fee',"Fee")
|
|
|
|
attributes = fields.Dict(
|
|
'product.attribute', 'Attributes',
|
|
domain=[
|
|
('sets', '=', Eval('attribute_set')),
|
|
],
|
|
states={
|
|
'readonly': ~Eval('attribute_set'),
|
|
},
|
|
depends=['product', 'attribute_set'],
|
|
help="Add attributes to the variant."
|
|
)
|
|
|
|
attribute_set = fields.Function(
|
|
fields.Many2One('product.attribute.set', "Attribute Set"),
|
|
'on_change_with_attribute_set'
|
|
)
|
|
|
|
attributes_name = fields.Function(
|
|
fields.Char("Attributes Name"),
|
|
'on_change_with_attributes_name'
|
|
)
|
|
|
|
@fields.depends('product')
|
|
def on_change_with_attribute_set(self, name=None):
|
|
if self.product and self.product.template and self.product.template.attribute_set:
|
|
return self.product.template.attribute_set.id
|
|
|
|
@fields.depends('product', 'attributes')
|
|
def on_change_with_attributes_name(self, name=None):
|
|
if not self.product or not self.product.attribute_set or not self.attributes:
|
|
return
|
|
|
|
def key(attribute):
|
|
return getattr(attribute, 'sequence', attribute.name)
|
|
|
|
values = []
|
|
for attribute in sorted(self.product.attribute_set.attributes, key=key):
|
|
if attribute.name in self.attributes:
|
|
value = self.attributes[attribute.name]
|
|
values.append(gettext(
|
|
'product_attribute.msg_label_value',
|
|
label=attribute.string,
|
|
value=attribute.format(value)
|
|
))
|
|
return " | ".join(filter(None, values))
|
|
|
|
@classmethod
|
|
def default_price_type(cls):
|
|
return 'priced'
|
|
|
|
@classmethod
|
|
def default_inherit_tol(cls):
|
|
return True
|
|
|
|
@classmethod
|
|
def default_enable_linked_currency(cls):
|
|
return False
|
|
|
|
@classmethod
|
|
def default_inherit_cer(cls):
|
|
return True
|
|
|
|
def get_matched_lines(self):
|
|
if self.lots:
|
|
LotQt = Pool().get('lot.qt')
|
|
return LotQt.search([('lot_p','=',self.lots[0].id),('lot_s','>',0)])
|
|
|
|
def get_date(self,trigger_event):
|
|
trigger_date = None
|
|
if self.estimated_date:
|
|
trigger_date = [d.estimated_date for d in self.estimated_date if d.trigger == trigger_event]
|
|
trigger_date = trigger_date[0] if trigger_date else None
|
|
return trigger_date
|
|
|
|
def get_tol_min(self,name):
|
|
if self.inherit_tol:
|
|
if self.purchase.tol_min and self.quantity_theorical:
|
|
return round((1-(self.purchase.tol_min/100))*Decimal(self.quantity_theorical),3)
|
|
else:
|
|
if self.tol_min and self.quantity_theorical:
|
|
return round((1-(self.tol_min/100))*Decimal(self.quantity_theorical),3)
|
|
|
|
def get_tol_max(self,name):
|
|
if self.inherit_tol:
|
|
if self.purchase.tol_max and self.quantity_theorical:
|
|
return round((1+(self.purchase.tol_max/100))*Decimal(self.quantity_theorical),3)
|
|
else:
|
|
if self.tol_max and self.quantity_theorical:
|
|
return round((1+(self.tol_max/100))*Decimal(self.quantity_theorical),3)
|
|
|
|
def get_progress(self,name):
|
|
PS = Pool().get('purchase.pricing.summary')
|
|
ps = PS.search(['line','=',self.id])
|
|
if ps:
|
|
return sum((e.progress if e.progress else 0) * (e.ratio if e.ratio else 0) / 100 for e in ps)
|
|
|
|
def getVirtualLot(self):
|
|
if self.lots:
|
|
return [l for l in self.lots if l.lot_type=='virtual'][0]
|
|
|
|
def get_basis_price(self):
|
|
price = Decimal(0)
|
|
for pc in self.price_components:
|
|
PP = Pool().get('purchase.pricing.summary')
|
|
pp = PP.search([('price_component','=',pc.id),('line','=',self.id)])
|
|
if pp:
|
|
price += pp[0].price * (pc.ratio / 100)
|
|
return round(price,4)
|
|
|
|
def get_price(self,lot_premium=0):
|
|
return (self.unit_price + Decimal(lot_premium)) if self.unit_price else Decimal(0) + (self.premium if self.premium else Decimal(0))
|
|
|
|
def get_price_linked_currency(self,lot_premium=0):
|
|
if self.linked_unit:
|
|
Uom = Pool().get('product.uom')
|
|
qt = Uom.compute_qty(self.unit, float(1), self.linked_unit)
|
|
return round(((self.linked_price + Decimal(lot_premium) + (self.premium if self.premium else Decimal(0))) * self.linked_currency.factor) * Decimal(qt), 4)
|
|
else:
|
|
return round((self.linked_price + Decimal(lot_premium) + (self.premium if self.premium else Decimal(0))) * self.linked_currency.factor, 4)
|
|
|
|
@fields.depends('id','unit','quantity','unit_price','price_pricing','price_type','price_components','premium','estimated_date','lots','fees','enable_linked_currency','linked_price','linked_currency','linked_unit')
|
|
def on_change_with_unit_price(self, name=None):
|
|
Date = Pool().get('ir.date')
|
|
logger.info("ONCHANGEUNITPRICE:%s",self.unit_price)
|
|
if self.price_type == 'basis' and self.lots: #self.price_pricing and self.price_components and
|
|
logger.info("ONCHANGEUNITPRICE_IN:%s",self.get_basis_price())
|
|
return self.get_basis_price()
|
|
if self.enable_linked_currency and self.linked_price and self.linked_currency and self.price_type == 'priced':
|
|
return self.get_price_linked_currency()
|
|
if self.price_type == 'efp':
|
|
if hasattr(self, 'derivatives') and self.derivatives:
|
|
for d in self.derivatives:
|
|
return round(d.price_index.get_price(Date.today(),self.unit,self.purchase.currency,True),4)
|
|
return self.unit_price
|
|
|
|
@classmethod
|
|
def copy(cls, lines, default=None):
|
|
if default is None:
|
|
default = {}
|
|
else:
|
|
default = default.copy()
|
|
default.setdefault('lots', None)
|
|
default.setdefault('quantity', Decimal(0))
|
|
default.setdefault('quantity_theorical', None)
|
|
default.setdefault('price_pricing', None)
|
|
return super().copy(lines, default=default)
|
|
|
|
@classmethod
|
|
def delete(cls, lines):
|
|
pool = Pool()
|
|
LotQt = pool.get('lot.qt')
|
|
Valuation = pool.get('valuation.valuation')
|
|
OpenPosition = pool.get('open.position')
|
|
for line in lines:
|
|
if line.lots:
|
|
vlot_p = line.lots[0].getVlot_p()
|
|
lqts = LotQt.search([('lot_p','=',vlot_p.id),('lot_s','!=',None),('lot_quantity','>',0)])
|
|
if lqts:
|
|
raise UserError("You cannot delete matched sale")
|
|
return
|
|
lqts = LotQt.search([('lot_p','=',vlot_p.id)])
|
|
LotQt.delete(lqts)
|
|
valuations = Valuation.search([('lot','in',line.lots)])
|
|
if valuations:
|
|
Valuation.delete(valuations)
|
|
# op = OpenPosition.search(['line','=',line.id])
|
|
# if op:
|
|
# OpenPosition.delete(op)
|
|
super(Line, cls).delete(lines)
|
|
|
|
@classmethod
|
|
def validate(cls, lines):
|
|
super(Line, cls).validate(lines)
|
|
for line in lines:
|
|
if line.price_components:
|
|
for pc in line.price_components:
|
|
if pc.triggers:
|
|
for tr in pc.triggers:
|
|
line.check_from_to(tr)
|
|
line.check_pricing()
|
|
#no lot need to create one with line quantity
|
|
if not line.created_by_code:
|
|
if not line.lots and line.product.type != 'service' and line.quantity != Decimal(0):
|
|
FeeLots = Pool().get('fee.lots')
|
|
LotQtHist = Pool().get('lot.qt.hist')
|
|
LotQtType = Pool().get('lot.qt.type')
|
|
Lot = Pool().get('lot.lot')
|
|
lot = Lot()
|
|
lot.line = line.id
|
|
lot.lot_qt = None
|
|
lot.lot_unit = None
|
|
lot.lot_unit_line = line.unit
|
|
lot.lot_quantity = round(Decimal(line.quantity),5)
|
|
lot.lot_gross_quantity = None
|
|
lot.lot_status = 'forecast'
|
|
lot.lot_type = 'virtual'
|
|
lot.lot_product = line.product
|
|
lqtt = LotQtType.search([('sequence','=',1)])
|
|
if lqtt:
|
|
lqh = LotQtHist()
|
|
lqh.quantity_type = lqtt[0]
|
|
lqh.quantity = lot.lot_quantity
|
|
logger.info("PURCHASE_VALIDATE:%s",lot.lot_quantity)
|
|
lqh.gross_quantity = lot.lot_quantity
|
|
lot.lot_hist = [lqh]
|
|
if line.quantity > 0:
|
|
Lot.save([lot])
|
|
#check if fees need to be updated
|
|
if line.fees:
|
|
for fee in line.fees:
|
|
fl_check = FeeLots.search([('fee','=',fee.id),('lot','=',lot.id),('line','=',line.id)])
|
|
if not fl_check:
|
|
fl = FeeLots()
|
|
fl.fee = fee.id
|
|
fl.lot = lot.id
|
|
fl.line = line.id
|
|
FeeLots.save([fl])
|
|
|
|
#update inherit fee qt
|
|
# if line.fees:
|
|
# Fee = Pool().get('fee.fee')
|
|
# for f in line.fees:
|
|
# if f.inherit_qt:
|
|
# f.quantity = round(line.quantity_theorical,4)
|
|
# f.unit = line.unit
|
|
# Fee.save([f])
|
|
#check if fee purchase is filled on fee
|
|
if line.fee_:
|
|
if not line.fee_.purchase:
|
|
Fee = Pool().get('fee.fee')
|
|
f = Fee(line.fee_)
|
|
f.purchase = line.purchase
|
|
Fee.save([f])
|
|
|
|
def check_from_to(self,tr):
|
|
if tr.pricing_period:
|
|
date_from,date_to, d, include, dates = tr.getDateWithEstTrigger(1)
|
|
if date_from:
|
|
tr.from_p = date_from.date()
|
|
if date_to:
|
|
tr.to_p = date_to.date()
|
|
if tr.application_period:
|
|
date_from,date_to, d, include, dates = tr.getDateWithEstTrigger(2)
|
|
if date_from:
|
|
tr.from_a = date_from.date()
|
|
if date_to:
|
|
tr.to_a = date_to.date()
|
|
TR = Pool().get('pricing.trigger')
|
|
TR.save([tr])
|
|
|
|
def check_pricing(self):
|
|
if self.price_components:
|
|
for pc in self.price_components:
|
|
if not pc.auto:
|
|
Pricing = Pool().get('pricing.pricing')
|
|
pricings = Pricing.search(['price_component','=',pc.id],order=[('pricing_date', 'ASC')])
|
|
if pricings:
|
|
cumul_qt = Decimal(0)
|
|
index = 0
|
|
for pr in pricings:
|
|
cumul_qt += pr.quantity
|
|
pr.fixed_qt = cumul_qt
|
|
pr.fixed_qt_price = pr.get_fixed_price()
|
|
pr.unfixed_qt = Decimal(pr.line.quantity_theorical) - pr.fixed_qt
|
|
pr.unfixed_qt_price = pr.fixed_qt_price
|
|
pr.eod_price = pr.get_eod_price_purchase()
|
|
if index == len(pricings) - 1:
|
|
pr.last = True
|
|
index += 1
|
|
Pricing.save([pr])
|
|
|
|
if pc.triggers and pc.auto:
|
|
prDate = []
|
|
prPrice = []
|
|
apDate = []
|
|
apPrice = []
|
|
for t in pc.triggers:
|
|
logger.info("CHECK_PRICING:%s",t)
|
|
prD, prP = t.getPricingListDates(pc.calendar)
|
|
logger.info("CHECK_PRICING2:%s",prP)
|
|
apD, apP = t.getApplicationListDates(pc.calendar)
|
|
prDate.extend(prD)
|
|
prPrice.extend(prP)
|
|
apDate.extend(apD)
|
|
apPrice.extend(apP)
|
|
if pc.quota:
|
|
prPrice = self.get_avg(prPrice)
|
|
self.generate_pricing(pc,apDate,prPrice)
|
|
|
|
def get_avg(self,lprice):
|
|
l = len(lprice)
|
|
if l > 0 :
|
|
cumulprice = float(0)
|
|
i = 1
|
|
for p in lprice:
|
|
if i > 1:
|
|
p['avg_minus_1'] = cumulprice / (i-1)
|
|
cumulprice += p['price']
|
|
p['avg'] = cumulprice / i
|
|
i += 1
|
|
return lprice
|
|
|
|
def getnearprice(self,pl,d,t,max_date=None):
|
|
if pl:
|
|
pl_sorted = sorted(pl, key=lambda x: x['date'])
|
|
pminus = pl_sorted[0]
|
|
if not max_date:
|
|
max_date = d.date()
|
|
for p in pl_sorted:
|
|
if p['date'].date() == d.date():
|
|
if p['isAvg'] and t == 'avg':
|
|
return p[t]
|
|
if not p['isAvg'] and t == 'avg':
|
|
return p['price']
|
|
elif p['date'].date() > d.date():
|
|
if pminus != p:
|
|
return pminus[t]
|
|
else:
|
|
return Decimal(0)
|
|
pminus = p
|
|
return pl_sorted[len(pl)-1][t]
|
|
return Decimal(0)
|
|
|
|
def generate_pricing(self,pc,dl,pl):
|
|
Pricing = Pool().get('pricing.pricing')
|
|
pricing = Pricing.search(['price_component','=',pc.id])
|
|
if pricing:
|
|
Pricing.delete(pricing)
|
|
cumul_qt = 0
|
|
index = 0
|
|
dl_sorted = sorted(dl)
|
|
for d in dl_sorted:
|
|
if pc.pricing_date and d.date() > pc.pricing_date:
|
|
break
|
|
p = Pricing()
|
|
p.line = self.id
|
|
logger.info("GENEDATE:%s",d)
|
|
logger.info("TYPEDATE:%s",type(d))
|
|
p.pricing_date = d.date()
|
|
p.price_component = pc.id
|
|
p.quantity = round(Decimal(pc.quota),5)
|
|
price = round(Decimal(self.getnearprice(pl,d,'price')),4)
|
|
p.settl_price = price
|
|
if price > 0:
|
|
cumul_qt += pc.quota
|
|
p.fixed_qt = round(Decimal(cumul_qt),5)
|
|
p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg')),4)
|
|
#p.fixed_qt_price = p.get_fixed_price()
|
|
if p.fixed_qt_price == 0:
|
|
p.fixed_qt_price = round(Decimal(self.getnearprice(pl,d,'avg_minus_1')),4)
|
|
p.unfixed_qt = round(Decimal(self.quantity_theorical) - Decimal(cumul_qt),5)
|
|
if p.unfixed_qt < 0.001:
|
|
p.unfixed_qt = Decimal(0)
|
|
p.fixed_qt = Decimal(self.quantity_theorical)
|
|
if price > 0:
|
|
logger.info("GENERATE_1:%s",price)
|
|
p.unfixed_qt_price = price
|
|
else:
|
|
pr = Decimal(pc.price_index.get_price(p.pricing_date,self.unit,self.purchase.currency,True))
|
|
pr = round(pr,4)
|
|
logger.info("GENERATE_2:%s",pr)
|
|
p.unfixed_qt_price = pr
|
|
p.eod_price = p.get_eod_price_purchase()
|
|
if (index == len(dl)-1) or (pc.pricing_date and (index < len(dl)-1 and dl_sorted[index+1].date() > pc.pricing_date)):
|
|
p.last = True
|
|
logger.info("GENERATE_3:%s",p.unfixed_qt_price)
|
|
Pricing.save([p])
|
|
index += 1
|
|
|
|
# @classmethod
|
|
# def view_attributes(cls):
|
|
# return super().view_attributes() + [
|
|
# ('/tree/field[@name="quantity"]', 'visual',
|
|
# If(Eval('quantity') & (Eval('quantity', 0) > 0),'success','danger')),
|
|
# ]
|
|
|
|
class GoToBi(Wizard):
|
|
__name__ = 'purchase.bi'
|
|
start_state = 'bi'
|
|
bi = StateAction('purchase_trade.url_bi')
|
|
|
|
def do_bi(self, action):
|
|
Configuration = Pool().get('gr.configuration')
|
|
config = Configuration.search(['id','>',0])[0]
|
|
|
|
ct_number = self.records[0].number
|
|
action['url'] = config.bi + '/dashboard/6-pnl?lot=&product=&purchase='+ ct_number + '&sale='
|
|
return action, {}
|
|
|
|
class PurchaseAllocationsWizard(Wizard):
|
|
'Open Allocations report from Purchase without modal'
|
|
__name__ = 'purchase.allocations.wizard'
|
|
|
|
start_state = 'open_report'
|
|
|
|
open_report = StateAction('purchase_trade.act_lot_report_form')
|
|
|
|
def do_open_report(self, action):
|
|
purchase_id = Transaction().context.get('active_id')
|
|
if not purchase_id:
|
|
raise ValueError("No active purchase ID in context")
|
|
action['context_model'] = 'lot.context'
|
|
action['pyson_context'] = PYSONEncoder().encode({
|
|
'purchase': purchase_id,
|
|
})
|
|
return action, {}
|
|
|
|
class PurchaseInvoiceReport(
|
|
ModelSQL, ModelView):
|
|
"Purchase invoices"
|
|
__name__ = 'purchase.invoice.report'
|
|
r_supplier = fields.Many2One('party.party',"Supplier")
|
|
r_purchase = fields.Many2One('purchase.purchase', "Purchase")
|
|
r_line = fields.Many2One('purchase.line',"Line")
|
|
r_lot = fields.Many2One('lot.lot',"Lot")
|
|
r_product = fields.Many2One('product.product', "Product")
|
|
r_pur_invoice = fields.Many2One('account.invoice',"Invoice")
|
|
r_inv_date = fields.Date("Inv. date")
|
|
r_pur_payment = fields.Many2Many('account.invoice-account.move.line','invoice', 'line', string='Payment')
|
|
r_invoice_amount = fields.Numeric("Amount",digits=(1,2))
|
|
r_payment_amount = fields.Numeric("Paid",digits=(1,2))
|
|
r_left_amount = fields.Numeric("Left",digits=(1,2))
|
|
r_curr = fields.Many2One('currency.currency',"Curr")
|
|
r_reconciliation = fields.Integer("Reconciliation")
|
|
r_move = fields.Many2One('account.move',"Move")
|
|
r_status = fields.Selection([
|
|
('not', 'Not'),
|
|
('paid', 'Paid'),
|
|
('partial', 'Partially'),
|
|
], 'Status')
|
|
|
|
@classmethod
|
|
def table_query(cls):
|
|
pool = Pool()
|
|
Invoice = pool.get('account.invoice')
|
|
InvoiceLine = pool.get('account.invoice.line')
|
|
PurchaseLine = pool.get('purchase.line')
|
|
Purchase = pool.get('purchase.purchase')
|
|
Party = pool.get('party.party')
|
|
InvoicePayment = pool.get('account.invoice-account.move.line')
|
|
MoveLine = pool.get('account.move.line')
|
|
Move = pool.get('account.move')
|
|
Currency = pool.get('currency.currency')
|
|
Lot = pool.get('lot.lot')
|
|
|
|
ai = Invoice.__table__()
|
|
ail = InvoiceLine.__table__()
|
|
pl = PurchaseLine.__table__()
|
|
pu = Purchase.__table__()
|
|
pa = Party.__table__()
|
|
aiaml = InvoicePayment.__table__()
|
|
aml = MoveLine.__table__()
|
|
lot = Lot.__table__()
|
|
cu = Currency.__table__()
|
|
mo = Move.__table__()
|
|
|
|
context = Transaction().context
|
|
supplier = context.get('supplier')
|
|
purchase = context.get('purchase')
|
|
asof = context.get('asof')
|
|
todate = context.get('todate')
|
|
state = context.get('state')
|
|
|
|
wh = Literal(True)
|
|
wh &= lot.lot_type == 'physic'
|
|
wh &= ai.type == 'in'
|
|
if supplier:
|
|
wh &= (ai.party == supplier)
|
|
if purchase:
|
|
wh &= (pu.id == purchase)
|
|
if asof and todate:
|
|
wh &= (ai.invoice_date >= asof) & (ai.invoice_date <= todate)
|
|
|
|
query = (
|
|
lot
|
|
.join(ail, 'LEFT', condition=ail.lot == lot.id)
|
|
.join(ai, 'LEFT', condition=ai.id == ail.invoice)
|
|
.join(pl, 'LEFT', condition=pl.id == lot.line)
|
|
.join(pu, 'LEFT', condition=pl.purchase == pu.id)
|
|
.join(pa, 'LEFT', condition=ai.party == pa.id)
|
|
.join(cu, 'LEFT', condition=cu.id == ail.currency)
|
|
.select(
|
|
Literal(0).as_('create_uid'),
|
|
CurrentTimestamp().as_('create_date'),
|
|
Literal(0).as_('write_uid'),
|
|
Literal(0).as_('write_date'),
|
|
Max(ail.id).as_('id'),
|
|
Max(pa.id).as_('r_supplier'),
|
|
pu.id.as_('r_purchase'),
|
|
pl.id.as_('r_line'),
|
|
Max(ail.product).as_('r_product'),
|
|
Max(lot.id).as_('r_lot'),
|
|
ai.id.as_('r_pur_invoice'),
|
|
Max(ai.invoice_date).as_('r_inv_date'),
|
|
Sum(ail.quantity*ail.unit_price).as_('r_invoice_amount'),
|
|
Max(cu.id).as_('r_curr'),
|
|
where=wh,
|
|
group_by=[pu.id,pl.id,ai.id]
|
|
)
|
|
)
|
|
query_alias = query
|
|
left = Case((Abs(Sum(aml.amount_second_currency))>0,(Max(query_alias.r_invoice_amount)-Sum(aml.amount_second_currency))),else_=(Max(query_alias.r_invoice_amount)-(Sum(aml.debit)-Sum(aml.credit))))
|
|
status = Case((left==0, 'paid'),else_=Case((left<Max(query_alias.r_invoice_amount),'partial'),else_='not'))
|
|
wh = Literal(True)
|
|
if state != 'all':
|
|
wh &= status == state
|
|
|
|
query2 = (
|
|
query_alias
|
|
.join(aiaml, 'LEFT', condition=(aiaml.invoice == query_alias.r_pur_invoice))
|
|
.join(aml, 'LEFT', condition=aml.id == aiaml.line)
|
|
.join(mo, 'LEFT', condition=mo.id == aml.move)
|
|
.select(
|
|
Literal(0).as_('create_uid'),
|
|
CurrentTimestamp().as_('create_date'),
|
|
Literal(0).as_('write_uid'),
|
|
Literal(0).as_('write_date'),
|
|
Max(query_alias.id).as_('id'),
|
|
Max(query_alias.r_supplier).as_('r_supplier'),
|
|
query_alias.r_purchase.as_('r_purchase'),
|
|
query_alias.r_line.as_('r_line'),
|
|
Max(query_alias.r_product).as_('r_product'),
|
|
Max(query_alias.r_lot).as_('r_lot'),
|
|
query_alias.r_pur_invoice.as_('r_pur_invoice'),
|
|
Max(query_alias.r_inv_date).as_('r_inv_date'),
|
|
Max(query_alias.r_invoice_amount).as_('r_invoice_amount'),
|
|
Sum(aml.amount_second_currency).as_('r_payment_amount'),
|
|
left.as_('r_left_amount'),
|
|
status.as_('r_status'),
|
|
Max(query_alias.r_curr).as_('r_curr'),
|
|
Max(aml.reconciliation).as_('r_reconciliation'),
|
|
Max(mo.id).as_('r_move'),
|
|
Max(aml.id).as_('r_pur_payment'),
|
|
where=wh,
|
|
group_by=[query_alias.r_purchase,query_alias.r_line,query_alias.r_pur_invoice]
|
|
))
|
|
|
|
return query2
|
|
|
|
class PurchaseInvoiceContext(ModelView):
|
|
"Purchase Invoice Context"
|
|
__name__ = 'purchase.invoice.context'
|
|
|
|
asof = fields.Date("As of")
|
|
todate = fields.Date("To")
|
|
supplier = fields.Many2One('party.party',"Supplier")
|
|
purchase = fields.Many2One('purchase.purchase', "Purchase")
|
|
state = fields.Selection([
|
|
('all', 'All'),
|
|
('not', 'Not'),
|
|
('paid', 'Paid'),
|
|
('partial', 'Partially')
|
|
], 'State')
|
|
|
|
@classmethod
|
|
def default_asof(cls):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
return Date.today().replace(day=1,month=1,year=1999)
|
|
|
|
@classmethod
|
|
def default_todate(cls):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
return Date.today()
|
|
|
|
@classmethod
|
|
def default_state(cls):
|
|
return 'all'
|
|
|
|
class InvoicePayment(Wizard):
|
|
'Payments'
|
|
__name__ = 'purchase.invoice.payment'
|
|
start_state = 'open_'
|
|
open_ = StateAction('account.act_move_line_form')
|
|
|
|
def do_open_(self, action):
|
|
if self.record.r_pur_invoice:
|
|
Payment = Pool().get('account.invoice-account.move.line')
|
|
payments = Payment.search(['invoice','=',self.record.r_pur_invoice.id])
|
|
if payments:
|
|
lines = [e.line.id for e in payments]
|
|
return action, {'res_id': lines}
|
|
|
|
class PnlReport(Wizard):
|
|
'Pnl report'
|
|
__name__ = 'pnl.report'
|
|
|
|
start = StateAction('purchase_trade.act_pnl_bi')
|
|
|
|
def do_start(self, action):
|
|
pool = Pool()
|
|
# action['views'].reverse()
|
|
return action, {'res_id': [1]}
|
|
|
|
class PnlBI(ModelSingleton,ModelSQL, ModelView):
|
|
'Pnl BI'
|
|
__name__ = 'pnl.bi'
|
|
|
|
input = fields.Text("BI")
|
|
metabase = fields.Function(fields.Text(""),'get_bi')
|
|
|
|
def get_bi(self,name=None):
|
|
Configuration = Pool().get('gr.configuration')
|
|
config = Configuration.search(['id','>',0])[0]
|
|
|
|
payload = {
|
|
"resource": {"dashboard": config.pnl_id},
|
|
"params": {},
|
|
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30),
|
|
}
|
|
token = jwt.encode(payload, config.payload, algorithm="HS256")
|
|
logger.info("TOKEN:%s",token)
|
|
if config.dark:
|
|
url = f"metabase:{config.bi}/embed/dashboard/{token}#theme=night&bordered=true&titled=true"
|
|
else:
|
|
url = f"metabase:{config.bi}/embed/dashboard/{token}#bordered=true&titled=true"
|
|
return url
|
|
|
|
class PositionReport(Wizard):
|
|
'Position report'
|
|
__name__ = 'position.report'
|
|
|
|
start = StateAction('purchase_trade.act_position_bi')
|
|
|
|
def do_start(self, action):
|
|
pool = Pool()
|
|
# action['views'].reverse()
|
|
return action, {'res_id': [1]}
|
|
|
|
class PositionBI(ModelSingleton,ModelSQL, ModelView):
|
|
'Position BI'
|
|
__name__ = 'position.bi'
|
|
|
|
input = fields.Text("BI")
|
|
metabase = fields.Function(fields.Text(""),'get_bi')
|
|
|
|
def get_bi(self,name=None):
|
|
Configuration = Pool().get('gr.configuration')
|
|
config = Configuration.search(['id','>',0])[0]
|
|
|
|
payload = {
|
|
"resource": {"dashboard": config.position_id},
|
|
"params": {},
|
|
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30),
|
|
}
|
|
token = jwt.encode(payload, config.payload, algorithm="HS256")
|
|
logger.info("TOKEN:%s",token)
|
|
if config.dark:
|
|
url = f"metabase:{config.bi}/embed/dashboard/{token}#theme=night&bordered=true&titled=true"
|
|
else:
|
|
url = f"metabase:{config.bi}/embed/dashboard/{token}#bordered=true&titled=true"
|
|
return url
|