This commit is contained in:
2026-01-26 12:42:49 +01:00
parent ef27c020e0
commit 4c70f0bc5f
4 changed files with 372 additions and 120 deletions

View File

@@ -2,6 +2,8 @@ from trytond.model import ModelSQL, ModelView, fields, Workflow
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.wizard import Button
from trytond.transaction import Transaction
from sql import Table
import requests
import io
import logging
@@ -203,6 +205,35 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
ShipmentWR.save([swr])
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:[]
# cls.run_ocr([doc])
# if cls.rule_set.structure_required and doc.state != "error":

View File

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

View File

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

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