Allow calculate margin percentatge from unit or cost price
This commit is contained in:
parent
166c6a4f95
commit
f0e626ce7e
|
@ -2,11 +2,13 @@
|
|||
#The COPYRIGHT file at the top level of this repository contains
|
||||
#the full copyright notices and license terms.
|
||||
from trytond.pool import Pool
|
||||
from . import configuration
|
||||
from . import sale
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
configuration.Configuration,
|
||||
sale.Sale,
|
||||
sale.SaleLine,
|
||||
module='sale_margin', type_='model')
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# This file is part sale_margin module for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains
|
||||
# the full copyright notices and license terms.
|
||||
from trytond.model import fields
|
||||
from trytond.pyson import Eval, Bool
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
__all__ = ['Configuration']
|
||||
|
||||
|
||||
class Configuration:
|
||||
__metaclass__ = PoolMeta
|
||||
__name__ = 'sale.configuration'
|
||||
sale_margin_method = fields.Property(fields.Selection([
|
||||
('unit_price', 'Unit Price'),
|
||||
('cost_price', 'Cost Price'),
|
||||
], 'Sale Margin Method', states={
|
||||
'required': Bool(Eval('context', {}).get('company')),
|
||||
}))
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="sale_configuration_view_form">
|
||||
<field name="model">sale.configuration</field>
|
||||
<field name="inherit" ref="sale.sale_configuration_view_form"/>
|
||||
<field name="name">configuration_form</field>
|
||||
</record>
|
||||
</data>
|
||||
<data noupdate="1">
|
||||
<record model="ir.property" id="property_sale_margin_method">
|
||||
<field name="field"
|
||||
search="[('model.model', '=', 'sale.configuration'), ('name', '=', 'sale_margin_method')]" />
|
||||
<field name="value">,cost_price</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
14
locale/ca.po
14
locale/ca.po
|
@ -1,7 +1,11 @@
|
|||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:sale.configuration,sale_margin_method:"
|
||||
msgid "Sale Margin Method"
|
||||
msgstr "Mètode marge venda"
|
||||
|
||||
msgctxt "field:sale.line,cost_price:"
|
||||
msgid "Cost Price"
|
||||
msgstr "Preu cost"
|
||||
|
@ -38,6 +42,14 @@ msgstr ""
|
|||
"Dóna la rendibilitat mitjançant el càlcul de la diferència entre el preu "
|
||||
"unitari i preu de cost."
|
||||
|
||||
msgctxt "selection:sale.configuration,sale_margin_method:"
|
||||
msgid "Cost Price"
|
||||
msgstr "Preu de cost"
|
||||
|
||||
msgctxt "selection:sale.configuration,sale_margin_method:"
|
||||
msgid "Unit Price"
|
||||
msgstr "Preu unitat"
|
||||
|
||||
msgctxt "view:sale.line:"
|
||||
msgid "%"
|
||||
msgstr "%"
|
||||
|
|
14
locale/es.po
14
locale/es.po
|
@ -1,7 +1,11 @@
|
|||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "field:sale.configuration,sale_margin_method:"
|
||||
msgid "Sale Margin Method"
|
||||
msgstr "Método margen venta"
|
||||
|
||||
msgctxt "field:sale.line,cost_price:"
|
||||
msgid "Cost Price"
|
||||
msgstr "Precio coste"
|
||||
|
@ -38,6 +42,14 @@ msgstr ""
|
|||
"Proporciona la rentabilidad calculando la diferencia entre el precio unidad "
|
||||
"y el precio de coste."
|
||||
|
||||
msgctxt "selection:sale.configuration,sale_margin_method:"
|
||||
msgid "Cost Price"
|
||||
msgstr "Precio de coste"
|
||||
|
||||
msgctxt "selection:sale.configuration,sale_margin_method:"
|
||||
msgid "Unit Price"
|
||||
msgstr "Precio unitario"
|
||||
|
||||
msgctxt "view:sale.line:"
|
||||
msgid "%"
|
||||
msgstr "%"
|
||||
|
|
60
sale.py
60
sale.py
|
@ -30,6 +30,15 @@ class Sale:
|
|||
margin_percent_cache = fields.Numeric('Margin (%) Cache',
|
||||
digits=(16, 4), readonly=True)
|
||||
|
||||
@classmethod
|
||||
def copy(cls, sales, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default = default.copy()
|
||||
default['margin_cache'] = None
|
||||
default['margin_percent_cache'] = None
|
||||
return super(Sale, cls).copy(sales, default=default)
|
||||
|
||||
def get_margin(self, name):
|
||||
'''
|
||||
Return the margin of each sales
|
||||
|
@ -43,15 +52,21 @@ class Sale:
|
|||
return Currency.round(self.currency, margin)
|
||||
|
||||
def get_margin_percent(self, name):
|
||||
Configuration = Pool().get('sale.configuration')
|
||||
|
||||
config = Configuration(1)
|
||||
sale_margin_method = config.sale_margin_method
|
||||
|
||||
if (self.state in self._states_cached
|
||||
and self.margin_percent_cache is not None):
|
||||
return self.margin_percent_cache
|
||||
|
||||
cost = sum(
|
||||
Decimal(str(fabs(l.quantity))) * (l.cost_price or Decimal(0))
|
||||
price = sum(
|
||||
Decimal(str(fabs(l.quantity))) * (
|
||||
getattr(l, sale_margin_method) or Decimal(0))
|
||||
for l in self.lines if l.type == 'line')
|
||||
if cost:
|
||||
return (self.margin / cost).quantize(Decimal('0.0001'))
|
||||
if price:
|
||||
return (self.margin / price).quantize(Decimal('0.0001'))
|
||||
else:
|
||||
return Decimal('1.0')
|
||||
|
||||
|
@ -137,9 +152,14 @@ class SaleLine:
|
|||
else:
|
||||
return Decimal(0)
|
||||
|
||||
@fields.depends('type', 'quantity', 'cost_price', '_parent_sale.currency',
|
||||
'_parent_sale.lines', methods=['margin'])
|
||||
@fields.depends('type', 'quantity', 'cost_price', 'unit_price',
|
||||
'_parent_sale.currency', '_parent_sale.lines', methods=['margin'])
|
||||
def on_change_with_margin_percent(self, name=None):
|
||||
Configuration = Pool().get('sale.configuration')
|
||||
|
||||
config = Configuration(1)
|
||||
sale_margin_method = config.sale_margin_method
|
||||
|
||||
if self.type not in ('line', 'subtotal'):
|
||||
return
|
||||
self.margin = self.on_change_with_margin()
|
||||
|
@ -148,23 +168,35 @@ class SaleLine:
|
|||
if self.type == 'line':
|
||||
if not self.quantity:
|
||||
return
|
||||
if not self.cost_price:
|
||||
if sale_margin_method == 'cost_price' and not self.cost_price:
|
||||
return Decimal('1.0')
|
||||
cost = self.get_cost_price()
|
||||
return (self.margin / cost).quantize(Decimal('0.0001'))
|
||||
if sale_margin_method == 'unit_price' and not self.unit_price:
|
||||
return Decimal('1.0')
|
||||
if sale_margin_method == 'unit_price':
|
||||
price = self.get_unit_price()
|
||||
else:
|
||||
price = self.get_cost_price()
|
||||
return (self.margin / price).quantize(Decimal('0.0001'))
|
||||
else:
|
||||
cost = Decimal(0)
|
||||
price = Decimal(0)
|
||||
for line2 in self.sale.lines:
|
||||
if self == line2:
|
||||
if not cost:
|
||||
if not price:
|
||||
return Decimal('1.0')
|
||||
else:
|
||||
return (self.margin / cost).quantize(Decimal('0.0001'))
|
||||
return (self.margin / price).quantize(Decimal('0.0001'))
|
||||
if line2.type == 'line':
|
||||
cost += line2.get_cost_price()
|
||||
if sale_margin_method == 'unit_price':
|
||||
price += line2.get_unit_price()
|
||||
else:
|
||||
price += line2.get_cost_price()
|
||||
elif line2.type == 'subtotal':
|
||||
cost = Decimal(0)
|
||||
price = Decimal(0)
|
||||
|
||||
def get_cost_price(self):
|
||||
return Decimal(str(fabs(self.quantity))) * (self.cost_price or
|
||||
Decimal(0))
|
||||
|
||||
def get_unit_price(self):
|
||||
return Decimal(str(fabs(self.quantity))) * (self.unit_price or
|
||||
Decimal(0))
|
||||
|
|
|
@ -130,6 +130,10 @@ Sale with 1 product::
|
|||
Decimal('10.00')
|
||||
>>> sale.margin_percent
|
||||
Decimal('1.0000')
|
||||
>>> sale_line.margin
|
||||
Decimal('10.00')
|
||||
>>> sale_line.margin_percent
|
||||
Decimal('1.0000')
|
||||
|
||||
Add second product and a subtotal::
|
||||
|
||||
|
@ -181,3 +185,38 @@ Confirm sale and check cache is done::
|
|||
True
|
||||
>>> sale.margin_percent and sale.margin_percent == sale.margin_percent_cache
|
||||
True
|
||||
|
||||
Change sale configuration::
|
||||
|
||||
>>> Configuration = Model.get('sale.configuration')
|
||||
>>> configuration = Configuration(1)
|
||||
>>> configuration.sale_margin_method = 'unit_price'
|
||||
>>> configuration.save()
|
||||
|
||||
Sale margin with and percentatge with unit price method::
|
||||
|
||||
>>> sale2 = Sale()
|
||||
>>> sale2.party = customer
|
||||
>>> sale2.payment_term = payment_term
|
||||
>>> sale2_line = SaleLine()
|
||||
>>> sale2.lines.append(sale2_line)
|
||||
>>> sale2_line.product = product
|
||||
>>> sale2_line.quantity = 2
|
||||
>>> sale2.save()
|
||||
>>> sale2.margin
|
||||
Decimal('10.00')
|
||||
>>> sale2.margin_percent
|
||||
Decimal('0.5000')
|
||||
>>> sale2_line.margin
|
||||
Decimal('10.00')
|
||||
>>> sale2_line.margin_percent
|
||||
Decimal('0.5000')
|
||||
|
||||
Confirm sale2 and check cache is done::
|
||||
|
||||
>>> Sale.quote([sale2.id], config.context)
|
||||
>>> Sale.confirm([sale2.id], config.context)
|
||||
>>> sale2.margin and sale2.margin == sale2.margin_cache
|
||||
True
|
||||
>>> sale2.margin_percent and sale2.margin_percent == sale2.margin_percent_cache
|
||||
True
|
||||
|
|
|
@ -7,4 +7,5 @@ depends:
|
|||
extras_depend:
|
||||
sale_discount
|
||||
xml:
|
||||
configuration.xml
|
||||
sale.xml
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/field[@name='sale_shipment_method']" position="after">
|
||||
<label name="sale_margin_method" />
|
||||
<field name="sale_margin_method" />
|
||||
</xpath>
|
||||
</data>
|
Loading…
Reference in New Issue