X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fstock%2Fstock.py;h=dd04132303374329a86f4dca770314f0d3e8bad5;hb=abff9e1a2577ace9ae38426defd911732c9b7c12;hp=49558fc741925e402b2fe17d70b0bf35224951e8;hpb=17c776c2ebb2dadb36df2ede3a19e71b5d749b03;p=odoo%2Fodoo.git diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 49558fc..dd04132 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -33,7 +33,7 @@ import tools from tools import float_compare import decimal_precision as dp import logging - +_logger = logging.getLogger(__name__) #---------------------------------------------------------- # Incoterms @@ -419,9 +419,8 @@ class stock_location(osv.osv): # 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) + _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 @@ -511,13 +510,17 @@ class stock_tracking(osv.osv): return self.name_get(cr, user, ids, context) def name_get(self, cr, uid, ids, context=None): + """Append the serial to the name""" if not len(ids): return [] - res = [(r['id'], r['name']+' ['+(r['serial'] or '')+']') for r in self.read(cr, uid, ids, ['name', 'serial'], context)] + res = [ (r['id'], r['serial'] and '%s [%s]' % (r['name'], r['serial']) + or r['name'] ) + for r in self.read(cr, uid, ids, ['name', 'serial'], + context=context) ] return res def unlink(self, cr, uid, ids, context=None): - raise osv.except_osv(_('Error'), _('You can not remove a lot line !')) + raise osv.except_osv(_('Error!'), _('You cannot remove a lot line.')) def action_traceability(self, cr, uid, ids, context={}): """ It traces the information of a product @@ -606,7 +609,7 @@ class stock_picking(osv.osv): res[pick]['min_date'] = dt1 res[pick]['max_date'] = dt2 return res - + def create(self, cr, user, vals, context=None): if ('name' not in vals) or (vals.get('name')=='/'): seq_obj_name = 'stock.picking.' + vals['type'] @@ -618,9 +621,9 @@ class stock_picking(osv.osv): _columns = { 'name': fields.char('Reference', size=64, select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}), - 'origin': fields.char('Origin', size=64, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="Reference of the document", select=True), + 'origin': fields.char('Source', 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), - 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal')], 'Shipping Type', required=True, select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="Shipping type specify, goods coming in or going out."), + 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal')], 'Shipping Type', required=True, select=True, readonly=True, help="Shipping type specify, goods coming in or going out."), 'note': fields.text('Notes', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}), 'stock_journal_id': fields.many2one('stock.journal','Stock Journal', select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}), 'location_id': fields.many2one('stock.location', 'Location', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="Keep empty if you produce at the location where the finished products are needed." \ @@ -630,11 +633,12 @@ class stock_picking(osv.osv): '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.selection([ ('draft', 'Draft'), + ('cancel', 'Cancelled'), ('auto', 'Waiting Another Operation'), ('confirmed', 'Waiting Availability'), ('assigned', 'Ready to Transfer'), ('done', 'Transferred'), - ('cancel', 'Cancelled'),], 'State', readonly=True, select=True, help=""" + ], 'Status', readonly=True, select=True, help=""" * Draft: not confirmed yet and will not be scheduled until confirmed\n * Waiting Another Operation: waiting for another move to proceed before it becomes automatically available (e.g. in Make-To-Order flows)\n * Waiting Availability: still waiting for the availability of products\n @@ -673,6 +677,13 @@ class stock_picking(osv.osv): ] def action_process(self, cr, uid, ids, context=None): + if context is None: + context = {} + context.update({ + 'active_model': self._name, + 'active_ids': ids, + 'active_id': len(ids) and ids[0] or False + }) return { 'view_type': 'form', 'view_mode': 'form', @@ -702,6 +713,15 @@ class stock_picking(osv.osv): for move in picking_obj.move_lines: move_obj.write(cr, uid, [move.id], {'tracking_id': False,'prodlot_id':False, 'move_history_ids2': [(6, 0, [])], 'move_history_ids': [(6, 0, [])]}) return res + + def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): + if view_type == 'form' and not view_id: + mod_obj = self.pool.get('ir.model.data') + if self._name == "stock.picking.in": + model,view_id = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_in_form') + if self._name == "stock.picking.out": + model,view_id = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form') + return super(stock_picking,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) def onchange_partner_in(self, cr, uid, context=None, partner_id=None): return {} @@ -713,14 +733,16 @@ class stock_picking(osv.osv): """ Confirms picking. @return: True """ + pickings = self.browse(cr, uid, ids, context=context) + for picking in pickings: + if picking.state <> 'confirmed': + self.confirm_send_note(cr, uid, [picking.id], context=context) self.write(cr, uid, ids, {'state': 'confirmed'}) todo = [] - for picking in self.browse(cr, uid, ids, context=context): + for picking in pickings: for r in picking.move_lines: if r.state == 'draft': todo.append(r.id) - - todo = self.action_explode(cr, uid, todo, context) if len(todo): self.pool.get('stock.move').action_confirm(cr, uid, todo, context=context) @@ -734,13 +756,13 @@ class stock_picking(osv.osv): """ Changes state of picking to available if all moves are confirmed. @return: True """ + wf_service = netsvc.LocalService("workflow") for pick in self.browse(cr, uid, ids): if pick.state == 'draft': - wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_confirm', cr) move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed'] if not move_ids: - raise osv.except_osv(_('Warning !'),_('Not enough stock, unable to reserve the products.')) + raise osv.except_osv(_('Warning!'),_('Not enough stock, unable to reserve the products.')) self.pool.get('stock.move').action_assign(cr, uid, move_ids) return True @@ -762,7 +784,7 @@ class stock_picking(osv.osv): wf_service = netsvc.LocalService("workflow") for pick in self.browse(cr, uid, ids): if not pick.move_lines: - raise osv.except_osv(_('Error !'),_('You can not process picking without stock moves')) + raise osv.except_osv(_('Error!'),_('You cannot process picking without stock moves.')) wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_confirm', cr) return True @@ -1154,15 +1176,16 @@ class stock_picking(osv.osv): return True for move in pick.move_lines: if move.state == 'done': - raise osv.except_osv(_('Error'), _('You cannot cancel picking because stock move is in done state !')) + raise osv.except_osv(_('Error!'), _('You cannot cancel the picking as some moves have been done. You should cancel the picking lines.')) return True + def unlink(self, cr, uid, ids, context=None): move_obj = self.pool.get('stock.move') if context is None: context = {} for pick in self.browse(cr, uid, ids, context=context): if pick.state in ['done','cancel']: - raise osv.except_osv(_('Error'), _('You cannot remove the picking which is in %s state !')%(pick.state,)) + raise osv.except_osv(_('Error!'), _('You cannot remove the picking which is in %s state!')%(pick.state,)) else: ids2 = [move.id for move in pick.move_lines] ctx = context.copy() @@ -1255,9 +1278,14 @@ class stock_picking(osv.osv): for move in too_few: product_qty = move_product_qty[move.id] if not new_picking: + new_picking_name = pick.name + self.write(cr, uid, [pick.id], + {'name': sequence_obj.get(cr, uid, + 'stock.picking.%s'%(pick.type)), + }) new_picking = self.copy(cr, uid, pick.id, { - 'name': sequence_obj.get(cr, uid, 'stock.picking.%s'%(pick.type)), + 'name': new_picking_name, 'move_lines' : [], 'state':'draft', }) @@ -1277,9 +1305,10 @@ class stock_picking(osv.osv): move_obj.copy(cr, uid, move.id, defaults) move_obj.write(cr, uid, [move.id], { - 'product_qty' : move.product_qty - partial_qty[move.id], + 'product_qty': move.product_qty - partial_qty[move.id], 'product_uos_qty': move.product_qty - partial_qty[move.id], #TODO: put correct uos_qty - + 'prodlot_id': False, + 'tracking_id': False, }) if new_picking: @@ -1322,7 +1351,7 @@ class stock_picking(osv.osv): delivered_pack = self.browse(cr, uid, delivered_pack_id, context=context) res[pick.id] = {'delivered_picking': delivered_pack.id or False} - + return res def log_picking(self, cr, uid, ids, context=None): @@ -1364,42 +1393,46 @@ class stock_picking(osv.osv): context.update({'view_id': res and res[1] or False}) message += state_list[pick.state] return True - + # ----------------------------------------- # OpenChatter methods and notifications # ----------------------------------------- - - def _get_document_type(self, type): + + def _get_document_type(self, cr, uid, obj, context=None): type_dict = { - 'out': 'Delivery order', - 'in': 'Shipment', - 'internal': 'Internal picking', + 'out': _('Delivery order'), + 'in': _('Shipment'), + 'internal': _('Internal picking'), } - return type_dict.get(type, 'Stock picking') - + return type_dict.get(obj.type, _('Picking')) + def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s has been created.") % (self._get_document_type(obj.type)), context=context) - + self.message_post(cr, uid, [obj.id], body=_("%s has been created.") % (self._get_document_type(cr, uid, obj, context=context)), context=context) + + def confirm_send_note(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids, context=context): + self.message_post(cr, uid, [obj.id], body=_("%s has been confirmed.") % (self._get_document_type(cr, uid, obj, context=context)), context=context) + def scrap_send_note(self, cr, uid, ids, quantity, uom, name, context=None): - return self.message_append_note(cr, uid, ids, body= _("%s %s %s has been moved to scrap.") % (quantity, uom, name), context=context) - + return self.message_post(cr, uid, ids, body= _("%s %s %s has been moved to scrap.") % (quantity, uom, name), context=context) + def back_order_send_note(self, cr, uid, ids, back_name, context=None): - return self.message_append_note(cr, uid, ids, body=_("Back order %s has been created.") % (back_name), context=context) - + return self.message_post(cr, uid, ids, body=_("Back order %s has been created.") % (back_name), context=context) + def ship_done_send_note(self, cr, uid, ids, context=None): type_dict = { - 'out': 'delivered', - 'in': 'received', - 'internal': 'moved', + 'out': _("Products have been delivered."), + 'in': _("Products have been received."), + 'internal': _("Products have been moved."), } for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Products have been %s.") % (type_dict.get(obj.type, 'move done')), context=context) - + self.message_post(cr, uid, [obj.id], body=type_dict.get(obj.type, _('Products have been moved.')), context=context) + def ship_cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s has been cancelled.") % (self._get_document_type(obj.type)), context=context) - + self.message_post(cr, uid, [obj.id], body=_("%s has been cancelled.") % (self._get_document_type(cr, uid, obj, context=context)), context=context) + stock_picking() @@ -1485,7 +1518,7 @@ class stock_production_lot(osv.osv): 'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type', '<>', 'service')]), 'date': fields.datetime('Creation Date', required=True), 'stock_available': fields.function(_get_stock, fnct_search=_stock_search, type="float", string="Available", select=True, - help="Current quantity of products with this Production Lot Number available in company warehouses", + help="Current quantity of products with this Serial Number available in company warehouses", digits_compute=dp.get_precision('Product Unit of Measure')), 'revisions': fields.one2many('stock.production.lot.revision', 'lot_id', 'Revisions'), 'company_id': fields.many2one('res.company', 'Company', select=True), @@ -1547,6 +1580,7 @@ class stock_move(osv.osv): cr.execute('select id from stock_tracking where create_uid=%s order by id desc limit 1', (uid,)) res = cr.fetchone() return (res and res[0]) or False + _name = "stock.move" _description = "Stock Move" _order = 'date_expected desc, id' @@ -1576,7 +1610,13 @@ class stock_move(osv.osv): def name_get(self, cr, uid, ids, context=None): res = [] for line in self.browse(cr, uid, ids, context=context): - res.append((line.id, (line.product_id.code or '/')+': '+line.location_id.name+' > '+line.location_dest_id.name)) + name = line.location_id.name+' > '+line.location_dest_id.name + # optional prefixes + if line.product_id.code: + name = line.product_id.code + ': ' + name + if line.picking_id.origin: + name = line.picking_id.origin + '/ ' + name + res.append((line.id, name)) return res def _check_tracking(self, cr, uid, ids, context=None): @@ -1633,11 +1673,12 @@ class stock_move(osv.osv): 'picking_id': fields.many2one('stock.picking', 'Reference', select=True,states={'done': [('readonly', True)]}), 'note': fields.text('Notes'), 'state': fields.selection([('draft', 'New'), + ('cancel', 'Cancelled'), ('waiting', 'Waiting Another Move'), ('confirmed', 'Waiting Availability'), ('assigned', 'Available'), ('done', 'Done'), - ('cancel', 'Cancelled')], 'State', readonly=True, select=True, + ], 'Status', readonly=True, select=True, 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"\ @@ -1647,25 +1688,29 @@ class stock_move(osv.osv): 'price_currency_id': fields.many2one('res.currency', 'Currency for average price', help="Technical field used to record the currency chosen by the user during a picking confirmation (when average price costing method is used)"), 'company_id': fields.many2one('res.company', 'Company', required=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), + 'origin': fields.related('picking_id','origin',type='char', size=64, relation="stock.picking", string="Source", store=True), # used for colors in tree views: 'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped', readonly=True), + 'type': fields.related('picking_id', 'type', type='selection', selection=[('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal')], string='Shipping Type'), } + def _check_location(self, cr, uid, ids, context=None): for record in self.browse(cr, uid, ids, context=context): - if (record.state=='done') and (record.location_dest_id.usage == 'view' or record.location_id.usage == 'view'): - return False + if (record.state=='done') and (record.location_id.usage == 'view'): + raise osv.except_osv(_('Error'), _('You cannot move product %s from a location of type view %s.')% (record.product_id.name, record.location_id.name)) + if (record.state=='done') and (record.location_dest_id.usage == 'view' ): + raise osv.except_osv(_('Error'), _('You cannot move product %s to a location of type view %s.')% (record.product_id.name, record.location_dest_id.name)) return True _constraints = [ (_check_tracking, - 'You must assign a serial number for this product', + 'You must assign a serial number for this product.', ['prodlot_id']), - (_check_location, 'You can not move products from or to a location of the type view.', + (_check_location, 'You cannot move products from or to a location of the type view.', ['location_id','location_dest_id']), (_check_product_lot, - 'You try to assign a lot which is not from the same product', + 'You try to assign a lot which is not from the same product.', ['prodlot_id'])] def _default_location_destination(self, cr, uid, context=None): @@ -1726,9 +1771,29 @@ class stock_move(osv.osv): location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', location_xml_id) return location_id + def _default_destination_address(self, cr, uid, context=None): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + return user.company_id.partner_id.id + + def _default_move_type(self, cr, uid, context=None): + """ Gets default type of move + @return: type + """ + if context is None: + context = {} + picking_type = context.get('picking_type') + type = 'internal' + if picking_type == 'in': + type = 'in' + elif picking_type == 'out': + type = 'out' + return type + _defaults = { 'location_id': _default_location_source, 'location_dest_id': _default_location_destination, + 'partner_id': _default_destination_address, + 'type': _default_move_type, 'state': 'draft', 'priority': '1', 'product_qty': 1.0, @@ -1746,8 +1811,8 @@ class stock_move(osv.osv): for move in self.browse(cr, uid, ids, context=context): if move.state == 'done': if frozen_fields.intersection(vals): - raise osv.except_osv(_('Operation forbidden'), - _('Quantities, Unit of Measures, Products and Locations cannot be modified on stock moves that have already been processed (except by the Administrator)')) + raise osv.except_osv(_('Operation forbidden !'), + _('Quantities, Unit of Measures, 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): @@ -1791,8 +1856,8 @@ class stock_move(osv.osv): warning = {} if (location.usage == 'internal') and (product_qty > (amount_actual or 0.0)): warning = { - 'title': _('Insufficient Stock in Lot !'), - 'message': _('You are moving %.2f %s products but only %.2f %s available in this lot.') % (product_qty, uom.name, amount_actual, uom.name) + 'title': _('Insufficient Stock for Serial Number !'), + 'message': _('You are moving %.2f %s but only %.2f %s available for this serial number.') % (product_qty, uom.name, amount_actual, uom.name) } return {'warning': warning} @@ -1872,7 +1937,8 @@ class stock_move(osv.osv): 'product_uom': product.uom_id.id, 'product_uos': uos_id, 'product_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'] + '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'], + 'prodlot_id' : False, } if not ids: result['name'] = product.partner_ref @@ -1882,6 +1948,32 @@ class stock_move(osv.osv): result['location_dest_id'] = loc_dest_id return {'value': result} + def onchange_move_type(self, cr, uid, ids, type, context=None): + """ On change of move type gives sorce and destination location. + @param type: Move Type + @return: Dictionary of values + """ + mod_obj = self.pool.get('ir.model.data') + location_source_id = False + location_dest_id = False + if type == 'in': + location_source_id = 'stock_location_suppliers' + location_dest_id = 'stock_location_stock' + elif type == 'out': + location_source_id = 'stock_location_stock' + location_dest_id = 'stock_location_customers' + if location_source_id: + try: + location_model, location_source_id = mod_obj.get_object_reference(cr, uid, 'stock', location_source_id) + except ValueError, e: + location_source_id = False + if location_dest_id: + try: + location_model, location_dest_id = mod_obj.get_object_reference(cr, uid, 'stock', location_dest_id) + except ValueError, e: + location_dest_id = False + return {'value':{'location_id': location_source_id, 'location_dest_id': location_dest_id}} + def onchange_date(self, cr, uid, ids, date, date_expected, context=None): """ On change of Scheduled Date gives a Move date. @param date_expected: Scheduled Date @@ -2080,11 +2172,12 @@ class stock_move(osv.osv): done.append(move.id) pickings[move.picking_id.id] = 1 r = res.pop(0) - cr.execute('update stock_move set location_id=%s, product_qty=%s where id=%s', (r[1], r[0], move.id)) + product_uos_qty = self.pool.get('stock.move').onchange_quantity(cr, uid, ids, move.product_id.id, r[0], move.product_id.uom_id.id, move.product_id.uos_id.id)['value']['product_uos_qty'] + cr.execute('update stock_move set location_id=%s, product_qty=%s, product_uos_qty=%s where id=%s', (r[1], r[0],product_uos_qty, move.id)) while res: r = res.pop(0) - move_id = self.copy(cr, uid, move.id, {'product_qty': r[0], 'location_id': r[1]}) + move_id = self.copy(cr, uid, move.id, {'product_uos_qty': product_uos_qty, 'product_qty': r[0], 'location_id': r[1]}) done.append(move_id) if done: count += len(done) @@ -2165,22 +2258,22 @@ class stock_move(osv.osv): journal_id = accounts['stock_journal'] if acc_dest == acc_valuation: - raise osv.except_osv(_('Error!'), _('Can not create Journal Entry, Output Account defined on this product and Valuation account on category of this product are same.')) + raise osv.except_osv(_('Error!'), _('Cannot create Journal Entry, Output Account of this product and Valuation account on category of this product are same.')) if acc_src == acc_valuation: - raise osv.except_osv(_('Error!'), _('Can not create Journal Entry, Input Account defined on this product and Valuation account on category of this product are same.')) + raise osv.except_osv(_('Error!'), _('Cannot create Journal Entry, Input Account of this product and Valuation account on category of this product are same.')) if not acc_src: - raise osv.except_osv(_('Error!'), _('There is no stock input account defined for this product or its category: "%s" (id: %d)') % \ + raise osv.except_osv(_('Error!'), _('Please define stock input account for this product or its category: "%s" (id: %d)') % \ (move.product_id.name, move.product_id.id,)) if not acc_dest: - raise osv.except_osv(_('Error!'), _('There is no stock output account defined for this product or its category: "%s" (id: %d)') % \ + raise osv.except_osv(_('Error!'), _('Please define stock output account for this product or its category: "%s" (id: %d)') % \ (move.product_id.name, move.product_id.id,)) if not journal_id: - raise osv.except_osv(_('Error!'), _('There is no journal defined on the product category: "%s" (id: %d)') % \ + raise osv.except_osv(_('Error!'), _('Please define journal on the product category: "%s" (id: %d)') % \ (move.product_id.categ_id.name, move.product_id.categ_id.id,)) if not acc_valuation: - raise osv.except_osv(_('Error!'), _('There is no inventory Valuation account defined on the product category: "%s" (id: %d)') % \ + raise osv.except_osv(_('Error!'), _('Please define inventory valuation account on the product category: "%s" (id: %d)') % \ (move.product_id.categ_id.name, move.product_id.categ_id.id,)) return journal_id, acc_src, acc_dest, acc_valuation @@ -2234,7 +2327,11 @@ class stock_move(osv.osv): or move.location_id.company_id != move.location_dest_id.company_id): journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, src_company_ctx) reference_amount, reference_currency_id = self._get_reference_accounting_values_for_valuation(cr, uid, move, src_company_ctx) - account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_valuation, acc_dest, reference_amount, reference_currency_id, context))] + #returning goods to supplier + if move.location_dest_id.usage == 'supplier': + account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_valuation, acc_src, reference_amount, reference_currency_id, context))] + else: + account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_valuation, acc_dest, reference_amount, reference_currency_id, context))] # Incoming moves (or cross-company input part) if move.location_dest_id.company_id \ @@ -2242,7 +2339,11 @@ class stock_move(osv.osv): or move.location_id.company_id != move.location_dest_id.company_id): journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, dest_company_ctx) reference_amount, reference_currency_id = self._get_reference_accounting_values_for_valuation(cr, uid, move, src_company_ctx) - account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_src, acc_valuation, reference_amount, reference_currency_id, context))] + #goods return from customer + if move.location_id.usage == 'customer': + account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_dest, acc_valuation, reference_amount, reference_currency_id, context))] + else: + account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, acc_src, acc_valuation, reference_amount, reference_currency_id, context))] move_obj = self.pool.get('account.move') for j_id, move_lines in account_moves: @@ -2360,7 +2461,7 @@ class stock_move(osv.osv): ctx = context.copy() for move in self.browse(cr, uid, ids, context=context): if move.state != 'draft' and not ctx.get('call_unlink',False): - raise osv.except_osv(_('UserError'), + raise osv.except_osv(_('User Error!'), _('You can only delete draft moves.')) return super(stock_move, self).unlink( cr, uid, ids, context=ctx) @@ -2386,7 +2487,7 @@ class stock_move(osv.osv): """ #quantity should in MOVE UOM if quantity <= 0: - raise osv.except_osv(_('Warning!'), _('Please provide a positive quantity to scrap!')) + 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 @@ -2430,7 +2531,7 @@ class stock_move(osv.osv): if context is None: context = {} if quantity <= 0: - raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !')) + raise osv.except_osv(_('Warning!'), _('Please provide proper quantity.')) res = [] @@ -2491,12 +2592,12 @@ class stock_move(osv.osv): if context is None: context = {} if quantity <= 0: - raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !')) + raise osv.except_osv(_('Warning!'), _('Please provide proper quantity.')) res = [] for move in self.browse(cr, uid, ids, context=context): move_qty = move.product_qty if move_qty <= 0: - raise osv.except_osv(_('Error!'), _('Can not consume a move with negative or zero quantity !')) + raise osv.except_osv(_('Error!'), _('Cannot consume a move with negative or zero quantity.')) quantity_rest = move.product_qty quantity_rest -= quantity uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty @@ -2530,11 +2631,6 @@ class stock_move(osv.osv): 'location_id': location_id or move.location_id.id, } self.write(cr, uid, [move.id], update_val) - - product_obj = self.pool.get('product.product') - for new_move in self.browse(cr, uid, res, context=context): - message = _("Product has been consumed with '%s' quantity.") % (new_move.product_qty) - product_obj.message_append_note(cr, uid, [new_move.product_id.id], body=message, context=context) self.action_done(cr, uid, res, context=context) @@ -2564,7 +2660,7 @@ class stock_move(osv.osv): if move.state in ('done', 'cancel'): continue partial_data = partial_datas.get('move%s'%(move.id), False) - assert partial_data, _('Missing partial picking data for move #%s') % (move.id) + assert partial_data, _('Missing partial picking data for move #%s.') % (move.id) product_qty = partial_data.get('product_qty',0.0) move_product_qty[move.id] = product_qty product_uom = partial_data.get('product_uom',False) @@ -2624,8 +2720,10 @@ class stock_move(osv.osv): complete.append(self.browse(cr, uid, new_move)) self.write(cr, uid, [move.id], { - 'product_qty' : move.product_qty - product_qty, - 'product_uos_qty':move.product_qty - product_qty, + 'product_qty': move.product_qty - product_qty, + 'product_uos_qty': move.product_qty - product_qty, + 'prodlot_id': False, + 'tracking_id': False, }) @@ -2666,7 +2764,7 @@ class stock_inventory(osv.osv): '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'), ('confirm','Confirmed'),('cancel','Cancelled')), 'State', readonly=True, select=True), + 'state': fields.selection( (('draft', 'Draft'), ('cancel','Cancelled'), ('confirm','Confirmed'), ('done', 'Done')), 'Status', readonly=True, select=True), 'company_id': fields.many2one('res.company', 'Company', required=True, select=True, readonly=True, states={'draft':[('readonly',False)]}), } @@ -2772,7 +2870,7 @@ class stock_inventory(osv.osv): account_move_data_l = account_move_obj.read(cr, uid, account_move_ids, ['state'], context=context) for account_move in account_move_data_l: if account_move['state'] == 'posted': - raise osv.except_osv(_('UserError'), + raise osv.except_osv(_('User Error!'), _('In order to cancel this inventory, you must first unpost related journal entries.')) account_move_obj.unlink(cr, uid, [account_move['id']], context=context) self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context) @@ -2792,7 +2890,15 @@ class stock_inventory_line(osv.osv): 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure')), 'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True, select=True, readonly=True), 'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"), - 'state': fields.related('inventory_id','state',type='char',string='State',readonly=True), + 'state': fields.related('inventory_id','state',type='char',string='Status',readonly=True), + } + + def _default_stock_location(self, cr, uid, context=None): + stock_location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock') + return stock_location.id + + _defaults = { + 'location_id': _default_stock_location } def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False, to_date=False): @@ -2803,11 +2909,11 @@ class stock_inventory_line(osv.osv): @return: Dictionary of changed values """ if not product: - return {'value': {'product_qty': 0.0, 'product_uom': False}} + return {'value': {'product_qty': 0.0, 'product_uom': False, 'prod_lot_id': False}} obj_product = self.pool.get('product.product').browse(cr, uid, product) uom = uom or obj_product.uom_id.id amount = self.pool.get('stock.location')._product_get(cr, uid, location_id, [product], {'uom': uom, 'to_date': to_date, 'compute_child': False})[product] - result = {'product_qty': amount, 'product_uom': uom} + result = {'product_qty': amount, 'product_uom': uom, 'prod_lot_id': False} return {'value': result} stock_inventory_line() @@ -2826,8 +2932,20 @@ class stock_warehouse(osv.osv): 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True, domain=[('usage','=','internal')]), 'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True, domain=[('usage','<>','view')]), } + + def _default_lot_input_stock_id(self, cr, uid, context=None): + lot_input_stock = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock') + return lot_input_stock.id + + def _default_lot_output_id(self, cr, uid, context=None): + lot_output = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_output') + return lot_output.id + _defaults = { 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c), + 'lot_input_id': _default_lot_input_stock_id, + 'lot_stock_id': _default_lot_input_stock_id, + 'lot_output_id': _default_lot_output_id, } stock_warehouse() @@ -2855,15 +2973,21 @@ class stock_picking_in(osv.osv): #instead of it's own workflow (which is not existing) return self.pool.get('stock.picking')._workflow_trigger(cr, uid, ids, trigger, context=context) + def _workflow_signal(self, cr, uid, ids, signal, context=None): + #override in order to fire the workflow signal on given stock.picking workflow instance + #instead of it's own workflow (which is not existing) + return self.pool.get('stock.picking')._workflow_signal(cr, uid, ids, signal, context=context) + _columns = { + 'backorder_id': fields.many2one('stock.picking.in', '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), 'state': fields.selection( [('draft', 'Draft'), ('auto', 'Waiting Another Operation'), ('confirmed', 'Waiting Availability'), ('assigned', 'Ready to Receive'), ('done', 'Received'), - ('cancel', 'Cancelled'),], - 'State', readonly=True, select=True, + ('cancel', 'Cancelled'),], + 'State', readonly=True, select=True, help="""* Draft: not confirmed yet and will not be scheduled until confirmed\n * Waiting Another Operation: waiting for another move to proceed before it becomes automatically available (e.g. in Make-To-Order flows)\n * Waiting Availability: still waiting for the availability of products\n @@ -2894,15 +3018,21 @@ class stock_picking_out(osv.osv): #instead of it's own workflow (which is not existing) return self.pool.get('stock.picking')._workflow_trigger(cr, uid, ids, trigger, context=context) + def _workflow_signal(self, cr, uid, ids, signal, context=None): + #override in order to fire the workflow signal on given stock.picking workflow instance + #instead of it's own workflow (which is not existing) + return self.pool.get('stock.picking')._workflow_signal(cr, uid, ids, signal, context=context) + _columns = { + 'backorder_id': fields.many2one('stock.picking.out', '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), 'state': fields.selection( [('draft', 'Draft'), ('auto', 'Waiting Another Operation'), ('confirmed', 'Waiting Availability'), ('assigned', 'Ready to Deliver'), ('done', 'Delivered'), - ('cancel', 'Cancelled'),], - 'State', readonly=True, select=True, + ('cancel', 'Cancelled'),], + 'State', readonly=True, select=True, help="""* Draft: not confirmed yet and will not be scheduled until confirmed\n * Waiting Another Operation: waiting for another move to proceed before it becomes automatically available (e.g. in Make-To-Order flows)\n * Waiting Availability: still waiting for the availability of products\n