changeset 48304d72685b in modules/sale_secondary_unit:default
details:
https://hg.tryton.org/modules/sale_secondary_unit?cmd=changeset&node=48304d72685b
description:
Add purchase_blanket_agreement and sale_blanket_agreement modules
issue10962
review391151002
diffstat:
__init__.py | 5 +
sale.py | 110 ++++++++++++
setup.py | 1 +
tests/scenario_sale_blanket_agreement_secondary_unit.rst | 133 +++++++++++++++
tests/test_module.py | 2 +-
tryton.cfg | 1 +
6 files changed, 251 insertions(+), 1 deletions(-)
diffs (300 lines):
diff -r b3787b2475d1 -r 48304d72685b __init__.py
--- a/__init__.py Mon May 02 17:51:28 2022 +0200
+++ b/__init__.py Mon Oct 10 18:35:01 2022 +0200
@@ -25,3 +25,8 @@
sale.OpportunityLine,
module='sale_secondary_unit', type_='model',
depends=['sale_opportunity'])
+ Pool.register(
+ sale.BlanketAgreementLine,
+ sale.LineBlanketAgreement,
+ module='sale_secondary_unit', type_='model',
+ depends=['sale_blanket_agreement'])
diff -r b3787b2475d1 -r 48304d72685b sale.py
--- a/sale.py Mon May 02 17:51:28 2022 +0200
+++ b/sale.py Mon Oct 10 18:35:01 2022 +0200
@@ -189,3 +189,113 @@
sale_line.secondary_quantity = self.quantity
sale_line.secondary_unit = self.unit
sale_line.on_change_secondary_quantity()
+
+
+class BlanketAgreementLine(metaclass=PoolMeta):
+ __name__ = 'sale.blanket_agreement.line'
+
+ product_secondary_uom_category = fields.Function(
+ fields.Many2One(
+ 'product.uom.category', "Product Secondary UOM Category"),
+ 'on_change_with_product_secondary_uom_category')
+
+ @classmethod
+ def _unit_categories(cls):
+ return super()._unit_categories() + ['product_secondary_uom_category']
+
+ def is_same_uom_category(self, sale_line):
+ return super().is_same_uom_category(sale_line) or (
+ sale_line.secondary_unit and (
+ self.unit.category == sale_line.secondary_unit.category))
+
+ def remainig_quantity_for_sale(self, line, round=True):
+ pool = Pool()
+ Uom = pool.get('product.uom')
+ remaining_quantity = super().remainig_quantity_for_sale(
+ line, round=round)
+ if (self.remaining_quantity is not None
+ and line.secondary_unit
+ and self.unit.category == line.secondary_unit.category):
+ remaining_quantity = Uom.compute_qty(
+ self.unit, self.remaining_quantity, line.unit,
+ factor=line.secondary_uom_rate,
+ rate=line.secondary_uom_factor,
+ round=round)
+ return remaining_quantity
+
+ @fields.depends('product')
+ def on_change_with_product_secondary_uom_category(self, name=None):
+ if self.product and self.product.sale_secondary_uom:
+ return self.product.sale_secondary_uom.category.id
+
+ def _set_sale_line_quantity(self, sale_line):
+ super()._set_sale_line_quantity(sale_line)
+ secondary_uom = self.product.sale_secondary_uom
+ if (secondary_uom
+ and self.unit.category == secondary_uom.category):
+ sale_line.unit = self.product.sale_uom
+ sale_line.secondary_quantity = self.remaining_quantity or 0
+ sale_line.secondary_unit = self.unit
+ sale_line.secondary_unit_price = self.unit_price
+ sale_line.on_change_secondary_quantity()
+
+
+class LineBlanketAgreement(metaclass=PoolMeta):
+ __name__ = 'sale.line'
+
+ @fields.depends(
+ 'blanket_agreement_line', '_parent_blanket_agreement_line.unit',
+ '_parent_blanket_agreement_line.unit_price', 'unit', 'secondary_unit')
+ def compute_unit_price(self):
+ pool = Pool()
+ Uom = pool.get('product.uom')
+ unit_price = super().compute_unit_price()
+ line = self.blanket_agreement_line
+ if (line
+ and self.unit
+ and self.secondary_unit
+ and line.unit
+ and self.secondary_unit.category == line.unit.category):
+ secondary_unit_price = Uom.compute_price(
+ line.unit, line.unit_price, self.secondary_unit)
+ unit_price = Uom.compute_price(
+ self.secondary_unit, secondary_unit_price, self.unit,
+ factor=self.secondary_uom_rate, rate=self.secondary_uom_factor)
+ unit_price = round_price(unit_price)
+ return unit_price
+
+ @fields.depends(
+ 'blanket_agreement_line',
+ '_parent_blanket_agreement_line.unit',
+ '_parent_blanket_agreement_line.remaining_quantity',
+ 'sale', 'unit', 'secondary_quantity', 'secondary_unit',
+ 'secondary_unit_price', methods=['on_change_secondary_quantity'])
+ def on_change_blanket_agreement_line(self):
+ pool = Pool()
+ Uom = pool.get('product.uom')
+ super().on_change_blanket_agreement_line()
+ if self.blanket_agreement_line:
+ line = self.blanket_agreement_line
+ if (self.secondary_unit and line.unit
+ and self.secondary_unit.category == line.unit.category):
+ if line.remaining_quantity is not None:
+ remaining_quantity = Uom.compute_qty(
+ line.unit, line.remaining_quantity,
+ self.secondary_unit)
+ if (self.secondary_quantity is None
+ or self.secondary_quantity > remaining_quantity):
+ self.secondary_quantity = remaining_quantity
+ self.on_change_secondary_quantity()
+
+ def quantity_for_blanket_agreement(self, line, round=True):
+ pool = Pool()
+ Uom = pool.get('product.uom')
+ quantity = super().quantity_for_blanket_agreement(line, round=round)
+ if (self.secondary_unit
+ and self.secondary_unit.category == line.unit.category):
+ quantity = Uom.compute_qty(
+ self.unit, (self.actual_quantity or self.quantity), line.unit,
+ factor=self.secondary_uom_factor,
+ rate=self.secondary_uom_rate,
+ round=round)
+ return quantity
diff -r b3787b2475d1 -r 48304d72685b setup.py
--- a/setup.py Mon May 02 17:51:28 2022 +0200
+++ b/setup.py Mon Oct 10 18:35:01 2022 +0200
@@ -66,6 +66,7 @@
tests_require = [get_require_version('proteus'),
get_require_version('trytond_account_invoice_secondary_unit'),
+ get_require_version('trytond_sale_blanket_agreement'),
get_require_version('trytond_stock_secondary_unit'),
get_require_version('trytond_sale_product_customer')]
dependency_links = []
diff -r b3787b2475d1 -r 48304d72685b
tests/scenario_sale_blanket_agreement_secondary_unit.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/scenario_sale_blanket_agreement_secondary_unit.rst Mon Oct 10
18:35:01 2022 +0200
@@ -0,0 +1,133 @@
+==============================================
+Sale Blanket Agreement Secondary Unit Scenario
+==============================================
+
+Imports::
+
+ >>> import datetime
+ >>> from dateutil.relativedelta import relativedelta
+ >>> from decimal import Decimal
+
+ >>> from proteus import Model, Wizard
+ >>> from trytond.tests.tools import activate_modules
+ >>> from trytond.modules.company.tests.tools import (
+ ... create_company, get_company)
+ >>> from trytond.modules.account.tests.tools import (
+ ... create_chart, get_accounts)
+
+ >>> today = datetime.date.today()
+ >>> later = today + relativedelta(days=30)
+
+
+Activate modules::
+
+ >>> config = activate_modules([
+ ... 'sale_secondary_unit', 'sale_blanket_agreement'])
+
+ >>> Party = Model.get('party.party')
+ >>> ProductCategory = Model.get('product.category')
+ >>> ProductUom = Model.get('product.uom')
+ >>> ProductTemplate = Model.get('product.template')
+ >>> SaleBlanketAgreement = Model.get('sale.blanket_agreement')
+
+Create company::
+
+ >>> _ = create_company()
+ >>> company = get_company()
+
+Create chart of accounts::
+
+ >>> _ = create_chart(company)
+ >>> accounts = get_accounts(company)
+
+Create customer::
+
+ >>> customer = Party(name="Customer")
+ >>> customer.save()
+
+Create account category::
+
+ >>> account_category = ProductCategory(name="Account Category")
+ >>> account_category.accounting = True
+ >>> account_category.account_expense = accounts['expense']
+ >>> account_category.account_revenue = accounts['revenue']
+ >>> account_category.save()
+
+Create product::
+
+ >>> unit, = ProductUom.find([('name', '=', 'Unit')])
+ >>> gr, = ProductUom.find([('name', '=', 'Gram')])
+ >>> template = ProductTemplate()
+ >>> template.name = "Product"
+ >>> template.default_uom = unit
+ >>> template.type = 'goods'
+ >>> template.list_price = Decimal('10.000')
+ >>> template.salable = True
+ >>> template.account_category = account_category
+ >>> template.sale_secondary_uom = gr
+ >>> template.sale_secondary_uom_factor = 100
+ >>> template.save()
+ >>> product, = template.products
+ >>> product.cost_price = Decimal('8.000')
+ >>> product.save()
+
+Create sale blanket agreement::
+
+ >>> blanket_agreement = SaleBlanketAgreement()
+ >>> blanket_agreement.customer = customer
+ >>> blanket_agreement.from_date = today
+ >>> blanket_agreement.to_date = later
+ >>> blanket_agreement_line = blanket_agreement.lines.new()
+ >>> blanket_agreement_line.product = product
+ >>> blanket_agreement_line.quantity = 800.0
+ >>> blanket_agreement_line.unit = gr
+ >>> blanket_agreement_line.unit_price = Decimal('9.000')
+ >>> blanket_agreement.click('run')
+ >>> blanket_agreement.state
+ 'running'
+
+Create sale from blanket agreement::
+
+ >>> create_sale = Wizard(
+ ... 'sale.blanket_agreement.create_sale', [blanket_agreement])
+ >>> create_sale.form.lines[0].unit == gr
+ True
+ >>> create_sale.execute('create_sale')
+
+ >>> sale, = create_sale.actions[0]
+ >>> line, = sale.lines
+ >>> line.product == product
+ True
+
+ >>> line.secondary_unit == gr
+ True
+ >>> line.secondary_quantity
+ 800.0
+ >>> line.secondary_unit_price
+ Decimal('9.0000')
+
+ >>> line.unit == unit
+ True
+ >>> line.quantity
+ 8.0
+ >>> line.unit_price
+ Decimal('900.0000')
+
+ >>> line.secondary_quantity = 300.0
+ >>> sale.save()
+
+ >>> blanket_agreement.reload()
+ >>> blanket_agreement_line, = blanket_agreement.lines
+ >>> blanket_agreement_line.remaining_quantity
+ 800.0
+
+Confirm sale::
+
+ >>> sale.click('quote')
+ >>> sale.click('confirm')
+ >>> sale.state
+ 'processing'
+
+ >>> blanket_agreement_line.reload()
+ >>> blanket_agreement_line.remaining_quantity
+ 500.0
diff -r b3787b2475d1 -r 48304d72685b tests/test_module.py
--- a/tests/test_module.py Mon May 02 17:51:28 2022 +0200
+++ b/tests/test_module.py Mon Oct 10 18:35:01 2022 +0200
@@ -9,7 +9,7 @@
module = 'sale_secondary_unit'
extras = [
'account_invoice_secondary_unit', 'stock_secondary_unit',
- 'sale_product_customer']
+ 'sale_product_customer', 'sale_blanket_agreement']
del ModuleTestCase
diff -r b3787b2475d1 -r 48304d72685b tryton.cfg
--- a/tryton.cfg Mon May 02 17:51:28 2022 +0200
+++ b/tryton.cfg Mon Oct 10 18:35:01 2022 +0200
@@ -8,6 +8,7 @@
stock
extras_depend:
account_invoice_secondary_unit
+ sale_blanket_agreement
sale_opportunity
sale_product_customer
stock_secondary_unit