Add margin_percent fields to sale and sale line

This commit is contained in:
?ngel ?lvarez 2014-11-12 12:35:46 +01:00
parent 6a86ba5cac
commit 79b7016797
9 changed files with 189 additions and 26 deletions

View File

@ -1,3 +1,5 @@
* Add margin_percent fields to sale and sale line
Version 3.4.0 - 2014-11-03
* Add compatibility (as extra dependency) with sale_discount
* Improve sale line margin computation unifying getter and on_change_with and

View File

@ -10,6 +10,10 @@ msgctxt "field:sale.line,margin:"
msgid "Margin"
msgstr "Marge"
msgctxt "field:sale.line,margin_percent:"
msgid "Margin (%)"
msgstr "Marge (%)"
msgctxt "field:sale.sale,margin:"
msgid "Margin"
msgstr "Marge"
@ -18,6 +22,26 @@ msgctxt "field:sale.sale,margin_cache:"
msgid "Margin Cache"
msgstr "Marge precalculat"
msgctxt "field:sale.sale,margin_percent:"
msgid "Margin (%)"
msgstr "Marge (%)"
msgctxt "field:sale.sale,margin_percent_cache:"
msgid "Margin (%) Cache"
msgstr "Marge (%) precalculat"
msgctxt "help:sale.sale,margin:"
msgid "It gives profitability by calculating the difference between the Unit Price and Cost Price."
msgstr "Dóna la rendibilitat mitjançant el càlcul de la diferència entre el preu unitari i preu de cost."
msgid ""
"It gives profitability by calculating the difference between the Unit Price "
"and Cost Price."
msgstr ""
"Dóna la rendibilitat mitjançant el càlcul de la diferència entre el preu "
"unitari i preu de cost."
msgctxt "view:sale.line:"
msgid "%"
msgstr "%"
msgctxt "view:sale.sale:"
msgid "%"
msgstr "%"

View File

@ -10,6 +10,10 @@ msgctxt "field:sale.line,margin:"
msgid "Margin"
msgstr "Margen"
msgctxt "field:sale.line,margin_percent:"
msgid "Margin (%)"
msgstr "Margen (%)"
msgctxt "field:sale.sale,margin:"
msgid "Margin"
msgstr "Margen"
@ -18,6 +22,26 @@ msgctxt "field:sale.sale,margin_cache:"
msgid "Margin Cache"
msgstr "Margen precalculado"
msgctxt "field:sale.sale,margin_percent:"
msgid "Margin (%)"
msgstr "Margen (%)"
msgctxt "field:sale.sale,margin_percent_cache:"
msgid "Margin (%) Cache"
msgstr "Margen (%) precalculado"
msgctxt "help:sale.sale,margin:"
msgid "It gives profitability by calculating the difference between the Unit Price and Cost Price."
msgstr "Proporciona la rentabilidad calculando la diferencia entre el precio unidad y el precio de coste."
msgid ""
"It gives profitability by calculating the difference between the Unit Price "
"and Cost Price."
msgstr ""
"Proporciona la rentabilidad calculando la diferencia entre el precio unidad "
"y el precio de coste."
msgctxt "view:sale.line:"
msgid "%"
msgstr "%"
msgctxt "view:sale.sale:"
msgid "%"
msgstr "%"

59
sale.py
View File

@ -19,11 +19,16 @@ class Sale:
depends=['currency_digits'],
help='It gives profitability by calculating the difference '
'between the Unit Price and Cost Price.'),
'get_margin')
'get_margin')
margin_cache = fields.Numeric('Margin Cache',
digits=(16, Eval('currency_digits', 2)),
readonly=True,
depends=['currency_digits'])
margin_percent = fields.Function(fields.Numeric('Margin (%)',
digits=(16, 4)),
'get_margin_percent')
margin_percent_cache = fields.Numeric('Margin (%) Cache',
digits=(16, 4), readonly=True)
def get_margin(self, name):
'''
@ -37,14 +42,23 @@ class Sale:
Decimal(0))
return Currency.round(self.currency, margin)
def get_margin_percent(self, name):
if (self.state in self._states_cached
and self.margin_percent_cache is not None):
return self.margin_percent_cache
cost = sum(Decimal(str(l.quantity)) * (l.cost_price or Decimal('0.0'))
for l in self.lines if l.type == 'line')
if cost:
return (self.margin / cost).quantize(Decimal('0.0001'))
@classmethod
def store_cache(cls, sales):
super(Sale, cls).store_cache(sales)
for sale in sales:
cls.write([sale], {
'untaxed_amount_cache': sale.untaxed_amount,
'tax_amount_cache': sale.tax_amount,
'total_amount_cache': sale.total_amount,
'margin_cache': sale.margin,
'margin_percent_cache': sale.margin_percent,
})
@ -62,6 +76,11 @@ class SaleLine:
},
depends=['type', 'amount']),
'on_change_with_margin')
margin_percent = fields.Function(fields.Numeric('Margin (%)',
digits=(16, 4), states={
'invisible': ~Eval('type').in_(['line', 'subtotal']),
}, depends=['type']),
'on_change_with_margin_percent')
@classmethod
def __setup__(cls):
@ -97,13 +116,41 @@ class SaleLine:
elif self.type == 'subtotal':
cost = Decimal('0.0')
for line2 in self.sale.lines:
if self == line2:
return cost
if line2.type == 'line':
cost2 = Decimal(str(line2.quantity)) * (line2.cost_price or
Decimal('0.0'))
cost += Currency.round(currency, line2.amount - cost2)
elif line2.type == 'subtotal':
if self == line2:
return cost
cost = Decimal('0.0')
else:
return Decimal('0.0')
@fields.depends('type', 'quantity', 'cost_price', '_parent_sale.currency',
'_parent_sale.lines', methods=['margin'])
def on_change_with_margin_percent(self, name=None):
Currency = Pool().get('currency.currency')
if self.type not in ('line', 'subtotal'):
return
self.margin = self.on_change_with_margin()
if not self.margin:
return
if self.type == 'line':
if not self.quantity or not self.cost_price:
return
cost = Decimal(str(self.quantity)) * (self.cost_price or
Decimal('0.0'))
return (self.margin / cost).quantize(Decimal('0.0001'))
else:
currency = self.sale.currency
cost = Decimal('0.0')
for line2 in self.sale.lines:
if self == line2:
return (self.margin / cost).quantize(Decimal('0.0001'))
if line2.type == 'line':
cost += (Decimal(str(line2.quantity))
* (line2.cost_price or Decimal('0.0')))
elif line2.type == 'subtotal':
cost = Decimal('0.0')

View File

@ -135,7 +135,7 @@ Create category::
>>> category = ProductCategory(name='Category')
>>> category.save()
Create product::
Create products::
>>> ProductUom = Model.get('product.uom')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
@ -143,7 +143,7 @@ Create product::
>>> Product = Model.get('product.product')
>>> product = Product()
>>> template = ProductTemplate()
>>> template.name = 'product'
>>> template.name = 'Product'
>>> template.category = category
>>> template.default_uom = unit
>>> template.type = 'goods'
@ -157,6 +157,22 @@ Create product::
>>> template.save()
>>> product.template = template
>>> product.save()
>>> product2 = Product()
>>> template2 = ProductTemplate()
>>> template2.name = 'Product 2'
>>> template2.category = category
>>> template2.default_uom = unit
>>> template2.type = 'goods'
>>> template2.purchasable = True
>>> template2.salable = True
>>> template2.list_price = Decimal('80')
>>> template2.cost_price = Decimal('50')
>>> template2.account_expense = expense
>>> template2.account_revenue = revenue
>>> template2.supply_on_sale = True
>>> template2.save()
>>> product2.template = template2
>>> product2.save()
Create payment term::
@ -167,7 +183,7 @@ Create payment term::
>>> payment_term.lines.append(payment_term_line)
>>> payment_term.save()
Sale 2 products::
Sale with 1 product::
>>> config.user = sale_user.id
>>> Sale = Model.get('sale.sale')
@ -182,16 +198,56 @@ Sale 2 products::
>>> sale.save()
>>> sale.margin
Decimal('10.00')
>>> sale.margin_percent
Decimal('1.0000')
Add second product and a subtotal::
>>> sale = Sale()
>>> sale.party = customer
>>> sale.payment_term = payment_term
>>> sale_line = SaleLine()
>>> sale.lines.append(sale_line)
>>> sale_line.description = 'New product'
>>> sale_line.quantity = 2
>>> sale_line.cost_price = Decimal('5')
>>> sale_line.unit_price = Decimal('10')
>>> sale_line.product = product2
>>> sale_line.quantity = 4
>>> sale.save()
>>> sale_line.margin
Decimal('120.00')
>>> sale_line.margin_percent
Decimal('0.6000')
>>> sale.margin
Decimal('10.00')
Decimal('130.00')
>>> sale.margin_percent
Decimal('0.6190')
Add subtotal and a line without product::
>>> sale_line = SaleLine()
>>> sale.lines.append(sale_line)
>>> sale_line.type = 'subtotal'
>>> sale_line.description = 'Subtotal'
>>> sale_line2 = SaleLine()
>>> sale.lines.append(sale_line2)
>>> sale_line2.description = 'New product'
>>> sale_line2.quantity = 2
>>> sale_line2.cost_price = Decimal('100')
>>> sale_line2.unit_price = Decimal('125')
>>> sale.save()
>>> sale_line.margin
Decimal('130.00')
>>> sale_line.margin_percent
Decimal('0.6190')
>>> sale_line2.margin
Decimal('50.00')
>>> sale_line2.margin_percent
Decimal('0.2500')
>>> sale.margin
Decimal('180.00')
>>> sale.margin_percent
Decimal('0.4390')
Confirm sale and check cache is done::
>>> Sale.quote([sale.id], config.context)
>>> Sale.confirm([sale.id], config.context)
>>> sale.margin and sale.margin == sale.margin_cache
True
>>> sale.margin_percent and sale.margin_percent == sale.margin_percent_cache
True

View File

@ -4,10 +4,14 @@ The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. -->
<data>
<xpath
expr="/form/notebook/page[@id=&quot;sale&quot;]/group[@id=&quot;amount_buttons&quot;]/field[@name=&quot;total_amount&quot;]"
position="after">
<newline/>
expr="/form/notebook/page[@id=&quot;sale&quot;]/group[@id=&quot;amount_buttons&quot;]/label[@name=&quot;untaxed_amount&quot;]"
position="before">
<label name="margin" xalign="1.0" xexpand="1"/>
<field name="margin" xalign="1.0" xexpand="0"/>
<label name="margin_percent" xalign="1.0" xexpand="1"/>
<group id="margin_percent" colspan="1" col="2" xexpand="0">
<field name="margin_percent" factor="100" xalign="1.0" xexpand="0"/>
<label name="margin_percent" string="%" xalign="0.0" xexpand="1" xfill="1"/>
</group>
</xpath>
</data>

View File

@ -12,7 +12,11 @@ copyright notices and license terms. -->
<xpath
expr="/form/notebook/page/field[@name=&quot;amount&quot;]"
position="after">
<label name="margin"/>
<field name="margin"/>
<label name="margin" xalign="1.0" xexpand="1"/>
<group id="margin_values" colspan="1" col="3">
<field name="margin" xalign="1.0" xexpand="0"/>
<field name="margin_percent" factor="100" xalign="1.0" xexpand="0"/>
<label name="margin_percent" string="%" xalign="0.0" xexpand="1" xfill="1"/>
</group>
</xpath>
</data>

View File

@ -12,5 +12,6 @@ copyright notices and license terms. -->
expr="/tree/field[@name=&quot;amount&quot;]"
position="after">
<field name="margin"/>
<field name="margin_percent" factor="100"/>
</xpath>
</data>

View File

@ -7,5 +7,6 @@ copyright notices and license terms. -->
expr="/tree/field[@name=&quot;untaxed_amount&quot;]"
position="after">
<field name="margin"/>
<field name="margin_percent" factor="100"/>
</xpath>
</data>