[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):
""" Compute if the message is unread by the current user. """
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_ids = notif_obj.search(cr, uid, [
('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):
""" Compute if the message is unread by the current user. """
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_ids = notif_obj.search(cr, uid, [
('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."))
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 = {
'type': 'email',
@ -266,7 +266,7 @@ class mail_message(osv.Model):
:return number of message mark as read
"""
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)]
if not create_missing:
domain += [('read', '=', not read)]
@ -294,7 +294,7 @@ class mail_message(osv.Model):
(i.e. when acting on displayed messages not notified)
"""
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)]
if not create_missing:
domain += [('starred', '=', not starred)]
@ -332,7 +332,7 @@ class mail_message(osv.Model):
"""
res_partner_obj = self.pool.get('res.partner')
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
partner_ids = set()
@ -653,7 +653,7 @@ class mail_message(osv.Model):
elif not 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([])
model_ids = {}
@ -714,7 +714,7 @@ class mail_message(osv.Model):
ids = [ids]
not_obj = self.pool.get('mail.notification')
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
message_values = dict.fromkeys(ids, {})

View File

@ -286,7 +286,7 @@ class mail_thread(osv.AbstractModel):
res = []
for field, operator, value in args:
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
res_ids = self.search(cr, uid, [('message_follower_ids', 'in', [partner_id])], context=context)
else: # is not a follower or unknown domain
@ -354,7 +354,7 @@ class mail_thread(osv.AbstractModel):
# subscribe uid unless asked not to
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.append([4, pid])
values['message_follower_ids'] = message_follower_ids
@ -1586,7 +1586,7 @@ class mail_thread(osv.AbstractModel):
mail_followers_obj = self.pool.get('mail.followers')
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]):
try:
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):
""" 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('''
UPDATE mail_notification SET
read=false
@ -1788,7 +1788,7 @@ class mail_thread(osv.AbstractModel):
def message_mark_as_read(self, cr, uid, ids, context=None):
""" 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('''
UPDATE mail_notification SET
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!')
]
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:
return qty
@ -807,7 +808,10 @@ class product_product(osv.osv):
result = []
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:
for s in sellers:
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.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP
class purchase_order(osv.osv):
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):
res = {}
for purchase_id in ids:
picking_ids = set()
move_ids = self.pool.get('stock.move').search(cr, uid, [('purchase_line_id.order_id', '=', purchase_id)], context=context)
for move in self.pool.get('stock.move').browse(cr, uid, move_ids, context=context):
picking_ids.add(move.picking_id.id)
res[purchase_id] = list(picking_ids)
query = """
SELECT picking_id, po.id FROM stock_picking p, stock_move m, purchase_order_line pol, purchase_order po
WHERE po.id in %s and po.id = pol.order_id and pol.id = m.purchase_line_id and m.picking_id = p.id
GROUP BY picking_id, po.id
"""
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
STATE_SELECTION = [

View File

@ -35,7 +35,7 @@ class stock_move(osv.osv):
ids = [ids]
res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
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):
if move.purchase_line_id and move.purchase_line_id.order_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):
picking_ids = set()
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)
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?',
help='Does the picking contains some moves related to a purchase order invoiceable on the reception?',
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),
}),
}

View File

@ -4,4 +4,4 @@
I duplicate 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
"""
if not context:
context = {}
procurement_obj = self.pool.get('procurement.order')
sale_line_obj = self.pool.get('sale.order.line')
for order in self.browse(cr, uid, ids, context=context):
@ -701,7 +703,11 @@ class sale_order(osv.osv):
proc_ids.append(proc_id)
#Confirm procurement order such that rules will be applied on it
#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 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'),
}
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):
res = set()
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)
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)]},
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={
'stock.move': (_get_orders, ['state'], 10),
'procurement.order': (_get_orders_procurements, ['state'], 10)
}),
'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)
invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_id}, context=context)
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):
_inherit = "procurement.order"
_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_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."),

View File

@ -30,9 +30,9 @@ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FO
from openerp import SUPERUSER_ID
import openerp.addons.decimal_precision as dp
import logging
from profilehooks import profile
_logger = logging.getLogger(__name__)
#----------------------------------------------------------
# Incoterms
#----------------------------------------------------------
@ -321,6 +321,8 @@ class stock_quant(osv.osv):
move.refresh()
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)
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):
"""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 new_quant and new_quant.package_id:
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()
return new_quant
@ -536,6 +539,8 @@ class stock_quant(osv.osv):
def quants_unreserve(self, cr, uid, move, context=None):
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)
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 location and (location.usage == 'internal') and location.company_id or False
def _check_location(self, cr, uid, ids, context=None):
for record in self.browse(cr, uid, ids, context=context):
if record.location_id.usage == 'view':
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))
def _check_location(self, cr, uid, location, context=None):
if location.usage == 'view':
raise osv.except_osv(_('Error'), _('You cannot move to a location of type view %s.') % (location.name))
return True
_constraints = [
(_check_location, 'You cannot move products to a location of the type view.', ['location_id'])
]
#----------------------------------------------------------
# Stock Picking
@ -644,19 +644,21 @@ class stock_picking(osv.osv):
'''
res = {}
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'
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'
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'
continue
order = {'confirmed': 0, 'waiting': 1, 'assigned': 2}
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':
res[pick.id] = order_inv[min(lst)]
else:
@ -666,24 +668,17 @@ class stock_picking(osv.osv):
res[pick.id] = order_inv[max(lst)]
if not all(x == 2 for x in lst):
#if all moves aren't assigned, check if we have one product partially available
for move in pick.move_lines:
if move.reserved_quant_ids:
for move in move_lines:
if move[1]:
res[pick.id] = 'partially_available'
break
return res
def _get_pickings(self, cr, uid, ids, context=None):
res = set()
for move in self.browse(cr, uid, ids, context=context):
if move.picking_id:
res.add(move.picking_id.id)
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)
for move in self.read(cr, uid, ids, ['picking_id'], context=context):
if move['picking_id']:
res.add(move['picking_id'][0])
return list(res)
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)]}),
'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={
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type', 'move_lines'], 20),
'stock.move': (_get_pickings, ['state', 'picking_id'], 20),
'stock.quant': (_get_pickings_from_quant, ['reservation_id'], 20)}, selection=[
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20),
'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)}, selection=[
('draft', 'Draft'),
('cancel', 'Cancelled'),
('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),
'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",
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_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)]}),
@ -846,8 +840,6 @@ class stock_picking(osv.osv):
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']]
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
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):
pack_op_obj = self.pool.get('stock.pack.operation')
quants_res = True
for picking in self.browse(cr, uid, picking_ids, context=context):
op_ids = [op.id for op in picking.pack_operation_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):
'''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)
continue
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)
self._create_extra_moves(cr, uid, picking, context=context)
if not quants_ok:
self._create_extra_moves(cr, uid, picking, context=context)
picking.refresh()
#split move lines eventually
todo_move_ids = []
toassign_move_ids = []
no_rereserve = True
for move in picking.move_lines:
remaining_qty = move.remaining_qty
if move.state in ('done', 'cancel'):
#ignore stock moves cancelled or already done
continue
elif move.state == 'draft':
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'):
todo_move_ids.append(move.id)
elif move.remaining_qty > 0 and move.remaining_qty < move.product_qty:
new_move = stock_move_obj.split(cr, uid, move, move.remaining_qty, context=context)
elif remaining_qty > 0 and remaining_qty < move.product_qty:
no_rereserve = False
new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context)
todo_move_ids.append(move.id)
#Assign move as it was assigned before
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'):
self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context)
elif context.get('do_only_split'):
@ -1339,7 +1339,7 @@ class stock_move(osv.osv):
uom_obj = self.pool.get('product.uom')
res = {}
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
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:
qty -= record.qty
#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
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
continue
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)
#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)
@ -1397,7 +1397,7 @@ class stock_move(osv.osv):
if move.reserved_availability:
if move.reserved_availability != total_available:
#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)
else:
#all available quantity is assigned
@ -1424,6 +1424,7 @@ class stock_move(osv.osv):
res += [x.id for x in picking.move_lines]
return res
_columns = {
'name': fields.char('Description', required=True, select=True),
'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"),
'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
'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'),
help='Quantity in the default UoM of the product'),
'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"\
"* Available: When products are reserved, it is set to \'Available\'.\n"\
"* 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
'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
@ -1546,16 +1547,6 @@ class stock_move(osv.osv):
'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):
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
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):
#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 frozen_fields.intersection(vals):
raise osv.except_osv(_('Operation Forbidden!'),
@ -1789,6 +1791,10 @@ class stock_move(osv.osv):
return {'value': result}
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:
return False
context = context or {}
@ -1814,6 +1820,40 @@ class stock_move(osv.osv):
move.write({'picking_id': pick})
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):
""" On change of Scheduled Date gives a Move 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])
return pack_obj._check_location_constraint(cr, uid, list(packs), context=context)
@profile(immediate=True)
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.
"""
@ -2002,6 +2043,7 @@ class stock_move(osv.osv):
for link in move.linked_move_operation_ids:
operations.add(link.operation_id)
#Sort operations according to entire packages first, then package + lot, package only, lot only
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))
@ -2035,7 +2077,10 @@ class stock_move(osv.osv):
fallback_domain = [('reservation_id', '=', False)]
self.check_tracking(cr, uid, move, move.restrict_lot_id.id, context=context)
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)
#unreserve the quants and make them available for other operations/moves
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')
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
defaults = {
@ -3250,9 +3295,9 @@ class stock_package(osv.osv):
def _get_packages(self, cr, uid, ids, context=None):
"""Returns packages from quants for store"""
res = set()
for quant in self.browse(cr, uid, ids, context=context):
if quant.package_id:
res.add(quant.package_id.id)
for quant in self.read(cr, uid, ids, ['package_id'], context=context):
if quant['package_id']:
res.add(quant['package_id'][0])
return list(res)
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
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):
uom_obj = self.pool.get('product.uom')
res = {}
@ -3429,12 +3483,12 @@ class stock_pack_operation(osv.osv):
else:
qty = ops.product_qty
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:
qty -= record.qty
#converting the remaining quantity in the pack operation UoM
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
return res
@ -3506,10 +3560,12 @@ class stock_pack_operation(osv.osv):
qty_to_assign = qty
for move in sorted_moves:
if move.product_id.id == product_id and move.state not in ['done', 'cancel']:
qty_on_link = min(move.remaining_qty, qty_to_assign)
link_obj.create(cr, uid, {'move_id': move.id, 'operation_id': op.id, 'qty': qty_on_link}, context=context)
qty_on_link = min(qty_move_rem[move.id], qty_to_assign)
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
move.refresh()
if qty_to_assign <= 0:
break
@ -3521,8 +3577,9 @@ class stock_pack_operation(osv.osv):
if not quants_done.get(quant.id):
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)
qty_move_rem[quant.reservation_id.id] -= quant.qty
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
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:
@ -3548,6 +3605,7 @@ class stock_pack_operation(osv.osv):
quants_done[quant.id] = 0
qty -= qty_todo
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')
uom_obj = self.pool.get('product.uom')
@ -3555,32 +3613,42 @@ class stock_pack_operation(osv.osv):
quant_obj = self.pool.get('stock.quant')
quants_done = {}
qty_rem = {}
qty_move_rem = {}
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))
sorted_moves = []
for op in operations:
if not sorted_moves:
#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.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]
if to_unlink_ids:
link_obj.unlink(cr, uid, to_unlink_ids, context=context)
_check_quants_reserved(op)
quants_reserve_ok = True
for op in operations:
op.refresh()
if op.product_id:
#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:
quants_reserve_ok = False
_create_link_for_product(op.product_id.id, normalized_qty)
elif op.package_id:
prod_quants = self._get_remaining_prod_quantities(cr, uid, op, context=context)
for product_id, qty in prod_quants.items():
if qty > 0:
quants_reserve_ok = False
_create_link_for_product(product_id, qty)
return quants_reserve_ok
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

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()