[FIX] mrp: fixed phantom bom

lp bug: https://launchpad.net/bugs/1278891 fixed

bzr revid: qdp-launchpad@openerp.com-20140214150330-an2my0n8vjdf38vx
This commit is contained in:
Quentin (OpenERP) 2014-02-14 16:03:30 +01:00
commit 4ad9811a90
12 changed files with 218 additions and 95 deletions

View File

@ -103,7 +103,7 @@
I configure the product with required accounts, and cost method = standard
-
!python {model: product.product}: |
self.write(cr, uid, [ref('product.product_product_3')], {'list_price': 20.00,'standard_price': 9,'categ_id': ref('product.product_category_4'),'valuation': 'real_time',
self.write(cr, uid, [ref('product.product_product_10')], {'list_price': 20.00,'standard_price': 9,'categ_id': ref('product.product_category_4'),'valuation': 'real_time',
'property_account_income': ref('account_anglo_income'),'property_account_expense': ref('account_anglo_cogs'),
'property_account_creditor_price_difference': ref('account_anglo_price_difference'),'property_stock_account_input': ref('account_anglo_stock_input'),
'property_stock_account_output': ref('account_anglo_stock_output'), 'cost_method': 'standard'})
@ -115,7 +115,7 @@
location_id: stock.stock_location_stock
pricelist_id: 1
order_line:
- product_id: product.product_product_3
- product_id: product.product_product_10
product_qty: 1
price_unit: 10
date_planned: !eval "'%s' % (time.strftime('%Y-%m-%d'))"
@ -194,7 +194,7 @@
move_lines:
- company_id: base.main_company
location_id: stock.stock_location_stock
product_id: product.product_product_3
product_id: product.product_product_10
product_uom_qty: 1.0
product_uom: product.product_uom_unit
location_dest_id: stock.stock_location_customers

View File

@ -1035,11 +1035,12 @@ class mrp_production(osv.osv):
'location_dest_id': destination_location_id,
'move_dest_id': production.move_prod_id.id,
'company_id': production.company_id.id,
'production_id': production.id,
}
move_id = stock_move.create(cr, uid, data, context=context)
stock_move.action_confirm(cr, uid, [move_id], context=context)
production.write({'move_created_ids': [(6, 0, [move_id])]}, context=context)
return move_id
#a phantom bom cannot be used in mrp order so it's ok to assume the list returned by action_confirm
#is 1 element long, so we can take the first.
return stock_move.action_confirm(cr, uid, [move_id], context=context)[0]
def _make_production_consume_line(self, cr, uid, production_line, parent_move_id, source_location_id=False, context=None):
stock_move = self.pool.get('stock.move')
@ -1062,10 +1063,10 @@ class mrp_production(osv.osv):
'location_dest_id': destination_location_id,
'company_id': production.company_id.id,
'procure_method': 'make_to_order',
'raw_material_production_id': production.id,
})
stock_move.action_confirm(cr, uid, [move_id], context=context)
production.write({'move_lines': [(4, move_id)]}, context=context)
return move_id
return True
def action_confirm(self, cr, uid, ids, context=None):
""" Confirms production order.

View File

@ -687,7 +687,7 @@
</div>
<group>
<group>
<field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False)]" class="oe_inline" context='{"default_supply_method":"produce", "default_type": "product"}'/>
<field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False),('bom_ids.type','!=','phantom')]" class="oe_inline" context='{"default_type": "product"}'/>
<label for="product_qty"/>
<div>
<field name="product_qty" class="oe_inline" on_change="product_id_change(product_id, product_qty)"/>

View File

@ -22,7 +22,9 @@
from openerp.osv import fields
from openerp.osv import osv
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
import time
class StockMove(osv.osv):
_inherit = 'stock.move'
@ -33,6 +35,12 @@ class StockMove(osv.osv):
'consumed_for': fields.many2one('stock.move', 'Consumed for', help='Technical field used to make the traceability of produced products'),
}
def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default['production_id'] = False
return super(StockMove, self).copy(cr, uid, id, default, context=context)
def check_tracking(self, cr, uid, move, lot_id, context=None):
super(StockMove, self).check_tracking(cr, uid, move, lot_id, context=context)
if move.product_id.track_production and (move.location_id.usage == 'production' or move.location_dest_id.usage == 'production') and not lot_id:
@ -40,6 +48,20 @@ class StockMove(osv.osv):
if move.raw_material_production_id and move.location_dest_id.usage == 'production' and move.raw_material_production_id.product_id.track_production and not move.consumed_for:
raise osv.except_osv(_('Warning!'), _("Because the product %s requires it, you must assign a serial number to your raw material %s to proceed further in your production. Please use the 'Produce' button to do so.") % (move.raw_material_production_id.product_id.name, move.product_id.name))
def _check_phantom_bom(self, cr, uid, move, context=None):
"""check if product associated to move has a phantom bom
return list of ids of mrp.bom for that product """
user_company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
#doing the search as SUPERUSER because a user with the permission to write on a stock move should be able to explode it
#without giving him the right to read the boms.
return self.pool.get('mrp.bom').search(cr, SUPERUSER_ID, [
('product_id', '=', move.product_id.id),
('bom_id', '=', False),
('type', '=', 'phantom'),
'|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
('company_id', '=', user_company)], context=context)
def _action_explode(self, cr, uid, move, context=None):
""" Explodes pickings.
@param move: Stock moves
@ -49,59 +71,52 @@ class StockMove(osv.osv):
move_obj = self.pool.get('stock.move')
procurement_obj = self.pool.get('procurement.order')
product_obj = self.pool.get('product.product')
processed_ids = [move.id]
bis = bom_obj.search(cr, uid, [
('product_id', '=', move.product_id.id),
('bom_id', '=', False),
('type', '=', 'phantom')])
to_explode_again_ids = []
processed_ids = []
bis = self._check_phantom_bom(cr, uid, move, context=context)
if bis:
factor = move.product_qty
bom_point = bom_obj.browse(cr, uid, bis[0], context=context)
res = bom_obj._bom_explode(cr, uid, bom_point, factor, [])
bom_point = bom_obj.browse(cr, SUPERUSER_ID, bis[0], context=context)
res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, factor, [])
state = 'confirmed'
if move.state == 'assigned':
state = 'assigned'
for line in res[0]:
valdef = {
'picking_id': move.picking_id.id,
'picking_id': move.picking_id.id if move.picking_id else False,
'product_id': line['product_id'],
'product_uom': line['product_uom'],
'product_qty': line['product_qty'],
'product_uos': line['product_uos'],
'product_uos_qty': line['product_uos_qty'],
'move_dest_id': move.id,
'state': state,
'name': line['name'],
'procurements': [],
}
mid = move_obj.copy(cr, uid, move.id, default=valdef)
processed_ids.append(mid)
prodobj = product_obj.browse(cr, uid, line['product_id'], context=context)
proc_id = procurement_obj.create(cr, uid, {
'name': (move.picking_id.origin or ''),
'origin': (move.picking_id.origin or ''),
'date_planned': move.date,
'product_id': line['product_id'],
'product_qty': line['product_qty'],
'product_uom': line['product_uom'],
'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False,
'product_uos': line['product_uos'],
'location_id': move.location_id.id,
'procure_method': prodobj.procure_method,
'move_id': mid,
})
procurement_obj.signal_button_confirm(cr, uid, [proc_id])
to_explode_again_ids.append(mid)
move_obj.write(cr, uid, [move.id], {
'location_dest_id': move.location_id.id, # dummy move for the kit
'picking_id': False,
'state': 'confirmed'
})
procurement_ids = procurement_obj.search(cr, uid, [('move_id', '=', move.id)], context)
procurement_obj.signal_button_confirm(cr, uid, procurement_ids)
procurement_obj.signal_button_wait_done(cr, uid, procurement_ids)
return processed_ids
#delete the move with original product which is not relevant anymore
move_obj.unlink(cr, SUPERUSER_ID, [move.id], context=context)
#check if new moves needs to be exploded
if to_explode_again_ids:
for new_move in self.browse(cr, uid, to_explode_again_ids, context=context):
processed_ids.extend(self._action_explode(cr, uid, new_move, context=context))
#return list of newly created move or the move id otherwise
return processed_ids or [move.id]
def action_confirm(self, cr, uid, ids, context=None):
move_ids = []
for move in self.browse(cr, uid, ids, context=context):
#in order to explode a move, we must have a picking_type_id on that move because otherwise the move
#won't be assigned to a picking and it would be weird to explode a move into several if they aren't
#all grouped in the same picking.
if move.picking_type_id:
move_ids.extend(self._action_explode(cr, uid, move, context=context))
else:
move_ids.append(move.id)
#we go further with the list of ids potentially changed by action_explode
return super(StockMove, self).action_confirm(cr, uid, move_ids, context=context)
def action_consume(self, cr, uid, ids, product_qty, location_id=False, restrict_lot_id=False, restrict_partner_id=False,
consumed_for=False, context=None):
@ -121,9 +136,15 @@ class StockMove(osv.osv):
if product_qty <= 0:
raise osv.except_osv(_('Warning!'), _('Please provide proper quantity.'))
#because of the action_confirm that can create extra moves in case of phantom bom, we need to make 2 loops
ids2 = []
for move in self.browse(cr, uid, ids, context=context):
if move.state == 'draft':
self.action_confirm(cr, uid, [move.id], context=context)
ids2.extend(self.action_confirm(cr, uid, [move.id], context=context))
else:
ids2.append(move.id)
for move in self.browse(cr, uid, ids2, context=context):
move_qty = move.product_qty
uom_qty = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, product_qty, move.product_uom.id)
if move_qty <= 0:
@ -182,22 +203,6 @@ class StockMove(osv.osv):
workflow.trg_trigger(uid, 'stock.move', move.id, cr)
return res
class StockPicking(osv.osv):
_inherit = 'stock.picking'
#
# Explode picking by replacing phantom BoMs
#
def action_explode(self, cr, uid, move_ids, *args):
"""Explodes moves by expanding kit components"""
move_obj = self.pool.get('stock.move')
todo = move_ids[:]
for move in move_obj.browse(cr, uid, move_ids):
todo.extend(move_obj._action_explode(cr, uid, move))
return list(set(todo))
class stock_warehouse(osv.osv):
_inherit = 'stock.warehouse'
_columns = {

View File

@ -250,6 +250,7 @@ class procurement_order(osv.osv):
def _run(self, cr, uid, procurement, context=None):
'''This method implements the resolution of the given procurement
:param procurement: browse record
:returns: True if the resolution of the procurement was a success, False otherwise to set it in exception
'''
return True

View File

@ -751,7 +751,7 @@ class purchase_order(osv.osv):
move = stock_move.create(cr, uid, vals, context=context)
todo_moves.append(move)
stock_move.action_confirm(cr, uid, todo_moves)
todo_moves = stock_move.action_confirm(cr, uid, todo_moves)
stock_move.force_assign(cr, uid, todo_moves)
def test_moves_done(self, cr, uid, ids, context=None):

View File

@ -44,7 +44,9 @@
I add the routes manufacture and mto to the product
-
!python {model: product.product, id: scheduler_product}: |
self.write(cr, uid, [ref("product_product_slidermobile0")], {"route_ids": [(4, ref("mrp.route_warehouse0_manufacture")), (4, ref("stock.route_warehouse0_mto"))]})
route_warehouse0_manufacture = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).manufacture_pull_id.route_id.id
route_warehouse0_mto = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).mto_pull_id.route_id.id
self.write(cr, uid, ref('product_product_slidermobile0'), { 'route_ids': [(6, 0, [route_warehouse0_mto,route_warehouse0_manufacture])]}, context=context)
-
I create a Bill of Material record for Slider Mobile
-

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_move_explode
checks = [
test_move_explode,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests import common
class TestMoveExplode(common.TransactionCase):
def setUp(self):
super(TestMoveExplode, self).setUp()
cr, uid = self.cr, self.uid
# Usefull models
self.ir_model_data = self.registry('ir.model.data')
self.sale_order_line = self.registry('sale.order.line')
self.sale_order = self.registry('sale.order')
self.mrp_bom = self.registry('mrp.bom')
#product that has a phantom bom
self.product_bom_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_4')[1]
#bom with that product
self.bom_id = self.ir_model_data.get_object_reference(cr, uid, 'mrp', 'mrp_bom_24')[1]
#partner agrolait
self.partner_id = self.ir_model_data.get_object_reference(cr, uid, 'base', 'res_partner_1')[1]
def test_00_sale_move_explode(self):
"""check that when creating a sale order with a product that has a phantom BoM, move explode into content of the
BoM"""
cr, uid, context = self.cr, self.uid, {}
#create sale order with one sale order line containing product with a phantom bom
so_id = self.sale_order.create(cr, uid, vals={'partner_id': self.partner_id}, context=context)
self.sale_order_line.create(cr, uid, values={'order_id': so_id, 'product_id': self.product_bom_id, 'product_uom_qty': 1}, context=context)
#confirm sale order
self.sale_order.action_button_confirm(cr, uid, [so_id], context=context)
#get all move associated to that sale_order
browse_move_ids = self.sale_order.browse(cr, uid, so_id, context=context).picking_ids[0].move_lines
move_ids = [x.id for x in browse_move_ids]
#we should have same amount of move as the component in the phatom bom
bom = self.mrp_bom.browse(cr, uid, self.bom_id, context=context)
bom_component_length = self.mrp_bom._bom_explode(cr, uid, bom, 1, [])
self.assertEqual(len(move_ids), len(bom_component_length[0]))

View File

@ -3,6 +3,17 @@
-
!context
uid: 'res_sale_stock_salesman'
-
Create a new SO to be sure we don't have one with product that can explode in mrp
-
!record {model: sale.order, id: sale_order_service}:
partner_id: base.res_partner_18
partner_invoice_id: base.res_partner_18
partner_shipping_id: base.res_partner_18
user_id: base.user_root
pricelist_id: product.list0
warehouse_id: stock.warehouse0
order_policy: picking
-
Add SO line with service type product in SO to check flow which contain service type product in SO(BUG#1167330).
-
@ -12,27 +23,37 @@
product_uom_qty: 1.0
product_uom: 1
price_unit: 150.0
order_id: sale.sale_order_6
order_id: sale_order_service
-
Add a second SO line with a normal product
-
!record {model: sale.order.line, id: sale_order_2}:
name: 'Mouse Optical'
product_id: product.product_product_10
product_uom_qty: 1.0
product_uom: 1
price_unit: 150.0
order_id: sale_order_service
-
First I check the total amount of the Quotation before Approved.
-
!assert {model: sale.order, id: sale.sale_order_6, string: The amount of the Quotation is not correctly computed}:
!assert {model: sale.order, id: sale_order_service, string: The amount of the Quotation is not correctly computed}:
- sum([l.price_subtotal for l in order_line]) == amount_untaxed
-
I set an explicit invoicing partner that is different from the main SO Customer
-
!python {model: sale.order, id: sale.sale_order_6}: |
order = self.browse(cr, uid, ref("sale.sale_order_6"))
!python {model: sale.order, id: sale_order_service}: |
order = self.browse(cr, uid, ref("sale_order_service"))
order.write({'partner_invoice_id': ref('base.res_partner_address_29')})
-
I confirm the quotation with Invoice based on deliveries policy.
-
!workflow {model: sale.order, action: order_confirm, ref: sale.sale_order_6}
!workflow {model: sale.order, action: order_confirm, ref: sale_order_service}
-
I check that invoice should not created before dispatch delivery.
-
!python {model: sale.order}: |
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.sale_order_6"))
order = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_service"))
assert order.state == 'progress', 'Order should be in inprogress.'
assert len(order.invoice_ids) == False, "Invoice should not created."
-
@ -42,7 +63,7 @@
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
order = self.browse(cr, uid, ref("sale.sale_order_6"))
order = self.browse(cr, uid, ref("sale_order_service"))
for order_line in order.order_line:
if order_line.product_id.type == 'product':
procurement = order_line.procurement_ids[0]
@ -74,7 +95,7 @@
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
sale_order = self.browse(cr, uid, ref("sale.sale_order_6"))
sale_order = self.browse(cr, uid, ref("sale_order_service"))
assert sale_order.picking_ids, "Delivery order is not created."
for picking in sale_order.picking_ids:
assert picking.state == "auto" or "confirmed", "Delivery order should be in 'Waitting Availability' state."
@ -103,7 +124,7 @@
Now, I dispatch delivery order.
-
!python {model: stock.picking}: |
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.sale_order_6"))
order = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_service"))
for pick in order.picking_ids:
data = pick.force_assign()
if data == True:
@ -117,7 +138,7 @@
I check sale order to verify shipment.
-
!python {model: sale.order}: |
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.sale_order_6"))
order = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_service"))
assert order.shipped == True, "Sale order is not Delivered."
#assert order.state == 'progress', 'Order should be in inprogress.'
assert len(order.invoice_ids) == False, "Invoice should not created on dispatch delivery order."
@ -126,7 +147,7 @@
-
!python {model: stock.invoice.onshipping}: |
sale = self.pool.get('sale.order')
sale_order = sale.browse(cr, uid, ref("sale.sale_order_6"))
sale_order = sale.browse(cr, uid, ref("sale_order_service"))
ship_ids = [x.id for x in sale_order.picking_ids]
wiz_id = self.create(cr, uid, {'journal_id': ref('account.sales_journal')},
{'active_ids': ship_ids, 'active_model': 'stock.picking'})
@ -135,7 +156,7 @@
I check the invoice details after dispatched delivery.
-
!python {model: sale.order}: |
order = self.browse(cr, uid, ref("sale.sale_order_6"))
order = self.browse(cr, uid, ref("sale_order_service"))
assert order.invoice_ids, "Invoice is not created."
ac = order.partner_invoice_id.property_account_receivable.id
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', order.company_id.id)])
@ -166,7 +187,7 @@
I open the Invoice.
-
!python {model: sale.order}: |
so = self.browse(cr, uid, ref("sale.sale_order_6"))
so = self.browse(cr, uid, ref("sale_order_service"))
account_invoice_obj = self.pool.get('account.invoice')
for invoice in so.invoice_ids:
account_invoice_obj.signal_invoice_open(cr, uid, [invoice.id])
@ -175,7 +196,7 @@
-
!python {model: account.invoice}: |
sale_order = self.pool.get('sale.order')
order = sale_order.browse(cr, uid, ref("sale.sale_order_6"))
order = sale_order.browse(cr, uid, ref("sale_order_service"))
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash'), ('company_id', '=', order.company_id.id)], limit=1)
for invoice in order.invoice_ids:
invoice.pay_and_reconcile(
@ -192,7 +213,7 @@
I check the order after paid invoice.
-
!python {model: sale.order}: |
order = self.browse(cr, uid, ref("sale.sale_order_6"))
order = self.browse(cr, uid, ref("sale_order_service"))
assert order.invoiced == True, "Sale order is not invoiced."
assert order.invoiced_rate == 100, "Invoiced progress is not 100%."
assert order.state == 'done', 'Order should be in closed.'
@ -203,7 +224,7 @@
import os
import openerp.report
from openerp import tools
data, format = openerp.report.render_report(cr, uid, [ref('sale.sale_order_6')], 'sale.order', {}, {})
data, format = openerp.report.render_report(cr, uid, [ref('sale_order_service')], 'sale.order', {}, {})
if tools.config['test_report_directory']:
file(os.path.join(tools.config['test_report_directory'], 'sale-sale_order.'+format), 'wb+').write(data)

View File

@ -217,7 +217,7 @@ class procurement_order(osv.osv):
move_dict = self._run_move_create(cr, uid, procurement, context=context)
move_id = move_obj.create(cr, uid, move_dict, context=context)
move_obj.action_confirm(cr, uid, [move_id], context=context)
return move_id
return True
return super(procurement_order, self)._run(cr, uid, procurement, context)
def _check(self, cr, uid, procurement, context=None):

View File

@ -843,6 +843,7 @@ class stock_picking(osv.osv):
for pick in self.browse(cr, uid, ids, context=context):
if pick.state == 'draft':
self.action_confirm(cr, uid, [pick.id], context=context)
pick.refresh()
#skip the moves that don't need to be checked
move_ids = [x.id for x in pick.move_lines if x.state not in ('draft', 'cancel', 'done')]
if not move_ids:
@ -877,9 +878,7 @@ class stock_picking(osv.osv):
todo = []
for move in pick.move_lines:
if move.state == 'draft':
self.pool.get('stock.move').action_confirm(cr, uid, [move.id],
context=context)
todo.append(move.id)
todo.extend(self.pool.get('stock.move').action_confirm(cr, uid, [move.id], context=context))
elif move.state in ('assigned', 'confirmed'):
todo.append(move.id)
if len(todo):
@ -1782,6 +1781,8 @@ class stock_move(osv.osv):
""" Confirms stock move or put it in waiting if it's linked to another move.
@return: List of ids.
"""
if isinstance(ids, (int, long)):
ids = [ids]
states = {
'confirmed': [],
'waiting': []
@ -1803,7 +1804,7 @@ class stock_move(osv.osv):
self._create_procurement(cr, uid, move, context=context)
moves = self.browse(cr, uid, ids, context=context)
self._push_apply(cr, uid, moves, context=context)
return True
return ids
def force_assign(self, cr, uid, ids, context=None):
""" Changes the state to assigned.
@ -1911,7 +1912,7 @@ class stock_move(osv.osv):
pack_op_obj = self.pool.get("stock.pack.operation")
todo = [move.id for move in self.browse(cr, uid, ids, context=context) if move.state == "draft"]
if todo:
self.action_confirm(cr, uid, todo, context=context)
ids = self.action_confirm(cr, uid, todo, context=context)
pickings = set()
procurement_ids = []
#Search operations that are linked to the moves
@ -2044,7 +2045,16 @@ class stock_move(osv.osv):
:param move: browse record
:param qty: float. quantity to split (given in product UoM)
:param context: dictionay. can contains the special key 'source_location_id' in order to force the source location when copying the move
returns the ID of the backorder move created
"""
if move.state in ('done', 'cancel'):
raise osv.except_osv(_('Error'), _('You cannot split a move done'))
if move.state == 'draft':
#we restrict the split of a draft move because if not confirmed yet, it may be replaced by several other moves in
#case of phantom bom (with mrp module). And we don't want to deal with this complexity by copying the product that will explode.
raise osv.except_osv(_('Error'), _('You cannot split a draft move. It needs to be confirmed first.'))
if move.product_qty <= qty or qty == 0:
return move.id
@ -2054,9 +2064,6 @@ class stock_move(osv.osv):
uom_qty = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, qty, move.product_uom.id)
uos_qty = uom_qty * move.product_uos_qty / move.product_uom_qty
if move.state in ('done', 'cancel'):
raise osv.except_osv(_('Error'), _('You cannot split a move done'))
defaults = {
'product_uom_qty': uom_qty,
'product_uos_qty': uos_qty,
@ -2083,9 +2090,9 @@ class stock_move(osv.osv):
if move.move_dest_id and move.propagate:
new_move_prop = self.split(cr, uid, move.move_dest_id, qty, context=context)
self.write(cr, uid, [new_move], {'move_dest_id': new_move_prop}, context=context)
self.action_confirm(cr, uid, [new_move], context=context)
return new_move
#returning the first element of list returned by action_confirm is ok because we checked it wouldn't be exploded (and
#thus the result of action_confirm should always be a list of 1 element length)
return self.action_confirm(cr, uid, [new_move], context=context)[0]
class stock_inventory(osv.osv):
@ -3100,6 +3107,7 @@ class stock_location_path(osv.osv):
'propagate': True,
'active': True,
}
def _apply(self, cr, uid, rule, move, context=None):
move_obj = self.pool.get('stock.move')
newdate = (datetime.strptime(move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta.relativedelta(days=rule.delay or 0)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
@ -3115,7 +3123,6 @@ class stock_location_path(osv.osv):
if rule.location_dest_id.id != old_dest_location:
#call again push_apply to see if a next step is defined
move_obj._push_apply(cr, uid, [move], context=context)
return move.id
else:
move_id = move_obj.copy(cr, uid, move.id, {
'location_id': move.location_dest_id.id,
@ -3133,7 +3140,6 @@ class stock_location_path(osv.osv):
'move_dest_id': move_id,
})
move_obj.action_confirm(cr, uid, [move_id], context=None)
return move_id
class stock_move_putaway(osv.osv):
_name = 'stock.move.putaway'