Files
2026-03-26 22:08:56 +01:00

866 lines
37 KiB
Python
Executable File

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()