26.01.26
This commit is contained in:
215
modules/purchase_trade/service.py
Normal file
215
modules/purchase_trade/service.py
Normal file
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ContractFactory:
|
||||
"""
|
||||
Factory métier pour créer des Purchase depuis Sale
|
||||
ou des Sale depuis Purchase.
|
||||
|
||||
Compatible :
|
||||
- Wizard (n contrats)
|
||||
- Appel direct depuis un modèle (1 contrat)
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def create_contracts(cls, contracts, *, type_, ct):
|
||||
"""
|
||||
:param contracts: iterable de contracts (wizard lines)
|
||||
:param type_: 'Purchase' ou 'Sale'
|
||||
:param ct: objet contenant le contexte (lot, product, unit, matched...)
|
||||
:return: liste des contracts créés
|
||||
"""
|
||||
pool = Pool()
|
||||
|
||||
Sale = pool.get('sale.sale')
|
||||
Purchase = pool.get('purchase.purchase')
|
||||
SaleLine = pool.get('sale.line')
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
Date = pool.get('ir.date')
|
||||
|
||||
created = []
|
||||
|
||||
base_contract = (
|
||||
ct.lot.sale_line.sale
|
||||
if type_ == 'Purchase'
|
||||
else ct.lot.line.purchase
|
||||
)
|
||||
|
||||
for c in contracts:
|
||||
contract = Purchase() if type_ == 'Purchase' else Sale()
|
||||
line = PurchaseLine() if type_ == 'Purchase' else SaleLine()
|
||||
|
||||
# ---------- CONTRACT ----------
|
||||
parts = c.currency_unit.split("_")
|
||||
contract.currency = int(parts[0]) or 1
|
||||
contract.party = c.party
|
||||
contract.crop = c.crop
|
||||
contract.tol_min = c.tol_min
|
||||
contract.tol_max = c.tol_max
|
||||
contract.reference = c.reference
|
||||
|
||||
if type_ == 'Purchase':
|
||||
contract.purchase_date = Date.today()
|
||||
else:
|
||||
contract.sale_date = Date.today()
|
||||
|
||||
cls._apply_locations(contract, base_contract, type_)
|
||||
cls._apply_party_data(contract, c.party, type_)
|
||||
cls._apply_payment_term(contract, c.party, type_)
|
||||
|
||||
contract.incoterm = c.incoterm
|
||||
|
||||
if c.party.addresses:
|
||||
contract.invoice_address = c.party.addresses[0]
|
||||
if type_ == 'Sale':
|
||||
contract.shipment_address = c.party.addresses[0]
|
||||
|
||||
contract.save()
|
||||
|
||||
# ---------- LINE ----------
|
||||
line.quantity = c.quantity
|
||||
line.quantity_theorical = c.quantity
|
||||
line.product = ct.product
|
||||
line.unit = ct.unit
|
||||
line.price_type = c.price_type
|
||||
line.created_by_code = ct.matched
|
||||
line.premium = Decimal(0)
|
||||
|
||||
if type_ == 'Purchase':
|
||||
line.purchase = contract.id
|
||||
else:
|
||||
line.sale = contract.id
|
||||
|
||||
cls._apply_price(line, c, parts)
|
||||
|
||||
line.del_period = c.del_period
|
||||
line.from_del = c.from_del
|
||||
line.to_del = c.to_del
|
||||
|
||||
line.save()
|
||||
|
||||
logger.info("CREATE_ID:%s", contract.id)
|
||||
logger.info("CREATE_LINE_ID:%s", line.id)
|
||||
|
||||
if ct.matched:
|
||||
cls._create_lot(line, c, ct, type_)
|
||||
|
||||
created.append(contract)
|
||||
|
||||
return created
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@staticmethod
|
||||
def _apply_locations(contract, base, type_):
|
||||
if not (base.from_location and base.to_location):
|
||||
return
|
||||
|
||||
if type_ == 'Purchase':
|
||||
contract.to_location = base.from_location
|
||||
else:
|
||||
contract.from_location = base.to_location
|
||||
|
||||
if (base.from_location.type == 'supplier'
|
||||
and base.to_location.type == 'customer'):
|
||||
contract.from_location = base.from_location
|
||||
contract.to_location = base.to_location
|
||||
|
||||
@staticmethod
|
||||
def _apply_party_data(contract, party, type_):
|
||||
if party.wb:
|
||||
contract.wb = party.wb
|
||||
if party.association:
|
||||
contract.association = party.association
|
||||
|
||||
@staticmethod
|
||||
def _apply_payment_term(contract, party, type_):
|
||||
if type_ == 'Purchase' and party.supplier_payment_term:
|
||||
contract.payment_term = party.supplier_payment_term
|
||||
elif type_ == 'Sale' and party.customer_payment_term:
|
||||
contract.payment_term = party.customer_payment_term
|
||||
|
||||
@staticmethod
|
||||
def _apply_price(line, c, parts):
|
||||
if int(parts[0]) == 0:
|
||||
line.enable_linked_currency = True
|
||||
line.linked_currency = 1
|
||||
line.linked_unit = int(parts[1])
|
||||
line.linked_price = c.price
|
||||
line.unit_price = line.get_price_linked_currency()
|
||||
else:
|
||||
line.unit_price = c.price if c.price else Decimal(0)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# LOT / MATCHING (repris tel quel du wizard)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@classmethod
|
||||
def _create_lot(cls, line, c, ct, type_):
|
||||
pool = Pool()
|
||||
Lot = pool.get('lot.lot')
|
||||
LotQtHist = pool.get('lot.qt.hist')
|
||||
LotQtType = pool.get('lot.qt.type')
|
||||
|
||||
lot = Lot()
|
||||
|
||||
if type_ == 'Purchase':
|
||||
lot.line = line.id
|
||||
else:
|
||||
lot.sale_line = line.id
|
||||
|
||||
lot.lot_qt = None
|
||||
lot.lot_unit = None
|
||||
lot.lot_unit_line = line.unit
|
||||
lot.lot_quantity = round(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 = round(lot.lot_quantity, 5)
|
||||
lqh.gross_quantity = round(lot.lot_quantity, 5)
|
||||
lot.lot_hist = [lqh]
|
||||
|
||||
lot.save()
|
||||
|
||||
vlot = ct.lot
|
||||
shipment_origin = cls._get_shipment_origin(ct)
|
||||
|
||||
qt = c.quantity
|
||||
|
||||
if type_ == 'Purchase':
|
||||
if not lot.updateVirtualPart(qt, shipment_origin, vlot):
|
||||
lot.createVirtualPart(qt, shipment_origin, vlot)
|
||||
|
||||
# Decrease forecasted virtual part non matched
|
||||
lot.updateVirtualPart(-qt, shipment_origin, vlot, 'only sale')
|
||||
else:
|
||||
if not vlot.updateVirtualPart(qt, shipment_origin, lot):
|
||||
vlot.createVirtualPart(qt, shipment_origin, lot)
|
||||
|
||||
# Decrease forecasted virtual part non matched
|
||||
vlot.updateVirtualPart(-qt, shipment_origin, None)
|
||||
|
||||
@staticmethod
|
||||
def _get_shipment_origin(ct):
|
||||
if ct.shipment_in:
|
||||
return 'stock.shipment.in,%s' % ct.shipment_in.id
|
||||
if ct.shipment_internal:
|
||||
return 'stock.shipment.internal,%s' % ct.shipment_internal.id
|
||||
if ct.shipment_out:
|
||||
return 'stock.shipment.out,%s' % ct.shipment_out.id
|
||||
return None
|
||||
Reference in New Issue
Block a user