# -*- 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.payment_term = c.payment_term contract.reference = c.reference contract.from_location = c.from_location contract.to_location = c.to_location context = Transaction().context contract.company = context.get('company') if context else None 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_) if type_ == 'Sale': contract.product_origin = getattr(base_contract, 'product_origin', None) 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