Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(15)

Unified Diff: bom.py

Issue 4306055: New production module (Closed)
Patch Set: To be sure Created 12 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « __tryton__.py ('k') | bom.xml » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: bom.py
===================================================================
new file mode 100644
--- /dev/null
+++ b/bom.py
@@ -0,0 +1,289 @@
+#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 ModelView, ModelSQL, fields
+from trytond.wizard import Wizard, StateView, Button
+from trytond.transaction import Transaction
+from trytond.pyson import Eval
+from trytond.pool import Pool
+
+
+class BOM(ModelSQL, ModelView):
+ "Bill of Material"
+ _name = 'production.bom'
+ _description = __doc__
+
+ name = fields.Char('Name', required=True, translate=True)
+ active = fields.Boolean('Active', select=1)
+ inputs = fields.One2Many('production.bom.input', 'bom', 'Inputs')
+ outputs = fields.One2Many('production.bom.output', 'bom', 'Outputs')
+ output_products = fields.Many2Many('production.bom.output',
+ 'bom', 'product', 'Output Products')
+
+ def default_active(self):
+ return True
+
+ def compute_factor(self, bom, product, quantity, uom):
+ '''
+ Compute factor for an output product
+ '''
+ uom_obj = Pool().get('product.uom')
+ for output in bom.outputs:
+ if output.product == product:
+ quantity = uom_obj.compute_qty(uom, quantity,
+ output.uom, round=False)
+ return quantity / output.quantity
+
+ def copy(self, ids, default=None):
+ if default is None:
+ default = {}
+ else:
+ default = default.copy()
+ default['output_products'] = False
+ return super(BOM, self).copy(ids, default=default)
+
+BOM()
+
+
+class BOMInput(ModelSQL, ModelView):
+ "Bill of Material Input"
+ _name = 'production.bom.input'
+ _description = __doc__
+ _rec_name = 'product'
+
+ bom = fields.Many2One('production.bom', 'BOM', required=True,
+ select=1)
+ product = fields.Many2One('product.product', 'Product',
+ required=True, domain=[
+ ('type', '!=', 'service'),
+ ], on_change=['product', 'uom'])
+ uom_category = fields.Function(fields.Many2One(
+ 'product.uom.category', 'Uom Category',
+ on_change_with=['product']), 'get_uom_category')
+ uom = fields.Many2One('product.uom', 'Uom', required=True,
+ domain=[
+ ('category', '=', Eval('uom_category')),
+ ], depends=['uom_category'])
+ unit_digits = fields.Function(fields.Integer('Unit Digits',
+ on_change_with=['uom']), 'get_unit_digits')
+ quantity = fields.Float('Quantity', required=True,
+ digits=(16, Eval('unit_digits', 2)),
+ depends=['unit_digits'])
+
+ def __init__(self):
+ super(BOMInput, self).__init__()
+ self._sql_constraints = [
+ ('product_bom_uniq', 'UNIQUE(product, bom)',
+ 'product_bom_uniq'),
+ ]
+ self._constraints += [
+ ('check_bom_recursion', 'recursive_bom'),
+ ]
+ self._error_messages.update({
+ 'product_bom_uniq': 'Product must be unique per BOM!',
+ 'recursive_bom': 'You can not create recursive BOMs!',
+ })
+
+ def on_change_product(self, vals):
+ product_obj = Pool().get('product.product')
+
+ res = {}
+ if vals.get('product'):
+ product = product_obj.browse(vals['product'])
+ uom_ids = [x.id for x in product.default_uom.category.uoms]
+ if (not vals.get('uom')
+ or vals.get('uom') not in uom_ids):
+ res['uom'] = product.default_uom.id
+ res['uom.rec_name'] = product.default_uom.rec_name
+ res['unit_digits'] = product.default_uom.digits
+ else:
+ res['uom'] = False
+ res['uom.rec_name'] = ''
+ res['unit_digits'] = 2
+ return res
+
+ def on_change_with_uom_category(self, vals):
+ product_obj = Pool().get('product.product')
+ if vals.get('product'):
+ product = product_obj.browse(vals['product'])
+ return product.default_uom.category.id
+ return False
+
+ def get_uom_category(self, ids, name):
+ res = {}
+ for input in self.browse(ids):
+ res[input.id] = input.product.default_uom.category.id
+ return res
+
+ def on_change_with_unit_digits(self, vals):
+ uom_obj = Pool().get('product.uom')
+ if vals.get('uom'):
+ uom = uom_obj.browse(vals['uom'])
+ return uom.digits
+ return 2
+
+ def get_unit_digits(self, ids, name):
+ res = {}
+ for input in self.browse(ids):
+ res[input.id] = input.uom.digits
+ return res
+
+ def check_bom_recursion(self, ids):
+ '''
+ Check BOM recursion
+ '''
+ product_obj = Pool().get('product.product')
+
+ inputs = self.browse(ids)
+ product_ids = [input.product.id for input in inputs]
+ return product_obj.check_bom_recursion(product_ids)
+
+ def compute_quantity(self, line, factor):
+ uom_obj = Pool().get('product.uom')
+ return uom_obj.round(line.quantity * factor, line.uom.rounding)
+
+BOMInput()
+
+
+class BOMOutput(BOMInput):
+ "Bill of Material Output"
+ _name = 'production.bom.output'
+ _description = __doc__
+
+BOMOutput()
+
+
+class BOMTree(ModelView):
+ 'BOM Tree'
+ _name = 'production.bom.tree'
+ _description = __doc__
+
+ product = fields.Many2One('product.product', 'Product')
+ quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)),
+ depends=['unit_digits'])
+ uom = fields.Many2One('product.uom', 'Uom')
+ unit_digits = fields.Integer('Unit Digits')
+ childs = fields.One2Many('production.bom.tree', None, 'Childs')
+
+ def tree(self, product, quantity, uom, bom=None):
+ bom_obj = Pool().get('production.bom')
+ input_obj = Pool().get('production.bom.input')
+
+ result = []
+ if bom is None:
+ if not product.boms:
+ return result
+ bom = product.boms[0].bom
+
+ factor = bom_obj.compute_factor(bom, product, quantity, uom)
+ for input in bom.inputs:
+ quantity = input_obj.compute_quantity(input, factor)
+ childs = self.tree(input.product, quantity, input.uom)
+ values = {
+ 'product': input.product.id,
+ 'quantity': quantity,
+ 'uom': input.uom.id,
+ 'unit_digits': input.uom.digits,
+ 'childs': childs,
+ }
+ result.append(values)
+ return result
+
+BOMTree()
+
+
+class OpenBOMTreeStart(ModelView):
+ 'Open BOM Tree'
+ _name = 'production.bom.tree.open.start'
+
+ quantity = fields.Float('Quantity', required=True,
+ digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
+ uom = fields.Many2One('product.uom', 'Unit', required=True,
+ domain=[
+ ('category', '=', Eval('category')),
+ ], depends=['category'])
+ unit_digits = fields.Integer('Unit Digits', readonly=True,
+ on_change_with=['uom'])
+ category = fields.Many2One('product.uom.category', 'Category',
+ readonly=True)
+ bom = fields.Many2One('product.product-production.bom',
+ 'BOM', required=True, domain=[
+ ('product', '=', Eval('product')),
+ ], depends=['product'])
+ product = fields.Many2One('product.product', 'Product', readonly=True)
+
+ def on_change_with_unit_digits(self, values):
+ uom_obj = Pool().get('product.uom')
+ if values.get('uom'):
+ uom = uom_obj.browse(values['uom'])
+ return uom.digits
+ return 2
+
+OpenBOMTreeStart()
+
+
+class OpenBOMTreeTree(ModelView):
+ 'Open BOM Tree'
+ _name = 'production.bom.tree.open.tree'
+
+ bom_tree = fields.One2Many('production.bom.tree', None, 'BOM Tree')
+
+ def tree(self, bom, product, quantity, uom):
+ pool = Pool()
+ tree_obj = pool.get('production.bom.tree')
+
+ childs = tree_obj.tree(product, quantity, uom, bom=bom)
+ bom_tree = [{
+ 'product': product.id,
+ 'quantity': quantity,
+ 'uom': uom.id,
+ 'unit_digits': uom.digits,
+ 'childs': childs,
+ }]
+ return {
+ 'bom_tree': bom_tree,
+ }
+
+OpenBOMTreeTree()
+
+
+class OpenBOMTree(Wizard):
+ 'Open BOM Tree'
+ _name = 'production.bom.tree.open'
+
+ start = StateView('production.bom.tree.open.start',
+ 'production.bom_tree_open_start_view_form', [
+ Button('Cancel', 'end', 'tryton-cancel'),
+ Button('Ok', 'tree', 'tryton-ok', True),
+ ])
+ tree = StateView('production.bom.tree.open.tree',
+ 'production.bom_tree_open_tree_view_form', [
+ Button('Change', 'start', 'tryton-go-previous'),
+ Button('Close', 'end', 'tryton-close'),
+ ])
+
+ def default_start(self, session, fields):
+ product_obj = Pool().get('product.product')
+ defaults = {}
+ product = product_obj.browse(Transaction().context['active_id'])
+ defaults['category'] = product.default_uom.category.id
+ if session.start.uom:
+ defaults['uom'] = session.start.uom.id
+ defaults['unit_digits'] = session.start.unit_digits
+ else:
+ defaults['uom'] = product.default_uom.id
+ defaults['unit_digits'] = product.default_uom.digits
+ defaults['product'] = product.id
+ if session.start.bom:
+ defaults['bom'] = session.start.bom.id
+ elif product.boms:
+ defaults['bom'] = product.boms[0].id
+ defaults['quantity'] = session.start.quantity
+ return defaults
+
+ def default_tree(self, session, fields):
+ pool = Pool()
+ bom_tree_obj = pool.get('production.bom.tree.open.tree')
+ return bom_tree_obj.tree(session.start.bom.bom, session.start.product,
+ session.start.quantity, session.start.uom)
+
+OpenBOMTree()
« no previous file with comments | « __tryton__.py ('k') | bom.xml » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b