From c8a39e734f2551de4b3a4c78c65e711b49d2344d Mon Sep 17 00:00:00 2001 From: Dhaval Panchal Date: Fri, 29 Apr 2016 16:18:07 +0200 Subject: [PATCH] [MOV] mrp: move and split Split files and move them according to the new API guidelines. Containing - mrp splitted into its main models and files - stock code moved into its own models and files - various renaming --- addons/mrp/__init__.py | 7 +- addons/mrp/__openerp__.py | 40 +- addons/mrp/{ => data}/mrp_data.xml | 0 .../mrp_data_workflow.xml} | 0 addons/mrp/{ => data}/mrp_demo.xml | 0 addons/mrp/models/__init__.py | 14 + addons/mrp/models/mrp_bom.py | 336 +++++ .../mrp_config_settings.py} | 0 .../mrp/{mrp.py => models/mrp_production.py} | 456 +------ addons/mrp/models/mrp_property.py | 33 + addons/mrp/models/mrp_routing.py | 57 + addons/mrp/models/mrp_workcenter.py | 57 + addons/mrp/{ => models}/procurement.py | 0 addons/mrp/{ => models}/product.py | 0 .../mrp/{company.py => models/res_company.py} | 0 addons/mrp/{stock.py => models/stock_move.py} | 76 -- addons/mrp/models/stock_warehouse.py | 86 ++ addons/mrp/mrp_demo.yml | 8 - addons/mrp/mrp_view.xml | 1102 ----------------- ...p_report_view.xml => mrp_report_views.xml} | 0 .../mrp_report_views_main.xml} | 0 addons/mrp/views/mrp_bom_views.xml | 225 ++++ .../mrp_config_settings_views.xml} | 0 addons/mrp/views/mrp_production_views.xml | 384 ++++++ addons/mrp/views/mrp_property_views.xml | 114 ++ addons/mrp/views/mrp_routing_views.xml | 130 ++ addons/mrp/views/mrp_views_menus.xml | 50 + addons/mrp/views/mrp_workcenter_views.xml | 105 ++ addons/mrp/views/procurement_views.xml | 33 + addons/mrp/views/product_views.xml | 79 ++ .../res_company_views.xml} | 0 addons/mrp/views/stock_warehouse_views.xml | 14 + addons/mrp/wizard/__init__.py | 2 +- ...ew.xml => change_production_qty_views.xml} | 0 ...view.xml => mrp_product_produce_views.xml} | 0 .../{stock_move.py => stock_move_consume.py} | 0 ..._view.xml => stock_move_consume_views.xml} | 0 37 files changed, 1744 insertions(+), 1664 deletions(-) rename addons/mrp/{ => data}/mrp_data.xml (100%) rename addons/mrp/{mrp_workflow.xml => data/mrp_data_workflow.xml} (100%) rename addons/mrp/{ => data}/mrp_demo.xml (100%) create mode 100644 addons/mrp/models/__init__.py create mode 100644 addons/mrp/models/mrp_bom.py rename addons/mrp/{res_config.py => models/mrp_config_settings.py} (100%) rename addons/mrp/{mrp.py => models/mrp_production.py} (65%) create mode 100644 addons/mrp/models/mrp_property.py create mode 100644 addons/mrp/models/mrp_routing.py create mode 100644 addons/mrp/models/mrp_workcenter.py rename addons/mrp/{ => models}/procurement.py (100%) rename addons/mrp/{ => models}/product.py (100%) rename addons/mrp/{company.py => models/res_company.py} (100%) rename addons/mrp/{stock.py => models/stock_move.py} (76%) create mode 100644 addons/mrp/models/stock_warehouse.py delete mode 100644 addons/mrp/mrp_demo.yml delete mode 100644 addons/mrp/mrp_view.xml rename addons/mrp/report/{mrp_report_view.xml => mrp_report_views.xml} (100%) rename addons/mrp/{mrp_report.xml => report/mrp_report_views_main.xml} (100%) create mode 100644 addons/mrp/views/mrp_bom_views.xml rename addons/mrp/{res_config_view.xml => views/mrp_config_settings_views.xml} (100%) create mode 100644 addons/mrp/views/mrp_production_views.xml create mode 100644 addons/mrp/views/mrp_property_views.xml create mode 100644 addons/mrp/views/mrp_routing_views.xml create mode 100644 addons/mrp/views/mrp_views_menus.xml create mode 100644 addons/mrp/views/mrp_workcenter_views.xml create mode 100644 addons/mrp/views/procurement_views.xml create mode 100644 addons/mrp/views/product_views.xml rename addons/mrp/{company_view.xml => views/res_company_views.xml} (100%) create mode 100644 addons/mrp/views/stock_warehouse_views.xml rename addons/mrp/wizard/{change_production_qty_view.xml => change_production_qty_views.xml} (100%) rename addons/mrp/wizard/{mrp_product_produce_view.xml => mrp_product_produce_views.xml} (100%) rename addons/mrp/wizard/{stock_move.py => stock_move_consume.py} (100%) rename addons/mrp/wizard/{stock_move_view.xml => stock_move_consume_views.xml} (100%) 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 similarity index 100% rename from addons/mrp/mrp_data.xml rename to addons/mrp/data/mrp_data.xml diff --git a/addons/mrp/mrp_workflow.xml b/addons/mrp/data/mrp_data_workflow.xml similarity index 100% rename from addons/mrp/mrp_workflow.xml rename to addons/mrp/data/mrp_data_workflow.xml diff --git a/addons/mrp/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml similarity index 100% rename from addons/mrp/mrp_demo.xml rename to 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 similarity index 100% rename from addons/mrp/res_config.py rename to addons/mrp/models/mrp_config_settings.py diff --git a/addons/mrp/mrp.py b/addons/mrp/models/mrp_production.py similarity index 65% rename from addons/mrp/mrp.py rename to 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 similarity index 100% rename from addons/mrp/procurement.py rename to addons/mrp/models/procurement.py diff --git a/addons/mrp/product.py b/addons/mrp/models/product.py similarity index 100% rename from addons/mrp/product.py rename to addons/mrp/models/product.py diff --git a/addons/mrp/company.py b/addons/mrp/models/res_company.py similarity index 100% rename from addons/mrp/company.py rename to addons/mrp/models/res_company.py diff --git a/addons/mrp/stock.py b/addons/mrp/models/stock_move.py similarity index 76% rename from addons/mrp/stock.py rename to 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - mrp.property.group.form - mrp.property.group - -
- - - - - - - -
- - Property Groups - ir.actions.act_window - mrp.property.group - form - tree,form - -

- Click to create a group of properties. -

- 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. -

- 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. -

-
-
- - - - - mrp.property.tree - mrp.property - - - - - - - - - - mrp.property.form - mrp.property - -
- - - - - - - - - -
- - mrp.property.search - mrp.property - - - - - - - - - - - - - Properties - ir.actions.act_window - mrp.property - form - tree,form - - -

- Click to create a new property. -

- 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. -

-
-
- - - - - mrp.workcenter.tree - mrp.workcenter - - - - - - - - - - - - mrp.workcenter.form - mrp.workcenter - -
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - mrp.workcenter.search - mrp.workcenter - - - - - - - - - - - - - - - - mrp.routing.workcenter.tree - mrp.routing.workcenter - - - - - - - - - - - - - mrp.routing.workcenter.form - mrp.routing.workcenter - -
- - - - - - - - - - - - - -
-
-
- - Work Centers - ir.actions.act_window - mrp.workcenter - form - - - -

- Click to add a work center. -

- 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. -

-
-
- - - - - mrp.routing.form - mrp.routing - -
- -
- -
- - - - - - - - - - - - - - - - - - -
-
-
-
- - mrp.routing.tree - mrp.routing - - - - - - - - - - - - mrp.routing.search - mrp.routing - - - - - - - - - - - - - - Routings - ir.actions.act_window - mrp.routing - form - - - -

- Click to create a new routing. -

- 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. -

-
-
- - - - - mrp.bom.form - mrp.bom - -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
-
- - mrp.bom.tree - mrp.bom - - - - - - - - - - - - - mrp.bom.select - mrp.bom - - - - - - - - - - - - - - - - - - Bill of Materials - ir.actions.act_window - mrp.bom - [] - form - - -

- Click to create a bill of material. -

- 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. -

- Odoo uses these BoMs to automatically propose manufacturing - orders according to procurement needs. -

-
-
- - - - - mrp.bom.component.tree - mrp.bom.line - - - - - - - - - - - - - - - - - - - mrp.bom.tree - mrp.bom.line - child_line_ids - - - - - - - - - - - - - mrp.bom.line.select - mrp.bom.line - - - - - - - - - - - - - - - - Bill of Materials Structure - ir.actions.act_window - mrp.bom.line - [('bom_id', 'in', active_ids)] - tree - - tree - - - - - Bill of Materials Structure - - - - - - mrp.production.form - mrp.production - -
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - {'default_product_id': active_id, 'search_default_product_id': active_id} - Bill of Materials - mrp.bom - [] - form - - - {'search_default_product_id': [active_id]} - Manufacturing Orders - mrp.production - - - - product.product.procurement - product.product - - - -
- - -
-
-
- - - - Stock Warehouse Inherited - stock.warehouse - - - - - - - - -
-
diff --git a/addons/mrp/report/mrp_report_view.xml b/addons/mrp/report/mrp_report_views.xml similarity index 100% rename from addons/mrp/report/mrp_report_view.xml rename to addons/mrp/report/mrp_report_views.xml diff --git a/addons/mrp/mrp_report.xml b/addons/mrp/report/mrp_report_views_main.xml similarity index 100% rename from addons/mrp/mrp_report.xml rename to 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 @@ + + + + + + mrp.bom.form + mrp.bom + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+
+ + mrp.bom.tree + mrp.bom + + + + + + + + + + + + + mrp.bom.select + mrp.bom + + + + + + + + + + + + + + + + + + Bill of Materials + ir.actions.act_window + mrp.bom + [] + form + + +

+ Click to create a bill of material. +

+ 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. +

+ Odoo uses these BoMs to automatically propose manufacturing + orders according to procurement needs. +

+
+
+ + + + + mrp.bom.component.tree + mrp.bom.line + + + + + + + + + + + + + + + + + + + mrp.bom.tree + mrp.bom.line + child_line_ids + + + + + + + + + + + + + mrp.bom.line.select + mrp.bom.line + + + + + + + + + + + + + + + + Bill of Materials Structure + ir.actions.act_window + mrp.bom.line + [('bom_id', 'in', active_ids)] + tree + + tree + + + + + Bill of Materials Structure + + + + + {'default_product_tmpl_id': active_id, 'search_default_product_tmpl_id': active_id} + Bill of Materials + mrp.bom + form + + + + {'default_product_id': active_id, 'search_default_product_id': active_id} + Bill of Materials + mrp.bom + [] + form + +
+
\ No newline at end of file diff --git a/addons/mrp/res_config_view.xml b/addons/mrp/views/mrp_config_settings_views.xml similarity index 100% rename from addons/mrp/res_config_view.xml rename to 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 @@ + + + + + mrp.production.form + mrp.production + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + mrp.routing.tree + mrp.routing + + + + + + + + + + + + mrp.routing.search + mrp.routing + + + + + + + + + + + + + + Routings + ir.actions.act_window + mrp.routing + form + + + +

+ Click to create a new routing. +

+ 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. +

+
+
+ +
+
\ 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 @@ + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + + + mrp.workcenter.tree + mrp.workcenter + + + + + + + + + + + + mrp.workcenter.form + mrp.workcenter + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + mrp.workcenter.search + mrp.workcenter + + + + + + + + + + + + + + + Work Centers + ir.actions.act_window + mrp.workcenter + form + + + +

+ Click to add a work center. +

+ 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. +

+
+
+ +
+
\ 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 @@ + + + + + + procurement.rule.form.stock.inherit.mrp + procurement.rule + + + + {'required': [('action', '=', 'move')], 'invisible': [('action', 'not in', ['move', 'manufacture'])]} + + + + + + procurement.order.form.inherit + procurement.order + + + + + + + + + + + + + + + \ 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 @@ + + + + + + product.form.mrp.inherited + product.template + + + + + + + + product.template.search.bom + product.template + + + + + + + + + + Products + product.template + form + kanban,tree,form + {} + + + + + product.template.procurement + product.template + + + +
+ + +
+
+
+ + + product.product.procurement + product.product + + + +
+ + +
+
+
+
+
\ No newline at end of file diff --git a/addons/mrp/company_view.xml b/addons/mrp/views/res_company_views.xml similarity index 100% rename from addons/mrp/company_view.xml rename to 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 @@ + + + + + Stock Warehouse Inherited + stock.warehouse + + + + + + + + \ 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 similarity index 100% rename from addons/mrp/wizard/change_production_qty_view.xml rename to 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 similarity index 100% rename from addons/mrp/wizard/mrp_product_produce_view.xml rename to 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 similarity index 100% rename from addons/mrp/wizard/stock_move.py rename to 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 similarity index 100% rename from addons/mrp/wizard/stock_move_view.xml rename to addons/mrp/wizard/stock_move_consume_views.xml