From 6231a6153b994e50cddd32aef8be84d47967883a Mon Sep 17 00:00:00 2001 From: Raimon Esteve Date: Tue, 19 Oct 2021 08:19:06 +0200 Subject: [PATCH] Cost distribution (#5) * Add feauture of Cost distribution #045169 --- __init__.py | 7 +- locale/ca.po | 176 ++++++++- locale/es.po | 184 +++++++++- message.xml | 19 +- plot.py | 2 +- production.py | 342 ++++++++++++++++-- production.xml | 114 ++++++ ...istribution_from_production_start_form.xml | 9 + ...uction_cost_distribution_template_form.xml | 11 + ...uction_cost_distribution_template_tree.xml | 7 + ...roduction_cost_price_distribution_form.xml | 11 + ...roduction_cost_price_distribution_tree.xml | 9 + view/production_form.xml | 7 + view/production_template_form.xml | 3 + 14 files changed, 862 insertions(+), 39 deletions(-) create mode 100644 view/create_cost_price_distribution_from_production_start_form.xml create mode 100644 view/production_cost_distribution_template_form.xml create mode 100644 view/production_cost_distribution_template_tree.xml create mode 100644 view/production_cost_price_distribution_form.xml create mode 100644 view/production_cost_price_distribution_tree.xml diff --git a/__init__.py b/__init__.py index d59ef7a..d8a8a6f 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ -# This file is part carviresa module for Tryton. +# This file is part agronomics module for Tryton. # The COPYRIGHT file at the top level of this repository contains # the full copyright notices and license terms. from trytond.pool import Pool @@ -44,8 +44,13 @@ def register(): production.Production, production.OutputDistribution, production.ProductionEnologyProduct, + production.ProductionCostPriceDistribution, +# production.ProductionProductionCostPriceDistribution, + production.ProductionCostPriceDistributionTemplate, + production.ProductionCostPriceDistributionTemplateProductionTemplateAsk, module='agronomics', type_='model') Pool.register( + production.ProductionCostPriceDistributionTemplateProductionTemplate, module='agronomics', type_='wizard') Pool.register( module='agronomics', type_='report') diff --git a/locale/ca.po b/locale/ca.po index dae7689..961670d 100644 --- a/locale/ca.po +++ b/locale/ca.po @@ -372,7 +372,7 @@ msgstr "Necesita mostres" msgctxt "field:product.product,production_quality_template:" msgid "Production Quality Template" -msgstr "" +msgstr "Plantilla Qualitat de producció" msgctxt "field:product.product,quality_sample:" msgid "Quality Sample" @@ -838,6 +838,18 @@ msgctxt "field:production,allowed_output_products:" msgid "Allowed Output Products" msgstr "Productes permesos a la sortida" +msgctxt "field:production,cost_distribution_template:" +msgid "Cost Distribution Template" +msgstr "Plantilla distribució de costos" + +msgctxt "field:production,cost_distribution_templates:" +msgid "Cost Product Templates" +msgstr "Plantilla cost del producte" + +msgctxt "field:production,cost_distributions:" +msgid "Cost Distributions" +msgstr "Distribució de costos" + msgctxt "field:production,enology_products:" msgid "Enology Products" msgstr "Productes Vinícules" @@ -850,6 +862,45 @@ msgctxt "field:production,production_template:" msgid "Production Template" msgstr "Plantilla de producció" +msgctxt "field:production,production_template_cost_distribution_templates:" +msgid "Cost Distribution Templates" +msgstr "Plantilla distribució costos" + +msgctxt "field:production.cost_price.distribution,origin:" +msgid "Origin" +msgstr "Origen" + +msgctxt "field:production.cost_price.distribution,percentatge:" +msgid "Percentatge" +msgstr "Percentatge" + +msgctxt "field:production.cost_price.distribution,template:" +msgid "Template" +msgstr "Plantilla" + +msgctxt "" +"field:production.cost_price.distribution.template,cost_distribution_templates:" +msgid "Cost Distribution" +msgstr "Distribució de costos" + +msgctxt "field:production.cost_price.distribution.template,name:" +msgid "Name" +msgstr "Nom" + +msgctxt "" +"field:production.cost_price.distribution.template,production_template:" +msgid "Production Template" +msgstr "Plantilla de producció" + +msgctxt "" +"field:production.cost_price.distribution.template.ask,cost_distribution_templates:" +msgid "Cost Distributions" +msgstr "Distribució de costos" + +msgctxt "field:production.cost_price.distribution.template.ask,name:" +msgid "Name" +msgstr "Nom" + msgctxt "field:production.enology.product,product:" msgid "Product" msgstr "Productes" @@ -878,6 +929,10 @@ msgctxt "field:production.output.distribution,initial_quantity:" msgid "Initial Quantity" msgstr "Quantitat inicial" +msgctxt "field:production.output.distribution,initial_quantity_readonly:" +msgid "Initial Quantity" +msgstr "Quantitat inicial" + msgctxt "field:production.output.distribution,location:" msgid "Location" msgstr "Ubicació" @@ -894,6 +949,10 @@ msgctxt "field:production.output.distribution,production:" msgid "Production" msgstr "Producció" +msgctxt "field:production.output.distribution,production_state:" +msgid "State" +msgstr "Estat" + msgctxt "field:production.output.distribution,unit_digits:" msgid "Unit Digits" msgstr "Dígits de la unitat" @@ -902,8 +961,16 @@ msgctxt "field:production.output.distribution,uom:" msgid "Uom" msgstr "UdM" +msgctxt "field:production.template,cost_distribution_template:" +msgid "Default cost distribution template" +msgstr "Plantilla distribució de cost per defecte" + +msgctxt "field:production.template,cost_distribution_templates:" +msgid "Cost Distribution Templates" +msgstr "Plantilla de distribucció de costos" + msgctxt "field:production.template,enology_products:" -msgid "Production Template" +msgid "Complementary Products" msgstr "Plantilla de producció" msgctxt "field:production.template,inputs:" @@ -1100,13 +1167,21 @@ msgctxt "model:ir.action,name:act_product_quantitative_test_lines" msgid "Quantitative Lines" msgstr "Línia quantitativa" +msgctxt "model:ir.action,name:act_production_cost_distribution_template_tree" +msgid "Templates Cost Price Distribution" +msgstr "Plantilles Distribució de cost" + +msgctxt "model:ir.action,name:act_production_cost_price_distribution_tree" +msgid "Cost Price Distribution" +msgstr "Preus de la Distribució de costos" + msgctxt "model:ir.action,name:act_production_enology_product_tree" msgid "Production Enology Product" msgstr "Productes Vinícules" msgctxt "model:ir.action,name:act_production_output_distribution_tree" msgid "Production Output Distribution" -msgstr "" +msgstr "Distribució sortida de producció" msgctxt "model:ir.action,name:act_production_template_line_tree" msgid "Production Template Line" @@ -1124,6 +1199,11 @@ msgctxt "model:ir.action,name:act_weighing_center_action" msgid "Weighing Center" msgstr "Centre de pesada" +msgctxt "" +"model:ir.action,name:wizard_create_cost_price_distribution_from_production" +msgid "Create Cost Price Distribution" +msgstr "Crear Distribució de costos" + msgctxt "model:ir.action.act_window.domain,name:act_weighing_domain_all" msgid "All" msgstr "Tots" @@ -1149,10 +1229,41 @@ msgstr "" "Els Beneficiares de les collites \"%(crop)s\" i la plantació " "\"%(plantation)s\" han de sumar 100." +msgctxt "model:ir.message,text:msg_check_cost_distribution" +msgid "The production \"%(production)s\" has same products in cost distribution." +msgstr "" +"La producció \"%(producció)s\" té els mateixos productes en distribució de " +"costos." + +msgctxt "model:ir.message,text:msg_check_cost_distribution_template" +msgid "" +"There are some products in cost distribution that has not in outputs in the " +"production \"%(production)s\"." +msgstr "" +"Hi ha alguns productes en distribució de costos que no tenen resultats en la" +" producció \"% (producció) s\"." + +msgctxt "" +"model:ir.message,text:msg_check_cost_distribution_template_percentatge" +msgid "Invalid percentatge \"%(percentatge)s\" in \"%(distribution)s\"." +msgstr "Percentatge \"%(percentatge)s\" no vàlid a \"%(distribució)s\"." + +msgctxt "model:ir.message,text:msg_check_cost_templates" +msgid "" +"Cost \"%(cost)s\" has template \"%(template)s\" not present in Production " +"Template Outputs\"." +msgstr "" +"Cost \"%(cost)s\" té una \"%(producte)s\" que no està present en les " +"sortides de la plantilla de producció." + +msgctxt "model:ir.message,text:msg_check_production_percentatge" +msgid "Invalid percentatge \"%(percentatge)s\" in \"%(production)s\"." +msgstr "Percentatge \"%(percentatge)s\" no vàlid a \"%(producció)s\"." + msgctxt "model:ir.message,text:msg_uom_not_fit" msgid "" -"Inputs from Production template \"%(producttion)s\" must be of uom " -"\"%(uom)s\" and we have \"%(uoms)s\" ." +"Inputs from Production template \"%(production)s\" must be of uom " +"\"%(uom)s\" and we have \"%(uoms)s\"." msgstr "" "Les entrades de la plantilla de producció \"%(production)s\" ha de tenir la " "uom \"%(uom)s\" i ens trobem \"%(uoms)\"." @@ -1201,6 +1312,10 @@ msgctxt "model:ir.sequence.type,name:sequence_type_weighing" msgid "Weighing" msgstr "Pesada" +msgctxt "model:ir.ui.menu,name:menu_act_production_cost_distribution_template" +msgid "Templates Cost Price Distribution" +msgstr "Plantilla distribució de preus de cost" + msgctxt "model:ir.ui.menu,name:menu_agronomics" msgid "Agronomics" msgstr "Agronomics" @@ -1281,6 +1396,19 @@ msgctxt "model:product.variety,name:" msgid "Product Variety" msgstr "Varietat" +msgctxt "model:production.cost_price.distribution,name:" +msgid "Production Distribution Cost Price" +msgstr "Distribució preu de cost producció" + +msgctxt "model:production.cost_price.distribution.template,name:" +msgid "Production Cost Price Distribution Template" +msgstr "Plantilla de distribució de preus de costos de la producció" + +msgctxt "model:production.cost_price.distribution.template.ask,name:" +msgid "" +"Production Cost Price Distribution Template from Production Template Ask" +msgstr "" + msgctxt "model:production.enology.product,name:" msgid "Production Enology Product" msgstr "Productes Vinícules" @@ -1589,6 +1717,44 @@ msgctxt "selection:product.template,agronomic_type:" msgid "Wine" msgstr "Vi" +msgctxt "selection:production.output.distribution,production_state:" +msgid "Assigned" +msgstr "Reservat" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Cancelled" +msgstr "Cancel·lat" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Done" +msgstr "Realitzat" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Draft" +msgstr "Esborrany" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Request" +msgstr "Sol·licitud" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Running" +msgstr "En execució" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Waiting" +msgstr "En espera" + msgctxt "view:product.product:" msgid "Agronomics" msgstr "Agronomics" + +msgctxt "" +"wizard_button:production.cost_price.distribution.template.from.production.template,ask,create_cost_distributions:" +msgid "Create" +msgstr "Crea" + +msgctxt "" +"wizard_button:production.cost_price.distribution.template.from.production.template,ask,end:" +msgid "Cancel" +msgstr "Cancel·lat" diff --git a/locale/es.po b/locale/es.po index 3c08b38..6d41b27 100644 --- a/locale/es.po +++ b/locale/es.po @@ -256,7 +256,7 @@ msgstr "Productos" msgctxt "field:agronomics.weighing,product_created:" msgid "Product Created" -msgstr "" +msgstr "Producto creado" msgctxt "field:agronomics.weighing,purchase_contract:" msgid "Purchase Contract" @@ -287,7 +287,7 @@ msgid "Weighing Center" msgstr "Centro de pesada" msgctxt "field:agronomics.weighing,weighing_date:" -msgid "Sale Date" +msgid "Date" msgstr "Fecha" msgctxt "field:agronomics.weighing,weight:" @@ -372,7 +372,7 @@ msgstr "Requiere muestras" msgctxt "field:product.product,production_quality_template:" msgid "Production Quality Template" -msgstr "" +msgstr "Plantilla calidad producción" msgctxt "field:product.product,quality_sample:" msgid "Quality Sample" @@ -838,6 +838,18 @@ msgctxt "field:production,allowed_output_products:" msgid "Allowed Output Products" msgstr "Productos de salida permitidos" +msgctxt "field:production,cost_distribution_template:" +msgid "Cost Distribution Template" +msgstr "Plantilla de distribución de costos" + +msgctxt "field:production,cost_distribution_templates:" +msgid "Cost Product Templates" +msgstr "Plantillas de productos de coste" + +msgctxt "field:production,cost_distributions:" +msgid "Cost Distributions" +msgstr "Distribución de costos" + msgctxt "field:production,enology_products:" msgid "Enology Products" msgstr "Productos vinícolas" @@ -850,6 +862,45 @@ msgctxt "field:production,production_template:" msgid "Production Template" msgstr "Plantilla producción" +msgctxt "field:production,production_template_cost_distribution_templates:" +msgid "Cost Distribution Templates" +msgstr "Plantilla de distribución de costos" + +msgctxt "field:production.cost_price.distribution,origin:" +msgid "Origin" +msgstr "Origen" + +msgctxt "field:production.cost_price.distribution,percentatge:" +msgid "Percentatge" +msgstr "" + +msgctxt "field:production.cost_price.distribution,template:" +msgid "Template" +msgstr "Plantilla" + +msgctxt "" +"field:production.cost_price.distribution.template,cost_distribution_templates:" +msgid "Cost Distribution" +msgstr "Distribución de costos" + +msgctxt "field:production.cost_price.distribution.template,name:" +msgid "Name" +msgstr "Nombre" + +msgctxt "" +"field:production.cost_price.distribution.template,production_template:" +msgid "Production Template" +msgstr "Plantilla producción" + +msgctxt "" +"field:production.cost_price.distribution.template.ask,cost_distribution_templates:" +msgid "Cost Distributions" +msgstr "Distribución de costos" + +msgctxt "field:production.cost_price.distribution.template.ask,name:" +msgid "Name" +msgstr "Nombre" + msgctxt "field:production.enology.product,product:" msgid "Product" msgstr "Productos" @@ -878,6 +929,10 @@ msgctxt "field:production.output.distribution,initial_quantity:" msgid "Initial Quantity" msgstr "Cantidad Inicial" +msgctxt "field:production.output.distribution,initial_quantity_readonly:" +msgid "Initial Quantity" +msgstr "Cantidad Inicial" + msgctxt "field:production.output.distribution,location:" msgid "Location" msgstr "Ubicación" @@ -894,6 +949,10 @@ msgctxt "field:production.output.distribution,production:" msgid "Production" msgstr "Producciones" +msgctxt "field:production.output.distribution,production_state:" +msgid "State" +msgstr "Estado" + msgctxt "field:production.output.distribution,unit_digits:" msgid "Unit Digits" msgstr "Dígitos unidad" @@ -902,6 +961,14 @@ msgctxt "field:production.output.distribution,uom:" msgid "Uom" msgstr "UdM" +msgctxt "field:production.template,cost_distribution_template:" +msgid "Default cost distribution templatea" +msgstr "Plantilla de distribución de costos predeterminada" + +msgctxt "field:production.template,cost_distribution_templates:" +msgid "Cost Distribution Templates" +msgstr "Plantilla de distribución de costos" + msgctxt "field:production.template,enology_products:" msgid "Complementary Products" msgstr "Productos complementarios" @@ -1100,13 +1167,21 @@ msgctxt "model:ir.action,name:act_product_quantitative_test_lines" msgid "Quantitative Lines" msgstr "Líneas cuantitativas" +msgctxt "model:ir.action,name:act_production_cost_distribution_template_tree" +msgid "Templates Cost Price Distribution" +msgstr "" + +msgctxt "model:ir.action,name:act_production_cost_price_distribution_tree" +msgid "Cost Price Distribution" +msgstr "Distribución de costos" + msgctxt "model:ir.action,name:act_production_enology_product_tree" msgid "Production Enology Product" msgstr "Productos Vinícolas" msgctxt "model:ir.action,name:act_production_output_distribution_tree" msgid "Production Output Distribution" -msgstr "" +msgstr "Distribución salidas de producción" msgctxt "model:ir.action,name:act_production_template_line_tree" msgid "Production Template Line" @@ -1124,6 +1199,11 @@ msgctxt "model:ir.action,name:act_weighing_center_action" msgid "Weighing Center" msgstr "Centro de pesada" +msgctxt "" +"model:ir.action,name:wizard_create_cost_price_distribution_from_production" +msgid "Create Cost Price Distribution" +msgstr "Crear distribución de precio de costo" + msgctxt "model:ir.action.act_window.domain,name:act_weighing_domain_all" msgid "All" msgstr "Todos" @@ -1149,10 +1229,41 @@ msgstr "" "Los beneficiarios de la cosecha \"%(crop)s\" y de la plantación " "\"%(plantation)s\" han de sumar 100." +msgctxt "model:ir.message,text:msg_check_cost_distribution" +msgid "The production \"%(production)s\" has same products in cost distribution." +msgstr "" +"La producción \"%(producción)s\" tiene los mismos productos en la " +"distribución de costos." + +msgctxt "model:ir.message,text:msg_check_cost_distribution_template" +msgid "" +"There are some products in cost distribution that has not in outputs in the " +"production \"%(production)s\"." +msgstr "" +"Hay algunos productos en distribución de costos que no tienen salidas en la " +"producción \"%(producción)s\"." + +msgctxt "" +"model:ir.message,text:msg_check_cost_distribution_template_percentatge" +msgid "Invalid percentatge \"%(percentatge)s\" in \"%(distribution)s\"." +msgstr "Porcentaje no válido \"%(porcentaje)s\" en \"%(distribución)s\"." + +msgctxt "model:ir.message,text:msg_check_cost_templates" +msgid "" +"Cost \"%(cost)s\" has template \"%(template)s\" not present in Production " +"Template Outputs\"." +msgstr "" +"l costo \"%(costo)s\" tiene la plantilla \"%(plantilla)s\" que no está " +"presente en las salidas de la plantilla de producción \". " + +msgctxt "model:ir.message,text:msg_check_production_percentatge" +msgid "Invalid percentatge \"%(percentatge)s\" in \"%(production)s\"." +msgstr "Porcentaje no válido \"%(porcentaje)s\" en \"% (distribución) s\"." + msgctxt "model:ir.message,text:msg_uom_not_fit" msgid "" -"Inputs from Production template \"%(producttion)s\" must be of uom " -"\"%(uom)s\" and we have \"%(uoms)s\" ." +"Inputs from Production template \"%(production)s\" must be of uom " +"\"%(uom)s\" and we have \"%(uoms)s\"." msgstr "" "Las entradas de la plantilla de producción solo puede contener productos con" " uom \"%(uom)s\" i nos encontramos \"%(uoms)s\"." @@ -1201,6 +1312,10 @@ msgctxt "model:ir.sequence.type,name:sequence_type_weighing" msgid "Weighing" msgstr "Pesada" +msgctxt "model:ir.ui.menu,name:menu_act_production_cost_distribution_template" +msgid "Templates Cost Price Distribution" +msgstr "Distribución de precios de costes de plantillas" + msgctxt "model:ir.ui.menu,name:menu_agronomics" msgid "Agronomics" msgstr "Agronomics" @@ -1279,6 +1394,19 @@ msgstr "Producto - Variedad" msgctxt "model:product.variety,name:" msgid "Product Variety" +msgstr "Variedad de producto" + +msgctxt "model:production.cost_price.distribution,name:" +msgid "Production Distribution Cost Price" +msgstr "" + +msgctxt "model:production.cost_price.distribution.template,name:" +msgid "Production Cost Price Distribution Template" +msgstr "Plantilla de distribución de precios de costos de producción" + +msgctxt "model:production.cost_price.distribution.template.ask,name:" +msgid "" +"Production Cost Price Distribution Template from Production Template Ask" msgstr "" msgctxt "model:production.enology.product,name:" @@ -1347,7 +1475,7 @@ msgstr "" msgctxt "model:quality.proof,name:wine_notes" msgid "Observacions" -msgstr "" +msgstr "Observaciones" msgctxt "model:quality.proof,name:wine_observing_phase" msgid "Fase visual" @@ -1443,7 +1571,7 @@ msgstr "" msgctxt "model:quality.proof.method,name:wine_notes_method" msgid "Observacions" -msgstr "" +msgstr "Observaciones" msgctxt "model:quality.proof.method,name:wine_observing_phase_method" msgid "Fase visual" @@ -1589,10 +1717,48 @@ msgctxt "selection:product.template,agronomic_type:" msgid "Wine" msgstr "Vino" +msgctxt "selection:production.output.distribution,production_state:" +msgid "Assigned" +msgstr "Reservado" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Cancelled" +msgstr "Cancelado" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Done" +msgstr "Realizado" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Draft" +msgstr "Esborany" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Request" +msgstr "Solicitud" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Running" +msgstr "En ejecución" + +msgctxt "selection:production.output.distribution,production_state:" +msgid "Waiting" +msgstr "En espera" + msgctxt "view:product.product:" msgid "Agronomics" msgstr "Agronomics" msgctxt "view:product.product:" msgid "Quality" -msgstr "" +msgstr "Calidad" + +msgctxt "" +"wizard_button:production.cost_price.distribution.template.from.production.template,ask,create_cost_distributions:" +msgid "Create" +msgstr "Crear" + +msgctxt "" +"wizard_button:production.cost_price.distribution.template.from.production.template,ask,end:" +msgid "Cancel" +msgstr "Cancel·lar" diff --git a/message.xml b/message.xml index 62a6b58..5a5a648 100644 --- a/message.xml +++ b/message.xml @@ -13,7 +13,24 @@ this repository contains the full copyright notices and license terms. --> Product "%(product)s" cannot have more than one Variety because of its agronomic type. - Inputs from Production template "%(producttion)s" must be of uom "%(uom)s" and we have "%(uoms)s". + Inputs from Production template "%(production)s" must be of uom "%(uom)s" and we have "%(uoms)s". + + The production "%(production)s" has same products in cost distribution. + + + There are some products in cost distribution that has not in outputs in the production "%(production)s". + + + Invalid percentatge "%(percentatge)s" in "%(distribution)s". + + + Invalid percentatge "%(percentatge)s" in "%(production)s". + + + Cost "%(cost)s" has template "%(template)s" not present in Production Template Outputs". + + + diff --git a/plot.py b/plot.py index c28553c..956c9d1 100644 --- a/plot.py +++ b/plot.py @@ -88,7 +88,7 @@ class Parcel(ModelSQL, ModelView): plantation = fields.Many2One('agronomics.plantation', 'Plantation', required=True) crop = fields.Many2One('agronomics.crop', 'Crop', required=True) - product = fields.Many2One('product.template', 'Product', required=True) + product = fields.Many2One('product.template', 'Product') #, required=True) species = fields.Many2One('product.taxon', 'Spices', domain=[('rank', '=', 'species')], required=True, depends=['species']) diff --git a/production.py b/production.py index 2bcb419..055cec9 100644 --- a/production.py +++ b/production.py @@ -5,8 +5,11 @@ from trytond.pool import PoolMeta, Pool from trytond.pyson import Eval, Bool, If from trytond.exceptions import UserError from trytond.i18n import gettext -from decimal import Decimal from trytond.transaction import Transaction +from trytond.wizard import Wizard, StateView, StateAction, Button +from trytond.modules.product import round_price +from trytond.model.exceptions import ValidationError +from decimal import Decimal class ProductionTemplate(ModelSQL, ModelView): @@ -27,6 +30,15 @@ class ProductionTemplate(ModelSQL, ModelView): enology_products = fields.One2Many('production.template.line', 'production_template', 'Complementary Products') pass_feature = fields.Boolean('Pass on Feature') + cost_distribution_template = fields.Many2One( + 'production.cost_price.distribution.template', + "Default cost distribution template", + domain=[ + ('production_template', '=', Eval('id', 0)), + ], depends=['id']) + cost_distribution_templates = fields.One2Many( + 'production.cost_price.distribution.template', + 'production_template', "Cost Distribution Templates") @fields.depends('uom') def on_change_with_unit_digits(self, name=None): @@ -34,22 +46,33 @@ class ProductionTemplate(ModelSQL, ModelView): 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] - uoms.append(category_uom) - if len(list(set(uoms))) > 1: - 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) + for record in records: + record.check_input_uoms() + record.check_cost_distribution() + + def check_input_uoms(self): + category_uom = self.uom.category + uoms = [i.default_uom.category for i in self.inputs] + uoms.append(category_uom) + if len(list(set(uoms))) > 1: + raise UserError(gettext('agronomics.msg_uom_not_fit', + production=self.rec_name, + uom=self.uom.rec_name, + uoms=",".join([x.rec_name for x in set(uoms)]))) + + def check_cost_distribution(self): + if not self.cost_distribution_template: + return + + output_templates = set([o for o in self.outputs]) + for c in self.cost_distribution_template.cost_distribution_templates: + if c.template not in output_templates: + raise ValidationError( + gettext('agronomics.msg_check_cost_distribution_template', + production=self.rec_name)) class ProductionTemplateInputsProductTemplate(ModelSQL): @@ -101,21 +124,31 @@ class Production(metaclass=PoolMeta): __name__ = 'production' production_template = fields.Many2One('production.template', - 'Production Template') + "Production Template", + states={ + 'readonly': ~Eval('state').in_(['request', 'draft']), + }, + depends=['state']) + production_template_cost_distribution_templates = fields.Function( + fields.Many2Many('production.cost_price.distribution.template', + None, None, "Cost Distribution Templates"), + 'on_change_with_production_template_cost_distribution_templates') enology_products = fields.One2Many('production.enology.product', 'production', "Enology Products", domain=[('product', 'in', Eval('allowed_enology_products')), If((Eval('state').in_(['waiting', 'draft'])), ('product.quantity', '>', 0), ())], states={ - 'invisible': ~Bool(Eval('production_template')) + 'invisible': ~Bool(Eval('production_template')), + 'readonly': ~Eval('state').in_(['request', 'draft']), }, depends=['allowed_enology_products', 'state']) output_distribution = fields.One2Many('production.output.distribution', - 'production', 'Output Distribution', + 'production', "Output Distribution", # domain=[('product', 'in', Eval('allowed_ouput_products'))], states={ - 'invisible': ~Bool(Eval('production_template')) - }, depends=['allowed_output_products']) + 'invisible': ~Bool(Eval('production_template')), + 'readonly': Eval('state').in_(['cancelled', 'done']), + }, depends=['allowed_output_products', 'state']) allowed_enology_products = fields.Function(fields.One2Many( 'product.product', None, 'Allowed Enology Products', readonly=True), 'on_change_with_allowed_enology_products', @@ -124,11 +157,39 @@ class Production(metaclass=PoolMeta): 'product.template', None, 'Allowed Output Products', readonly=True), 'on_change_with_allowed_output_products', setter='set_allowed_products') + cost_distributions = fields.One2Many( + 'production.cost_price.distribution', + 'origin', "Cost Distributions", + states={ + 'readonly': Eval('state').in_(['cancelled', 'done']), + }, domain=[ + ('template', 'in', Eval('cost_distribution_templates')), + ], depends=['state', 'cost_distribution_template', + 'cost_distribution_templates']) + cost_distribution_template = fields.Many2One( + 'production.cost_price.distribution.template', + "Cost Distribution Template", + domain=[ + ('id', 'in', Eval('production_template_cost_distribution_templates')) + ], states={ + 'readonly': Eval('state').in_(['cancelled', 'done']), + }, depends=['state', 'production_template_cost_distribution_templates']) + cost_distribution_templates = fields.Function( + fields.Many2Many('product.template', + None, None, "Cost Product Templates"), + 'on_change_with_cost_distribution_templates') @classmethod def set_allowed_products(cls, productions, name, value): pass + @fields.depends('production_template') + def on_change_production_template(self): + if (self.production_template and + self.production_template.cost_distribution_template): + self.cost_distribution_template = \ + self.production_template.cost_distribution_template + @fields.depends('production_template') def on_change_with_allowed_enology_products(self, name=None): products = [] @@ -144,14 +205,65 @@ class Production(metaclass=PoolMeta): return [] return [x.id for x in self.production_template.outputs] + @fields.depends('production_template', + '_parent_production_template.cost_distribution_templates') + def on_change_with_production_template_cost_distribution_templates(self, + name=None): + if self.production_template: + return [s.id for s in + self.production_template.cost_distribution_templates] + + @fields.depends('cost_distribution_template', + '_parent_cost_distribution_template.cost_distribution_templates') + def on_change_with_cost_distribution_templates(self, name=None): + if self.production_template: + return [s.id for s in self.production_template.outputs] + + @classmethod + def validate(cls, productions): + super(Production, cls).validate(productions) + for production in productions: + production.check_cost_distribution() + production.check_percentatge() + + def check_cost_distribution(self): + if (self.state in ('cancelled', 'done') + or not self.cost_distribution_template + or not self.cost_distributions): + return + distribution_templates = set([c.template + for c in self.cost_distribution_template.cost_distribution_templates]) + for c in self.cost_distributions: + if c.template not in distribution_templates: + raise ValidationError( + gettext('agronomics.msg_check_cost_distribution', + production=self.rec_name)) + + def check_percentatge(self): + if not self.cost_distributions: + return + + percentatge = sum(template.percentatge + for template in self.cost_distributions) + if percentatge != 1: + raise ValidationError( + gettext('agronomics.msg_check_production_percentatge', + production=self.rec_name, + percentatge=percentatge * 100, + )) + @classmethod def wait(cls, productions): - Move = Pool().get('stock.move') - Uom = Pool().get('product.uom') - OutputDistribution = Pool().get('production.output.distribution') + pool = Pool() + Move = pool.get('stock.move') + Uom = pool.get('product.uom') + OutputDistribution = pool.get('production.output.distribution') + CostDistribution = pool.get('production.cost_price.distribution') + moves = [] delete = [] outputs = [] + costs = [] delete_outputs = [] for production in productions: @@ -196,10 +308,27 @@ class Production(metaclass=PoolMeta): od.production = production outputs.append(od) + if not production.cost_distributions: + if production.cost_distribution_template: + cost_distribution_template = production.cost_distribution_template + elif production.production_template: + cost_distribution_template = production.production_template.cost_distribution_template + else: + cost_distribution_template = None + if cost_distribution_template: + for c in cost_distribution_template.cost_distribution_templates: + cost = CostDistribution() + cost.template = c.template + cost.percentatge = c.percentatge + cost.origin = str(production) + costs.append(cost) + + CostDistribution.save(costs) OutputDistribution.delete(delete_outputs) OutputDistribution.save(outputs) Move.save(moves) Move.delete(delete) + super().wait(productions) def create_variant(self, template, pass_feature): @@ -264,6 +393,46 @@ class Production(metaclass=PoolMeta): Move.save(moves) super().done(productions) + @classmethod + def set_cost(cls, productions): + pool = Pool() + Move = pool.get('stock.move') + Uom = pool.get('product.uom') + + not_cost_distribution = [] + moves = [] + for production in productions: + if not production.cost_distributions: + not_cost_distribution.append(production) + continue + + production_cost = production.cost + for output in production.outputs: + has_product = False + output_cost = Decimal(0) + total_output = sum([Uom.compute_qty(x.uom, x.quantity, + x.product.default_uom) for x in production.outputs + if x.product.template == output.product.template]) + + for cdist in production.cost_distributions: + products = cdist.template.products + if output.product not in products: + continue + has_product = True + cost = production_cost * (1 + cdist.percentatge) - production_cost + output_cost += round_price(cost / Decimal(total_output)) + + output_cost = output_cost if has_product else Decimal(0) + if output.unit_price != output_cost: + output.unit_price = output_cost + moves.append(output) + + if moves: + Move.save(moves) + + if not_cost_distribution: + super(Production, cls).set_cost(not_cost_distribution) + class OutputDistribution(ModelSQL, ModelView): 'Output Distribution' @@ -352,6 +521,7 @@ class OutputDistribution(ModelSQL, ModelView): return ((self.final_quantity or 0) - (self.initial_quantity or 0)) + class ProductionEnologyProduct(ModelSQL, ModelView): 'Production Enology Product' __name__ = 'production.enology.product' @@ -382,3 +552,131 @@ class ProductionEnologyProduct(ModelSQL, ModelView): if not self.product: return self.quantity = self.product.quantity + + +class ProductionCostPriceDistribution(ModelSQL, ModelView): + "Production Distribution Cost Price" + __name__ = 'production.cost_price.distribution' + template = fields.Many2One('product.template', "Template", required=True, + ondelete='RESTRICT') + origin = fields.Reference('Origin', selection='_get_models', required=True,) + percentatge = fields.Numeric("Percentatge", digits=(16, 4), required=True) + + @classmethod + def __setup__(cls): + BOMInput = Pool().get('production.bom.input') + super(ProductionCostPriceDistribution, cls).__setup__() + cls.template.domain = [('type', 'in', BOMInput.get_product_types())] + + @staticmethod + def _get_models(): + return [ + ('production', 'Production'), + ('production.cost_price.distribution.template', 'Templates'), + ] + + +class ProductionCostPriceDistributionTemplate(ModelSQL, ModelView): + "Production Cost Price Distribution Template" + __name__ = 'production.cost_price.distribution.template' + name = fields.Char("Name", required=True) + production_template = fields.Many2One('production.template', + "Production Template", required=True) + cost_distribution_templates = fields.One2Many( + 'production.cost_price.distribution', + 'origin', "Cost Distribution") + + @classmethod + def validate(cls, templates): + super(ProductionCostPriceDistributionTemplate, cls).validate(templates) + for template in templates: + template.check_percentatge() + template.check_product_templates() + + def check_product_templates(self): + for cost in self.cost_distribution_templates: + if cost.template not in self.production_template.outputs: + raise ValidationError(gettext( + 'agronomics.msg_check_cost_templates', + cost=cost.rec_name, + template=cost.template.rec_name, + )) + + + def check_percentatge(self): + percentatge = sum(t.percentatge + for t in self.cost_distribution_templates) + if percentatge != 1: + raise ValidationError( + gettext( + 'agronomics.msg_check_cost_distribution_template_percentatge', + distribution=self.rec_name, + percentatge=percentatge * 100, + )) + + +class ProductionCostPriceDistributionTemplateProductionTemplateAsk(ModelView): + 'Production Cost Price Distribution Template from Production Template Ask' + __name__ = 'production.cost_price.distribution.template.ask' + name = fields.Char("Name", required=True) + cost_distribution_templates = fields.One2Many( + 'production.cost_price.distribution', + None, "Cost Distributions") + + +class ProductionCostPriceDistributionTemplateProductionTemplate(Wizard): + "Production Cost Price Distribution Template from Production Template" + __name__ = 'production.cost_price.distribution.template.from.production.template' + start_state = 'ask' + ask = StateView('production.cost_price.distribution.template.ask', + 'agronomics.create_cost_price_distribution_from_production_start_view_form', [ + Button('Cancel', 'end', 'tryton-cancel'), + Button('Create', 'create_cost_distributions', 'tryton-ok', True), + ]) + create_cost_distributions = StateAction( + 'agronomics.act_production_cost_distribution_template_tree') + + def do_create_cost_distributions(self, action): + pool = Pool() + Template = pool.get('production.cost_price.distribution.template') + Distribution = pool.get('production.cost_price.distribution') + + to_create = [] + for record in self.records: + tpl = Template() + tpl.name = self.ask.name + tpl.production_template = record + cost_distributions = [] + for cost_distribution in self.ask.cost_distribution_templates: + dt = Distribution() + dt.template = cost_distribution.template + dt.percentatge = cost_distribution.percentatge + cost_distributions.append(dt) + if cost_distributions: + tpl.cost_distribution_templates = cost_distributions + to_create.append(tpl._save_values) + tpls = Template.create(to_create) + + data = {'res_id': [tpl.id for tpl in tpls]} + if len(tpls) == 1: + action['views'].reverse() + return action, data + + def default_ask(self, fields): + pool = Pool() + ProductionTemplate = pool.get('production.template') + + default = {} + context = Transaction().context + + active_id = context.get('active_id') + if active_id: + ptpl = ProductionTemplate(active_id) + cost_distributions = [] + for output in ptpl.outputs: + cost_distributions.append({ + 'template': output.id, + 'template.': {'rec_name': output.rec_name}, + }) + default['cost_distribution_templates'] = cost_distributions + return default diff --git a/production.xml b/production.xml index a1365b5..d62d847 100644 --- a/production.xml +++ b/production.xml @@ -97,6 +97,7 @@ production.enology.product + production @@ -109,5 +110,118 @@ production_list + + + production.cost_price.distribution.template + form + production_cost_distribution_template_form + + + production.cost_price.distribution.template + tree + production_cost_distribution_template_tree + + + + Templates Cost Price Distribution + production.cost_price.distribution.template + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + production.cost_price.distribution + form + production_cost_price_distribution_form + + + production.cost_price.distribution + tree + production_cost_price_distribution_tree + + + + Cost Price Distribution + production.cost_price.distribution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + production.cost_price.distribution.template.ask + form + create_cost_price_distribution_from_production_start_form + + + + Create Cost Price Distribution + production.cost_price.distribution.template.from.production.template + production.template + + + + + + + form_action + production.template,-1 + + diff --git a/view/create_cost_price_distribution_from_production_start_form.xml b/view/create_cost_price_distribution_from_production_start_form.xml new file mode 100644 index 0000000..5d4b40a --- /dev/null +++ b/view/create_cost_price_distribution_from_production_start_form.xml @@ -0,0 +1,9 @@ + + +
+