#The COPYRIGHT file at the top level of this repository contains the full #copyright notices and license terms. from trytond.model import Workflow, ModelSQL, ModelView, fields from trytond.pyson import Eval, If, In, Bool, Id from trytond.pool import Pool, PoolMeta from trytond.transaction import Transaction from trytond.i18n import gettext from trytond.exceptions import UserError _STATES = { 'readonly': Eval('state') != 'draft', } _DEPENDS = ['state'] class Configuration(metaclass=PoolMeta): __name__ = 'stock.configuration' external_reception_sequence = fields.MultiValue(fields.Many2One( 'ir.sequence', "External Reception Sequence", required=True, domain=[ ('company', 'in', [Eval('context', {}).get('company', -1), None]), ('sequence_type', '=', Id('stock_external_reception', 'sequence_type_external_reception')), ])) @classmethod def multivalue_model(cls, field): pool = Pool() if field == 'external_reception_sequence': return pool.get('stock.configuration.sequence') return super(Configuration, cls).multivalue_model(field) @staticmethod def default_external_reception_sequence(**pattern): pool = Pool() ModelData = pool.get('ir.model.data') return ModelData.get_id('stock_external_reception', 'sequence_external_reception') class ConfigurationSequence(metaclass=PoolMeta): __name__ = 'stock.configuration.sequence' external_reception_sequence = fields.Many2One( 'ir.sequence', "External Reception Sequence", required=True, domain=[ ('company', 'in', [Eval('company', -1), None]), ('sequence_type', '=', Id('stock_external_reception', 'sequence_type_external_reception')), ], depends=['company']) class ExternalReception(Workflow, ModelSQL, ModelView): "External Reception" __name__ = 'stock.external.reception' _rec_name = 'code' code = fields.Char("Code", size=None, readonly=True) company = fields.Many2One('company.company', 'Company', required=True, states=_STATES, depends=_DEPENDS, domain=[ ('id', If(In('company', Eval('context', {})), '=', '!='), Eval('context', {}).get('company', -1)), ]) party = fields.Many2One('party.party', 'Party', required=True, context={ 'company': Eval('company', -1), }, states=_STATES, depends=_DEPENDS + ['company']) reference = fields.Char("Reference", size=None, states=_STATES, depends=_DEPENDS) warehouse = fields.Many2One('stock.location', "Warehouse", required=True, domain=[('type', '=', 'warehouse')], states=_STATES, depends=_DEPENDS) effective_date = fields.Date('Effective Date', required=True, states=_STATES, depends=_DEPENDS) lines = fields.One2Many('stock.external.reception.line', 'reception', 'Lines', states={ 'readonly': ~Eval('state').in_(['draft', 'received']), }, depends=_DEPENDS) notes = fields.Text('Notes') shipments = fields.One2Many('stock.shipment.external', 'reception', 'External Shipments', readonly=True) state = fields.Selection([ ('draft', 'Draft'), ('received', 'Received'), ('done', 'Done'), ], 'State', readonly=True) @classmethod def __setup__(cls): super(ExternalReception, cls).__setup__() cls._order[0] = ('id', 'DESC') cls._transitions |= set(( ('draft', 'received'), ('received', 'draft'), ('received', 'done'), )) cls._buttons.update({ 'draft': { 'invisible': Eval('state') != 'received', 'icon': 'tryton-back', }, 'receive': { 'invisible': Eval('state') != 'draft', 'icon': 'tryton-forward', }, 'done': { 'invisible': Eval('state') != 'received', 'icon': 'tryton-ok', }, }) @staticmethod def default_state(): return 'draft' @staticmethod def default_company(): return Transaction().context.get('company') @classmethod def default_warehouse(cls): Location = Pool().get('stock.location') locations = Location.search(cls.warehouse.domain) if len(locations) == 1: return locations[0].id @staticmethod def default_effective_date(): pool = Pool() Date = pool.get('ir.date') return Date.today() @classmethod @ModelView.button @Workflow.transition('draft') def draft(cls, receptions): pass @classmethod @ModelView.button @Workflow.transition('received') def receive(cls, receptions): pass @classmethod @ModelView.button @Workflow.transition('done') def done(cls, receptions): pool = Pool() Shipment = pool.get('stock.shipment.external') to_create = [] for reception in receptions: shipment = reception._get_shipment() shipment.reception = reception moves = [] for line in reception.lines: if not line.product: raise UserError( gettext('stock_external_reception.missing_product', line=line.rec_name, shipment=reception.rec_name)) move = line._get_move() move.to_location = shipment.to_location move.from_location = shipment.from_location if move.on_change_with_unit_price_required(): move.unit_price = line.product.cost_price move.currency = line.reception.company.currency moves.append(move._save_values) shipment.moves = moves vals = shipment._save_values vals['moves'] = [('create', moves)] to_create.append(vals) if to_create: with Transaction().set_user(0): shipments = Shipment.create(to_create) Shipment.wait(shipments) Shipment.assign_force(shipments) Shipment.done(shipments) def _get_shipment(self): 'Return the shipment to be generated by the external reception' pool = Pool() Shipment = pool.get('stock.shipment.external') shipment = Shipment() shipment.company = self.company shipment.effective_date = self.effective_date shipment.party = self.party shipment.reference = self.rec_name shipment.from_location = self.party.customer_location shipment.to_location = self.warehouse.storage_location return shipment @classmethod def create(cls, vlist): pool = Pool() Config = pool.get('stock.configuration') vlist = [x.copy() for x in vlist] config = Config(1) for values in vlist: values['code'] = config.external_reception_sequence.get() shipments = super(ExternalReception, cls).create(vlist) return shipments @classmethod def copy(cls, receptions, default=None): if default is None: default = {} else: default = default.copy() default['shipments'] = None return super(ExternalReception, cls).copy(receptions, default=default) class ExternalReceptionLine(ModelSQL, ModelView): "External Reception Line" __name__ = 'stock.external.reception.line' _rec_name = 'description' reception = fields.Many2One('stock.external.reception', 'Reception', ondelete='CASCADE') sequence = fields.Integer('Sequence') description = fields.Text('Description', required=True) product = fields.Many2One('product.product', 'Product') product_uom_category = fields.Function( fields.Many2One('product.uom.category', 'Product Uom Category'), 'on_change_with_product_uom_category') quantity = fields.Float('Quantity', digits='unit') unit = fields.Many2One('product.uom', 'Unit', states={ 'required': Bool(Eval('product', 0)), }, domain=[ If(Bool(Eval('product_uom_category')), ('category', '=', Eval('product_uom_category')), ('category', '!=', -1)), ], depends=['product_uom_category', 'product']) notes = fields.Text('Notes') @fields.depends('product', 'unit', 'description') def on_change_product(self): if self.product: category = self.product.default_uom.category if not self.unit or self.unit not in category.uoms: self.unit = self.product.default_uom if not self.description: self.description = self.product.rec_name @fields.depends('product') def on_change_with_product_uom_category(self, name=None): if self.product: return self.product.default_uom_category.id def _get_move(self): 'Return the move to be generated by the external line' pool = Pool() Move = pool.get('stock.move') move = Move() move.product = self.product move.quantity = self.quantity move.unit = self.unit move.company = self.reception.company move.effective_date = self.reception.effective_date move.state = 'draft' return move class ShipmentExternal(metaclass=PoolMeta): __name__ = 'stock.shipment.external' reception = fields.Many2One('stock.external.reception', 'External Reception', readonly=True)