from functools import wraps from trytond.model import fields from trytond.report import Report from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, Id, If from trytond.model import (ModelSQL, ModelView) from trytond.tools import is_full_text, lstrip_wildcard from trytond.transaction import Transaction, inactive_records from decimal import getcontext, Decimal, ROUND_UP, ROUND_HALF_UP from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr from sql.conditionals import Case from sql import Column, Literal from sql.functions import CurrentTimestamp, DateTrunc from trytond.wizard import Button, StateTransition, StateView, Wizard from itertools import chain, groupby from operator import itemgetter import datetime import logging from collections import defaultdict from trytond.exceptions import UserWarning, UserError from trytond.modules.account.exceptions import PeriodNotFoundError from trytond.modules.purchase_trade.finance_tools import InterestCalculator logger = logging.getLogger(__name__) def filter_state(state): def filter(func): @wraps(func) def wrapper(cls, fees): fees = [f for f in fees if f.state == state] return func(cls, fees) return wrapper return filter class Fee(ModelSQL,ModelView): "Fee" __name__ = 'fee.fee' line = fields.Many2One('purchase.line',"Line") shipment_in = fields.Many2One('stock.shipment.in') shipment_out = fields.Many2One('stock.shipment.out') shipment_internal = fields.Many2One('stock.shipment.internal') currency = fields.Many2One('currency.currency',"Currency") supplier = fields.Many2One('party.party',"Supplier", required=True) type = fields.Selection([ ('budgeted', 'Budgeted'), ('ordered', 'Ordered'), ('actual', 'Actual'), ], "Type", required=True) p_r = fields.Selection([ ('pay', 'PAY'), ('rec', 'REC'), ], "P/R", required=True) product = fields.Many2One('product.product',"Product", required=True, domain=[('type', '=', 'service')]) price = fields.Numeric("Price",digits=(1,4)) mode = fields.Selection([ ('lumpsum', 'Lump sum'), ('perqt', 'Per qt'), ('pprice', '% price'), ('rate', '% rate'), ('pcost', '% cost price'), ('ppack', 'Per packing'), ], 'Mode', required=True) auto_calculation = fields.Boolean("Auto",states={'readonly': (Eval('mode') != 'ppack')}) inherit_qt = fields.Boolean("Inh Qt",states={'readonly': Eval('mode') != 'ppack'}) quantity = fields.Numeric("Qt",digits='unit',states={'readonly': (Eval('mode') != 'ppack') | Bool(Eval('auto_calculation'))}) unit = fields.Many2One('product.uom',"Unit",domain=[ If(Eval('mode') == 'ppack', ('category', '=', Eval('packing_category')), ()), ], states={ 'readonly': (Bool(Eval('mode') != 'ppack') & Bool(Eval('mode') != 'perqt')), }, depends=['mode', 'packing_category']) packing_category = fields.Function(fields.Many2One('product.uom.category',"Packing Category"),'on_change_with_packing_category') inherit_shipment = fields.Boolean("Inh Sh",states={ 'invisible': (Eval('shipment_in')), }) purchase = fields.Many2One('purchase.purchase',"Purchase", ondelete='CASCADE') qt_state = fields.Many2One('lot.qt.type',"Qt State") amount = fields.Function(fields.Numeric("Amount", digits='currency'),'get_amount') fee_lots = fields.Function(fields.Many2Many('lot.lot', None, None, "Lots"),'get_lots')#, searcher='search_lots') lots = fields.Many2Many('fee.lots', 'fee', 'lot',"Lots",domain=[('id', 'in', Eval('fee_lots',-1))] ) lots_cp = fields.Integer("Lots number") state = fields.Selection([ ('not invoiced', 'Not invoiced'), ('invoiced', 'Invoiced'), ], string='State', readonly=True) fee_landed_cost = fields.Function(fields.Boolean("Inventory"),'get_landed_status') inv = fields.Function(fields.Many2One('account.invoice',"Invoice"),'get_invoice') weight_type = fields.Selection([ ('net', 'Net'), ('brut', 'Gross'), ], string='W. type') fee_date = fields.Date("Date") @classmethod def default_fee_date(cls): Date = Pool().get('ir.date') return Date.today() @classmethod def default_qt_state(cls): LotQtType = Pool().get('lot.qt.type') lqt = LotQtType.search([('name','=','BL')]) if lqt: return lqt[0].id @fields.depends('mode','unit') def on_change_with_packing_category(self, name=None): UnitCategory = Pool().get('product.uom.category') packing = UnitCategory.search(['name','=','Packing']) if packing: return packing[0] @fields.depends('line','sale_line','shipment_in','lots','price','unit','auto_calculation','mode','_parent_line.unit','_parent_line.lots','_parent_sale_line.unit','_parent_sale_line.lots','_parent_shipment_in.id') def on_change_with_quantity(self, name=None): qt = None unit = None line = self.line logger.info("ON_CHANGE_WITH_LINE:%s",line) if not line: line = self.sale_line if line: if line.lots: qt = sum([e.get_current_quantity_converted(0,self.unit) for e in line.lots]) qt_ = sum([e.get_current_quantity_converted(0) for e in line.lots]) unit = line.lots[0].lot_unit logger.info("ON_CHANGE_WITH_QT0:%s",qt) logger.info("ON_CHANGE_WITH_SI:%s",self.shipment_in) if self.shipment_in: Lot = Pool().get('lot.lot') lots = Lot.search([('lot_shipment_in','=',self.shipment_in.id)]) logger.info("ON_CHANGE_WITH_LOTS:%s",lots) if lots: qt = sum([e.get_current_quantity_converted(0,self.unit) for e in lots]) qt_ = sum([e.get_current_quantity_converted(0) for e in lots]) unit = lots[0].lot_unit if not qt: logger.info("ON_CHANGE_WITH_QT1:%s",qt) LotQt = Pool().get('lot.qt') if self.shipment_in: lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) if lqts: qt = Decimal(lqts[0].lot_quantity) qt_ = qt unit = lqts[0].lot_unit logger.info("ON_CHANGE_WITH_QT2:%s",qt) if self.mode != 'ppack': return qt else: if self.auto_calculation: logger.info("AUTOCALCULATION:%s",qt) logger.info("AUTOCALCULATION2:%s",qt_) logger.info("AUTOCALCULATION3:%s",Decimal(unit.factor)) logger.info("AUTOCALCULATION4:%s",Decimal(self.unit.factor)) return (qt_ * Decimal(unit.factor) / Decimal(self.unit.factor)).to_integral_value(rounding=ROUND_UP) @fields.depends('price','mode','_parent_line.lots','_parent_sale_line.lots','shipment_in') def on_change_with_unit(self, name=None): if self.mode != 'ppack' and self.mode != 'perqt': line = self.line if not line: line = self.sale_line if line: if line.lots: if len(line.lots) == 1: return line.lots[0].lot_unit_line else: return line.lots[1].lot_unit_line if self.shipment_in: Lot = Pool().get('lot.lot') lots = Lot.search([('lot_shipment_in','=',self.shipment_in.id)]) logger.info("ON_CHANGE_WITH_UNIT:%s",lots) if lots: return lots[0].lot_unit_line else: return self.unit def get_lots(self, name): logger.info("GET_LOTS_LINE:%s",self.line) logger.info("GET_LOTS_SHIPMENT_IN:%s",self.shipment_in) Lot = Pool().get('lot.lot') if self.line: return self.line.lots if self.shipment_in: lots = Lot.search([('lot_shipment_in','=',self.shipment_in.id)]) logger.info("LOTSDOMAIN:%s",lots) if lots: return lots + [lots[0].getVlot_p()] if self.shipment_internal: return Lot.search([('lot_shipment_internal','=',self.shipment_internal.id)]) if self.shipment_out: return Lot.search([('lot_shipment_out','=',self.shipment_out.id)]) return Lot.search(['id','>',0]) def get_cog(self,lot): MoveLine = Pool().get('account.move.line') Currency = Pool().get('currency.currency') Date = Pool().get('ir.date') AccountConfiguration = Pool().get('account.configuration') account_configuration = AccountConfiguration(1) Uom = Pool().get('product.uom') ml = MoveLine.search([ ('lot', '=', lot.id), ('fee', '=', self.id), ('account', '=', self.product.account_stock_in_used.id), ('origin', 'ilike', '%stock.move%'), ]) logger.info("GET_COG_FEE:%s",ml) if ml: return round(Decimal(sum([e.credit-e.debit for e in ml if e.description != 'Delivery fee'])),2) def get_non_cog(self,lot): MoveLine = Pool().get('account.move.line') Currency = Pool().get('currency.currency') Date = Pool().get('ir.date') AccountConfiguration = Pool().get('account.configuration') account_configuration = AccountConfiguration(1) Uom = Pool().get('product.uom') ml = MoveLine.search([ ('lot', '=', lot.id), ('fee', '=', self.id), ('account', '=', self.product.account_stock_in_used.id), ]) logger.info("GET_NON_COG_FEE:%s",ml) if ml: return round(Decimal(sum([e.credit-e.debit for e in ml])),2) @classmethod def __setup__(cls): super().__setup__() cls._buttons.update({ 'invoice': { 'invisible': (Eval('state') == 'invoiced'), 'depends': ['state'], }, }) @classmethod def default_state(cls): return 'not invoiced' @classmethod def default_p_r(cls): return 'pay' def get_unit(self, name=None): FeeLots = Pool().get('fee.lots') fl = FeeLots.search(['fee','=',self.id]) if fl: if fl[0].lot.line: return fl[0].lot.line.unit if fl[0].lot.sale_line: return fl[0].lot.sale_line.unit @classmethod @ModelView.button @filter_state('not invoiced') def invoice(cls, fees): Purchase = Pool().get('purchase.purchase') FeeLots = Pool().get('fee.lots') for fee in fees: if fee.purchase: fl = FeeLots.search([('fee','=',fee.id)]) logger.info("PROCESS_FROM_FEE:%s",fl) Purchase._process_invoice([fee.purchase],[e.lot for e in fl],'service') cls.write(fees, {'state': 'invoiced',}) @classmethod def default_type(cls): return 'budgeted' @classmethod def default_weight_type(cls): return 'brut' def get_price_per_qt(self): price = Decimal(0) if self.mode == 'lumpsum': if self.quantity: if self.quantity > 0: return round(self.price / self.quantity,4) elif self.mode == 'perqt': return self.price elif self.mode == 'ppack': unit = self.get_unit() if unit and self.unit: return round(self.price / Decimal(self.unit.factor) * Decimal(unit.factor),4) elif self.mode == 'pprice' or self.mode == 'pcost': if self.line and self.price: return round(self.price * Decimal(self.line.unit_price) / 100,4) if self.sale_line and self.price: return round(self.price * Decimal(self.sale_line.unit_price) / 100,4) if self.shipment_in: StockMove = Pool().get('stock.move') sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) if sm: if sm[0].lot: return round(self.price * Decimal(sm[0].lot.get_lot_price()) / 100,4) return price def get_invoice(self,name): if self.purchase: if self.purchase.invoices: return self.purchase.invoices[0] def get_landed_status(self,name): if self.product: return self.product.template.landed_cost def get_quantity(self,name=None): qt = self.get_fee_lots_qt() if qt: return qt LotQt = Pool().get('lot.qt') lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) if lqts: return Decimal(lqts[0].lot_quantity) def get_amount(self,name=None): Date = Pool().get('ir.date') sign = Decimal(1) if self.price: # if self.p_r: # if self.p_r == 'pay': # sign = -1 if self.mode == 'lumpsum': return self.price * sign elif self.mode == 'ppack': return round(self.price * self.quantity,2) elif self.mode == 'rate': #take period with estimated trigger date if self.line: if self.line.estimated_date: beg_date = self.fee_date if self.fee_date else Date.today() est_lines = [dd for dd in self.line.estimated_date if dd.trigger == 'bldate'] est_line = est_lines[0] if est_lines else None if est_line and est_line.estimated_date: est_date = est_line.estimated_date + datetime.timedelta( days=est_line.fin_int_delta or 0 ) if est_date and beg_date: factor = InterestCalculator.calculate( start_date=beg_date, end_date=est_date, rate=self.price/100, rate_type='annual', convention='ACT/360', compounding='simple' ) return round(factor * self.line.unit_price * (self.quantity if self.quantity else 0) * sign,2) if self.sale_line: if self.sale_line.sale.payment_term: beg_date = self.fee_date if self.fee_date else Date.today() est_date = self.sale_line.sale.payment_term.lines[0].get_date(beg_date,self.sale_line) logger.info("EST_DATE:%s",est_date) if est_date and beg_date: factor = InterestCalculator.calculate( start_date=beg_date, end_date=est_date, rate=self.price/100, rate_type='annual', convention='ACT/360', compounding='simple' ) logger.info("FACTOR:%s",factor) return round(factor * self.sale_line.unit_price * (self.quantity if self.quantity else 0) * sign,2) elif self.mode == 'perqt': if self.shipment_in: StockMove = Pool().get('stock.move') sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) if sm: unique_lots = {e.lot for e in sm if e.lot} return round(self.price * Decimal(sum([e.get_current_quantity_converted(0,self.unit) for e in unique_lots])) * sign,2) LotQt = Pool().get('lot.qt') lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) if lqts: return round(self.price * Decimal(lqts[0].lot_quantity) * sign,2) return round((self.quantity if self.quantity else 0) * self.price * sign,2) elif self.mode == 'pprice': if self.line: return round(self.price / 100 * self.line.unit_price * (self.quantity if self.quantity else 0) * sign,2) if self.sale_line: return round(self.price / 100 * self.sale_line.unit_price * (self.quantity if self.quantity else 0) * sign,2) if self.shipment_in: StockMove = Pool().get('stock.move') sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)]) if sm: if sm[0].lot: return round(self.price * Decimal(sum([e.lot.get_lot_price() for e in sm if e.lot])) / 100 * self.quantity * sign,2) LotQt = Pool().get('lot.qt') lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id]) if lqts: return round(self.price * Decimal(lqts[0].lot_p.get_lot_price()) / 100 * lqts[0].lot_quantity * sign,2) @classmethod def write(cls, *args): super().write(*args) fees = sum(args[::2], []) for fee in fees: fee.adjust_purchase_values() @classmethod def copy(cls, fees, default=None): if default is None: default = {} else: default = default.copy() # Important : on vide le champ 'lots' default.setdefault('lots', []) return super().copy(fees, default=default) def get_fee_lots_qt(self,state_id=0): qt = Decimal(0) FeeLots = Pool().get('fee.lots') fee_lots = FeeLots.search([('fee', '=', self.id)]) if fee_lots: qt = sum([e.lot.get_current_quantity_converted(state_id,self.unit) for e in fee_lots]) logger.info("GET_FEE_LOTS_QT:%s",qt) return qt def adjust_purchase_values(self): Purchase = Pool().get('purchase.purchase') PurchaseLine = Pool().get('purchase.line') logger.info("ADJUST_PURCHASE_VALUES:%s",self) if self.type == 'ordered' and self.state == 'not invoiced' and self.purchase: logger.info("ADJUST_PURCHASE_VALUES_QT:%s",self.purchase.lines[0].quantity) if self.mode == 'lumpsum': if self.amount != self.purchase.lines[0].unit_price: self.purchase.lines[0].unit_price = self.amount elif self.mode == 'ppack': if self.amount != self.purchase.lines[0].amount: self.purchase.lines[0].unit_price = self.price self.purchase.lines[0].quantity = self.quantity else: if self.get_price_per_qt() != self.purchase.lines[0].unit_price: self.purchase.lines[0].unit_price = self.get_price_per_qt() if self.quantity != self.purchase.lines[0].quantity: self.purchase.lines[0].quantity = self.quantity if self.product != self.purchase.lines[0].product: self.purchase.lines[0].product = self.product PurchaseLine.save([self.purchase.lines[0]]) if self.supplier != self.purchase.party: self.purchase.party = self.supplier if self.currency != self.purchase.currency: self.purchase.currency = self.currency Purchase.save([self.purchase]) # @classmethod # def validate(cls, fees): # super(Fee, cls).validate(fees) @classmethod def create(cls, vlist): vlist = [x.copy() for x in vlist] fees = super(Fee, cls).create(vlist) qt_sh = Decimal(0) qt_line = Decimal(0) unit = None for fee in fees: FeeLots = Pool().get('fee.lots') Lots = Pool().get('lot.lot') LotQt = Pool().get('lot.qt') if fee.line: for l in fee.line.lots: if (l.lot_type == 'virtual' and len(fee.line.lots)==1) or (l.lot_type == 'physic' and len(fee.line.lots)>1): fl = FeeLots() fl.fee = fee.id fl.lot = l.id fl.line = l.line.id FeeLots.save([fl]) qt_line += l.get_current_quantity_converted() unit = l.line.unit if fee.sale_line: for l in fee.sale_line.lots: if (l.lot_type == 'virtual' and len(fee.sale_line.lots)==1) or (l.lot_type == 'physic' and len(fee.sale_line.lots)>1): fl = FeeLots() fl.fee = fee.id fl.lot = l.id fl.sale_line = l.sale_line.id FeeLots.save([fl]) qt_line += l.get_current_quantity_converted() unit = l.sale_line.unit if fee.shipment_in: if fee.shipment_in.state == 'draft'or fee.shipment_in.state == 'started': lots = Lots.search(['lot_shipment_in','=',fee.shipment_in.id]) if lots: for l in lots: fl = FeeLots() fl.fee = fee.id fl.lot = l.id FeeLots.save([fl]) qt_sh += l.get_current_quantity_converted() unit = l.line.unit else: lqts = LotQt.search(['lot_shipment_in','=',fee.shipment_in.id]) if lqts: for l in lqts: qt_sh += l.lot_p.get_current_quantity_converted() unit = l.lot_p.line.unit else: raise UserError("You cannot add fee on received shipment!") type = fee.type if type == 'ordered': Purchase = Pool().get('purchase.purchase') PurchaseLine = Pool().get('purchase.line') pl = PurchaseLine() pl.product = fee.product if fee.line or fee.sale_line: pl.quantity = round(qt_line,5) if fee.shipment_in: pl.quantity = round(qt_sh,5) logger.info("CREATE_PURHCASE_FOR_FEE_QT:%s",pl.quantity) pl.unit = unit pl.fee_ = fee.id if fee.price: fee_price = fee.get_price_per_qt() logger.info("GET_FEE_PRICE_PER_QT:%s",fee_price) pl.unit_price = round(Decimal(fee_price),4) if fee.mode == 'lumpsum': pl.quantity = 1 pl.unit_price = round(Decimal(fee.amount),4) elif fee.mode == 'ppack': pl.unit_price = fee.price p = Purchase() p.lines = [pl] p.party = fee.supplier if p.party.addresses: p.invoice_address = p.party.addresses[0] p.currency = fee.currency p.line_type = 'service' p.from_location = fee.shipment_in.from_location if fee.shipment_in else (fee.line.purchase.from_location if fee.line else fee.sale_line.sale.from_location) p.to_location = fee.shipment_in.to_location if fee.shipment_in else (fee.line.purchase.to_location if fee.line else fee.sale_line.sale.to_location) if fee.shipment_in and fee.shipment_in.lotqt: p.payment_term = fee.shipment_in.lotqt[0].lot_p.line.purchase.payment_term elif fee.line: p.payment_term = fee.line.purchase.payment_term elif fee.sale_line: p.payment_term = fee.sale_line.sale.payment_term Purchase.save([p]) #if reception of moves done we need to generate accrual for fee if not fee.sale_line: feelots = FeeLots.search(['fee','=',fee.id]) for fl in feelots: if fee.product.template.landed_cost: move = fl.lot.get_received_move() if move: Warning = Pool().get('res.user.warning') warning_name = Warning.format("Lot ever received", []) if Warning.check(warning_name): raise UserWarning(warning_name, "By clicking yes, an accrual for this fee will be created") AccountMove = Pool().get('account.move') account_move = move._get_account_stock_move_fee(fee) AccountMove.save([account_move]) else: AccountMove = Pool().get('account.move') account_move = fee._get_account_move_fee(fl.lot) AccountMove.save([account_move]) return fees def _get_account_move_fee(self,lot,in_out='in',amt = None): pool = Pool() AccountMove = pool.get('account.move') Date = pool.get('ir.date') Period = pool.get('account.period') AccountConfiguration = pool.get('account.configuration') if self.product.type != 'service': return today = Date.today() company = lot.line.purchase.company if lot.line else lot.sale_line.sale.company for date in [today]: try: period = Period.find(company, date=date, test_state=False) except PeriodNotFoundError: if date < today: return continue break else: return if period.state != 'open': date = today period = Period.find(company, date=date) AccountMoveLine = pool.get('account.move.line') Currency = pool.get('currency.currency') move_line = AccountMoveLine() move_line.lot = lot move_line.fee = self move_line.origin = None move_line_ = AccountMoveLine() move_line_.lot = lot move_line_.fee = self move_line_.origin = None amount = amt if amt else self.amount if self.currency != company.currency: with Transaction().set_context(date=today): amount_converted = amount amount = Currency.compute(self.currency, amount, company.currency) move_line.second_currency = self.currency if self.p_r == 'pay': move_line.debit = amount move_line.credit = Decimal(0) move_line.account = self.product.account_stock_used if in_out == 'in' else self.product.account_cogs_used if hasattr(move_line, 'second_currency') and move_line.second_currency: move_line.amount_second_currency = amount_converted move_line_.debit = Decimal(0) move_line_.credit = amount move_line_.account = self.product.account_stock_in_used if in_out == 'in' else self.product.account_stock_out_used if hasattr(move_line_, 'second_currency') and move_line_.second_currency: move_line_.amount_second_currency = -amount_converted else: move_line.debit = Decimal(0) move_line.credit = amount move_line.account = self.product.account_stock_used if in_out == 'in' else self.product.account_cogs_used if hasattr(move_line, 'second_currency') and move_line.second_currency: move_line.amount_second_currency = -amount_converted move_line_.debit = amount move_line_.credit = Decimal(0) move_line_.account = self.product.account_stock_in_used if in_out == 'in' else self.product.account_stock_out_used if hasattr(move_line_, 'second_currency') and move_line_.second_currency: move_line_.amount_second_currency = amount_converted logger.info("FEE_MOVELINES_1:%s",move_line) logger.info("FEE_MOVELINES_2:%s",move_line_) AccountJournal = Pool().get('account.journal') journal = AccountJournal.search(['type','=','expense']) if journal: journal = journal[0] description = None description = 'Fee' return AccountMove( journal=journal, period=period, date=date, origin=None, description=description, lines=[move_line,move_line_], ) class FeeLots(ModelSQL,ModelView): "Fee lots" __name__ = 'fee.lots' fee = fields.Many2One('fee.fee',"Fee",required=True, ondelete='CASCADE') lot = fields.Many2One('lot.lot',"Lot",required=True, ondelete='CASCADE') class FeeReport( ModelSQL, ModelView): "Fee Report" __name__ = 'fee.report' r_purchase_line = fields.Many2One('purchase.line', "Purchase line") r_sale_line = fields.Many2One('sale.line', "Sale line") r_shipment_in = fields.Many2One('stock.shipment.in', "Shipment in") r_shipment_out = fields.Many2One('stock.shipment.out', "Shipment out") r_shipment_internal = fields.Many2One('stock.shipment.internal', "Shipment internal") r_fee_type = fields.Many2One('product.product', 'Fee type') r_fee_counterparty = fields.Many2One('party.party', "Counterparty", required=True) r_type = fields.Selection([ ('ordered', 'Ordered'), ('budgeted', 'Budgeted'), ('actual', 'Actual') ], 'Type') r_fee_paystatus = fields.Selection([ ('pay', 'PAY'), ('rec', 'REC') ], 'Pay status') r_mode = fields.Selection([ ('lumpsum', 'Lump sum'), ('perqt', 'Per qt'), ('pprice', '% price'), ('pcost', '% cost price'), ], 'Mode', required=True) r_fee_quantity = fields.Function(fields.Numeric("Qt",digits=(1,4)),'get_quantity') r_fee_unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit') r_purchase = fields.Many2One('purchase.purchase',"Purchase", ondelete='CASCADE') r_fee_amount = fields.Function(fields.Numeric("Amount", digits=(1,4)),'get_amount') r_inv = fields.Function(fields.Many2One('account.invoice',"Invoice"),'get_invoice') r_state = fields.Selection([ ('not invoiced', 'Not invoiced'), ('invoiced', 'Invoiced'), ], string='State', readonly=True) #r_fee_lots = fields.Function(fields.Many2Many('lot.lot', None, None, "Lots"),'get_lots')#, searcher='search_lots') #r_lots = fields.Many2Many('fee.lots', 'fee', 'lot',"Lots",domain=[('id', 'in', Eval('r_fee_lots',[]))] ) r_fee_currency = fields.Many2One('currency.currency',"Currency") r_fee_price = fields.Numeric("Price",digits=(1,4)) r_shipment_origin = fields.Function( fields.Reference( selection=[ ("stock.shipment.in", "In"), ("stock.shipment.out", "Out"), ("stock.shipment.internal", "Internal"), ], string="Shipment", ), "get_shipment_origin", ) def get_invoice(self,name): if self.r_purchase: if self.r_purchase.invoices: return self.r_purchase.invoices[0] def get_shipment_origin(self, name): if self.r_shipment_in: return 'stock.shipment.in,' + str(self.r_shipment_in.id) elif self.r_shipment_out: return 'stock.shipment.out,' + str(self.r_shipment_out.id) elif self.r_shipment_internal: return 'stock.shipment.internal,' + str(self.r_shipment_internal.id) return None # def get_lots(self, name): # if self.r_purchase_line: # return self.r_purchase_line.lots def get_unit(self, name): if self.r_purchase_line: return self.r_purchase_line.unit def get_quantity(self,name=None): Fee = Pool().get('fee.fee') fee = Fee(self.id) return fee.get_quantity() def get_amount(self,name=None): Fee = Pool().get('fee.fee') fee = Fee(self.id) return fee.get_amount() @classmethod def table_query(cls): FeeReport = Pool().get('fee.fee') fr = FeeReport.__table__() Purchase = Pool().get('purchase.purchase') pu = Purchase.__table__() PurchaseLine = Pool().get('purchase.line') pl = PurchaseLine.__table__() Sale = Pool().get('sale.sale') sa = Sale.__table__() SaleLine = Pool().get('sale.line') sl = SaleLine.__table__() context = Transaction().context party = context.get('party') fee_type = context.get('fee_type') purchase = context.get('purchase') sale = context.get('sale') shipment_in = context.get('shipment_in') shipment_out = context.get('shipment_out') shipment_internal = context.get('shipment_internal') asof = context.get('asof') todate = context.get('todate') wh = ((fr.create_date >= asof) & ((fr.create_date-datetime.timedelta(1)) <= todate)) if party: wh &= (fr.fee_counterparty == party) if fee_type: wh &= (fr.fee_type == fee_type) if purchase: wh &= (pu.id == purchase) if sale: wh &= (sa.id == sale) if shipment_in: wh &= (fr.shipment_in == shipment_in) # if shipment_out: # wh &= (fr.shipment_out == shipment_out) query = fr.join(pl,'LEFT',condition=fr.line == pl.id).join(pu,'LEFT', condition=pl.purchase == pu.id).select( Literal(0).as_('create_uid'), CurrentTimestamp().as_('create_date'), Literal(None).as_('write_uid'), Literal(None).as_('write_date'), fr.id.as_('id'), fr.line.as_('r_purchase_line'), Literal(None).as_('r_sale_line'), fr.shipment_in.as_('r_shipment_in'), Literal(None).as_('r_shipment_out'), fr.shipment_internal.as_('r_shipment_internal'), fr.product.as_('r_fee_type'), fr.supplier.as_('r_fee_counterparty'), fr.type.as_('r_type'), fr.p_r.as_('r_fee_paystatus'), fr.mode.as_('r_mode'), fr.state.as_('r_state'), fr.purchase.as_('r_purchase'), #fr.amount.as_('r_fee_amount'), fr.price.as_('r_fee_price'), fr.currency.as_('r_fee_currency'), #fr.fee_lots.as_('r_fee_lots'), #fr.lots.as_('r_lots'), where=wh) return query @classmethod def search_rec_name(cls, name, clause): _, operator, operand, *extra = clause if operator.startswith('!') or operator.startswith('not '): bool_op = 'AND' else: bool_op = 'OR' code_value = operand if operator.endswith('like') and is_full_text(operand): code_value = lstrip_wildcard(operand) return [bool_op, ('r_fee_type', operator, operand, *extra), ('r_fee_counterparty', operator, operand, *extra), ] class FeeContext(ModelView): "Fee Context" __name__ = 'fee.context' asof = fields.Date("As of") todate = fields.Date("To") party = fields.Many2One('party.party', "Counterparty") fee_type = fields.Many2One('product.product', 'Fee type') purchase = fields.Many2One('purchase.purchase', "Purchase") sale = fields.Many2One('sale.sale', "Sale") shipment_in = fields.Many2One('stock.shipment.in',"Shipment In") shipment_out = fields.Many2One('stock.shipment.out',"Shipment Out") shipment_internal = fields.Many2One('stock.shipment.internal',"Shipment Internal") @classmethod def default_asof(cls): pool = Pool() Date = pool.get('ir.date') return Date.today().replace(day=1,month=1,year=1999) @classmethod def default_todate(cls): pool = Pool() Date = pool.get('ir.date') return Date.today()