Merge branch 'master' of https://github.com/odoo/odoo
[odoo/odoo.git] / addons / mrp_byproduct / mrp_byproduct.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from openerp.osv import fields
23 from openerp.osv import osv
24 import openerp.addons.decimal_precision as dp
25 from openerp.tools.translate import _
26
27 class mrp_subproduct(osv.osv):
28     _name = 'mrp.subproduct'
29     _description = 'Byproduct'
30     _columns={
31         'product_id': fields.many2one('product.product', 'Product', required=True),
32         'product_qty': fields.float('Product Qty', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
33         'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
34         'subproduct_type': fields.selection([('fixed','Fixed'),('variable','Variable')], 'Quantity Type', required=True, help="Define how the quantity of byproducts will be set on the production orders using this BoM.\
35   'Fixed' depicts a situation where the quantity of created byproduct is always equal to the quantity set on the BoM, regardless of how many are created in the production order.\
36   By opposition, 'Variable' means that the quantity will be computed as\
37     '(quantity of byproduct set on the BoM / quantity of manufactured product set on the BoM * quantity of manufactured product in the production order.)'"),
38         'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade'),
39     }
40     _defaults={
41         'subproduct_type': 'variable',
42         'product_qty': lambda *a: 1.0,
43     }
44
45     def onchange_product_id(self, cr, uid, ids, product_id, context=None):
46         """ Changes UoM if product_id changes.
47         @param product_id: Changed product_id
48         @return: Dictionary of changed values
49         """
50         if product_id:
51             prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
52             v = {'product_uom': prod.uom_id.id}
53             return {'value': v}
54         return {}
55
56     def onchange_uom(self, cr, uid, ids, product_id, product_uom, context=None):
57         res = {'value':{}}
58         if not product_uom or not product_id:
59             return res
60         product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
61         uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context)
62         if uom.category_id.id != product.uom_id.category_id.id:
63             res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')}
64             res['value'].update({'product_uom': product.uom_id.id})
65         return res
66
67
68 class mrp_bom(osv.osv):
69     _name = 'mrp.bom'
70     _description = 'Bill of Material'
71     _inherit='mrp.bom'
72
73     _columns={
74         'sub_products':fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts', copy=True),
75     }
76
77
78 class mrp_production(osv.osv):
79     _description = 'Production'
80     _inherit= 'mrp.production'
81
82
83     def action_confirm(self, cr, uid, ids, context=None):
84         """ Confirms production order and calculates quantity based on subproduct_type.
85         @return: Newly generated picking Id.
86         """
87         move_obj = self.pool.get('stock.move')
88         picking_id = super(mrp_production,self).action_confirm(cr, uid, ids, context=context)
89         product_uom_obj = self.pool.get('product.uom')
90         for production in self.browse(cr, uid, ids):
91             source = production.product_id.property_stock_production.id
92             if not production.bom_id:
93                 continue
94             for sub_product in production.bom_id.sub_products:
95                 product_uom_factor = product_uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, production.bom_id.product_uom.id)
96                 qty1 = sub_product.product_qty
97                 qty2 = production.product_uos and production.product_uos_qty or False
98                 product_uos_factor = 0.0
99                 if qty2 and production.bom_id.product_uos.id:
100                     product_uos_factor = product_uom_obj._compute_qty(cr, uid, production.product_uos.id, production.product_uos_qty, production.bom_id.product_uos.id)
101                 if sub_product.subproduct_type == 'variable':
102                     if production.product_qty:
103                         qty1 *= product_uom_factor / (production.bom_id.product_qty or 1.0)
104                     if production.product_uos_qty:
105                         qty2 *= product_uos_factor / (production.bom_id.product_uos_qty or 1.0)
106                 data = {
107                     'name': 'PROD:'+production.name,
108                     'date': production.date_planned,
109                     'product_id': sub_product.product_id.id,
110                     'product_uom_qty': qty1,
111                     'product_uom': sub_product.product_uom.id,
112                     'product_uos_qty': qty2,
113                     'product_uos': production.product_uos and production.product_uos.id or False,
114                     'location_id': source,
115                     'location_dest_id': production.location_dest_id.id,
116                     'move_dest_id': production.move_prod_id.id,
117                     'production_id': production.id
118                 }
119                 move_id = move_obj.create(cr, uid, data, context=context)
120                 move_obj.action_confirm(cr, uid, [move_id], context=context)
121
122         return picking_id
123
124     def _get_subproduct_factor(self, cr, uid, production_id, move_id=None, context=None):
125         """Compute the factor to compute the qty of procucts to produce for the given production_id. By default, 
126             it's always equal to the quantity encoded in the production order or the production wizard, but with 
127             the module mrp_byproduct installed it can differ for byproducts having type 'variable'.
128         :param production_id: ID of the mrp.order
129         :param move_id: ID of the stock move that needs to be produced. Identify the product to produce.
130         :return: The factor to apply to the quantity that we should produce for the given production order and stock move.
131         """
132         sub_obj = self.pool.get('mrp.subproduct')
133         move_obj = self.pool.get('stock.move')
134         production_obj = self.pool.get('mrp.production')
135         production_browse = production_obj.browse(cr, uid, production_id, context=context)
136         move_browse = move_obj.browse(cr, uid, move_id, context=context)
137         subproduct_factor = 1
138         sub_id = sub_obj.search(cr, uid,[('product_id', '=', move_browse.product_id.id),('bom_id', '=', production_browse.bom_id.id), ('subproduct_type', '=', 'variable')], context=context)
139         if sub_id:
140             subproduct_record = sub_obj.browse(cr ,uid, sub_id[0], context=context)
141             if subproduct_record.bom_id.product_qty:
142                 subproduct_factor = subproduct_record.product_qty / subproduct_record.bom_id.product_qty
143                 return subproduct_factor
144         return super(mrp_production, self)._get_subproduct_factor(cr, uid, production_id, move_id, context=context)
145
146
147 class change_production_qty(osv.osv_memory):
148     _inherit = 'change.production.qty'
149
150     def _update_product_to_produce(self, cr, uid, prod, qty, context=None):
151         bom_obj = self.pool.get('mrp.bom')
152         move_lines_obj = self.pool.get('stock.move')
153         prod_obj = self.pool.get('mrp.production')
154         for m in prod.move_created_ids:
155             if m.product_id.id == prod.product_id.id:
156                 move_lines_obj.write(cr, uid, [m.id], {'product_uom_qty': qty})
157             else:
158                 for sub_product_line in prod.bom_id.sub_products:
159                     if sub_product_line.product_id.id == m.product_id.id:
160                         factor = prod_obj._get_subproduct_factor(cr, uid, prod.id, m.id, context=context)
161                         subproduct_qty = sub_product_line.subproduct_type == 'variable' and qty * factor or sub_product_line.product_qty
162                         move_lines_obj.write(cr, uid, [m.id], {'product_uom_qty': subproduct_qty})
163
164 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: