from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
-from openerp import SUPERUSER_ID
+from openerp import SUPERUSER_ID, api
import openerp.addons.decimal_precision as dp
from openerp.addons.procurement import procurement
import logging
_name = "stock.incoterms"
_description = "Incoterms"
_columns = {
- 'name': fields.char('Name', size=64, required=True, help="Incoterms are series of sales terms. They are used to divide transaction costs and responsibilities between buyer and seller and reflect state-of-the-art transportation practices."),
+ 'name': fields.char('Name', required=True, help="Incoterms are series of sales terms. They are used to divide transaction costs and responsibilities between buyer and seller and reflect state-of-the-art transportation practices."),
'code': fields.char('Code', size=3, required=True, help="Incoterm Standard Code"),
'active': fields.boolean('Active', help="By unchecking the active field, you may hide an INCOTERM you will not use."),
}
return res
_columns = {
- 'name': fields.char('Location Name', size=64, required=True, translate=True),
+ 'name': fields.char('Location Name', required=True, translate=True),
'active': fields.boolean('Active', help="By unchecking the active field, you may hide a location without deleting it."),
- 'usage': fields.selection([('supplier', 'Supplier Location'), ('view', 'View'), ('internal', 'Internal Location'), ('customer', 'Customer Location'), ('inventory', 'Inventory'), ('procurement', 'Procurement'), ('production', 'Production'), ('transit', 'Transit Location')], 'Location Type', required=True,
- help="""* Supplier Location: Virtual location representing the source location for products coming from your suppliers
+ 'usage': fields.selection([
+ ('supplier', 'Supplier Location'),
+ ('view', 'View'),
+ ('internal', 'Internal Location'),
+ ('customer', 'Customer Location'),
+ ('inventory', 'Inventory'),
+ ('procurement', 'Procurement'),
+ ('production', 'Production'),
+ ('transit', 'Transit Location')],
+ 'Location Type', required=True,
+ help="""* Supplier Location: Virtual location representing the source location for products coming from your suppliers
\n* View: Virtual location used to create a hierarchical structures for your warehouse, aggregating its child locations ; can't directly contain products
\n* Internal Location: Physical locations inside your own warehouses,
\n* Customer Location: Virtual location representing the destination location for products sent to your customers
\n* Production: Virtual counterpart location for production operations: this location consumes the raw material and produces finished products
\n* Transit Location: Counterpart location that should be used in inter-companies or inter-warehouses operations
""", select=True),
-
'complete_name': fields.function(_complete_name, type='char', string="Location Name",
store={'stock.location': (_get_sublocations, ['name', 'location_id', 'active'], 10)}),
'location_id': fields.many2one('stock.location', 'Parent Location', select=True, ondelete='cascade'),
return self._default_removal_strategy(cr, uid, context=context)
+ def get_warehouse(self, cr, uid, location, context=None):
+ """
+ Returns warehouse id of warehouse that contains location
+ :param location: browse record (stock.location)
+ """
+ wh_obj = self.pool.get("stock.warehouse")
+ whs = wh_obj.search(cr, uid, [('view_location_id.parent_left', '<=', location.parent_left),
+ ('view_location_id.parent_right', '>=', location.parent_left)], context=context)
+ return whs and whs[0] or False
+
#----------------------------------------------------------
# Routes
#----------------------------------------------------------
_columns = {
'name': fields.char('Route Name', required=True),
'sequence': fields.integer('Sequence'),
- 'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules'),
+ 'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules', copy=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the route without removing it."),
- 'push_ids': fields.one2many('stock.location.path', 'route_id', 'Push Rules'),
+ 'push_ids': fields.one2many('stock.location.path', 'route_id', 'Push Rules', copy=True),
'product_selectable': fields.boolean('Applicable on Product'),
'product_categ_selectable': fields.boolean('Applicable on Product Category'),
'warehouse_selectable': fields.boolean('Applicable on Warehouse'),
return res
def _calc_inventory_value(self, cr, uid, ids, name, attr, context=None):
+ context = dict(context or {})
res = {}
uid_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
for quant in self.browse(cr, uid, ids, context=context):
_columns = {
'name': fields.function(_get_quant_name, type='char', string='Identifier'),
- 'product_id': fields.many2one('product.product', 'Product', required=True, ondelete="restrict"),
- 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict"),
- 'qty': fields.float('Quantity', required=True, help="Quantity of products in this quant, in the default unit of measure of the product"),
- 'package_id': fields.many2one('stock.quant.package', string='Package', help="The package containing this quant"),
- 'packaging_type_id': fields.related('package_id', 'packaging_id', type='many2one', relation='product.packaging', string='Type of packaging', store=True),
- 'reservation_id': fields.many2one('stock.move', 'Reserved for Move', help="The move the quant is reserved for"),
- 'lot_id': fields.many2one('stock.production.lot', 'Lot'),
+ 'product_id': fields.many2one('product.product', 'Product', required=True, ondelete="restrict", readonly=True, select=True),
+ 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True),
+ 'qty': fields.float('Quantity', required=True, help="Quantity of products in this quant, in the default unit of measure of the product", readonly=True, select=True),
+ 'package_id': fields.many2one('stock.quant.package', string='Package', help="The package containing this quant", readonly=True, select=True),
+ 'packaging_type_id': fields.related('package_id', 'packaging_id', type='many2one', relation='product.packaging', string='Type of packaging', readonly=True, store=True),
+ 'reservation_id': fields.many2one('stock.move', 'Reserved for Move', help="The move the quant is reserved for", readonly=True, select=True),
+ 'lot_id': fields.many2one('stock.production.lot', 'Lot', readonly=True, select=True),
'cost': fields.float('Unit Cost'),
- 'owner_id': fields.many2one('res.partner', 'Owner', help="This is the owner of the quant"),
+ 'owner_id': fields.many2one('res.partner', 'Owner', help="This is the owner of the quant", readonly=True, select=True),
- 'create_date': fields.datetime('Creation Date'),
- 'in_date': fields.datetime('Incoming Date'),
+ 'create_date': fields.datetime('Creation Date', readonly=True),
+ 'in_date': fields.datetime('Incoming Date', readonly=True, select=True),
'history_ids': fields.many2many('stock.move', 'stock_quant_move_rel', 'quant_id', 'move_id', 'Moves', help='Moves that operate(d) on this quant'),
- 'company_id': fields.many2one('res.company', 'Company', help="The company to which the quants belong", required=True),
+ 'company_id': fields.many2one('res.company', 'Company', help="The company to which the quants belong", required=True, readonly=True, select=True),
'inventory_value': fields.function(_calc_inventory_value, string="Inventory Value", type='float', readonly=True),
# Used for negative quants to reconcile after compensated by a new positive one
- 'propagated_from_id': fields.many2one('stock.quant', 'Linked Quant', help='The negative quant this is coming from'),
- 'negative_move_id': fields.many2one('stock.move', 'Move Negative Quant', help='If this is a negative quant, this will be the move that caused this negative quant.'),
- 'negative_dest_location_id': fields.related('negative_move_id', 'location_dest_id', type='many2one', relation='stock.location', string="Negative Destination Location", help="Technical field used to record the destination location of a move that created a negative quant"),
+ 'propagated_from_id': fields.many2one('stock.quant', 'Linked Quant', help='The negative quant this is coming from', readonly=True, select=True),
+ 'negative_move_id': fields.many2one('stock.move', 'Move Negative Quant', help='If this is a negative quant, this will be the move that caused this negative quant.', readonly=True),
+ 'negative_dest_location_id': fields.related('negative_move_id', 'location_dest_id', type='many2one', relation='stock.location', string="Negative Destination Location", readonly=True,
+ help="Technical field used to record the destination location of a move that created a negative quant"),
}
_defaults = {
self.pool.get('stock.move').write(cr, uid, [move.id], {'partially_available': True}, context=context)
def quants_move(self, cr, uid, quants, move, location_to, location_from=False, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, context=None):
- """Moves all given stock.quant in the given destination location.
+ """Moves all given stock.quant in the given destination location. Unreserve from current move.
:param quants: list of tuple(browse record(stock.quant) or None, quantity to move)
:param move: browse record (stock.move)
:param location_to: browse record (stock.location) depicting where the quants have to be moved
def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, context=None):
vals = {'location_id': location_dest_id.id,
'history_ids': [(4, move.id)],
- 'package_id': dest_package_id}
+ 'package_id': dest_package_id,
+ 'reservation_id': False}
self.write(cr, SUPERUSER_ID, [q.id for q in quants], vals, context=context)
def quants_get_prefered_domain(self, cr, uid, location, product, qty, domain=None, prefered_domain_list=[], restrict_lot_id=False, restrict_partner_id=False, context=None):
move = m
return move
+ @api.cr_uid_ids_context
def _quants_merge(self, cr, uid, solved_quant_ids, solving_quant, context=None):
path = []
for move in solving_quant.history_ids:
raise osv.except_osv(_('Error'), _('You cannot move to a location of type view %s.') % (location.name))
return True
-
#----------------------------------------------------------
# Stock Picking
#----------------------------------------------------------
self.pool.get('stock.pack.operation').write(cr, uid, packop_ids, {'owner_id': picking.owner_id.id}, context=context)
_columns = {
- 'name': fields.char('Reference', size=64, select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
- 'origin': fields.char('Source Document', size=64, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True),
- 'backorder_id': fields.many2one('stock.picking', 'Back Order of', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True),
+ 'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
+ 'origin': fields.char('Source Document', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True),
+ 'backorder_id': fields.many2one('stock.picking', 'Back Order of', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True, copy=False),
'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",
+ 'state': fields.function(_state_get, type="selection", copy=False,
store={
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20),
'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)},
* Cancelled: has been cancelled, can't be confirmed anymore"""
),
'priority': fields.function(get_min_max_date, multi="min_max_date", fnct_inv=_set_priority, type='selection', selection=procurement.PROCUREMENT_PRIORITIES, string='Priority',
- store={'stock.move': (_get_pickings, ['priority'], 20)}, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, select=1, help="Priority for this picking. Setting manually a value here would set it as priority for all the moves",
+ store={'stock.move': (_get_pickings, ['priority', 'picking_id'], 20)}, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, select=1, help="Priority for this picking. Setting manually a value here would set it as priority for all the moves",
track_visibility='onchange', required=True),
'min_date': fields.function(get_min_max_date, multi="min_max_date", fnct_inv=_set_min_date,
- 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'),
+ store={'stock.move': (_get_pickings, ['date_expected', 'picking_id'], 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, ['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)]}),
+ store={'stock.move': (_get_pickings, ['date_expected', 'picking_id'], 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('Creation Date', help="Creation Date, usually the time of the order", 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)]}, copy=False),
+ 'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=True),
'quant_reserved_exist': fields.function(_get_quant_reserved_exist, type='boolean', string='Quant already reserved ?', help='technical field used to know if there is already at least one quant reserved on moves of a given picking'),
'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'owner_id': fields.many2one('res.partner', 'Owner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Default Owner"),
# Used to search on pickings
'product_id': fields.related('move_lines', 'product_id', type='many2one', relation='product.product', string='Product'),
- 'recompute_pack_op': fields.boolean('Recompute pack operation?', help='True if reserved quants changed, which mean we might need to recompute the package operations'),
+ 'recompute_pack_op': fields.boolean('Recompute pack operation?', help='True if reserved quants changed, which mean we might need to recompute the package operations', copy=False),
'location_id': fields.related('move_lines', 'location_id', type='many2one', relation='stock.location', string='Location', readonly=True),
'location_dest_id': fields.related('move_lines', 'location_dest_id', type='many2one', relation='stock.location', string='Destination Location', readonly=True),
'group_id': fields.related('move_lines', 'group_id', type='many2one', relation='procurement.group', string='Procurement Group', readonly=True,
}
_defaults = {
- 'name': lambda self, cr, uid, context: '/',
+ 'name': '/',
'state': 'draft',
'move_type': 'direct',
'priority': '1', # normal
('name_uniq', 'unique(name, company_id)', 'Reference must be unique per company!'),
]
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- picking_obj = self.browse(cr, uid, id, context=context)
- if ('name' not in default) or (picking_obj.name == '/'):
- default['name'] = '/'
- if not default.get('backorder_id'):
- default['backorder_id'] = False
- default['pack_operation_ids'] = []
- default['date_done'] = False
- return super(stock_picking, self).copy(cr, uid, id, default, context)
-
def do_print_picking(self, cr, uid, ids, context=None):
'''This function prints the picking list'''
- context = context or {}
- context['active_ids'] = ids
+ context = dict(context or {}, active_ids=ids)
return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_picking', context=context)
'pack_operation_ids': [],
'backorder_id': picking.id,
})
- self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (picking.name), context=context)
+ backorder = self.browse(cr, uid, backorder_id, context=context)
+ self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (backorder.name), context=context)
move_obj = self.pool.get("stock.move")
move_obj.write(cr, uid, backorder_move_ids, {'picking_id': backorder_id}, context=context)
return backorder_id
return False
+ @api.cr_uid_ids_context
def recheck_availability(self, cr, uid, picking_ids, context=None):
self.action_assign(cr, uid, picking_ids, context=context)
self.do_prepare_partial(cr, uid, picking_ids, context=context)
})
return vals
+ @api.cr_uid_ids_context
def open_barcode_interface(self, cr, uid, picking_ids, context=None):
- final_url="/barcode/web/#action=stock.ui&picking_id="+str(picking_ids[0])
+ final_url="/stock/barcode/#action=stock.ui&picking_id="+str(picking_ids[0])
return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
+ @api.cr_uid_ids_context
def do_partial_open_barcode(self, cr, uid, picking_ids, context=None):
self.do_prepare_partial(cr, uid, picking_ids, context=context)
return self.open_barcode_interface(cr, uid, picking_ids, context=context)
+ @api.cr_uid_ids_context
def do_prepare_partial(self, cr, uid, picking_ids, context=None):
context = context or {}
pack_operation_obj = self.pool.get('stock.pack.operation')
self.do_recompute_remaining_quantities(cr, uid, picking_ids, context=context)
self.write(cr, uid, picking_ids, {'recompute_pack_op': False}, context=context)
+ @api.cr_uid_ids_context
def do_unreserve(self, cr, uid, picking_ids, context=None):
"""
Will remove all quants for picking in picking_ids
need_rereserve = False
#sort the operations in order to give higher priority to those with a package, then a serial number
operations = picking.pack_operation_ids
- 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 = sorted(operations, 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))
#delete existing operations to start again from scratch
cr.execute("DELETE FROM stock_move_operation_link WHERE operation_id in %s", (tuple([x.id for x in operations]),))
#check if the quant is matching the operation details
if ops.package_id:
- flag = quant.package_id and bool(package_obj.search(cr, uid, [('id', 'child_of', [ops.package_id.id]), ('id', '=', quant.package_id.id)], context=context)) or False
+ flag = quant.package_id and bool(package_obj.search(cr, uid, [('id', 'child_of', [ops.package_id.id])], context=context)) or False
else:
flag = not quant.package_id.id
flag = flag and ((ops.lot_id and ops.lot_id.id == quant.lot_id.id) or not ops.lot_id)
need_rereserve, all_op_processed = self.recompute_remaining_qty(cr, uid, picking, context=context)
return need_rereserve, all_op_processed
+ @api.cr_uid_ids_context
def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None):
for picking in self.browse(cr, uid, picking_ids, context=context):
if picking.pack_operation_ids:
self.recompute_remaining_qty(cr, uid, picking, context=context)
+ def _prepare_values_extra_move(self, cr, uid, op, product, remaining_qty, context=None):
+ """
+ Creates an extra move when there is no corresponding original move to be copied
+ """
+ picking = op.picking_id
+ res = {
+ 'picking_id': picking.id,
+ 'location_id': picking.location_id.id,
+ 'location_dest_id': picking.location_dest_id.id,
+ 'product_id': product.id,
+ 'product_uom': product.uom_id.id,
+ 'product_uom_qty': remaining_qty,
+ 'name': _('Extra Move: ') + product.name,
+ 'state': 'draft',
+ }
+ return 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
unexpected product transfers (or exceeding quantities) found in the pack operations.
for product_id, remaining_qty in operation_obj._get_remaining_prod_quantities(cr, uid, op, context=context).items():
if remaining_qty > 0:
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
- vals = {
- 'picking_id': picking.id,
- 'location_id': picking.location_id.id,
- 'location_dest_id': picking.location_dest_id.id,
- 'product_id': product_id,
- 'product_uom': product.uom_id.id,
- 'product_uom_qty': remaining_qty,
- 'name': _('Extra Move: ') + product.name,
- 'state': 'draft',
- }
+ vals = self._prepare_values_extra_move(cr, uid, op, product, remaining_qty, context=context)
moves.append(move_obj.create(cr, uid, vals, context=context))
if moves:
move_obj.action_confirm(cr, uid, moves, context=context)
return moves
+ def rereserve_pick(self, cr, uid, ids, context=None):
+ """
+ This can be used to provide a button that rereserves taking into account the existing pack operations
+ """
+ for pick in self.browse(cr, uid, ids, context=context):
+ self.rereserve_quants(cr, uid, pick, move_ids = [x.id for x in pick.move_lines], context=context)
+
def rereserve_quants(self, cr, uid, picking, move_ids=[], context=None):
""" Unreserve quants then try to reassign quants."""
stock_move_obj = self.pool.get('stock.move')
stock_move_obj.do_unreserve(cr, uid, move_ids, context=context)
stock_move_obj.action_assign(cr, uid, move_ids, context=context)
+ @api.cr_uid_ids_context
+ def do_enter_transfer_details(self, cr, uid, picking, context=None):
+ if not context:
+ context = {}
+
+ context.update({
+ 'active_model': self._name,
+ 'active_ids': picking,
+ 'active_id': len(picking) and picking[0] or False
+ })
+
+ created_id = self.pool['stock.transfer_details'].create(cr, uid, {'picking_id': len(picking) and picking[0] or False}, context)
+ return self.pool['stock.transfer_details'].wizard_view(cr, uid, created_id, context)
+
+
+ @api.cr_uid_ids_context
def do_transfer(self, cr, uid, picking_ids, context=None):
"""
If no pack operation, we do simple action_done of the picking
todo_move_ids = []
if not all_op_processed:
todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context)
-
+
picking.refresh()
#split move lines eventually
-
+
toassign_move_ids = []
for move in picking.move_lines:
remaining_qty = move.remaining_qty
todo_move_ids.append(move.id)
#Assign move as it was assigned before
toassign_move_ids.append(new_move)
- if (need_rereserve or not all_op_processed) and not picking.location_id.usage in ("supplier", "production", "inventory"):
- self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
+ if need_rereserve or not all_op_processed:
+ if not picking.location_id.usage in ("supplier", "production", "inventory"):
+ self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
self.do_recompute_remaining_quantities(cr, uid, [picking.id], 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'):
- context.update({'split': todo_move_ids})
+ context = dict(context, split=todo_move_ids)
picking.refresh()
self._create_backorder(cr, uid, picking, context=context)
if toassign_move_ids:
stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context)
return True
+ @api.cr_uid_ids_context
def do_split(self, cr, uid, picking_ids, context=None):
""" just split the picking (create a backorder) without making it 'done' """
if context is None:
#return id of next picking to work on
return self.get_next_picking_for_ui(cr, uid, context=context)
+ @api.cr_uid_ids_context
def action_pack(self, cr, uid, picking_ids, operation_filter_ids=None, context=None):
""" Create a package with the current pack_operation_ids of the picking that aren't yet in a pack.
Used in the barcode scanner UI and the normal interface as well.
stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0, 'lot_id': False}, context=context)
op = stock_operation_obj.browse(cr, uid, new_operation, context=context)
pack_operation_ids.append(op.id)
- for record in op.linked_move_operation_ids:
- stock_move_obj.check_tracking(cr, uid, record.move_id, op.package_id.id or op.lot_id.id, context=context)
+ if op.product_id and op.location_id and op.location_dest_id:
+ stock_move_obj.check_tracking_product(cr, uid, op.product_id, op.lot_id.id, op.location_id, op.location_dest_id, context=context)
package_id = package_obj.create(cr, uid, {}, context=context)
stock_operation_obj.write(cr, uid, pack_operation_ids, {'result_package_id': package_id}, context=context)
return True
_inherit = ['mail.thread']
_description = 'Lot/Serial'
_columns = {
- 'name': fields.char('Serial Number', size=64, required=True, help="Unique Serial Number"),
- 'ref': fields.char('Internal Reference', size=256, help="Internal reference number in case it differs from the manufacturer's serial number"),
+ 'name': fields.char('Serial Number', required=True, help="Unique Serial Number"),
+ 'ref': fields.char('Internal Reference', help="Internal reference number in case it differs from the manufacturer's serial number"),
'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type', '<>', 'service')]),
'quant_ids': fields.one2many('stock.quant', 'lot_id', 'Quants', readonly=True),
'create_date': fields.datetime('Creation Date'),
res.append((line.id, name))
return res
- def create(self, cr, uid, vals, context=None):
- if vals.get('product_id') and not vals.get('price_unit'):
- prod_obj = self.pool.get('product.product')
- vals['price_unit'] = prod_obj.browse(cr, uid, vals['product_id'], context=context).standard_price
- return super(stock_move, self).create(cr, uid, vals, context=context)
-
def _quantity_normalize(self, cr, uid, ids, name, args, context=None):
uom_obj = self.pool.get('product.uom')
res = {}
'partner_id': fields.many2one('res.partner', 'Destination Address ', states={'done': [('readonly', True)]}, help="Optional address where goods are to be delivered, specifically used for allotment"),
- 'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Optional: next stock move when chaining them", select=True),
+ 'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Optional: next stock move when chaining them", select=True, copy=False),
'move_orig_ids': fields.one2many('stock.move', 'move_dest_id', 'Original Move', help="Optional: previous stock move when chaining them", select=True),
'picking_id': fields.many2one('stock.picking', 'Reference', select=True, states={'done': [('readonly', True)]}),
('confirmed', 'Waiting Availability'),
('assigned', 'Available'),
('done', 'Done'),
- ], 'Status', readonly=True, select=True,
+ ], 'Status', readonly=True, select=True, copy=False,
help= "* New: When the stock move is created and not yet confirmed.\n"\
"* Waiting Another Move: This state can be seen when a move is waiting for another one, for example in a chained flow.\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"\
"* 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"),
+ 'partially_available': fields.boolean('Partially Available', readonly=True, help="Checks if the move has some stock reserved", copy=False),
'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),
- 'split_from': fields.many2one('stock.move', string="Move Split From", help="Technical field used to track the origin of a split move, which can be useful in case of debug"),
+ 'split_from': fields.many2one('stock.move', string="Move Split From", help="Technical field used to track the origin of a split move, which can be useful in case of debug", copy=False),
'backorder_id': fields.related('picking_id', 'backorder_id', type='many2one', relation="stock.picking", string="Back Order of", select=True),
'origin': fields.char("Source"),
'procure_method': fields.selection([('make_to_stock', 'Default: Take From Stock'), ('make_to_order', 'Advanced: Apply Procurement Rules')], 'Supply Method', required=True,
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
'inventory_id': fields.many2one('stock.inventory', 'Inventory'),
'lot_ids': fields.function(_get_lot_ids, type='many2many', relation='stock.production.lot', string='Lots'),
- 'origin_returned_move_id': fields.many2one('stock.move', 'Origin return move', help='move that created the return move'),
+ 'origin_returned_move_id': fields.many2one('stock.move', 'Origin return move', help='move that created the return move', copy=False),
'returned_move_ids': fields.one2many('stock.move', 'origin_returned_move_id', 'All returned moves', help='Optional: all returned moves created from this move'),
'reserved_availability': fields.function(_get_reserved_availability, type='float', string='Quantity Reserved', readonly=True, help='Quantity that has already been reserved for this move'),
'availability': fields.function(_get_product_availability, type='float', string='Quantity Available', readonly=True, help='Quantity in stock that can still be reserved for this move'),
'date_expected': fields.datetime.now,
'procure_method': 'make_to_stock',
'propagate': True,
+ 'partially_available': False,
}
def _check_uom(self, cr, uid, ids, context=None):
['product_uom']),
]
- def copy_data(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default['move_orig_ids'] = []
- default['quant_ids'] = []
- default['move_dest_id'] = False
- default['reserved_quant_ids'] = []
- default['returned_move_ids'] = []
- default['linked_move_operation_ids'] = []
- default['partially_available'] = False
- if not default.get('origin_returned_move_id'):
- default['origin_returned_move_id'] = False
- default['state'] = 'draft'
- return super(stock_move, self).copy_data(cr, uid, id, default, context)
-
+ @api.cr_uid_ids_context
def do_unreserve(self, cr, uid, move_ids, context=None):
quant_obj = self.pool.get("stock.quant")
for move in self.browse(cr, uid, move_ids, context=context):
self.write(cr, uid, [move.id], {'state': 'confirmed'}, context=context)
def _prepare_procurement_from_move(self, cr, uid, move, context=None):
- origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or "/")
+ origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or move.origin or "/")
group_id = move.group_id and move.group_id.id or False
if move.rule_id:
if move.rule_id.group_propagation_option == 'fixed' and move.rule_id.group_id:
if wh_route_ids:
rules = push_obj.search(cr, uid, domain + [('route_id', 'in', wh_route_ids)], order='route_sequence, sequence', context=context)
if not rules:
- #if no specialized push rule has been found yet, we try to find a general one
- rules = push_obj.search(cr, uid, domain, order='route_sequence, sequence', context=context)
+ #if no specialized push rule has been found yet, we try to find a general one (without route)
+ rules = push_obj.search(cr, uid, domain + [('route_id', '=', False)], order='sequence', context=context)
if rules:
rule = push_obj.browse(cr, uid, rules[0], context=context)
push_obj._apply(cr, uid, rule, move, context=context)
warning.update({
'title': _('Information'),
'message': _("By changing this quantity here, you accept the "
- "new quantity as complete: OpenERP will not "
+ "new quantity as complete: Odoo will not "
"automatically generate a back order.")})
break
product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0]
uos_id = product.uos_id and product.uos_id.id or False
result = {
+ 'name': product.partner_ref,
'product_uom': product.uom_id.id,
'product_uos': uos_id,
'product_uom_qty': 1.00,
'product_uos_qty': self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)['value']['product_uos_qty'],
}
- if not ids:
- result['name'] = product.partner_ref
if loc_id:
result['location_id'] = loc_id
if loc_dest_id:
result['location_dest_id'] = loc_dest_id
return {'value': result}
+ @api.cr_uid_ids_context
def _picking_assign(self, cr, uid, move_ids, procurement_group, location_from, location_to, context=None):
"""Assign a picking on the given move_ids, which is a list of move supposed to share the same procurement_group, location_from and location_to
(and company). Those attributes are also given as parameters.
date_expected = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
return {'value': {'date': date_expected}}
+ def attribute_price(self, cr, uid, move, context=None):
+ """
+ Attribute price to move, important in inter-company moves or receipts with only one partner
+ """
+ if not move.price_unit:
+ price = move.product_id.standard_price
+ self.write(cr, uid, [move.id], {'price_unit': price})
def action_confirm(self, cr, uid, ids, context=None):
""" Confirms stock move or put it in waiting if it's linked to another move.
}
to_assign = {}
for move in self.browse(cr, uid, ids, context=context):
+ self.attribute_price(cr, uid, move, context=context)
state = 'confirmed'
#if the move is preceeded, then it's waiting (if preceeding move is done, then action_assign has been called already and its state is already available)
if move.move_orig_ids:
"""
return self.write(cr, uid, ids, {'state': 'assigned'}, context=context)
- def check_tracking(self, cr, uid, move, lot_id, context=None):
- """ Checks if serial number is assigned to stock move or not and raise an error if it had to.
- """
+ def check_tracking_product(self, cr, uid, product, lot_id, location, location_dest, context=None):
check = False
- if move.product_id.track_all and not move.location_dest_id.usage == 'inventory':
+ if product.track_all and not location_dest.usage == 'inventory':
check = True
- elif move.product_id.track_incoming and move.location_id.usage in ('supplier', 'transit', 'inventory') and move.location_dest_id.usage == 'internal':
+ elif product.track_incoming and location.usage in ('supplier', 'transit', 'inventory') and location_dest.usage == 'internal':
check = True
- elif move.product_id.track_outgoing and move.location_dest_id.usage in ('customer', 'transit') and move.location_id.usage == 'internal':
+ elif product.track_outgoing and location_dest.usage in ('customer', 'transit') and location.usage == 'internal':
check = True
if check and not lot_id:
- raise osv.except_osv(_('Warning!'), _('You must assign a serial number for the product %s') % (move.product_id.name))
+ raise osv.except_osv(_('Warning!'), _('You must assign a serial number for the product %s') % (product.name))
+
+
+ def check_tracking(self, cr, uid, move, lot_id, context=None):
+ """ Checks if serial number is assigned to stock move or not and raise an error if it had to.
+ """
+ self.check_tracking_product(cr, uid, move.product_id, lot_id, move.location_id, move.location_dest_id, context=context)
+
def action_assign(self, cr, uid, ids, context=None):
""" Checks the product type and accordingly writes the state.
quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, qty, domain=domain, prefered_domain_list=[], restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
quant_obj.quants_reserve(cr, uid, quants, move, record, context=context)
for move in todo_moves:
+ if move.linked_move_operation_ids:
+ continue
move.refresh()
#then if the move isn't totally assigned, try to find quants without any specific domain
if move.state != 'assigned':
"""
procurement_obj = self.pool.get('procurement.order')
context = context or {}
+ procs_to_check = []
for move in self.browse(cr, uid, ids, context=context):
if move.state == 'done':
raise osv.except_osv(_('Operation Forbidden!'),
if move.propagate:
procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
procurement_obj.cancel(cr, uid, procurement_ids, context=context)
- elif move.move_dest_id:
- #cancel chained moves
- if move.propagate:
- self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
- # If we have a long chain of moves to be cancelled, it is easier for the user to handle
- # only the last procurement which will go into exception, instead of all procurements
- # along the chain going into exception. We need to check if there are no split moves not cancelled however
- if move.procurement_id:
- proc = move.procurement_id
- if all([x.state == 'cancel' for x in proc.move_ids if x.id != move.id]):
- procurement_obj.write(cr, uid, [proc.id], {'state': 'cancel'})
-
- elif move.move_dest_id.state == 'waiting':
- self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
- return self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
+ else:
+ if move.move_dest_id:
+ if move.propagate:
+ self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
+ elif move.move_dest_id.state == 'waiting':
+ #If waiting, the chain will be broken and we are not sure if we can still wait for it (=> could take from stock instead)
+ self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
+ if move.procurement_id:
+ # Does the same as procurement check, only eliminating a refresh
+ procs_to_check.append(move.procurement_id.id)
+
+ res = self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
+ if procs_to_check:
+ procurement_obj.check(cr, uid, procs_to_check, context=context)
+ return res
def _check_package_from_moves(self, cr, uid, ids, context=None):
pack_obj = self.pool.get("stock.quant.package")
move2 = not move2.move_orig_ids and move2.split_from or False
return ancestors
+ @api.cr_uid_ids_context
def recalculate_move_state(self, cr, uid, move_ids, context=None):
'''Recompute the state of moves given because their reserved quants were used to fulfill another operation'''
for move in self.browse(cr, uid, move_ids, context=context):
self.write(cr, uid, [move.id], vals, context=context)
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 completely the moves given as ids and if all moves are done, it will finish the picking.
"""
context = context or {}
picking_obj = self.pool.get("stock.picking")
main_domain = [('qty', '>', 0)]
for record in ops.linked_move_operation_ids:
move = record.move_id
- self.check_tracking(cr, uid, move, ops.package_id.id or ops.lot_id.id, context=context)
+ self.check_tracking(cr, uid, move, not ops.product_id and ops.package_id.id or ops.lot_id.id, context=context)
prefered_domain = [('reservation_id', '=', move.id)]
fallback_domain = [('reservation_id', '=', False)]
fallback_domain2 = ['&', ('reservation_id', '!=', move.id), ('reservation_id', '!=', False)]
self.pool.get('stock.quant.package').write(cr, SUPERUSER_ID, [ops.package_id.id], {'parent_id': ops.result_package_id.id}, context=context)
move_qty[move.id] -= record.qty
#Check for remaining qtys and unreserve/check move_dest_id in
+ move_dest_ids = set()
for move in self.browse(cr, uid, ids, context=context):
if move_qty[move.id] > 0: # (=In case no pack operations in picking)
main_domain = [('qty', '>', 0)]
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_list=prefered_domain_list, 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, move.location_dest_id, 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)
- #Check moves that were pushed
- if move.move_dest_id.state in ('waiting', 'confirmed'):
- # FIXME is opw 607970 still present with new WMS?
- # (see commits 1ef2c181033bd200906fb1e5ce35e234bf566ac6
- # and 41c5ceb8ebb95c1b4e98d8dd1f12b8e547a24b1d)
- other_upstream_move_ids = self.search(cr, uid, [('id', '!=', move.id), ('state', 'not in', ['done', 'cancel']),
- ('move_dest_id', '=', move.move_dest_id.id)], context=context)
- #If no other moves for the move that got pushed:
- if not other_upstream_move_ids and move.move_dest_id.state in ('waiting', 'confirmed'):
- self.action_assign(cr, uid, [move.move_dest_id.id], context=context)
+ # If the move has a destination, add it to the list to reserve
+ if move.move_dest_id and move.move_dest_id.state in ('waiting', 'confirmed'):
+ move_dest_ids.add(move.move_dest_id.id)
+
if move.procurement_id:
procurement_ids.append(move.procurement_id.id)
+ #unreserve the quants and make them available for other operations/moves
+ quant_obj.quants_unreserve(cr, uid, move, context=context)
# Check the packages have been placed in the correct locations
self._check_package_from_moves(cr, uid, ids, context=context)
#set the move as done
self.write(cr, uid, ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
self.pool.get('procurement.order').check(cr, uid, procurement_ids, context=context)
+ #assign destination moves
+ if move_dest_ids:
+ self.action_assign(cr, uid, list(move_dest_ids), context=context)
#check picking state to set the date_done is needed
done_picking = []
for picking in picking_obj.browse(cr, uid, list(pickings), context=context):
defaults = {
'product_uom_qty': uom_qty,
'product_uos_qty': uos_qty,
- 'state': move.state,
'procure_method': 'make_to_stock',
'restrict_lot_id': restrict_lot_id,
'restrict_partner_id': restrict_partner_id,
'split_from': move.id,
+ 'procurement_id': move.procurement_id.id,
'move_dest_id': move.move_dest_id.id,
}
if context.get('source_location_id'):
'product_uos_qty': move.product_uos_qty - uos_qty,
}, context=ctx)
- if move.move_dest_id and move.propagate:
+ if move.move_dest_id and move.propagate and move.move_dest_id.state not in ('done', 'cancel'):
new_move_prop = self.split(cr, uid, move.move_dest_id, qty, context=context)
self.write(cr, uid, [new_move], {'move_dest_id': new_move_prop}, context=context)
#returning the first element of list returned by action_confirm is ok because we checked it wouldn't be exploded (and
return self.action_confirm(cr, uid, [new_move], context=context)[0]
+ def get_code_from_locs(self, cr, uid, move, location_id=False, location_dest_id=False, context=None):
+ """
+ Returns the code the picking type should have. This can easily be used
+ to check if a move is internal or not
+ move, location_id and location_dest_id are browse records
+ """
+ code = 'internal'
+ src_loc = location_id or move.location_id
+ dest_loc = location_dest_id or move.location_dest_id
+ if src_loc.usage == 'internal' and dest_loc.usage != 'internal':
+ code = 'outgoing'
+ if src_loc.usage != 'internal' and dest_loc.usage == 'internal':
+ code = 'incoming'
+ return code
+
+
class stock_inventory(osv.osv):
_name = "stock.inventory"
_description = "Inventory"
]
_columns = {
- 'name': fields.char('Inventory Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Inventory Name."),
+ 'name': fields.char('Inventory Reference', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Inventory Name."),
'date': fields.datetime('Inventory Date', required=True, readonly=True, help="The date that will be used for the stock level check of the products and the validation of the stock move related to this inventory."),
- 'line_ids': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=False, states={'done': [('readonly', True)]}, help="Inventory Lines."),
+ 'line_ids': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=False, states={'done': [('readonly', True)]}, help="Inventory Lines.", copy=True),
'move_ids': fields.one2many('stock.move', 'inventory_id', 'Created Moves', help="Inventory Moves.", states={'done': [('readonly', True)]}),
- 'state': fields.selection(INVENTORY_STATE_SELECTION, 'Status', readonly=True, select=True),
+ 'state': fields.selection(INVENTORY_STATE_SELECTION, 'Status', readonly=True, select=True, copy=False),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True, readonly=True, states={'draft': [('readonly', False)]}),
'location_id': fields.many2one('stock.location', 'Inventoried Location', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_id': fields.many2one('product.product', 'Inventoried Product', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Product to focus your inventory on a particular Product."),
'package_id': fields.many2one('stock.quant.package', 'Inventoried Pack', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Pack to focus your inventory on a particular Pack."),
'partner_id': fields.many2one('res.partner', 'Inventoried Owner', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Owner to focus your inventory on a particular Owner."),
- 'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number."),
+ 'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number.", copy=False),
'move_ids_exist': fields.function(_get_move_ids_exist, type='boolean', string=' Stock Move Exists?', help='technical field for attrs in view'),
'filter': fields.selection(_get_available_filters, 'Selection Filter', required=True),
'total_qty': fields.function(_get_total_qty, type="float"),
self.pool.get('stock.inventory.line').write(cr, uid, line_ids, {'product_qty': 0})
return True
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- default = default.copy()
- default.update({'move_ids': []})
- return super(stock_inventory, self).copy(cr, uid, id, default, context=context)
-
def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
""" Creates a stock move from an inventory line
@param inventory_line:
domain += ' and lot_id = %s'
args += (inventory.lot_id.id,)
if inventory.product_id:
- domain += 'and product_id = %s'
+ domain += ' and product_id = %s'
args += (inventory.product_id.id,)
if inventory.package_id:
domain += ' and package_id = %s'
'product_qty': 1,
}
+ def create(self, cr, uid, values, context=None):
+ if context is None:
+ context = {}
+ product_obj = self.pool.get('product.product')
+ if 'product_id' in values and not 'product_uom_id' in values:
+ values['product_uom_id'] = product_obj.browse(cr, uid, values.get('product_id'), context=context).uom_id.id
+ return super(stock_inventory_line, self).create(cr, uid, values, context=context)
+
def _resolve_inventory_line(self, cr, uid, inventory_line, context=None):
stock_move_obj = self.pool.get('stock.move')
diff = inventory_line.theoretical_qty - inventory_line.product_qty
_description = "Warehouse"
_columns = {
- 'name': fields.char('Warehouse Name', size=128, required=True, select=True),
+ 'name': fields.char('Warehouse Name', required=True, select=True),
'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, select=True),
'partner_id': fields.many2one('res.partner', 'Address'),
'view_location_id': fields.many2one('stock.location', 'View Location', required=True, domain=[('usage', '=', 'view')]),
- 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True, domain=[('usage', '=', 'internal')]),
+ 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', domain=[('usage', '=', 'internal')], required=True),
'code': fields.char('Short Name', size=5, required=True, help="Short name used to identify your warehouse"),
'route_ids': fields.many2many('stock.location.route', 'stock_route_warehouse', 'warehouse_id', 'route_id', 'Routes', domain="[('warehouse_selectable', '=', True)]", help='Defaults routes through the warehouse'),
'reception_steps': fields.selection([
'in_type_id': fields.many2one('stock.picking.type', 'In Type'),
'int_type_id': fields.many2one('stock.picking.type', 'Internal Type'),
'crossdock_route_id': fields.many2one('stock.location.route', 'Crossdock Route'),
- 'reception_route_id': fields.many2one('stock.location.route', 'Reception Route'),
+ 'reception_route_id': fields.many2one('stock.location.route', 'Receipt Route'),
'delivery_route_id': fields.many2one('stock.location.route', 'Delivery Route'),
'resupply_from_wh': fields.boolean('Resupply From Other Warehouses'),
'resupply_wh_ids': fields.many2many('stock.warehouse', 'stock_wh_resupply_table', 'supplied_wh_id', 'supplier_wh_id', 'Resupply Warehouses'),
if wh.delivery_steps == 'ship_only':
output_loc = wh.lot_stock_id
# Create extra MTO rule (only for 'ship only' because in the other cases MTO rules already exists)
- mto_pull_vals = self._get_mto_pull_rule(cr, uid, wh, [(output_loc, transit_location, wh.out_type_id.id)], context=context)
+ mto_pull_vals = self._get_mto_pull_rule(cr, uid, wh, [(output_loc, transit_location, wh.out_type_id.id)], context=context)[0]
pull_obj.create(cr, uid, mto_pull_vals, context=context)
inter_wh_route_vals = self._get_inter_wh_route(cr, uid, warehouse, wh, context=context)
inter_wh_route_id = route_obj.create(cr, uid, vals=inter_wh_route_vals, context=context)
if default_resupply_wh and default_resupply_wh.id == wh.id:
self.write(cr, uid, [warehouse.id], {'route_ids': [(4, inter_wh_route_id)]}, context=context)
- def _default_stock_id(self, cr, uid, context=None):
- #lot_input_stock = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock')
- try:
- warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0')
- return warehouse.lot_stock_id.id
- except:
- return False
-
_defaults = {
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c),
- 'lot_stock_id': _default_stock_id,
'reception_steps': 'one_step',
'delivery_steps': 'ship_only',
}
def _get_mto_pull_rule(self, cr, uid, warehouse, values, context=None):
mto_route_id = self._get_mto_route(cr, uid, context=context)
- from_loc, dest_loc, pick_type_id = values[0]
- return {
+ res = []
+ for value in values:
+ from_loc, dest_loc, pick_type_id = value
+ res += [{
'name': self._format_rulename(cr, uid, warehouse, from_loc, dest_loc, context=context) + _(' MTO'),
'location_src_id': from_loc.id,
'location_id': dest_loc.id,
'procure_method': 'make_to_order',
'active': True,
'warehouse_id': warehouse.id,
- }
+ }]
+ return res
def _get_crossdock_route(self, cr, uid, warehouse, route_name, context=None):
return {
for pull_rule in pull_rules_list:
pull_obj.create(cr, uid, vals=pull_rule, context=context)
#create MTO pull rule and link it to the generic MTO route
- mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)
+ mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)[0]
mto_pull_id = pull_obj.create(cr, uid, mto_pull_vals, context=context)
#create a route for cross dock operations, that can be set on products and product categories
for pull_rule in pull_rules_list:
pull_obj.create(cr, uid, vals=pull_rule, context=context)
- #update reception route and rules: unlink the existing rules of the warehouse reception route and recreate it
+ #update receipt route and rules: unlink the existing rules of the warehouse receipt route and recreate it
pull_obj.unlink(cr, uid, [pu.id for pu in warehouse.reception_route_id.pull_ids], context=context)
push_obj.unlink(cr, uid, [pu.id for pu in warehouse.reception_route_id.push_ids], context=context)
route_name, values = routes_dict[new_reception_step]
for push_rule in push_rules_list:
push_obj.create(cr, uid, vals=push_rule, context=context)
for pull_rule in pull_rules_list:
- #all pull rules in reception route are mto, because we don't want to wait for the scheduler to trigger an orderpoint on input location
+ #all pull rules in receipt route are mto, because we don't want to wait for the scheduler to trigger an orderpoint on input location
pull_rule['procure_method'] = 'make_to_order'
pull_obj.create(cr, uid, vals=pull_rule, context=context)
#change MTO rule
dummy, values = routes_dict[new_delivery_step]
- mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)
+ mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)[0]
pull_obj.write(cr, uid, warehouse.mto_pull_id.id, mto_pull_vals, context=context)
return True
max_sequence = max_sequence and max_sequence[0]['sequence'] or 0
in_type_id = picking_type_obj.create(cr, uid, vals={
- 'name': _('Receptions'),
+ 'name': _('Receipts'),
'warehouse_id': warehouse.id,
'code': 'incoming',
'sequence_id': in_seq_id,
location_obj = self.pool.get('stock.location')
#create view location for warehouse
- wh_loc_id = location_obj.create(cr, uid, {
+ loc_vals = {
'name': _(vals.get('code')),
'usage': 'view',
- 'location_id': data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_locations')[1]
- }, context=context)
+ 'location_id': data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_locations')[1],
+ }
+ if vals.get('company_id'):
+ loc_vals['company_id'] = vals.get('company_id')
+ wh_loc_id = location_obj.create(cr, uid, loc_vals, context=context)
vals['view_location_id'] = wh_loc_id
#create all location
def_values = self.default_get(cr, uid, {'reception_steps', 'delivery_steps'})
{'name': _('Packing Zone'), 'active': delivery_steps == 'pick_pack_ship', 'field': 'wh_pack_stock_loc_id'},
]
for values in sub_locations:
- location_id = location_obj.create(cr, uid, {
+ loc_vals = {
'name': values['name'],
'usage': 'internal',
'location_id': wh_loc_id,
'active': values['active'],
- }, context=context_with_inactive)
+ }
+ if vals.get('company_id'):
+ loc_vals['company_id'] = vals.get('company_id')
+ location_id = location_obj.create(cr, uid, loc_vals, context=context_with_inactive)
vals[values['field']] = location_id
#create WH
customer_loc, supplier_loc = self._get_partner_locations(cr, uid, ids, context=context)
return {
- 'one_step': (_('Reception in 1 step'), []),
- 'two_steps': (_('Reception in 2 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
- 'three_steps': (_('Reception in 3 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.wh_qc_stock_loc_id, warehouse.int_type_id.id), (warehouse.wh_qc_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
+ 'one_step': (_('Receipt in 1 step'), []),
+ 'two_steps': (_('Receipt in 2 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
+ 'three_steps': (_('Receipt in 3 steps'), [(warehouse.wh_input_stock_loc_id, warehouse.wh_qc_stock_loc_id, warehouse.int_type_id.id), (warehouse.wh_qc_stock_loc_id, warehouse.lot_stock_id, warehouse.int_type_id.id)]),
'crossdock': (_('Cross-Dock'), [(warehouse.wh_input_stock_loc_id, warehouse.wh_output_stock_loc_id, warehouse.int_type_id.id), (warehouse.wh_output_stock_loc_id, customer_loc, warehouse.out_type_id.id)]),
'ship_only': (_('Ship Only'), [(warehouse.lot_stock_id, customer_loc, warehouse.out_type_id.id)]),
'pick_ship': (_('Pick + Ship'), [(warehouse.lot_stock_id, warehouse.wh_output_stock_loc_id, warehouse.pick_type_id.id), (warehouse.wh_output_stock_loc_id, customer_loc, warehouse.out_type_id.id)]),
route_obj = self.pool.get("stock.location.route")
pull_obj = self.pool.get("procurement.rule")
routes = route_obj.search(cr, uid, [('supplier_wh_id','=', warehouse.id)], context=context)
- pulls= pull_obj.search(cr, uid, ['&', ('route_id', 'in', routes), ('location_id.usage', '=', 'transit')], context=context)
+ pulls = pull_obj.search(cr, uid, ['&', ('route_id', 'in', routes), ('location_id.usage', '=', 'transit')], context=context)
if pulls:
pull_obj.write(cr, uid, pulls, {'location_src_id': new_location, 'procure_method': change_to_multiple and "make_to_order" or "make_to_stock"}, context=context)
# Create or clean MTO rules
transfer_locs = list(set([x.location_id for x in pull_recs]))
vals = [(warehouse.lot_stock_id , x, warehouse.out_type_id.id) for x in transfer_locs]
mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, vals, context=context)
- pull_obj.create(cr, uid, mto_pull_vals, context=context)
+ for mto_pull_val in mto_pull_vals:
+ pull_obj.create(cr, uid, mto_pull_val, context=context)
else:
# We need to delete all the MTO pull rules, otherwise they risk to be used in the system
pulls = pull_obj.search(cr, uid, ['&', ('route_id', '=', mto_route_id), ('location_id.usage', '=', 'transit'), ('location_src_id', '=', warehouse.lot_stock_id.id)], context=context)
def _check_reception_resupply(self, cr, uid, warehouse, new_location, context=None):
"""
- Will check if the resupply routes to this warehouse follow the changes of number of reception steps
+ Will check if the resupply routes to this warehouse follow the changes of number of receipt steps
"""
#Check routes that are being delivered by this warehouse and change the rule coming from transit location
route_obj = self.pool.get("stock.location.route")
'limit': 20
}
+
class stock_location_path(osv.osv):
_name = "stock.location.path"
_description = "Pushed Flows"
return res
_columns = {
- 'name': fields.char('Operation Name', size=64, required=True),
+ 'name': fields.char('Operation Name', required=True),
'company_id': fields.many2one('res.company', 'Company'),
'route_id': fields.many2one('stock.location.route', 'Route'),
'location_from_id': fields.many2one('stock.location', 'Source Location', ondelete='cascade', select=1, required=True),
'active': True,
}
+ def _prepare_push_apply(self, cr, uid, rule, move, context=None):
+ newdate = (datetime.strptime(move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta.relativedelta(days=rule.delay or 0)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+ return {
+ 'location_id': move.location_dest_id.id,
+ 'location_dest_id': rule.location_dest_id.id,
+ 'date': newdate,
+ 'company_id': rule.company_id and rule.company_id.id or False,
+ 'date_expected': newdate,
+ 'picking_id': False,
+ 'picking_type_id': rule.picking_type_id and rule.picking_type_id.id or False,
+ 'propagate': rule.propagate,
+ 'push_rule_id': rule.id,
+ 'warehouse_id': rule.warehouse_id and rule.warehouse_id.id or False,
+ }
+
def _apply(self, cr, uid, rule, move, context=None):
move_obj = self.pool.get('stock.move')
newdate = (datetime.strptime(move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta.relativedelta(days=rule.delay or 0)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
#call again push_apply to see if a next step is defined
move_obj._push_apply(cr, uid, [move], context=context)
else:
- move_id = move_obj.copy(cr, uid, move.id, {
- 'location_id': move.location_dest_id.id,
- 'location_dest_id': rule.location_dest_id.id,
- 'date': newdate,
- 'company_id': rule.company_id and rule.company_id.id or False,
- 'date_expected': newdate,
- 'picking_id': False,
- 'picking_type_id': rule.picking_type_id and rule.picking_type_id.id or False,
- 'propagate': rule.propagate,
- 'push_rule_id': rule.id,
- 'warehouse_id': rule.warehouse_id and rule.warehouse_id.id or False,
- })
+ vals = self._prepare_push_apply(cr, uid, rule, move, context=context)
+ move_id = move_obj.copy(cr, uid, move.id, vals, context=context)
move_obj.write(cr, uid, [move.id], {
'move_dest_id': move_id,
})
# -------------------------
from openerp.report import report_sxw
-report_sxw.report_sxw('report.stock.quant.package.barcode', 'stock.quant.package', 'addons/stock/report/package_barcode.rml')
class stock_package(osv.osv):
"""
def _get_package_info(self, cr, uid, ids, name, args, context=None):
default_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
- res = {}.fromkeys(ids, {'location_id': False, 'company_id': default_company_id, 'owner_id': False})
+ res = dict((res_id, {'location_id': False, 'company_id': default_company_id, 'owner_id': False}) for res_id in ids)
for pack in self.browse(cr, uid, ids, context=context):
if pack.quant_ids:
res[pack.id]['location_id'] = pack.quant_ids[0].location_id.id
return res
_columns = {
- 'name': fields.char('Package Reference', size=64, select=True),
+ 'name': fields.char('Package Reference', select=True, copy=False),
'complete_name': fields.function(_complete_name, type='char', string="Package Name",),
'parent_left': fields.integer('Left Parent', select=1),
'parent_right': fields.integer('Right Parent', select=1),
- 'packaging_id': fields.many2one('product.packaging', 'Packaging', help="This field should be completed only if everything inside the package share the same product, otherwise it doesn't really makes sense."),
+ 'packaging_id': fields.many2one('product.packaging', 'Packaging', help="This field should be completed only if everything inside the package share the same product, otherwise it doesn't really makes sense.", select=True),
'ul_id': fields.many2one('product.ul', 'Logistic Unit'),
'location_id': fields.function(_get_package_info, type='many2one', relation='stock.location', string='Location', multi="package",
store={
'stock.quant': (_get_packages, ['location_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['quant_ids', 'children_ids', 'parent_id'], 10),
- }, readonly=True),
+ }, readonly=True, select=True),
'quant_ids': fields.one2many('stock.quant', 'package_id', 'Bulk Content', readonly=True),
'parent_id': fields.many2one('stock.quant.package', 'Parent Package', help="The package containing this item", ondelete='restrict', readonly=True),
'children_ids': fields.one2many('stock.quant.package', 'parent_id', 'Contained Packages', readonly=True),
store={
'stock.quant': (_get_packages, ['company_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['quant_ids', 'children_ids', 'parent_id'], 10),
- }, readonly=True),
+ }, readonly=True, select=True),
'owner_id': fields.function(_get_package_info, type='many2one', relation='res.partner', string='Owner', multi="package",
store={
'stock.quant': (_get_packages, ['owner_id'], 10),
'stock.quant.package': (_get_packages_to_relocate, ['quant_ids', 'children_ids', 'parent_id'], 10),
- }, readonly=True),
+ }, readonly=True, select=True),
}
_defaults = {
'name': lambda self, cr, uid, context: self.pool.get('ir.sequence').get(cr, uid, 'stock.quant.package') or _('Unknown Pack')
return True
def action_print(self, cr, uid, ids, context=None):
- context = context or {}
- context['active_ids'] = ids
- return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_package_barcode', context=context)
+ context = dict(context or {}, active_ids=ids)
+ return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_package_barcode_small', context=context)
def unpack(self, cr, uid, ids, context=None):
res[quant.product_id.id] += quant.qty
return res
- def copy(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- if not default.get('name'):
- default['name'] = self.pool.get('ir.sequence').get(cr, uid, 'stock.quant.package') or _('Unknown Pack')
- default['quant_ids'] = []
- default['children_ids'] = []
- return super(stock_package, self).copy(cr, uid, id, default, context=context)
-
def copy_pack(self, cr, uid, id, default_pack_values=None, default=None, context=None):
stock_pack_operation_obj = self.pool.get('stock.pack.operation')
if default is None:
'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
'qty_done': fields.float('Quantity Processed', digits_compute=dp.get_precision('Product Unit of Measure')),
- 'package_id': fields.many2one('stock.quant.package', 'Package'), # 2
+ 'package_id': fields.many2one('stock.quant.package', 'Source Package'), # 2
'lot_id': fields.many2one('stock.production.lot', 'Lot/Serial Number'),
- 'result_package_id': fields.many2one('stock.quant.package', 'Container Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
+ 'result_package_id': fields.many2one('stock.quant.package', 'Destination Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
'date': fields.datetime('Date', required=True),
'owner_id': fields.many2one('res.partner', 'Owner', help="Owner of the quants"),
#'update_cost': fields.boolean('Need cost update'),
'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
'linked_move_operation_ids': fields.one2many('stock.move.operation.link', 'operation_id', string='Linked Moves', readonly=True, help='Moves impacted by this operation for the computation of the remaining quantities'),
'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Qty'),
- 'location_id': fields.many2one('stock.location', 'Location From', required=True),
- 'location_dest_id': fields.many2one('stock.location', 'Location To', required=True),
+ 'location_id': fields.many2one('stock.location', 'Source Location', required=True),
+ 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True),
'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True),
}
operation in two to process the one with the qty moved
'''
processed_ids = []
+ move_obj = self.pool.get("stock.move")
for pack_op in self.browse(cr, uid, ids, context=None):
+ if pack_op.product_id and pack_op.location_id and pack_op.location_dest_id:
+ move_obj.check_tracking_product(cr, uid, pack_op.product_id, pack_op.lot_id.id, pack_op.location_id, pack_op.location_dest_id, context=context)
op = pack_op.id
if pack_op.qty_done < pack_op.product_qty:
# we split the operation in two
new_lot_id = lots[0]
val.update({'name': name})
- if not obj.lot_id:
- if not new_lot_id:
- new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context)
- self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context)
+ if not new_lot_id:
+ new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context)
+ self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context)
def _search_and_increment(self, cr, uid, picking_id, domain, filter_visible=False, visible_op_ids=False, increment=True, context=None):
'''Search for an operation with given 'domain' in a picking, if it exists increment the qty (+1) otherwise create it
continue
procurement_qty = uom_obj._compute_qty_obj(cr, uid, procurement.product_uom, procurement.product_qty, procurement.product_id.uom_id, context=context)
for move in procurement.move_ids:
- if move.state not in ('draft', 'cancel'):
+ #need to add the moves in draft as they aren't in the virtual quantity + moves that have not been created yet
+ if move.state not in ('draft'):
#if move is already confirmed, assigned or done, the virtual stock is already taking this into account so it shouldn't be deducted
procurement_qty -= move.product_qty
qty += procurement_qty
for rule in self.browse(cr, uid, ids, context=context):
if rule.product_id.uom_id.category_id.id != rule.product_uom.category_id.id:
return False
-
return True
def action_view_proc_to_process(self, cr, uid, ids, context=None):
return result
_columns = {
- 'name': fields.char('Name', size=32, required=True),
+ 'name': fields.char('Name', required=True, copy=False),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the orderpoint without removing it."),
'logic': fields.selection([('max', 'Order to Max'), ('price', 'Best price (not yet active!)')], 'Reordering Mode', required=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type', '=', 'product')]),
'product_uom': fields.related('product_id', 'uom_id', type='many2one', relation='product.uom', string='Product Unit of Measure', readonly=True, required=True),
'product_min_qty': fields.float('Minimum Quantity', required=True,
- help="When the virtual stock goes below the Min Quantity specified for this field, OpenERP generates "\
+ digits_compute=dp.get_precision('Product Unit of Measure'),
+ help="When the virtual stock goes below the Min Quantity specified for this field, Odoo generates "\
"a procurement to bring the forecasted quantity to the Max Quantity."),
'product_max_qty': fields.float('Maximum Quantity', required=True,
- help="When the virtual stock goes below the Min Quantity, OpenERP generates "\
+ digits_compute=dp.get_precision('Product Unit of Measure'),
+ help="When the virtual stock goes below the Min Quantity, Odoo generates "\
"a procurement to bring the forecasted quantity to the Quantity specified as Max Quantity."),
- 'qty_multiple': fields.integer('Qty Multiple', required=True,
- help="The procurement quantity will be rounded up to this multiple."),
+ 'qty_multiple': fields.float('Qty Multiple', required=True,
+ digits_compute=dp.get_precision('Product Unit of Measure'),
+ help="The procurement quantity will be rounded up to this multiple. If it is 0, the exact quantity will be used. "),
'procurement_ids': fields.one2many('procurement.order', 'orderpoint_id', 'Created Procurements'),
- 'group_id': fields.many2one('procurement.group', 'Procurement Group', help="Moves created through this orderpoint will be put in this procurement group. If none is given, the moves generated by procurement rules will be grouped into one big picking."),
+ 'group_id': fields.many2one('procurement.group', 'Procurement Group', help="Moves created through this orderpoint will be put in this procurement group. If none is given, the moves generated by procurement rules will be grouped into one big picking.", copy=False),
'company_id': fields.many2one('res.company', 'Company', required=True),
}
_defaults = {
'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.orderpoint', context=context)
}
_sql_constraints = [
- ('qty_multiple_check', 'CHECK( qty_multiple > 0 )', 'Qty Multiple must be greater than zero.'),
+ ('qty_multiple_check', 'CHECK( qty_multiple >= 0 )', 'Qty Multiple must be greater than or equal to zero.'),
]
_constraints = [
(_check_product_uom, 'You have to select a product unit of measure in the same category than the default unit of measure of the product', ['product_id', 'product_uom']),
return {'value': v, 'domain': d}
return {'domain': {'product_uom': []}}
- def copy_data(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
- default.update({
- 'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.orderpoint') or '',
- 'procurement_ids': [],
- 'group_id': False
- })
- return super(stock_warehouse_orderpoint, self).copy_data(cr, uid, id, default, context=context)
-
-
class stock_picking_type(osv.osv):
_name = "stock.picking.type"
_description = "The picking type determines the picking view"
_order = 'sequence'
def open_barcode_interface(self, cr, uid, ids, context=None):
- final_url = "/barcode/web/#action=stock.ui&picking_type_id=" + str(ids[0]) if len(ids) else '0'
+ final_url = "/stock/barcode/#action=stock.ui&picking_type_id=" + str(ids[0]) if len(ids) else '0'
return {'type': 'ir.actions.act_url', 'url': final_url, 'target': 'self'}
def _get_tristate_values(self, cr, uid, ids, field_name, arg, context=None):
picking_obj = self.pool.get('stock.picking')
- res = dict.fromkeys(ids, [])
+ res = {}
for picking_type_id in ids:
#get last 10 pickings of this type
picking_ids = picking_obj.search(cr, uid, [('picking_type_id', '=', picking_type_id), ('state', '=', 'done')], order='date_done desc', limit=10, context=context)
tristates = []
for picking in picking_obj.browse(cr, uid, picking_ids, context=context):
if picking.date_done > picking.date:
- tristates.insert(0, {'tooltip': picking.name or '' + _(': Late'), 'value': -1})
+ tristates.insert(0, {'tooltip': picking.name or '' + ": " + _('Late'), 'value': -1})
elif picking.backorder_id:
- tristates.insert(0, {'tooltip': picking.name or '' + _(': Backorder exists'), 'value': 0})
+ tristates.insert(0, {'tooltip': picking.name or '' + ": " + _('Backorder exists'), 'value': 0})
else:
- tristates.insert(0, {'tooltip': picking.name or '' + _(': OK'), 'value': 1})
+ tristates.insert(0, {'tooltip': picking.name or '' + ": " + _('OK'), 'value': 1})
res[picking_type_id] = json.dumps(tristates)
return res