# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from trytond.i18n import gettext from trytond.model import fields from trytond.modules.product import uom_conversion_digits from trytond.modules.product.exceptions import UOMValidationError from trytond.pool import PoolMeta from trytond.pyson import Bool, Eval class PurchaseSecondaryMixin: __slots__ = () purchase_secondary_uom = fields.Many2One( 'product.uom', "Purchase Secondary UoM", domain=[ ('category', '!=', Eval('default_uom_category', -1)), ], help="The secondary Unit of Measure for purchases.") purchase_secondary_uom_factor = fields.Float( "Purchase Secondary UoM Factor", digits=uom_conversion_digits, states={ 'required': Bool(Eval('purchase_secondary_uom')), 'invisible': ~Eval('purchase_secondary_uom'), }, help="The coefficient for the formula:\n" "1 (purchase unit) = coefficient (secondary unit)") purchase_secondary_uom_rate = fields.Float( "Purchase Secondary UoM Rate", digits=uom_conversion_digits, states={ 'required': Bool(Eval('purchase_secondary_uom')), 'invisible': ~Eval('purchase_secondary_uom'), }, help="The coefficient for the formula:\n" "coefficient (purchase unit) = 1 (secondary unit)") purchase_secondary_uom_category = fields.Function( fields.Many2One( 'product.uom.category', "Purchase Secondary UoM Category"), 'on_change_with_purchase_secondary_uom_category', searcher='search_purchase_secondary_uom_category') @fields.depends('purchase_secondary_uom_factor') def on_change_purchase_secondary_uom_factor(self): if not self.purchase_secondary_uom_factor: self.purchase_secondary_uom_rate = None else: self.purchase_secondary_uom_rate = round( 1. / self.purchase_secondary_uom_factor, uom_conversion_digits[1]) @fields.depends('purchase_secondary_uom_rate') def on_change_purchase_secondary_uom_rate(self): if not self.purchase_secondary_uom_rate: self.purchase_secondary_uom_factor = None else: self.purchase_secondary_uom_factor = round( 1. / self.purchase_secondary_uom_rate, uom_conversion_digits[1]) @fields.depends('purchase_secondary_uom') def on_change_with_purchase_secondary_uom_category(self, name=None): if self.purchase_secondary_uom: return self.purchase_secondary_uom.category @classmethod def search_purchase_secondary_uom_category(cls, name, clause): return [('purchase_secondary_uom.category' + clause[0][len(name):], *clause[1:])] @property def purchase_secondary_uom_normal_rate(self): uom = self.purchase_secondary_uom rate = self.purchase_secondary_uom_rate if self.purchase_uom and rate and uom: if self.purchase_uom.accurate_field == 'factor': rate *= self.purchase_uom.factor else: rate /= self.purchase_uom.rate if uom.accurate_field == 'factor': rate /= uom.factor else: rate *= uom.rate return rate @property def purchase_secondary_uom_normal_factor(self): uom = self.purchase_secondary_uom factor = self.purchase_secondary_uom_factor if self.purchase_uom and factor and uom: if uom.accurate_field == 'factor': factor *= uom.factor else: factor /= uom.rate if self.purchase_uom.accurate_field == 'factor': factor /= self.purchase_uom.factor else: factor *= self.purchase_uom.rate return factor @classmethod def validate_fields(cls, records, field_names): super().validate_fields(records, field_names) cls.check_purchase_secondary_uom_factor_and_rate(records, field_names) @classmethod def check_purchase_secondary_uom_factor_and_rate( cls, records, field_names): if field_names and not (field_names & { 'purchase_secondary_uom_factor', 'purchase_secondary_uom_rate'}): return for record in records: factor = record.purchase_secondary_uom_factor rate = record.purchase_secondary_uom_rate if factor and rate: new_rate = round(1. / factor, uom_conversion_digits[1]) new_factor = round(1. / rate, uom_conversion_digits[1]) if rate != new_rate and factor != new_factor: raise UOMValidationError( gettext('purchase_secondary_unit' '.msg_secondary_uom_incompatible_factor_rate', record=record.rec_ame)) class Template(PurchaseSecondaryMixin, metaclass=PoolMeta): __name__ = 'product.template' @classmethod def __setup__(cls): super().__setup__() cls.purchase_secondary_uom.states = { 'invisible': ~Eval('purchasable', False), } class Product(metaclass=PoolMeta): __name__ = 'product.product' @property def purchase_secondary_uom_normal_rate(self): return self.template.purchase_secondary_uom_normal_rate @property def purchase_secondary_uom_normal_factor(self): return self.template.purchase_secondary_uom_normal_factor class ProductSupplier(PurchaseSecondaryMixin, metaclass=PoolMeta): __name__ = 'purchase.product_supplier' default_uom_category = fields.Function( fields.Many2One( 'product.uom.category', "Default UoM Category", help="The category of the default Unit of Measure."), 'on_change_with_default_uom_category') @fields.depends('template', '_parent_template.purchase_uom', 'product', '_parent_product.purchase_uom') def on_change_with_default_uom_category(self, name=None): if self.product and self.product.purchase_uom: return self.product.purchase_uom.category elif self.template and self.template.purchase_uom: return self.template.purchase_uom.category @property def purchase_uom(self): if self.product and self.product.purchase_uom: return self.product.purchase_uom elif self.template and self.template.purchase_uom: return self.template.purchase_uom