Allow calculate margin percentatge from unit or cost price

From changeset-195a62e4f229
This commit is contained in:
Raimon Esteve 2019-04-05 11:32:18 +02:00
parent b05a659175
commit 489b0c96f9
9 changed files with 202 additions and 20 deletions

View File

@ -1,12 +1,14 @@
#This file is part margin module for Tryton.
#The COPYRIGHT file at the top level of this repository contains
#The COPYRIGHT file at the top level of this repository contains
#the full copyright notices and license terms.
from trytond.pool import Pool
from .sale import *
from . import configuration
from . import sale
def register():
Pool.register(
Sale,
SaleLine,
configuration.Configuration,
sale.Sale,
sale.SaleLine,
module='sale_margin', type_='model')

48
configuration.py Normal file
View File

@ -0,0 +1,48 @@
# 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 Pool, 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')),
}))
@classmethod
def __setup__(cls):
super(Configuration, cls).__setup__()
cls._error_messages.update({
'change_sale_margin_method': ('You cannot change the sale '
'margin method because has sales.'),
})
cls._modify_no_sale = [
('sale_margin_method', 'change_sale_margin_method'),
]
@classmethod
def write(cls, *args):
actions = iter(args)
for _, values in zip(actions, actions):
for field, error in cls._modify_no_sale:
if field in values:
cls.check_no_sale(error)
break
super(Configuration, cls).write(*args)
@classmethod
def check_no_sale(cls, error):
Sale = Pool().get('sale.sale')
sales = Sale.search([], limit=1, order=[])
if sales:
cls.raise_user_error(error)

19
configuration.xml Normal file
View File

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

View File

@ -1,7 +1,15 @@
#
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "error:sale.configuration:"
msgid "You cannot change the sale margin method because has sales."
msgstr "No podeu canviar el mètode marge venta perquè ja existeixen ventes."
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 +46,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 "%"

View File

@ -1,7 +1,15 @@
#
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "error:sale.configuration:"
msgid "You cannot change the sale margin method because has sales."
msgstr "No puede cambiar el método de margen de venta porque ya existen ventas."
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 +46,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
View File

@ -31,6 +31,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
@ -44,15 +53,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')
@ -135,9 +150,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()
@ -146,23 +166,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))

View File

@ -200,6 +200,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::
@ -251,3 +255,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

View File

@ -7,4 +7,5 @@ depends:
depends_extra:
sale_discount
xml:
configuration.xml
sale.xml

View File

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