[MERGE] merge from perf3 until merge

bzr revid: jco@openerp.com-20140304161944-hbb58wtdj2e4c2u7
This commit is contained in:
Josse Colpaert 2014-03-04 17:19:44 +01:00
commit 0a9bf2fe25
14 changed files with 757 additions and 97 deletions

View File

@ -100,7 +100,7 @@ class mail_message(osv.Model):
def _get_to_read(self, cr, uid, ids, name, arg, context=None): def _get_to_read(self, cr, uid, ids, name, arg, context=None):
""" Compute if the message is unread by the current user. """ """ Compute if the message is unread by the current user. """
res = dict((id, False) for id in ids) res = dict((id, False) for id in ids)
partner_id = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id partner_id = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
notif_obj = self.pool.get('mail.notification') notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, uid, [ notif_ids = notif_obj.search(cr, uid, [
('partner_id', 'in', [partner_id]), ('partner_id', 'in', [partner_id]),
@ -119,7 +119,7 @@ class mail_message(osv.Model):
def _get_starred(self, cr, uid, ids, name, arg, context=None): def _get_starred(self, cr, uid, ids, name, arg, context=None):
""" Compute if the message is unread by the current user. """ """ Compute if the message is unread by the current user. """
res = dict((id, False) for id in ids) res = dict((id, False) for id in ids)
partner_id = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id partner_id = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
notif_obj = self.pool.get('mail.notification') notif_obj = self.pool.get('mail.notification')
notif_ids = notif_obj.search(cr, uid, [ notif_ids = notif_obj.search(cr, uid, [
('partner_id', 'in', [partner_id]), ('partner_id', 'in', [partner_id]),
@ -208,7 +208,7 @@ class mail_message(osv.Model):
raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias."))
def _get_default_author(self, cr, uid, context=None): def _get_default_author(self, cr, uid, context=None):
return self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id return self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
_defaults = { _defaults = {
'type': 'email', 'type': 'email',
@ -266,7 +266,7 @@ class mail_message(osv.Model):
:return number of message mark as read :return number of message mark as read
""" """
notification_obj = self.pool.get('mail.notification') notification_obj = self.pool.get('mail.notification')
user_pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id user_pid = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)] domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)]
if not create_missing: if not create_missing:
domain += [('read', '=', not read)] domain += [('read', '=', not read)]
@ -294,7 +294,7 @@ class mail_message(osv.Model):
(i.e. when acting on displayed messages not notified) (i.e. when acting on displayed messages not notified)
""" """
notification_obj = self.pool.get('mail.notification') notification_obj = self.pool.get('mail.notification')
user_pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id user_pid = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)] domain = [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)]
if not create_missing: if not create_missing:
domain += [('starred', '=', not starred)] domain += [('starred', '=', not starred)]
@ -332,7 +332,7 @@ class mail_message(osv.Model):
""" """
res_partner_obj = self.pool.get('res.partner') res_partner_obj = self.pool.get('res.partner')
ir_attachment_obj = self.pool.get('ir.attachment') ir_attachment_obj = self.pool.get('ir.attachment')
pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id pid = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
# 1. Aggregate partners (author_id and partner_ids) and attachments # 1. Aggregate partners (author_id and partner_ids) and attachments
partner_ids = set() partner_ids = set()
@ -653,7 +653,7 @@ class mail_message(osv.Model):
elif not ids: elif not ids:
return ids return ids
pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id pid = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
author_ids, partner_ids, allowed_ids = set([]), set([]), set([]) author_ids, partner_ids, allowed_ids = set([]), set([]), set([])
model_ids = {} model_ids = {}
@ -714,7 +714,7 @@ class mail_message(osv.Model):
ids = [ids] ids = [ids]
not_obj = self.pool.get('mail.notification') not_obj = self.pool.get('mail.notification')
fol_obj = self.pool.get('mail.followers') fol_obj = self.pool.get('mail.followers')
partner_id = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=None).partner_id.id partner_id = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
# Read mail_message.ids to have their values # Read mail_message.ids to have their values
message_values = dict.fromkeys(ids, {}) message_values = dict.fromkeys(ids, {})

View File

@ -286,7 +286,7 @@ class mail_thread(osv.AbstractModel):
res = [] res = []
for field, operator, value in args: for field, operator, value in args:
assert field == name assert field == name
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id partner_id = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
if (operator == '=' and value) or (operator == '!=' and not value): # is a follower if (operator == '=' and value) or (operator == '!=' and not value): # is a follower
res_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context) res_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context)
else: # is not a follower or unknown domain else: # is not a follower or unknown domain
@ -354,7 +354,7 @@ class mail_thread(osv.AbstractModel):
# subscribe uid unless asked not to # subscribe uid unless asked not to
if not context.get('mail_create_nosubscribe'): if not context.get('mail_create_nosubscribe'):
pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid).partner_id.id pid = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False
message_follower_ids.append([4, pid]) message_follower_ids.append([4, pid])
values['message_follower_ids'] = message_follower_ids values['message_follower_ids'] = message_follower_ids
@ -1586,7 +1586,7 @@ class mail_thread(osv.AbstractModel):
mail_followers_obj = self.pool.get('mail.followers') mail_followers_obj = self.pool.get('mail.followers')
subtype_obj = self.pool.get('mail.message.subtype') subtype_obj = self.pool.get('mail.message.subtype')
user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id user_pid = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
if set(partner_ids) == set([user_pid]): if set(partner_ids) == set([user_pid]):
try: try:
self.check_access_rights(cr, uid, 'read') self.check_access_rights(cr, uid, 'read')
@ -1776,7 +1776,7 @@ class mail_thread(osv.AbstractModel):
def message_mark_as_unread(self, cr, uid, ids, context=None): def message_mark_as_unread(self, cr, uid, ids, context=None):
""" Set as unread. """ """ Set as unread. """
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id partner_id = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
cr.execute(''' cr.execute('''
UPDATE mail_notification SET UPDATE mail_notification SET
read=false read=false
@ -1788,7 +1788,7 @@ class mail_thread(osv.AbstractModel):
def message_mark_as_read(self, cr, uid, ids, context=None): def message_mark_as_read(self, cr, uid, ids, context=None):
""" Set as read. """ """ Set as read. """
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id partner_id = self.pool.get('res.users').read(cr, SUPERUSER_ID, [uid], ['partner_id'], context=context)[0]['partner_id'][0]
cr.execute(''' cr.execute('''
UPDATE mail_notification SET UPDATE mail_notification SET
read=true read=true

View File

@ -159,6 +159,7 @@ class product_uom(osv.osv):
('factor_gt_zero', 'CHECK (factor!=0)', 'The conversion ratio for a unit of measure cannot be 0!') ('factor_gt_zero', 'CHECK (factor!=0)', 'The conversion ratio for a unit of measure cannot be 0!')
] ]
def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False, round=True): def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False, round=True):
if not from_uom_id or not qty or not to_uom_id: if not from_uom_id or not qty or not to_uom_id:
return qty return qty
@ -807,7 +808,10 @@ class product_product(osv.osv):
result = [] result = []
for product in self.browse(cr, SUPERUSER_ID, ids, context=context): for product in self.browse(cr, SUPERUSER_ID, ids, context=context):
sellers = filter(lambda x: x.name.id == partner_id, product.seller_ids) if partner_id:
sellers = filter(lambda x: x.name.id == partner_id, product.seller_ids)
else:
sellers = False
if sellers: if sellers:
for s in sellers: for s in sellers:
mydict = { mydict = {

View File

@ -31,6 +31,7 @@ import openerp.addons.decimal_precision as dp
from openerp.osv.orm import browse_record, browse_null from openerp.osv.orm import browse_record, browse_null
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP
class purchase_order(osv.osv): class purchase_order(osv.osv):
def _amount_all(self, cr, uid, ids, field_name, arg, context=None): def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
@ -155,12 +156,19 @@ class purchase_order(osv.osv):
def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None): def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None):
res = {} res = {}
for purchase_id in ids: query = """
picking_ids = set() SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po
move_ids = self.pool.get('stock.move').search(cr, uid, [('purchase_line_id.order_id', '=', purchase_id)], context=context) WHERE po.id in %s and po.id = pol.order_id and pol.id = m.purchase_line_id and m.picking_id = p.id
for move in self.pool.get('stock.move').browse(cr, uid, move_ids, context=context): GROUP BY picking_id, po.id
picking_ids.add(move.picking_id.id)
res[purchase_id] = list(picking_ids) """
cr.execute(query, (tuple(ids), ))
picks = cr.fetchall()
for pick in picks:
if not res.get(pick[1]):
res[pick[1]] = [pick[0]]
else:
res[pick[1]].append(pick[0])
return res return res
STATE_SELECTION = [ STATE_SELECTION = [

View File

@ -35,7 +35,7 @@ class stock_move(osv.osv):
ids = [ids] ids = [ids]
res = super(stock_move, self).write(cr, uid, ids, vals, context=context) res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
from openerp import workflow from openerp import workflow
if 'state' in vals: if 'state' in vals and vals['state'] in ['done', 'cancel']:
for move in self.browse(cr, uid, ids, context=context): for move in self.browse(cr, uid, ids, context=context):
if move.purchase_line_id and move.purchase_line_id.order_id: if move.purchase_line_id and move.purchase_line_id.order_id:
order_id = move.purchase_line_id.order_id.id order_id = move.purchase_line_id.order_id.id
@ -71,7 +71,7 @@ class stock_picking(osv.osv):
def _get_picking_to_recompute(self, cr, uid, ids, context=None): def _get_picking_to_recompute(self, cr, uid, ids, context=None):
picking_ids = set() picking_ids = set()
for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context): for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
if move.picking_id: if move.picking_id and move.purchase_line_id:
picking_ids.add(move.picking_id.id) picking_ids.add(move.picking_id.id)
return list(picking_ids) return list(picking_ids)
@ -79,7 +79,6 @@ class stock_picking(osv.osv):
'reception_to_invoice': fields.function(_get_to_invoice, type='boolean', string='Invoiceable on incoming shipment?', 'reception_to_invoice': fields.function(_get_to_invoice, type='boolean', string='Invoiceable on incoming shipment?',
help='Does the picking contains some moves related to a purchase order invoiceable on the reception?', help='Does the picking contains some moves related to a purchase order invoiceable on the reception?',
store={ store={
'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 10),
'stock.move': (_get_picking_to_recompute, ['purchase_line_id', 'picking_id'], 10), 'stock.move': (_get_picking_to_recompute, ['purchase_line_id', 'picking_id'], 10),
}), }),
} }

View File

@ -4,4 +4,4 @@
I duplicate order. I duplicate order.
- -
!python {model: purchase.order}: | !python {model: purchase.order}: |
self.copy(cr, uid, ref('purchase_order_1'), context) self.copy_data(cr, uid, ref('purchase_order_1'), context)

View File

@ -676,6 +676,8 @@ class sale_order(osv.osv):
:return: True :return: True
""" """
if not context:
context = {}
procurement_obj = self.pool.get('procurement.order') procurement_obj = self.pool.get('procurement.order')
sale_line_obj = self.pool.get('sale.order.line') sale_line_obj = self.pool.get('sale.order.line')
for order in self.browse(cr, uid, ids, context=context): for order in self.browse(cr, uid, ids, context=context):
@ -701,7 +703,11 @@ class sale_order(osv.osv):
proc_ids.append(proc_id) proc_ids.append(proc_id)
#Confirm procurement order such that rules will be applied on it #Confirm procurement order such that rules will be applied on it
#note that the workflow normally ensure proc_ids isn't an empty list #note that the workflow normally ensure proc_ids isn't an empty list
procurement_obj.run(cr, uid, proc_ids, context=context) ctx = context.copy()
ctx["no_picking_assign"] = True
procurement_obj.run(cr, uid, proc_ids, context=ctx)
#Check all moves associated and do the picking_assign
procurement_obj.group_picking_assign(cr, uid, proc_ids, context=context)
#if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO #if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO
if order.state == 'shipping_except': if order.state == 'shipping_except':
@ -1170,3 +1176,5 @@ class procurement_order(osv.osv):
'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'), 'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'),
} }
def group_picking_assign(self, cr, uid, proc_ids, context=None):
return True

View File

@ -67,7 +67,7 @@ class sale_order(osv.osv):
def _get_orders_procurements(self, cr, uid, ids, context=None): def _get_orders_procurements(self, cr, uid, ids, context=None):
res = set() res = set()
for proc in self.pool.get('procurement.order').browse(cr, uid, ids, context=context): for proc in self.pool.get('procurement.order').browse(cr, uid, ids, context=context):
if proc.sale_line_id: if proc.state =='done' and proc.sale_line_id:
res.add(proc.sale_line_id.order_id.id) res.add(proc.sale_line_id.order_id.id)
return list(res) return list(res)
@ -102,7 +102,6 @@ class sale_order(osv.osv):
], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""), help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""),
'shipped': fields.function(_get_shipped, string='Delivered', type='boolean', store={ 'shipped': fields.function(_get_shipped, string='Delivered', type='boolean', store={
'stock.move': (_get_orders, ['state'], 10),
'procurement.order': (_get_orders_procurements, ['state'], 10) 'procurement.order': (_get_orders_procurements, ['state'], 10)
}), }),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True), 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True),
@ -454,3 +453,18 @@ class stock_picking(osv.osv):
created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context) created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context)
invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_id}, context=context) invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_id}, context=context)
return invoice_id return invoice_id
class procurement_order(osv.osv):
_inherit = 'procurement.order'
def group_picking_assign(self, cr, uid, proc_ids, context=None):
moves = []
procurements = proc_ids
while procurements:
related_moves = []
for proc in self.browse(cr, uid, procurements, context=context):
related_moves += proc.move_ids
procurements = self.search(cr, uid, [('move_dest_id', 'in', [x.id for x in related_moves])], context=context)
moves += related_moves
self.pool.get("stock.move")._group_picking_assign(cr, uid, moves, context=context)

View File

@ -95,7 +95,7 @@ class procurement_rule(osv.osv):
class procurement_order(osv.osv): class procurement_order(osv.osv):
_inherit = "procurement.order" _inherit = "procurement.order"
_columns = { _columns = {
'location_id': fields.many2one('stock.location', 'Procurement Location'), # not required because task may create procurements that aren't linked to a location with project_mrp 'location_id': fields.many2one('stock.location', 'Procurement Location'), # not required because task may create procurements that aren't linked to a location with project_mrp
'move_ids': fields.one2many('stock.move', 'procurement_id', 'Moves', help="Moves created by the procurement"), 'move_ids': fields.one2many('stock.move', 'procurement_id', 'Moves', help="Moves created by the procurement"),
'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Move which caused (created) the procurement"), 'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Move which caused (created) the procurement"),
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_procurement', 'procurement_id', 'route_id', 'Preferred Routes', help="Preferred route to be followed by the procurement order. Usually copied from the generating document (SO) but could be set up manually."), 'route_ids': fields.many2many('stock.location.route', 'stock_location_route_procurement', 'procurement_id', 'route_id', 'Preferred Routes', help="Preferred route to be followed by the procurement order. Usually copied from the generating document (SO) but could be set up manually."),

View File

@ -30,9 +30,9 @@ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FO
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
import openerp.addons.decimal_precision as dp import openerp.addons.decimal_precision as dp
import logging import logging
from profilehooks import profile
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
#---------------------------------------------------------- #----------------------------------------------------------
# Incoterms # Incoterms
#---------------------------------------------------------- #----------------------------------------------------------
@ -321,6 +321,8 @@ class stock_quant(osv.osv):
move.refresh() move.refresh()
if move.reserved_availability == move.product_qty and move.state in ('confirmed', 'waiting'): if move.reserved_availability == move.product_qty and move.state in ('confirmed', 'waiting'):
self.pool.get('stock.move').write(cr, uid, [move.id], {'state': 'assigned'}, context=context) self.pool.get('stock.move').write(cr, uid, [move.id], {'state': 'assigned'}, context=context)
elif move.reserved_availability > 0 and not move.partially_available:
self.pool.get('stock.move').write(cr, uid, [move.id], {'partially_available': True}, context=context)
def quants_move(self, cr, uid, quants, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, location_dest_id = False, context=None): def quants_move(self, cr, uid, quants, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, location_dest_id = False, context=None):
"""Moves all given stock.quant in the destination location of the given move. """Moves all given stock.quant in the destination location of the given move.
@ -358,7 +360,8 @@ class stock_quant(osv.osv):
#if the quant we are moving had been split and was inside a package, it means we unpacked it #if the quant we are moving had been split and was inside a package, it means we unpacked it
if new_quant and new_quant.package_id: if new_quant and new_quant.package_id:
vals['package_id'] = False vals['package_id'] = False
self.write(cr, SUPERUSER_ID, [quant.id], vals, context=context) if self._check_location(cr, uid, location_to, context):
self.write(cr, SUPERUSER_ID, [quant.id], vals, context=context)
quant.refresh() quant.refresh()
return new_quant return new_quant
@ -536,6 +539,8 @@ class stock_quant(osv.osv):
def quants_unreserve(self, cr, uid, move, context=None): def quants_unreserve(self, cr, uid, move, context=None):
related_quants = [x.id for x in move.reserved_quant_ids] related_quants = [x.id for x in move.reserved_quant_ids]
if related_quants and move.partially_available:
self.pool.get("stock.move").write(cr, uid, [move.id], {'partially_available': False}, context=context)
return self.write(cr, SUPERUSER_ID, related_quants, {'reservation_id': False, 'link_move_operation_id': False}, context=context) return self.write(cr, SUPERUSER_ID, related_quants, {'reservation_id': False, 'link_move_operation_id': False}, context=context)
def _quants_get_order(self, cr, uid, location, product, quantity, domain=[], orderby='in_date', context=None): def _quants_get_order(self, cr, uid, location, product, quantity, domain=[], orderby='in_date', context=None):
@ -577,16 +582,11 @@ class stock_quant(osv.osv):
''' Return the company owning the location if any ''' ''' Return the company owning the location if any '''
return location and (location.usage == 'internal') and location.company_id or False return location and (location.usage == 'internal') and location.company_id or False
def _check_location(self, cr, uid, ids, context=None): def _check_location(self, cr, uid, location, context=None):
for record in self.browse(cr, uid, ids, context=context): if location.usage == 'view':
if record.location_id.usage == 'view': raise osv.except_osv(_('Error'), _('You cannot move to a location of type view %s.') % (location.name))
raise osv.except_osv(_('Error'), _('You cannot move product %s to a location of type view %s.') % (record.product_id.name, record.location_id.name))
return True return True
_constraints = [
(_check_location, 'You cannot move products to a location of the type view.', ['location_id'])
]
#---------------------------------------------------------- #----------------------------------------------------------
# Stock Picking # Stock Picking
@ -644,19 +644,21 @@ class stock_picking(osv.osv):
''' '''
res = {} res = {}
for pick in self.browse(cr, uid, ids, context=context): for pick in self.browse(cr, uid, ids, context=context):
if (not pick.move_lines) or any([x.state == 'draft' for x in pick.move_lines]): cr.execute("select state, partially_available from stock_move where picking_id = %s", (pick.id,))
move_lines = cr.fetchall() #x[0] = state, x[1] partially available
if (not move_lines) or any([x[0] == 'draft' for x in move_lines]):
res[pick.id] = 'draft' res[pick.id] = 'draft'
continue continue
if all([x.state == 'cancel' for x in pick.move_lines]): if all([x[0] == 'cancel' for x in move_lines]):
res[pick.id] = 'cancel' res[pick.id] = 'cancel'
continue continue
if all([x.state in ('cancel', 'done') for x in pick.move_lines]): if all([x[0] in ('cancel', 'done') for x in move_lines]):
res[pick.id] = 'done' res[pick.id] = 'done'
continue continue
order = {'confirmed': 0, 'waiting': 1, 'assigned': 2} order = {'confirmed': 0, 'waiting': 1, 'assigned': 2}
order_inv = {0: 'confirmed', 1: 'waiting', 2: 'assigned'} order_inv = {0: 'confirmed', 1: 'waiting', 2: 'assigned'}
lst = [order[x.state] for x in pick.move_lines if x.state not in ('cancel', 'done')] lst = [order[x[0]] for x in move_lines if x[0] not in ('cancel', 'done')]
if pick.move_type == 'one': if pick.move_type == 'one':
res[pick.id] = order_inv[min(lst)] res[pick.id] = order_inv[min(lst)]
else: else:
@ -666,24 +668,17 @@ class stock_picking(osv.osv):
res[pick.id] = order_inv[max(lst)] res[pick.id] = order_inv[max(lst)]
if not all(x == 2 for x in lst): if not all(x == 2 for x in lst):
#if all moves aren't assigned, check if we have one product partially available #if all moves aren't assigned, check if we have one product partially available
for move in pick.move_lines: for move in move_lines:
if move.reserved_quant_ids: if move[1]:
res[pick.id] = 'partially_available' res[pick.id] = 'partially_available'
break break
return res return res
def _get_pickings(self, cr, uid, ids, context=None): def _get_pickings(self, cr, uid, ids, context=None):
res = set() res = set()
for move in self.browse(cr, uid, ids, context=context): for move in self.read(cr, uid, ids, ['picking_id'], context=context):
if move.picking_id: if move['picking_id']:
res.add(move.picking_id.id) res.add(move['picking_id'][0])
return list(res)
def _get_pickings_from_quant(self, cr, uid, ids, context=None):
res = set()
for quant in self.browse(cr, uid, ids, context=context):
if quant.reservation_id and quant.reservation_id.picking_id:
res.add(quant.reservation_id.picking_id.id)
return list(res) return list(res)
def _get_pack_operation_exist(self, cr, uid, ids, field_name, arg, context=None): def _get_pack_operation_exist(self, cr, uid, ids, field_name, arg, context=None):
@ -716,9 +711,8 @@ class stock_picking(osv.osv):
'note': fields.text('Notes', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}), 'note': fields.text('Notes', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"), 'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"),
'state': fields.function(_state_get, type="selection", store={ 'state': fields.function(_state_get, type="selection", store={
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type', 'move_lines'], 20), 'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20),
'stock.move': (_get_pickings, ['state', 'picking_id'], 20), 'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)}, selection=[
'stock.quant': (_get_pickings_from_quant, ['reservation_id'], 20)}, selection=[
('draft', 'Draft'), ('draft', 'Draft'),
('cancel', 'Cancelled'), ('cancel', 'Cancelled'),
('waiting', 'Waiting Another Operation'), ('waiting', 'Waiting Another Operation'),
@ -737,9 +731,9 @@ class stock_picking(osv.osv):
), ),
'priority': fields.selection([('0', 'Low'), ('1', 'Normal'), ('2', 'High')], states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Priority', required=True), 'priority': fields.selection([('0', 'Low'), ('1', 'Normal'), ('2', 'High')], states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Priority', required=True),
'min_date': fields.function(get_min_max_date, multi="min_max_date", fnct_inv=_set_min_date, 'min_date': fields.function(get_min_max_date, multi="min_max_date", fnct_inv=_set_min_date,
store={'stock.move': (_get_pickings, ['state', 'date_expected'], 20)}, type='datetime', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Scheduled Date', select=1, help="Scheduled time for the first part of the shipment to be processed. Setting manually a value here would set it as expected date for all the stock moves.", track_visibility='onchange'), store={'stock.move': (_get_pickings, ['date_expected'], 20)}, type='datetime', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Scheduled Date', select=1, help="Scheduled time for the first part of the shipment to be processed. Setting manually a value here would set it as expected date for all the stock moves.", track_visibility='onchange'),
'max_date': fields.function(get_min_max_date, multi="min_max_date", 'max_date': fields.function(get_min_max_date, multi="min_max_date",
store={'stock.move': (_get_pickings, ['state', 'date_expected'], 20)}, type='datetime', string='Max. Expected Date', select=2, help="Scheduled time for the last part of the shipment to be processed"), store={'stock.move': (_get_pickings, ['date_expected'], 20)}, type='datetime', string='Max. Expected Date', select=2, help="Scheduled time for the last part of the shipment to be processed"),
'date': fields.datetime('Commitment Date', help="Date promised for the completion of the transfer order, usually set the time of the order and revised later on.", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'), 'date': fields.datetime('Commitment Date', help="Date promised for the completion of the transfer order, usually set the time of the order and revised later on.", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'),
'date_done': fields.datetime('Date of Transfer', help="Date of Completion", states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}), 'date_done': fields.datetime('Date of Transfer', help="Date of Completion", states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}), 'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
@ -846,8 +840,6 @@ class stock_picking(osv.osv):
for pick in self.browse(cr, uid, ids, context=context): for pick in self.browse(cr, uid, ids, context=context):
move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed', 'waiting']] move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed', 'waiting']]
self.pool.get('stock.move').force_assign(cr, uid, move_ids, context=context) self.pool.get('stock.move').force_assign(cr, uid, move_ids, context=context)
if pick.pack_operation_exist:
self.do_prepare_partial(cr, uid, [pick.id], context=None)
return True return True
def action_cancel(self, cr, uid, ids, context=None): def action_cancel(self, cr, uid, ids, context=None):
@ -1110,10 +1102,12 @@ class stock_picking(osv.osv):
def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None): def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None):
pack_op_obj = self.pool.get('stock.pack.operation') pack_op_obj = self.pool.get('stock.pack.operation')
quants_res = True
for picking in self.browse(cr, uid, picking_ids, context=context): for picking in self.browse(cr, uid, picking_ids, context=context):
op_ids = [op.id for op in picking.pack_operation_ids] op_ids = [op.id for op in picking.pack_operation_ids]
if op_ids: if op_ids:
pack_op_obj.recompute_rem_qty_from_operation(cr, uid, op_ids, context=context) quants_res = quants_res and pack_op_obj.recompute_rem_qty_from_operation(cr, uid, op_ids, context=context)
return quants_res
def _create_extra_moves(self, cr, uid, picking, context=None): def _create_extra_moves(self, cr, uid, picking, context=None):
'''This function creates move lines on a picking, at the time of do_transfer, based on '''This function creates move lines on a picking, at the time of do_transfer, based on
@ -1161,28 +1155,34 @@ class stock_picking(osv.osv):
self.action_done(cr, uid, [picking.id], context=context) self.action_done(cr, uid, [picking.id], context=context)
continue continue
else: else:
self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context) quants_ok = self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context)
#create extra moves in the picking (unexpected product moves coming from pack operations) #create extra moves in the picking (unexpected product moves coming from pack operations)
self._create_extra_moves(cr, uid, picking, context=context) if not quants_ok:
self._create_extra_moves(cr, uid, picking, context=context)
picking.refresh() picking.refresh()
#split move lines eventually #split move lines eventually
todo_move_ids = [] todo_move_ids = []
toassign_move_ids = [] toassign_move_ids = []
no_rereserve = True
for move in picking.move_lines: for move in picking.move_lines:
remaining_qty = move.remaining_qty
if move.state in ('done', 'cancel'): if move.state in ('done', 'cancel'):
#ignore stock moves cancelled or already done #ignore stock moves cancelled or already done
continue continue
elif move.state == 'draft': elif move.state == 'draft':
toassign_move_ids.append(move.id) toassign_move_ids.append(move.id)
if move.remaining_qty == 0: no_rereserve = False
if remaining_qty == 0:
if move.state in ('draft', 'assigned', 'confirmed'): if move.state in ('draft', 'assigned', 'confirmed'):
todo_move_ids.append(move.id) todo_move_ids.append(move.id)
elif move.remaining_qty > 0 and move.remaining_qty < move.product_qty: elif remaining_qty > 0 and remaining_qty < move.product_qty:
new_move = stock_move_obj.split(cr, uid, move, move.remaining_qty, context=context) no_rereserve = False
new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context)
todo_move_ids.append(move.id) todo_move_ids.append(move.id)
#Assign move as it was assigned before #Assign move as it was assigned before
toassign_move_ids.append(new_move) toassign_move_ids.append(new_move)
self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context) if not quants_ok or not no_rereserve:
self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
if todo_move_ids and not context.get('do_only_split'): if todo_move_ids and not context.get('do_only_split'):
self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context) self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context)
elif context.get('do_only_split'): elif context.get('do_only_split'):
@ -1339,7 +1339,7 @@ class stock_move(osv.osv):
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
res = {} res = {}
for m in self.browse(cr, uid, ids, context=context): for m in self.browse(cr, uid, ids, context=context):
res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, round=False) res[m.id] = uom_obj._compute_qty_obj(cr, uid, m.product_uom, m.product_uom_qty, m.product_id.uom_id, round=False, context=context)
return res return res
def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None): def _get_remaining_qty(self, cr, uid, ids, field_name, args, context=None):
@ -1350,7 +1350,7 @@ class stock_move(osv.osv):
for record in move.linked_move_operation_ids: for record in move.linked_move_operation_ids:
qty -= record.qty qty -= record.qty
#converting the remaining quantity in the move UoM #converting the remaining quantity in the move UoM
res[move.id] = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, qty, move.product_uom.id) res[move.id] = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, qty, move.product_uom, round=False, context=context)
return res return res
def _get_lot_ids(self, cr, uid, ids, field_name, args, context=None): def _get_lot_ids(self, cr, uid, ids, field_name, args, context=None):
@ -1386,7 +1386,7 @@ class stock_move(osv.osv):
res[move.id] = '' # 'not applicable' or 'n/a' could work too res[move.id] = '' # 'not applicable' or 'n/a' could work too
continue continue
total_available = min(move.product_qty, move.reserved_availability + move.availability) total_available = min(move.product_qty, move.reserved_availability + move.availability)
total_available = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, total_available, move.product_uom.id) total_available = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, total_available, move.product_uom, context=context)
info = str(total_available) info = str(total_available)
#look in the settings if we need to display the UoM name or not #look in the settings if we need to display the UoM name or not
config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context) config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context)
@ -1397,7 +1397,7 @@ class stock_move(osv.osv):
if move.reserved_availability: if move.reserved_availability:
if move.reserved_availability != total_available: if move.reserved_availability != total_available:
#some of the available quantity is assigned and some are available but not reserved #some of the available quantity is assigned and some are available but not reserved
reserved_available = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, move.reserved_availability, move.product_uom.id) reserved_available = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, move.reserved_availability, move.product_uom, context=context)
info += _(' (%s reserved)') % str(reserved_available) info += _(' (%s reserved)') % str(reserved_available)
else: else:
#all available quantity is assigned #all available quantity is assigned
@ -1424,6 +1424,7 @@ class stock_move(osv.osv):
res += [x.id for x in picking.move_lines] res += [x.id for x in picking.move_lines]
return res return res
_columns = { _columns = {
'name': fields.char('Description', required=True, select=True), 'name': fields.char('Description', required=True, select=True),
'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'), 'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'),
@ -1432,7 +1433,7 @@ class stock_move(osv.osv):
'date_expected': fields.datetime('Expected Date', states={'done': [('readonly', True)]}, required=True, select=True, help="Scheduled date for the processing of this move"), 'date_expected': fields.datetime('Expected Date', states={'done': [('readonly', True)]}, required=True, select=True, help="Scheduled date for the processing of this move"),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type', '<>', 'service')], states={'done': [('readonly', True)]}), 'product_id': fields.many2one('product.product', 'Product', required=True, select=True, domain=[('type', '<>', 'service')], states={'done': [('readonly', True)]}),
# TODO: improve store to add dependency on product UoM # TODO: improve store to add dependency on product UoM
'product_qty': fields.function(_quantity_normalize, type='float', store=True, string='Quantity', 'product_qty': fields.function(_quantity_normalize, type='float', store={'stock.move': (lambda self, cr, uid, ids, ctx: ids, ['product_uom_qty', 'product_uom'], 20)}, string='Quantity',
digits_compute=dp.get_precision('Product Unit of Measure'), digits_compute=dp.get_precision('Product Unit of Measure'),
help='Quantity in the default UoM of the product'), help='Quantity in the default UoM of the product'),
'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), 'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
@ -1476,7 +1477,7 @@ class stock_move(osv.osv):
"* Waiting Availability: This state is reached when the procurement resolution is not straight forward. It may need the scheduler to run, a component to me manufactured...\n"\ "* Waiting Availability: This state is reached when the procurement resolution is not straight forward. It may need the scheduler to run, a component to me manufactured...\n"\
"* Available: When products are reserved, it is set to \'Available\'.\n"\ "* Available: When products are reserved, it is set to \'Available\'.\n"\
"* Done: When the shipment is processed, the state is \'Done\'."), "* Done: When the shipment is processed, the state is \'Done\'."),
'partially_available': fields.boolean('Partially Available', readonly = True, help = "Checks if the move has some stock reserved"),
'price_unit': fields.float('Unit Price', help="Technical field used to record the product cost set by the user during a picking confirmation (when costing method used is 'average price' or 'real'). Value given in company currency and in product uom."), # as it's a technical field, we intentionally don't provide the digits attribute 'price_unit': fields.float('Unit Price', help="Technical field used to record the product cost set by the user during a picking confirmation (when costing method used is 'average price' or 'real'). Value given in company currency and in product uom."), # as it's a technical field, we intentionally don't provide the digits attribute
'company_id': fields.many2one('res.company', 'Company', required=True, select=True), 'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
@ -1546,16 +1547,6 @@ class stock_move(osv.osv):
'propagate': True, 'propagate': True,
} }
def _check_uom(self, cr, uid, ids, context=None):
for move in self.browse(cr, uid, ids, context=context):
if move.product_id.uom_id.category_id.id != move.product_uom.category_id.id:
return False
return True
_constraints = [
(_check_uom,
'You try to move a product using a UoM that is not compatible with the UoM of the product moved. Please use an UoM in the same UoM category.',
['product_uom'])]
def copy_data(self, cr, uid, id, default=None, context=None): def copy_data(self, cr, uid, id, default=None, context=None):
if default is None: if default is None:
@ -1640,6 +1631,17 @@ class stock_move(osv.osv):
# Check that we do not modify a stock.move which is done # Check that we do not modify a stock.move which is done
frozen_fields = set(['product_qty', 'product_uom', 'product_uos_qty', 'product_uos', 'location_id', 'location_dest_id', 'product_id']) frozen_fields = set(['product_qty', 'product_uom', 'product_uos_qty', 'product_uos', 'location_id', 'location_dest_id', 'product_id'])
for move in self.browse(cr, uid, ids, context=context): for move in self.browse(cr, uid, ids, context=context):
#Check UoM in meantime
if vals.get('product_uom') or vals.get('product_id'):
product_uom = move.product_id.uom_id
move_uom = move.product_uom
if vals.get('product_uom'):
move_uom = self.pool.get('product.uom').browse(cr, uid, vals['product_uom'], context=context)
if vals.get('product_id'):
product_uom = self.pool.get('product.product').browse(cr, uid, vals['product_id'], context=context).uom_id
if move_uom.category_id.id != product_uom.category_id.id:
raise osv.except_osv(_('Operation Forbidden'),
_('Category of Product UoM must be the same as the category of the UoM of the move'))
if move.state == 'done': if move.state == 'done':
if frozen_fields.intersection(vals): if frozen_fields.intersection(vals):
raise osv.except_osv(_('Operation Forbidden!'), raise osv.except_osv(_('Operation Forbidden!'),
@ -1789,6 +1791,10 @@ class stock_move(osv.osv):
return {'value': result} return {'value': result}
def _picking_assign(self, cr, uid, move, context=None): def _picking_assign(self, cr, uid, move, context=None):
if not context:
context = {}
if context.get("no_picking_assign") and context['no_picking_assign']:
return False
if move.picking_id or not move.picking_type_id: if move.picking_id or not move.picking_type_id:
return False return False
context = context or {} context = context or {}
@ -1814,6 +1820,40 @@ class stock_move(osv.osv):
move.write({'picking_id': pick}) move.write({'picking_id': pick})
return True return True
def _group_picking_assign(self, cr, uid, moves, context=None):
if not context:
context = {}
if context.get("no_picking_assign") and context['no_picking_assign']:
return False
move_dict = {}
for move in moves:
group_by = (move.location_id, move.location_dest_id, move.group_id)
if not move_dict.get(group_by, False):
move_dict[group_by] = [move]
else:
move_dict[group_by].append(move)
pick_obj = self.pool.get("stock.picking")
for to_compare in move_dict.keys():
picks = pick_obj.search(cr, uid, [
('group_id', '=', to_compare[2].id),
('location_id', '=', to_compare[0].id),
('location_dest_id', '=', to_compare[1].id),
('state', 'in', ['draft', 'confirmed', 'waiting']),
], context=context)
if picks:
pick = picks[0]
else:
move = move_dict[to_compare][0]
values = {
'origin': move.origin,
'company_id': move.company_id and move.company_id.id or False,
'move_type': move.group_id and move.group_id.move_type or 'one',
'partner_id': move.group_id and move.group_id.partner_id and move.group_id.partner_id.id or False,
'picking_type_id': move.picking_type_id and move.picking_type_id.id or False,
}
pick = pick_obj.create(cr, uid, values, context=context)
self.write(cr, uid, [x.id for x in move_dict[to_compare]], {'picking_id': pick}, context=context)
def onchange_date(self, cr, uid, ids, date, date_expected, context=None): def onchange_date(self, cr, uid, ids, date, date_expected, context=None):
""" On change of Scheduled Date gives a Move date. """ On change of Scheduled Date gives a Move date.
@param date_expected: Scheduled Date @param date_expected: Scheduled Date
@ -1982,6 +2022,7 @@ class stock_move(osv.osv):
packs |= set([q.package_id.id for q in move.quant_ids if q.package_id and q.qty > 0]) packs |= set([q.package_id.id for q in move.quant_ids if q.package_id and q.qty > 0])
return pack_obj._check_location_constraint(cr, uid, list(packs), context=context) return pack_obj._check_location_constraint(cr, uid, list(packs), context=context)
@profile(immediate=True)
def action_done(self, cr, uid, ids, context=None): def action_done(self, cr, uid, ids, context=None):
""" Process completly the moves given as ids and if all moves are done, it will finish the picking. """ Process completly the moves given as ids and if all moves are done, it will finish the picking.
""" """
@ -2002,6 +2043,7 @@ class stock_move(osv.osv):
for link in move.linked_move_operation_ids: for link in move.linked_move_operation_ids:
operations.add(link.operation_id) operations.add(link.operation_id)
#Sort operations according to entire packages first, then package + lot, package only, lot only #Sort operations according to entire packages first, then package + lot, package only, lot only
operations = list(operations) operations = list(operations)
operations.sort(key = lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0)) operations.sort(key = lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
@ -2035,7 +2077,10 @@ class stock_move(osv.osv):
fallback_domain = [('reservation_id', '=', False)] fallback_domain = [('reservation_id', '=', False)]
self.check_tracking(cr, uid, move, move.restrict_lot_id.id, context=context) self.check_tracking(cr, uid, move, move.restrict_lot_id.id, context=context)
qty = move_qty[move.id] qty = move_qty[move.id]
quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain, prefered_domain=prefered_domain, fallback_domain=fallback_domain, restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context) if move.location_id.usage in ('supplier', 'inventory', 'production'):
quants = [(None, move.product_uom_qty)]
else:
quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain, prefered_domain=prefered_domain, fallback_domain=fallback_domain, restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
quant_obj.quants_move(cr, uid, quants, move, lot_id=move.restrict_lot_id.id, owner_id=move.restrict_partner_id.id, context=context) quant_obj.quants_move(cr, uid, quants, move, lot_id=move.restrict_lot_id.id, owner_id=move.restrict_partner_id.id, context=context)
#unreserve the quants and make them available for other operations/moves #unreserve the quants and make them available for other operations/moves
quant_obj.quants_unreserve(cr, uid, move, context=context) quant_obj.quants_unreserve(cr, uid, move, context=context)
@ -2139,7 +2184,7 @@ class stock_move(osv.osv):
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
context = context or {} context = context or {}
uom_qty = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, qty, move.product_uom.id) uom_qty = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, qty, move.product_uom)
uos_qty = uom_qty * move.product_uos_qty / move.product_uom_qty uos_qty = uom_qty * move.product_uos_qty / move.product_uom_qty
defaults = { defaults = {
@ -3250,9 +3295,9 @@ class stock_package(osv.osv):
def _get_packages(self, cr, uid, ids, context=None): def _get_packages(self, cr, uid, ids, context=None):
"""Returns packages from quants for store""" """Returns packages from quants for store"""
res = set() res = set()
for quant in self.browse(cr, uid, ids, context=context): for quant in self.read(cr, uid, ids, ['package_id'], context=context):
if quant.package_id: if quant['package_id']:
res.add(quant.package_id.id) res.add(quant['package_id'][0])
return list(res) return list(res)
def _get_packages_to_relocate(self, cr, uid, ids, context=None): def _get_packages_to_relocate(self, cr, uid, ids, context=None):
@ -3417,6 +3462,15 @@ class stock_pack_operation(osv.osv):
res[record.move_id.product_id.id] -= record.qty res[record.move_id.product_id.id] -= record.qty
return res return res
def _get_remaining_qty_product_uom(self, cr, uid, ops, context=None):
uom_obj = self.pool.get('product.uom')
qty = ops.product_qty
if ops.product_uom_id:
qty = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context)
for record in ops.linked_move_operation_ids:
qty -= record.qty
return qty
def _get_remaining_qty(self, cr, uid, ids, name, args, context=None): def _get_remaining_qty(self, cr, uid, ids, name, args, context=None):
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
res = {} res = {}
@ -3429,12 +3483,12 @@ class stock_pack_operation(osv.osv):
else: else:
qty = ops.product_qty qty = ops.product_qty
if ops.product_uom_id: if ops.product_uom_id:
qty = uom_obj._compute_qty(cr, uid, ops.product_uom_id.id, ops.product_qty, ops.product_id.uom_id.id) qty = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context)
for record in ops.linked_move_operation_ids: for record in ops.linked_move_operation_ids:
qty -= record.qty qty -= record.qty
#converting the remaining quantity in the pack operation UoM #converting the remaining quantity in the pack operation UoM
if ops.product_uom_id: if ops.product_uom_id:
qty = uom_obj._compute_qty(cr, uid, ops.product_id.uom_id.id, qty, ops.product_uom_id.id) qty = uom_obj._compute_qty_obj(cr, uid, ops.product_id.uom_id, qty, ops.product_uom_id, context=context)
res[ops.id] = qty res[ops.id] = qty
return res return res
@ -3506,10 +3560,12 @@ class stock_pack_operation(osv.osv):
qty_to_assign = qty qty_to_assign = qty
for move in sorted_moves: for move in sorted_moves:
if move.product_id.id == product_id and move.state not in ['done', 'cancel']: if move.product_id.id == product_id and move.state not in ['done', 'cancel']:
qty_on_link = min(move.remaining_qty, qty_to_assign) qty_on_link = min(qty_move_rem[move.id], qty_to_assign)
link_obj.create(cr, uid, {'move_id': move.id, 'operation_id': op.id, 'qty': qty_on_link}, context=context) cr.execute("""insert into stock_move_operation_link (move_id, operation_id, qty) values
(%s, %s, %s)""", (move.id, op.id, qty_on_link,))
qty_move_rem[move.id] -= qty_on_link
# link_obj.create(cr, uid, {'move_id': move.id, 'operation_id': op.id, 'qty': qty_on_link}, context=context)
qty_to_assign -= qty_on_link qty_to_assign -= qty_on_link
move.refresh()
if qty_to_assign <= 0: if qty_to_assign <= 0:
break break
@ -3521,8 +3577,9 @@ class stock_pack_operation(osv.osv):
if not quants_done.get(quant.id): if not quants_done.get(quant.id):
quants_done[quant.id] = 0 quants_done[quant.id] = 0
link_obj.create(cr, uid, {'move_id': quant.reservation_id.id, 'operation_id': ops.id, 'qty': quant.qty}, context=context) link_obj.create(cr, uid, {'move_id': quant.reservation_id.id, 'operation_id': ops.id, 'qty': quant.qty}, context=context)
qty_move_rem[quant.reservation_id.id] -= quant.qty
else: else:
qty = uom_obj._compute_qty(cr, uid, ops.product_uom_id.id, ops.product_qty, ops.product_id.uom_id.id) qty = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context)
#Check moves with same product #Check moves with same product
for move in [x for x in ops.picking_id.move_lines if ops.product_id.id == x.product_id.id]: for move in [x for x in ops.picking_id.move_lines if ops.product_id.id == x.product_id.id]:
for quant in move.reserved_quant_ids: for quant in move.reserved_quant_ids:
@ -3548,6 +3605,7 @@ class stock_pack_operation(osv.osv):
quants_done[quant.id] = 0 quants_done[quant.id] = 0
qty -= qty_todo qty -= qty_todo
link_obj.create(cr, uid, {'move_id': quant.reservation_id.id, 'operation_id': ops.id, 'qty': qty_todo}, context=context) link_obj.create(cr, uid, {'move_id': quant.reservation_id.id, 'operation_id': ops.id, 'qty': qty_todo}, context=context)
qty_move_rem[quant.reservation_id.id] -= qty_todo
link_obj = self.pool.get('stock.move.operation.link') link_obj = self.pool.get('stock.move.operation.link')
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
@ -3555,32 +3613,42 @@ class stock_pack_operation(osv.osv):
quant_obj = self.pool.get('stock.quant') quant_obj = self.pool.get('stock.quant')
quants_done = {} quants_done = {}
qty_rem = {}
qty_move_rem = {}
operations = self.browse(cr, uid, op_ids, context=context) operations = self.browse(cr, uid, op_ids, context=context)
operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0)) operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
sorted_moves = [] sorted_moves = []
for op in operations: for op in operations:
if not sorted_moves: if not sorted_moves:
#sort moves in order to process first the ones that have already reserved quants #sort moves in order to process first the ones that have already reserved quants
for move in op.picking_id.move_lines:
prod_qty = move.product_qty
qty_rem[move.id] = prod_qty - move.reserved_availability
qty_move_rem[move.id] = prod_qty
sorted_moves = op.picking_id.move_lines sorted_moves = op.picking_id.move_lines
sorted_moves.sort(key=lambda x: x.product_qty - x.reserved_availability) sorted_moves.sort(key=lambda x: qty_rem[x.id])
to_unlink_ids = [x.id for x in op.linked_move_operation_ids] to_unlink_ids = [x.id for x in op.linked_move_operation_ids]
if to_unlink_ids: if to_unlink_ids:
link_obj.unlink(cr, uid, to_unlink_ids, context=context) link_obj.unlink(cr, uid, to_unlink_ids, context=context)
_check_quants_reserved(op) _check_quants_reserved(op)
quants_reserve_ok = True
for op in operations: for op in operations:
op.refresh() op.refresh()
if op.product_id: if op.product_id:
#TODO: Remaining qty: UoM conversions are done twice #TODO: Remaining qty: UoM conversions are done twice
normalized_qty = uom_obj._compute_qty(cr, uid, op.product_uom_id.id, op.remaining_qty, op.product_id.uom_id.id) normalized_qty = self._get_remaining_qty_product_uom(cr, uid, op, context)
if normalized_qty > 0: if normalized_qty > 0:
quants_reserve_ok = False
_create_link_for_product(op.product_id.id, normalized_qty) _create_link_for_product(op.product_id.id, normalized_qty)
elif op.package_id: elif op.package_id:
prod_quants = self._get_remaining_prod_quantities(cr, uid, op, context=context) prod_quants = self._get_remaining_prod_quantities(cr, uid, op, context=context)
for product_id, qty in prod_quants.items(): for product_id, qty in prod_quants.items():
if qty > 0: if qty > 0:
quants_reserve_ok = False
_create_link_for_product(product_id, qty) _create_link_for_product(product_id, qty)
return quants_reserve_ok
def process_packaging(self, cr, uid, operation, quants, context=None): def process_packaging(self, cr, uid, operation, quants, context=None):
''' Process the packaging of a given operation, after the quants have been moved. If there was not enough quants found ''' Process the packaging of a given operation, after the quants have been moved. If there was not enough quants found

View File

@ -0,0 +1,23 @@
# -*- 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/>.
#
##############################################################################
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,47 @@
# -*- 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/>.
#
##############################################################################
{
'name': 'Dropshipping Module',
'version': '1.0',
'category': 'Hidden',
'summary': 'Dropshipping',
'description': """
Manage sales quotations and stock locations
==========================================
This adds the route to make dropshipping sales orders in which the product sold are directly transfered from the reseller to the customer (direct delivery) without creating any internal document for the transfer.
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images': [],
'depends': ['stock_dropshipping'],
'init_xml': [],
'data': [],
'demo_xml': [],
'test': [
'test/megatestmtobuy.yml'
],
'installable': True,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,286 @@
-
I first create a warehouse with pick-pack-ship and reception in 2 steps
-
!record {model: stock.warehouse, id: mwh_pps}:
name: Mega WareHouse PickPackShip
code: mwhpps
reception_steps: 'two_steps'
delivery_steps: 'pick_pack_ship'
-
Next I create a new product in this warehouse
-
!record {model: product.product, id: mprod_mto}:
name: "My Product"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
I will create another product in this warehouse
-
!record {model: product.product, id: mprod_mto2}:
name: "My Product 2"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 2
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
I will create another product in this warehouse
-
!record {model: product.product, id: mprod_mto3}:
name: "My Product 3"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 4
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
I will create another product in this warehouse
-
!record {model: product.product, id: mprod_mts}:
name: "My Product MTS"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 2
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
Set routes on product to be MTO and Buy
-
!python {model: product.product}: |
route_warehouse0_buy = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).buy_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('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3')], { 'route_ids': [(6, 0, [route_warehouse0_mto,route_warehouse0_buy])]}, context=context)
-
Create a sales order with 36 lines.
-
!record {model: sale.order, id: sale_order_product_mto2}:
partner_id: base.res_partner_3
note: Create Sales order
warehouse_id: mwh_pps
order_line:
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 99.00
- product_id: mprod_mto3
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
- product_id: mprod_mto3
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mts
product_uom_qty: 100.00
- product_id: mprod_mts
product_uom_qty: 67.00
- product_id: mprod_mts
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mto2
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto3
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mts
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mts
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto3
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto3
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
- product_id: mprod_mto2
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto3
product_uom_qty: 67.00
- product_id: mprod_mto2
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
-
Confirm the sale order
-
!workflow {model: sale.order, action: order_confirm, ref: sale_order_product_mto2}
-
Check sales order is confirmed
-
!python {model: sale.order}:
print self.browse(cr, uid, ref('sale_order_product_mto2')).state
-
Create a sales order with 36 lines.
-
!record {model: sale.order, id: sale_order_product_mto3}:
partner_id: base.res_partner_3
note: Create Sales order
warehouse_id: mwh_pps
order_line:
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
- product_id: mprod_mto
product_uom_qty: 500.00
route_id: stock_dropshipping.route_drop_shipping
-
Confirm the sale order
-
!workflow {model: sale.order, action: order_confirm, ref: sale_order_product_mto3}
-
Check sales order is confirmed
-
!python {model: sale.order}:
print self.browse(cr, uid, ref('sale_order_product_mto3')).state
-
Confirm all purchase orders that are in draft related
-
!python {model: purchase.order}: |
po_ids = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2')), ('state', '=', 'draft')])
self.signal_purchase_confirm(cr, uid, po_ids)
-
Doing all incoming shipments
-
!python {model: purchase.order.line}: |
import time
beforebefore = time.time()
print time.time()
prod_list = [ref('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3'), ref('mprod_mts')]
po_line_ids = self.search(cr, uid, [('product_id', 'in', prod_list)])
# Search all related moves
move_obj = self.pool.get("stock.move")
related_moves = move_obj.search(cr, uid, [('purchase_line_id', 'in', po_line_ids)])
moves = move_obj.browse(cr, uid, related_moves)
related_pickings = [x.picking_id for x in moves]
pickings = list(set(related_pickings))
op_obj = self.pool.get('stock.pack.operation')
pack_obj = self.pool.get('stock.quant.package')
pick_obj = self.pool.get('stock.picking')
# Process those pickings and put in boxes of 20 pieces
for pick in pickings:
pick_obj.do_prepare_partial(cr, uid, [pick.id])
#for ops in pick.pack_operation_ids:
# pick
# while ops.product_qty > 100:
# op_obj.write(cr, uid, [ops.id], {'product_qty': ops.product_qty - 100})
# pack_id = pack_obj.create(cr, uid, {}, context=context)
# op_obj.copy(cr, uid, ops.id, {'product_qty': 100, 'result_package_id': pack_id})
# print "copy"
# ops.refresh()
print "moves", len(pick.move_lines), "pack_ops", len(pick.pack_operation_ids), time.time()
pick.refresh()
pick_obj.do_transfer(cr, uid, [pick.id])
print "Done transfer", time.time()
print "totaltime", time.time() - beforebefore
#Search all dests of moves
move_dest_ids = [x.move_dest_id for x in moves]
move_dest_ids = list(set(move_dest_ids))
related_dest_pickings = [x.picking_id for x in move_dest_ids if x.picking_id]
picks = list(set(related_dest_pickings))
print "picks", picks
for pick in picks:
pick_obj.do_prepare_partial(cr, uid, [pick.id])
pick_obj.do_transfer(cr, uid, [pick.id])
print "after second transfer", time.time()
move_dest_ids = [x.move_dest_id.move_dest_id for x in moves if x.move_dest_id]
move_dest_ids = list(set(move_dest_ids))
related_dest_pickings = [x.picking_id for x in move_dest_ids if x.picking_id]
picks = list(set(related_dest_pickings))
print "picks", picks
for pick in picks:
pick_obj.do_prepare_partial(cr, uid, [pick.id])
pick_obj.do_transfer(cr, uid, [pick.id])
print "after third transfer", time.time()

View File

@ -0,0 +1,203 @@
-
I first create a warehouse with pick-pack-ship and reception in 2 steps
-
!record {model: stock.warehouse, id: mwh_pps}:
name: Mega WareHouse Simple
code: mwhpps
-
Next I create a new product in this warehouse
-
!record {model: product.product, id: mprod_mto}:
name: "My Product"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 1
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
I will create another product in this warehouse
-
!record {model: product.product, id: mprod_mto2}:
name: "My Product 2"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 2
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
I will create another product in this warehouse
-
!record {model: product.product, id: mprod_mto3}:
name: "My Product 3"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 4
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
I will create another product in this warehouse
-
!record {model: product.product, id: mprod_mts}:
name: "My Product MTS"
type: product
uom_id: product.product_uom_unit
uom_po_id: product.product_uom_unit
seller_ids:
- delay: 2
name: base.res_partner_2
min_qty: 2.0
qty: 10.0
-
Set routes on product to be MTO and Buy
-
!python {model: product.product}: |
route_warehouse0_buy = self.pool.get('stock.warehouse').browse(cr, uid, ref('stock.warehouse0')).buy_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('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3')], { 'route_ids': [(6, 0, [route_warehouse0_mto,route_warehouse0_buy])]}, context=context)
-
Create a sales order with 36 lines.
-
!record {model: sale.order, id: sale_order_product_mto2}:
partner_id: base.res_partner_3
note: Create Sales order
warehouse_id: mwh_pps
order_line:
- product_id: mprod_mto
product_uom_qty: 500.00
- product_id: mprod_mto
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mto
product_uom_qty: 500.00
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 99.00
- product_id: mprod_mto3
product_uom_qty: 500.00
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
- product_id: mprod_mto3
product_uom_qty: 500.00
- product_id: mprod_mts
product_uom_qty: 100.00
- product_id: mprod_mts
product_uom_qty: 67.00
- product_id: mprod_mts
product_uom_qty: 500.00
- product_id: mprod_mto
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mto2
product_uom_qty: 500.00
- product_id: mprod_mto3
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mts
product_uom_qty: 500.00
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mts
product_uom_qty: 500.00
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto
product_uom_qty: 67.00
- product_id: mprod_mto
product_uom_qty: 500.00
- product_id: mprod_mto3
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
- product_id: mprod_mto
product_uom_qty: 500.00
- product_id: mprod_mto3
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
- product_id: mprod_mto2
product_uom_qty: 500.00
- product_id: mprod_mto2
product_uom_qty: 100.00
- product_id: mprod_mto3
product_uom_qty: 67.00
- product_id: mprod_mto2
product_uom_qty: 500.00
- product_id: mprod_mto
product_uom_qty: 100.00
- product_id: mprod_mto2
product_uom_qty: 67.00
-
Confirm the sale order
-
!workflow {model: sale.order, action: order_confirm, ref: sale_order_product_mto2}
-
Check sales order is confirmed
-
!python {model: sale.order}:
print self.browse(cr, uid, ref('sale_order_product_mto2')).state
-
Confirm all purchase orders that are in draft related
-
!python {model: purchase.order}: |
po_ids = self.search(cr, uid, [('partner_id', '=', ref('base.res_partner_2')), ('state', '=', 'draft')])
self.signal_purchase_confirm(cr, uid, po_ids)
-
Doing all incoming shipments
-
!python {model: purchase.order.line}: |
import time
beforebefore = time.time()
print time.time()
prod_list = [ref('mprod_mto'), ref('mprod_mto2'), ref('mprod_mto3'), ref('mprod_mts')]
po_line_ids = self.search(cr, uid, [('product_id', 'in', prod_list)])
# Search all related moves
move_obj = self.pool.get("stock.move")
related_moves = move_obj.search(cr, uid, [('purchase_line_id', 'in', po_line_ids)])
moves = move_obj.browse(cr, uid, related_moves)
related_pickings = [x.picking_id for x in moves]
pickings = list(set(related_pickings))
op_obj = self.pool.get('stock.pack.operation')
pack_obj = self.pool.get('stock.quant.package')
pick_obj = self.pool.get('stock.picking')
# Process those pickings and put in boxes of 20 pieces
for pick in pickings:
#pick_obj.do_prepare_partial(cr, uid, [pick.id])
#for ops in pick.pack_operation_ids:
# pick
# while ops.product_qty > 100:
# op_obj.write(cr, uid, [ops.id], {'product_qty': ops.product_qty - 100})
# pack_id = pack_obj.create(cr, uid, {}, context=context)
# op_obj.copy(cr, uid, ops.id, {'product_qty': 100, 'result_package_id': pack_id})
# print "copy"
# ops.refresh()
print "moves", len(pick.move_lines), "pack_ops", len(pick.pack_operation_ids), time.time()
pick.refresh()
pick_obj.do_transfer(cr, uid, [pick.id])
print "Done transfer", time.time()
print "totaltime", time.time() - beforebefore
#Search all dests of moves
move_dest_ids = [x.move_dest_id for x in moves]
move_dest_ids = list(set(move_dest_ids))
related_dest_pickings = [x.picking_id for x in move_dest_ids if x.picking_id]
picks = list(set(related_dest_pickings))
for pick in picks:
pick_obj.do_prepare_partial(cr, uid, [pick.id])
pick_obj.do_transfer(cr, uid, [pick.id])
print "after second transfer", time.time()