[IMP] stock: stock partial move wizard
bzr revid: hmo@tinyerp.com-20100326145201-3viwi4gcsujnvfhd
This commit is contained in:
parent
d9dc66b994
commit
373b2701c5
|
@ -44,6 +44,7 @@ Thanks to the double entry management, the inventory controlling is powerful and
|
||||||
"stock_data.xml",
|
"stock_data.xml",
|
||||||
"wizard/stock_move_view.xml",
|
"wizard/stock_move_view.xml",
|
||||||
"wizard/stock_partial_picking_view.xml",
|
"wizard/stock_partial_picking_view.xml",
|
||||||
|
"wizard/stock_partial_move_view.xml",
|
||||||
"wizard/stock_inventory_set_stock_zero_view.xml",
|
"wizard/stock_inventory_set_stock_zero_view.xml",
|
||||||
"wizard/stock_fill_inventory_view.xml",
|
"wizard/stock_fill_inventory_view.xml",
|
||||||
"wizard/stock_invoice_onshipping_view.xml",
|
"wizard/stock_invoice_onshipping_view.xml",
|
||||||
|
|
|
@ -890,6 +890,8 @@ class stock_picking(osv.osv):
|
||||||
complete, too_many, too_few = [], [], []
|
complete, too_many, too_few = [], [], []
|
||||||
move_product_qty = {}
|
move_product_qty = {}
|
||||||
for move in pick.move_lines:
|
for move in pick.move_lines:
|
||||||
|
if move.state in ('done', 'cancel'):
|
||||||
|
continue
|
||||||
partial_data = partial_datas.get('move%s'%(move.id), False)
|
partial_data = partial_datas.get('move%s'%(move.id), False)
|
||||||
assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
|
assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
|
||||||
product_qty = partial_data.get('product_qty',0.0)
|
product_qty = partial_data.get('product_qty',0.0)
|
||||||
|
@ -1117,10 +1119,9 @@ class stock_delivery(osv.osv):
|
||||||
'partner_id': fields.many2one('res.partner', 'Partner', required=True),
|
'partner_id': fields.many2one('res.partner', 'Partner', required=True),
|
||||||
'address_id': fields.many2one('res.partner.address', 'Address', required=True),
|
'address_id': fields.many2one('res.partner.address', 'Address', required=True),
|
||||||
'move_delivered':fields.one2many('stock.move', 'delivered_id', 'Move Delivered'),
|
'move_delivered':fields.one2many('stock.move', 'delivered_id', 'Move Delivered'),
|
||||||
'picking_id': fields.many2one('stock.picking', 'Picking list', required=True),
|
'picking_id': fields.many2one('stock.picking', 'Picking list'),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stock_delivery()
|
stock_delivery()
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Move
|
# Move
|
||||||
|
@ -1788,7 +1789,134 @@ class stock_move(osv.osv):
|
||||||
self.write(cr, uid, [move.id], update_val)
|
self.write(cr, uid, [move.id], update_val)
|
||||||
|
|
||||||
self.action_done(cr, uid, res)
|
self.action_done(cr, uid, res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def do_partial(self, cr, uid, ids, partial_datas, context={}):
|
||||||
|
"""
|
||||||
|
@ partial_datas : dict. contain details of partial picking
|
||||||
|
like partner_id, address_id, delivery_date, delivery moves with product_id, product_qty, uom
|
||||||
|
"""
|
||||||
|
res = {}
|
||||||
|
picking_obj = self.pool.get('stock.picking')
|
||||||
|
delivery_obj = self.pool.get('stock.delivery')
|
||||||
|
product_obj = self.pool.get('product.product')
|
||||||
|
currency_obj = self.pool.get('res.currency')
|
||||||
|
users_obj = self.pool.get('res.users')
|
||||||
|
uom_obj = self.pool.get('product.uom')
|
||||||
|
price_type_obj = self.pool.get('product.price.type')
|
||||||
|
sequence_obj = self.pool.get('ir.sequence')
|
||||||
|
wf_service = netsvc.LocalService("workflow")
|
||||||
|
partner_id = partial_datas.get('partner_id', False)
|
||||||
|
address_id = partial_datas.get('address_id', False)
|
||||||
|
delivery_date = partial_datas.get('delivery_date', False)
|
||||||
|
|
||||||
|
new_moves = []
|
||||||
|
|
||||||
|
complete, too_many, too_few = [], [], []
|
||||||
|
move_product_qty = {}
|
||||||
|
for move in self.browse(cr, uid, ids, context=context):
|
||||||
|
if move.state in ('done', 'cancel'):
|
||||||
|
continue
|
||||||
|
partial_data = partial_datas.get('move%s'%(move.id), False)
|
||||||
|
assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
|
||||||
|
product_qty = partial_data.get('product_qty',0.0)
|
||||||
|
move_product_qty[move.id] = product_qty
|
||||||
|
product_uom = partial_data.get('product_uom',False)
|
||||||
|
product_price = partial_data.get('product_price',0.0)
|
||||||
|
product_currency = partial_data.get('product_currency',False)
|
||||||
|
if move.product_qty == product_qty:
|
||||||
|
complete.append(move)
|
||||||
|
elif move.product_qty > product_qty:
|
||||||
|
too_few.append(move)
|
||||||
|
else:
|
||||||
|
too_many.append(move)
|
||||||
|
|
||||||
|
# Average price computation
|
||||||
|
if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
|
||||||
|
product = product_obj.browse(cr, uid, move.product_id.id)
|
||||||
|
user = users_obj.browse(cr, uid, uid)
|
||||||
|
context['currency_id'] = move.company_id.currency_id.id
|
||||||
|
qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
|
||||||
|
pricetype = False
|
||||||
|
if user.company_id.property_valuation_price_type:
|
||||||
|
pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
|
||||||
|
if pricetype and qty > 0:
|
||||||
|
new_price = currency_obj.compute(cr, uid, product_currency,
|
||||||
|
user.company_id.currency_id.id, product_price)
|
||||||
|
new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
|
||||||
|
product.uom_id.id)
|
||||||
|
if product.qty_available <= 0:
|
||||||
|
new_std_price = new_price
|
||||||
|
else:
|
||||||
|
# Get the standard price
|
||||||
|
amount_unit = product.price_get(pricetype.field, context)[product.id]
|
||||||
|
new_std_price = ((amount_unit * product.qty_available)\
|
||||||
|
+ (new_price * qty))/(product.qty_available + qty)
|
||||||
|
|
||||||
|
# Write the field according to price type field
|
||||||
|
product_obj.write(cr, uid, [product.id],
|
||||||
|
{pricetype.field: new_std_price})
|
||||||
|
self.write(cr, uid, [move.id], {'price_unit': new_price})
|
||||||
|
|
||||||
|
for move in too_few:
|
||||||
|
product_qty = move_product_qty[move.id]
|
||||||
|
if product_qty != 0:
|
||||||
|
new_move = self.copy(cr, uid, move.id,
|
||||||
|
{
|
||||||
|
'product_qty' : product_qty,
|
||||||
|
'product_uos_qty': product_qty,
|
||||||
|
'picking_id' : move.picking_id.id,
|
||||||
|
'state': 'assigned',
|
||||||
|
'move_dest_id': False,
|
||||||
|
'price_unit': move.price_unit,
|
||||||
|
})
|
||||||
|
complete.append(self.browse(cr, uid, new_move))
|
||||||
|
self.write(cr, uid, move.id,
|
||||||
|
{
|
||||||
|
'product_qty' : move.product_qty - product_qty,
|
||||||
|
'product_uos_qty':move.product_qty - product_qty,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
for move in too_many:
|
||||||
|
self.write(cr, uid, move.id,
|
||||||
|
{
|
||||||
|
'product_qty': product_qty,
|
||||||
|
'product_uos_qty': product_qty
|
||||||
|
})
|
||||||
|
complete.append(move)
|
||||||
|
|
||||||
|
for move in complete:
|
||||||
|
self.action_done(cr, uid, [move.id])
|
||||||
|
|
||||||
|
# TOCHECK : Done picking if all moves are done
|
||||||
|
cr.execute("""
|
||||||
|
SELECT move.id FROM stock_picking pick
|
||||||
|
RIGHT JOIN stock_move move ON move.picking_id = pick.id AND move.state = %s
|
||||||
|
WHERE pick.id = %s""",
|
||||||
|
('done', move.picking_id.id))
|
||||||
|
res = cr.fetchall()
|
||||||
|
if len(res) == len(move.picking_id.move_lines):
|
||||||
|
picking_obj.action_move(cr, uid, [move.picking_id.id])
|
||||||
|
wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
|
||||||
|
|
||||||
|
ref = {}
|
||||||
|
done_move_ids = []
|
||||||
|
for move in complete:
|
||||||
|
done_move_ids.append(move.id)
|
||||||
|
if move.picking_id.id not in ref:
|
||||||
|
delivery_id = delivery_obj.create(cr, uid, {
|
||||||
|
'partner_id': partner_id,
|
||||||
|
'address_id': address_id,
|
||||||
|
'date': delivery_date,
|
||||||
|
'name' : move.picking_id.name,
|
||||||
|
'picking_id': move.picking_id.id
|
||||||
|
}, context=context)
|
||||||
|
ref[move.picking_id.id] = delivery_id
|
||||||
|
delivery_obj.write(cr, uid, ref[move.picking_id.id], {
|
||||||
|
'move_delivered' : [(4,move.id)]
|
||||||
|
})
|
||||||
|
return done_move_ids
|
||||||
|
|
||||||
stock_move()
|
stock_move()
|
||||||
|
|
||||||
|
|
|
@ -1488,7 +1488,7 @@
|
||||||
<field name="date_planned"/>
|
<field name="date_planned"/>
|
||||||
<field name="backorder_id"/>
|
<field name="backorder_id"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<button name="%(partial_move)d" string="Partial" type="action" states="assigned" icon="gtk-justify-fill"/>
|
<button name="%(action_partial_move)d" string="Partial" type="action" states="assigned" icon="gtk-justify-fill"/>
|
||||||
<button name="action_cancel" states="assigned,confirmed" string="Cancel" type="object" icon="gtk-cancel"/>
|
<button name="action_cancel" states="assigned,confirmed" string="Cancel" type="object" icon="gtk-cancel"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
@ -1534,7 +1534,7 @@
|
||||||
<button name="action_confirm" states="draft" string="Confirm" type="object" icon="gtk-apply"/>
|
<button name="action_confirm" states="draft" string="Confirm" type="object" icon="gtk-apply"/>
|
||||||
<button name="action_assign" states="confirmed" string="Set Available" type="object" icon="gtk-yes"/>
|
<button name="action_assign" states="confirmed" string="Set Available" type="object" icon="gtk-yes"/>
|
||||||
<button name="action_cancel" states="assigned,confirmed" string="Cancel" type="object" icon="gtk-cancel"/>
|
<button name="action_cancel" states="assigned,confirmed" string="Cancel" type="object" icon="gtk-cancel"/>
|
||||||
<button name="%(partial_move)d" states="assigned" string="Partial" type="action" icon="gtk-justify-fill"/>
|
<button name="%(action_partial_move)d" states="assigned" string="Partial" type="action" icon="gtk-justify-fill"/>
|
||||||
<button name="action_done" states="assigned" string="Done" type="object" icon="gtk-jump-to"/>
|
<button name="action_done" states="assigned" string="Done" type="object" icon="gtk-jump-to"/>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
|
|
|
@ -29,14 +29,7 @@
|
||||||
menu="False"
|
menu="False"
|
||||||
keyword="client_action_multi"
|
keyword="client_action_multi"
|
||||||
name="stock.return.picking"
|
name="stock.return.picking"
|
||||||
string="Return picking"/>
|
string="Return picking"/>
|
||||||
|
|
||||||
<wizard
|
|
||||||
id="partial_move"
|
|
||||||
model="stock.move"
|
|
||||||
multi="True"
|
|
||||||
name="stock.partial_move"
|
|
||||||
string="Partial Move"/>
|
|
||||||
|
|
||||||
<wizard
|
<wizard
|
||||||
id="split_inventory_lots"
|
id="split_inventory_lots"
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
import stock_traceability
|
import stock_traceability
|
||||||
import stock_move
|
import stock_move
|
||||||
import stock_partial_picking
|
import stock_partial_picking
|
||||||
import wizard_partial_move
|
import stock_partial_move
|
||||||
import wizard_picking_make
|
import wizard_picking_make
|
||||||
import wizard_replacement
|
import wizard_replacement
|
||||||
import wizard_return
|
import wizard_return
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||||
|
#
|
||||||
|
# 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 osv import fields, osv
|
||||||
|
from service import web_services
|
||||||
|
from tools.translate import _
|
||||||
|
import netsvc
|
||||||
|
import pooler
|
||||||
|
import time
|
||||||
|
|
||||||
|
class stock_partial_move(osv.osv_memory):
|
||||||
|
_name = "stock.partial.move"
|
||||||
|
_description = "Partial Move"
|
||||||
|
_columns = {
|
||||||
|
'date': fields.datetime('Date', required=True),
|
||||||
|
'partner_id': fields.many2one('res.partner',string="Partner", required=True),
|
||||||
|
'address_id': fields.many2one('res.partner.address', 'Delivery Address', help="Address where goods are to be delivered", required=True),
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def view_init(self, cr, uid, fields_list, context=None):
|
||||||
|
res = super(stock_partial_move, self).view_init(cr, uid, fields_list, context=context)
|
||||||
|
move_obj = self.pool.get('stock.move')
|
||||||
|
if not context:
|
||||||
|
context={}
|
||||||
|
moveids = []
|
||||||
|
for m in move_obj.browse(cr, uid, context.get('active_ids', [])):
|
||||||
|
if m.state in ('done', 'cancel'):
|
||||||
|
continue
|
||||||
|
if 'move%s_product_id'%(m.id) not in self._columns:
|
||||||
|
self._columns['move%s_product_id'%(m.id)] = fields.many2one('product.product',string="Product")
|
||||||
|
if 'move%s_product_qty'%(m.id) not in self._columns:
|
||||||
|
self._columns['move%s_product_qty'%(m.id)] = fields.float("Quantity")
|
||||||
|
if 'move%s_product_uom'%(m.id) not in self._columns:
|
||||||
|
self._columns['move%s_product_uom'%(m.id)] = fields.many2one('product.uom',string="Product UOM")
|
||||||
|
|
||||||
|
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
|
||||||
|
if 'move%s_product_price'%(m.id) not in self._columns:
|
||||||
|
self._columns['move%s_product_price'%(m.id)] = fields.float("Price")
|
||||||
|
if 'move%s_product_currency'%(m.id) not in self._columns:
|
||||||
|
self._columns['move%s_product_currency'%(m.id)] = fields.many2one('res.currency',string="Currency")
|
||||||
|
return res
|
||||||
|
|
||||||
|
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False,submenu=False):
|
||||||
|
result = super(stock_partial_move, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
|
||||||
|
move_obj = self.pool.get('stock.move')
|
||||||
|
move_ids = context.get('active_ids', False)
|
||||||
|
move_ids = move_obj.search(cr, uid, [('id','in',move_ids)])
|
||||||
|
_moves_arch_lst = """<form string="Deliver Products">
|
||||||
|
<separator colspan="4" string="Delivery Information"/>
|
||||||
|
<field name="date" colspan="4" />
|
||||||
|
<field name="partner_id"/>
|
||||||
|
<field name="address_id"/>
|
||||||
|
<newline/>
|
||||||
|
<separator colspan="4" string="Move Detail"/>
|
||||||
|
"""
|
||||||
|
_moves_fields = result['fields']
|
||||||
|
if move_ids and view_type in ['form']:
|
||||||
|
for m in move_obj.browse(cr, uid, move_ids, context):
|
||||||
|
if m.state in ('done', 'cancel'):
|
||||||
|
continue
|
||||||
|
_moves_fields.update({
|
||||||
|
'move%s_product_id'%(m.id) : {
|
||||||
|
'string': _('Product'),
|
||||||
|
'type' : 'many2one',
|
||||||
|
'relation': 'product.product',
|
||||||
|
'required' : True,
|
||||||
|
'readonly' : True,
|
||||||
|
},
|
||||||
|
'move%s_product_qty'%(m.id) : {
|
||||||
|
'string': _('Quantity'),
|
||||||
|
'type' : 'float',
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
'move%s_product_uom'%(m.id) : {
|
||||||
|
'string': _('Product UOM'),
|
||||||
|
'type' : 'many2one',
|
||||||
|
'relation': 'product.uom',
|
||||||
|
'required' : True,
|
||||||
|
'readonly' : True,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
_moves_arch_lst += """
|
||||||
|
<group colspan="4" col="10">
|
||||||
|
<field name="move%s_product_id" nolabel="1"/>
|
||||||
|
<field name="move%s_product_qty" string="Qty" />
|
||||||
|
<field name="move%s_product_uom" nolabel="1" />
|
||||||
|
"""%(m.id, m.id, m.id)
|
||||||
|
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
|
||||||
|
_moves_fields.update({
|
||||||
|
'move%s_product_price'%(m.id) : {
|
||||||
|
'string': _('Price'),
|
||||||
|
'type' : 'float',
|
||||||
|
},
|
||||||
|
'move%s_product_currency'%(m.id): {
|
||||||
|
'string': _('Currency'),
|
||||||
|
'type' : 'float',
|
||||||
|
'type' : 'many2one',
|
||||||
|
'relation': 'res.currency',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
_moves_arch_lst += """
|
||||||
|
<field name="move%s_product_price" />
|
||||||
|
<field name="move%s_product_currency" nolabel="1"/>
|
||||||
|
"""%(m.id, m.id)
|
||||||
|
_moves_arch_lst += """
|
||||||
|
</group>
|
||||||
|
"""
|
||||||
|
_moves_arch_lst += """
|
||||||
|
<separator string="" colspan="4" />
|
||||||
|
<label string="" colspan="2"/>
|
||||||
|
<group col="2" colspan="2">
|
||||||
|
<button icon='gtk-cancel' special="cancel"
|
||||||
|
string="_Cancel" />
|
||||||
|
<button name="do_partial" string="_Deliver"
|
||||||
|
colspan="1" type="object" icon="gtk-apply" />
|
||||||
|
</group>
|
||||||
|
</form>"""
|
||||||
|
result['arch'] = _moves_arch_lst
|
||||||
|
result['fields'] = _moves_fields
|
||||||
|
return result
|
||||||
|
|
||||||
|
def default_get(self, cr, uid, fields, context=None):
|
||||||
|
"""
|
||||||
|
To get default values for the object.
|
||||||
|
|
||||||
|
@param self: The object pointer.
|
||||||
|
@param cr: A database cursor
|
||||||
|
@param uid: ID of the user currently logged in
|
||||||
|
@param fields: List of fields for which we want default values
|
||||||
|
@param context: A standard dictionary
|
||||||
|
|
||||||
|
@return: A dictionary which of fields with values.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
res = super(stock_partial_move, self).default_get(cr, uid, fields, context=context)
|
||||||
|
move_obj = self.pool.get('stock.move')
|
||||||
|
if not context:
|
||||||
|
context={}
|
||||||
|
moveids = []
|
||||||
|
if 'date' in fields:
|
||||||
|
res.update({'date': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
for m in move_obj.browse(cr, uid, context.get('active_ids', [])):
|
||||||
|
if m.state in ('done', 'cancel'):
|
||||||
|
continue
|
||||||
|
if 'move%s_product_id'%(m.id) in fields:
|
||||||
|
res['move%s_product_id'%(m.id)] = m.product_id.id
|
||||||
|
if 'move%s_product_qty'%(m.id) in fields:
|
||||||
|
res['move%s_product_qty'%(m.id)] = m.product_qty
|
||||||
|
if 'move%s_product_uom'%(m.id) in fields:
|
||||||
|
res['move%s_product_uom'%(m.id)] = m.product_uom.id
|
||||||
|
|
||||||
|
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
|
||||||
|
price = 0
|
||||||
|
if hasattr(m, 'purchase_line_id') and m.purchase_line_id:
|
||||||
|
price = m.purchase_line_id.price_unit
|
||||||
|
|
||||||
|
currency = False
|
||||||
|
if hasattr(m.picking_id, 'purchase_id') and m.picking_id.purchase_id:
|
||||||
|
currency = m.picking_id.purchase_id.pricelist_id.currency_id.id
|
||||||
|
|
||||||
|
if 'move%s_product_price'%(m.id) in fields:
|
||||||
|
res['move%s_product_price'%(m.id)] = price
|
||||||
|
if 'move%s_product_currency'%(m.id) in fields:
|
||||||
|
res['move%s_product_currency'%(m.id)] = currency
|
||||||
|
return res
|
||||||
|
|
||||||
|
def do_partial(self, cr, uid, ids, context):
|
||||||
|
move_obj = self.pool.get('stock.move')
|
||||||
|
move_ids = context.get('active_ids', False)
|
||||||
|
partial = self.browse(cr, uid, ids[0], context)
|
||||||
|
partial_datas = {
|
||||||
|
'partner_id' : partial.partner_id and partial.partner_id.id or False,
|
||||||
|
'address_id' : partial.address_id and partial.address_id.id or False,
|
||||||
|
'delivery_date' : partial.date
|
||||||
|
}
|
||||||
|
for m in move_obj.browse(cr, uid, move_ids):
|
||||||
|
if m.state in ('done', 'cancel'):
|
||||||
|
continue
|
||||||
|
partial_datas['move%s'%(m.id)] = {
|
||||||
|
'product_id' : getattr(partial, 'move%s_product_id'%(m.id)).id,
|
||||||
|
'product_qty' : getattr(partial, 'move%s_product_qty'%(m.id)),
|
||||||
|
'product_uom' : getattr(partial, 'move%s_product_uom'%(m.id)).id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
|
||||||
|
partial_datas['move%s'%(m.id)].update({
|
||||||
|
'product_price' : getattr(partial, 'move%s_product_price'%(m.id)),
|
||||||
|
'product_currency': getattr(partial, 'move%s_product_currency'%(m.id)).id
|
||||||
|
})
|
||||||
|
|
||||||
|
res = move_obj.do_partial(cr, uid, move_ids, partial_datas, context=context)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
stock_partial_move()
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="view_stock_partial_move" model="ir.ui.view">
|
||||||
|
<field name="name">Delivery Product</field>
|
||||||
|
<field name="model">stock.partial.move</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Delivery Product">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<act_window name="Delivery Product"
|
||||||
|
res_model="stock.partial.move"
|
||||||
|
src_model="stock.move"
|
||||||
|
view_mode="form"
|
||||||
|
target="new"
|
||||||
|
key2="client_action_multi"
|
||||||
|
id="action_partial_move"/>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -62,11 +62,9 @@ class stock_partial_picking(osv.osv_memory):
|
||||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False,submenu=False):
|
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False,submenu=False):
|
||||||
result = super(stock_partial_picking, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
|
result = super(stock_partial_picking, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
|
||||||
pick_obj = self.pool.get('stock.picking')
|
pick_obj = self.pool.get('stock.picking')
|
||||||
picking_ids = context.get('active_ids', False)
|
picking_ids = context.get('active_ids', False)
|
||||||
if not picking_ids:
|
picking_ids = pick_obj.search(cr, uid, [('id', 'in', picking_ids)])
|
||||||
return result
|
_moves_arch_lst = """<form string="Deliver Products">
|
||||||
if view_type in ['form']:
|
|
||||||
_moves_arch_lst = """<form string="Deliver Products">
|
|
||||||
<separator colspan="4" string="Delivery Information"/>
|
<separator colspan="4" string="Delivery Information"/>
|
||||||
<field name="date" colspan="4" />
|
<field name="date" colspan="4" />
|
||||||
<field name="partner_id"/>
|
<field name="partner_id"/>
|
||||||
|
@ -74,7 +72,8 @@ class stock_partial_picking(osv.osv_memory):
|
||||||
<newline/>
|
<newline/>
|
||||||
<separator colspan="4" string="Move Detail"/>
|
<separator colspan="4" string="Move Detail"/>
|
||||||
"""
|
"""
|
||||||
_moves_fields = result['fields']
|
_moves_fields = result['fields']
|
||||||
|
if picking_ids and view_type in ['form']:
|
||||||
for pick in pick_obj.browse(cr, uid, picking_ids, context):
|
for pick in pick_obj.browse(cr, uid, picking_ids, context):
|
||||||
for m in pick.move_lines:
|
for m in pick.move_lines:
|
||||||
if m.state in ('done', 'cancel'):
|
if m.state in ('done', 'cancel'):
|
||||||
|
@ -127,18 +126,18 @@ class stock_partial_picking(osv.osv_memory):
|
||||||
_moves_arch_lst += """
|
_moves_arch_lst += """
|
||||||
</group>
|
</group>
|
||||||
"""
|
"""
|
||||||
_moves_arch_lst += """
|
_moves_arch_lst += """
|
||||||
<separator string="" colspan="4" />
|
<separator string="" colspan="4" />
|
||||||
<label string="" colspan="2"/>
|
<label string="" colspan="2"/>
|
||||||
<group col="2" colspan="2">
|
<group col="2" colspan="2">
|
||||||
<button icon='gtk-cancel' special="cancel"
|
<button icon='gtk-cancel' special="cancel"
|
||||||
string="_Cancel" />
|
string="_Cancel" />
|
||||||
<button name="do_partial" string="_Deliver"
|
<button name="do_partial" string="_Deliver"
|
||||||
colspan="1" type="object" icon="gtk-apply" />
|
colspan="1" type="object" icon="gtk-apply" />
|
||||||
</group>
|
</group>
|
||||||
</form>"""
|
</form>"""
|
||||||
result['arch'] = _moves_arch_lst
|
result['arch'] = _moves_arch_lst
|
||||||
result['fields'] = _moves_fields
|
result['fields'] = _moves_fields
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def default_get(self, cr, uid, fields, context=None):
|
def default_get(self, cr, uid, fields, context=None):
|
||||||
|
@ -160,13 +159,13 @@ class stock_partial_picking(osv.osv_memory):
|
||||||
if not context:
|
if not context:
|
||||||
context={}
|
context={}
|
||||||
moveids = []
|
moveids = []
|
||||||
|
if 'date' in fields:
|
||||||
|
res.update({'date': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
for pick in pick_obj.browse(cr, uid, context.get('active_ids', [])):
|
for pick in pick_obj.browse(cr, uid, context.get('active_ids', [])):
|
||||||
if 'partner_id' in fields:
|
if 'partner_id' in fields:
|
||||||
res.update({'partner_id': pick.address_id.partner_id.id})
|
res.update({'partner_id': pick.address_id.partner_id.id})
|
||||||
if 'address_id' in fields:
|
if 'address_id' in fields:
|
||||||
res.update({'address_id': pick.address_id.id})
|
res.update({'address_id': pick.address_id.id})
|
||||||
if 'date' in fields:
|
|
||||||
res.update({'date': pick.date})
|
|
||||||
for m in pick.move_lines:
|
for m in pick.move_lines:
|
||||||
if m.state in ('done', 'cancel'):
|
if m.state in ('done', 'cancel'):
|
||||||
continue
|
continue
|
||||||
|
@ -215,8 +214,7 @@ class stock_partial_picking(osv.osv_memory):
|
||||||
partial_datas['move%s'%(m.id)].update({
|
partial_datas['move%s'%(m.id)].update({
|
||||||
'product_price' : getattr(partial, 'move%s_product_price'%(m.id)),
|
'product_price' : getattr(partial, 'move%s_product_price'%(m.id)),
|
||||||
'product_currency': getattr(partial, 'move%s_product_currency'%(m.id)).id
|
'product_currency': getattr(partial, 'move%s_product_currency'%(m.id)).id
|
||||||
})
|
})
|
||||||
|
|
||||||
res = pick_obj.do_partial(cr, uid, picking_ids, partial_datas, context=context)
|
res = pick_obj.do_partial(cr, uid, picking_ids, partial_datas, context=context)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
@ -1,220 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
import time
|
|
||||||
import netsvc
|
|
||||||
from tools.misc import UpdateableStr, UpdateableDict
|
|
||||||
import pooler
|
|
||||||
|
|
||||||
import wizard
|
|
||||||
from osv import osv
|
|
||||||
import tools
|
|
||||||
from tools.translate import _
|
|
||||||
|
|
||||||
_moves_arch = UpdateableStr()
|
|
||||||
_moves_fields = UpdateableDict()
|
|
||||||
|
|
||||||
_moves_arch_end = '''<?xml version="1.0"?>
|
|
||||||
<form string="Picking result">
|
|
||||||
<label string="Move(s) have been successfully Done !" colspan="4"/>
|
|
||||||
</form>'''
|
|
||||||
|
|
||||||
def make_default(val):
|
|
||||||
def fct(uid, data, state):
|
|
||||||
return val
|
|
||||||
return fct
|
|
||||||
|
|
||||||
def _to_xml(s):
|
|
||||||
return (s or '').replace('&','&').replace('<','<').replace('>','>')
|
|
||||||
|
|
||||||
def _get_moves(self, cr, uid, data, context):
|
|
||||||
move_obj = pooler.get_pool(cr.dbname).get('stock.move')
|
|
||||||
move_lines = move_obj.browse(cr, uid, data['ids'], context)
|
|
||||||
res = {}
|
|
||||||
|
|
||||||
_moves_fields.clear()
|
|
||||||
_moves_arch_lst = ['<?xml version="1.0"?>', '<form string="Partial Stock Moves">']
|
|
||||||
|
|
||||||
for move in move_lines:
|
|
||||||
quantity = move.product_qty
|
|
||||||
if move.state != 'assigned':
|
|
||||||
quantity = 0
|
|
||||||
|
|
||||||
_moves_arch_lst.append('<field name="move%s" />' % (move.id,))
|
|
||||||
_moves_fields['move%s' % move.id] = {
|
|
||||||
'string': _to_xml(move.name),
|
|
||||||
'type' : 'float', 'required' : True, 'default' : make_default(quantity)}
|
|
||||||
|
|
||||||
if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
|
|
||||||
price = 0
|
|
||||||
if hasattr(move, 'purchase_line_id') and move.purchase_line_id:
|
|
||||||
price = move.purchase_line_id.price_unit
|
|
||||||
|
|
||||||
currency = 0
|
|
||||||
if hasattr(move.picking_id, 'purchase_id') and move.picking_id.purchase_id:
|
|
||||||
currency = move.picking_id.purchase_id.pricelist_id.currency_id.id
|
|
||||||
|
|
||||||
_moves_arch_lst.append('<group col="6"><field name="uom%s" nolabel="1"/>\
|
|
||||||
<field name="price%s"/>' % (move.id, move.id,))
|
|
||||||
|
|
||||||
_moves_fields['price%s' % move.id] = {'string': 'Unit Price',
|
|
||||||
'type': 'float', 'required': True, 'default': make_default(price)}
|
|
||||||
|
|
||||||
_moves_fields['uom%s' % move.id] = {'string': 'UOM', 'type': 'many2one',
|
|
||||||
'relation': 'product.uom', 'required': True,
|
|
||||||
'default': make_default(move.product_uom.id)}
|
|
||||||
|
|
||||||
_moves_arch_lst.append('<field name="currency%d" nolabel="1"/></group>' % (move.id,))
|
|
||||||
_moves_fields['currency%s' % m.id] = {'string': 'Currency',
|
|
||||||
'type': 'many2one', 'relation': 'res.currency',
|
|
||||||
'required': True, 'default': make_default(currency)}
|
|
||||||
|
|
||||||
_moves_arch_lst.append('<newline/>')
|
|
||||||
res.setdefault('moves', []).append(move.id)
|
|
||||||
|
|
||||||
_moves_arch_lst.append('</form>')
|
|
||||||
_moves_arch.string = '\n'.join(_moves_arch_lst)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _do_split(self, cr, uid, data, context):
|
|
||||||
pool = pooler.get_pool(cr.dbname)
|
|
||||||
move_obj = pool.get('stock.move')
|
|
||||||
pick_obj = pool.get('stock.picking')
|
|
||||||
product_obj = pool.get('product.product')
|
|
||||||
currency_obj = pool.get('res.currency')
|
|
||||||
users_obj = pool.get('res.users')
|
|
||||||
uom_obj = pool.get('product.uom')
|
|
||||||
move_lines = move_obj.browse(cr, uid, data['ids'])
|
|
||||||
wf_service = netsvc.LocalService("workflow")
|
|
||||||
complete, too_few, too_many = [], [], []
|
|
||||||
for move in move_lines:
|
|
||||||
states = []
|
|
||||||
|
|
||||||
if move.product_qty == data['form']['move%s' % move.id]:
|
|
||||||
complete.append(move)
|
|
||||||
elif move.product_qty > data['form']['move%s' % move.id]:
|
|
||||||
too_few.append(move)
|
|
||||||
else:
|
|
||||||
too_many.append(move)
|
|
||||||
# Average price computation
|
|
||||||
if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
|
|
||||||
product = move.product_id
|
|
||||||
user = users_obj.browse(cr, uid, uid)
|
|
||||||
qty = data['form']['move%s' % move.id]
|
|
||||||
uom = data['form']['uom%s' % move.id]
|
|
||||||
price = data['form']['price%s' % move.id]
|
|
||||||
currency = data['form']['currency%s' % move.id]
|
|
||||||
|
|
||||||
qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id)
|
|
||||||
pricetype=pool.get('product.price.type').browse(cr,uid,user.company_id.property_valuation_price_type.id)
|
|
||||||
|
|
||||||
if (qty > 0):
|
|
||||||
new_price = currency_obj.compute(cr, uid, currency,
|
|
||||||
user.company_id.currency_id.id, price)
|
|
||||||
new_price = uom_obj._compute_price(cr, uid, uom, new_price,
|
|
||||||
product.uom_id.id)
|
|
||||||
if product.qty_available<=0:
|
|
||||||
new_std_price = new_price
|
|
||||||
else:
|
|
||||||
# Get the standard price
|
|
||||||
amount_unit=product.price_get(pricetype.field, context)[product.id]
|
|
||||||
new_std_price = ((amount_unit * product.qty_available)\
|
|
||||||
+ (new_price * qty))/(product.qty_available + qty)
|
|
||||||
|
|
||||||
product_obj.write(cr, uid, [product.id],
|
|
||||||
{pricetype.field: new_std_price})
|
|
||||||
move_obj.write(cr, uid, move.id, {'price_unit': new_price})
|
|
||||||
|
|
||||||
for move in too_few:
|
|
||||||
if data['form']['move%s' % move.id] != 0:
|
|
||||||
new_move = move_obj.copy(cr, uid, move.id,
|
|
||||||
{
|
|
||||||
'product_qty' : data['form']['move%s' % move.id],
|
|
||||||
'product_uos_qty':data['form']['move%s' % move.id],
|
|
||||||
'picking_id' : move.picking_id.id,
|
|
||||||
'state': 'assigned',
|
|
||||||
'move_dest_id': False,
|
|
||||||
'price_unit': move.price_unit,
|
|
||||||
})
|
|
||||||
complete.append(move_obj.browse(cr, uid, new_move))
|
|
||||||
move_obj.write(cr, uid, move.id,
|
|
||||||
{
|
|
||||||
'product_qty' : move.product_qty - data['form']['move%s' % move.id],
|
|
||||||
'product_uos_qty':move.product_qty - data['form']['move%s' % move.id],
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
for move in too_many:
|
|
||||||
move_obj.write(cr, uid, move.id,
|
|
||||||
{
|
|
||||||
'product_qty': data['form']['move%s' % move.id],
|
|
||||||
'product_uos_qty': data['form']['move%s' % move.id]
|
|
||||||
})
|
|
||||||
complete.append(move)
|
|
||||||
|
|
||||||
for move in complete:
|
|
||||||
move_obj.action_done(cr, uid, [move.id])
|
|
||||||
|
|
||||||
# TOCHECK : Done picking if all moves are done
|
|
||||||
cr.execute('select move.id from stock_picking pick \
|
|
||||||
right join stock_move move on move.picking_id = pick.id and move.state = ''%s'' where pick.id = %s',
|
|
||||||
('done', move.picking_id.id))
|
|
||||||
res = cr.fetchall()
|
|
||||||
if len(res) == len(move.picking_id.move_lines):
|
|
||||||
pick_obj.action_move(cr, uid, [move.picking_id.id])
|
|
||||||
wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
|
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class partial_move(wizard.interface):
|
|
||||||
|
|
||||||
states = {
|
|
||||||
'init': {
|
|
||||||
'actions': [ _get_moves ],
|
|
||||||
'result': {'type': 'form', 'arch': _moves_arch, 'fields': _moves_fields,
|
|
||||||
'state' : (
|
|
||||||
('end', 'Cancel', 'gtk-cancel'),
|
|
||||||
('split', 'Partial', 'gtk-apply', True)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'split': {
|
|
||||||
'actions': [ _do_split ],
|
|
||||||
'result': {'type': 'state', 'state': 'end2'},
|
|
||||||
},
|
|
||||||
'end2': {
|
|
||||||
'actions': [],
|
|
||||||
'result': {'type': 'form', 'arch': _moves_arch_end,
|
|
||||||
'fields': {},
|
|
||||||
'state': (
|
|
||||||
('end', 'Close'),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
partial_move('stock.partial_move')
|
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|
Loading…
Reference in New Issue