diff --git a/addons/stock/__terp__.py b/addons/stock/__terp__.py
index 4c379ccc371..5fa272238d5 100644
--- a/addons/stock/__terp__.py
+++ b/addons/stock/__terp__.py
@@ -44,6 +44,7 @@ Thanks to the double entry management, the inventory controlling is powerful and
"stock_data.xml",
"wizard/stock_move_view.xml",
"wizard/stock_partial_picking_view.xml",
+ "wizard/stock_partial_move_view.xml",
"wizard/stock_inventory_set_stock_zero_view.xml",
"wizard/stock_fill_inventory_view.xml",
"wizard/stock_invoice_onshipping_view.xml",
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index a801fe4fcd9..68450edd6d0 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -890,6 +890,8 @@ class stock_picking(osv.osv):
complete, too_many, too_few = [], [], []
move_product_qty = {}
for move in pick.move_lines:
+ 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)
@@ -1117,10 +1119,9 @@ class stock_delivery(osv.osv):
'partner_id': fields.many2one('res.partner', 'Partner', required=True),
'address_id': fields.many2one('res.partner.address', 'Address', required=True),
'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()
# ----------------------------------------------------
# Move
@@ -1788,7 +1789,134 @@ class stock_move(osv.osv):
self.write(cr, uid, [move.id], update_val)
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()
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index c32eddfbf4d..877ea313d1b 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -1488,7 +1488,7 @@
-
+
@@ -1534,7 +1534,7 @@
-
+
diff --git a/addons/stock/stock_wizard.xml b/addons/stock/stock_wizard.xml
index e966c27a6c1..f9a2dfc93b0 100644
--- a/addons/stock/stock_wizard.xml
+++ b/addons/stock/stock_wizard.xml
@@ -29,14 +29,7 @@
menu="False"
keyword="client_action_multi"
name="stock.return.picking"
- string="Return picking"/>
-
-
+ string="Return picking"/>
).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+from 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 = """"""
+ 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:
+
diff --git a/addons/stock/wizard/stock_partial_move_view.xml b/addons/stock/wizard/stock_partial_move_view.xml
new file mode 100644
index 00000000000..30b7f3fca0e
--- /dev/null
+++ b/addons/stock/wizard/stock_partial_move_view.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Delivery Product
+ stock.partial.move
+ form
+
+
+
+
+
+
+
+
+
diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py
index 494fb3b1231..eba095718cd 100644
--- a/addons/stock/wizard/stock_partial_picking.py
+++ b/addons/stock/wizard/stock_partial_picking.py
@@ -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):
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')
- picking_ids = context.get('active_ids', False)
- if not picking_ids:
- return result
- if view_type in ['form']:
- _moves_arch_lst = """"""
- result['arch'] = _moves_arch_lst
- result['fields'] = _moves_fields
+ _moves_arch_lst += """
+
+
+
+
+
+
+ """
+ result['arch'] = _moves_arch_lst
+ result['fields'] = _moves_fields
return result
def default_get(self, cr, uid, fields, context=None):
@@ -160,13 +159,13 @@ class stock_partial_picking(osv.osv_memory):
if not context:
context={}
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', [])):
if 'partner_id' in fields:
res.update({'partner_id': pick.address_id.partner_id.id})
if 'address_id' in fields:
- res.update({'address_id': pick.address_id.id})
- if 'date' in fields:
- res.update({'date': pick.date})
+ res.update({'address_id': pick.address_id.id})
for m in pick.move_lines:
if m.state in ('done', 'cancel'):
continue
@@ -215,8 +214,7 @@ class stock_partial_picking(osv.osv_memory):
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 = pick_obj.do_partial(cr, uid, picking_ids, partial_datas, context=context)
return {}
diff --git a/addons/stock/wizard/wizard_partial_move.py b/addons/stock/wizard/wizard_partial_move.py
deleted file mode 100644
index 928d96461c8..00000000000
--- a/addons/stock/wizard/wizard_partial_move.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# 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 .
-#
-##############################################################################
-
-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 = '''
-'''
-
-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 = ['', '')
- _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:
-