Upgrade from 4.2 series
This commit is contained in:
parent
ee903a7bc2
commit
bca0d0769d
|
@ -1,6 +1,3 @@
|
|||
* Add DiscountMixin
|
||||
* Make gross_unit_price a functional field
|
||||
|
||||
Version 4.2.0 - 2016-11-28
|
||||
* Bug fixes (see mercurial logs for details)
|
||||
|
||||
|
|
128
invoice.py
128
invoice.py
|
@ -1,89 +1,113 @@
|
|||
import copy
|
||||
from decimal import Decimal
|
||||
|
||||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.config import config as config_
|
||||
from trytond.modules.product import price_digits
|
||||
|
||||
__all__ = ['InvoiceLine', 'discount_digits']
|
||||
|
||||
STATES = {
|
||||
'invisible': Eval('type') != 'line',
|
||||
'required': Eval('type') == 'line',
|
||||
'readonly': Eval('invoice_state') != 'draft',
|
||||
}
|
||||
DEPENDS = ['type', 'invoice_state']
|
||||
discount_digits = (16, config_.getint('product', 'discount_decimal',
|
||||
default=4))
|
||||
|
||||
|
||||
class DiscountMixin(object):
|
||||
gross_unit_price = fields.Function(fields.Numeric('Gross Price',
|
||||
digits=price_digits),
|
||||
'on_change_with_gross_unit_price', setter='set_gross_unit_price')
|
||||
class InvoiceLine:
|
||||
__metaclass__ = PoolMeta
|
||||
__name__ = 'account.invoice.line'
|
||||
|
||||
gross_unit_price = fields.Numeric('Gross Price', digits=price_digits,
|
||||
states=STATES, depends=DEPENDS)
|
||||
gross_unit_price_wo_round = fields.Numeric('Gross Price without rounding',
|
||||
digits=(16, price_digits[1] + discount_digits[1]), readonly=True)
|
||||
discount = fields.Numeric('Discount', digits=discount_digits,
|
||||
)
|
||||
states=STATES, depends=DEPENDS)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(DiscountMixin, cls).__setup__()
|
||||
if set(cls.unit_price.depends) - set(cls.discount.depends):
|
||||
cls.discount.states = copy.deepcopy(cls.unit_price.states)
|
||||
cls.gross_unit_price.states = copy.deepcopy(
|
||||
cls.unit_price.states)
|
||||
cls.discount.depends = copy.deepcopy(cls.unit_price.depends)
|
||||
cls.gross_unit_price.depends = copy.deepcopy(
|
||||
cls.unit_price.depends)
|
||||
super(InvoiceLine, cls).__setup__()
|
||||
cls.unit_price.states['readonly'] = True
|
||||
cls.unit_price.digits = (20, price_digits[1] + discount_digits[1])
|
||||
# Some models may not have the amount field defined
|
||||
if hasattr(cls, 'amount'):
|
||||
if 'discount' not in cls.amount.on_change_with:
|
||||
cls.amount.on_change_with.add('discount')
|
||||
if 'gross_unit_price' not in cls.amount.on_change_with:
|
||||
cls.amount.on_change_with.add('gross_unit_price')
|
||||
if 'discount' not in cls.amount.on_change_with:
|
||||
cls.amount.on_change_with.add('discount')
|
||||
if 'gross_unit_price' not in cls.amount.on_change_with:
|
||||
cls.amount.on_change_with.add('gross_unit_price')
|
||||
|
||||
@staticmethod
|
||||
def default_discount():
|
||||
return Decimal(0)
|
||||
|
||||
@fields.depends('unit_price', 'discount')
|
||||
def on_change_with_gross_unit_price(self, name=None):
|
||||
digits = self.__class__.gross_unit_price.digits[1]
|
||||
if self.discount == Decimal(1):
|
||||
return Decimal(0)
|
||||
if self.unit_price:
|
||||
discount = self.discount or Decimal(0)
|
||||
gross_unit_price = self.unit_price / (1 - discount)
|
||||
gross_unit_price = gross_unit_price.quantize(
|
||||
Decimal(str(10.0 ** -digits)))
|
||||
return gross_unit_price
|
||||
return self.unit_price
|
||||
|
||||
@classmethod
|
||||
def set_gross_unit_price(cls, lines, name, gross_unit_price):
|
||||
for line in lines:
|
||||
line.set_unit_price_from_gross_unit_price(gross_unit_price)
|
||||
cls.save(lines)
|
||||
|
||||
def set_unit_price_from_gross_unit_price(self, gross_unit_price):
|
||||
def update_prices(self):
|
||||
unit_price = self.unit_price
|
||||
if gross_unit_price is not None and self.discount is not None:
|
||||
unit_price = gross_unit_price * (1 - self.discount)
|
||||
digits = self.__class__.gross_unit_price.digits[1]
|
||||
gross_unit_price = gross_unit_price_wo_round = self.gross_unit_price
|
||||
|
||||
if self.gross_unit_price is not None and self.discount is not None:
|
||||
unit_price = self.gross_unit_price * (1 - self.discount)
|
||||
digits = self.__class__.unit_price.digits[1]
|
||||
unit_price = unit_price.quantize(Decimal(str(10.0 ** -digits)))
|
||||
# Ony trigger the change if it has really changed
|
||||
if unit_price != self.unit_price:
|
||||
self.unit_price = unit_price
|
||||
|
||||
if self.discount != 1:
|
||||
gross_unit_price_wo_round = unit_price / (1 - self.discount)
|
||||
gross_unit_price = gross_unit_price_wo_round.quantize(
|
||||
Decimal(str(10.0 ** -digits)))
|
||||
elif self.unit_price and self.discount:
|
||||
gross_unit_price_wo_round = self.unit_price / (1 - self.discount)
|
||||
gross_unit_price = gross_unit_price_wo_round.quantize(
|
||||
Decimal(str(10.0 ** -digits)))
|
||||
|
||||
if gross_unit_price_wo_round:
|
||||
digits = self.__class__.gross_unit_price_wo_round.digits[1]
|
||||
gross_unit_price_wo_round = gross_unit_price_wo_round.quantize(
|
||||
Decimal(str(10.0 ** -digits)))
|
||||
|
||||
self.gross_unit_price = gross_unit_price
|
||||
self.gross_unit_price_wo_round = gross_unit_price_wo_round
|
||||
self.unit_price = unit_price
|
||||
|
||||
@fields.depends('gross_unit_price', 'discount', 'unit_price')
|
||||
def on_change_gross_unit_price(self):
|
||||
self.set_unit_price_from_gross_unit_price(self.gross_unit_price)
|
||||
return self.update_prices()
|
||||
|
||||
@fields.depends('gross_unit_price', 'discount', 'unit_price')
|
||||
def on_change_discount(self):
|
||||
self.set_unit_price_from_gross_unit_price(self.gross_unit_price)
|
||||
return self.update_prices()
|
||||
|
||||
@fields.depends('gross_unit_price', 'unit_price', 'discount')
|
||||
def on_change_product(self):
|
||||
super(InvoiceLine, self).on_change_product()
|
||||
if self.unit_price:
|
||||
self.gross_unit_price = self.unit_price
|
||||
self.discount = Decimal(0)
|
||||
self.update_prices()
|
||||
if not self.discount:
|
||||
self.discount = Decimal(0)
|
||||
|
||||
class InvoiceLine(DiscountMixin):
|
||||
__metaclass__ = PoolMeta
|
||||
__name__ = 'account.invoice.line'
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
vlist = [x.copy() for x in vlist]
|
||||
for vals in vlist:
|
||||
if vals.get('type') != 'line':
|
||||
continue
|
||||
gross_unit_price = (vals.get('unit_price', Decimal('0.0'))
|
||||
or Decimal('0.0'))
|
||||
if vals.get('discount') not in (None, 1):
|
||||
gross_unit_price = gross_unit_price / (1 - vals['discount'])
|
||||
digits = cls.gross_unit_price.digits[1]
|
||||
gross_unit_price = gross_unit_price.quantize(
|
||||
Decimal(str(10.0 ** -digits)))
|
||||
vals['gross_unit_price'] = gross_unit_price
|
||||
if not vals.get('discount'):
|
||||
vals['discount'] = Decimal(0)
|
||||
return super(InvoiceLine, cls).create(vlist)
|
||||
|
||||
def _credit(self):
|
||||
line = super(InvoiceLine, self)._credit()
|
||||
line.discount = self.discount
|
||||
for field in ('gross_unit_price', 'discount'):
|
||||
setattr(line, field, getattr(self, field))
|
||||
return line
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
|
@ -10,6 +10,10 @@ msgctxt "field:account.invoice.line,gross_unit_price:"
|
|||
msgid "Gross Price"
|
||||
msgstr "Preu brut"
|
||||
|
||||
msgctxt "field:account.invoice.line,gross_unit_price_wo_round:"
|
||||
msgid "Gross Price without rounding"
|
||||
msgstr "Preu brut sense arrodoniment"
|
||||
|
||||
msgctxt "report:account.invoice:"
|
||||
msgid "Discount"
|
||||
msgstr "Descompte"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
|
@ -10,6 +10,10 @@ msgctxt "field:account.invoice.line,gross_unit_price:"
|
|||
msgid "Gross Price"
|
||||
msgstr "Precio bruto"
|
||||
|
||||
msgctxt "field:account.invoice.line,gross_unit_price_wo_round:"
|
||||
msgid "Gross Price without rounding"
|
||||
msgstr "Precio bruto sin redondeo"
|
||||
|
||||
msgctxt "report:account.invoice:"
|
||||
msgid "Discount"
|
||||
msgstr "Descuento"
|
||||
|
|
|
@ -110,20 +110,16 @@ Add line defining Gross Unit Price and Discount (Unit Price is calculated)::
|
|||
>>> line.amount
|
||||
Decimal('18.67')
|
||||
|
||||
A line defining Unit Price sets Gross Unit price::
|
||||
Add line defining Unit Price and Discount, Gross Unit Price is calculated::
|
||||
|
||||
>>> line = InvoiceLine()
|
||||
>>> invoice.lines.append(line)
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('20.0000')
|
||||
>>> line.gross_unit_price
|
||||
Decimal('20.0000')
|
||||
>>> line.unit_price = Decimal('17.60')
|
||||
>>> line.discount = Decimal('0.12')
|
||||
>>> line.gross_unit_price
|
||||
Decimal('20.0000')
|
||||
>>> line.unit_price
|
||||
Decimal('17.60000000')
|
||||
>>> line.amount
|
||||
Decimal('88.00')
|
||||
|
||||
|
@ -197,33 +193,3 @@ Discounts are copyied when crediting the invoice::
|
|||
Decimal('-8.80')
|
||||
>>> credit_invoice.total_amount
|
||||
Decimal('-115.47')
|
||||
|
||||
Division by zero is avoided if discount is zero::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice_line = invoice.lines.new()
|
||||
>>> invoice_line.product = product
|
||||
>>> invoice_line.quantity = 1.0
|
||||
>>> invoice_line.unit_price = Decimal('5')
|
||||
>>> invoice_line.discount = Decimal('1')
|
||||
>>> invoice_line.amount
|
||||
Decimal('0.00')
|
||||
>>> invoice_line.discount = Decimal('0.12')
|
||||
>>> invoice_line.gross_unit_price = Decimal('5')
|
||||
>>> invoice_line.amount
|
||||
Decimal('4.40')
|
||||
>>> invoice_line.quantity = 2.0
|
||||
>>> invoice_line.amount
|
||||
Decimal('8.80')
|
||||
>>> invoice_line = invoice.lines.new()
|
||||
>>> invoice_line.type = 'comment'
|
||||
>>> invoice_line.description = 'Comment'
|
||||
>>> invoice_line = invoice.lines.new()
|
||||
>>> invoice_line.product = product
|
||||
>>> invoice_line.unit_price = Decimal('5')
|
||||
>>> invoice_line.quantity = 3.0
|
||||
>>> invoice_line.amount
|
||||
Decimal('15.00')
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('23.80')
|
||||
|
|
Loading…
Reference in New Issue