Impacted versions: 10.0

Steps to reproduce: Create Sale Order (line in step 1 product qty = 100, in step 2 line product qty = 1000) with two or more purchase procurements  and Cancel it.

Current behavior:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 640, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 677, in dispatch
    result = self._call_function(**self.params)
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 333, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/odoo/service/model.py", line 101, in wrapper
    return f(dbname, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 326, in checked_call
    result = self.endpoint(*a, **kw)
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 935, in __call__
    return self.method(*args, **kw)
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 506, in response_wrap
    response = f(*args, **kw)
  File "/usr/lib/python2.7/dist-packages/odoo/addons/web/controllers/main.py", line 889, in call_button
    action = self._call_kw(model, method, args, {})
  File "/usr/lib/python2.7/dist-packages/odoo/addons/web/controllers/main.py", line 877, in _call_kw
    return call_kw(request.env[model], method, args, kwargs)
  File "/usr/lib/python2.7/dist-packages/odoo/api.py", line 689, in call_kw
    return call_kw_multi(method, model, args, kwargs)
  File "/usr/lib/python2.7/dist-packages/odoo/api.py", line 680, in call_kw_multi
    result = method(recs, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/odoo/addons/sale_stock/models/sale_order.py", line 66, in action_cancel
    self.mapped('order_line').mapped('procurement_ids').cancel()
  File "/usr/lib/python2.7/dist-packages/odoo/addons/stock/models/procurement.py", line 85, in cancel
    propagated_procurements = self.filtered(lambda order: order.state != 'done').propagate_cancels()
  File "/usr/lib/python2.7/dist-packages/odoo/addons/purchase/models/purchase.py", line 898, in propagate_cancels
    price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, procurement.purchase_line_id.product_id.supplier_taxes_id, procurement.purchase_line_id.taxes_id, self.company_id) if seller else 0.0
  File "/usr/lib/python2.7/dist-packages/odoo/fields.py", line 904, in __get__
    record.ensure_one()
  File "/usr/lib/python2.7/dist-packages/odoo/models.py", line 4823, in ensure_one
    raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: procurement.order(37, 38)

Expected behavior:

Order Canceled

Comment:

Error exist in line:

 price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, procurement.purchase_line_id.product_id.supplier_taxes_id, procurement.purchase_line_id.taxes_id, self.company_id) if seller else 0.0

the problem is with value: self.company_id

should be: procurement.company_id





addons/purchase/models/purchase.py
currency_id = fields.Many2one('res.currency', 'Currency', required=True, states=READONLY_STATES,\
        default=lambda self: self.env.user.company_id.currency_id.id)
    state = fields.Selection([
        ('draft', 'RFQ'),
        ('sent', 'RFQ Sent'),
        ('to approve', 'To Approve'),
        ('purchase', 'Purchase Order'),
        ('done', 'Locked'),
        ('cancel', 'Cancelled')
        ], string='Status', readonly=True, index=True, copy=False, default='draft', track_visibility='onchange')
    order_line = fields.One2many('purchase.order.line', 'order_id', string='Order Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True)
    notes = fields.Text('Terms and Conditions')
    invoice_count = fields.Integer(compute="_compute_invoice", string='# of Bills', copy=False, default=0)
    invoice_ids = fields.Many2many('account.invoice', compute="_compute_invoice", string='Bills', copy=False)
    invoice_status = fields.Selection([
        ('no', 'Nothing to Bill'),
        ('to invoice', 'Waiting Bills'),
        ('invoiced', 'Bills Received'),
        ], string='Billing Status', compute='_get_invoiced', store=True, readonly=True, copy=False, default='no')
    picking_count = fields.Integer(compute='_compute_picking', string='Receptions', default=0)
    picking_ids = fields.Many2many('stock.picking', compute='_compute_picking', string='Receptions', copy=False)
    
    date_planned = fields.Datetime(string='Scheduled Date', compute='_compute_date_planned', store=True, index=True)
    amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all', track_visibility='always')
    amount_tax = fields.Monetary(string='Taxes', store=True, readonly=True, compute='_amount_all')
    amount_total = fields.Monetary(string='Total', store=True, readonly=True, compute='_amount_all')
    fiscal_position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position', oldname='fiscal_position')
    payment_term_id = fields.Many2one('account.payment.term', 'Payment Terms')
    incoterm_id = fields.Many2one('stock.incoterms', 'Incoterm', states={'done': [('readonly', True)]}, help="International Commercial Terms are a series of predefined commercial terms used in international transactions.")
    product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product')