[MERGE] forward port of branch saas-4 up to 65f68c1
authorChristophe Simonis <chs@odoo.com>
Tue, 3 Jun 2014 17:45:19 +0000 (19:45 +0200)
committerChristophe Simonis <chs@odoo.com>
Tue, 3 Jun 2014 17:45:19 +0000 (19:45 +0200)
1  2 
addons/delivery/sale.py
addons/event_sale/event_sale.py
addons/mail/mail_thread.py
addons/purchase/purchase.py
addons/sale/sale.py
addons/stock/stock.py
addons/web_kanban/static/src/js/kanban.js
openerp/osv/orm.py

Simple merge
Simple merge
@@@ -943,10 -933,11 +943,11 @@@ class mail_thread(osv.AbstractModel)
                  mail_message = mail_msg_obj.browse(cr, uid, mail_message_ids[0], context=context)
                  route = self.message_route_verify(cr, uid, message, message_dict,
                                  (mail_message.model, mail_message.res_id, custom_values, uid, None),
 -                                update_author=True, assert_model=True, create_fallback=True, context=context)
 +                                update_author=True, assert_model=True, create_fallback=True, allow_private=True, context=context)
                  if route:
-                     _logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s',
-                                  email_from, email_to, message_id, mail_message.id, custom_values, uid)
+                     _logger.info(
+                         'Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s',
+                         email_from, email_to, message_id, mail_message.id, custom_values, uid)
                      return [route]
  
          # 3. Look for a matching mail.alias entry
@@@ -655,14 -589,16 +655,16 @@@ class purchase_order(osv.osv)
              self.pool.get('stock.picking') \
                  .signal_button_cancel(cr, uid, map(attrgetter('id'), purchase.picking_ids))
              for inv in purchase.invoice_ids:
 -                if inv and inv.state not in ('cancel','draft'):
 +                if inv and inv.state not in ('cancel', 'draft'):
                      raise osv.except_osv(
                          _('Unable to cancel this purchase order.'),
 -                        _('You must first cancel all receptions related to this purchase order.'))
 +                        _('You must first cancel all invoices related to this purchase order.'))
              self.pool.get('account.invoice') \
                  .signal_invoice_cancel(cr, uid, map(attrgetter('id'), purchase.invoice_ids))
+             self.pool['purchase.order.line'].write(cr, uid, [l.id for l in  purchase.order_line],
+                     {'state': 'cancel'})
 -        self.write(cr,uid,ids,{'state':'cancel'})
 -
 +        self.write(cr, uid, ids, {'state': 'cancel'})
 +        self.set_order_line_status(cr, uid, ids, 'cancel', context=context)
          self.signal_purchase_cancel(cr, uid, ids)
          return True
  
@@@ -982,10 -905,14 +984,13 @@@ class purchase_order_line(osv.osv)
          return super(purchase_order_line, self).copy_data(cr, uid, id, default, context)
  
      def unlink(self, cr, uid, ids, context=None):
 -        procurement_ids_to_cancel = []
+         for line in self.browse(cr, uid, ids, context=context):
+             if line.state not in ['draft', 'cancel']:
+                 raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a purchase order line which is in state \'%s\'.') %(line.state,))
 -            if line.move_dest_id:
 -                procurement_ids_to_cancel.extend(procurement.id for procurement in line.move_dest_id.procurements)
 +        procurement_obj = self.pool.get('procurement.order')
 +        procurement_ids_to_cancel = procurement_obj.search(cr, uid, [('purchase_line_id', 'in', ids)], context=context)
          if procurement_ids_to_cancel:
 -            self.pool['procurement.order'].action_cancel(cr, uid, procurement_ids_to_cancel)
 +            self.pool['procurement.order'].cancel(cr, uid, procurement_ids_to_cancel)
          return super(purchase_order_line, self).unlink(cr, uid, ids, context=context)
  
      def onchange_product_uom(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
Simple merge
@@@ -2146,166 -2225,273 +2146,169 @@@ class stock_move(osv.osv)
          """ Cancels the moves and if all moves are cancelled it cancels the picking.
          @return: True
          """
 -        if not len(ids):
 -            return True
 -        if context is None:
 -            context = {}
 -        pickings = set()
 +        procurement_obj = self.pool.get('procurement.order')
 +        context = context or {}
          for move in self.browse(cr, uid, ids, context=context):
 -            if move.state in ('confirmed', 'waiting', 'assigned', 'draft'):
 -                if move.picking_id:
 -                    pickings.add(move.picking_id.id)
 -            if move.move_dest_id and move.move_dest_id.state == 'waiting':
 -                self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
 -                if context.get('call_unlink',False) and move.move_dest_id.picking_id:
 -                    workflow.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
 -        self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
 -        if not context.get('call_unlink',False):
 -            for pick in self.pool.get('stock.picking').browse(cr, uid, list(pickings), context=context):
 -                if all(move.state == 'cancel' for move in pick.move_lines):
 -                    self.pool.get('stock.picking').write(cr, uid, [pick.id], {'state': 'cancel'}, context=context)
 -
 -        for id in ids:
 -            workflow.trg_trigger(uid, 'stock.move', id, cr)
 -        return True
 -
 -    def _get_accounting_data_for_valuation(self, cr, uid, move, context=None):
 -        """
 -        Return the accounts and journal to use to post Journal Entries for the real-time
 -        valuation of the move.
 -
 -        :param context: context dictionary that can explicitly mention the company to consider via the 'force_company' key
 -        :raise: osv.except_osv() is any mandatory account or journal is not defined.
 -        """
 -        product_obj=self.pool.get('product.product')
 -        accounts = product_obj.get_product_accounts(cr, uid, move.product_id.id, context)
 -        if move.location_id.valuation_out_account_id:
 -            acc_src = move.location_id.valuation_out_account_id.id
 -        else:
 -            acc_src = accounts['stock_account_input']
 -
 -        if move.location_dest_id.valuation_in_account_id:
 -            acc_dest = move.location_dest_id.valuation_in_account_id.id
 -        else:
 -            acc_dest = accounts['stock_account_output']
 -
 -        acc_valuation = accounts.get('property_stock_valuation_account_id', False)
 -        journal_id = accounts['stock_journal']
 -
 -        if acc_dest == acc_valuation:
 -            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!'),  _('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!'),  _('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!'),  _('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!'), _('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!'), _('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
 -
 -    def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
 -        """
 -        Return the reference amount and reference currency representing the inventory valuation for this move.
 -        These reference values should possibly be converted before being posted in Journals to adapt to the primary
 -        and secondary currencies of the relevant accounts.
 -        """
 -        product_uom_obj = self.pool.get('product.uom')
 -
 -        # by default the reference currency is that of the move's company
 -        reference_currency_id = move.company_id.currency_id.id
 -
 -        default_uom = move.product_id.uom_id.id
 -        qty = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
 -
 -        # if product is set to average price and a specific value was entered in the picking wizard,
 -        # we use it
 -        if move.location_dest_id.usage != 'internal' and move.product_id.cost_method == 'average':
 -            reference_amount = qty * move.product_id.standard_price
 -        elif move.product_id.cost_method == 'average' and move.price_unit:
 -            reference_amount = qty * move.price_unit
 -            reference_currency_id = move.price_currency_id.id or reference_currency_id
 -
 -        # Otherwise we default to the company's valuation price type, considering that the values of the
 -        # valuation field are expressed in the default currency of the move's company.
 -        else:
 -            if context is None:
 -                context = {}
 -            currency_ctx = dict(context, currency_id = move.company_id.currency_id.id)
 -            amount_unit = move.product_id.price_get('standard_price', context=currency_ctx)[move.product_id.id]
 -            reference_amount = amount_unit * qty
 -
 -        return reference_amount, reference_currency_id
 -
 -
 -    def _create_product_valuation_moves(self, cr, uid, move, context=None):
 -        """
 -        Generate the appropriate accounting moves if the product being moves is subject
 -        to real_time valuation tracking, and the source or destination location is
 -        a transit location or is outside of the company.
 -        """
 -        if move.product_id.valuation == 'real_time': # FIXME: product valuation should perhaps be a property?
 -            if context is None:
 -                context = {}
 -            src_company_ctx = dict(context,force_company=move.location_id.company_id.id)
 -            dest_company_ctx = dict(context,force_company=move.location_dest_id.company_id.id)
 -            account_moves = []
 -            # Outgoing moves (or cross-company output part)
 -            if move.location_id.company_id \
 -                and (move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal'\
 -                     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)
 -                #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 \
 -                and (move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal'\
 -                     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)
 -                #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))]
 +            if move.state == 'done':
 +                raise osv.except_osv(_('Operation Forbidden!'),
 +                        _('You cannot cancel a stock move that has been set to \'Done\'.'))
 +            if move.reserved_quant_ids:
 +                self.pool.get("stock.quant").quants_unreserve(cr, uid, move, context=context)
 +            if context.get('cancel_procurement'):
 +                if move.propagate:
 +                    procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
 +                    procurement_obj.cancel(cr, uid, procurement_ids, context=context)
 +            elif move.move_dest_id:
 +                #cancel chained moves
 +                if move.propagate:
 +                    self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
 +                    # If we have a long chain of moves to be cancelled, it is easier for the user to handle
 +                    # only the last procurement which will go into exception, instead of all procurements
 +                    # along the chain going into exception.  We need to check if there are no split moves not cancelled however
 +                    if move.procurement_id:
 +                        proc = move.procurement_id
 +                        if all([x.state == 'cancel' for x in proc.move_ids if x.id != move.id]):
 +                            procurement_obj.write(cr, uid, [proc.id], {'state': 'cancel'})
 +
 +                elif move.move_dest_id.state == 'waiting':
 +                    self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
 +        return self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
 +
 +    def _check_package_from_moves(self, cr, uid, ids, context=None):
 +        pack_obj = self.pool.get("stock.quant.package")
 +        packs = set()
 +        for move in self.browse(cr, uid, ids, context=context):
 +            packs |= set([q.package_id for q in move.quant_ids if q.package_id and q.qty > 0])
 +        return pack_obj._check_location_constraint(cr, uid, list(packs), context=context)
 +
 +    def find_move_ancestors(self, cr, uid, move, context=None):
 +        '''Find the first level ancestors of given move '''
 +        ancestors = []
 +        move2 = move
 +        while move2:
 +            ancestors += [x.id for x in move2.move_orig_ids]
 +            #loop on the split_from to find the ancestor of split moves only if the move has not direct ancestor (priority goes to them)
 +            move2 = not move2.move_orig_ids and move2.split_from or False
 +        return ancestors
 +
 +    def recalculate_move_state(self, cr, uid, move_ids, context=None):
 +        '''Recompute the state of moves given because their reserved quants were used to fulfill another operation'''
 +        for move in self.browse(cr, uid, move_ids, context=context):
 +            vals = {}
 +            reserved_quant_ids = move.reserved_quant_ids
 +            if len(reserved_quant_ids) > 0 and not move.partially_available:
 +                vals['partially_available'] = True
 +            if len(reserved_quant_ids) == 0 and move.partially_available:
 +                vals['partially_available'] = False
 +            if move.state == 'assigned':
 +                if self.find_move_ancestors(cr, uid, move, context=context):
 +                    vals['state'] = 'waiting'
                  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:
 -                move_obj.create(cr, uid,
 -                        {
 -                         'journal_id': j_id,
 -                         'line_id': move_lines,
 -                         'ref': move.picking_id and move.picking_id.name}, context=context)
 +                    vals['state'] = 'confirmed'
 +            if vals:
 +                self.write(cr, uid, [move.id], vals, context=context)
  
      def action_done(self, cr, uid, ids, context=None):
 -        """ Makes the move done and if all moves are done, it will finish the picking.
 -        @return:
 +        """ Process completly the moves given as ids and if all moves are done, it will finish the picking.
          """
 -        picking_ids = []
 -        move_ids = []
 -        if context is None:
 -            context = {}
 -
 -        todo = []
 -        for move in self.browse(cr, uid, ids, context=context):
 -            if move.state=="draft":
 -                todo.append(move.id)
 +        context = context or {}
 +        picking_obj = self.pool.get("stock.picking")
 +        quant_obj = self.pool.get("stock.quant")
 +        todo = [move.id for move in self.browse(cr, uid, ids, context=context) if move.state == "draft"]
          if todo:
 -            self.action_confirm(cr, uid, todo, context=context)
 -            todo = []
 -
 +            ids = self.action_confirm(cr, uid, todo, context=context)
 +        pickings = set()
 +        procurement_ids = []
 +        #Search operations that are linked to the moves
 +        operations = set()
 +        move_qty = {}
          for move in self.browse(cr, uid, ids, context=context):
 -            if move.state in ['done','cancel']:
 -                continue
 -            move_ids.append(move.id)
 -
 -            if move.picking_id:
 -                picking_ids.append(move.picking_id.id)
 -            if move.move_dest_id.id and (move.state != 'done'):
 -                # Downstream move should only be triggered if this move is the last pending upstream move
 -                other_upstream_move_ids = self.search(cr, uid, [('id','not in',move_ids),('state','not in',['done','cancel']),
 -                                            ('move_dest_id','=',move.move_dest_id.id)], context=context)
 -                if not other_upstream_move_ids:
 -                    self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]})
 -                    if move.move_dest_id.state in ('waiting', 'confirmed'):
 -                        self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
 -                        if move.move_dest_id.picking_id:
 -                            workflow.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
 -                        if move.move_dest_id.auto_validate:
 -                            self.action_done(cr, uid, [move.move_dest_id.id], context=context)
 -
 -            self._create_product_valuation_moves(cr, uid, move, context=context)
 -            if move.state not in ('confirmed','done','assigned'):
 -                todo.append(move.id)
 -
 -        if todo:
 -            self.action_confirm(cr, uid, todo, context=context)
 -
 -        self.write(cr, uid, move_ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
 -        for id in move_ids:
 -             workflow.trg_trigger(uid, 'stock.move', id, cr)
 -
 -        for pick_id in picking_ids:
 -            workflow.trg_write(uid, 'stock.picking', pick_id, cr)
 -
 +            move_qty[move.id] = move.product_qty
 +            for link in move.linked_move_operation_ids:
 +                operations.add(link.operation_id)
 +
 +        #Sort operations according to entire packages first, then package + lot, package only, lot only
 +        operations = list(operations)
 +        operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))
 +
 +        for ops in operations:
 +            if ops.picking_id:
 +                pickings.add(ops.picking_id.id)
 +            main_domain = [('qty', '>', 0)]
 +            for record in ops.linked_move_operation_ids:
 +                move = record.move_id
 +                self.check_tracking(cr, uid, move, ops.package_id.id or ops.lot_id.id, context=context)
 +                prefered_domain = [('reservation_id', '=', move.id)]
 +                fallback_domain = [('reservation_id', '=', False)]
 +                fallback_domain2 = ['&', ('reservation_id', '!=', move.id), ('reservation_id', '!=', False)]
 +                prefered_domain_list = [prefered_domain] + [fallback_domain] + [fallback_domain2]
 +                dom = main_domain + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context)
 +                quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, record.qty, domain=dom, prefered_domain_list=prefered_domain_list,
 +                                                          restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
 +                if ops.result_package_id.id:
 +                    #if a result package is given, all quants go there
 +                    quant_dest_package_id = ops.result_package_id.id
 +                elif ops.product_id and ops.package_id:
 +                    #if a package and a product is given, we will remove quants from the pack.
 +                    quant_dest_package_id = False
 +                else:
 +                    #otherwise we keep the current pack of the quant, which may mean None
 +                    quant_dest_package_id = ops.package_id.id
 +                quant_obj.quants_move(cr, uid, quants, move, ops.location_dest_id, location_from=ops.location_id, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=quant_dest_package_id, context=context)
 +                # Handle pack in pack
 +                if not ops.product_id and ops.package_id and ops.result_package_id.id != ops.package_id.parent_id.id:
 +                    self.pool.get('stock.quant.package').write(cr, SUPERUSER_ID, [ops.package_id.id], {'parent_id': ops.result_package_id.id}, context=context)
 +                move_qty[move.id] -= record.qty
 +        #Check for remaining qtys and unreserve/check move_dest_id in
 +        for move in self.browse(cr, uid, ids, context=context):
 +            if move_qty[move.id] > 0:  # (=In case no pack operations in picking)
 +                main_domain = [('qty', '>', 0)]
 +                prefered_domain = [('reservation_id', '=', move.id)]
 +                fallback_domain = [('reservation_id', '=', False)]
 +                fallback_domain2 = ['&', ('reservation_id', '!=', move.id), ('reservation_id', '!=', False)]
 +                prefered_domain_list = [prefered_domain] + [fallback_domain] + [fallback_domain2]
 +                self.check_tracking(cr, uid, move, move.restrict_lot_id.id, context=context)
 +                qty = move_qty[move.id]
 +                quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain, prefered_domain_list=prefered_domain_list, restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
 +                quant_obj.quants_move(cr, uid, quants, move, move.location_dest_id, lot_id=move.restrict_lot_id.id, owner_id=move.restrict_partner_id.id, context=context)
 +            #unreserve the quants and make them available for other operations/moves
 +            quant_obj.quants_unreserve(cr, uid, move, context=context)
 +
 +            #Check moves that were pushed
 +            if move.move_dest_id.state in ('waiting', 'confirmed'):
++                # FIXME is opw 607970 still present with new WMS?
++                # (see commits 1ef2c181033bd200906fb1e5ce35e234bf566ac6
++                # and 41c5ceb8ebb95c1b4e98d8dd1f12b8e547a24b1d)
 +                other_upstream_move_ids = self.search(cr, uid, [('id', '!=', move.id), ('state', 'not in', ['done', 'cancel']),
 +                                            ('move_dest_id', '=', move.move_dest_id.id)], context=context)
 +                #If no other moves for the move that got pushed:
 +                if not other_upstream_move_ids and move.move_dest_id.state in ('waiting', 'confirmed'):
 +                    self.action_assign(cr, uid, [move.move_dest_id.id], context=context)
 +            if move.procurement_id:
 +                procurement_ids.append(move.procurement_id.id)
 +
 +        # Check the packages have been placed in the correct locations
 +        self._check_package_from_moves(cr, uid, ids, context=context)
 +        #set the move as done
 +        self.write(cr, uid, ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
 +        self.pool.get('procurement.order').check(cr, uid, procurement_ids, context=context)
 +        #check picking state to set the date_done is needed
 +        done_picking = []
 +        for picking in picking_obj.browse(cr, uid, list(pickings), context=context):
 +            if picking.state == 'done' and not picking.date_done:
 +                done_picking.append(picking.id)
 +        if done_picking:
 +            picking_obj.write(cr, uid, done_picking, {'date_done': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
          return True
  
 -    def _create_account_move_line(self, cr, uid, move, src_account_id, dest_account_id, reference_amount, reference_currency_id, context=None):
 -        """
 -        Generate the account.move.line values to post to track the stock valuation difference due to the
 -        processing of the given stock move.
 -        """
 -        # prepare default values considering that the destination accounts have the reference_currency_id as their main currency
 -        partner_id = (move.picking_id.partner_id and self.pool.get('res.partner')._find_accounting_partner(move.picking_id.partner_id).id) or False
 -        debit_line_vals = {
 -                    'name': move.name,
 -                    'product_id': move.product_id and move.product_id.id or False,
 -                    'quantity': move.product_qty,
 -                    'ref': move.picking_id and move.picking_id.name or False,
 -                    'date': time.strftime('%Y-%m-%d'),
 -                    'partner_id': partner_id,
 -                    'debit': reference_amount,
 -                    'account_id': dest_account_id,
 -        }
 -        credit_line_vals = {
 -                    'name': move.name,
 -                    'product_id': move.product_id and move.product_id.id or False,
 -                    'quantity': move.product_qty,
 -                    'ref': move.picking_id and move.picking_id.name or False,
 -                    'date': time.strftime('%Y-%m-%d'),
 -                    'partner_id': partner_id,
 -                    'credit': reference_amount,
 -                    'account_id': src_account_id,
 -        }
 -
 -        # if we are posting to accounts in a different currency, provide correct values in both currencies correctly
 -        # when compatible with the optional secondary currency on the account.
 -        # Financial Accounts only accept amounts in secondary currencies if there's no secondary currency on the account
 -        # or if it's the same as that of the secondary amount being posted.
 -        account_obj = self.pool.get('account.account')
 -        src_acct, dest_acct = account_obj.browse(cr, uid, [src_account_id, dest_account_id], context=context)
 -        src_main_currency_id = src_acct.company_id.currency_id.id
 -        dest_main_currency_id = dest_acct.company_id.currency_id.id
 -        cur_obj = self.pool.get('res.currency')
 -        if reference_currency_id != src_main_currency_id:
 -            # fix credit line:
 -            credit_line_vals['credit'] = cur_obj.compute(cr, uid, reference_currency_id, src_main_currency_id, reference_amount, context=context)
 -            if (not src_acct.currency_id) or src_acct.currency_id.id == reference_currency_id:
 -                credit_line_vals.update(currency_id=reference_currency_id, amount_currency=-reference_amount)
 -        if reference_currency_id != dest_main_currency_id:
 -            # fix debit line:
 -            debit_line_vals['debit'] = cur_obj.compute(cr, uid, reference_currency_id, dest_main_currency_id, reference_amount, context=context)
 -            if (not dest_acct.currency_id) or dest_acct.currency_id.id == reference_currency_id:
 -                debit_line_vals.update(currency_id=reference_currency_id, amount_currency=reference_amount)
 -
 -        return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)]
 -
      def unlink(self, cr, uid, ids, context=None):
 -        if context is None:
 -            context = {}
 -        ctx = context.copy()
 +        context = context or {}
          for move in self.browse(cr, uid, ids, context=context):
 -            if move.state != 'draft' and not ctx.get('call_unlink', False):
 +            if move.state not in ('draft', 'cancel'):
                  raise osv.except_osv(_('User Error!'), _('You can only delete draft moves.'))
 -        return super(stock_move, self).unlink(
 -            cr, uid, ids, context=ctx)
 -
 -    # _create_lot function is not used anywhere
 -    def _create_lot(self, cr, uid, ids, product_id, prefix=False):
 -        """ Creates production lot
 -        @return: Production lot id
 -        """
 -        prodlot_obj = self.pool.get('stock.production.lot')
 -        prodlot_id = prodlot_obj.create(cr, uid, {'prefix': prefix, 'product_id': product_id})
 -        return prodlot_id
 +        return super(stock_move, self).unlink(cr, uid, ids, context=context)
  
 -    def action_scrap(self, cr, uid, ids, quantity, location_id, context=None):
 +    def action_scrap(self, cr, uid, ids, quantity, location_id, restrict_lot_id=False, restrict_partner_id=False, context=None):
          """ Move the scrap/damaged product into scrap location
          @param cr: the database cursor
          @param uid: the user id
Simple merge