From da0e5ee7a683649cf95826d7856da2b956440fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20=C3=80lvarez?= Date: Fri, 3 Sep 2021 11:35:21 +0200 Subject: [PATCH] production template #045158 --- __init__.py | 9 +- product.py | 21 +- production.py | 339 +++++++++++++++++++ production.xml | 114 +++++++ tests/scenario_production_template.rst | 329 ++++++++++++++++++ tests/test_agronomics.py | 9 +- tryton.cfg | 3 + view/production_enology_product_form.xml | 8 + view/production_enology_product_list.xml | 5 + view/production_form.xml | 19 ++ view/production_list.xml | 8 + view/production_output_distribution_form.xml | 17 + view/production_output_distribution_list.xml | 11 + view/production_template_form.xml | 16 + view/production_template_line_form.xml | 11 + view/production_template_line_list.xml | 8 + view/production_template_list.xml | 9 + view/template_form.xml | 3 + view/template_list.xml | 3 + 19 files changed, 928 insertions(+), 14 deletions(-) create mode 100644 production.py create mode 100644 production.xml create mode 100644 tests/scenario_production_template.rst create mode 100644 view/production_enology_product_form.xml create mode 100644 view/production_enology_product_list.xml create mode 100644 view/production_form.xml create mode 100644 view/production_list.xml create mode 100644 view/production_output_distribution_form.xml create mode 100644 view/production_output_distribution_list.xml create mode 100644 view/production_template_form.xml create mode 100644 view/production_template_line_form.xml create mode 100644 view/production_template_line_list.xml create mode 100644 view/production_template_list.xml diff --git a/__init__.py b/__init__.py index 74585e8..d59ef7a 100644 --- a/__init__.py +++ b/__init__.py @@ -7,7 +7,7 @@ from . import plot from . import product from . import weighing from . import quality - +from . import production def register(): Pool.register( @@ -37,6 +37,13 @@ def register(): quality.QualityTest, quality.QuantitativeTestLine, quality.QualitativeTestLine, + production.ProductionTemplate, + production.ProductionTemplateLine, + production.ProductionTemplateInputsProductTemplate, + production.ProductionTemplateOutputsProductTemplate, + production.Production, + production.OutputDistribution, + production.ProductionEnologyProduct, module='agronomics', type_='model') Pool.register( module='agronomics', type_='wizard') diff --git a/product.py b/product.py index f3a9658..e4c2a56 100644 --- a/product.py +++ b/product.py @@ -64,13 +64,21 @@ class Template(metaclass=PoolMeta): return [('container.capacity',) + tuple(clause[1:])] +class ProductVariety(ModelSQL, ModelView): + 'Product Variety' + __name__ = 'product.variety' + + variety = fields.Many2One('product.taxon', 'Variety', required=True) + percent = fields.Float('Percent', digits=(16, 4), required=True) + product = fields.Many2One('product.product', 'Product', required=True) + + class Product(WineMixin, metaclass=PoolMeta): __name__ = 'product.product' vintages = fields.Many2Many('product.product-agronomics.crop', 'product', 'crop', 'Vintages') - varieties = fields.Many2Many('product.product-product.taxon', 'product', - 'variety', 'Varieties') + varieties = fields.One2Many('product.variety', 'product', 'Varieties') denominations_of_origin = fields.Many2Many( 'product.product-agronomics.denomination_of_origin', 'product', 'do', 'DOs', @@ -131,15 +139,6 @@ class ProductCrop(ModelSQL): ondelete='CASCADE', select=True, required=True) -class ProductVariety(ModelSQL): - "Product - Variety" - __name__ = 'product.product-product.taxon' - product = fields.Many2One('product.product', 'Product', - ondelete='CASCADE', select=True, required=True) - variety = fields.Many2One('product.taxon', 'Variety', - ondelete='CASCADE', select=True, required=True) - - class ProductDO(ModelSQL): "Product - DO" __name__ = 'product.product-agronomics.denomination_of_origin' diff --git a/production.py b/production.py new file mode 100644 index 0000000..88fa138 --- /dev/null +++ b/production.py @@ -0,0 +1,339 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +from trytond.model import ModelSQL, ModelView, fields +from trytond.pool import PoolMeta, Pool +from trytond.pyson import Eval, Bool +from trytond.exceptions import UserError +from trytond.i18n import gettext +from decimal import Decimal +from trytond.transaction import Transaction + +class ProductionTemplate(ModelSQL, ModelView): + "Produciton Template" + __name__ = 'production.template' + + name = fields.Char('Name', required=True) + uom = fields.Many2One('product.uom', 'Uom', required=True) + unit_digits = fields.Function(fields.Integer('Unit Digits'), + 'on_change_with_unit_digits') + quantity = fields.Float('Quantity', + digits=(16, Eval('unit_digits', 2)), + depends=['unit_digits']) + + inputs = fields.Many2Many('production.template.inputs-product.template', + 'production_template', 'template', "Inputs") + + outputs = fields.Many2Many('production.template.outputs-product.template', + 'production_template', 'template', "Outputs") + + enology_products = fields.One2Many('production.template.line', + 'production_template', 'Production Template') + pass_feature = fields.Boolean('Pass on Feature') + + @fields.depends('uom') + def on_change_with_unit_digits(self, name=None): + if self.uom: + return self.uom.digits + return 2 + + @classmethod + def check_input_uoms(cls, records): + for record in records: + category_uom = record.uom.category + uoms = [i.default_uom.category for i in record.inputs] + if not all(uoms+[category_uom]): + raise UserError(gettext('agronomics.msg_uom_not_fit', + production=record.rec_name, + uom=record.uom.rec_name, + uoms=",".join([x.rec_name for x in set(uoms)]))) + + @classmethod + def validate(cls, records): + super().validate(records) + cls.check_input_uoms(records) + + +class ProductionTemplateInputsProductTemplate(ModelSQL): + 'Production Template Inputs- Product Template' + __name__ = 'production.template.inputs-product.template' + production_template = fields.Many2One('production.template', + 'Production Template', ondelete='CASCADE', required=True, select=True) + template = fields.Many2One('product.template', 'Template', + ondelete='CASCADE', required=True, select=True) + + +class ProductionTemplateOutputsProductTemplate(ModelSQL): + 'Production Template Inputs- Product Template' + __name__ = 'production.template.outputs-product.template' + production_template = fields.Many2One('production.template', + 'Production Template', ondelete='CASCADE', required=True, select=True) + template = fields.Many2One('product.template', 'Product', + ondelete='CASCADE', required=True, select=True) + + +class ProductionTemplateLine(ModelSQL, ModelView): + "Production Template Line" + __name__ = 'production.template.line' + + product = fields.Many2One('product.product', 'Producte', required=True) + uom = fields.Many2One('product.uom', 'Uom') + unit_digits = fields.Function(fields.Integer('Unit Digits'), + 'on_change_with_unit_digits') + quantity = fields.Float('Quantity', + digits=(16, Eval('unit_digits', 2)), + depends=['unit_digits']) + production_template = fields.Many2One('production.template', + 'Production Template') + + @fields.depends('uom') + def on_change_with_unit_digits(self, name=None): + if self.uom: + return self.uom.digits + return 2 + + @fields.depends('product') + def on_change_with_uom(self): + if not self.product: + return + return self.product.default_uom and self.product.default_uom.id + + +class Production(metaclass=PoolMeta): + __name__ = 'production' + + production_template = fields.Many2One('production.template', + 'Production Template') + enology_products = fields.One2Many('production.enology.product', + 'production', "Enology Products", + domain=[('product', 'in', Eval('allowed_enology_products')), + ('product.quantity', '>', 0)], + states={ + 'invisible': ~Bool(Eval('production_template')) + }, depends=['allowed_enology_products']) + output_distribution = fields.One2Many('production.output.distribution', + 'production', 'Output Distribution', + # domain=[('product', 'in', Eval('allowed_ouput_products'))], + states={ + 'invisible': ~Bool(Eval('production_template')) + }, depends=['allowed_output_products']) + allowed_enology_products = fields.Function(fields.One2Many( + 'product.product', None, 'Allowed Enology Products', readonly=True), + 'on_change_with_allowed_enology_products', + setter='set_allowed_products') + allowed_output_products = fields.Function(fields.One2Many( + 'product.template', None, 'Allowed Output Products', readonly=True), + 'on_change_with_allowed_output_products', + setter='set_allowed_products') + + @classmethod + def set_allowed_products(cls, productions, name, value): + pass + + @fields.depends('production_template') + def on_change_with_allowed_enology_products(self, name=None): + products = [] + if not self.production_template: + return [] + for template in self.production_template.inputs: + products += template.products + return [x.id for x in products] + + @fields.depends('production_template') + def on_change_with_allowed_output_products(self, name=None): + if not self.production_template: + return [] + return [x.id for x in self.production_template.outputs] + + @classmethod + def wait(cls, productions): + Move = Pool().get('stock.move') + Uom = Pool().get('product.uom') + moves = [] + delete = [] + for production in productions: + delete = [x for x in production.inputs] + input_quantity = 0 + for enology in production.enology_products: + move = production._move(production.picking_location, + production.location, + production.company, + enology.product, + enology.uom.id, + enology.quantity) + move.production_input = production + moves.append(move) + input_quantity += Uom.compute_qty(enology.uom, enology.quantity, + production.production_template.uom) + enology_products = (production.production_template and + production.production_template.enology_products or []) + for enology in enology_products: + quantity = Uom.compute_qty(enology.uom, enology.quantity, + production.production_template.uom) + ratio = quantity / (input_quantity or 1) + qty = Decimal(str(input_quantity*ratio)) + move = production._move(production.picking_location, + production.location, + production.company, + enology.product, + enology.uom.id, + float(qty)) + move.production_input = production + moves.append(move) + + Move.save(moves) + Move.delete(delete) + super().wait(productions) + + def create_variant(self, template, pass_feature): + Product = Pool().get('product.product') + product = Product() + product.template = template + return product + + def pass_feature(self, product): + Variety = Pool().get('product.variety') + Uom = Pool().get('product.uom') + total_output = sum([Uom.compute_qty(x.uom, x.quantity, + x.product.default_uom) + for x in self.inputs]) + vintages = [] + do = [] + ecologicals = [] + for input in self.inputs: + vintages += input.product.vintages + do += input.product.denominations_of_origin + ecologicals = input.product.ecologicals + + product.denominations_of_origin = list(set(do)) + product.ecologicals = list(set(ecologicals)) + product.vintages = list(set(vintages)) + varieties = {} + for input in self.inputs: + percent = round(input.quantity/total_output, 2) + for variety in input.product.varieties: + new_variety = varieties.get(variety.variety) + if not new_variety: + new_variety = Variety() + new_variety.percent = 0 + new_variety.variety = variety.variety + new_variety.percent += variety.percent/100*percent + varieties[new_variety.variety] = new_variety + for key, variety in varieties.items(): + variety.percent = "%.4f" % round(100*variety.percent, 4) + product.varieties = varieties.values() + return product + + @classmethod + def done(cls, productions): + Move = Pool().get('stock.move') + moves = [] + for production in productions: + for distrib in production.output_distribution: + product = production.create_variant(distrib.product, + production.production_template.pass_feature) + product = production.pass_feature(product) + move = production._move(production.location, + distrib.location, + production.company, + product, + distrib.uom, + distrib.produced_quantity) + move.production_output = production + move.unit_price = Decimal(0) + moves.append(move) + Move.save(moves) + super().done(productions) + +class OutputDistribution(ModelSQL, ModelView): + 'Output Distribution' + __name__ = 'production.output.distribution' + + production = fields.Many2One('production', 'Production', + required=True) + product = fields.Many2One('product.template', 'Template', required=True) + location = fields.Many2One('stock.location', 'Location', required=True) + uom = fields.Many2One('product.uom', 'Uom') + unit_digits = fields.Function(fields.Integer('Unit Digits'), + 'on_change_with_unit_digits') + initial_quantity = fields.Float('Initial Quantity', + digits=(16, Eval('unit_digits', 2)), + depends=['unit_digits'], readonly=True) + final_quantity = fields.Float('Final Quantity', + digits=(16, Eval('unit_digits', 2)), + depends=['unit_digits'], readonly=True) + produced_quantity = fields.Float('Produced Quantity', + digits=(16, Eval('unit_digits', 2)), + depends=['unit_digits']) + + @fields.depends('product') + def on_change_with_uom(self): + if not self.product: + return + return self.product.default_uom and self.product.default_uom.id + + @fields.depends('uom') + def on_change_with_unit_digits(self, name=None): + if self.uom: + return self.uom.digits + return 2 + + @fields.depends('initial_quantity', 'final_quantity', 'produced_quantity', + 'location', 'product') + def on_change_product(self): + Product = Pool().get('product.product') + if not self.product: + self.initial_quantity = 0 + self.final_quantity = self.produced_quantity + return + if not self.location: + return + context = Transaction().context + context['locations'] = [self.location.id] + with Transaction().set_context(context): + quantities = Product.get_quantity(self.product.products, 'quantity') + + self.initial_quantity = sum(quantities.values()) + self.final_quantity = self.initial_quantity + (self.produced_quantity + or 0) + + @fields.depends('location', methods=['on_change_product']) + def on_change_location(self): + if not self.location: + return + self.on_change_product() + + @fields.depends('produced_quantity', 'final_quantity', 'initial_quantity') + def on_change_produced_quantity(self): + self.final_quantity = ((self.initial_quantity or 0) + + (self.produced_quantity or 0)) + +class ProductionEnologyProduct(ModelSQL, ModelView): + 'Production Enology Product' + __name__ = 'production.enology.product' + production = fields.Many2One('production', 'Production', + select=True) + product = fields.Many2One('product.product', 'Product', required=True) + uom = fields.Many2One('product.uom', 'Uom') + unit_digits = fields.Function(fields.Integer('Unit Digits'), + 'on_change_with_unit_digits') + quantity = fields.Float('Quantity', + digits=(16, Eval('unit_digits', 2)), + depends=['unit_digits']) + + @fields.depends('uom') + def on_change_with_unit_digits(self, name=None): + if self.uom: + return self.uom.digits + return 2 + + @fields.depends('product') + def on_change_with_uom(self): + if not self.product: + return + return self.product.default_uom and self.product.default_uom.id + + @fields.depends('product') + def on_change_product(self): + if not self.product: + return + self.quantity = self.product.quantity diff --git a/production.xml b/production.xml new file mode 100644 index 0000000..721cbbc --- /dev/null +++ b/production.xml @@ -0,0 +1,114 @@ + + + + + production.template + form + production_template_form + + + production.template + tree + + production_template_list + + + + Production Template + production.template + + + + + + + + + + + + + + + + + + + + + + + + + + + production.template.line + form + production_template_line_form + + + production.template.line + tree + + production_template_line_list + + + + Production Template Line + production.template.line + + + + + + production.output.distribution + form + production_output_distribution_form + + + production.output.distribution + tree + + production_output_distribution_list + + + + Production Output Distribution + production.output.distribution + + + + + production.enology.product + form + production_enology_product_form + + + production.enology.product + tree + + production_enology_product_list + + + + Production Enology Product + production.enology.product + + + + production + + production_form + + + + production + + production_list + + + + diff --git a/tests/scenario_production_template.rst b/tests/scenario_production_template.rst new file mode 100644 index 0000000..9c4a14b --- /dev/null +++ b/tests/scenario_production_template.rst @@ -0,0 +1,329 @@ +=================== +Production 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.production.production import BOM_CHANGES + >>> today = datetime.date.today() + >>> yesterday = today - relativedelta(days=1) + >>> before_yesterday = yesterday - relativedelta(days=1) + +Activate modules:: + + >>> config = activate_modules('agronomics') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + + +Create product:: + + >>> ProductUom = Model.get('product.uom') + >>> liter, = ProductUom.find([('name', '=', 'Liter')]) + >>> kg, = ProductUom.find([('name', '=', 'Kilogram')]) + + >>> ProductTemplate = Model.get('product.template') + >>> Product = Model.get('product.product') + >>> Taxon = Model.get('product.taxon') + >>> DO = Model.get('agronomics.denomination_of_origin') + >>> Ecological = Model.get('agronomics.ecological') + +Create Denomination Of Origin:: + + >>> catalunya = DO() + >>> catalunya.name = 'Catalunya' + >>> catalunya.save() + + >>> barcelona = DO() + >>> barcelona.name = 'Barcelona' + >>> barcelona.save() + +Create Taxon:: + + >>> macabeu = Taxon() + >>> macabeu.rank = 'variety' + >>> macabeu.name = 'Macabeu' + >>> macabeu.save() + + >>> parellada = Taxon() + >>> parellada.rank = 'variety' + >>> parellada.name = 'Parellada' + >>> parellada.save() + +Create Ecological:: + + >>> ecological = Ecological() + >>> ecological.name = 'Ecological' + >>> ecological.save() + + # Raim Blanc + >>> template = ProductTemplate() + >>> template.name = 'Raim Blanc' + >>> template.default_uom = kg + >>> template.type = 'goods' + >>> template.producible = True + >>> template.list_price = Decimal(0) + >>> product, = template.products + >>> product.cost_price = Decimal(10) + >>> template.save() + >>> product, = template.products + >>> productA = Product() + >>> productA.code = "A" + >>> productA.template = template + >>> productA.denominations_of_origin.append(catalunya) + >>> productA.save() + >>> catalunya, = DO.find([('name', '=', 'Catalunya')]) + >>> productB = Product() + >>> productB.code = "B" + >>> productB.template = template + >>> productB.denominations_of_origin.append(catalunya) + >>> productB.save() + >>> catalunya, = DO.find([('name', '=' , 'Catalunya')]) + >>> productC = Product() + >>> productC.code = "C" + >>> productC.template = template + >>> productC.denominations_of_origin.append(catalunya) + >>> productC.save() + +Create Variety:: + + >>> Variety = Model.get('product.variety') + >>> AM = Variety() + >>> AM.variety = macabeu + >>> AM.percent = 100.0 + >>> AM.product = productA + >>> AM.save() + + >>> BM = Variety() + >>> BM.variety = macabeu + >>> BM.percent = 100.0 + >>> BM.product = productB + >>> BM.save() + + >>> CP = Variety() + >>> CP.variety = parellada + >>> CP.percent = 100.0 + >>> CP.product = productC + >>> CP.save() + + # Sulforos + >>> template = ProductTemplate() + >>> template.name = 'Sulforos' + >>> template.default_uom = kg + >>> template.type = 'goods' + >>> template.producible = True + >>> template.list_price = Decimal(0) + >>> product2, = template.products + >>> product2.cost_price = Decimal(10) + >>> template.save() + >>> product2, = template.products + + # Encims + >>> template = ProductTemplate() + >>> template.name = 'Encims' + >>> template.default_uom = kg + >>> template.type = 'goods' + >>> template.producible = True + >>> template.list_price = Decimal(0) + >>> product3, = template.products + >>> product3.cost_price = Decimal(10) + >>> template.save() + >>> product3, = template.products + + # Carbo actiu + >>> template = ProductTemplate() + >>> template.name = 'Carbo actiu' + >>> template.default_uom = kg + >>> template.type = 'goods' + >>> template.producible = True + >>> template.list_price = Decimal(0) + >>> product4, = template.products + >>> product4.cost_price = Decimal(10) + >>> template.save() + >>> product4, = template.products + + # Most flor + >>> mostflor = ProductTemplate() + >>> mostflor.name = 'Most Flor' + >>> mostflor.default_uom = liter + >>> mostflor.type = 'goods' + >>> mostflor.producible = True + >>> mostflor.list_price = Decimal(0) + >>> mostflor.save() + >>> product5, = mostflor.products + >>> product5.cost_price = Decimal(0) + >>> product5.save() + + # Most Primeres + >>> mostprimeres = ProductTemplate() + >>> mostprimeres.name = 'Most Primeres' + >>> mostprimeres.default_uom = liter + >>> mostprimeres.type = 'goods' + >>> mostprimeres.producible = True + >>> mostprimeres.list_price = Decimal(0) + >>> mostprimeres.save() + >>> product6, = mostprimeres.products + >>> product6.cost_price = Decimal(0) + >>> product6.save() + +Create Production Template:: + + >>> ProductionTemplate = Model.get('production.template') + >>> ProductionTemplateLine = Model.get("production.template.line") + >>> production_template = ProductionTemplate() + >>> production_template.name = 'Premsat i desfangat de raim blanc' + >>> production_template.uom = kg + >>> production_template.quantity = 10000 + >>> production_template.pass_feature = True + >>> production_template.inputs.append(product.template) + >>> production_template.outputs.append(mostflor) + >>> production_template.outputs.append(mostprimeres) + >>> line = ProductionTemplateLine() + >>> line.product = product2 + >>> line.quantity = 100 + >>> production_template.enology_products.append(line) + >>> line = ProductionTemplateLine() + >>> line.product = product3 + >>> line.quantity = 50 + >>> production_template.enology_products.append(line) + >>> line = ProductionTemplateLine() + >>> line.product = product4 + >>> line.quantity =150 + >>> production_template.enology_products.append(line) + >>> production_template.save() + + +Create an Inventory:: + + >>> Inventory = Model.get('stock.inventory') + >>> InventoryLine = Model.get('stock.inventory.line') + >>> Location = Model.get('stock.location') + >>> storage, = Location.find([ + ... ('code', '=', 'STO'), + ... ]) + >>> inventory = Inventory() + >>> inventory.location = storage + >>> inventory_line1 = InventoryLine() + >>> inventory.lines.append(inventory_line1) + >>> inventory_line1.product = productA + >>> inventory_line1.quantity = 5000 + >>> inventory_line2 = InventoryLine() + >>> inventory.lines.append(inventory_line2) + >>> inventory_line2.product = productB + >>> inventory_line2.quantity = 10000 + >>> inventory_line3 = InventoryLine() + >>> inventory.lines.append(inventory_line3) + >>> inventory_line3.product = productC + >>> inventory_line3.quantity = 3000 + + >>> inventory_line3 = InventoryLine() + >>> inventory.lines.append(inventory_line3) + >>> inventory_line3.product = product2 + >>> inventory_line3.quantity = 1000 + + >>> inventory_line3 = InventoryLine() + >>> inventory.lines.append(inventory_line3) + >>> inventory_line3.product = product3 + >>> inventory_line3.quantity = 1000 + + >>> inventory_line3 = InventoryLine() + >>> inventory.lines.append(inventory_line3) + >>> inventory_line3.product = product4 + >>> inventory_line3.quantity = 1000 + + >>> inventory.click('confirm') + >>> inventory.state + 'done' + +Create Production + + >>> Production = Model.get('production') + >>> EnologyProduct = Model.get('production.enology.product') + >>> production = Production() + >>> production.production_template = production_template + >>> production.save() + >>> line = EnologyProduct() + >>> line.product = productA + >>> line.production = production + >>> line.quantity = 5000 + >>> line.save() + >>> # production.enology_products.append(productA) + >>> line = EnologyProduct() + >>> line.product = productB + >>> line.quantity = 10000 + >>> line.production = production + >>> line.save() + >>> # production.enology_products.append(productB) + >>> line = EnologyProduct() + >>> line.product = productC + >>> line.quantity = 3000 + >>> line.production = production + >>> line.save() + >>> # production.enology_products.append(productC) + >>> production.reload() + >>> production.click('wait') + >>> production.state + 'waiting' + >>> len(production.inputs) + 6 + >>> input, = [i for i in production.inputs if i.product == product2] + >>> input.quantity + 100.0 + >>> input, = [i for i in production.inputs if i.product == product3] + >>> input.quantity + 50.0 + >>> input, = [i for i in production.inputs if i.product == product4] + >>> input.quantity + 150.0 + + >>> OutputDistribution = Model.get('production.output.distribution') + >>> m1 = OutputDistribution() + >>> m1.production = production + >>> m1.product = mostflor + >>> m1.location = storage + >>> m1.produced_quantity = 3000 + >>> production.output_distribution.append(m1) + + >>> m2 = OutputDistribution() + >>> m2.production = production + >>> m2.product = mostflor + >>> m2.location = storage + >>> m2.produced_quantity = 1500 + >>> production.output_distribution.append(m2) + + >>> m3 = OutputDistribution() + >>> m3.production = production + >>> m3.product = mostprimeres + >>> m3.location = storage + >>> m3.produced_quantity = 3500 + >>> production.output_distribution.append(m3) + >>> #import pdb; pdb.set_trace() + >>> production.save() + >>> production.reload() + >>> len(production.output_distribution) + 3 + >>> #[x.name for x in production.allowed_output_products] + >>> #[x.name for x in production.production_template.outputs] + >>> production.click('assign_try') + True + >>> production.click('run') + >>> production.click('done') + >>> len(production.outputs) + 3 + >>> most = production.outputs[0] + >>> len(most.product.varieties) + 2 + >>> [(x.variety.name, x.percent) for x in most.product.varieties] + [('Parellada', 16.0), ('Macabeu', 82.0)] + >>> [x.name for x in most.product.denominations_of_origin] + ['Catalunya'] diff --git a/tests/test_agronomics.py b/tests/test_agronomics.py index 463be84..51754b9 100644 --- a/tests/test_agronomics.py +++ b/tests/test_agronomics.py @@ -2,10 +2,11 @@ # The COPYRIGHT file at the top level of this repository contains # the full copyright notices and license terms. import unittest - - +import doctest from trytond.tests.test_tryton import ModuleTestCase from trytond.tests.test_tryton import suite as test_suite +from trytond.tests.test_tryton import doctest_teardown +from trytond.tests.test_tryton import doctest_checker class AgronomicsTestCase(ModuleTestCase): @@ -17,4 +18,8 @@ def suite(): suite = test_suite() suite.addTests(unittest.TestLoader().loadTestsFromTestCase( AgronomicsTestCase)) + suite.addTests(doctest.DocFileSuite('scenario_production_template.rst', + tearDown=doctest_teardown, encoding='utf-8', + checker=doctest_checker, + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite diff --git a/tryton.cfg b/tryton.cfg index 9e11d21..4ce2ddc 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -6,8 +6,10 @@ depends: party product_classification product_classification_taxonomic + product_template_form_quantity quality_control_sample purchase_contract + production xml: plot.xml party.xml @@ -15,3 +17,4 @@ xml: weighning.xml wine.xml message.xml + production.xml diff --git a/view/production_enology_product_form.xml b/view/production_enology_product_form.xml new file mode 100644 index 0000000..21a81dd --- /dev/null +++ b/view/production_enology_product_form.xml @@ -0,0 +1,8 @@ +
+