mirror of
https://github.com/NaN-tic/trytond-agronomics.git
synced 2023-12-14 05:33:01 +01:00
production template #045158
This commit is contained in:
parent
e813e592eb
commit
da0e5ee7a6
|
@ -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')
|
||||
|
|
21
product.py
21
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'
|
||||
|
|
339
production.py
Normal file
339
production.py
Normal file
|
@ -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
|
114
production.xml
Normal file
114
production.xml
Normal file
|
@ -0,0 +1,114 @@
|
|||
<tryton>
|
||||
<data>
|
||||
<!-- Production Template -->
|
||||
<record model="ir.ui.view" id="production_template_view_form">
|
||||
<field name="model">production.template</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">production_template_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="production_template_view_tree">
|
||||
<field name="model">production.template</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">production_template_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_production_template_tree">
|
||||
<field name="name">Production Template</field>
|
||||
<field name="res_model">production.template</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window.view" id="act_production_template_tree_view">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="production_template_view_tree"/>
|
||||
<field name="act_window" ref="act_production_template_tree"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_production_template_form_view">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="production_template_view_form"/>
|
||||
<field name="act_window" ref="act_production_template_tree"/>
|
||||
</record>
|
||||
|
||||
<menuitem parent="production.menu_configuration" sequence="1"
|
||||
action="act_production_template_tree" id="menu_production_template_list"/>
|
||||
|
||||
<record model="ir.ui.menu-res.group"
|
||||
id="menu_production_template_list_group_productions">
|
||||
<field name="menu" ref="menu_production_template_list"/>
|
||||
<field name="group" ref="production.group_production"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu-res.group"
|
||||
id="menu_production_template_list_group_productions_admin">
|
||||
<field name="menu" ref="menu_production_template_list"/>
|
||||
<field name="group" ref="production.group_production_admin"/>
|
||||
</record>
|
||||
|
||||
<!-- Production Template Line -->
|
||||
<record model="ir.ui.view" id="production_template_line_view_form">
|
||||
<field name="model">production.template.line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">production_template_line_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="production_template_line_view_tree">
|
||||
<field name="model">production.template.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">production_template_line_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_production_template_line_tree">
|
||||
<field name="name">Production Template Line</field>
|
||||
<field name="res_model">production.template.line</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Production Output Distribution -->
|
||||
<record model="ir.ui.view" id="production_output_distribution_view_form">
|
||||
<field name="model">production.output.distribution</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">production_output_distribution_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="production_output_distribution_view_tree">
|
||||
<field name="model">production.output.distribution</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">production_output_distribution_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_production_output_distribution_tree">
|
||||
<field name="name">Production Output Distribution</field>
|
||||
<field name="res_model">production.output.distribution</field>
|
||||
</record>
|
||||
|
||||
<!-- Production Enology -->
|
||||
<record model="ir.ui.view" id="production_enology_product_view_form">
|
||||
<field name="model">production.enology.product</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">production_enology_product_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="production_enology_product_view_tree">
|
||||
<field name="model">production.enology.product</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">production_enology_product_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_production_enology_product_tree">
|
||||
<field name="name">Production Enology Product</field>
|
||||
<field name="res_model">production.enology.product</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="production_view_form">
|
||||
<field name="model">production</field>
|
||||
<field name="inherit" ref="production.production_view_form"/>
|
||||
<field name="name">production_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="production_view_list">
|
||||
<field name="model">production</field>
|
||||
<field name="inherit" ref="production.production_view_list"/>
|
||||
<field name="name">production_list</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
329
tests/scenario_production_template.rst
Normal file
329
tests/scenario_production_template.rst
Normal file
|
@ -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']
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
8
view/production_enology_product_form.xml
Normal file
8
view/production_enology_product_form.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<form>
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
</form>
|
5
view/production_enology_product_list.xml
Normal file
5
view/production_enology_product_list.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<tree>
|
||||
<field name="product"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
</tree>
|
19
view/production_form.xml
Normal file
19
view/production_form.xml
Normal 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. -->
|
||||
<data>
|
||||
<xpath expr="/form/label[@name='bom']" position="before">
|
||||
<label name="production_template"/>
|
||||
<field name="production_template"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook" position="inside">
|
||||
<page name="enology_products">
|
||||
<field name="enology_products" colspan="4"/>
|
||||
<field name="allowed_enology_products" colspan="4" invisible="1"/>
|
||||
</page>
|
||||
<page name="output_distribution">
|
||||
<field name="output_distribution" colspan="4"/>
|
||||
<field name="allowed_output_products" colspan="4" invisible="1"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
8
view/production_list.xml
Normal file
8
view/production_list.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/tree/field[@name='state']" position="before">
|
||||
<field name="cost" sum="Total"/>
|
||||
</xpath>
|
||||
</data>
|
17
view/production_output_distribution_form.xml
Normal file
17
view/production_output_distribution_form.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?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. -->
|
||||
<form>
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="location"/>
|
||||
<field name="location"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
<label name="initial_quantity"/>
|
||||
<field name="initial_quantity"/>
|
||||
<label name="final_quantity"/>
|
||||
<field name="final_quantity"/>
|
||||
<label name="produced_quantity"/>
|
||||
<field name="produced_quantity"/>
|
||||
</form>
|
11
view/production_output_distribution_list.xml
Normal file
11
view/production_output_distribution_list.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?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. -->
|
||||
<tree>
|
||||
<field name="product"/>
|
||||
<field name="location"/>
|
||||
<field name="uom"/>
|
||||
<field name="initial_quantity"/>
|
||||
<field name="final_quantity"/>
|
||||
<field name="produced_quantity"/>
|
||||
</tree>
|
16
view/production_template_form.xml
Normal file
16
view/production_template_form.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?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. -->
|
||||
<form>
|
||||
<label name="name"/>
|
||||
<field name="name"/>
|
||||
<label name="pass_feature"/>
|
||||
<field name="pass_feature"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
<field name="inputs" colspan="2"/>
|
||||
<field name="outputs" colspan="2"/>
|
||||
<field name="enology_products" colspan="4"/>
|
||||
</form>
|
11
view/production_template_line_form.xml
Normal file
11
view/production_template_line_form.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?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. -->
|
||||
<form>
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
</form>
|
8
view/production_template_line_list.xml
Normal file
8
view/production_template_line_list.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?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. -->
|
||||
<tree editable="1">
|
||||
<field name="product"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
</tree>
|
9
view/production_template_list.xml
Normal file
9
view/production_template_list.xml
Normal 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. -->
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="pass_feature"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
</tree>
|
|
@ -1,3 +1,6 @@
|
|||
<?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/notebook/page[@id='general']/field[@name='cost_price_method']" position="after">
|
||||
<label name="agronomic_type"/>
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<?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="/tree" position="inside">
|
||||
<field name="agronomic_type" tree_invisible="1"/>
|
||||
|
|
Loading…
Reference in a new issue