Files
tradon/modules/purchase_trade/party.py
2026-04-06 09:50:15 +02:00

184 lines
6.3 KiB
Python
Executable File

from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import PoolMeta, Pool
from trytond.exceptions import UserError
from trytond.modules.purchase_trade.purchase import (TRIGGERS)
from trytond.transaction import Transaction
from decimal import getcontext, Decimal, ROUND_HALF_UP
from sql import Table
from trytond.pyson import Bool, Eval, Id, If
class PartyExecution(ModelSQL,ModelView):
"Party Execution"
__name__ = 'party.execution'
party = fields.Many2One('party.party',"Party")
area = fields.Many2One('country.region',"Area")
percent = fields.Numeric("% targeted")
achieved_percent = fields.Function(fields.Numeric("% achieved"),'get_percent')
@staticmethod
def _to_decimal(value):
if value is None:
return Decimal('0')
if isinstance(value, Decimal):
return value
return Decimal(str(value))
@classmethod
def _round_percent(cls, value):
return cls._to_decimal(value).quantize(
Decimal('0.01'), rounding=ROUND_HALF_UP)
def matches_country(self, country):
if not self.area or not country or not getattr(country, 'region', None):
return False
region = country.region
while region:
if region.id == self.area.id:
return True
region = getattr(region, 'parent', None)
return False
def matches_shipment(self, shipment):
location = getattr(shipment, 'to_location', None)
country = getattr(location, 'country', None)
return self.matches_country(country)
@classmethod
def compute_achieved_percent_for(cls, party, area):
if not party or not area:
return Decimal('0')
Shipment = Pool().get('stock.shipment.in')
shipments = Shipment.search([
('controller', '!=', None),
])
execution = cls()
execution.area = area
shipments = [
shipment for shipment in shipments
if execution.matches_shipment(shipment)]
total = len(shipments)
if not total:
return Decimal('0')
achieved = sum(
1 for shipment in shipments
if shipment.controller and shipment.controller.id == party.id)
return cls._round_percent(
(Decimal(achieved) * Decimal('100')) / Decimal(total))
def compute_achieved_percent(self):
return self.__class__.compute_achieved_percent_for(
self.party, self.area)
def get_target_gap(self):
return self._to_decimal(self.percent) - self.compute_achieved_percent()
def get_percent(self,name):
return self.compute_achieved_percent()
class PartyExecutionSla(ModelSQL,ModelView):
"Party Execution Sla"
__name__ = 'party.execution.sla'
party = fields.Many2One('party.party',"Party")
reference = fields.Char("Reference")
product = fields.Many2One('product.product',"Product")
date_from = fields.Date("From")
date_to = fields.Date("To")
places = fields.One2Many('party.execution.place','pes',"")
class PartyExecutionPlace(ModelSQL,ModelView):
"Party Sla Place"
__name__ = 'party.execution.place'
pes = fields.Many2One('party.execution.sla',"Sla")
location = fields.Many2One('stock.location',"Location")
cost = fields.Numeric("Cost",digits=(16,4))
mode = fields.Selection([
('lumpsum', 'Lump sum'),
('perqt', 'Per qt'),
('pprice', '% price'),
('rate', '% rate'),
('pcost', '% cost price'),
('ppack', 'Per packing'),
], 'Mode', required=True)
currency = fields.Many2One('currency.currency',"Currency")
unit = fields.Many2One('product.uom',"Unit",domain=[
If(Eval('mode') == 'ppack',
('category', '=', 8),
()),
],
states={
'readonly': Eval('mode') != 'ppack',
})
class Party(metaclass=PoolMeta):
__name__ = 'party.party'
tol_min = fields.Numeric("Tol - in %")
tol_max = fields.Numeric("Tol + in %")
wb = fields.Many2One('purchase.weight.basis',"Weight basis")
association = fields.Many2One('purchase.association',"Association")
origin =fields.Char("Origin")
execution = fields.One2Many('party.execution','party',"")
sla = fields.One2Many('party.execution.sla','party', "Sla")
initial = fields.Char("Initials")
def IsAvailableForControl(self,sh):
return True
def get_controller_execution_priority(self, shipment):
best_rule = None
best_gap = None
for execution in self.execution or []:
if not execution.matches_shipment(shipment):
continue
gap = execution.get_target_gap()
if best_gap is None or gap > best_gap:
best_gap = gap
best_rule = execution
return best_gap, best_rule
def get_sla_cost(self,location):
if self.sla:
for sla in self.sla:
SlaPlace = Pool().get('party.execution.place')
sp = SlaPlace.search([('pes','=', sla.id),('location','=',location)])
if sp:
return sp[0].cost,sp[0].mode,sp[0].currency,sp[0].unit
def get_alf(self):
if self.name == 'CARGO CONTROL':
return 105
t = Table('alf')
cursor = Transaction().connection.cursor()
cursor.execute(*t.select(
t.ALF_CODE,
where=t.SHORT_NAME.ilike(f'%{self.name}%')
))
rows = cursor.fetchall()
if rows:
return int(rows[0][0])
@classmethod
def getPartyByName(cls, party, category=None):
party = party.upper()
p = cls.search([('name', '=', party)], limit=1)
if p:
return p[0]
else:
p = cls()
p.name = party
cls.save([p])
if category:
Category = Pool().get('party.category')
cat = Category.search(['name','=',category])
if cat:
PartyCategory = Pool().get('party.party-party.category')
pc = PartyCategory()
pc.party = p.id
pc.category = cat[0].id
PartyCategory.save([pc])
return p