trytonpsk-purchase_discount/purchase.py

237 lines
8.9 KiB
Python

# This file is part of purchase_discount module for Tryton.
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from datetime import date
from decimal import Decimal
from trytond.exceptions import UserError
from trytond.model import ModelView, fields
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval
from trytond.modules.purchase.purchase import PurchaseReport
from trytond.config import config as config_
from trytond.transaction import Transaction
from trytond.modules.product import price_digits, round_price
STATES = {
'invisible': Eval('type') != 'line',
'required': Eval('type') == 'line',
}
DIGITS = config_.getint('product', 'price_decimal', default=4)
DISCOUNT_DIGITS = 6
class Configuration(metaclass=PoolMeta):
__name__ = 'purchase.configuration'
description = fields.Char('Default Description')
class Purchase(metaclass=PoolMeta):
__name__ = 'purchase.purchase'
@staticmethod
def default_description():
config = Pool().get('purchase.configuration')(1)
if config.description:
return config.description
class PurchaseLine(metaclass=PoolMeta):
__name__ = 'purchase.line'
base_price = fields.Numeric('Base Price', digits=(16, DIGITS))
# gross_unit_price_wo_round = fields.Numeric('Gross Price without rounding',
# digits=(16, DIGITS + DISCOUNT_DIGITS), readonly=True)
discount_amount = fields.Function(fields.Numeric(
"Discount Amount", digits=price_digits,
states={
'invisible': Eval('type') != 'line',
'readonly': Eval('purchase_state') != 'draft',
},
depends=['type', 'purchase_state']),
'on_change_with_discount_amount', setter='set_discount_amount')
discount_rate = fields.Function(fields.Numeric(
"Discount Rate", digits=(16, 4),
states={
'invisible': Eval('type') != 'line',
'readonly': Eval('purchase_state') != 'draft',
},
depends=['type', 'purchase_state']),
'on_change_with_discount_rate', setter='set_discount_rate')
discount = fields.Function(fields.Char(
"Discount",
states={
'invisible': ~Eval('discount'),
}),
'on_change_with_discount')
stock_quantity = fields.Function(fields.Float('Stock Quantity',
digits=(16, 2), depends=['product'], states=STATES),
'on_change_with_stock_quantity')
# product_description = fields.Text('Application')
@classmethod
def __setup__(cls):
super(PurchaseLine, cls).__setup__()
@classmethod
def __register__(cls, module_name):
table_h = cls.__table_handler__(module_name)
if table_h.column_exist('gross_unit_price'):
table_h.column_rename('gross_unit_price', 'base_price')
if table_h.column_exist('discount'):
table_h.column_rename('discount', 'discount_old')
super(PurchaseLine, cls).__register__(module_name)
def get_invoice_line(self):
'Return a list of invoice lines for sale line'
line = super(PurchaseLine, self).get_invoice_line()
if line and isinstance(line, list):
for l in line:
l.base_price = self.base_price
return line
@fields.depends(
methods=[
'compute_base_price', 'on_change_with_discount_rate',
'on_change_with_discount_amount', 'on_change_with_discount'])
def on_change_product(self):
super().on_change_product()
if self.product:
self.base_price = self.compute_base_price()
self.discount_rate = self.on_change_with_discount_rate()
self.discount_amount = self.on_change_with_discount_amount()
self.discount = self.on_change_with_discount()
@fields.depends('product', 'unit')
def compute_base_price(self):
pool = Pool()
Uom = pool.get('product.uom')
if self.product:
price = self.product.cost_price
if self.unit:
price = Uom.compute_price(
self.product.default_uom, price, self.unit)
return round_price(price)
@fields.depends('unit_price', 'base_price')
def on_change_with_discount_rate(self, name=None):
if self.unit_price is None or not self.base_price:
return
rate = 1 - self.unit_price / self.base_price
return rate.quantize(
Decimal(1) / 10 ** self.__class__.discount_rate.digits[1])
@classmethod
def set_discount_rate(cls, lines, name, value):
pass
@fields.depends('unit_price', 'base_price')
def on_change_with_discount_amount(self, name=None):
if self.unit_price is None or self.base_price is None:
return
return round_price(self.base_price - self.unit_price)
@fields.depends(
'base_price', 'discount_rate',
methods=['on_change_with_discount_amount', 'on_change_with_discount',
'on_change_with_amount'])
def on_change_discount_rate(self):
if self.base_price is not None and self.discount_rate is not None:
self.unit_price = round_price(
self.base_price * (1 - self.discount_rate))
self.discount_amount = self.on_change_with_discount_amount()
self.discount = self.on_change_with_discount()
self.amount = self.on_change_with_amount()
@fields.depends(
'base_price', 'discount_amount',
methods=['on_change_with_discount_rate', 'on_change_with_discount',
'on_change_with_amount'])
def on_change_discount_amount(self):
if self.base_price is not None and self.discount_amount is not None:
self.unit_price = round_price(
self.base_price - self.discount_amount)
self.discount_rate = self.on_change_with_discount_rate()
self.discount = self.on_change_with_discount()
self.amount = self.on_change_with_amount()
@fields.depends(
'purchase', '_parent_purchase.currency',
methods=[
'on_change_with_discount_rate', 'on_change_with_discount_amount'])
def on_change_with_discount(self, name=None):
pool = Pool()
Lang = pool.get('ir.lang')
lang = Lang.get()
rate = self.on_change_with_discount_rate()
if not rate or rate % Decimal('0.01'):
amount = self.on_change_with_discount_amount()
if amount:
return lang.currency(
amount, self.purchase.currency, digits=price_digits[1])
else:
return lang.format('%i', rate * 100) + '%'
@fields.depends('product', 'purchase')
def on_change_with_stock_quantity(self, name=None):
res = 0
if not self.purchase.warehouse:
raise UserError('purchase_without_warehouse')
location_id = self.purchase.warehouse.id
if self.product:
stock_context = {
'stock_date_end': date.today(),
'locations': [location_id],
}
with Transaction().set_context(stock_context):
try:
res_dict = self.product._get_quantity([self.product], 'quantity', [location_id], grouping_filter=([self.product.id],))
if res_dict.get(self.product.id):
res += res_dict[self.product.id]
except AttributeError as error:
print(error)
return res
@classmethod
def set_discount_amount(cls, lines, name, value):
pass
class PurchaseDiscountWizardStart(ModelView):
'Purchase Discount Wizard Start'
__name__ = 'purchase.discount_wizard.start'
discount = fields.Float('Discount', digits=(2, 2))
class PurchaseDiscountWizard(Wizard):
'Purchase Discount Wizard'
__name__ = 'purchase.discount_wizard'
start = StateView('purchase.discount_wizard.start',
'purchase_discount.purchase_wizard_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'accept', 'tryton-ok', default=True),
])
accept = StateTransition()
def transition_accept(self):
Purchase = Pool().get('purchase.purchase')
purchases = Purchase.browse(Transaction().context['active_ids'])
purchases = [i for i in purchases if i.state == 'draft']
for purchase in purchases:
for line in purchase.lines:
if line.type != 'line':
continue
value = self.start.discount / 100
line.write([line], {'discount': Decimal(value).quantize(Decimal(100) ** -2)})
line.update_prices()
line.save()
# Purchase.update_taxes(purchases)
return 'end'
class PurchaseDiscountReport(PurchaseReport):
__name__ = 'purchase.purchase.discount'