- A procurement order is used to record a need for a specific
- product at a specific location. Procurement orders are usually
- created automatically from sales orders, pull logistic rules or
- minimum stock rules.
-
+ Click to create a procurement order.
+
+
+ A procurement order is used to record a need for a specific
+ product at a specific location. Procurement orders are usually
+ created automatically from sales orders, pull logistic rules or
+ minimum stock rules.
+
+
When the procurement order is confirmed, it automatically
creates the necessary operations to fullfil the need: purchase
order proposition, manufacturing order, etc.
@@ -168,9 +168,19 @@
{}
-
- Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location. Sales Orders are one typical source of Procurement Orders (but these are distinct documents). Depending on the procurement parameters and the product configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, ordering products from a supplier, or passing a manufacturing order, etc. A Procurement Exception occurs when the system cannot find a way to fulfill a procurement. Some exceptions will resolve themselves automatically, but others require manual intervention (those are identified by a specific error message).
-
+
+ Click to create a Procurement.
+
+
+ Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location.
+
+
+ Sales Orders are one typical source of Procurement Orders (but these are distinct documents).
+ Depending on the procurement parameters and the product configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, ordering products from a supplier, or passing a manufacturing order, etc...
+
+
+ A Procurement Exception occurs when the system cannot find a way to fulfill a procurement. Some exceptions will resolve themselves automatically, but others require manual intervention (those are identified by a specific error message in the chatter).
+
diff --git a/addons/product/product.py b/addons/product/product.py
index 46d26adc43d..58289d12cdf 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -239,6 +239,19 @@ class product_category(osv.osv):
res.append((record['id'], name))
return res
+ def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
+ if not args:
+ args = []
+ if not context:
+ context = {}
+ if name:
+ # Be sure name_search is symetric to name_get
+ name = name.split(' / ')[-1]
+ ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context)
+ else:
+ ids = self.search(cr, uid, args, limit=limit, context=context)
+ return self.name_get(cr, uid, ids, context)
+
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
res = self.name_get(cr, uid, ids, context=context)
return dict(res)
@@ -279,8 +292,6 @@ class product_category(osv.osv):
_constraints = [
(_check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
]
- def child_get(self, cr, uid, ids):
- return [ids]
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index fd654699b67..de127f9773a 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index e2e722f38ce..d9fc6ae2cdf 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -197,7 +197,7 @@
-
+ Requests for Quotationir.actions.act_windowpurchase.order
- {'special_shortened_wh_name': True}
+ {}[('state','in',('draft','sent','bid','cancel', 'confirmed'))]tree,form,graph,calendar
@@ -373,7 +373,7 @@
ir.actions.act_windowpurchase.ordertree,form,graph,calendar
- {'special_shortened_wh_name': True}
+ {}[('state','not in',('draft','sent','bid', 'confirmed'))]
diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml
index 7f3f2f12871..ad5642eaaab 100644
--- a/addons/sale_stock/sale_stock_view.xml
+++ b/addons/sale_stock/sale_stock_view.xml
@@ -15,7 +15,7 @@
+ attrs="{'invisible': ['|','|','|',('picking_ids','=',False),('picking_ids','=',[]), ('state', 'not in', ('progress','manual', 'done')),('shipped','=',True)]}" groups="base.group_user"/>
diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py
index 706945e32ee..6fcd6ac996f 100644
--- a/addons/stock/procurement.py
+++ b/addons/stock/procurement.py
@@ -406,9 +406,10 @@ class procurement_order(osv.osv):
if qty <= 0:
continue
if op.product_id.type not in ('consu'):
- if op.procurement_draft_ids:
+ procurement_draft_ids = orderpoint_obj.get_draft_procurements(cr, uid, op. id, context=context)
+ if procurement_draft_ids:
# Check draft procurement related to this order point
- pro_ids = [x.id for x in op.procurement_draft_ids]
+ pro_ids = [x.id for x in procurement_draft_ids]
procure_datas = procurement_obj.read(
cr, uid, pro_ids, ['id', 'product_qty'], context=context)
to_generate = qty
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index c1ceeecc6d7..490399ede6e 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -172,9 +172,9 @@ class stock_location_route(osv.osv):
'pull_ids': fields.one2many('procurement.rule', 'route_id', 'Pull Rules'),
'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'),
- 'product_selectable': fields.boolean('Selectable on Product'),
- 'product_categ_selectable': fields.boolean('Selectable on Product Category'),
- 'warehouse_selectable': fields.boolean('Selectable on Warehouse'),
+ 'product_selectable': fields.boolean('Applicable on Product'),
+ 'product_categ_selectable': fields.boolean('Applicable on Product Category'),
+ 'warehouse_selectable': fields.boolean('Applicable on Warehouse'),
'supplied_wh_id': fields.many2one('stock.warehouse', 'Supplied Warehouse'),
'supplier_wh_id': fields.many2one('stock.warehouse', 'Supplier Warehouse'),
}
@@ -239,6 +239,25 @@ class stock_quant(osv.osv):
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.quant', context=c),
}
+ def action_view_quant_history(self, cr, uid, ids, context=None):
+ '''
+ This function returns an action that display the history of the quant, which
+ mean all the stock moves that lead to this quant creation with this quant quantity.
+ '''
+ mod_obj = self.pool.get('ir.model.data')
+ act_obj = self.pool.get('ir.actions.act_window')
+
+ result = mod_obj.get_object_reference(cr, uid, 'stock', 'action_move_form2')
+ id = result and result[1] or False
+ result = act_obj.read(cr, uid, [id], context={})[0]
+
+ move_ids = []
+ for quant in self.browse(cr, uid, ids, context=context):
+ move_ids += [move.id for move in quant.history_ids]
+
+ result['domain'] = "[('id','in',[" + ','.join(map(str, move_ids)) + "])]"
+ return result
+
def quants_reserve(self, cr, uid, quants, move, context=None):
toreserve = []
for quant,qty in quants:
@@ -1937,7 +1956,7 @@ class stock_move(osv.osv):
if move.move_dest_id and move.propagate:
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)
-
+
self.action_confirm(cr, uid, [new_move], context=context)
return new_move
@@ -1953,6 +1972,31 @@ class stock_inventory(osv.osv):
res[inv.id] = True
return res
+ def _get_available_filters(self, cr, uid, context=None):
+ """
+ This function will return the list of filter allowed according to the options checked
+ in 'Settings\Warehouse'.
+
+ :rtype: list of tuple
+ """
+ #default available choices
+ res_filter = [('none', ' All products of a whole location'), ('product', 'One product only')]
+ settings_obj = self.pool.get('stock.config.settings')
+ config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context)
+ #If we don't have updated config until now, all fields are by default false and so should be not dipslayed
+ if not config_ids:
+ return res_filter
+
+ stock_settings = settings_obj.browse(cr, uid, config_ids[0], context=context)
+ if stock_settings.group_stock_tracking_owner:
+ res_filter.append(('owner', 'One owner only'))
+ res_filter.append(('product_owner', 'One product for a specific owner'))
+ if stock_settings.group_stock_production_lot:
+ res_filter.append(('lot', 'One Lot/Serial Number'))
+ if stock_settings.group_stock_tracking_lot:
+ res_filter.append(('pack', 'A Pack'))
+ return res_filter
+
_columns = {
'name': fields.char('Inventory Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Inventory Name."),
'date': fields.datetime('Inventory Date', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Inventory Create Date."),
@@ -1967,12 +2011,12 @@ class stock_inventory(osv.osv):
'partner_id': fields.many2one('res.partner', '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', 'Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number."),
'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([('product', 'Product'), ('owner', 'Owner'), ('product_owner','Product & Owner'), ('lot','Lot/Serial Number'), ('pack','Pack'), ('none', 'None')], 'Selection Filter'),
+ 'filter': fields.selection(_get_available_filters, 'Selection Filter'),
}
def _default_stock_location(self, cr, uid, context=None):
try:
- warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0')
+ warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0')
return warehouse.lot_stock_id.id
except:
return False
@@ -2129,7 +2173,8 @@ class stock_inventory(osv.osv):
product_line['inventory_id'] = inventory.id
product_line['th_qty'] = product_line['product_qty']
if product_line['product_id']:
- product_line['product_uom_id'] = product_obj.browse(cr, uid, product_line['product_id'], context=context).uom_id.id
+ product = product_obj.browse(cr, uid, product_line['product_id'], context=context)
+ product_line['product_uom_id'] = product.uom_id.id
vals.append(product_line)
return vals
@@ -2578,8 +2623,9 @@ class stock_warehouse(osv.osv):
}, context=context)
vals['view_location_id'] = wh_loc_id
#create all location
- reception_steps = vals.get('reception_steps', False)
- delivery_steps = vals.get('delivery_steps', False)
+ def_values = self.default_get(cr, uid, {'reception_steps', 'delivery_steps'})
+ reception_steps = vals.get('reception_steps', def_values['reception_steps'])
+ delivery_steps = vals.get('delivery_steps', def_values['delivery_steps'])
context_with_inactive = context.copy()
context_with_inactive['active_test'] = False
sub_locations = [
@@ -2627,8 +2673,10 @@ class stock_warehouse(osv.osv):
#choose the next available color for the picking types of this warehouse
all_used_colors = self.pool.get('stock.picking.type').search_read(cr, uid, [('warehouse_id', '!=', False), ('color', '!=', False)], ['color'], order='color')
- not_used_colors = list(set(range(1, 10)) - set([x['color'] for x in all_used_colors]))
- color = not_used_colors and not_used_colors[0] or 1
+ not_used_colors = list(set(range(0, 9)) - set([x['color'] for x in all_used_colors]))
+ color = 0
+ if not_used_colors:
+ color = not_used_colors[0]
in_type_id = picking_type_obj.create(cr, uid, vals={
'name': _('Receptions'),
@@ -3178,15 +3226,16 @@ class stock_warehouse_orderpoint(osv.osv):
_name = "stock.warehouse.orderpoint"
_description = "Minimum Inventory Rule"
- def _get_draft_procurements(self, cr, uid, ids, field_name, arg, context=None):
+ def get_draft_procurements(self, cr, uid, ids, context=None):
if context is None:
context = {}
result = {}
+ if not isinstance(ids, list):
+ ids = [ids]
procurement_obj = self.pool.get('procurement.order')
for orderpoint in self.browse(cr, uid, ids, context=context):
- procurement_ids = procurement_obj.search(cr, uid, [('state', '=', 'draft'), ('product_id', '=', orderpoint.product_id.id), ('location_id', '=', orderpoint.location_id.id)])
- result[orderpoint.id] = procurement_ids
- return result
+ procurement_ids = procurement_obj.search(cr, uid, [('state', 'not in', ('cancel', 'done')), ('product_id', '=', orderpoint.product_id.id), ('location_id', '=', orderpoint.location_id.id)], context=context)
+ return list(set(procurement_ids))
def _check_product_uom(self, cr, uid, ids, context=None):
'''
@@ -3201,6 +3250,18 @@ class stock_warehouse_orderpoint(osv.osv):
return True
+ def action_view_proc_to_process(self, cr, uid, ids, context=None):
+ act_obj = self.pool.get('ir.actions.act_window')
+ mod_obj = self.pool.get('ir.model.data')
+ draft_ids = self.get_draft_procurements(cr, uid, ids, context=context)
+ result = mod_obj.get_object_reference(cr, uid, 'procurement', 'do_view_procurements')
+ if not result:
+ return False
+
+ result = act_obj.read(cr, uid, [result[1]], context=context)[0]
+ result['domain'] = "[('id', 'in', [" + ','.join(map(str, draft_ids)) + "])]"
+ return result
+
_columns = {
'name': fields.char('Name', size=32, required=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the orderpoint without removing it."),
@@ -3218,9 +3279,7 @@ class stock_warehouse_orderpoint(osv.osv):
'qty_multiple': fields.integer('Qty Multiple', required=True,
help="The procurement quantity will be rounded up to this multiple."),
'procurement_id': fields.many2one('procurement.order', 'Latest procurement', ondelete="set null"),
- 'company_id': fields.many2one('res.company', 'Company', required=True),
- 'procurement_draft_ids': fields.function(_get_draft_procurements, type='many2many', relation="procurement.order", \
- string="Related Procurement Orders", help="Draft procurement of the product and location of that orderpoint"),
+ 'company_id': fields.many2one('res.company', 'Company', required=True)
}
_defaults = {
'active': lambda *a: 1,
@@ -3424,11 +3483,11 @@ class stock_picking_type(osv.osv):
return res and res[0] or False
_columns = {
- 'name': fields.char('name', translate=True, required=True),
+ 'name': fields.char('Name', translate=True, required=True),
'complete_name': fields.function(_get_name, type='char', string='Name'),
'pack': fields.boolean('Prefill Pack Operations', help='This picking type needs packing interface'),
'auto_force_assign': fields.boolean('Automatic Availability', help='This picking type does\'t need to check for the availability in source location.'),
- 'color': fields.integer('Color Index'),
+ 'color': fields.integer('Color'),
'delivery': fields.boolean('Print delivery'),
'sequence_id': fields.many2one('ir.sequence', 'Reference Sequence', required=True),
'default_location_src_id': fields.many2one('stock.location', 'Default Source Location'),
@@ -3436,7 +3495,7 @@ class stock_picking_type(osv.osv):
#TODO: change field name to "code" as it's not a many2one anymore
'code_id': fields.selection([('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')], 'Picking type code', required=True),
'return_picking_type_id': fields.many2one('stock.picking.type', 'Picking Type for Returns'),
- 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
+ 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', ondelete='cascade'),
'active': fields.boolean('Active'),
# Statistics for the kanban view
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index cd10100f232..32b6718b5cf 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -11,7 +11,7 @@
+ parent="stock.menu_stock_product" sequence="0" groups="base.group_no_one"/>
@@ -47,24 +47,6 @@
-
- stock.inventory.line.form
- stock.inventory.line
-
-
-
- stock.inventory.filter
@@ -120,11 +102,11 @@
+
Specify filters to focus your inventory a on particular product, lot, pack and product's owner.