[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:
commit
4ad9811a90
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)"/>
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
-
|
||||
|
|
|
@ -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:
|
|
@ -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]))
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue