Upgrade from 4.2 series

This commit is contained in:
Raimon Esteve 2016-12-30 14:41:46 +01:00
parent ee903a7bc2
commit bca0d0769d
5 changed files with 88 additions and 93 deletions

View File

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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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')