# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import datetime from decimal import Decimal from sql import Null from trytond.model import ( ModelSQL, ModelView, DeactivableMixin, MatchMixin, fields) from trytond.pyson import Eval, If, Bool from trytond.pool import Pool from trytond.modules.product import price_digits # Use 2 chars numbering to allow string comparison MONTHS = [ (None, ''), ('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December'), ] class TariffCode(DeactivableMixin, ModelSQL, ModelView, MatchMixin): 'Tariff Code' __name__ = 'customs.tariff.code' _rec_name = 'code' code = fields.Char('Code', required=True, help='The code from Harmonized System of Nomenclature.') description = fields.Char('Description', translate=True) country = fields.Many2One('country.country', 'Country') # TODO country group start_month = fields.Selection(MONTHS, 'Start Month', sort=False, states={ 'required': Eval('end_month') | Eval('start_day'), }, depends=['end_month', 'start_day']) start_day = fields.Integer('Start Day', domain=['OR', ('start_day', '<=', If(Eval('start_month').in_( ['01', '03', '05', '07', '08', '10', '12']), 31, If(Eval('start_month') == '02', 29, 30))), ('start_day', '=', None), ], states={ 'required': Bool(Eval('start_month')), }, depends=['start_month']) end_month = fields.Selection(MONTHS, 'End Month', sort=False, states={ 'required': Eval('start_month') | Eval('end_day'), }, depends=['start_month', 'end_day']) end_day = fields.Integer('End Day', domain=['OR', ('end_day', '<=', If(Eval('end_month').in_( ['01', '03', '05', '07', '08', '10', '12']), 31, If(Eval('end_month') == '02', 29, 30))), ('end_day', '=', None), ], states={ 'required': Bool(Eval('end_month')), }, depends=['end_month']) duty_rates = fields.One2Many('customs.duty.rate', 'tariff_code', 'Duty Rates') @classmethod def __setup__(cls): super(TariffCode, cls).__setup__() cls._order.insert(0, ('code', 'ASC')) def match(self, pattern): if 'date' in pattern: pattern = pattern.copy() date = pattern.pop('date') if self.start_month and self.end_month: start = (int(self.start_month), self.start_day) end = (int(self.end_month), self.end_day) date = (date.month, date.day) if start <= end: if not (start <= date <= end): return False else: if end <= date <= start: return False return super(TariffCode, self).match(pattern) def get_duty_rate(self, pattern): for rate in self.duty_rates: if rate.match(pattern): return rate class DutyRate(ModelSQL, ModelView, MatchMixin): 'Duty Rate' __name__ = 'customs.duty.rate' tariff_code = fields.Many2One('customs.tariff.code', 'Tariff Code', required=True, select=True) country = fields.Many2One('country.country', 'Country') # TODO country group type = fields.Selection([ ('import', 'Import'), ('export', 'Export'), ], 'Type') start_date = fields.Date('Start Date', domain=['OR', ('start_date', '<=', If(Bool(Eval('end_date')), Eval('end_date', datetime.date.max), datetime.date.max)), ('start_date', '=', None), ], depends=['end_date']) end_date = fields.Date('End Date', domain=['OR', ('end_date', '>=', If(Bool(Eval('start_date')), Eval('start_date', datetime.date.min), datetime.date.min)), ('end_date', '=', None), ], depends=['start_date']) computation_type = fields.Selection([ ('amount', 'Amount'), ('quantity', 'Quantity'), ], 'Computation Type') amount = fields.Numeric('Amount', digits=price_digits, states={ 'required': Eval('computation_type').in_(['amount', 'quantity']), 'invisible': ~Eval('computation_type').in_(['amount', 'quantity']), }, depends=['computation_type']) currency = fields.Many2One('currency.currency', 'Currency', states={ 'required': Eval('computation_type').in_(['amount', 'quantity']), 'invisible': ~Eval('computation_type').in_(['amount', 'quantity']), }, depends=['computation_type']) uom = fields.Many2One('product.uom', 'Uom', states={ 'required': Eval('computation_type') == 'quantity', 'invisible': Eval('computation_type') != 'quantity', }, depends=['computation_type']) @classmethod def __setup__(cls): super(DutyRate, cls).__setup__() cls._order.insert(0, ('start_date', 'ASC')) cls._order.insert(0, ('end_date', 'ASC')) @classmethod def default_type(cls): return 'import' @staticmethod def order_start_date(tables): table, _ = tables[None] return [table.start_date == Null, table.start_date] @staticmethod def order_end_date(tables): table, _ = tables[None] return [table.end_date == Null, table.end_date] def match(self, pattern): if 'date' in pattern: pattern = pattern.copy() start = self.start_date or datetime.date.min end = self.end_date or datetime.date.max if not (start <= pattern.pop('date') <= end): return False return super(DutyRate, self).match(pattern) def compute(self, currency, quantity, uom, **kwargs): return getattr(self, 'compute_%s' % self.computation_type)( currency, quantity, uom, **kwargs) def compute_amount(self, currency, quantity, uom, **kwargs): pool = Pool() Currency = pool.get('currency.currency') return Currency.compute(self.currency, self.amount, currency) def compute_quantity(self, currency, quantity, uom, **kwargs): pool = Pool() Currency = pool.get('currency.currency') Uom = pool.get('product.uom') amount = Uom.compute_price(self.uom, self.amount, uom) amount *= Decimal(str(quantity)) return Currency.compute(self.currency, amount, currency)