diff --git a/bom_split/__init__.py b/bom_split/__init__.py deleted file mode 100644 index 14419a9..0000000 --- a/bom_split/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2010-2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import mrp - - diff --git a/bom_split/__openerp__.py b/bom_split/__openerp__.py deleted file mode 100644 index 2a6f210..0000000 --- a/bom_split/__openerp__.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2010-2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'BoMs Split', - 'version': '1.0.1', - 'category': 'Hidden', - 'description': """ -Technical module to allow the split of Bills of Materials. -Used in sale_bom_split and purchase_bom_split. - """, - 'author': "Camptocamp,Odoo Community Association (OCA)", - 'website': 'http://www.camptocamp.com', - 'license': 'AGPL-3', - 'depends': ['mrp'], - 'data': [], - 'demo': ['bom_split_demo.yml'], - 'installable': False, - 'autoinstall': False, -} - diff --git a/bom_split/bom_split_demo.yml b/bom_split/bom_split_demo.yml deleted file mode 100644 index 31ca3a3..0000000 --- a/bom_split/bom_split_demo.yml +++ /dev/null @@ -1,43 +0,0 @@ -- - I create a product "Drilling Kit" -- - !record {model: product.product, id: product1}: - name: Drilling Kit - type: product - procure_method: make_to_order - supply_method: produce - uom_id: product.product_uom_unit -- - I create a product "Driller" -- - !record {model: product.product, id: product2}: - name: Driller - type: product - procure_method: make_to_order - supply_method: buy - uom_id: product.product_uom_unit -- - I create a product "Drill bit" -- - !record {model: product.product, id: product3}: - name: Drill bit - type: product - procure_method: make_to_order - supply_method: buy - uom_id: product.product_uom_unit -- - I explain how to build Driller Kits -- - !record {model: mrp.bom, id: bom_prod1}: - name: half pipe - product_id: product1 - product_qty: 1 - product_uom: product.product_uom_dozen - type: phantom - bom_lines: - - product_id: product2 - product_qty: 1 - product_uom: product.product_uom_dozen - - product_id: product3 - product_qty: 48 - product_uom: product.product_uom_unit diff --git a/bom_split/mrp.py b/bom_split/mrp.py deleted file mode 100644 index f38b758..0000000 --- a/bom_split/mrp.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv.orm import Model -from openerp.addons.mrp.mrp import rounding - - -class mrp_bom(Model): - - _inherit = 'mrp.bom' - - def _bom_split_vals(self, cr, uid, bom, factor, context=None): - product_obj = self.pool['product.product'] - return { - 'name': product_obj.name_get(cr, uid, [bom.product_id.id])[0][1], - 'product_id': bom.product_id.id, - 'product_qty': bom.product_qty * factor, - 'product_uom': bom.product_uom.id, - 'product_uos_qty': bom.product_uos and bom.product_uos_qty * factor or False, - 'product_uos': bom.product_uos and bom.product_uos.id or False, - } - - def bom_split(self, cr, uid, bom, factor, addthis=False, level=0): - """ - split the bom into components. - factor is the quantity to produce expressed in bom product_uom - """ - factor = factor / (bom.product_efficiency or 1.0) - factor = rounding(factor, bom.product_rounding) - if factor < bom.product_rounding: - factor = bom.product_rounding - result = [] - phantom = False - if bom.type == 'phantom' and not bom.bom_lines: - newbom = self._bom_find( - cr, uid, bom.product_id.id, bom.product_uom.id) - - if newbom: - result += self.bom_split( - cr, uid, self.browse(cr, uid, [newbom])[0], - factor * bom.product_qty, - addthis=True, - level=level+10) - phantom = True - else: - phantom = False - - if bom.type == 'normal' and bom.bom_lines: - result.append(self._bom_split_vals(cr, uid, bom, factor)) - - elif not phantom: - if addthis and not bom.bom_lines: - result.append(self._bom_split_vals(cr, uid, bom, factor)) - - for bom2 in bom.bom_lines: - result += self.bom_split( - cr, uid, bom2, factor, addthis=True, level=level + 10) - return result - diff --git a/purchase_bom_split/__init__.py b/purchase_bom_split/__init__.py deleted file mode 100644 index 876e1d4..0000000 --- a/purchase_bom_split/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2010-2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import purchase - diff --git a/purchase_bom_split/__openerp__.py b/purchase_bom_split/__openerp__.py deleted file mode 100644 index 9c42b3b..0000000 --- a/purchase_bom_split/__openerp__.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2010-2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Purchase BoMs split', - 'version': '1.0.1', - 'category': 'Generic Modules/Sales & Purchases', - 'description': """ - This module split the product into the related packing according - to their Bill of Material when validating a purchase order. - - BoM Example structure: - - Product A (fantom) - Product B (normal) - Product B1 (normal) - Product B2 (normal) - Product C (fantom) - Product C1 (normal) - Product C2 (normal) - - With this module, purchase of Product A will result in a packing of : - - Product B - Product C1 - Product C2 - - Without it will result in a packing of : - - Product A - """, - 'author': "Camptocamp,Odoo Community Association (OCA)", - 'website': 'http://www.camptocamp.com', - 'license': 'AGPL-3', - 'depends': ['bom_split', 'purchase'], - 'data': [], - 'demo': [], - 'test': ['tests/test_purchase_bom_split.yml'], - 'installable': False, - 'auto_install': False, -} - diff --git a/purchase_bom_split/purchase.py b/purchase_bom_split/purchase.py deleted file mode 100644 index 7e23746..0000000 --- a/purchase_bom_split/purchase.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.osv.orm import Model - - -class purchase_order(Model): - - _inherit = "purchase.order" - - def _prepare_order_line_split_move(self, cr, uid, order, base_line, component, picking_id, context=None): - vals = super(purchase_order, self)._prepare_order_line_move( - cr, uid, order, base_line, picking_id, context=context) - vals.update({ - 'name': ("PO: %s" % component['name'])[:250], - 'product_id': component['product_id'], - 'product_qty': component['product_qty'], - 'product_uom': component['product_uom'], - 'product_uos_qty': component['product_qty'], - 'product_uos': component['product_uos'] - }) - return vals - - def _create_pickings(self, cr, uid, order, order_lines, picking_id=False, context=None): - """Creates pickings and appropriate stock moves for given order lines, then - confirms the moves, makes them available, and confirms the picking. - - If ``picking_id`` is provided, the stock moves will be added to it, otherwise - a standard outgoing picking will be created to wrap the stock moves, as returned - by :meth:`~._prepare_order_picking`. - - Modules that wish to customize the procurements or partition the stock moves over - multiple stock pickings may override this method and call ``super()`` with - different subsets of ``order_lines`` and/or preset ``picking_id`` values. - - Inherited in order to explode BoMs in many move lines in the picking. - - :param browse_record order: purchase order to which the order lines belong - :param list(browse_record) order_lines: purchase order line records for which picking - and moves should be created. - :param int picking_id: optional ID of a stock picking to which the created stock moves - will be added. A new picking will be created if omitted. - :return: list of IDs of pickings used/created for the given order lines (usually just one) - """ - move_obj = self.pool.get('stock.move') - bom_obj = self.pool.get('mrp.bom') - picking_obj = self.pool.get('stock.picking') - product_obj = self.pool.get('product.product') - uom_obj = self.pool['product.uom'] - bom_order_lines = [] - normal_order_lines = order_lines[:] - for line in order_lines: - if not line.product_id: - continue - bom_id = bom_obj._bom_find( - cr, uid, line.product_id.id, line.product_uom.id) - if not bom_id: - continue - # extract lines with a phantom bom, they need - # a special handling (explode bom and create - # a move line per component) - bom = bom_obj.browse(cr, uid, bom_id, context=context) - if bom.type == 'phantom': - bom_order_lines.append((line, bom)) - normal_order_lines.remove(line) - - # if we have at least one line which have to be split - # we prepare the picking so we'll be able to bind it - # to the move lines - picking_id = False - if bom_order_lines: - picking_id = picking_obj.create( - cr, uid, self._prepare_order_picking(cr, uid, order, context=context)) - - new_move_ids = [] - for line, bom in bom_order_lines: - factor = uom_obj._compute_qty_obj(cr, uid, line.product_uom, line.product_qty, bom.product_uom) - bom_components = bom_obj.bom_split( - cr, uid, bom, factor) - - move_dest_id = None - for component in bom_components: - product = product_obj.browse( - cr, uid, component['product_id'], context=context) - # do not create a move for service products - if product.type in ('product', 'consu'): - # if at least one stockable product - # update the order line destination move - move_dest_id = line.move_dest_id - - vals = self._prepare_order_line_split_move( - cr, uid, - order, - line, - component, - picking_id, - context=context) - move_id = move_obj.create(cr, uid, vals, context=context) - new_move_ids.append(move_id) - if move_dest_id: - line.move_dest_id.write({'location_id': move_dest_id.location_id.id}) - - move_obj.action_confirm(cr, uid, new_move_ids) - move_obj.force_assign(cr, uid, new_move_ids) - return super(purchase_order, self)._create_pickings( - cr, uid, order, normal_order_lines, picking_id=picking_id, context=context) - diff --git a/purchase_bom_split/tests/test_purchase_bom_split.yml b/purchase_bom_split/tests/test_purchase_bom_split.yml deleted file mode 100644 index 8674e4b..0000000 --- a/purchase_bom_split/tests/test_purchase_bom_split.yml +++ /dev/null @@ -1,26 +0,0 @@ -- - I create a purchase order for 24 bom_split.product1 -- - !record {model: purchase.order, id: purchase_order_test1}: - partner_id: base.res_partner_2 - order_line: - - product_id: bom_split.product1 - price_unit: 11 - product_qty: 24 - product_uom: product.product_uom_unit -- - I confirm the purchase order -- - !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_test1} -- - I check the picking created for the purchase order -- - !assert {model: purchase.order, id: purchase_order_test1}: - - len(picking_ids) == 1 - - len(picking_ids[0].move_lines) == 2 - - picking_ids[0].move_lines[0].product_id.id == ref('bom_split.product2') - - picking_ids[0].move_lines[0].product_uom.id == ref('product.product_uom_dozen') - - picking_ids[0].move_lines[0].product_qty == 2 - - picking_ids[0].move_lines[1].product_id.id == ref('bom_split.product3') - - picking_ids[0].move_lines[1].product_uom.id == ref('product.product_uom_unit') - - picking_ids[0].move_lines[1].product_qty == 96 diff --git a/sale_bom_split/__init__.py b/sale_bom_split/__init__.py deleted file mode 100644 index 12c88b6..0000000 --- a/sale_bom_split/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2010-2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import sale - diff --git a/sale_bom_split/__openerp__.py b/sale_bom_split/__openerp__.py deleted file mode 100644 index afaa7e6..0000000 --- a/sale_bom_split/__openerp__.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2010-2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Sales BoMs Split', - 'version': '1.0.1', - 'category': 'Generic Modules/Sales & Purchases', - 'description': """ - This module splits the products into the related packing according to their - Bill of Materials when validating a sale order. - - BoM example structure: - - Product A (fantom) - Product B (normal) - Product B1 (normal) - Product B2 (normal) - Product C (fantom) - Product C1 (normal) - Product C2 (normal) - - With this module, a sale order of Product A will result in a packing of : - - Product B - Product C1 - Product C2 - - Without this module, it would result in a packing with : - - Product A - - """, - 'author': "Camptocamp,Odoo Community Association (OCA)", - 'website': 'http://www.camptocamp.com', - 'license': 'AGPL-3', - 'depends': ['sale', 'bom_split'], - 'data': [], - 'demo': [], - 'test': ['tests/test_sale_bom_split.yml', - ], - 'installable': False, - 'auto_install': False, -} - diff --git a/sale_bom_split/sale.py b/sale_bom_split/sale.py deleted file mode 100644 index c20c1e9..0000000 --- a/sale_bom_split/sale.py +++ /dev/null @@ -1,161 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp import netsvc -from openerp.osv.orm import Model - - -class sale_order(Model): - - _inherit = "sale.order" - - def _prepare_order_line_split_procurement(self, cr, uid, order, base_line, - component, move_id, date_planned, context=None): - vals = super(sale_order, self)._prepare_order_line_procurement( - cr, uid, order, base_line, move_id, date_planned, context=context) - vals.update({ - 'name': ("SO: %s" % component['name'])[:250], - 'product_id': component['product_id'], - 'product_qty': component['product_qty'], - 'product_uom': component['product_uom'], - 'product_uos_qty': component['product_qty'], - 'product_uos': component['product_uos'] - }) - return vals - - def _prepare_order_line_split_move(self, cr, uid, order, base_line, component, picking_id, date_planned, context=None): - vals = super(sale_order, self)._prepare_order_line_move( - cr, uid, order, base_line, picking_id, date_planned, context=context) - vals.update({ - 'name': ("SO: %s" % component['name'])[:250], - 'product_id': component['product_id'], - 'product_qty': component['product_qty'], - 'product_uom': component['product_uom'], - 'product_uos_qty': component['product_qty'], - 'product_uos': component['product_uos'] - }) - return vals - - def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None): - """Create the required procurements to supply sale order lines, also connecting - the procurements to appropriate stock moves in order to bring the goods to the - sale order's requested location. - - If ``picking_id`` is provided, the stock moves will be added to it, otherwise - a standard outgoing picking will be created to wrap the stock moves, as returned - by :meth:`~._prepare_order_picking`. - - Modules that wish to customize the procurements or partition the stock moves over - multiple stock pickings may override this method and call ``super()`` with - different subsets of ``order_lines`` and/or preset ``picking_id`` values. - - Inherited in order to explode BoMs in many move lines in the picking. - - :param browse_record order: sale order to which the order lines belong - :param list(browse_record) order_lines: sale order line records to procure - :param int picking_id: optional ID of a stock picking to which the created stock moves - will be added. A new picking will be created if ommitted. - :return: True - """ - move_obj = self.pool.get('stock.move') - bom_obj = self.pool.get('mrp.bom') - picking_obj = self.pool.get('stock.picking') - product_obj = self.pool.get('product.product') - procurement_obj = self.pool.get('procurement.order') - uom_obj = self.pool['product.uom'] - - bom_order_lines = [] - normal_order_lines = order_lines[:] - for line in order_lines: - if line.state == 'done': - continue - if not line.product_id: - continue - bom_id = bom_obj._bom_find( - cr, uid, line.product_id.id, line.product_uom.id) - if not bom_id: - continue - # extract lines with a phantom bom, they need - # a special handling (explode bom and create - # a move line per component) - bom = bom_obj.browse(cr, uid, bom_id, context=context) - if bom.type == 'phantom': - bom_order_lines.append((line, bom)) - normal_order_lines.remove(line) - - # if we have at least one line which have to be split - # we prepare the picking so we'll be able to bind it - # to the move lines - if bom_order_lines and not picking_id: - picking_id = picking_obj.create( - cr, uid, self._prepare_order_picking(cr, uid, order, context=context)) - - proc_ids = [] - for line, bom in bom_order_lines: - factor = uom_obj._compute_qty_obj(cr, uid, line.product_uom, line.product_uom_qty, bom.product_uom) - bom_components = bom_obj.bom_split( - cr, uid, bom, factor) - - - date_planned = self._get_date_planned( - cr, uid, order, line, order.date_order, context=context) - - first_component = None - for component in bom_components: - product = product_obj.browse( - cr, uid, component['product_id'], context=context) - # do not create a move for service products - move_id = False - if product.type in ('product', 'consu'): - vals = self._prepare_order_line_split_move( - cr, uid, - order, - line, - component, - picking_id, - date_planned, - context=context) - move_id = move_obj.create(cr, uid, vals, context=context) - - proc_vals = self._prepare_order_line_split_procurement( - cr, uid, order, line, component, move_id, date_planned, context=context) - proc_id = procurement_obj.create( - cr, uid, proc_vals, context=context) - - proc_ids.append(proc_id) - if first_component is None: - first_component = (move_id, proc_id) - - # The sale order line could be linked only - # to 1 procurement, so we link it with - # the first procurement. - line.write({'procurement_id': first_component[1]}) - self.ship_recreate(cr, uid, order, line, first_component[0], first_component[1]) - - res = super(sale_order, self)._create_pickings_and_procurements( - cr, uid, order, normal_order_lines, picking_id=picking_id, context=context) - - wf_service = netsvc.LocalService("workflow") - for proc_id in proc_ids: - wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr) - - return res - diff --git a/sale_bom_split/tests/test_sale_bom_split.yml b/sale_bom_split/tests/test_sale_bom_split.yml deleted file mode 100644 index a2fa4e4..0000000 --- a/sale_bom_split/tests/test_sale_bom_split.yml +++ /dev/null @@ -1,25 +0,0 @@ -- - I create a sale order for 24 bom_split.product1 -- - !record {model: sale.order, id: sale_order_test1}: - partner_id: base.res_partner_2 - order_line: - - product_id: bom_split.product1 - product_uom_qty: 24 - product_uom: product.product_uom_unit -- - I confirm the sale order -- - !workflow {model: sale.order, action: order_confirm, ref: sale_order_test1} -- - I check the picking created for the sale order -- - !assert {model: sale.order, id: sale_order_test1}: - - len(picking_ids) == 1 - - len(picking_ids[0].move_lines) == 2 - - picking_ids[0].move_lines[0].product_id.id == ref('bom_split.product2') - - picking_ids[0].move_lines[0].product_uom.id == ref('product.product_uom_dozen') - - picking_ids[0].move_lines[0].product_qty == 2 - - picking_ids[0].move_lines[1].product_id.id == ref('bom_split.product3') - - picking_ids[0].move_lines[1].product_uom.id == ref('product.product_uom_unit') - - picking_ids[0].move_lines[1].product_qty == 96