main #7

Merged
admin merged 620 commits from main into dev 2026-03-29 13:03:25 +00:00
4 changed files with 372 additions and 120 deletions
Showing only changes of commit 4c70f0bc5f - Show all commits

View File

@@ -2,6 +2,8 @@ from trytond.model import ModelSQL, ModelView, fields, Workflow
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval from trytond.pyson import Eval
from trytond.wizard import Button from trytond.wizard import Button
from trytond.transaction import Transaction
from sql import Table
import requests import requests
import io import io
import logging import logging
@@ -203,6 +205,35 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
ShipmentWR.save([swr]) ShipmentWR.save([swr])
doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n" doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n"
t = Table('freight_booking_lots')
cursor = Transaction().connection.cursor()
cursor.execute(*t.select(
t.BOOKING_NUMBER,
t.LOT_NUMBER,
t.LOT_NBR_BALES,
t.LOT_GROSS_WEIGHT,
t.LOT_NET_WEIGHT,
t.LOT_UOM,
t.LOT_QUALITY,
t.CUSTOMER,
t.SELL_PRICE_CURRENCY,
t.SELL_PRICE_UNIT,
t.SALE_INVOICE,
t.SELL_INV_AMOUNT,
t.SALE_INVOICE_DATE,
t.SELL_PREMIUM,
t.SALE_CONTRACT,
where=(t.BOOKING_NUMBER == sh[0].bl_number)
))
rows = cursor.fetchall()
if rows:
#Purchase & Sale creation
Purchase = Pool().get('purchase.purchase')
PurchaseLine = Pool().get('purchase.line')
for row in rows:
#Lots creation
pass
# if cls.rule_set.ocr_required:[] # if cls.rule_set.ocr_required:[]
# cls.run_ocr([doc]) # cls.run_ocr([doc])
# if cls.rule_set.structure_required and doc.state != "error": # if cls.rule_set.structure_required and doc.state != "error":

View File

@@ -29,6 +29,7 @@ from . import (
credit_risk, credit_risk,
valuation, valuation,
weight_report, weight_report,
service,
) )
def register(): def register():

View File

@@ -20,6 +20,7 @@ import datetime
import json import json
import logging import logging
from trytond.exceptions import UserWarning, UserError from trytond.exceptions import UserWarning, UserError
from purchase_trade.service import ContractFactory
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -3142,127 +3143,131 @@ class CreateContracts(Wizard):
} }
def transition_creating(self): def transition_creating(self):
SaleLine = Pool().get('sale.line') ContractFactory.create_contracts(
Sale = Pool().get('sale.sale') self.ct.contracts,
PurchaseLine = Pool().get('purchase.line') type_=self.ct.type,
Purchase = Pool().get('purchase.purchase') ct=self.ct,
LotQt = Pool().get('lot.qt') )
LotQtHist = Pool().get('lot.qt.hist') # SaleLine = Pool().get('sale.line')
LotQtType = Pool().get('lot.qt.type') # Sale = Pool().get('sale.sale')
Lot = Pool().get('lot.lot') # PurchaseLine = Pool().get('purchase.line')
Date = Pool().get('ir.date') # Purchase = Pool().get('purchase.purchase')
self.sale_lines = [] # LotQt = Pool().get('lot.qt')
type = self.ct.type # LotQtHist = Pool().get('lot.qt.hist')
base_contract = self.ct.lot.sale_line.sale if type == 'Purchase' else self.ct.lot.line.purchase # LotQtType = Pool().get('lot.qt.type')
for c in self.ct.contracts: # Lot = Pool().get('lot.lot')
contract = Purchase() if type == 'Purchase' else Sale() # Date = Pool().get('ir.date')
contract_line = PurchaseLine() if type == 'Purchase' else SaleLine() # self.sale_lines = []
parts = c.currency_unit.split("_") # type = self.ct.type
if int(parts[0]) != 0: # base_contract = self.ct.lot.sale_line.sale if type == 'Purchase' else self.ct.lot.line.purchase
contract.currency = int(parts[0]) # for c in self.ct.contracts:
else: # contract = Purchase() if type == 'Purchase' else Sale()
contract.currency = 1 # contract_line = PurchaseLine() if type == 'Purchase' else SaleLine()
contract.party = c.party # parts = c.currency_unit.split("_")
contract.crop = c.crop # if int(parts[0]) != 0:
contract.tol_min = c.tol_min # contract.currency = int(parts[0])
contract.tol_max = c.tol_max # else:
if type == 'Purchase': # contract.currency = 1
contract.purchase_date = Date.today() # contract.party = c.party
else: # contract.crop = c.crop
contract.sale_date = Date.today() # contract.tol_min = c.tol_min
contract.reference = c.reference # contract.tol_max = c.tol_max
if base_contract.from_location and base_contract.to_location: # if type == 'Purchase':
if type == 'Purchase': # contract.purchase_date = Date.today()
contract.to_location = base_contract.from_location # else:
else: # contract.sale_date = Date.today()
contract.from_location = base_contract.to_location # contract.reference = c.reference
if base_contract.from_location.type == 'supplier' and base_contract.to_location.type == 'customer': # if base_contract.from_location and base_contract.to_location:
contract.from_location = base_contract.from_location # if type == 'Purchase':
contract.to_location = base_contract.to_location # contract.to_location = base_contract.from_location
if c.party.wb: # else:
contract.wb = c.party.wb # contract.from_location = base_contract.to_location
if c.party.association: # if base_contract.from_location.type == 'supplier' and base_contract.to_location.type == 'customer':
contract.association = c.party.association # contract.from_location = base_contract.from_location
if type == 'Purchase': # contract.to_location = base_contract.to_location
if c.party.supplier_payment_term: # if c.party.wb:
contract.payment_term = c.party.supplier_payment_term # contract.wb = c.party.wb
else: # if c.party.association:
if c.party.customer_payment_term: # contract.association = c.party.association
contract.payment_term = c.party.customer_payment_term # if type == 'Purchase':
contract.incoterm = c.incoterm # if c.party.supplier_payment_term:
if c.party.addresses: # contract.payment_term = c.party.supplier_payment_term
contract.invoice_address = c.party.addresses[0] # else:
if type == 'Sale': # if c.party.customer_payment_term:
contract.shipment_address = c.party.addresses[0] # contract.payment_term = c.party.customer_payment_term
contract.__class__.save([contract]) # contract.incoterm = c.incoterm
contract_line.quantity = c.quantity # if c.party.addresses:
contract_line.quantity_theorical = c.quantity # contract.invoice_address = c.party.addresses[0]
contract_line.product = self.ct.product # if type == 'Sale':
contract_line.price_type = c.price_type # contract.shipment_address = c.party.addresses[0]
contract_line.unit = self.ct.unit # contract.__class__.save([contract])
if type == 'Purchase': # contract_line.quantity = c.quantity
contract_line.purchase = contract.id # contract_line.quantity_theorical = c.quantity
else: # contract_line.product = self.ct.product
contract_line.sale = contract.id # contract_line.price_type = c.price_type
contract_line.created_by_code = self.ct.matched # contract_line.unit = self.ct.unit
contract_line.premium = Decimal(0) # if type == 'Purchase':
if int(parts[0]) == 0: # contract_line.purchase = contract.id
contract_line.enable_linked_currency = True # else:
contract_line.linked_currency = 1 # contract_line.sale = contract.id
contract_line.linked_unit = int(parts[1]) # contract_line.created_by_code = self.ct.matched
contract_line.linked_price = c.price # contract_line.premium = Decimal(0)
contract_line.unit_price = contract_line.get_price_linked_currency() # if int(parts[0]) == 0:
else: # contract_line.enable_linked_currency = True
contract_line.unit_price = c.price if c.price else Decimal(0) # contract_line.linked_currency = 1
contract_line.del_period = c.del_period # contract_line.linked_unit = int(parts[1])
contract_line.from_del = c.from_del # contract_line.linked_price = c.price
contract_line.to_del = c.to_del # contract_line.unit_price = contract_line.get_price_linked_currency()
contract_line.__class__.save([contract_line]) # else:
logger.info("CREATE_ID:%s",contract.id) # contract_line.unit_price = c.price if c.price else Decimal(0)
logger.info("CREATE_LINE_ID:%s",contract_line.id) # contract_line.del_period = c.del_period
if self.ct.matched: # contract_line.from_del = c.from_del
lot = Lot() # contract_line.to_del = c.to_del
if type == 'Purchase': # contract_line.__class__.save([contract_line])
lot.line = contract_line.id # logger.info("CREATE_ID:%s",contract.id)
else: # logger.info("CREATE_LINE_ID:%s",contract_line.id)
lot.sale_line = contract_line.id # if self.ct.matched:
lot.lot_qt = None # lot = Lot()
lot.lot_unit = None # if type == 'Purchase':
lot.lot_unit_line = contract_line.unit # lot.line = contract_line.id
lot.lot_quantity = round(contract_line.quantity,5) # else:
lot.lot_gross_quantity = None # lot.sale_line = contract_line.id
lot.lot_status = 'forecast' # lot.lot_qt = None
lot.lot_type = 'virtual' # lot.lot_unit = None
lot.lot_product = contract_line.product # lot.lot_unit_line = contract_line.unit
lqtt = LotQtType.search([('sequence','=',1)]) # lot.lot_quantity = round(contract_line.quantity,5)
if lqtt: # lot.lot_gross_quantity = None
lqh = LotQtHist() # lot.lot_status = 'forecast'
lqh.quantity_type = lqtt[0] # lot.lot_type = 'virtual'
lqh.quantity = round(lot.lot_quantity,5) # lot.lot_product = contract_line.product
lqh.gross_quantity = round(lot.lot_quantity,5) # lqtt = LotQtType.search([('sequence','=',1)])
lot.lot_hist = [lqh] # if lqtt:
Lot.save([lot]) # lqh = LotQtHist()
vlot = self.ct.lot # lqh.quantity_type = lqtt[0]
shipment_origin = None # lqh.quantity = round(lot.lot_quantity,5)
if self.ct.shipment_in: # lqh.gross_quantity = round(lot.lot_quantity,5)
shipment_origin = 'stock.shipment.in,' + str(self.ct.shipment_in.id) # lot.lot_hist = [lqh]
elif self.ct.shipment_internal: # Lot.save([lot])
shipment_origin = 'stock.shipment.internal,' + str(self.ct.shipment_internal.id) # vlot = self.ct.lot
elif self.ct.shipment_out: # shipment_origin = None
shipment_origin = 'stock.shipment.out,' + str(self.ct.shipment_out.id) # if self.ct.shipment_in:
# shipment_origin = 'stock.shipment.in,' + str(self.ct.shipment_in.id)
qt = c.quantity # elif self.ct.shipment_internal:
if type == 'Purchase': # shipment_origin = 'stock.shipment.internal,' + str(self.ct.shipment_internal.id)
if not lot.updateVirtualPart(qt,shipment_origin,vlot): # elif self.ct.shipment_out:
lot.createVirtualPart(qt,shipment_origin,vlot) # shipment_origin = 'stock.shipment.out,' + str(self.ct.shipment_out.id)
#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)
# 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)
return 'end' return 'end'

View 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