866 lines
37 KiB
Python
Executable File
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()
|