diff options
37 files changed, 1744 insertions, 1664 deletions
diff --git a/addons/mrp/__init__.py b/addons/mrp/__init__.py index 73b441cae9b..8f7bf4b27dd 100644 --- a/addons/mrp/__init__.py +++ b/addons/mrp/__init__.py @@ -1,11 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import mrp -import stock -import product +import models import wizard import report -import company -import procurement -import res_config diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py index b405f402d81..f5604cc4a0a 100644 --- a/addons/mrp/__openerp__.py +++ b/addons/mrp/__openerp__.py @@ -36,26 +36,34 @@ Dashboard / Reports for MRP will include: 'data': [ 'security/mrp_security.xml', 'security/ir.model.access.csv', - 'mrp_workflow.xml', - 'mrp_data.xml', - 'wizard/mrp_product_produce_view.xml', - 'wizard/change_production_qty_view.xml', - 'wizard/stock_move_view.xml', - 'mrp_view.xml', - 'mrp_report.xml', - 'company_view.xml', - 'report/mrp_report_view.xml', - 'res_config_view.xml', + 'data/mrp_data_workflow.xml', + 'data/mrp_data.xml', + 'wizard/mrp_product_produce_views.xml', + 'wizard/change_production_qty_views.xml', + 'wizard/stock_move_consume_views.xml', + 'views/mrp_views_menus.xml', + 'views/mrp_property_views.xml', + 'views/mrp_workcenter_views.xml', + 'views/mrp_routing_views.xml', + 'views/mrp_production_views.xml', + 'views/mrp_bom_views.xml', + 'views/procurement_views.xml', + 'views/product_views.xml', + 'views/stock_warehouse_views.xml', + 'views/res_company_views.xml', + 'views/mrp_config_settings_views.xml', + 'report/mrp_report_views_main.xml', + 'report/mrp_report_views.xml', 'views/report_mrporder.xml', 'views/report_mrpbomstructure.xml', ], - 'demo': ['mrp_demo.xml'], + 'demo': ['data/mrp_demo.xml'], 'test': [ - 'test/bom_with_service_type_product.yml', - 'test/mrp_users.yml', - 'test/order_demo.yml', - 'test/order_process.yml', - 'test/cancel_order.yml', + 'test/bom_with_service_type_product.yml', + 'test/mrp_users.yml', + 'test/order_demo.yml', + 'test/order_process.yml', + 'test/cancel_order.yml', ], 'installable': True, 'application': True, diff --git a/addons/mrp/mrp_data.xml b/addons/mrp/data/mrp_data.xml index 8a391272698..8a391272698 100644 --- a/addons/mrp/mrp_data.xml +++ b/addons/mrp/data/mrp_data.xml diff --git a/addons/mrp/mrp_workflow.xml b/addons/mrp/data/mrp_data_workflow.xml index 1892e0a29c0..1892e0a29c0 100644 --- a/addons/mrp/mrp_workflow.xml +++ b/addons/mrp/data/mrp_data_workflow.xml diff --git a/addons/mrp/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml index 589adf81019..589adf81019 100644 --- a/addons/mrp/mrp_demo.xml +++ b/addons/mrp/data/mrp_demo.xml diff --git a/addons/mrp/models/__init__.py b/addons/mrp/models/__init__.py new file mode 100644 index 00000000000..83c0a722281 --- /dev/null +++ b/addons/mrp/models/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import mrp_config_settings +import mrp_bom +import mrp_production +import mrp_property +import mrp_routing +import mrp_workcenter +import procurement +import product +import res_company +import stock_move +import stock_warehouse
\ No newline at end of file diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py new file mode 100644 index 00000000000..16554e3eb4d --- /dev/null +++ b/addons/mrp/models/mrp_bom.py @@ -0,0 +1,336 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import time + +import openerp.addons.decimal_precision as dp +from openerp import tools +from openerp.exceptions import UserError +from openerp.osv import fields, osv +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT +from openerp.tools.translate import _ + + +class mrp_bom(osv.osv): + """ + Defines bills of material for a product. + """ + _name = 'mrp.bom' + _description = 'Bill of Material' + _inherit = ['mail.thread'] + + _columns = { + 'code': fields.char('Reference', size=16), + 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the bills of material without removing it."), + 'type': fields.selection([('normal','Manufacture this product'),('phantom','Ship this product as a set of components (kit)')], 'BoM Type', required=True, + help= "Set: When processing a sales order for this product, the delivery order will contain the raw materials, instead of the finished product."), + 'position': fields.char('Internal Reference', help="Reference to a position in an external plan."), + 'product_tmpl_id': fields.many2one('product.template', 'Product', domain="[('type', 'in', ['product', 'consu'])]", required=True), + 'product_id': fields.many2one('product.product', 'Product Variant', + domain="['&', ('product_tmpl_id','=',product_tmpl_id), ('type', 'in', ['product', 'consu'])]", + help="If a product variant is defined the BOM is available only for this product."), + 'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines', copy=True), + 'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"), + 'date_start': fields.date('Valid From', help="Validity of this BoM. Keep empty if it's always valid."), + 'date_stop': fields.date('Valid Until', help="Validity of this BoM. Keep empty if it's always valid."), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of bills of material."), + 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. "\ + "The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."), + 'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."), + 'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% during the production process."), + 'property_ids': fields.many2many('mrp.property', string='Properties'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + } + + def _get_uom_id(self, cr, uid, *args): + return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0] + _defaults = { + 'active': lambda *a: 1, + 'product_qty': lambda *a: 1.0, + 'product_efficiency': lambda *a: 1.0, + 'product_rounding': lambda *a: 0.0, + 'type': lambda *a: 'normal', + 'product_uom': _get_uom_id, + 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.bom', context=c), + } + _order = "sequence" + + + def _bom_find(self, cr, uid, product_tmpl_id=None, product_id=None, properties=None, context=None): + """ Finds BoM for particular product and product uom. + @param product_tmpl_id: Selected product. + @param product_uom: Unit of measure of a product. + @param properties: List of related properties. + @return: False or BoM id. + """ + if not context: + context = {} + if properties is None: + properties = [] + if product_id: + if not product_tmpl_id: + product_tmpl_id = self.pool['product.product'].browse(cr, uid, product_id, context=context).product_tmpl_id.id + domain = [ + '|', + ('product_id', '=', product_id), + '&', + ('product_id', '=', False), + ('product_tmpl_id', '=', product_tmpl_id) + ] + elif product_tmpl_id: + domain = [('product_id', '=', False), ('product_tmpl_id', '=', product_tmpl_id)] + else: + # neither product nor template, makes no sense to search + return False + if context.get('company_id'): + domain = domain + [('company_id', '=', context['company_id'])] + domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT)), + '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT))] + # order to prioritize bom with product_id over the one without + ids = self.search(cr, uid, domain, order='sequence, product_id', context=context) + # Search a BoM which has all properties specified, or if you can not find one, you could + # pass a BoM without any properties with the smallest sequence + bom_empty_prop = False + for bom in self.pool.get('mrp.bom').browse(cr, uid, ids, context=context): + if not set(map(int, bom.property_ids or [])) - set(properties or []): + if not properties or bom.property_ids: + return bom.id + elif not bom_empty_prop: + bom_empty_prop = bom.id + return bom_empty_prop + + def _skip_bom_line(self, cr, uid, line, product, context=None): + """ Control if a BoM line should be produce, can be inherited for add + custom control. + @param line: BoM line. + @param product: Selected product produced. + @return: True or False + """ + if line.date_start and line.date_start > time.strftime(DEFAULT_SERVER_DATE_FORMAT) or \ + line.date_stop and line.date_stop < time.strftime(DEFAULT_SERVER_DATE_FORMAT): + return True + # all bom_line_id variant values must be in the product + if line.attribute_value_ids: + if not product or (set(map(int,line.attribute_value_ids or [])) - set(map(int,product.attribute_value_ids))): + return True + return False + + def _prepare_wc_line(self, cr, uid, bom, wc_use, level=0, factor=1, context=None): + wc = wc_use.workcenter_id + d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle) + mult = (d + (m and 1.0 or 0.0)) + cycle = mult * wc_use.cycle_nbr + return { + 'name': tools.ustr(wc_use.name) + ' - ' + tools.ustr(bom.product_tmpl_id.name_get()[0][1]), + 'workcenter_id': wc.id, + 'sequence': level + (wc_use.sequence or 0), + 'cycle': cycle, + 'hour': float(wc_use.hour_nbr * mult + ((wc.time_start or 0.0) + (wc.time_stop or 0.0) + cycle * (wc.time_cycle or 0.0)) * (wc.time_efficiency or 100)), + } + + def _prepare_consume_line(self, cr, uid, bom_line_id, quantity, context=None): + return { + 'name': bom_line_id.product_id.name, + 'product_id': bom_line_id.product_id.id, + 'product_qty': quantity, + 'product_uom': bom_line_id.product_uom.id + } + + def _bom_explode(self, cr, uid, bom, product, factor, properties=None, level=0, routing_id=False, previous_products=None, master_bom=None, context=None): + """ Finds Products and Work Centers for related BoM for manufacturing order. + @param bom: BoM of particular product template. + @param product: Select a particular variant of the BoM. If False use BoM without variants. + @param factor: Factor represents the quantity, but in UoM of the BoM, taking into account the numbers produced by the BoM + @param properties: A List of properties Ids. + @param level: Depth level to find BoM lines starts from 10. + @param previous_products: List of product previously use by bom explore to avoid recursion + @param master_bom: When recursion, used to display the name of the master bom + @return: result: List of dictionaries containing product details. + result2: List of dictionaries containing Work Center details. + """ + uom_obj = self.pool.get("product.uom") + routing_obj = self.pool.get('mrp.routing') + master_bom = master_bom or bom + + + def _factor(factor, product_efficiency, product_rounding): + factor = factor / (product_efficiency or 1.0) + if product_rounding: + factor = tools.float_round(factor, + precision_rounding=product_rounding, + rounding_method='UP') + if factor < product_rounding: + factor = product_rounding + return factor + + factor = _factor(factor, bom.product_efficiency, bom.product_rounding) + + result = [] + result2 = [] + + routing = (routing_id and routing_obj.browse(cr, uid, routing_id)) or bom.routing_id or False + if routing: + for wc_use in routing.workcenter_lines: + result2.append(self._prepare_wc_line( + cr, uid, bom, wc_use, level=level, factor=factor, + context=context)) + + for bom_line_id in bom.bom_line_ids: + if self._skip_bom_line(cr, uid, bom_line_id, product, context=context): + continue + if set(map(int, bom_line_id.property_ids or [])) - set(properties or []): + continue + + if previous_products and bom_line_id.product_id.product_tmpl_id.id in previous_products: + raise UserError(_('BoM "%s" contains a BoM line with a product recursion: "%s".') % (master_bom.code or "", bom_line_id.product_id.name_get()[0][1])) + + quantity = _factor(bom_line_id.product_qty * factor, bom_line_id.product_efficiency, bom_line_id.product_rounding) + bom_id = self._bom_find(cr, uid, product_id=bom_line_id.product_id.id, properties=properties, context=context) + + #If BoM should not behave like kit, just add the product, otherwise explode further + if (not bom_id) or (self.browse(cr, uid, bom_id, context=context).type != "phantom"): + result.append(self._prepare_consume_line( + cr, uid, bom_line_id, quantity, context=context)) + else: + all_prod = [bom.product_tmpl_id.id] + (previous_products or []) + bom2 = self.browse(cr, uid, bom_id, context=context) + # We need to convert to units/UoM of chosen BoM + factor2 = uom_obj._compute_qty(cr, uid, bom_line_id.product_uom.id, quantity, bom2.product_uom.id) + quantity2 = factor2 / bom2.product_qty + res = self._bom_explode(cr, uid, bom2, bom_line_id.product_id, quantity2, + properties=properties, level=level + 10, previous_products=all_prod, master_bom=master_bom, context=context) + result = result + res[0] + result2 = result2 + res[1] + return result, result2 + + def copy_data(self, cr, uid, id, default=None, context=None): + if default is None: + default = {} + bom_data = self.read(cr, uid, id, [], context=context) + default.update(name=_("%s (copy)") % (bom_data['display_name'])) + return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context) + + def onchange_uom(self, cr, uid, ids, product_tmpl_id, product_uom, context=None): + res = {'value': {}} + if not product_uom or not product_tmpl_id: + return res + product = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} + res['value'].update({'product_uom': product.uom_id.id}) + return res + + def unlink(self, cr, uid, ids, context=None): + if self.pool['mrp.production'].search(cr, uid, [('bom_id', 'in', ids), ('state', 'not in', ['done', 'cancel'])], context=context): + raise UserError(_('You can not delete a Bill of Material with running manufacturing orders.\nPlease close or cancel it first.')) + return super(mrp_bom, self).unlink(cr, uid, ids, context=context) + + def onchange_product_tmpl_id(self, cr, uid, ids, product_tmpl_id, product_qty=0, context=None): + """ Changes UoM and name if product_id changes. + @param product_id: Changed product_id + @return: Dictionary of changed values + """ + res = {} + if product_tmpl_id: + prod = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=context) + res['value'] = { + 'product_uom': prod.uom_id.id, + } + return res + + def name_get(self, cr, uid, ids, context=None): + res = [] + for record in self.browse(cr, uid, ids, context=context): + name = record.product_tmpl_id.name + if record.code: + name = '[%s] %s' % (record.code, name) + res.append((record.id, name)) + return res + + +class mrp_bom_line(osv.osv): + _name = 'mrp.bom.line' + _order = "sequence" + _rec_name = "product_id" + + def _get_child_bom_lines(self, cr, uid, ids, field_name, arg, context=None): + """If the BOM line refers to a BOM, return the ids of the child BOM lines""" + bom_obj = self.pool['mrp.bom'] + res = {} + for bom_line in self.browse(cr, uid, ids, context=context): + bom_id = bom_obj._bom_find(cr, uid, + product_tmpl_id=bom_line.product_id.product_tmpl_id.id, + product_id=bom_line.product_id.id, context=context) + if bom_id: + child_bom = bom_obj.browse(cr, uid, bom_id, context=context) + res[bom_line.id] = [x.id for x in child_bom.bom_line_ids] + else: + res[bom_line.id] = False + return res + + _columns = { + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')), + 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, + help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"), + + 'date_start': fields.date('Valid From', help="Validity of component. Keep empty if it's always valid."), + 'date_stop': fields.date('Valid Until', help="Validity of component. Keep empty if it's always valid."), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying."), + 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."), + 'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."), + 'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% within the production process."), + 'property_ids': fields.many2many('mrp.property', string='Properties'), #Not used + + 'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True, required=True), + 'attribute_value_ids': fields.many2many('product.attribute.value', string='Variants', help="BOM Product Variants needed form apply this line."), + 'child_line_ids': fields.function(_get_child_bom_lines, relation="mrp.bom.line", string="BOM lines of the referred bom", type="one2many") + } + + def _get_uom_id(self, cr, uid, *args): + return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0] + _defaults = { + 'product_qty': lambda *a: 1.0, + 'product_efficiency': lambda *a: 1.0, + 'product_rounding': lambda *a: 0.0, + 'product_uom': _get_uom_id, + 'sequence': 1, + } + _sql_constraints = [ + ('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\n' \ + 'You should install the mrp_byproduct module if you want to manage extra products on BoMs !'), + ] + + def create(self, cr, uid, values, context=None): + if context is None: + context = {} + product_obj = self.pool.get('product.product') + if 'product_id' in values and not 'product_uom' in values: + values['product_uom'] = product_obj.browse(cr, uid, values.get('product_id'), context=context).uom_id.id + return super(mrp_bom_line, self).create(cr, uid, values, context=context) + + def onchange_uom(self, cr, uid, ids, product_id, product_uom, context=None): + res = {'value': {}} + if not product_uom or not product_id: + return res + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} + res['value'].update({'product_uom': product.uom_id.id}) + return res + + def onchange_product_id(self, cr, uid, ids, product_id, product_qty=0, context=None): + """ Changes UoM if product_id changes. + @param product_id: Changed product_id + @return: Dictionary of changed values + """ + res = {} + if product_id: + prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + res['value'] = { + 'product_uom': prod.uom_id.id, + } + return res
\ No newline at end of file diff --git a/addons/mrp/res_config.py b/addons/mrp/models/mrp_config_settings.py index 7d68399af33..7d68399af33 100644 --- a/addons/mrp/res_config.py +++ b/addons/mrp/models/mrp_config_settings.py diff --git a/addons/mrp/mrp.py b/addons/mrp/models/mrp_production.py index 5173746c195..22dae0e2ad9 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/models/mrp_production.py @@ -6,466 +6,12 @@ from collections import OrderedDict import openerp.addons.decimal_precision as dp from openerp.osv import fields, osv -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT from openerp.tools import float_compare, float_is_zero from openerp.tools.translate import _ -from openerp import tools, SUPERUSER_ID +from openerp import SUPERUSER_ID from openerp.exceptions import UserError, AccessError -class mrp_property_group(osv.osv): - """ - Group of mrp properties. - """ - _name = 'mrp.property.group' - _description = 'Property Group' - _columns = { - 'name': fields.char('Property Group', required=True), - 'description': fields.text('Description'), - } - -class mrp_property(osv.osv): - """ - Properties of mrp. - """ - _name = 'mrp.property' - _description = 'Property' - _columns = { - 'name': fields.char('Name', required=True), - 'composition': fields.selection([('min','min'),('max','max'),('plus','plus')], 'Properties composition', required=True, help="Not used in computations, for information purpose only."), - 'group_id': fields.many2one('mrp.property.group', 'Property Group', required=True), - 'description': fields.text('Description'), - } - _defaults = { - 'composition': lambda *a: 'min', - } -#---------------------------------------------------------- -# Work Centers -#---------------------------------------------------------- -# capacity_hour : capacity per hour. default: 1.0. -# Eg: If 5 concurrent operations at one time: capacity = 5 (because 5 employees) -# unit_per_cycle : how many units are produced for one cycle - -class mrp_workcenter(osv.osv): - _name = 'mrp.workcenter' - _description = 'Work Center' - _inherits = {'resource.resource':"resource_id"} - _columns = { - 'active': fields.boolean('Active'), - 'note': fields.text('Description', help="Description of the Work Center. Explain here what's a cycle according to this Work Center."), - 'capacity_per_cycle': fields.float('Capacity per Cycle', help="Number of operations this Work Center can do in parallel. If this Work Center represents a team of 5 workers, the capacity per cycle is 5."), - 'time_cycle': fields.float('Time for 1 cycle (hour)', help="Time in hours for doing one cycle."), - 'time_start': fields.float('Time before prod.', help="Time in hours for the setup."), - 'time_stop': fields.float('Time after prod.', help="Time in hours for the cleaning."), - 'costs_hour': fields.float('Cost per hour', help="Specify Cost of Work Center per hour."), - 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', - help="Fill this only if you want automatic analytic accounting entries on production orders."), - 'costs_cycle': fields.float('Cost per cycle', help="Specify Cost of Work Center per cycle."), - 'costs_cycle_account_id': fields.many2one('account.analytic.account', 'Cycle Account', - help="Fill this only if you want automatic analytic accounting entries on production orders."), - 'costs_general_account_id': fields.many2one('account.account', 'General Account', domain=[('deprecated', '=', False)]), - 'resource_id': fields.many2one('resource.resource','Resource', ondelete='cascade', required=True), - 'product_id': fields.many2one('product.product','Work Center Product', help="Fill this product to easily track your production costs in the analytic accounting."), - } - _defaults = { - 'active': True, - 'capacity_per_cycle': 1.0, - 'resource_type': 'material', - } - - def on_change_product_cost(self, cr, uid, ids, product_id, context=None): - value = {} - - if product_id: - cost = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - value = {'costs_hour': cost.standard_price} - return {'value': value} - - def _check_capacity_per_cycle(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - if obj.capacity_per_cycle <= 0.0: - return False - return True - - _constraints = [ - (_check_capacity_per_cycle, 'The capacity per cycle must be strictly positive.', ['capacity_per_cycle']), - ] - -class mrp_routing(osv.osv): - """ - For specifying the routings of Work Centers. - """ - _name = 'mrp.routing' - _description = 'Routings' - _columns = { - 'name': fields.char('Name', required=True), - 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the routing without removing it."), - 'code': fields.char('Code', size=8), - - 'note': fields.text('Description'), - 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Work Centers', copy=True), - - 'location_id': fields.many2one('stock.location', 'Production Location', - help="Keep empty if you produce at the location where the finished products are needed." \ - "Set a location if you produce at a fixed location. This can be a partner location " \ - "if you subcontract the manufacturing operations." - ), - 'company_id': fields.many2one('res.company', 'Company'), - } - _defaults = { - 'active': lambda *a: 1, - 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.routing', context=context) - } - -class mrp_routing_workcenter(osv.osv): - """ - Defines working cycles and hours of a Work Center using routings. - """ - _name = 'mrp.routing.workcenter' - _description = 'Work Center Usage' - _order = 'sequence, id' - _columns = { - 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), - 'name': fields.char('Name', required=True), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of routing Work Centers."), - 'cycle_nbr': fields.float('Number of Cycles', required=True, - help="Number of iterations this work center has to do in the specified operation of the routing."), - 'hour_nbr': fields.float('Number of Hours', required=True, help="Time in hours for this Work Center to achieve the operation of the specified routing."), - 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade', - help="Routings indicates all the Work Centers used, for how long and/or cycles." \ - "If Routings is set then,the third tab of a production order (Work Centers) will be automatically pre-completed."), - 'note': fields.text('Description'), - 'company_id': fields.related('routing_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - } - _defaults = { - 'cycle_nbr': lambda *a: 1.0, - 'hour_nbr': lambda *a: 0.0, - 'sequence': 100, - } - -class mrp_bom(osv.osv): - """ - Defines bills of material for a product. - """ - _name = 'mrp.bom' - _description = 'Bill of Material' - _inherit = ['mail.thread'] - - _columns = { - 'code': fields.char('Reference', size=16), - 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the bills of material without removing it."), - 'type': fields.selection([('normal','Manufacture this product'),('phantom','Ship this product as a set of components (kit)')], 'BoM Type', required=True, - help= "Set: When processing a sales order for this product, the delivery order will contain the raw materials, instead of the finished product."), - 'position': fields.char('Internal Reference', help="Reference to a position in an external plan."), - 'product_tmpl_id': fields.many2one('product.template', 'Product', domain="[('type', 'in', ['product', 'consu'])]", required=True), - 'product_id': fields.many2one('product.product', 'Product Variant', - domain="['&', ('product_tmpl_id','=',product_tmpl_id), ('type', 'in', ['product', 'consu'])]", - help="If a product variant is defined the BOM is available only for this product."), - 'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines', copy=True), - 'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')), - 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"), - 'date_start': fields.date('Valid From', help="Validity of this BoM. Keep empty if it's always valid."), - 'date_stop': fields.date('Valid Until', help="Validity of this BoM. Keep empty if it's always valid."), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of bills of material."), - 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. "\ - "The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."), - 'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."), - 'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% during the production process."), - 'property_ids': fields.many2many('mrp.property', string='Properties'), - 'company_id': fields.many2one('res.company', 'Company', required=True), - } - - def _get_uom_id(self, cr, uid, *args): - return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0] - _defaults = { - 'active': lambda *a: 1, - 'product_qty': lambda *a: 1.0, - 'product_efficiency': lambda *a: 1.0, - 'product_rounding': lambda *a: 0.0, - 'type': lambda *a: 'normal', - 'product_uom': _get_uom_id, - 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.bom', context=c), - } - _order = "sequence" - - - def _bom_find(self, cr, uid, product_tmpl_id=None, product_id=None, properties=None, context=None): - """ Finds BoM for particular product and product uom. - @param product_tmpl_id: Selected product. - @param product_uom: Unit of measure of a product. - @param properties: List of related properties. - @return: False or BoM id. - """ - if not context: - context = {} - if properties is None: - properties = [] - if product_id: - if not product_tmpl_id: - product_tmpl_id = self.pool['product.product'].browse(cr, uid, product_id, context=context).product_tmpl_id.id - domain = [ - '|', - ('product_id', '=', product_id), - '&', - ('product_id', '=', False), - ('product_tmpl_id', '=', product_tmpl_id) - ] - elif product_tmpl_id: - domain = [('product_id', '=', False), ('product_tmpl_id', '=', product_tmpl_id)] - else: - # neither product nor template, makes no sense to search - return False - if context.get('company_id'): - domain = domain + [('company_id', '=', context['company_id'])] - domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT)), - '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT))] - # order to prioritize bom with product_id over the one without - ids = self.search(cr, uid, domain, order='sequence, product_id', context=context) - # Search a BoM which has all properties specified, or if you can not find one, you could - # pass a BoM without any properties with the smallest sequence - bom_empty_prop = False - for bom in self.pool.get('mrp.bom').browse(cr, uid, ids, context=context): - if not set(map(int, bom.property_ids or [])) - set(properties or []): - if not properties or bom.property_ids: - return bom.id - elif not bom_empty_prop: - bom_empty_prop = bom.id - return bom_empty_prop - - def _skip_bom_line(self, cr, uid, line, product, context=None): - """ Control if a BoM line should be produce, can be inherited for add - custom control. - @param line: BoM line. - @param product: Selected product produced. - @return: True or False - """ - if line.date_start and line.date_start > time.strftime(DEFAULT_SERVER_DATE_FORMAT) or \ - line.date_stop and line.date_stop < time.strftime(DEFAULT_SERVER_DATE_FORMAT): - return True - # all bom_line_id variant values must be in the product - if line.attribute_value_ids: - if not product or (set(map(int,line.attribute_value_ids or [])) - set(map(int,product.attribute_value_ids))): - return True - return False - - def _prepare_wc_line(self, cr, uid, bom, wc_use, level=0, factor=1, context=None): - wc = wc_use.workcenter_id - d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle) - mult = (d + (m and 1.0 or 0.0)) - cycle = mult * wc_use.cycle_nbr - return { - 'name': tools.ustr(wc_use.name) + ' - ' + tools.ustr(bom.product_tmpl_id.name_get()[0][1]), - 'workcenter_id': wc.id, - 'sequence': level + (wc_use.sequence or 0), - 'cycle': cycle, - 'hour': float(wc_use.hour_nbr * mult + ((wc.time_start or 0.0) + (wc.time_stop or 0.0) + cycle * (wc.time_cycle or 0.0)) * (wc.time_efficiency or 100)), - } - - def _prepare_consume_line(self, cr, uid, bom_line_id, quantity, context=None): - return { - 'name': bom_line_id.product_id.name, - 'product_id': bom_line_id.product_id.id, - 'product_qty': quantity, - 'product_uom': bom_line_id.product_uom.id - } - - def _bom_explode(self, cr, uid, bom, product, factor, properties=None, level=0, routing_id=False, previous_products=None, master_bom=None, context=None): - """ Finds Products and Work Centers for related BoM for manufacturing order. - @param bom: BoM of particular product template. - @param product: Select a particular variant of the BoM. If False use BoM without variants. - @param factor: Factor represents the quantity, but in UoM of the BoM, taking into account the numbers produced by the BoM - @param properties: A List of properties Ids. - @param level: Depth level to find BoM lines starts from 10. - @param previous_products: List of product previously use by bom explore to avoid recursion - @param master_bom: When recursion, used to display the name of the master bom - @return: result: List of dictionaries containing product details. - result2: List of dictionaries containing Work Center details. - """ - uom_obj = self.pool.get("product.uom") - routing_obj = self.pool.get('mrp.routing') - master_bom = master_bom or bom - - - def _factor(factor, product_efficiency, product_rounding): - factor = factor / (product_efficiency or 1.0) - if product_rounding: - factor = tools.float_round(factor, - precision_rounding=product_rounding, - rounding_method='UP') - if factor < product_rounding: - factor = product_rounding - return factor - - factor = _factor(factor, bom.product_efficiency, bom.product_rounding) - - result = [] - result2 = [] - - routing = (routing_id and routing_obj.browse(cr, uid, routing_id)) or bom.routing_id or False - if routing: - for wc_use in routing.workcenter_lines: - result2.append(self._prepare_wc_line( - cr, uid, bom, wc_use, level=level, factor=factor, - context=context)) - - for bom_line_id in bom.bom_line_ids: - if self._skip_bom_line(cr, uid, bom_line_id, product, context=context): - continue - if set(map(int, bom_line_id.property_ids or [])) - set(properties or []): - continue - - if previous_products and bom_line_id.product_id.product_tmpl_id.id in previous_products: - raise UserError(_('BoM "%s" contains a BoM line with a product recursion: "%s".') % (master_bom.code or "", bom_line_id.product_id.name_get()[0][1])) - - quantity = _factor(bom_line_id.product_qty * factor, bom_line_id.product_efficiency, bom_line_id.product_rounding) - bom_id = self._bom_find(cr, uid, product_id=bom_line_id.product_id.id, properties=properties, context=context) - - #If BoM should not behave like kit, just add the product, otherwise explode further - if (not bom_id) or (self.browse(cr, uid, bom_id, context=context).type != "phantom"): - result.append(self._prepare_consume_line( - cr, uid, bom_line_id, quantity, context=context)) - else: - all_prod = [bom.product_tmpl_id.id] + (previous_products or []) - bom2 = self.browse(cr, uid, bom_id, context=context) - # We need to convert to units/UoM of chosen BoM - factor2 = uom_obj._compute_qty(cr, uid, bom_line_id.product_uom.id, quantity, bom2.product_uom.id) - quantity2 = factor2 / bom2.product_qty - res = self._bom_explode(cr, uid, bom2, bom_line_id.product_id, quantity2, - properties=properties, level=level + 10, previous_products=all_prod, master_bom=master_bom, context=context) - result = result + res[0] - result2 = result2 + res[1] - return result, result2 - - def copy_data(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - bom_data = self.read(cr, uid, id, [], context=context) - default.update(name=_("%s (copy)") % (bom_data['display_name'])) - return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context) - - def onchange_uom(self, cr, uid, ids, product_tmpl_id, product_uom, context=None): - res = {'value': {}} - if not product_uom or not product_tmpl_id: - return res - product = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} - res['value'].update({'product_uom': product.uom_id.id}) - return res - - def unlink(self, cr, uid, ids, context=None): - if self.pool['mrp.production'].search(cr, uid, [('bom_id', 'in', ids), ('state', 'not in', ['done', 'cancel'])], context=context): - raise UserError(_('You can not delete a Bill of Material with running manufacturing orders.\nPlease close or cancel it first.')) - return super(mrp_bom, self).unlink(cr, uid, ids, context=context) - - def onchange_product_tmpl_id(self, cr, uid, ids, product_tmpl_id, product_qty=0, context=None): - """ Changes UoM and name if product_id changes. - @param product_id: Changed product_id - @return: Dictionary of changed values - """ - res = {} - if product_tmpl_id: - prod = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=context) - res['value'] = { - 'product_uom': prod.uom_id.id, - } - return res - - def name_get(self, cr, uid, ids, context=None): - res = [] - for record in self.browse(cr, uid, ids, context=context): - name = record.product_tmpl_id.name - if record.code: - name = '[%s] %s' % (record.code, name) - res.append((record.id, name)) - return res - -class mrp_bom_line(osv.osv): - _name = 'mrp.bom.line' - _order = "sequence" - _rec_name = "product_id" - - def _get_child_bom_lines(self, cr, uid, ids, field_name, arg, context=None): - """If the BOM line refers to a BOM, return the ids of the child BOM lines""" - bom_obj = self.pool['mrp.bom'] - res = {} - for bom_line in self.browse(cr, uid, ids, context=context): - bom_id = bom_obj._bom_find(cr, uid, - product_tmpl_id=bom_line.product_id.product_tmpl_id.id, - product_id=bom_line.product_id.id, context=context) - if bom_id: - child_bom = bom_obj.browse(cr, uid, bom_id, context=context) - res[bom_line.id] = [x.id for x in child_bom.bom_line_ids] - else: - res[bom_line.id] = False - return res - - _columns = { - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')), - 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, - help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"), - - 'date_start': fields.date('Valid From', help="Validity of component. Keep empty if it's always valid."), - 'date_stop': fields.date('Valid Until', help="Validity of component. Keep empty if it's always valid."), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying."), - 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."), - 'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."), - 'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% within the production process."), - 'property_ids': fields.many2many('mrp.property', string='Properties'), #Not used - - 'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True, required=True), - 'attribute_value_ids': fields.many2many('product.attribute.value', string='Variants', help="BOM Product Variants needed form apply this line."), - 'child_line_ids': fields.function(_get_child_bom_lines, relation="mrp.bom.line", string="BOM lines of the referred bom", type="one2many") - } - - def _get_uom_id(self, cr, uid, *args): - return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0] - _defaults = { - 'product_qty': lambda *a: 1.0, - 'product_efficiency': lambda *a: 1.0, - 'product_rounding': lambda *a: 0.0, - 'product_uom': _get_uom_id, - 'sequence': 1, - } - _sql_constraints = [ - ('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\n' \ - 'You should install the mrp_byproduct module if you want to manage extra products on BoMs !'), - ] - - def create(self, cr, uid, values, context=None): - if context is None: - context = {} - product_obj = self.pool.get('product.product') - if 'product_id' in values and not 'product_uom' in values: - values['product_uom'] = product_obj.browse(cr, uid, values.get('product_id'), context=context).uom_id.id - return super(mrp_bom_line, self).create(cr, uid, values, context=context) - - def onchange_uom(self, cr, uid, ids, product_id, product_uom, context=None): - res = {'value': {}} - if not product_uom or not product_id: - return res - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context) - if uom.category_id.id != product.uom_id.category_id.id: - res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')} - res['value'].update({'product_uom': product.uom_id.id}) - return res - - def onchange_product_id(self, cr, uid, ids, product_id, product_qty=0, context=None): - """ Changes UoM if product_id changes. - @param product_id: Changed product_id - @return: Dictionary of changed values - """ - res = {} - if product_id: - prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context) - res['value'] = { - 'product_uom': prod.uom_id.id, - } - return res - class mrp_production(osv.osv): """ Production Orders / Manufacturing Orders diff --git a/addons/mrp/models/mrp_property.py b/addons/mrp/models/mrp_property.py new file mode 100644 index 00000000000..d74b34ef67e --- /dev/null +++ b/addons/mrp/models/mrp_property.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp.osv import fields, osv + + +class mrp_property_group(osv.osv): + """ + Group of mrp properties. + """ + _name = 'mrp.property.group' + _description = 'Property Group' + _columns = { + 'name': fields.char('Property Group', required=True), + 'description': fields.text('Description'), + } + + +class mrp_property(osv.osv): + """ + Properties of mrp. + """ + _name = 'mrp.property' + _description = 'Property' + _columns = { + 'name': fields.char('Name', required=True), + 'composition': fields.selection([('min','min'),('max','max'),('plus','plus')], 'Properties composition', required=True, help="Not used in computations, for information purpose only."), + 'group_id': fields.many2one('mrp.property.group', 'Property Group', required=True), + 'description': fields.text('Description'), + } + _defaults = { + 'composition': lambda *a: 'min', + } diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py new file mode 100644 index 00000000000..0b20411f1a8 --- /dev/null +++ b/addons/mrp/models/mrp_routing.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp.osv import fields, osv + + +class mrp_routing(osv.osv): + """ + For specifying the routings of Work Centers. + """ + _name = 'mrp.routing' + _description = 'Routings' + _columns = { + 'name': fields.char('Name', required=True), + 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the routing without removing it."), + 'code': fields.char('Code', size=8), + + 'note': fields.text('Description'), + 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Work Centers', copy=True), + + 'location_id': fields.many2one('stock.location', 'Production Location', + help="Keep empty if you produce at the location where the finished products are needed." \ + "Set a location if you produce at a fixed location. This can be a partner location " \ + "if you subcontract the manufacturing operations." + ), + 'company_id': fields.many2one('res.company', 'Company'), + } + _defaults = { + 'active': lambda *a: 1, + 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.routing', context=context) + } + +class mrp_routing_workcenter(osv.osv): + """ + Defines working cycles and hours of a Work Center using routings. + """ + _name = 'mrp.routing.workcenter' + _description = 'Work Center Usage' + _order = 'sequence, id' + _columns = { + 'workcenter_id': fields.many2one('mrp.workcenter', 'Work Center', required=True), + 'name': fields.char('Name', required=True), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of routing Work Centers."), + 'cycle_nbr': fields.float('Number of Cycles', required=True, + help="Number of iterations this work center has to do in the specified operation of the routing."), + 'hour_nbr': fields.float('Number of Hours', required=True, help="Time in hours for this Work Center to achieve the operation of the specified routing."), + 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade', + help="Routings indicates all the Work Centers used, for how long and/or cycles." \ + "If Routings is set then,the third tab of a production order (Work Centers) will be automatically pre-completed."), + 'note': fields.text('Description'), + 'company_id': fields.related('routing_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), + } + _defaults = { + 'cycle_nbr': lambda *a: 1.0, + 'hour_nbr': lambda *a: 0.0, + 'sequence': 100, + }
\ No newline at end of file diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py new file mode 100644 index 00000000000..708bbc38123 --- /dev/null +++ b/addons/mrp/models/mrp_workcenter.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp.osv import fields, osv + +#---------------------------------------------------------- +# Work Centers +#---------------------------------------------------------- +# capacity_hour : capacity per hour. default: 1.0. +# Eg: If 5 concurrent operations at one time: capacity = 5 (because 5 employees) +# unit_per_cycle : how many units are produced for one cycle + + +class mrp_workcenter(osv.osv): + _name = 'mrp.workcenter' + _description = 'Work Center' + _inherits = {'resource.resource':"resource_id"} + _columns = { + 'active': fields.boolean('Active'), + 'note': fields.text('Description', help="Description of the Work Center. Explain here what's a cycle according to this Work Center."), + 'capacity_per_cycle': fields.float('Capacity per Cycle', help="Number of operations this Work Center can do in parallel. If this Work Center represents a team of 5 workers, the capacity per cycle is 5."), + 'time_cycle': fields.float('Time for 1 cycle (hour)', help="Time in hours for doing one cycle."), + 'time_start': fields.float('Time before prod.', help="Time in hours for the setup."), + 'time_stop': fields.float('Time after prod.', help="Time in hours for the cleaning."), + 'costs_hour': fields.float('Cost per hour', help="Specify Cost of Work Center per hour."), + 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', + help="Fill this only if you want automatic analytic accounting entries on production orders."), + 'costs_cycle': fields.float('Cost per cycle', help="Specify Cost of Work Center per cycle."), + 'costs_cycle_account_id': fields.many2one('account.analytic.account', 'Cycle Account', + help="Fill this only if you want automatic analytic accounting entries on production orders."), + 'costs_general_account_id': fields.many2one('account.account', 'General Account', domain=[('deprecated', '=', False)]), + 'resource_id': fields.many2one('resource.resource','Resource', ondelete='cascade', required=True), + 'product_id': fields.many2one('product.product','Work Center Product', help="Fill this product to easily track your production costs in the analytic accounting."), + } + _defaults = { + 'active': True, + 'capacity_per_cycle': 1.0, + 'resource_type': 'material', + } + + def on_change_product_cost(self, cr, uid, ids, product_id, context=None): + value = {} + + if product_id: + cost = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + value = {'costs_hour': cost.standard_price} + return {'value': value} + + def _check_capacity_per_cycle(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids, context=context): + if obj.capacity_per_cycle <= 0.0: + return False + return True + + _constraints = [ + (_check_capacity_per_cycle, 'The capacity per cycle must be strictly positive.', ['capacity_per_cycle']), + ]
\ No newline at end of file diff --git a/addons/mrp/procurement.py b/addons/mrp/models/procurement.py index 613713b6fa1..613713b6fa1 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/models/procurement.py diff --git a/addons/mrp/product.py b/addons/mrp/models/product.py index 41e6d0a9049..41e6d0a9049 100644 --- a/addons/mrp/product.py +++ b/addons/mrp/models/product.py diff --git a/addons/mrp/company.py b/addons/mrp/models/res_company.py index e69235da007..e69235da007 100644 --- a/addons/mrp/company.py +++ b/addons/mrp/models/res_company.py diff --git a/addons/mrp/stock.py b/addons/mrp/models/stock_move.py index 2299e6ba853..745ea2c143d 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/models/stock_move.py @@ -222,79 +222,3 @@ class StockMove(osv.osv): if self.pool.get('mrp.production').test_ready(cr, uid, [order_id]): workflow.trg_validate(uid, 'mrp.production', order_id, 'moves_ready', cr) return res - -class stock_warehouse(osv.osv): - _inherit = 'stock.warehouse' - _columns = { - 'manufacture_to_resupply': fields.boolean('Manufacture in this Warehouse', - help="When products are manufactured, they can be manufactured in this warehouse."), - 'manufacture_pull_id': fields.many2one('procurement.rule', 'Manufacture Rule'), - } - - _defaults = { - 'manufacture_to_resupply': True, - } - - def _get_manufacture_pull_rule(self, cr, uid, warehouse, context=None): - route_obj = self.pool.get('stock.location.route') - data_obj = self.pool.get('ir.model.data') - try: - manufacture_route_id = data_obj.get_object_reference(cr, uid, 'mrp', 'route_warehouse0_manufacture')[1] - except: - manufacture_route_id = route_obj.search(cr, uid, [('name', 'like', _('Manufacture'))], context=context) - manufacture_route_id = manufacture_route_id and manufacture_route_id[0] or False - if not manufacture_route_id: - raise UserError(_('Can\'t find any generic Manufacture route.')) - - return { - 'name': warehouse._format_routename(_(' Manufacture')), - 'location_id': warehouse.lot_stock_id.id, - 'route_id': manufacture_route_id, - 'action': 'manufacture', - 'picking_type_id': warehouse.int_type_id.id, - 'propagate': False, - 'warehouse_id': warehouse.id, - } - - def create_routes(self, cr, uid, ids, context=None): - pull_obj = self.pool.get('procurement.rule') - res = super(stock_warehouse, self).create_routes(cr, uid, ids, context=context) - warehouse = self.browse(cr, uid, ids, context=context)[0] - if warehouse.manufacture_to_resupply: - manufacture_pull_vals = self._get_manufacture_pull_rule(cr, uid, warehouse, context=context) - manufacture_pull_id = pull_obj.create(cr, uid, manufacture_pull_vals, context=context) - res['manufacture_pull_id'] = manufacture_pull_id - return res - - def write(self, cr, uid, ids, vals, context=None): - pull_obj = self.pool.get('procurement.rule') - if isinstance(ids, (int, long)): - ids = [ids] - - if 'manufacture_to_resupply' in vals: - if vals.get("manufacture_to_resupply"): - for warehouse in self.browse(cr, uid, ids, context=context): - if not warehouse.manufacture_pull_id: - manufacture_pull_vals = self._get_manufacture_pull_rule(cr, uid, warehouse, context=context) - manufacture_pull_id = pull_obj.create(cr, uid, manufacture_pull_vals, context=context) - vals['manufacture_pull_id'] = manufacture_pull_id - else: - for warehouse in self.browse(cr, uid, ids, context=context): - if warehouse.manufacture_pull_id: - pull_obj.unlink(cr, uid, warehouse.manufacture_pull_id.id, context=context) - return super(stock_warehouse, self).write(cr, uid, ids, vals, context=context) - - @api.multi - def _get_all_routes(self): - routes = super(stock_warehouse, self).get_all_routes_for_wh() - routes |= self.filtered(lambda self: self.manufacture_to_resupply and self.manufacture_pull_id and self.manufacture_pull_id.route_id).mapped('manufacture_pull_id').mapped('route_id') - return routes - - def _handle_renaming(self, cr, uid, ids, name, code, context=None): - res = super(stock_warehouse, self)._handle_renaming(cr, uid, ids, name, code, context=context) - warehouse = self.browse(cr, uid, ids[0], context=context) - pull_obj = self.pool.get('procurement.rule') - #change the manufacture procurement rule name - if warehouse.manufacture_pull_id: - pull_obj.write(cr, uid, warehouse.manufacture_pull_id.id, {'name': warehouse.manufacture_pull_id.name.replace(warehouse.name, name, 1)}, context=context) - return res diff --git a/addons/mrp/models/stock_warehouse.py b/addons/mrp/models/stock_warehouse.py new file mode 100644 index 00000000000..969b60b9cb0 --- /dev/null +++ b/addons/mrp/models/stock_warehouse.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + + +from openerp import api +from openerp.osv import fields +from openerp.osv import osv +from openerp.tools.translate import _ +from openerp.exceptions import UserError + + +class stock_warehouse(osv.osv): + _inherit = 'stock.warehouse' + _columns = { + 'manufacture_to_resupply': fields.boolean('Manufacture in this Warehouse', + help="When products are manufactured, they can be manufactured in this warehouse."), + 'manufacture_pull_id': fields.many2one('procurement.rule', 'Manufacture Rule'), + } + + _defaults = { + 'manufacture_to_resupply': True, + } + + def _get_manufacture_pull_rule(self, cr, uid, warehouse, context=None): + route_obj = self.pool.get('stock.location.route') + data_obj = self.pool.get('ir.model.data') + try: + manufacture_route_id = data_obj.get_object_reference(cr, uid, 'mrp', 'route_warehouse0_manufacture')[1] + except: + manufacture_route_id = route_obj.search(cr, uid, [('name', 'like', _('Manufacture'))], context=context) + manufacture_route_id = manufacture_route_id and manufacture_route_id[0] or False + if not manufacture_route_id: + raise UserError(_('Can\'t find any generic Manufacture route.')) + + return { + 'name': warehouse._format_routename(_(' Manufacture')), + 'location_id': warehouse.lot_stock_id.id, + 'route_id': manufacture_route_id, + 'action': 'manufacture', + 'picking_type_id': warehouse.int_type_id.id, + 'propagate': False, + 'warehouse_id': warehouse.id, + } + + def create_routes(self, cr, uid, ids, context=None): + pull_obj = self.pool.get('procurement.rule') + res = super(stock_warehouse, self).create_routes(cr, uid, ids, context=context) + warehouse = self.browse(cr, uid, ids, context=context)[0] + if warehouse.manufacture_to_resupply: + manufacture_pull_vals = self._get_manufacture_pull_rule(cr, uid, warehouse, context=context) + manufacture_pull_id = pull_obj.create(cr, uid, manufacture_pull_vals, context=context) + res['manufacture_pull_id'] = manufacture_pull_id + return res + + def write(self, cr, uid, ids, vals, context=None): + pull_obj = self.pool.get('procurement.rule') + if isinstance(ids, (int, long)): + ids = [ids] + + if 'manufacture_to_resupply' in vals: + if vals.get("manufacture_to_resupply"): + for warehouse in self.browse(cr, uid, ids, context=context): + if not warehouse.manufacture_pull_id: + manufacture_pull_vals = self._get_manufacture_pull_rule(cr, uid, warehouse, context=context) + manufacture_pull_id = pull_obj.create(cr, uid, manufacture_pull_vals, context=context) + vals['manufacture_pull_id'] = manufacture_pull_id + else: + for warehouse in self.browse(cr, uid, ids, context=context): + if warehouse.manufacture_pull_id: + pull_obj.unlink(cr, uid, warehouse.manufacture_pull_id.id, context=context) + return super(stock_warehouse, self).write(cr, uid, ids, vals, context=context) + + @api.multi + def _get_all_routes(self): + routes = super(stock_warehouse, self).get_all_routes_for_wh() + routes |= self.filtered(lambda self: self.manufacture_to_resupply and self.manufacture_pull_id and self.manufacture_pull_id.route_id).mapped('manufacture_pull_id').mapped('route_id') + return routes + + def _handle_renaming(self, cr, uid, ids, name, code, context=None): + res = super(stock_warehouse, self)._handle_renaming(cr, uid, ids, name, code, context=context) + warehouse = self.browse(cr, uid, ids[0], context=context) + pull_obj = self.pool.get('procurement.rule') + #change the manufacture procurement rule name + if warehouse.manufacture_pull_id: + pull_obj.write(cr, uid, warehouse.manufacture_pull_id.id, {'name': warehouse.manufacture_pull_id.name.replace(warehouse.name, name, 1)}, context=context) + return res diff --git a/addons/mrp/mrp_demo.yml b/addons/mrp/mrp_demo.yml deleted file mode 100644 index 69abe7ec830..00000000000 --- a/addons/mrp/mrp_demo.yml +++ /dev/null @@ -1,8 +0,0 @@ -- - !python {model: ir.model.data} : | - account_id = self.pool.get('account.account').search(cr, uid, [('user_type_id', '=', ref('account.data_account_type_expenses')), ('tag_ids', 'in', [ref('account.account_tag_operating')])]) - if not account_id: - account_id = self.pool.get('account.account').search(cr, uid, [('user_type_id', '=', ref('account.data_account_type_expenses'))]) - if account_id: - self._update(cr, uid, 'mrp.workcenter', 'mrp', {'costs_general_account_id': account_id[0]}, 'mrp_workcenter_0') -- diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml deleted file mode 100644 index 2a22fd7be51..00000000000 --- a/addons/mrp/mrp_view.xml +++ /dev/null @@ -1,1102 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - <!-- Top menu item --> - <menuitem id="base.menu_mrp_root" - name="Manufacturing" - groups="group_mrp_user,group_mrp_manager" - web_icon="mrp,static/description/icon.png" - sequence="35"/> - - <menuitem id="menu_mrp_manufacturing" - name="Manufacturing" - parent="base.menu_mrp_root" - sequence="1"/> - - <menuitem id="menu_mrp_planning" - name="Planning" - parent="base.menu_mrp_root" - sequence="2"/> - - <menuitem id="menu_mrp_bom" - name="Products" - parent="base.menu_mrp_root" - sequence="5"/> - - <menuitem id="menu_mrp_configuration" - name="Configuration" - parent="base.menu_mrp_root" - groups="group_mrp_manager" - sequence="100"/> - - <menuitem id="menu_pm_resources_config" - name="Resources" - parent="menu_mrp_configuration" - sequence="100"/> - - <menuitem id="menu_view_resource_calendar_search_mrp" - action="resource.action_resource_calendar_form" - parent="menu_pm_resources_config" - sequence="10" - groups="base.group_no_one"/> - - <menuitem id="menu_view_resource_calendar_leaves_search_mrp" - action="resource.action_resource_calendar_leave_tree" - parent="menu_pm_resources_config" - sequence="20" - groups="base.group_no_one"/> - - <!-- Property Group --> - <record id="mrp_property_group_form_view" model="ir.ui.view"> - <field name="name">mrp.property.group.form</field> - <field name="model">mrp.property.group</field> - <field name="arch" type="xml"> - <form string="Properties categories"> - <group> - <field name="name"/> - </group> - <separator string="Description"/> - <field name="description" nolabel="1"/> - </form> - </field> - </record> - <record id="mrp_property_group_action" model="ir.actions.act_window"> - <field name="name">Property Groups</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.property.group</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to create a group of properties. - </p><p> - Define specific property groups that can be assigned to your - bill of materials and sales orders. Properties allows Odoo - to automatically select the right bill of materials according - to properties selected in the sales order by salesperson. - </p><p> - For instance, in the property group "Warranty", you an have - two properties: 1 year warranty, 3 years warranty. Depending - on the propoerties selected in the sales order, Odoo will - schedule a production using the matching bill of materials. - </p> - </field> - </record> - <menuitem id="menu_mrp_property_group_action" - action="mrp_property_group_action" - parent="menu_mrp_configuration" - groups="base.group_no_one,product.group_mrp_properties" - sequence="35"/> - - <!-- Property --> - <record id="mrp_property_tree_view" model="ir.ui.view"> - <field name="name">mrp.property.tree</field> - <field name="model">mrp.property</field> - <field name="arch" type="xml"> - <tree string="Properties"> - <field name="name"/> - <field name="composition"/> - <field name="group_id"/> - </tree> - </field> - </record> - <record id="mrp_property_form_view" model="ir.ui.view"> - <field name="name">mrp.property.form</field> - <field name="model">mrp.property</field> - <field name="arch" type="xml"> - <form string="Properties"> - <group col="4"> - <field colspan="4" name="name"/> - <field name="group_id"/> - <field name="composition"/> - </group> - <separator string="Description"/> - <field name="description" nolabel="1"/> - </form> - </field> - </record> - <record id="view_mrp_property_search" model="ir.ui.view"> - <field name="name">mrp.property.search</field> - <field name="model">mrp.property</field> - <field name="arch" type="xml"> - <search string="Search"> - <field name="name" string="Name"/> - <field name="composition"/> - <field name="group_id" /> - <group expand="0" string="Group By"> - <filter string="Property Group" domain="[]" context="{'group_by':'group_id'}"/> - </group> - </search> - </field> - </record> - <record id="mrp_property_action" model="ir.actions.act_window"> - <field name="name">Properties</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.property</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="search_view_id" ref="view_mrp_property_search"/> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to create a new property. - </p><p> - The Properties in Odoo are used to select the right bill of - materials for manufacturing a product when you have different - ways of building the same product. You can assign several - properties to each bill of materials. When a salesperson - creates a sales order, they can relate it to several properties - and Odoo will automatically select the BoM to use according - the needs. - </p> - </field> - </record> - <menuitem id="menu_mrp_property_action" - action="mrp_property_action" - groups="product.group_mrp_properties" - parent="menu_mrp_configuration" - sequence="30"/> - - <!-- Work Centers --> - <record id="mrp_workcenter_tree_view" model="ir.ui.view"> - <field name="name">mrp.workcenter.tree</field> - <field name="model">mrp.workcenter</field> - <field name="arch" type="xml"> - <tree string="Work Center"> - <field name="code"/> - <field name="name"/> - <field name="active" invisible="1"/> - <field name="resource_type"/> - <field name="company_id" groups="base.group_multi_company"/> - </tree> - </field> - </record> - <record id="mrp_workcenter_view" model="ir.ui.view"> - <field name="name">mrp.workcenter.form</field> - <field name="model">mrp.workcenter</field> - <field name="arch" type="xml"> - <form string="Work Center"> - <div class="oe_button_box" name="button_box"> - <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive"> - <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/> - </button> - </div> - <group col="4"> - <group> - <field name="name"/> - <field name="resource_type"/> - <field name="calendar_id" widget="selection"/> - </group> - <group> - <field name="code"/> - <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> - </group> - </group> - <notebook> - <page string="General Information"> - <group> - <group string="Capacity Information"> - <field name="time_efficiency"/> - <field name="capacity_per_cycle"/> - <field name="time_cycle" widget="float_time"/> - <field name="time_start" widget="float_time"/> - <field name="time_stop" widget="float_time"/> - </group> - <group string="Costing Information"> - <field name="product_id" on_change="on_change_product_cost(product_id)"/> - <field name="costs_hour"/> - <field name="costs_hour_account_id" groups="analytic.group_analytic_accounting"/> - <field name="costs_cycle"/> - <field name="costs_cycle_account_id" groups="analytic.group_analytic_accounting"/> - <field name="costs_general_account_id" attrs="{ 'required':['|',('costs_cycle_account_id', '=', True),('costs_hour_account_id', '=', True)]}" groups="analytic.group_analytic_accounting"/> - </group> - </group> - <separator string="Description"/> - <field name="note" nolabel="1"/> - </page> - </notebook> - </form> - </field> - </record> - <record id="view_mrp_workcenter_search" model="ir.ui.view"> - <field name="name">mrp.workcenter.search</field> - <field name="model">mrp.workcenter</field> - <field name="arch" type="xml"> - <search string="Search for mrp workcenter"> - <field name="name" string="Mrp Workcenter" filter_domain="['|',('name','ilike',self),('code','ilike',self)]"/> - <field name="resource_type"/> - <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> - <group expand="0" string="Group By..."> - <filter string="Type" domain="[]" context="{'group_by':'resource_type'}"/> - <filter string="Company" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> - </group> - </search> - </field> - </record> - - <!-- Routings Workcenter --> - <record id="mrp_routing_workcenter_tree_view" model="ir.ui.view"> - <field name="name">mrp.routing.workcenter.tree</field> - <field name="model">mrp.routing.workcenter</field> - <field name="arch" type="xml"> - <tree string="Routing Work Centers"> - <field name="sequence" widget="handle"/> - <field name="name"/> - <field name="workcenter_id"/> - <field name="cycle_nbr"/> - <field name="hour_nbr"/> - <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> - </tree> - </field> - </record> - <record id="mrp_routing_workcenter_form_view" model="ir.ui.view"> - <field name="name">mrp.routing.workcenter.form</field> - <field name="model">mrp.routing.workcenter</field> - <field name="arch" type="xml"> - <form string="Routing Work Centers"> - <sheet> - <group> - <group> - <field name="name"/> - <field name="workcenter_id"/> - <field name="sequence" groups="base.group_no_one"/> - </group><group> - <field name="cycle_nbr"/> - <field name="hour_nbr" widget="float_time"/> - <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> - </group> - </group> - <label for="note"/> - <field name="note"/> - </sheet> - </form> - </field> - </record> - <record id="mrp_workcenter_action" model="ir.actions.act_window"> - <field name="name">Work Centers</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.workcenter</field> - <field name="view_type">form</field> - <field name="view_id" ref="mrp_workcenter_tree_view"/> - <field name="search_view_id" ref="view_mrp_workcenter_search"/> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to add a work center. - </p><p> - Work Centers allow you to create and manage manufacturing - units. They consist of workers and/or machines, which are - considered as units for task assignation as well as capacity - and planning forecast. - </p> - </field> - </record> - <menuitem id="menu_view_resource_search_mrp" - action="mrp_workcenter_action" - groups="group_mrp_routings" - parent="menu_mrp_configuration" - sequence="40"/> - - <!-- Routings --> - <record id="mrp_routing_form_view" model="ir.ui.view"> - <field name="name">mrp.routing.form</field> - <field name="model">mrp.routing</field> - <field name="arch" type="xml"> - <form string="Routing"> - <sheet> - <div class="oe_button_box" name="button_box"> - <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive"> - <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/> - </button> - </div> - <group> - <group> - <field name="name"/> - <field name="code"/> - </group> - <group> - <field name="location_id"/> - <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> - </group> - </group> - <notebook> - <page string="Work Center Operations"> - <field name="workcenter_lines" string="Operation"/> - </page> - <page string="Notes"> - <field name="note"/> - </page> - </notebook> - </sheet> - </form> - </field> - </record> - <record id="mrp_routing_tree_view" model="ir.ui.view"> - <field name="name">mrp.routing.tree</field> - <field name="model">mrp.routing</field> - <field name="arch" type="xml"> - <tree string="Routing"> - <field name="code"/> - <field name="name"/> - <field name="active" invisible="1"/> - <field name="location_id"/> - <field name="company_id" groups="base.group_multi_company"/> - </tree> - </field> - </record> - <record id="mrp_routing_search_view" model="ir.ui.view"> - <field name="name">mrp.routing.search</field> - <field name="model">mrp.routing</field> - <field name="arch" type="xml"> - <search string="Routing"> - <field name="name" string="Routing" filter_domain="['|',('name','ilike',self),('code','ilike',self)]"/> - <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> - <field name="location_id"/> - <field name="company_id" groups="base.group_multi_company"/> - <group expand="0" string="Group By..."> - <filter string="Production Location" domain="[]" context="{'group_by':'location_id'}"/> - </group> - </search> - </field> - </record> - <record id="mrp_routing_action" model="ir.actions.act_window"> - <field name="name">Routings</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.routing</field> - <field name="view_type">form</field> - <field name="view_id" ref="mrp_routing_tree_view"/> - <field name="search_view_id" ref="mrp_routing_search_view"/> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to create a new routing. - </p><p> - Routings allow you to create and manage the manufacturing - operations that should be followed within your work centers in - order to produce a product. They are attached to bills of - materials that will define the required raw materials. - </p> - </field> - </record> - <menuitem id="menu_mrp_routing_action" - action="mrp_routing_action" - parent="menu_mrp_configuration" - groups="group_mrp_routings" - sequence="50"/> - - <!-- Bill of Materials --> - <record id="mrp_bom_form_view" model="ir.ui.view"> - <field name="name">mrp.bom.form</field> - <field name="model">mrp.bom</field> - <field name="arch" type="xml"> - <form string="Bill of Material"> - <sheet> - <div class="oe_button_box" name="button_box"> - <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive"> - <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/> - </button> - </div> - <group> - <group> - <field name="product_tmpl_id" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/> - <field name="product_id" groups="product.group_product_variant"/> - <label for="product_qty" string="Quantity"/> - <div> - <field name="product_qty" class="oe_inline" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" class="oe_inline" on_change="onchange_uom(product_tmpl_id, product_uom)" groups="product.group_uom"/> - </div> - <label for="routing_id" class="oe_inline" groups="mrp.group_mrp_routings"/> - <div groups="mrp.group_mrp_routings"> - <field name="routing_id" class="oe_inline"/> - </div> - </group> - <group> - <field name="code"/> - <field name="type" widget="radio"/> - <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> - </group> - </group> - <notebook> - <page string="Components"> - <field name="bom_line_ids" widget="one2many_list"> - <tree string="Components" editable="bottom"> - <field name="sequence" widget="handle"/> - <field name="product_id" on_change="onchange_product_id(product_id, product_qty)"/> - <field name="product_qty"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/> - <field name="product_rounding" groups="mrp.group_rounding_efficiency"/> - <field name="product_efficiency" groups="mrp.group_rounding_efficiency"/> - <field name="date_start"/> - <field name="date_stop"/> - <field name="attribute_value_ids" widget="many2many_tags" domain="[('product_ids.product_tmpl_id', '=', parent.product_tmpl_id)]" groups="product.group_product_variant"/> - <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> - </tree> - </field> - </page> - <page string="Properties" groups="base.group_no_one"> - <group> - <group> - <field name="position"/> - <field name="sequence"/> - </group> - <group> - <field name="date_start"/> - <field name="date_stop"/> - <field name="product_rounding"/> - <field name="product_efficiency"/> - </group> - </group> - <separator string="Properties" groups="product.group_mrp_properties"/> - <group> - <group> - <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> - </group> - <group/> - </group> - </page> - </notebook> - </sheet> - <div class="oe_chatter"> - <field name="message_follower_ids" widget="mail_followers"/> - <field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/> - </div> - </form> - </field> - </record> - <record id="mrp_bom_tree_view" model="ir.ui.view"> - <field name="name">mrp.bom.tree</field> - <field name="model">mrp.bom</field> - <field name="arch" type="xml"> - <tree string="Bill of Materials" default_order="product_tmpl_id"> - <field name="active" invisible="1"/> - <field name="product_tmpl_id"/> - <field name="code"/> - <field name="product_id" groups="product.group_product_variant"/> - <field name="product_qty"/> - <field name="product_uom" groups="product.group_uom"/> - </tree> - </field> - </record> - <record id="view_mrp_bom_filter" model="ir.ui.view"> - <field name="name">mrp.bom.select</field> - <field name="model">mrp.bom</field> - <field name="arch" type="xml"> - <search string="Search Bill Of Material"> - <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> - <field name="code"/> - <field name="bom_line_ids" string="Components"/> - <field name="product_tmpl_id" string="Product"/> - <field name="company_id" groups="base.group_multi_company"/> - <group expand="0" string="Group By..."> - <filter string="Product" domain="[]" context="{'group_by':'product_tmpl_id'}"/> - <filter string='Default Unit of Measure' domain="[]" context="{'group_by' : 'product_uom'}"/> - <filter string="Routings" domain="[]" context="{'group_by':'routing_id'}"/> - <filter string="Valid From Month" domain="[]" context="{'group_by':'date_start'}" help="Valid From Date by Month"/> - </group> - </search> - </field> - </record> - <record id="mrp_bom_form_action" model="ir.actions.act_window"> - <field name="name">Bill of Materials</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.bom</field> - <field name="domain">[]</field> <!-- force empty --> - <field name="view_type">form</field> - <field name="search_view_id" ref="view_mrp_bom_filter"/> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to create a bill of material. - </p><p> - Bills of Materials allow you to define the list of required raw - materials used to make a finished product; through a manufacturing - order or a pack of products. - </p><p> - Odoo uses these BoMs to automatically propose manufacturing - orders according to procurement needs. - </p> - </field> - </record> - <menuitem id="menu_mrp_bom_form_action" - action="mrp_bom_form_action" - parent="menu_mrp_bom" - sequence="99"/> - - <!-- BOM Line --> - <record id="mrp_bom_component_tree_view" model="ir.ui.view"> - <field name="name">mrp.bom.component.tree</field> - <field name="model">mrp.bom.line</field> - <field name="arch" type="xml"> - <tree string="Components" editable="top"> - <field name="sequence" widget="handle"/> - <field name="bom_id"/> - <field name="product_id" on_change="onchange_product_id(product_id)"/> - <field name="product_qty"/> - <field name="product_rounding"/> - <field name="product_efficiency"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/> - <field name="date_start"/> - <field name="date_stop"/> - <field name="routing_id"/> - <field name="attribute_value_ids" widget="many2many_tags" groups="product.group_product_variant"/> - <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> - </tree> - </field> - </record> - <record id="mrp_bom_line_tree_view" model="ir.ui.view"> - <field name="name">mrp.bom.tree</field> - <field name="model">mrp.bom.line</field> - <field name="field_parent">child_line_ids</field> - <field name="arch" type="xml"> - <tree string="Bill of Materials"> - <field name="sequence" invisible="1"/> - <field name="product_id"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> - <field name="routing_id" groups="mrp.group_mrp_routings"/> - <field name="date_start"/> - <field name="date_stop"/> - </tree> - </field> - </record> - <record id="view_mrp_bom_line_filter" model="ir.ui.view"> - <field name="name">mrp.bom.line.select</field> - <field name="model">mrp.bom.line</field> - <field name="arch" type="xml"> - <search string="Search Bill Of Material Components"> - <field name="bom_id"/> - <field name="product_id"/> - <group expand="0" string="Group By..."> - <filter string="Bill Of Material" domain="[]" context="{'group_by':'bom_id'}"/> - <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/> - <filter string='Default Unit of Measure' domain="[]" context="{'group_by' : 'product_uom'}"/> - <filter string="Routings" domain="[]" context="{'group_by':'routing_id'}"/> - <filter string=" Valid From Month" domain="[]" context="{'group_by':'date_start'}" help="Valid From Date by Month"/> - </group> - </search> - </field> - </record> - <record id="action2" model="ir.actions.act_window"> - <field name="name">Bill of Materials Structure</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.bom.line</field> - <field name="domain">[('bom_id', 'in', active_ids)]</field> - <field name="view_type">tree</field> - <field name="view_id" ref="mrp_bom_line_tree_view"/> - <field name="view_type">tree</field> - </record> - <record id="ir_BOM_structure" model="ir.values"> - <field eval="'client_action_multi'" name="key2"/> - <field eval="'mrp.bom'" name="model"/> - <field name="name">Bill of Materials Structure</field> - <field eval="'ir.actions.act_window,'+str(action2)" name="value"/> - </record> - - <!-- Production Order --> - <record id="mrp_production_form_view" model="ir.ui.view"> - <field name="name">mrp.production.form</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <form string="Manufacturing Orders"> - <header> - <button name="button_confirm" states="draft" string="Confirm Production" class="oe_highlight"/> - <button name="%(act_mrp_product_produce)d" states="ready,in_production" string="Produce" type="action" class="oe_highlight"/> - <button name="action_assign" states="confirmed,picking_except" string="Reserve" type="object" class="oe_highlight"/> - <button name="force_production" states="confirmed" string="Force Reservation" type="object"/> - <button name="button_produce" states="ready" string="Mark as Started"/> - <button name="button_cancel" states="draft,ready,in_production" string="Cancel Production"/> - <button name="action_cancel" type="object" states="confirmed" string="Cancel Production"/> - <field name="state" widget="statusbar" statusbar_visible="draft,ready,in_production,done" statusbar_colors='{"confirmed":"blue"}'/> - </header> - <sheet> - <div class="oe_title"> - <label for="name" class="oe_edit_only"/> - <h1><field name="name" placeholder="Manufacturing Reference"/></h1> - </div> - <group> - <group> - <field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.type','!=','phantom')]" context='{"default_type": "product"}'/> - <field name="product_tmpl_id" invisible="1"/> - <label for="product_qty"/> - <div class="o_row"> - <field name="product_qty" on_change="product_id_change(product_id, product_qty)"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> - <button type="action" - name="%(mrp.action_change_production_qty)d" - string="Update" states="confirmed" class="oe_edit_only oe_link"/> - </div> - <field name="date_planned"/> - </group> - <group> - <field name="bom_id" - domain="['|', - ('product_id','=',product_id), - '&', - ('product_tmpl_id.product_variant_ids','=',product_id), - ('product_id','=',False)]" - context="{'default_product_id': product_id, 'default_product_tmpl_id': product_tmpl_id}" on_change="bom_id_change(bom_id)" required="1"/> - <field name="routing_id" groups="mrp.group_mrp_routings"/> - <field name="user_id"/> - <field name="origin"/> - </group> - <group groups="stock.group_stock_multi_locations"> - <field name="location_src_id" domain="[('usage','=','internal')]" on_change="location_id_change(location_src_id,location_dest_id)"/> - <field name="location_dest_id" domain="[('usage','=','internal')]"/> - </group> - </group> - <notebook> - <page string="Consumed Products"> - <group> - <group string="Products to Consume"> - <field name="move_lines" nolabel="1" options="{'reload_on_button': true}"> - <tree decoration-info="state == 'draft'" decoration-muted="state in ('cancel','done')" decoration-danger="state in ('confirmed','waiting')" string="Products to Consume"> - <field name="product_id"/> - <field name="product_uom_qty" string="Quantity"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" string="Unit of Measure" groups="product.group_uom"/> - <field name="state" invisible="1"/> - <button name="%(mrp.move_consume)d" - string="Consume Products" type="action" - icon="fa-arrow-right text-success" context="{'consume': True}" - states="assigned" - /> - <button name="%(stock.move_scrap)d" - string="Scrap Products" type="action" - icon="fa-sign-in text-warning" context="{'scrap': True}" - states="draft,waiting,confirmed,assigned"/> - </tree> - </field> - </group> - <group string="Consumed Products"> - <field name="move_lines2" nolabel="1" options="{'reload_on_button': true}"> - <tree decoration-danger="scrapped==True" decoration-info="state == 'draft'" decoration-muted="state == 'cancel'" string="Consumed Products" editable="bottom"> - <field name="product_id" readonly="1"/> - <field name="restrict_lot_id" context="{'product_id': product_id}" groups="stock.group_production_lot"/> - <field name="product_uom_qty" readonly="1"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" readonly="1" string="Unit of Measure" groups="product.group_uom"/> - <field name="state" invisible="1"/> - <field name="scrapped" invisible="1"/> - </tree> - </field> - </group> - </group> - </page> - <page string="Finished Products"> - <group> - <group string="Products to Produce"> - <field name="move_created_ids" nolabel="1" options="{'reload_on_button': true}"> - <tree string="Products to Finish"> - <field name="product_id"/> - <field name="product_uom_qty"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" string="Unit of Measure" groups="product.group_uom"/> - <field name="state" invisible="1"/> - </tree> - </field> - </group> - <group string="Produced Products"> - <field name="move_created_ids2" nolabel="1" options="{'reload_on_button': true}"> - <tree decoration-danger="scrapped==True" decoration-info="state == 'draft'" decoration-muted="state == 'cancel'" string="Finished Products"> - <field name="product_id" readonly="1"/> - <field name="product_uom_qty" readonly="1"/> - <field name="restrict_lot_id" groups="stock.group_production_lot"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" readonly="1" string="Unit of Measure" groups="product.group_uom"/> - <field name="location_dest_id" readonly="1" string="Destination Loc." widget="selection" groups="stock.group_stock_multi_locations"/> - <field name="scrapped" invisible="1"/> - <field name="state" invisible="1"/> - <button name="%(stock.move_scrap)d" - string="Scrap Products" type="action" icon="fa-sign-in text-warning" - states="done,cancel"/> - </tree> - </field> - </group> - </group> - </page> - <page string="Work Orders" groups="mrp.group_mrp_routings"> - <button name="action_compute" states="draft" - string="Compute Data" type="object" - icon="fa-calculator"/> - <field name="workcenter_lines" options="{'reload_on_button': true}"> - <form string="Production Work Centers"> - <group col="4"> - <field colspan="4" name="name"/> - <field name="workcenter_id"/> - <field name="sequence"/> - <field name="cycle"/> - <field name="hour" widget="float_time"/> - </group> - </form> - <tree string="Production Work Centers"> - <field name="sequence"/> - <field name="name"/> - <field name="workcenter_id"/> - <field name="cycle"/> - <field name="hour" widget="float_time"/> - </tree> - </field> - </page> - <page string="Scheduled Products"> - <button name="action_compute" states="draft" - string="Compute Data" type="object" - icon="fa-calculator"/> - <field name="product_lines"/> - </page> - <page string="Extra Information"> - <group> - <group> - <field name="priority"/> - <field name="date_start" invisible="1"/> - <field name="date_finished" invisible="1"/> - </group> - <group> - <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> - <field name="move_prod_id" groups="base.group_no_one"/> - </group> - </group> - </page> - </notebook> - </sheet> - <div class="oe_chatter"> - <field name="message_follower_ids" widget="mail_followers"/> - <field name="message_ids" widget="mail_thread"/> - </div> - </form> - </field> - </record> - <record id="mrp_production_tree_view" model="ir.ui.view"> - <field name="name">mrp.production.tree</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <tree decoration-bf="message_needaction==True" decoration-info="state in ('draft','confirmed')" decoration-danger="date_planned<current_date and state not in ('done','cancel')" decoration-muted="state in ('done','cancel')" string="Manufacturing Orders"> - <field name="message_needaction" invisible="1"/> - <field name="name"/> - <field name="date_planned"/> - <field name="product_id"/> - <field name="product_qty" sum="Total Qty"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> - <field name="routing_id" groups="mrp.group_mrp_routings"/> - <field name="hour_total" sum="Total Hours"/> - <field name="cycle_total" sum="Total Cycles"/> - <field name="origin"/> - <field name="state"/> - </tree> - </field> - </record> - <record id="mrp_production_kanban_view" model="ir.ui.view"> - <field name="name">mrp.production.kanban</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <kanban class="o_kanban_mobile"> - <field name="name"/> - <field name="product_id"/> - <field name="product_qty"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}"/> - <field name="date_planned"/> - <field name="state"/> - <templates> - <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_card oe_kanban_global_click"> - <div class="row"> - <div class="col-xs-6"> - <strong><span><t t-esc="record.product_id.value"/></span></strong> - </div> - <div class="col-xs-6"> - <strong><span class="pull-right text-right"><t t-esc="record.product_qty.value"/> <t t-esc="record.product_uom.value"/></span></strong> - </div> - </div> - <div class="row"> - <div class="col-xs-6 text-muted"> - <span><t t-esc="record.name.value"/> <t t-esc="record.date_planned.value and record.date_planned.value.split(' ')[0] or False"/></span> - </div> - <div class="col-xs-6"> - <span class="pull-right text-right"> - <field name="state" widget="kanban_label_selection" options="{'classes': {'draft': 'default', 'cancel': 'default', 'none': 'danger', 'confirmed': 'warning', - 'done': 'success'}}"/> - </span> - </div> - </div> - </div> - </t> - </templates> - </kanban> - </field> - </record> - <record id="view_production_calendar" model="ir.ui.view"> - <field name="name">mrp.production.calendar</field> - <field name="model">mrp.production</field> - <field eval="2" name="priority"/> - <field name="arch" type="xml"> - <calendar color="routing_id" date_start="date_planned" string="Manufacturing Orders"> - <field name="name"/> - <field name="product_id"/> - <field name="product_qty"/> - </calendar> - </field> - </record> - <record id="view_production_gantt" model="ir.ui.view"> - <field name="name">mrp.production.gantt</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <gantt date_stop="date_finished" date_start="date_start" progress="progress" string="Productions" default_group_by="routing_id"> - </gantt> - </field> - </record> - <record id="view_production_pivot" model="ir.ui.view"> - <field name="name">mrp.production.pivot</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <pivot string="Manufacturing Orders"> - <field name="date_planned" type="row"/> - <field name="hour_total" type="measure"/> - <field name="cycle_total" type="measure"/> - </pivot> - </field> - </record> - <record id="view_production_graph" model="ir.ui.view"> - <field name="name">mrp.production.graph</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <graph string="Manufacturing Orders"> - <field name="date_planned"/> - <field name="hour_total" type="measure"/> - <field name="cycle_total" type="measure"/> - </graph> - </field> - </record> - <record id="view_mrp_production_filter" model="ir.ui.view"> - <field name="name">mrp.production.select</field> - <field name="model">mrp.production</field> - <field name="arch" type="xml"> - <search string="Search Production"> - <field name="name" string="Production" filter_domain="['|',('name','ilike',self),('origin','ilike',self)]"/> - <filter string="Pending" name="pending" domain="[('state','=','confirmed')]" - help="Manufacturing Orders which are waiting for raw materials."/> - <filter string="Ready" name="ready" domain="[('state','=','ready')]" - help="Manufacturing Orders which are ready to start production."/> - <filter string="In Production" name="inprogress" domain="[('state','=','in_production')]" - help="Manufacturing Orders which are currently in production."/> - <separator/> - <filter string="Late" domain="['&', ('date_planned','<', current_date), ('state', 'in', ('draft', 'confirmed', 'ready'))]" - help="Production started late"/> - <field name="product_tmpl_id"/> - <field name="product_id"/> - <field name="routing_id" groups="mrp.group_mrp_routings"/> - <group expand="0" string="Group By..."> - <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/> - <filter string="Routing" domain="[]" context="{'group_by':'routing_id'}"/> - <filter string="Status" domain="[]" context="{'group_by':'state'}"/> - <filter string="Scheduled Month" domain="[]" context="{'group_by':'date_planned'}" help="Scheduled Date by Month"/> - </group> - </search> - </field> - </record> - <record id="mrp_production_action" model="ir.actions.act_window"> - <field name="name">Manufacturing Orders</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.production</field> - <field name="view_type">form</field> - <field name="view_mode">tree,kanban,form,calendar,pivot,graph,gantt</field> - <field name="view_id" eval="False"/> - <field name="search_view_id" ref="view_mrp_production_filter"/> - <field name="context">{}</field> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to create a manufacturing order. - </p><p> - A manufacturing order, based on a bill of materials, will - consume raw materials and produce finished products. - </p><p> - Manufacturing orders are usually proposed automatically based - on customer requirements or automated rules like the minimum - stock rule. - </p> - </field> - </record> - <menuitem action="mrp_production_action" - id="menu_mrp_production_action" - parent="menu_mrp_manufacturing" - sequence="1"/> - - <record id="mrp_production_action_planning" model="ir.actions.act_window"> - <field name="name">Manufacturing Orders</field> - <field name="type">ir.actions.act_window</field> - <field name="res_model">mrp.production</field> - <field name="view_type">form</field> - <field name="view_mode">calendar,tree,form,pivot,graph</field> - <field name="domain">[('state','in',('ready','confirmed','in_production'))]</field> - <field name="help" type="html"> - <p class="oe_view_nocontent_create"> - Click to start a new manufacturing order. - </p><p> - A manufacturing order, based on a bill of materials, will - consume raw materials and produce finished products. - </p><p> - Manufacturing orders are usually proposed automatically based - on customer requirements or automated rules like the minimum - stock rule. - </p> - </field> - </record> - <menuitem action="mrp.mrp_production_action_planning" - id="menu_mrp_production_order_action" name="Order Planning" - parent="menu_mrp_planning" sequence="1"/> - - <!-- Production Order Line --> - <record id="mrp_production_product_form_view" model="ir.ui.view"> - <field name="name">mrp.production.product.line.form</field> - <field name="model">mrp.production.product.line</field> - <field name="arch" type="xml"> - <form string="Scheduled Products"> - <group col="4"> - <field name="name"/> - <field name="product_id"/> - <field name="product_qty"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> - </group> - </form> - </field> - </record> - <record id="mrp_production_product_tree_view" model="ir.ui.view"> - <field name="name">mrp.production.product.line.tree</field> - <field name="model">mrp.production.product.line</field> - <field name="arch" type="xml"> - <tree string="Scheduled Products"> - <field name="product_id"/> - <field name="product_qty"/> - <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> - </tree> - </field> - </record> - - <!-- Procurements --> - <record id="view_procurement_rule_form_stock_inherit_mrp" model="ir.ui.view"> - <field name="name">procurement.rule.form.stock.inherit.mrp</field> - <field name="model">procurement.rule</field> - <field name="inherit_id" ref="stock.view_procurement_rule_form_stock_inherit"/> - <field name="arch" type="xml"> - <field name="location_src_id" position="attributes"> - <attribute name="attrs">{'required': [('action', '=', 'move')], 'invisible': [('action', 'not in', ['move', 'manufacture'])]}</attribute> - </field> - </field> - </record> - - <record id="view_procurement_form_inherit" model="ir.ui.view"> - <field name="name">procurement.order.form.inherit</field> - <field name="model">procurement.order</field> - <field name="inherit_id" ref="procurement.procurement_form_view"/> - <field name="arch" type="xml"> - <data> - <field name="origin" position="before"> - <field name="bom_id" domain="[('product_id','=',product_id)]"/> - <field name="production_id" attrs="{'invisible': [('production_id','=',False)]}"/> - </field> - <field name="origin" position="after"> - <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> - </field> - </data> - </field> - </record> - - <!-- Product Template --> - <record id="view_mrp_product_template_form_inherited" model="ir.ui.view"> - <field name="name">product.form.mrp.inherited</field> - <field name="model">product.template</field> - <field name="inherit_id" ref="stock.view_template_property_form"/> - <field name="arch" type="xml"> - <group name="sale_condition" position="inside"> - <label for="produce_delay" attrs="{'invisible':[('type','=','service')]}"/> - <div attrs="{'invisible':[('type','=','service')]}"> - <field name="produce_delay" class="oe_inline"/> days - </div> - </group> - </field> - </record> - <record id="product_template_search_view_procurment" model="ir.ui.view"> - <field name="name">product.template.search.bom</field> - <field name="model">product.template</field> - <field name="inherit_id" ref="product.product_template_search_view"/> - <field name="arch" type="xml"> - <xpath expr="//filter[@name='consumable']" position="after"> - <separator/> - <filter string="Components" name="components" domain="[('bom_ids','not in',[])]"/> - </xpath> - </field> - </record> - <record id="product_template_action" model="ir.actions.act_window"> - <field name="name">Products</field> - <field name="res_model">product.template</field> - <field name="view_type">form</field> - <field name="view_mode">kanban,tree,form</field> - <field name="context">{}</field> - </record> - <menuitem id="menu_mrp_product_form" - name="Products" - action="product_template_action" - parent="menu_mrp_bom"/> - - <record id="template_open_bom" model="ir.actions.act_window"> - <field name="context">{'default_product_tmpl_id': active_id, 'search_default_product_tmpl_id': active_id}</field> - <field name="name">Bill of Materials</field> - <field name="res_model">mrp.bom</field> - <field name="view_type">form</field> - </record> - <record id="product_template_form_view_bom_button" model="ir.ui.view"> - <field name="name">product.template.procurement</field> - <field name="model">product.template</field> - <field name="inherit_id" ref="product.product_template_only_form_view"/> - <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/> - <field name="arch" type="xml"> - <div name="button_box" position="inside"> - <button class="oe_stat_button" name="%(template_open_bom)d" type="action" - attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-flask"> - <field string="Bill of Materials" name="bom_count" widget="statinfo" /> - </button> - <button class="oe_stat_button" name="action_view_mos" type="object" - attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-list-alt"> - <field string="Manufacturing" name="mo_count" widget="statinfo" /> - </button> - </div> - </field> - </record> - - <!-- Product Product --> - <record id="product_open_bom" model="ir.actions.act_window"> - <field name="context">{'default_product_id': active_id, 'search_default_product_id': active_id}</field> - <field name="name">Bill of Materials</field> - <field name="res_model">mrp.bom</field> - <field name="domain">[]</field> <!-- Force empty --> - <field name="view_type">form</field> - </record> - <record id="act_product_mrp_production" model="ir.actions.act_window"> - <field name="context">{'search_default_product_id': [active_id]}</field> - <field name="name">Manufacturing Orders</field> - <field name="res_model">mrp.production</field> - <field name="view_id" ref="mrp_production_tree_view"/> - </record> - <record id="product_product_form_view_bom_button" model="ir.ui.view"> - <field name="name">product.product.procurement</field> - <field name="model">product.product</field> - <field name="inherit_id" ref="product.product_normal_form_view"/> - <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/> - <field name="arch" type="xml"> - <div name="button_box" position="inside"> - <button class="oe_stat_button" name="action_view_bom" type="object" - attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-flask"> - <field string="Bill of Materials" name="bom_count" widget="statinfo" /> - </button> - <button class="oe_stat_button" name="%(act_product_mrp_production)d" type="action" - attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-list-alt"> - <field string="Manufacturing" name="mo_count" widget="statinfo" /> - </button> - </div> - </field> - </record> - - <!-- Warehouse --> - <record id="view_warehouse_inherited" model="ir.ui.view"> - <field name="name">Stock Warehouse Inherited</field> - <field name="model">stock.warehouse</field> - <field name="inherit_id" ref="stock.view_warehouse"/> - <field name="arch" type="xml"> - <xpath expr="//field[@name='default_resupply_wh_id']" position="before"> - <field name="manufacture_to_resupply" /> - </xpath> - </field> - </record> - - </data> -</openerp> diff --git a/addons/mrp/report/mrp_report_view.xml b/addons/mrp/report/mrp_report_views.xml index 8f1641967a8..8f1641967a8 100644 --- a/addons/mrp/report/mrp_report_view.xml +++ b/addons/mrp/report/mrp_report_views.xml diff --git a/addons/mrp/mrp_report.xml b/addons/mrp/report/mrp_report_views_main.xml index 903749cf1a3..903749cf1a3 100644 --- a/addons/mrp/mrp_report.xml +++ b/addons/mrp/report/mrp_report_views_main.xml diff --git a/addons/mrp/views/mrp_bom_views.xml b/addons/mrp/views/mrp_bom_views.xml new file mode 100644 index 00000000000..ee9872bfd51 --- /dev/null +++ b/addons/mrp/views/mrp_bom_views.xml @@ -0,0 +1,225 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Bill of Materials --> + <record id="mrp_bom_form_view" model="ir.ui.view"> + <field name="name">mrp.bom.form</field> + <field name="model">mrp.bom</field> + <field name="arch" type="xml"> + <form string="Bill of Material"> + <sheet> + <div class="oe_button_box" name="button_box"> + <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive"> + <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/> + </button> + </div> + <group> + <group> + <field name="product_tmpl_id" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/> + <field name="product_id" groups="product.group_product_variant"/> + <label for="product_qty" string="Quantity"/> + <div> + <field name="product_qty" class="oe_inline" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" class="oe_inline" on_change="onchange_uom(product_tmpl_id, product_uom)" groups="product.group_uom"/> + </div> + <label for="routing_id" class="oe_inline" groups="mrp.group_mrp_routings"/> + <div groups="mrp.group_mrp_routings"> + <field name="routing_id" class="oe_inline"/> + </div> + </group> + <group> + <field name="code"/> + <field name="type" widget="radio"/> + <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> + </group> + </group> + <notebook> + <page string="Components"> + <field name="bom_line_ids" widget="one2many_list"> + <tree string="Components" editable="bottom"> + <field name="sequence" widget="handle"/> + <field name="product_id" on_change="onchange_product_id(product_id, product_qty)"/> + <field name="product_qty"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/> + <field name="product_rounding" groups="mrp.group_rounding_efficiency"/> + <field name="product_efficiency" groups="mrp.group_rounding_efficiency"/> + <field name="date_start"/> + <field name="date_stop"/> + <field name="attribute_value_ids" widget="many2many_tags" domain="[('product_ids.product_tmpl_id', '=', parent.product_tmpl_id)]" groups="product.group_product_variant"/> + <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> + </tree> + </field> + </page> + <page string="Properties" groups="base.group_no_one"> + <group> + <group> + <field name="position"/> + <field name="sequence"/> + </group> + <group> + <field name="date_start"/> + <field name="date_stop"/> + <field name="product_rounding"/> + <field name="product_efficiency"/> + </group> + </group> + <separator string="Properties" groups="product.group_mrp_properties"/> + <group> + <group> + <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> + </group> + <group/> + </group> + </page> + </notebook> + </sheet> + <div class="oe_chatter"> + <field name="message_follower_ids" widget="mail_followers"/> + <field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/> + </div> + </form> + </field> + </record> + <record id="mrp_bom_tree_view" model="ir.ui.view"> + <field name="name">mrp.bom.tree</field> + <field name="model">mrp.bom</field> + <field name="arch" type="xml"> + <tree string="Bill of Materials" default_order="product_tmpl_id"> + <field name="active" invisible="1"/> + <field name="product_tmpl_id"/> + <field name="code"/> + <field name="product_id" groups="product.group_product_variant"/> + <field name="product_qty"/> + <field name="product_uom" groups="product.group_uom"/> + </tree> + </field> + </record> + <record id="view_mrp_bom_filter" model="ir.ui.view"> + <field name="name">mrp.bom.select</field> + <field name="model">mrp.bom</field> + <field name="arch" type="xml"> + <search string="Search Bill Of Material"> + <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> + <field name="code"/> + <field name="bom_line_ids" string="Components"/> + <field name="product_tmpl_id" string="Product"/> + <field name="company_id" groups="base.group_multi_company"/> + <group expand="0" string="Group By..."> + <filter string="Product" domain="[]" context="{'group_by':'product_tmpl_id'}"/> + <filter string='Default Unit of Measure' domain="[]" context="{'group_by' : 'product_uom'}"/> + <filter string="Routings" domain="[]" context="{'group_by':'routing_id'}"/> + <filter string="Valid From Month" domain="[]" context="{'group_by':'date_start'}" help="Valid From Date by Month"/> + </group> + </search> + </field> + </record> + <record id="mrp_bom_form_action" model="ir.actions.act_window"> + <field name="name">Bill of Materials</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.bom</field> + <field name="domain">[]</field> <!-- force empty --> + <field name="view_type">form</field> + <field name="search_view_id" ref="view_mrp_bom_filter"/> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a bill of material. + </p><p> + Bills of Materials allow you to define the list of required raw + materials used to make a finished product; through a manufacturing + order or a pack of products. + </p><p> + Odoo uses these BoMs to automatically propose manufacturing + orders according to procurement needs. + </p> + </field> + </record> + <menuitem id="menu_mrp_bom_form_action" + action="mrp_bom_form_action" + parent="menu_mrp_bom" + sequence="99"/> + + <!-- BOM Line --> + <record id="mrp_bom_component_tree_view" model="ir.ui.view"> + <field name="name">mrp.bom.component.tree</field> + <field name="model">mrp.bom.line</field> + <field name="arch" type="xml"> + <tree string="Components" editable="top"> + <field name="sequence" widget="handle"/> + <field name="bom_id"/> + <field name="product_id" on_change="onchange_product_id(product_id)"/> + <field name="product_qty"/> + <field name="product_rounding"/> + <field name="product_efficiency"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/> + <field name="date_start"/> + <field name="date_stop"/> + <field name="routing_id"/> + <field name="attribute_value_ids" widget="many2many_tags" groups="product.group_product_variant"/> + <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> + </tree> + </field> + </record> + <record id="mrp_bom_line_tree_view" model="ir.ui.view"> + <field name="name">mrp.bom.tree</field> + <field name="model">mrp.bom.line</field> + <field name="field_parent">child_line_ids</field> + <field name="arch" type="xml"> + <tree string="Bill of Materials"> + <field name="sequence" invisible="1"/> + <field name="product_id"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> + <field name="routing_id" groups="mrp.group_mrp_routings"/> + <field name="date_start"/> + <field name="date_stop"/> + </tree> + </field> + </record> + <record id="view_mrp_bom_line_filter" model="ir.ui.view"> + <field name="name">mrp.bom.line.select</field> + <field name="model">mrp.bom.line</field> + <field name="arch" type="xml"> + <search string="Search Bill Of Material Components"> + <field name="bom_id"/> + <field name="product_id"/> + <group expand="0" string="Group By..."> + <filter string="Bill Of Material" domain="[]" context="{'group_by':'bom_id'}"/> + <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/> + <filter string='Default Unit of Measure' domain="[]" context="{'group_by' : 'product_uom'}"/> + <filter string="Routings" domain="[]" context="{'group_by':'routing_id'}"/> + <filter string=" Valid From Month" domain="[]" context="{'group_by':'date_start'}" help="Valid From Date by Month"/> + </group> + </search> + </field> + </record> + <record id="action2" model="ir.actions.act_window"> + <field name="name">Bill of Materials Structure</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.bom.line</field> + <field name="domain">[('bom_id', 'in', active_ids)]</field> + <field name="view_type">tree</field> + <field name="view_id" ref="mrp_bom_line_tree_view"/> + <field name="view_type">tree</field> + </record> + <record id="ir_BOM_structure" model="ir.values"> + <field eval="'client_action_multi'" name="key2"/> + <field eval="'mrp.bom'" name="model"/> + <field name="name">Bill of Materials Structure</field> + <field eval="'ir.actions.act_window,'+str(action2)" name="value"/> + </record> + + <record id="template_open_bom" model="ir.actions.act_window"> + <field name="context">{'default_product_tmpl_id': active_id, 'search_default_product_tmpl_id': active_id}</field> + <field name="name">Bill of Materials</field> + <field name="res_model">mrp.bom</field> + <field name="view_type">form</field> + </record> + + <record id="product_open_bom" model="ir.actions.act_window"> + <field name="context">{'default_product_id': active_id, 'search_default_product_id': active_id}</field> + <field name="name">Bill of Materials</field> + <field name="res_model">mrp.bom</field> + <field name="domain">[]</field> <!-- Force empty --> + <field name="view_type">form</field> + </record> + </data> +</openerp>
\ No newline at end of file diff --git a/addons/mrp/res_config_view.xml b/addons/mrp/views/mrp_config_settings_views.xml index 011a14dcb4a..011a14dcb4a 100644 --- a/addons/mrp/res_config_view.xml +++ b/addons/mrp/views/mrp_config_settings_views.xml diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml new file mode 100644 index 00000000000..2e780d03c0e --- /dev/null +++ b/addons/mrp/views/mrp_production_views.xml @@ -0,0 +1,384 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record id="mrp_production_form_view" model="ir.ui.view"> + <field name="name">mrp.production.form</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <form string="Manufacturing Orders"> + <header> + <button name="button_confirm" states="draft" string="Confirm Production" class="oe_highlight"/> + <button name="%(act_mrp_product_produce)d" states="ready,in_production" string="Produce" type="action" class="oe_highlight"/> + <button name="action_assign" states="confirmed,picking_except" string="Reserve" type="object" class="oe_highlight"/> + <button name="force_production" states="confirmed" string="Force Reservation" type="object"/> + <button name="button_produce" states="ready" string="Mark as Started"/> + <button name="button_cancel" states="draft,ready,in_production" string="Cancel Production"/> + <button name="action_cancel" type="object" states="confirmed" string="Cancel Production"/> + <field name="state" widget="statusbar" statusbar_visible="draft,ready,in_production,done" statusbar_colors='{"confirmed":"blue"}'/> + </header> + <sheet> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/> + <h1><field name="name" placeholder="Manufacturing Reference"/></h1> + </div> + <group> + <group> + <field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.type','!=','phantom')]" context='{"default_type": "product"}'/> + <field name="product_tmpl_id" invisible="1"/> + <label for="product_qty"/> + <div class="o_row"> + <field name="product_qty" on_change="product_id_change(product_id, product_qty)"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> + <button type="action" + name="%(mrp.action_change_production_qty)d" + string="Update" states="confirmed" class="oe_edit_only oe_link"/> + </div> + <field name="date_planned"/> + </group> + <group> + <field name="bom_id" + domain="['|', + ('product_id','=',product_id), + '&', + ('product_tmpl_id.product_variant_ids','=',product_id), + ('product_id','=',False)]" + context="{'default_product_id': product_id, 'default_product_tmpl_id': product_tmpl_id}" on_change="bom_id_change(bom_id)" required="1"/> + <field name="routing_id" groups="mrp.group_mrp_routings"/> + <field name="user_id"/> + <field name="origin"/> + </group> + <group groups="stock.group_stock_multi_locations"> + <field name="location_src_id" domain="[('usage','=','internal')]" on_change="location_id_change(location_src_id,location_dest_id)"/> + <field name="location_dest_id" domain="[('usage','=','internal')]"/> + </group> + </group> + <notebook> + <page string="Consumed Products"> + <group> + <group string="Products to Consume"> + <field name="move_lines" nolabel="1" options="{'reload_on_button': true}"> + <tree decoration-info="state == 'draft'" decoration-muted="state in ('cancel','done')" decoration-danger="state in ('confirmed','waiting')" string="Products to Consume"> + <field name="product_id"/> + <field name="product_uom_qty" string="Quantity"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" string="Unit of Measure" groups="product.group_uom"/> + <field name="state" invisible="1"/> + <button name="%(mrp.move_consume)d" + string="Consume Products" type="action" + icon="gtk-go-forward" context="{'consume': True}" + states="assigned" + /> + <button name="%(stock.move_scrap)d" + string="Scrap Products" type="action" + icon="terp-gtk-jump-to-ltr" context="{'scrap': True}" + states="draft,waiting,confirmed,assigned"/> + </tree> + </field> + </group> + <group string="Consumed Products"> + <field name="move_lines2" nolabel="1" options="{'reload_on_button': true}"> + <tree decoration-danger="scrapped==True" decoration-info="state == 'draft'" decoration-muted="state == 'cancel'" string="Consumed Products" editable="bottom"> + <field name="product_id" readonly="1"/> + <field name="restrict_lot_id" context="{'product_id': product_id}" groups="stock.group_production_lot"/> + <field name="product_uom_qty" readonly="1"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" readonly="1" string="Unit of Measure" groups="product.group_uom"/> + <field name="state" invisible="1"/> + <field name="scrapped" invisible="1"/> + </tree> + </field> + </group> + </group> + </page> + <page string="Finished Products"> + <group> + <group string="Products to Produce"> + <field name="move_created_ids" nolabel="1" options="{'reload_on_button': true}"> + <tree string="Products to Finish"> + <field name="product_id"/> + <field name="product_uom_qty"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" string="Unit of Measure" groups="product.group_uom"/> + <field name="state" invisible="1"/> + </tree> + </field> + </group> + <group string="Produced Products"> + <field name="move_created_ids2" nolabel="1" options="{'reload_on_button': true}"> + <tree decoration-danger="scrapped==True" decoration-info="state == 'draft'" decoration-muted="state == 'cancel'" string="Finished Products"> + <field name="product_id" readonly="1"/> + <field name="product_uom_qty" readonly="1"/> + <field name="restrict_lot_id" groups="stock.group_production_lot"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" readonly="1" string="Unit of Measure" groups="product.group_uom"/> + <field name="location_dest_id" readonly="1" string="Destination Loc." widget="selection" groups="stock.group_stock_multi_locations"/> + <field name="scrapped" invisible="1"/> + <field name="state" invisible="1"/> + <button name="%(stock.move_scrap)d" + string="Scrap Products" type="action" icon="terp-gtk-jump-to-ltr" + states="done,cancel"/> + </tree> + </field> + </group> + </group> + </page> + <page string="Work Orders" groups="mrp.group_mrp_routings"> + <button name="action_compute" states="draft" + string="Compute Data" type="object" + icon="terp-stock_format-scientific"/> + <field name="workcenter_lines" options="{'reload_on_button': true}"> + <form string="Production Work Centers"> + <group col="4"> + <field colspan="4" name="name"/> + <field name="workcenter_id"/> + <field name="sequence"/> + <field name="cycle"/> + <field name="hour" widget="float_time"/> + </group> + </form> + <tree string="Production Work Centers"> + <field name="sequence"/> + <field name="name"/> + <field name="workcenter_id"/> + <field name="cycle"/> + <field name="hour" widget="float_time"/> + </tree> + </field> + </page> + <page string="Scheduled Products"> + <button name="action_compute" states="draft" + string="Compute Data" type="object" + icon="terp-stock_format-scientific"/> + <field name="product_lines"/> + </page> + <page string="Extra Information"> + <group> + <group> + <field name="priority"/> + <field name="date_start" invisible="1"/> + <field name="date_finished" invisible="1"/> + </group> + <group> + <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> + <field name="move_prod_id" groups="base.group_no_one"/> + </group> + </group> + </page> + </notebook> + </sheet> + <div class="oe_chatter"> + <field name="message_follower_ids" widget="mail_followers"/> + <field name="message_ids" widget="mail_thread"/> + </div> + </form> + </field> + </record> + <record id="mrp_production_tree_view" model="ir.ui.view"> + <field name="name">mrp.production.tree</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <tree decoration-bf="message_needaction==True" decoration-info="state in ('draft','confirmed')" decoration-danger="date_planned<current_date and state not in ('done','cancel')" decoration-muted="state in ('done','cancel')" string="Manufacturing Orders"> + <field name="message_needaction" invisible="1"/> + <field name="name"/> + <field name="date_planned"/> + <field name="product_id"/> + <field name="product_qty" sum="Total Qty"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> + <field name="routing_id" groups="mrp.group_mrp_routings"/> + <field name="hour_total" sum="Total Hours"/> + <field name="cycle_total" sum="Total Cycles"/> + <field name="origin"/> + <field name="state"/> + </tree> + </field> + </record> + <record id="mrp_production_kanban_view" model="ir.ui.view"> + <field name="name">mrp.production.kanban</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <kanban class="o_kanban_mobile"> + <field name="name"/> + <field name="product_id"/> + <field name="product_qty"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}"/> + <field name="date_planned"/> + <field name="state"/> + <templates> + <t t-name="kanban-box"> + <div t-attf-class="oe_kanban_card oe_kanban_global_click"> + <div class="row"> + <div class="col-xs-6"> + <strong><span><t t-esc="record.product_id.value"/></span></strong> + </div> + <div class="col-xs-6"> + <strong><span class="pull-right text-right"><t t-esc="record.product_qty.value"/> <t t-esc="record.product_uom.value"/></span></strong> + </div> + </div> + <div class="row"> + <div class="col-xs-6 text-muted"> + <span><t t-esc="record.name.value"/> <t t-esc="record.date_planned.value and record.date_planned.value.split(' ')[0] or False"/></span> + </div> + <div class="col-xs-6"> + <span class="pull-right text-right"> + <field name="state" widget="kanban_label_selection" options="{'classes': {'draft': 'default', 'cancel': 'default', 'none': 'danger', 'confirmed': 'warning', + 'done': 'success'}}"/> + </span> + </div> + </div> + </div> + </t> + </templates> + </kanban> + </field> + </record> + <record id="view_production_calendar" model="ir.ui.view"> + <field name="name">mrp.production.calendar</field> + <field name="model">mrp.production</field> + <field eval="2" name="priority"/> + <field name="arch" type="xml"> + <calendar color="routing_id" date_start="date_planned" string="Manufacturing Orders"> + <field name="name"/> + <field name="product_id"/> + <field name="product_qty"/> + </calendar> + </field> + </record> + <record id="view_production_gantt" model="ir.ui.view"> + <field name="name">mrp.production.gantt</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <gantt date_stop="date_finished" date_start="date_start" progress="progress" string="Productions" default_group_by="routing_id"> + </gantt> + </field> + </record> + <record id="view_production_pivot" model="ir.ui.view"> + <field name="name">mrp.production.pivot</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <pivot string="Manufacturing Orders"> + <field name="date_planned" type="row"/> + <field name="hour_total" type="measure"/> + <field name="cycle_total" type="measure"/> + </pivot> + </field> + </record> + <record id="view_production_graph" model="ir.ui.view"> + <field name="name">mrp.production.graph</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <graph string="Manufacturing Orders"> + <field name="date_planned"/> + <field name="hour_total" type="measure"/> + <field name="cycle_total" type="measure"/> + </graph> + </field> + </record> + <record id="view_mrp_production_filter" model="ir.ui.view"> + <field name="name">mrp.production.select</field> + <field name="model">mrp.production</field> + <field name="arch" type="xml"> + <search string="Search Production"> + <field name="name" string="Production" filter_domain="['|',('name','ilike',self),('origin','ilike',self)]"/> + <filter string="Pending" name="pending" domain="[('state','=','confirmed')]" + help="Manufacturing Orders which are waiting for raw materials."/> + <filter string="Ready" name="ready" domain="[('state','=','ready')]" + help="Manufacturing Orders which are ready to start production."/> + <filter string="In Production" name="inprogress" domain="[('state','=','in_production')]" + help="Manufacturing Orders which are currently in production."/> + <separator/> + <filter string="Late" domain="['&', ('date_planned','<', current_date), ('state', 'in', ('draft', 'confirmed', 'ready'))]" + help="Production started late"/> + <field name="product_tmpl_id"/> + <field name="product_id"/> + <field name="routing_id" groups="mrp.group_mrp_routings"/> + <group expand="0" string="Group By..."> + <filter string="Product" domain="[]" context="{'group_by':'product_id'}"/> + <filter string="Routing" domain="[]" context="{'group_by':'routing_id'}"/> + <filter string="Status" domain="[]" context="{'group_by':'state'}"/> + <filter string="Scheduled Month" domain="[]" context="{'group_by':'date_planned'}" help="Scheduled Date by Month"/> + </group> + </search> + </field> + </record> + <record id="mrp_production_action" model="ir.actions.act_window"> + <field name="name">Manufacturing Orders</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.production</field> + <field name="view_type">form</field> + <field name="view_mode">tree,kanban,form,calendar,pivot,graph,gantt</field> + <field name="view_id" eval="False"/> + <field name="search_view_id" ref="view_mrp_production_filter"/> + <field name="context">{}</field> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a manufacturing order. + </p><p> + A manufacturing order, based on a bill of materials, will + consume raw materials and produce finished products. + </p><p> + Manufacturing orders are usually proposed automatically based + on customer requirements or automated rules like the minimum + stock rule. + </p> + </field> + </record> + <menuitem action="mrp_production_action" + id="menu_mrp_production_action" + parent="menu_mrp_manufacturing" + sequence="1"/> + + <record id="mrp_production_action_planning" model="ir.actions.act_window"> + <field name="name">Manufacturing Orders</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.production</field> + <field name="view_type">form</field> + <field name="view_mode">calendar,tree,form,pivot,graph</field> + <field name="domain">[('state','in',('ready','confirmed','in_production'))]</field> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to start a new manufacturing order. + </p><p> + A manufacturing order, based on a bill of materials, will + consume raw materials and produce finished products. + </p><p> + Manufacturing orders are usually proposed automatically based + on customer requirements or automated rules like the minimum + stock rule. + </p> + </field> + </record> + <menuitem action="mrp.mrp_production_action_planning" + id="menu_mrp_production_order_action" name="Order Planning" + parent="menu_mrp_planning" sequence="1"/> + + <!-- Production Order Line --> + <record id="mrp_production_product_form_view" model="ir.ui.view"> + <field name="name">mrp.production.product.line.form</field> + <field name="model">mrp.production.product.line</field> + <field name="arch" type="xml"> + <form string="Scheduled Products"> + <group col="4"> + <field name="name"/> + <field name="product_id"/> + <field name="product_qty"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> + </group> + </form> + </field> + </record> + <record id="mrp_production_product_tree_view" model="ir.ui.view"> + <field name="name">mrp.production.product.line.tree</field> + <field name="model">mrp.production.product.line</field> + <field name="arch" type="xml"> + <tree string="Scheduled Products"> + <field name="product_id"/> + <field name="product_qty"/> + <field name="product_uom" options="{'no_open':True,'no_create':True}" groups="product.group_uom"/> + </tree> + </field> + </record> + + <record id="act_product_mrp_production" model="ir.actions.act_window"> + <field name="context">{'search_default_product_id': [active_id]}</field> + <field name="name">Manufacturing Orders</field> + <field name="res_model">mrp.production</field> + <field name="view_id" ref="mrp_production_tree_view"/> + </record> + </data> +</openerp> diff --git a/addons/mrp/views/mrp_property_views.xml b/addons/mrp/views/mrp_property_views.xml new file mode 100644 index 00000000000..626128dc43c --- /dev/null +++ b/addons/mrp/views/mrp_property_views.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Property Group --> + <record id="mrp_property_group_form_view" model="ir.ui.view"> + <field name="name">mrp.property.group.form</field> + <field name="model">mrp.property.group</field> + <field name="arch" type="xml"> + <form string="Properties categories"> + <group> + <field name="name"/> + </group> + <separator string="Description"/> + <field name="description" nolabel="1"/> + </form> + </field> + </record> + <record id="mrp_property_group_action" model="ir.actions.act_window"> + <field name="name">Property Groups</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.property.group</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a group of properties. + </p><p> + Define specific property groups that can be assigned to your + bill of materials and sales orders. Properties allows Odoo + to automatically select the right bill of materials according + to properties selected in the sales order by salesperson. + </p><p> + For instance, in the property group "Warranty", you an have + two properties: 1 year warranty, 3 years warranty. Depending + on the propoerties selected in the sales order, Odoo will + schedule a production using the matching bill of materials. + </p> + </field> + </record> + <menuitem id="menu_mrp_property_group_action" + action="mrp_property_group_action" + parent="menu_mrp_configuration" + groups="base.group_no_one,product.group_mrp_properties" + sequence="35"/> + + <!-- Property --> + <record id="mrp_property_tree_view" model="ir.ui.view"> + <field name="name">mrp.property.tree</field> + <field name="model">mrp.property</field> + <field name="arch" type="xml"> + <tree string="Properties"> + <field name="name"/> + <field name="composition"/> + <field name="group_id"/> + </tree> + </field> + </record> + <record id="mrp_property_form_view" model="ir.ui.view"> + <field name="name">mrp.property.form</field> + <field name="model">mrp.property</field> + <field name="arch" type="xml"> + <form string="Properties"> + <group col="4"> + <field colspan="4" name="name"/> + <field name="group_id"/> + <field name="composition"/> + </group> + <separator string="Description"/> + <field name="description" nolabel="1"/> + </form> + </field> + </record> + <record id="view_mrp_property_search" model="ir.ui.view"> + <field name="name">mrp.property.search</field> + <field name="model">mrp.property</field> + <field name="arch" type="xml"> + <search string="Search"> + <field name="name" string="Name"/> + <field name="composition"/> + <field name="group_id" /> + <group expand="0" string="Group By"> + <filter string="Property Group" domain="[]" context="{'group_by':'group_id'}"/> + </group> + </search> + </field> + </record> + <record id="mrp_property_action" model="ir.actions.act_window"> + <field name="name">Properties</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.property</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="search_view_id" ref="view_mrp_property_search"/> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a new property. + </p><p> + The Properties in Odoo are used to select the right bill of + materials for manufacturing a product when you have different + ways of building the same product. You can assign several + properties to each bill of materials. When a salesperson + creates a sales order, they can relate it to several properties + and Odoo will automatically select the BoM to use according + the needs. + </p> + </field> + </record> + <menuitem id="menu_mrp_property_action" + action="mrp_property_action" + groups="product.group_mrp_properties" + parent="menu_mrp_configuration" + sequence="30"/> + </data> +</openerp>
\ No newline at end of file diff --git a/addons/mrp/views/mrp_routing_views.xml b/addons/mrp/views/mrp_routing_views.xml new file mode 100644 index 00000000000..53a84747d0d --- /dev/null +++ b/addons/mrp/views/mrp_routing_views.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Routings Workcenter --> + <record id="mrp_routing_workcenter_tree_view" model="ir.ui.view"> + <field name="name">mrp.routing.workcenter.tree</field> + <field name="model">mrp.routing.workcenter</field> + <field name="arch" type="xml"> + <tree string="Routing Work Centers"> + <field name="sequence" widget="handle"/> + <field name="name"/> + <field name="workcenter_id"/> + <field name="cycle_nbr"/> + <field name="hour_nbr"/> + <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> + </tree> + </field> + </record> + <record id="mrp_routing_workcenter_form_view" model="ir.ui.view"> + <field name="name">mrp.routing.workcenter.form</field> + <field name="model">mrp.routing.workcenter</field> + <field name="arch" type="xml"> + <form string="Routing Work Centers"> + <sheet> + <group> + <group> + <field name="name"/> + <field name="workcenter_id"/> + <field name="sequence" groups="base.group_no_one"/> + </group><group> + <field name="cycle_nbr"/> + <field name="hour_nbr" widget="float_time"/> + <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> + </group> + </group> + <label for="note"/> + <field name="note"/> + </sheet> + </form> + </field> + </record> + + + <!-- Routings --> + <record id="mrp_routing_form_view" model="ir.ui.view"> + <field name="name">mrp.routing.form</field> + <field name="model">mrp.routing</field> + <field name="arch" type="xml"> + <form string="Routing"> + <sheet> + <div class="oe_button_box" name="button_box"> + <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive"> + <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/> + </button> + </div> + <group> + <group> + <field name="name"/> + <field name="code"/> + </group> + <group> + <field name="location_id"/> + <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> + </group> + </group> + <notebook> + <page string="Work Center Operations"> + <field name="workcenter_lines" string="Operation"/> + </page> + <page string="Notes"> + <field name="note"/> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + <record id="mrp_routing_tree_view" model="ir.ui.view"> + <field name="name">mrp.routing.tree</field> + <field name="model">mrp.routing</field> + <field name="arch" type="xml"> + <tree string="Routing"> + <field name="code"/> + <field name="name"/> + <field name="active" invisible="1"/> + <field name="location_id"/> + <field name="company_id" groups="base.group_multi_company"/> + </tree> + </field> + </record> + <record id="mrp_routing_search_view" model="ir.ui.view"> + <field name="name">mrp.routing.search</field> + <field name="model">mrp.routing</field> + <field name="arch" type="xml"> + <search string="Routing"> + <field name="name" string="Routing" filter_domain="['|',('name','ilike',self),('code','ilike',self)]"/> + <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> + <field name="location_id"/> + <field name="company_id" groups="base.group_multi_company"/> + <group expand="0" string="Group By..."> + <filter string="Production Location" domain="[]" context="{'group_by':'location_id'}"/> + </group> + </search> + </field> + </record> + <record id="mrp_routing_action" model="ir.actions.act_window"> + <field name="name">Routings</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.routing</field> + <field name="view_type">form</field> + <field name="view_id" ref="mrp_routing_tree_view"/> + <field name="search_view_id" ref="mrp_routing_search_view"/> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to create a new routing. + </p><p> + Routings allow you to create and manage the manufacturing + operations that should be followed within your work centers in + order to produce a product. They are attached to bills of + materials that will define the required raw materials. + </p> + </field> + </record> + <menuitem id="menu_mrp_routing_action" + action="mrp_routing_action" + parent="menu_mrp_configuration" + groups="group_mrp_routings" + sequence="50"/> + </data> +</openerp>
\ No newline at end of file diff --git a/addons/mrp/views/mrp_views_menus.xml b/addons/mrp/views/mrp_views_menus.xml new file mode 100644 index 00000000000..08407c4f8a8 --- /dev/null +++ b/addons/mrp/views/mrp_views_menus.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Top menu item --> + <menuitem id="base.menu_mrp_root" + name="Manufacturing" + groups="group_mrp_user,group_mrp_manager" + icon="fa-wrench" + web_icon="mrp,static/description/icon.png" + sequence="35"/> + + <menuitem id="menu_mrp_manufacturing" + name="Manufacturing" + parent="base.menu_mrp_root" + sequence="1"/> + + <menuitem id="menu_mrp_planning" + name="Planning" + parent="base.menu_mrp_root" + sequence="2"/> + + <menuitem id="menu_mrp_bom" + name="Products" + parent="base.menu_mrp_root" + sequence="5"/> + + <menuitem id="menu_mrp_configuration" + name="Configuration" + parent="base.menu_mrp_root" + groups="group_mrp_manager" + sequence="100"/> + + <menuitem id="menu_pm_resources_config" + name="Resources" + parent="menu_mrp_configuration" + sequence="100"/> + + <menuitem id="menu_view_resource_calendar_search_mrp" + action="resource.action_resource_calendar_form" + parent="menu_pm_resources_config" + sequence="10" + groups="base.group_no_one"/> + + <menuitem id="menu_view_resource_calendar_leaves_search_mrp" + action="resource.action_resource_calendar_leave_tree" + parent="menu_pm_resources_config" + sequence="20" + groups="base.group_no_one"/> + </data> +</openerp>
\ No newline at end of file diff --git a/addons/mrp/views/mrp_workcenter_views.xml b/addons/mrp/views/mrp_workcenter_views.xml new file mode 100644 index 00000000000..2f908a8f3bc --- /dev/null +++ b/addons/mrp/views/mrp_workcenter_views.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Work Centers --> + <record id="mrp_workcenter_tree_view" model="ir.ui.view"> + <field name="name">mrp.workcenter.tree</field> + <field name="model">mrp.workcenter</field> + <field name="arch" type="xml"> + <tree string="Work Center"> + <field name="code"/> + <field name="name"/> + <field name="active" invisible="1"/> + <field name="resource_type"/> + <field name="company_id" groups="base.group_multi_company"/> + </tree> + </field> + </record> + <record id="mrp_workcenter_view" model="ir.ui.view"> + <field name="name">mrp.workcenter.form</field> + <field name="model">mrp.workcenter</field> + <field name="arch" type="xml"> + <form string="Work Center"> + <div class="oe_button_box" name="button_box"> + <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive"> + <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/> + </button> + </div> + <group col="4"> + <group> + <field name="name"/> + <field name="resource_type"/> + <field name="calendar_id" widget="selection"/> + </group> + <group> + <field name="code"/> + <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> + </group> + </group> + <notebook> + <page string="General Information"> + <group> + <group string="Capacity Information"> + <field name="time_efficiency"/> + <field name="capacity_per_cycle"/> + <field name="time_cycle" widget="float_time"/> + <field name="time_start" widget="float_time"/> + <field name="time_stop" widget="float_time"/> + </group> + <group string="Costing Information"> + <field name="product_id" on_change="on_change_product_cost(product_id)"/> + <field name="costs_hour"/> + <field name="costs_hour_account_id" groups="analytic.group_analytic_accounting"/> + <field name="costs_cycle"/> + <field name="costs_cycle_account_id" groups="analytic.group_analytic_accounting"/> + <field name="costs_general_account_id" attrs="{ 'required':['|',('costs_cycle_account_id', '=', True),('costs_hour_account_id', '=', True)]}" groups="analytic.group_analytic_accounting"/> + </group> + </group> + <separator string="Description"/> + <field name="note" nolabel="1"/> + </page> + </notebook> + </form> + </field> + </record> + <record id="view_mrp_workcenter_search" model="ir.ui.view"> + <field name="name">mrp.workcenter.search</field> + <field name="model">mrp.workcenter</field> + <field name="arch" type="xml"> + <search string="Search for mrp workcenter"> + <field name="name" string="Mrp Workcenter" filter_domain="['|',('name','ilike',self),('code','ilike',self)]"/> + <field name="resource_type"/> + <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> + <group expand="0" string="Group By..."> + <filter string="Type" domain="[]" context="{'group_by':'resource_type'}"/> + <filter string="Company" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> + </group> + </search> + </field> + </record> + + <record id="mrp_workcenter_action" model="ir.actions.act_window"> + <field name="name">Work Centers</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">mrp.workcenter</field> + <field name="view_type">form</field> + <field name="view_id" ref="mrp_workcenter_tree_view"/> + <field name="search_view_id" ref="view_mrp_workcenter_search"/> + <field name="help" type="html"> + <p class="oe_view_nocontent_create"> + Click to add a work center. + </p><p> + Work Centers allow you to create and manage manufacturing + units. They consist of workers and/or machines, which are + considered as units for task assignation as well as capacity + and planning forecast. + </p> + </field> + </record> + <menuitem id="menu_view_resource_search_mrp" + action="mrp_workcenter_action" + groups="group_mrp_routings" + parent="menu_mrp_configuration" + sequence="40"/> + </data> +</openerp>
\ No newline at end of file diff --git a/addons/mrp/views/procurement_views.xml b/addons/mrp/views/procurement_views.xml new file mode 100644 index 00000000000..9e1d35ad799 --- /dev/null +++ b/addons/mrp/views/procurement_views.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- Procurements --> + <record id="view_procurement_rule_form_stock_inherit_mrp" model="ir.ui.view"> + <field name="name">procurement.rule.form.stock.inherit.mrp</field> + <field name="model">procurement.rule</field> + <field name="inherit_id" ref="stock.view_procurement_rule_form_stock_inherit"/> + <field name="arch" type="xml"> + <field name="location_src_id" position="attributes"> + <attribute name="attrs">{'required': [('action', '=', 'move')], 'invisible': [('action', 'not in', ['move', 'manufacture'])]}</attribute> + </field> + </field> + </record> + + <record id="view_procurement_form_inherit" model="ir.ui.view"> + <field name="name">procurement.order.form.inherit</field> + <field name="model">procurement.order</field> + <field name="inherit_id" ref="procurement.procurement_form_view"/> + <field name="arch" type="xml"> + <data> + <field name="origin" position="before"> + <field name="bom_id" domain="[('product_id','=',product_id)]"/> + <field name="production_id" attrs="{'invisible': [('production_id','=',False)]}"/> + </field> + <field name="origin" position="after"> + <field name="property_ids" widget="many2many_tags" groups="product.group_mrp_properties"/> + </field> + </data> + </field> + </record> + </data> +</odoo>
\ No newline at end of file diff --git a/addons/mrp/views/product_views.xml b/addons/mrp/views/product_views.xml new file mode 100644 index 00000000000..aa6c13f10e9 --- /dev/null +++ b/addons/mrp/views/product_views.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- Product Template --> + <record id="view_mrp_product_template_form_inherited" model="ir.ui.view"> + <field name="name">product.form.mrp.inherited</field> + <field name="model">product.template</field> + <field name="inherit_id" ref="stock.view_template_property_form"/> + <field name="arch" type="xml"> + <group name="sale_condition" position="inside"> + <label for="produce_delay" attrs="{'invisible':[('type','=','service')]}"/> + <div attrs="{'invisible':[('type','=','service')]}"> + <field name="produce_delay" class="oe_inline"/> days + </div> + </group> + </field> + </record> + <record id="product_template_search_view_procurment" model="ir.ui.view"> + <field name="name">product.template.search.bom</field> + <field name="model">product.template</field> + <field name="inherit_id" ref="product.product_template_search_view"/> + <field name="arch" type="xml"> + <xpath expr="//filter[@name='consumable']" position="after"> + <separator/> + <filter string="Components" name="components" domain="[('bom_ids','not in',[])]"/> + </xpath> + </field> + </record> + <record id="product_template_action" model="ir.actions.act_window"> + <field name="name">Products</field> + <field name="res_model">product.template</field> + <field name="view_type">form</field> + <field name="view_mode">kanban,tree,form</field> + <field name="context">{}</field> + </record> + <menuitem id="menu_mrp_product_form" + name="Products" + action="product_template_action" + parent="menu_mrp_bom"/> + + <record id="product_template_form_view_bom_button" model="ir.ui.view"> + <field name="name">product.template.procurement</field> + <field name="model">product.template</field> + <field name="inherit_id" ref="product.product_template_only_form_view"/> + <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/> + <field name="arch" type="xml"> + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="%(template_open_bom)d" type="action" + attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-flask"> + <field string="Bill of Materials" name="bom_count" widget="statinfo" /> + </button> + <button class="oe_stat_button" name="action_view_mos" type="object" + attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-list-alt"> + <field string="Manufacturing" name="mo_count" widget="statinfo" /> + </button> + </div> + </field> + </record> + + <record id="product_product_form_view_bom_button" model="ir.ui.view"> + <field name="name">product.product.procurement</field> + <field name="model">product.product</field> + <field name="inherit_id" ref="product.product_normal_form_view"/> + <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/> + <field name="arch" type="xml"> + <div name="button_box" position="inside"> + <button class="oe_stat_button" name="action_view_bom" type="object" + attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-flask"> + <field string="Bill of Materials" name="bom_count" widget="statinfo" /> + </button> + <button class="oe_stat_button" name="%(act_product_mrp_production)d" type="action" + attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}" icon="fa-list-alt"> + <field string="Manufacturing" name="mo_count" widget="statinfo" /> + </button> + </div> + </field> + </record> + </data> +</odoo>
\ No newline at end of file diff --git a/addons/mrp/company_view.xml b/addons/mrp/views/res_company_views.xml index 559f5f02330..559f5f02330 100644 --- a/addons/mrp/company_view.xml +++ b/addons/mrp/views/res_company_views.xml diff --git a/addons/mrp/views/stock_warehouse_views.xml b/addons/mrp/views/stock_warehouse_views.xml new file mode 100644 index 00000000000..2cc559115d1 --- /dev/null +++ b/addons/mrp/views/stock_warehouse_views.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <!-- Warehouse --> + <record id="view_warehouse_inherited" model="ir.ui.view"> + <field name="name">Stock Warehouse Inherited</field> + <field name="model">stock.warehouse</field> + <field name="inherit_id" ref="stock.view_warehouse"/> + <field name="arch" type="xml"> + <xpath expr="//field[@name='default_resupply_wh_id']" position="before"> + <field name="manufacture_to_resupply" /> + </xpath> + </field> + </record> +</odoo>
\ No newline at end of file diff --git a/addons/mrp/wizard/__init__.py b/addons/mrp/wizard/__init__.py index 0a37f7a0522..956e2a72109 100644 --- a/addons/mrp/wizard/__init__.py +++ b/addons/mrp/wizard/__init__.py @@ -3,4 +3,4 @@ import mrp_product_produce import change_production_qty -import stock_move +import stock_move_consume diff --git a/addons/mrp/wizard/change_production_qty_view.xml b/addons/mrp/wizard/change_production_qty_views.xml index ebe2bfd9585..ebe2bfd9585 100644 --- a/addons/mrp/wizard/change_production_qty_view.xml +++ b/addons/mrp/wizard/change_production_qty_views.xml diff --git a/addons/mrp/wizard/mrp_product_produce_view.xml b/addons/mrp/wizard/mrp_product_produce_views.xml index 3db1f180b8e..3db1f180b8e 100644 --- a/addons/mrp/wizard/mrp_product_produce_view.xml +++ b/addons/mrp/wizard/mrp_product_produce_views.xml diff --git a/addons/mrp/wizard/stock_move.py b/addons/mrp/wizard/stock_move_consume.py index d00d282a4a7..d00d282a4a7 100644 --- a/addons/mrp/wizard/stock_move.py +++ b/addons/mrp/wizard/stock_move_consume.py diff --git a/addons/mrp/wizard/stock_move_view.xml b/addons/mrp/wizard/stock_move_consume_views.xml index 55b7914e980..55b7914e980 100644 --- a/addons/mrp/wizard/stock_move_view.xml +++ b/addons/mrp/wizard/stock_move_consume_views.xml |
