Add margin_percent fields to sale and sale line
This commit is contained in:
parent
6a86ba5cac
commit
79b7016797
|
@ -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
|
||||
|
|
|
@ -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 "%"
|
||||
|
|
|
@ -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
59
sale.py
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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="sale"]/group[@id="amount_buttons"]/field[@name="total_amount"]"
|
||||
position="after">
|
||||
<newline/>
|
||||
expr="/form/notebook/page[@id="sale"]/group[@id="amount_buttons"]/label[@name="untaxed_amount"]"
|
||||
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>
|
||||
|
|
|
@ -12,7 +12,11 @@ copyright notices and license terms. -->
|
|||
<xpath
|
||||
expr="/form/notebook/page/field[@name="amount"]"
|
||||
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>
|
||||
|
|
|
@ -12,5 +12,6 @@ copyright notices and license terms. -->
|
|||
expr="/tree/field[@name="amount"]"
|
||||
position="after">
|
||||
<field name="margin"/>
|
||||
<field name="margin_percent" factor="100"/>
|
||||
</xpath>
|
||||
</data>
|
||||
|
|
|
@ -7,5 +7,6 @@ copyright notices and license terms. -->
|
|||
expr="/tree/field[@name="untaxed_amount"]"
|
||||
position="after">
|
||||
<field name="margin"/>
|
||||
<field name="margin_percent" factor="100"/>
|
||||
</xpath>
|
||||
</data>
|
||||
|
|
Loading…
Reference in New Issue