diff --git a/addons/account/product_view.xml b/addons/account/product_view.xml
index 97b99084a44..cc9351d55f4 100644
--- a/addons/account/product_view.xml
+++ b/addons/account/product_view.xml
@@ -9,15 +9,19 @@
+
-
+
+
+
+
diff --git a/addons/base_setup/installer.py b/addons/base_setup/installer.py
index e33039c19de..5da836d7f80 100644
--- a/addons/base_setup/installer.py
+++ b/addons/base_setup/installer.py
@@ -90,12 +90,6 @@ class base_setup_installer(osv.osv_memory):
'crm': True,
}
- def _if_mrp(self, cr, uid, ids, context=None):
- if self.pool.get('res.users').browse(cr, uid, uid, context=context)\
- .view == 'simple':
- return ['mrp_jit']
- return None
-
def _if_knowledge(self, cr, uid, ids, context=None):
if self.pool.get('res.users').browse(cr, uid, uid, context=context)\
.view == 'simple':
diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/delivery_view.xml
index 0955174b209..0dbba7323e2 100644
--- a/addons/delivery/delivery_view.xml
+++ b/addons/delivery/delivery_view.xml
@@ -99,8 +99,8 @@
-
-
+
+
diff --git a/addons/mrp/installer.py b/addons/mrp/installer.py
index 31a683fd3d9..eeb00ece494 100644
--- a/addons/mrp/installer.py
+++ b/addons/mrp/installer.py
@@ -46,4 +46,8 @@ class mrp_installer(osv.osv_memory):
help="Enables warranty and repair management (and their impact "
"on stocks and invoicing)."),
}
+
+ _defaults = {
+ 'mrp_jit': lambda self,cr,uid,*a: self.pool.get('res.users').browse(cr, uid, uid).view == 'simple',
+ }
mrp_installer()
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index fad20da2589..def60b7d458 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -690,25 +690,25 @@ class mrp_production(osv.osv):
produced_qty = production_qty
for produced_product in production.move_created_ids2:
- if (produced_product.scraped) or (produced_product.product_id.id<>production.product_id.id):
+ if (produced_product.scrapped) or (produced_product.product_id.id<>production.product_id.id):
continue
produced_qty += produced_product.product_qty
if production_mode in ['consume','consume_produce']:
consumed_products = {}
check = {}
- scraped = map(lambda x:x.scraped,production.move_lines2).count(True)
+ scrapped = map(lambda x:x.scrapped,production.move_lines2).count(True)
for consumed_product in production.move_lines2:
consumed = consumed_product.product_qty
- if consumed_product.scraped:
+ if consumed_product.scrapped:
continue
if not consumed_products.get(consumed_product.product_id.id, False):
consumed_products[consumed_product.product_id.id] = consumed_product.product_qty
check[consumed_product.product_id.id] = 0
for f in production.product_lines:
if f.product_id.id == consumed_product.product_id.id:
- if (len(production.move_lines2) - scraped) > len(production.product_lines):
+ if (len(production.move_lines2) - scrapped) > len(production.product_lines):
check[consumed_product.product_id.id] += consumed_product.product_qty
consumed = check[consumed_product.product_id.id]
rest_consumed = produced_qty * f.product_qty / production.product_qty - consumed
@@ -730,7 +730,7 @@ class mrp_production(osv.osv):
stock_mov_obj.write(cr, uid, final_product_todo, vals)
produced_products = {}
for produced_product in production.move_created_ids2:
- if produced_product.scraped:
+ if produced_product.scrapped:
continue
if not produced_products.get(produced_product.product_id.id, False):
produced_products[produced_product.product_id.id] = 0
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index b84ba1aca95..6b59287f3ce 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -608,7 +608,7 @@
-
+
@@ -658,13 +658,12 @@
-
+
-
diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py
index 39032ea8e4a..aea6fb41bc0 100644
--- a/addons/mrp/wizard/mrp_product_produce.py
+++ b/addons/mrp/wizard/mrp_product_produce.py
@@ -46,7 +46,7 @@ class mrp_product_produce(osv.osv_memory):
context['active_id'], context=context)
done = 0.0
for move in prod.move_created_ids2:
- if not move.scraped:
+ if not move.scrapped:
done += move.product_qty
return (prod.product_qty - done) or prod.product_qty
diff --git a/addons/product/product.py b/addons/product/product.py
index 57f8316f939..8cb0a08f52d 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -252,7 +252,7 @@ class product_template(osv.osv):
'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."),
'state': fields.selection([('',''),('draft', 'In Development'),('sellable','In Production'),('end','End of Lifecycle'),('obsolete','Obsolete')], 'State', help="Tells the user if he can use the product or not."),
'uom_id': fields.many2one('product.uom', 'Default Unit Of Measure', required=True, help="Default Unit of Measure used for all stock operation."),
- 'uom_po_id': fields.many2one('product.uom', 'Purchase UoM', required=True, help="Default Unit of Measure used for purchase orders. It must be in the same category than the default unit of measure."),
+ 'uom_po_id': fields.many2one('product.uom', 'Purchase Unit of Measure', required=True, help="Default Unit of Measure used for purchase orders. It must be in the same category than the default unit of measure."),
'uos_id' : fields.many2one('product.uom', 'Unit of Sale',
help='Used by companies that manage two units of measure: invoicing and inventory management. For example, in food industries, you will manage a stock of ham but invoice in Kg. Keep empty to use the default UOM.'),
'uos_coeff': fields.float('UOM -> UOS Coeff', digits=(16,4),
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index ede23a6788c..1d40945f2f8 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -10,6 +10,8 @@
+
diff --git a/addons/purchase/stock_view.xml b/addons/purchase/stock_view.xml
index a991b79d0e9..2b741536d3b 100644
--- a/addons/purchase/stock_view.xml
+++ b/addons/purchase/stock_view.xml
@@ -8,7 +8,7 @@
form
-
+
diff --git a/addons/sale/stock_view.xml b/addons/sale/stock_view.xml
index 73df12db523..6b3c0e2c684 100644
--- a/addons/sale/stock_view.xml
+++ b/addons/sale/stock_view.xml
@@ -7,7 +7,7 @@
form
-
+
diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py
index 3fb9e2c741a..e33e347d612 100644
--- a/addons/stock/__openerp__.py
+++ b/addons/stock/__openerp__.py
@@ -57,7 +57,6 @@ Thanks to the double entry management, the inventory controlling is powerful and
"wizard/stock_splitinto_view.xml",
"wizard/stock_inventory_line_split_view.xml",
"wizard/stock_change_standard_price_view.xml",
- "wizard/stock_traceability_view.xml",
'wizard/stock_picking_make_view.xml',
'wizard/stock_return_picking_view.xml',
"stock_workflow.xml",
diff --git a/addons/stock/product.py b/addons/stock/product.py
index c79896f07aa..951c3badcf4 100644
--- a/addons/stock/product.py
+++ b/addons/stock/product.py
@@ -314,10 +314,10 @@ class product_product(osv.osv):
'virtual_available': fields.function(_product_available, method=True, type='float', string='Virtual Stock', help="Future stock for this product according to the selected locations or all internal if none have been selected. Computed as: Real Stock - Outgoing + Incoming.", multi='qty_available'),
'incoming_qty': fields.function(_product_available, method=True, type='float', string='Incoming', help="Quantities of products that are planned to arrive in selected locations or all internal if none have been selected.", multi='qty_available'),
'outgoing_qty': fields.function(_product_available, method=True, type='float', string='Outgoing', help="Quantities of products that are planned to leave in selected locations or all internal if none have been selected.", multi='qty_available'),
- 'track_production': fields.boolean('Track Production Lots' , help="Forces to use a Production Lot during production order"),
- 'track_incoming': fields.boolean('Track Incoming Lots', help="Forces to use a tracking lot during receptions"),
- 'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to use a tracking lot during deliveries"),
- 'location_id': fields.dummy(string='Location', relation='stock.location', type='many2one'),
+ 'track_production': fields.boolean('Track Manufacturing Lots' , help="Forces to specify a Production Lot for all moves containing this product and generated by a Manufacturing Order"),
+ 'track_incoming': fields.boolean('Track Incoming Lots', help="Forces to specify a Production Lot for all moves containing this product and coming from a Supplier Location"),
+ 'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to specify a Production Lot for all moves containing this product and going to a Customer Location"),
+ 'location_id': fields.dummy(string='Stock Location', relation='stock.location', type='many2one'),
'valuation':fields.selection([('manual_periodic', 'Periodic (manual)'),
('real_time','Real Time (automatized)'),], 'Stock Valuation', help="Decide if the system must automatically creates account moves based on stock moves", required=True),
}
diff --git a/addons/stock/report/report_stock_view.xml b/addons/stock/report/report_stock_view.xml
index de7c6b41003..cb690cea931 100644
--- a/addons/stock/report/report_stock_view.xml
+++ b/addons/stock/report/report_stock_view.xml
@@ -62,7 +62,7 @@
- Dates of Latest Inventories by Product
+ Last Product Inventoriesreport.stock.lines.dateform{'search_default_stockable':1}
@@ -84,28 +84,5 @@
-
- stock.location.tree
- stock.location
- tree
-
-
-
-
-
-
-
-
-
-
-
- Inventory Valuation
- stock.location
- form
- tree,graph
-
-
-
-
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index e19e36b641f..5ecbd5f40ba 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -28,6 +28,7 @@ from tools.translate import _
import netsvc
import tools
import decimal_precision as dp
+import logging
#----------------------------------------------------------
@@ -39,7 +40,7 @@ class stock_incoterms(osv.osv):
_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."),
'code': fields.char('Code', size=3, required=True, help="Code for Incoterms"),
- 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the incoterms without removing it."),
+ 'active': fields.boolean('Active', help="By unchecking the active field, you may hide an INCOTERM without deleting it."),
}
_defaults = {
'active': True,
@@ -167,9 +168,17 @@ class stock_location(osv.osv):
_columns = {
'name': fields.char('Location Name', size=64, required=True, translate=True),
- 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the stock location without removing it."),
- 'usage': fields.selection([('supplier', 'Supplier Location'), ('view', 'View'), ('internal', 'Internal Location'), ('customer', 'Customer Location'), ('inventory', 'Inventory'), ('procurement', 'Procurement'), ('production', 'Production'), ('transit', 'Transit Location for Inter-Companies Transfers')], 'Location Type', required=True),
- 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=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 for Inter-Companies Transfers')], '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* Inventory: Virtual location serving as counterpart for inventory operations used to correct stock levels (Physical inventories)
+ \n* Procurement: Virtual location serving as temporary counterpart for procurement operations when the source (supplier or production) is not known yet. This location should be empty when the procurement scheduler has finished running.
+ \n* Production: Virtual counterpart location for production operations: this location consumes the raw material and produces finished products
+ """, select = True),
+ # temporarily removed, as it's unused: 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True),
'complete_name': fields.function(_complete_name, method=True, type='char', size=100, string="Location Name"),
'stock_real': fields.function(_product_qty_available, method=True, type='float', string='Real Stock', multi="stock"),
@@ -178,44 +187,46 @@ class stock_location(osv.osv):
'location_id': fields.many2one('stock.location', 'Parent Location', select=True, ondelete='cascade'),
'child_ids': fields.one2many('stock.location', 'location_id', 'Contains'),
- 'chained_journal_id': fields.many2one('stock.journal', 'Chained Journal'),
+ 'chained_journal_id': fields.many2one('stock.journal', 'Chaining Journal',help="Inventory Journal in which the chained move will be written, if the Chaining Type is not Transparent (no journal is used if left empty)"),
'chained_location_id': fields.many2one('stock.location', 'Chained Location If Fixed'),
'chained_location_type': fields.selection([('none', 'None'), ('customer', 'Customer'), ('fixed', 'Fixed Location')],
'Chained Location Type', required=True,
- help="This field is set to determine the destination location.\n" \
- "If the field is set to 'customer', the location is given by the properties of the partner form.\n"\
- "If the field is set to 'fixed', the destination location is given by the field Location if link is fixed."),
+ help="Determines whether this location is chained to another location, i.e. any incoming product in this location \n" \
+ "should next go to the chained location. The chained location is determined according to the type :"\
+ "\n* None: No chaining at all"\
+ "\n* Customer: The chained location will be taken from the Customer Location field on the Partner form of the Partner that is specified in the Picking list of the incoming products." \
+ "\n* Fixed Location: The chained location is taken from the next field: Chained Location if Fixed." \
+ ),
'chained_auto_packing': fields.selection(
[('auto', 'Automatic Move'), ('manual', 'Manual Operation'), ('transparent', 'Automatic No Step Added')],
- 'Automatic Move',
+ 'Chaining Type',
required=True,
help="This is used only if you select a chained location type.\n" \
"The 'Automatic Move' value will create a stock move after the current one that will be "\
"validated automatically. With 'Manual Operation', the stock move has to be validated "\
"by a worker. With 'Automatic No Step Added', the location is replaced in the original move."
),
- 'chained_picking_type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', help="Shipping type specify of the chained move, goods coming in or going out."),
- 'chained_company_id': fields.many2one('res.company', 'Chained Company', help='Set here the belonging company of the chained move'),
- 'chained_delay': fields.integer('Chained lead time (days)'),
- 'address_id': fields.many2one('res.partner.address', 'Location Address'),
- 'icon': fields.selection(tools.icons, 'Icon', size=64),
+ 'chained_picking_type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', help="Shipping Type of the Picking List that will contain the chained move (leave empty to automatically detect the type based on the source and destination locations)."),
+ 'chained_company_id': fields.many2one('res.company', 'Chained Company', help='The company the Picking List containing the chained move will belong to (leave empty to use the default company determination rules'),
+ 'chained_delay': fields.integer('Chaining Lead Time',help="Delay between original move and chained move in days"),
+ 'address_id': fields.many2one('res.partner.address', 'Location Address',help="Address of customer or supplier."),
+ 'icon': fields.selection(tools.icons, 'Icon', size=64,help="Icon show in hierarchical tree view"),
'comment': fields.text('Additional Information'),
- 'posx': fields.integer('Corridor (X)'),
- 'posy': fields.integer('Shelves (Y)'),
- 'posz': fields.integer('Height (Z)'),
+ 'posx': fields.integer('Corridor (X)',help="Optional localization details, for information purpose only"),
+ 'posy': fields.integer('Shelves (Y)', help="Optional localization details, for information purpose only"),
+ 'posz': fields.integer('Height (Z)', help="Optional localization details, for information purpose only"),
'parent_left': fields.integer('Left Parent', select=1),
'parent_right': fields.integer('Right Parent', select=1),
'stock_real_value': fields.function(_product_value, method=True, type='float', string='Real Stock Value', multi="stock"),
'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock"),
- 'company_id': fields.many2one('res.company', 'Company', select=1, help='Let this field empty if this location is shared for every companies'),
- 'scrap_location': fields.boolean('Scrap Location', help='Check this box if the current location is a place for destroyed items'),
+ 'company_id': fields.many2one('res.company', 'Company', select=1, help='Let this field empty if this location is shared between all companies'),
+ 'scrap_location': fields.boolean('Scrap Location', help='Check this box to allow using this location to put scrapped/damaged goods.'),
}
_defaults = {
'active': True,
'usage': 'internal',
- 'allocation_method': 'fifo',
'chained_location_type': 'none',
'chained_auto_packing': 'manual',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.location', context=c),
@@ -274,6 +285,9 @@ class stock_location(osv.osv):
pricetype = self.pool.get('product.price.type').browse(cr, uid, price_type_id)
context['currency_id'] = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id
+ # To be able to offer recursive or non-recursive reports we need to prevent recursive quantities by default
+ context['compute_child'] = False
+
if not product_ids:
product_ids = product_obj.search(cr, uid, [])
@@ -360,20 +374,82 @@ class stock_location(osv.osv):
def _product_virtual_get(self, cr, uid, id, product_ids=False, context=None, states=['done']):
return self._product_all_get(cr, uid, id, product_ids, context, ['confirmed', 'waiting', 'assigned', 'done'])
- def _product_reserve(self, cr, uid, ids, product_id, product_qty, context=None):
+ def _product_reserve(self, cr, uid, ids, product_id, product_qty, context=None, lock=False):
"""
- @param product_id: Id of product
- @param product_qty: Quantity of product
- @return: List of Values or False
+ Attempt to find a quantity ``product_qty`` (in the product's default uom or the uom passed in ``context``) of product ``product_id``
+ in locations with id ``ids`` and their child locations. If ``lock`` is True, the stock.move lines
+ of product with id ``product_id`` in the searched location will be write-locked using Postgres's
+ "FOR UPDATE NOWAIT" option until the transaction is committed or rolled back, to prevent reservin
+ twice the same products.
+ If ``lock`` is True and the lock cannot be obtained (because another transaction has locked some of
+ the same stock.move lines), a log line will be output and False will be returned, as if there was
+ not enough stock.
+
+ :param product_id: Id of product to reserve
+ :param product_qty: Quantity of product to reserve (in the product's default uom or the uom passed in ``context``)
+ :param lock: if True, the stock.move lines of product with id ``product_id`` in all locations (and children locations) with ``ids`` will
+ be write-locked using postgres's "FOR UPDATE NOWAIT" option until the transaction is committed or rolled back. This is
+ to prevent reserving twice the same products.
+ :param context: optional context dictionary: it a 'uom' key is present it will be used instead of the default product uom to
+ compute the ``product_qty`` and in the return value.
+ :return: List of tuples in the form (qty, location_id) with the (partial) quantities that can be taken in each location to
+ reach the requested product_qty (``qty`` is expressed in the default uom of the product), of False if enough
+ products could not be found, or the lock could not be obtained (and ``lock`` was True).
"""
result = []
amount = 0.0
if context is None:
context = {}
for id in self.search(cr, uid, [('location_id', 'child_of', ids)]):
- cr.execute("select product_uom,sum(product_qty) as product_qty from stock_move where location_dest_id=%s and location_id<>%s and product_id=%s and state='done' group by product_uom", (id, id, product_id))
+ if lock:
+ try:
+ # Must lock with a separate select query because FOR UPDATE can't be used with
+ # aggregation/group by's (when individual rows aren't identifiable).
+ # We use a SAVEPOINT to be able to rollback this part of the transaction without
+ # failing the whole transaction in case the LOCK cannot be acquired.
+ cr.execute("SAVEPOINT stock_location_product_reserve")
+ cr.execute("""SELECT id FROM stock_move
+ WHERE product_id=%s AND
+ (
+ (location_dest_id=%s AND
+ location_id<>%s AND
+ state='done')
+ OR
+ (location_id=%s AND
+ location_dest_id<>%s AND
+ state in ('done', 'assigned'))
+ )
+ FOR UPDATE of stock_move NOWAIT""", (product_id, id, id, id, id), log_exceptions=False)
+ except Exception:
+ # Here it's likely that the FOR UPDATE NOWAIT failed to get the LOCK,
+ # so we ROLLBACK to the SAVEPOINT to restore the transaction to its earlier
+ # state, we return False as if the products were not available, and log it:
+ cr.execute("ROLLBACK TO stock_location_product_reserve")
+ logger = logging.getLogger('stock.location')
+ logger.warn("Failed attempt to reserve %s x product %s, likely due to another transaction already in progress. Next attempt is likely to work. Detailed error available at DEBUG level.", product_qty, product_id)
+ logger.debug("Trace of the failed product reservation attempt: ", exc_info=True)
+ return False
+
+ # XXX TODO: rewrite this with one single query, possibly even the quantity conversion
+ cr.execute("""SELECT product_uom, sum(product_qty) AS product_qty
+ FROM stock_move
+ WHERE location_dest_id=%s AND
+ location_id<>%s AND
+ product_id=%s AND
+ state='done'
+ GROUP BY product_uom
+ """,
+ (id, id, product_id))
results = cr.dictfetchall()
- cr.execute("select product_uom,-sum(product_qty) as product_qty from stock_move where location_id=%s and location_dest_id<>%s and product_id=%s and state in ('done', 'assigned') group by product_uom", (id, id, product_id))
+ cr.execute("""SELECT product_uom,-sum(product_qty) AS product_qty
+ FROM stock_move
+ WHERE location_id=%s AND
+ location_dest_id<>%s AND
+ product_id=%s AND
+ state in ('done', 'assigned')
+ GROUP BY product_uom
+ """,
+ (id, id, product_id))
results += cr.dictfetchall()
total = 0.0
@@ -419,11 +495,11 @@ class stock_tracking(osv.osv):
return sequence + str(self.checksum(sequence))
_columns = {
- 'name': fields.char('Tracking ID', size=64, required=True),
- 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the pack without removing it."),
- 'serial': fields.char('Reference', size=64),
- 'move_ids': fields.one2many('stock.move', 'tracking_id', 'Moves Tracked'),
- 'date': fields.datetime('Created Date', required=True),
+ 'name': fields.char('Pack Reference', size=64, required=True, select=True),
+ 'active': fields.boolean('Active', help="By unchecking the active field, you may hide a pack without deleting it."),
+ 'serial': fields.char('Additional Reference', size=64, select=True, help="Other reference or serial number"),
+ 'move_ids': fields.one2many('stock.move', 'tracking_id', 'Moves for this pack', readonly=True),
+ 'date': fields.datetime('Creation Date', required=True),
}
_defaults = {
'active': 1,
@@ -448,7 +524,19 @@ class stock_tracking(osv.osv):
def unlink(self, cr, uid, ids, context=None):
raise osv.except_osv(_('Error'), _('You can not remove a lot line !'))
-
+
+ def action_traceability(self, cr, uid, ids, context={}):
+ """ It traces the information of a product
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param ids: List of IDs selected
+ @param context: A standard dictionary
+ @return: A dictionary of values
+ """
+ value={}
+ value=self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
+ return value
stock_tracking()
#----------------------------------------------------------
@@ -534,16 +622,15 @@ class stock_picking(osv.osv):
_columns = {
'name': fields.char('Reference', size=64, select=True),
- 'origin': fields.char('Origin', size=64, help="Reference of the document that produced this picking."),
- 'backorder_id': fields.many2one('stock.picking', 'Back Order', help="If the picking is splitted then the picking id in available state of move for this picking is stored in Backorder."),
+ 'origin': fields.char('Origin', size=64, help="Reference of the document that produced this picking.", select=True),
+ 'backorder_id': fields.many2one('stock.picking', 'Back Order of', help="If this picking was split this field links to the picking that contains the other part that has been processed already.", select=True),
'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', required=True, select=True, help="Shipping type specify, goods coming in or going out."),
- 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the picking without removing it."),
'note': fields.text('Notes'),
- 'stock_journal_id': fields.many2one('stock.journal','Stock Journal'),
+ 'stock_journal_id': fields.many2one('stock.journal','Stock Journal', select=True),
'location_id': fields.many2one('stock.location', 'Location', help="Keep empty if you produce at the location where the finished products are needed." \
"Set a location if you produce at a fixed location. This can be a partner location " \
- "if you subcontract the manufacturing operations."),
- 'location_dest_id': fields.many2one('stock.location', 'Dest. Location',help="Location where the system will stock the finished products."),
+ "if you subcontract the manufacturing operations.", select=True),
+ 'location_dest_id': fields.many2one('stock.location', 'Dest. Location',help="Location where the system will stock the finished products.", select=True),
'move_type': fields.selection([('direct', 'Direct Delivery'), ('one', 'All at once')], 'Delivery Method', required=True, help="It specifies goods to be delivered all at once or by direct delivery"),
'state': fields.selection([
('draft', 'Draft'),
@@ -553,13 +640,15 @@ class stock_picking(osv.osv):
('done', 'Done'),
('cancel', 'Cancelled'),
], 'State', readonly=True, select=True,
- help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed picking. \
- \n* The \'Confirmed\' state is used for stock movement to do with unavailable products. \
- \n* The \'Available\' state is set automatically when the products are ready to be moved.\
- \n* The \'Waiting\' state is used in MTO moves when a movement is waiting for another one.'),
+ help="* Draft: not confirmed yet and will not be scheduled until confirmed\n"\
+ "* Confirmed: still waiting for the availability of products\n"\
+ "* Available: products reserved, simply waiting for confirmation.\n"\
+ "* Waiting: waiting for another move to proceed before it becomes automatically available (e.g. in Make-To-Order flows)\n"\
+ "* Done: has been processed, can't be modified or cancelled anymore\n"\
+ "* Cancelled: has been cancelled, can't be confirmed anymore"),
'min_date': fields.function(get_min_max_date, fnct_inv=_set_minimum_date, multi="min_max_date",
- method=True, store=True, type='datetime', string='Expected Date', select=1, help="Expected date for Picking. Default it takes current date"),
- 'date': fields.datetime('Order Date', help="Date of Order"),
+ method=True, store=True, type='datetime', string='Expected Date', select=1, help="Expected date for the picking to be processed. Will be set to date of actual processing if not specified."),
+ 'date': fields.datetime('Order Date', help="Date of Order", select=True),
'date_done': fields.datetime('Date Done', help="Date of Completion"),
'max_date': fields.function(get_min_max_date, fnct_inv=_set_maximum_date, multi="min_max_date",
method=True, store=True, type='datetime', string='Max. Expected Date', select=2),
@@ -569,13 +658,12 @@ class stock_picking(osv.osv):
'invoice_state': fields.selection([
("invoiced", "Invoiced"),
("2binvoiced", "To Be Invoiced"),
- ("none", "Not from Picking")], "Invoice Status",
+ ("none", "Not from Picking")], "Invoice Control",
select=True, required=True, readonly=True, states={'draft': [('readonly', False)]}),
- 'company_id': fields.many2one('res.company', 'Company', required=True, select=1),
+ 'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
}
_defaults = {
'name': lambda self, cr, uid, context: '/',
- 'active': 1,
'state': 'draft',
'move_type': 'direct',
'type': 'in',
@@ -589,10 +677,18 @@ class stock_picking(osv.osv):
default = {}
default = default.copy()
picking_obj = self.browse(cr, uid, [id], context)[0]
+ move_obj=self.pool.get('stock.move')
if ('name' not in default) or (picking_obj.name=='/'):
seq_obj_name = 'stock.picking.' + picking_obj.type
default['name'] = self.pool.get('ir.sequence').get(cr, uid, seq_obj_name)
- return super(stock_picking, self).copy(cr, uid, id, default, context)
+ default['origin'] = ''
+ default['backorder_id'] = False
+ res=super(stock_picking, self).copy(cr, uid, id, default, context)
+ if res:
+ picking_obj = self.browse(cr, uid, [res], context)[0]
+ for move in picking_obj.move_lines:
+ move_obj.write(cr, uid, [move.id], {'tracking_id': False,'prodlot_id':False})
+ return res
def onchange_partner_in(self, cr, uid, context=None, partner_id=None):
return {}
@@ -1164,6 +1260,7 @@ class stock_picking(osv.osv):
@param ids: List of Picking Ids
@param context: A standard dictionary for contextual values
"""
+ msg=''
for pick in self.browse(cr, uid, ids, context=context):
type_list = {
'out':'Picking List',
@@ -1172,11 +1269,14 @@ class stock_picking(osv.osv):
'delivery': 'Delivery order'
}
message = type_list.get(pick.type, _('Document')) + " '" + (pick.name or 'n/a') + "' "
+ if pick.min_date:
+ msg=datetime.strptime(pick.min_date, '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d') + "'."
state_list = {
- 'confirmed': "is scheduled for the '" + datetime.strptime(pick.min_date, '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d') + "'.",
+ 'confirmed': "is scheduled for the '" + msg ,
'assigned': 'is ready to process.',
'cancel': 'is Cancelled.',
'done': 'is processed.',
+ 'draft':'is draft.',
}
message += state_list[pick.state]
self.log(cr, uid, pick.id, message)
@@ -1247,14 +1347,15 @@ class stock_production_lot(osv.osv):
return ids
_columns = {
- 'name': fields.char('Serial', size=64, required=True),
- 'ref': fields.char('Internal Reference', size=256),
- 'prefix': fields.char('Prefix', size=64),
+ 'name': fields.char('Serial Number', size=64, required=True, help="Unique serial number, will be displayed as: PREFIX/SERIAL [INT_REF]"),
+ 'ref': fields.char('Internal Reference', size=256, help="Internal reference number in case it differs from the manufacturer's serial number"),
+ 'prefix': fields.char('Prefix', size=64, help="Optional prefix to prepend when displaying this serial number: PREFIX/SERIAL [INT_REF]"),
'product_id': fields.many2one('product.product', 'Product', required=True),
- 'date': fields.datetime('Created Date', required=True),
- 'stock_available': fields.function(_get_stock, fnct_search=_stock_search, method=True, type="float", string="Available", select="2"),
+ 'date': fields.datetime('Creation Date', required=True),
+ 'stock_available': fields.function(_get_stock, fnct_search=_stock_search, method=True, type="float", string="Available", select="2", help="Current quantity of products with this Production Lot Number available in company warehouses"),
'revisions': fields.one2many('stock.production.lot.revision', 'lot_id', 'Revisions'),
- 'company_id': fields.many2one('res.company','Company',select=1),
+ 'company_id': fields.many2one('res.company', 'Company', select=True),
+ 'move_ids': fields.one2many('stock.move', 'prodlot_id', 'Moves for this production lot', readonly=True),
}
_defaults = {
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
@@ -1262,9 +1363,19 @@ class stock_production_lot(osv.osv):
'product_id': lambda x, y, z, c: c.get('product_id', False),
}
_sql_constraints = [
- ('name_ref_uniq', 'unique (name, ref)', 'The serial/ref must be unique !'),
+ ('name_ref_uniq', 'unique (name, ref)', _('The combination of serial number and internal reference must be unique !')),
]
-
+ def action_traceability(self, cr, uid, ids, context={}):
+ """ It traces the information of a product
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param ids: List of IDs selected
+ @param context: A standard dictionary
+ @return: A dictionary of values
+ """
+ value=self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
+ return value
stock_production_lot()
class stock_production_lot_revision(osv.osv):
@@ -1275,7 +1386,7 @@ class stock_production_lot_revision(osv.osv):
'name': fields.char('Revision Name', size=64, required=True),
'description': fields.text('Description'),
'date': fields.date('Revision Date'),
- 'indice': fields.char('Revision', size=16),
+ 'indice': fields.char('Revision Number', size=16),
'author_id': fields.many2one('res.users', 'Author'),
'lot_id': fields.many2one('stock.production.lot', 'Production lot', select=True, ondelete='cascade'),
'company_id': fields.related('lot_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
@@ -1321,10 +1432,10 @@ class stock_move(osv.osv):
if not move.prodlot_id and \
(move.state == 'done' and \
( \
- (move.product_id.track_production and move.location_id.usage=='production') or \
- (move.product_id.track_production and move.location_dest_id.usage=='production') or \
- (move.product_id.track_incoming and move.location_id.usage in ('supplier','internal')) or \
- (move.product_id.track_outgoing and move.location_dest_id.usage in ('customer','internal')) \
+ (move.product_id.track_production and move.location_id.usage == 'production') or \
+ (move.product_id.track_production and move.location_dest_id.usage == 'production') or \
+ (move.product_id.track_incoming and move.location_id.usage == 'supplier') or \
+ (move.product_id.track_outgoing and move.location_dest_id.usage == 'customer') \
)):
return False
return True
@@ -1342,7 +1453,7 @@ class stock_move(osv.osv):
'name': fields.char('Name', size=64, required=True, select=True),
'priority': fields.selection([('0', 'Not urgent'), ('1', 'Urgent')], 'Priority'),
- 'date': fields.datetime('Created Date'),
+ 'date': fields.datetime('Creation Date', select=True),
'date_planned': fields.datetime('Date', required=True, help="Scheduled date for the movement of the products or real date if the move is done."),
'date_expected': fields.datetime('Date Expected', readonly=True,required=True, help="Scheduled date for the movement of the products"),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
@@ -1354,17 +1465,17 @@ class stock_move(osv.osv):
'product_packaging': fields.many2one('product.packaging', 'Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."),
'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
- 'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True, help="Location where the system will stock the finished products."),
- 'address_id': fields.many2one('res.partner.address', 'Dest. Address', help="Address where goods are to be delivered"),
+ 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, select=True, help="Location where the system will stock the finished products."),
+ 'address_id': fields.many2one('res.partner.address', 'Destination Address', help="Optional address where goods are to be delivered, specifically used for allotment"),
- 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production"),
- 'tracking_id': fields.many2one('stock.tracking', 'Pack', select=True, help="This is the code that will be put on the logistical unit: pallet, box, pack."),
+ 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
+ 'tracking_id': fields.many2one('stock.tracking', 'Pack', select=True, help="Logistical shipping unit: pallet, box, pack ..."),
'auto_validate': fields.boolean('Auto Validate'),
- 'move_dest_id': fields.many2one('stock.move', 'Dest. Move'),
- 'move_history_ids': fields.many2many('stock.move', 'stock_move_history_ids', 'parent_id', 'child_id', 'Move History'),
- 'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History'),
+ 'move_dest_id': fields.many2one('stock.move', 'Destination Move', help="Optional: next stock move when chaining them", select=True),
+ 'move_history_ids': fields.many2many('stock.move', 'stock_move_history_ids', 'parent_id', 'child_id', 'Move History (child moves)'),
+ 'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History (parent moves)'),
'picking_id': fields.many2one('stock.picking', 'Picking List', select=True),
'note': fields.text('Notes'),
'state': fields.selection([('draft', 'Draft'), ('waiting', 'Waiting'), ('confirmed', 'Confirmed'), ('assigned', 'Available'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, select=True,
@@ -1372,12 +1483,13 @@ class stock_move(osv.osv):
\nThe state is \'Waiting\' if the move is waiting for another one.'),
'price_unit': fields.float('Unit Price',
digits_compute= dp.get_precision('Account')),
- 'company_id': fields.many2one('res.company', 'Company', required=True, select=1),
- 'partner_id': fields.related('picking_id','address_id','partner_id',type='many2one', relation="res.partner", string="Partner", store=True),
- 'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Order"),
- 'origin': fields.related('picking_id','origin',type='char', size=64, relation="stock.picking", string="Origin",store=True),
- 'scraped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scraped'),
- 'move_stock_return_history': fields.many2many('stock.move', 'stock_move_return_history', 'move_id', 'return_move_id', 'Move Return History',readonly=True),
+ 'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
+ 'partner_id': fields.related('picking_id','address_id','partner_id',type='many2one', relation="res.partner", string="Partner", store=True, select=True),
+ 'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Order", select=True),
+ 'origin': fields.related('picking_id','origin',type='char', size=64, relation="stock.picking", string="Origin", store=True),
+
+ # used for colors in tree views:
+ 'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped'),
}
_constraints = [
(_check_tracking,
@@ -1422,18 +1534,27 @@ class stock_move(osv.osv):
'state': 'draft',
'priority': '1',
'product_qty': 1.0,
- 'scraped' : False,
+ 'scrapped' : False,
'date_planned': time.strftime('%Y-%m-%d %H:%M:%S'),
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.move', context=c),
'date_expected': time.strftime('%Y-%m-%d %H:%M:%S'),
}
+ def write(self, cr, uid, ids, vals, context=None):
+ if uid != 1:
+ for move in self.browse(cr, uid, ids):
+ if move.state == 'done':
+ frozen_fields = set(['product_qty', 'product_uom', 'product_uos_qty', 'product_uos', 'location_id', 'location_dest_id', 'product_id'])
+ if frozen_fields.intersection(vals):
+ raise osv.except_osv(_('Operation forbidden'),
+ _('Quantities, UoMs, Products and Locations cannot be modified on stock moves that have already been processed (except by the Administrator)'))
+ return super(stock_move, self).write(cr, uid, ids, vals, context=context)
+
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
default = default.copy()
- default['move_stock_return_history'] = []
return super(stock_move, self).copy(cr, uid, id, default, context=context)
def _auto_init(self, cursor, context=None):
@@ -1659,19 +1780,20 @@ class stock_move(osv.osv):
if context is None:
context = {}
for move in self.browse(cr, uid, ids, context=context):
- if move.product_id.type == 'consu':
+ if move.product_id.type == 'consu' or move.location_id.usage == 'supplier':
if move.state in ('confirmed', 'waiting'):
done.append(move.id)
pickings[move.picking_id.id] = 1
continue
if move.state in ('confirmed', 'waiting'):
- res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, {'uom': move.product_uom.id})
+ # Important: we must pass lock=True to _product_reserve() to avoid race conditions and double reservations
+ res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, {'uom': move.product_uom.id}, lock=True)
if res:
#_product_available_test depends on the next status for correct functioning
#the test does not work correctly if the same product occurs multiple times
#in the same order. This is e.g. the case when using the button 'split in two' of
#the stock outgoing form
- self.write(cr, uid, move.id, {'state':'assigned'})
+ self.write(cr, uid, [move.id], {'state':'assigned'})
done.append(move.id)
pickings[move.picking_id.id] = 1
r = res.pop(0)
@@ -1906,14 +2028,14 @@ class stock_move(osv.osv):
""" Move the scrap/damaged product into scrap location
@param cr: the database cursor
@param uid: the user id
- @param ids: ids of stock move object to be scraped
+ @param ids: ids of stock move object to be scrapped
@param quantity : specify scrap qty
@param location_id : specify scrap location
@param context: context arguments
@return: Scraped lines
"""
if quantity <= 0:
- raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+ raise osv.except_osv(_('Warning!'), _('Please provide a positive quantity to scrap!'))
res = []
for move in self.browse(cr, uid, ids, context=context):
move_qty = move.product_qty
@@ -1922,15 +2044,17 @@ class stock_move(osv.osv):
'product_qty': quantity,
'product_uos_qty': uos_qty,
'state': move.state,
- 'scraped' : True,
- 'location_dest_id': location_id
+ 'scrapped' : True,
+ 'location_dest_id': location_id,
+ 'tracking_id':False,
+ 'prodlot_id':False
}
new_move = self.copy(cr, uid, move.id, default_val)
+
res += [new_move]
product_obj = self.pool.get('product.product')
for (id, name) in product_obj.name_get(cr, uid, [move.product_id.id]):
- message = _('Product ') + " '" + name + "' "+ _("is scraped with") + " '" + str(move.product_qty) + "' "+ _("quantity.")
- self.log(cr, uid, move.id, message)
+ self.log(cr, uid, move.id, "%s x %s %s" % (move.product_qty, name, _("were scrapped")))
self.action_done(cr, uid, res)
return res
@@ -2191,13 +2315,13 @@ class stock_inventory(osv.osv):
_name = "stock.inventory"
_description = "Inventory"
_columns = {
- 'name': fields.char('Inventory', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
- 'date': fields.datetime('Date create', required=True, readonly=True, states={'draft': [('readonly', False)]}),
+ 'name': fields.char('Inventory Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
+ 'date': fields.datetime('Creation Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'date_done': fields.datetime('Date done'),
'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', states={'done': [('readonly', True)]}),
'move_ids': fields.many2many('stock.move', 'stock_inventory_move_rel', 'inventory_id', 'move_id', 'Created Moves'),
- 'state': fields.selection( (('draft', 'Draft'), ('done', 'Done'), ('cancel','Cancelled')), 'State', readonly=True),
- 'company_id': fields.many2one('res.company','Company',required=True,select=1),
+ 'state': fields.selection( (('draft', 'Draft'), ('done', 'Done'), ('cancel','Cancelled')), 'State', readonly=True, select=True),
+ 'company_id': fields.many2one('res.company','Company',required=True,select=True),
}
_defaults = {
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
@@ -2289,10 +2413,10 @@ class stock_inventory_line(osv.osv):
_columns = {
'inventory_id': fields.many2one('stock.inventory', 'Inventory', ondelete='cascade', select=True),
'location_id': fields.many2one('stock.location', 'Location', required=True),
- 'product_id': fields.many2one('product.product', 'Product', required=True),
+ 'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
'product_qty': fields.float('Quantity'),
- 'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
+ 'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True, select=True),
'prod_lot_id': fields.many2one('stock.production.lot', 'Production Lot', domain="[('product_id','=',product_id)]"),
'state': fields.related('inventory_id','state',type='char',string='State',readonly=True),
}
@@ -2322,8 +2446,8 @@ class stock_warehouse(osv.osv):
_name = "stock.warehouse"
_description = "Warehouse"
_columns = {
- 'name': fields.char('Name', size=60, required=True),
- 'company_id': fields.many2one('res.company','Company',required=True,select=1),
+ 'name': fields.char('Name', size=128, required=True, select=True),
+ 'company_id': fields.many2one('res.company', 'Company', required=True, select=True),
'partner_address_id': fields.many2one('res.partner.address', 'Owner Address'),
'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True, domain=[('usage','<>','view')]),
'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True, domain=[('usage','<>','view')]),
@@ -2390,7 +2514,7 @@ stock_picking_move_wizard()
class report_products_to_received_planned(osv.osv):
_name = "report.products.to.received.planned"
- _description = "Product to Received Vs Planned"
+ _description = "Received Products vs Planned"
_auto = False
_columns = {
'date':fields.date('Date'),
@@ -2424,7 +2548,7 @@ report_products_to_received_planned()
class report_delivery_products_planned(osv.osv):
_name = "report.delivery.products.planned"
- _description = "Number of Delivery products vs planned"
+ _description = "Delivered products vs Planned"
_auto = False
_columns = {
'date':fields.date('Date'),
diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml
index 205e4a44396..6933db289a0 100644
--- a/addons/stock/stock_data.xml
+++ b/addons/stock/stock_data.xml
@@ -37,8 +37,8 @@
viewterp-mrp
-
- Scraped
+
+ ScrappedTrueinventory
diff --git a/addons/stock/stock_demo.xml b/addons/stock/stock_demo.xml
index 31cd94e040d..6607d97b2b3 100644
--- a/addons/stock/stock_demo.xml
+++ b/addons/stock/stock_demo.xml
@@ -224,7 +224,6 @@
-
diff --git a/addons/stock/stock_report.xml b/addons/stock/stock_report.xml
index 855a56a0f75..1332e840e10 100644
--- a/addons/stock/stock_report.xml
+++ b/addons/stock/stock_report.xml
@@ -1,12 +1,12 @@
-
+
-
-
-
+
+
+
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 203b190053c..609fecf8288 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -8,7 +8,7 @@
-
+
@@ -98,7 +98,7 @@
-
+
@@ -157,10 +157,25 @@
form
@@ -173,7 +188,6 @@
-
@@ -203,6 +217,22 @@
+
+ Pack Search
+ stock.tracking
+ search
+
+
+
+
+
+
+
+
+
+
+
+
stock.production.lot.revision.formstock.production.lot.revision
@@ -239,15 +269,26 @@
form
@@ -361,16 +402,20 @@
form
-
- Fill From Unreceived Products
- stock.picking.move.wizard
- stock.picking
- ir.actions.act_window
- form
- form
-
- new
- {'action_id': active_id}
-
+
+
stock.picking.calendarstock.picking
@@ -565,25 +607,35 @@
stock.pickingform
-
@@ -698,7 +752,7 @@
-
+
@@ -738,28 +792,43 @@
stock.pickingform
-
@@ -1623,8 +1761,6 @@
-
-
@@ -1653,7 +1789,7 @@
ir.actions.act_windowformtree,form
- [('picking_id.type','=','in')]
+ ['|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]{"search_default_receive":1}
@@ -1862,7 +1998,7 @@
ir.actions.act_windowformtree,form
- [('picking_id','!=',False),('picking_id.type','=','out')]
+ ['|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','out')]{"search_default_receive":1}
diff --git a/addons/stock/wizard/stock_invoice_onshipping.py b/addons/stock/wizard/stock_invoice_onshipping.py
index b8692783824..f931e575b2f 100644
--- a/addons/stock/wizard/stock_invoice_onshipping.py
+++ b/addons/stock/wizard/stock_invoice_onshipping.py
@@ -53,7 +53,7 @@ class stock_invoice_onshipping(osv.osv_memory):
if pick.invoice_state == 'invoiced':
raise osv.except_osv(_('UserError'), _('Invoice is already created.'))
if pick.invoice_state == 'none':
- raise osv.except_osv(_('UserError'), _('Invoice cannot be created from Picking.'))
+ raise osv.except_osv(_('UserError'), _('This picking does not require any invoicing.'))
if pick.move_lines:
usage = pick.move_lines[0].location_id.usage
diff --git a/addons/stock/wizard/stock_invoice_onshipping_view.xml b/addons/stock/wizard/stock_invoice_onshipping_view.xml
index 62689019af0..e1e742d7aee 100644
--- a/addons/stock/wizard/stock_invoice_onshipping_view.xml
+++ b/addons/stock/wizard/stock_invoice_onshipping_view.xml
@@ -22,12 +22,15 @@
-
+
+ Create Invoice
+ stock.invoice.onshipping
+ ir.actions.act_window
+ form
+ tree,form
+
+ new
+
+
diff --git a/addons/stock/wizard/stock_move.py b/addons/stock/wizard/stock_move.py
index e50266706d5..a5a401800d3 100644
--- a/addons/stock/wizard/stock_move.py
+++ b/addons/stock/wizard/stock_move.py
@@ -142,7 +142,7 @@ class stock_move_scrap(osv.osv_memory):
return res
def move_scrap(self, cr, uid, ids, context={}):
- """ To move scraped products
+ """ To move scrapped products
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
@@ -191,10 +191,10 @@ class split_in_production_lot(osv.osv_memory):
_columns = {
'qty': fields.integer('Quantity'),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
- 'product_uom': fields.many2one('product.uom', 'Product UOM'),
- 'line_ids': fields.one2many('stock.move.split.lines', 'lot_id', 'Lots Number'),
- 'line_exist_ids': fields.one2many('stock.move.split.lines.exist', 'lot_id', 'Lots Existing Numbers'),
- 'use_exist' : fields.boolean('Existing Lot'),
+ 'product_uom': fields.many2one('product.uom', 'UoM'),
+ 'line_ids': fields.one2many('stock.move.split.lines', 'lot_id', 'Production Lots'),
+ 'line_exist_ids': fields.one2many('stock.move.split.lines.exist', 'lot_id', 'Production Lots'),
+ 'use_exist' : fields.boolean('Existing Lots', help="Check this option to select existing lots in the list below, otherwise you should enter new ones line by line."),
}
def split_lot(self, cr, uid, ids, context=None):
@@ -261,7 +261,7 @@ class split_in_production_lot(osv.osv_memory):
'name': line.name,
'product_id': move.product_id.id},
context=context)
- print 'write', current_move, {'prodlot_id': prodlot_id, 'state':move.state}
+
move_obj.write(cr, uid, [current_move], {'prodlot_id': prodlot_id, 'state':move.state})
update_val = {}
diff --git a/addons/stock/wizard/stock_move_view.xml b/addons/stock/wizard/stock_move_view.xml
index 980a1b5f402..46d3893900b 100644
--- a/addons/stock/wizard/stock_move_view.xml
+++ b/addons/stock/wizard/stock_move_view.xml
@@ -73,7 +73,7 @@
-
+
@@ -121,24 +121,24 @@
-
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py
index 51429058eb5..17515f86252 100644
--- a/addons/stock/wizard/stock_partial_move.py
+++ b/addons/stock/wizard/stock_partial_move.py
@@ -28,9 +28,7 @@ class stock_partial_move(osv.osv_memory):
_description = "Partial Move"
_columns = {
'date': fields.datetime('Date', required=True),
- 'partner_id': fields.many2one('res.partner',string="Partner"),
'type': fields.char("Type", size=3),
- 'address_id': fields.many2one('res.partner.address', 'Address', help="Address where goods are to be delivered"),
}
def view_init(self, cr, uid, fields_list, context=None):
@@ -66,12 +64,6 @@ class stock_partial_move(osv.osv_memory):
_moves_arch_lst = """
-
-
-
-
-
-
"""
_moves_fields = result['fields']
@@ -164,21 +156,7 @@ class stock_partial_move(osv.osv_memory):
if m.state in ('done', 'cancel'):
continue
res['type'] = m.picking_id and m.picking_id.type or ''
- address_ids = list(set([(pick.address_id and pick.address_id.id, pick.address_id and pick.address_id.partner_id and pick.address_id.partner_id.id) for pick in move_obj.browse(cr, uid, move_ids)]))
- address_ids1 = list(set([(pick.address_id and pick.address_id.id, pick.address_id and pick.address_id.partner_id and pick.address_id.partner_id.id) for pick in move_obj.browse(cr, uid, move_ids) if pick.address_id]))
- if len(address_ids1) == 1:
- if m.picking_id and m.picking_id.type=='out':
- res['address_id'] = address_ids[0][0] or False
- res['partner_id'] = address_ids[0][1] or False
- if m.picking_id and m.picking_id.type=='in':
- res['partner_id'] = m.company_id.partner_id.id or False
- if m.company_id.partner_id:
- address_default = [add.id for add in m.company_id.partner_id.address if add.type=='default']
- address_delivery = [add.id for add in m.company_id.partner_id.address if add.type=='delivery']
- if len(address_delivery):
- res['address_id'] = address_delivery and address_delivery[0] or False
- else:
- res['address_id'] = address_default and address_default[0] or False
+
if 'move%s_product_id'%(m.id) in fields:
res['move%s_product_id'%(m.id)] = m.product_id.id
@@ -230,8 +208,6 @@ class stock_partial_move(osv.osv_memory):
move_ids = context.get('active_ids', False)
partial = self.browse(cr, uid, ids[0], context)
partial_datas = {
- 'partner_id' : partial.partner_id and partial.partner_id.id or False,
- 'address_id' : partial.address_id and partial.address_id.id or False,
'delivery_date' : partial.date
}
for m in move_obj.browse(cr, uid, move_ids):
diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py
index e1aa0dc4e13..30403e3b1d0 100644
--- a/addons/stock/wizard/stock_return_picking.py
+++ b/addons/stock/wizard/stock_return_picking.py
@@ -103,7 +103,7 @@ class stock_return_picking(osv.osv_memory):
return_history = {}
for m_line in pick.move_lines:
return_history[m_line.id] = 0
- for rec in m_line.move_stock_return_history:
+ for rec in m_line.move_history_ids2:
return_history[m_line.id] += rec.product_qty
res['fields'].clear()
arch_lst=['', '
' % _('Return lines'), '' % _('Provide the quantities of the returned products.')]
@@ -169,7 +169,7 @@ class stock_return_picking(osv.osv_memory):
new_qty = data['return%s' % move.id]
returned_qty = move.product_qty
- for rec in move.move_stock_return_history:
+ for rec in move.move_history_ids2:
returned_qty -= rec.product_qty
if returned_qty != new_qty:
@@ -182,7 +182,7 @@ class stock_return_picking(osv.osv_memory):
'picking_id':new_picking, 'state':'draft',
'location_id':new_location, 'location_dest_id':move.location_id.id,
'date':date_cur, 'date_planned':date_cur,})
- move_obj.write(cr, uid, [move.id], {'move_stock_return_history':[(4,new_move)]})
+ move_obj.write(cr, uid, [move.id], {'move_history_ids2':[(4,new_move)]})
if set_invoice_state_to_none:
pick_obj.write(cr, uid, [pick.id], {'invoice_state':'none'})
diff --git a/addons/stock/wizard/stock_splitinto.py b/addons/stock/wizard/stock_splitinto.py
index 8623ba65875..5e75801d2f6 100644
--- a/addons/stock/wizard/stock_splitinto.py
+++ b/addons/stock/wizard/stock_splitinto.py
@@ -41,6 +41,14 @@ class stock_split_into(osv.osv_memory):
quantity = self.browse(cr, uid, data[0], context).quantity or 0.0
for move in move_obj.browse(cr, uid, rec_id):
quantity_rest = move.product_qty - quantity
+ if move.tracking_id :
+ raise osv.except_osv(_('Error!'), _('The current move line is already assigned to a pack, please remove it first if you really want to change it ' \
+ 'for this product: "%s" (id: %d)') % \
+ (move.product_id.name, move.product_id.id,))
+ if quantity > move.product_qty:
+ raise osv.except_osv(_('Error!'), _('Total quantity after split exceeds the quantity to split ' \
+ 'for this product: "%s" (id: %d)') % \
+ (move.product_id.name, move.product_id.id,))
if quantity > 0:
move_obj.setlast_tracking(cr, uid, [move.id], context=context)
move_obj.write(cr, uid, [move.id], {
@@ -51,14 +59,17 @@ class stock_split_into(osv.osv_memory):
if quantity_rest>0:
quantity_rest = move.product_qty - quantity
tracking_id = track_obj.create(cr, uid, {})
- default_val = {
+ if quantity==0.0:
+ move_obj.write(cr, uid, [move.id], {'tracking_id': tracking_id})
+ else:
+ default_val = {
'product_qty': quantity_rest,
'product_uos_qty': quantity_rest,
'tracking_id': tracking_id,
'state': move.state,
'product_uos': move.product_uom.id
}
- current_move = move_obj.copy(cr, uid, move.id, default_val)
+ current_move = move_obj.copy(cr, uid, move.id, default_val)
return {}
stock_split_into()
diff --git a/addons/stock/wizard/stock_splitinto_view.xml b/addons/stock/wizard/stock_splitinto_view.xml
index b6c19db5e9c..a325519ca75 100644
--- a/addons/stock/wizard/stock_splitinto_view.xml
+++ b/addons/stock/wizard/stock_splitinto_view.xml
@@ -7,7 +7,7 @@
form
-
+
diff --git a/addons/stock/wizard/stock_traceability.py b/addons/stock/wizard/stock_traceability.py
index 063c094d296..815f90810ed 100644
--- a/addons/stock/wizard/stock_traceability.py
+++ b/addons/stock/wizard/stock_traceability.py
@@ -39,7 +39,7 @@ class action_traceability(osv.osv_memory):
@return: A dictionary of values
"""
- type1 = context['type'] or 'move_history_ids'
+ type1 = context['type'] or 'move_history_ids2'
field = context['field'] or 'tracking_id'
obj = self.pool.get('stock.move')
ids = obj.search(cr, uid, [(field, 'in',context['active_ids'])])
@@ -47,60 +47,17 @@ class action_traceability(osv.osv_memory):
view_id = cr.fetchone()[0]
value = {
'domain': "[('id','in',["+','.join(map(str, ids))+"])]",
- 'name': ((type1=='move_history_ids') and 'Upstream Traceability') or 'Downstream Traceability',
+ 'name': ((type1=='move_history_ids2') and 'Upstream Traceability') or 'Downstream Traceability',
'view_type': 'tree',
'res_model': 'stock.move',
'field_parent': type1,
'view_id': (view_id,'View'),
- 'type': 'ir.actions.act_window'
+ 'type': 'ir.actions.act_window',
+ 'nodestroy':True,
}
return value
action_traceability()
-class stock_traceability_downstream(osv.osv_memory):
- """
- This class is defined for Stock traceability downstream wizard
-
- """
- _name = "stock.traceability.downstream"
- _inherit = "action.traceability"
- _description = "Stock traceability downstream"
-
-stock_traceability_downstream()
-
-class stock_traceability_upstream(osv.osv_memory):
- """
- This class is defined for Stock traceability upstream wizard
-
- """
- _name = "stock.traceability.upstream"
- _inherit = "action.traceability"
- _description = "Stock traceability upstream"
-
-stock_traceability_upstream()
-
-class stock_traceability_lot_upstream(osv.osv_memory):
- """
- This class is defined for Stock traceability lot upstream wizard
-
- """
- _name = "stock.traceability.lot.upstream"
- _inherit = "action.traceability"
- _description = "Stock traceability lot upstream"
-
-stock_traceability_lot_upstream()
-
-class stock_traceability_lot_downstream(osv.osv_memory):
- """
- This class is defined for Stock traceability lot downstream wizard
-
- """
- _name = "stock.traceability.lot.downstream"
- _inherit = "action.traceability"
- _description = "Stock traceability lot downstream"
-
-stock_traceability_lot_downstream()
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_traceability_view.xml b/addons/stock/wizard/stock_traceability_view.xml
deleted file mode 100644
index 47f244b2cb6..00000000000
--- a/addons/stock/wizard/stock_traceability_view.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
-
- Stock traceability downstream
- stock.traceability.downstream
- form
-
-