Odoo调用action_post()报Expected singleton: res.company()错误解决
Odoo调用account.move.action_post()时触发Expected singleton: res.company()错误
问题场景
开发API用于搜索并冲销account.move模型条目,已成功找到目标条目并通过refund_moves()完成冲销,但调用action_post()确认冲销条目时,抛出Expected singleton: res.company()错误。此前在sale.order/account.move等其他模型中调用action_post()均正常。
相关代码
API路由及核心逻辑
@http.route('/update_invoice', website="false", auth='custom_auth', type='json', methods=['POST']) def update_invoice(self, **kwargs): # 搜索目标条目 invoice = request.env['account.move'].sudo().search([ ('matter_id','=',matterID), ('account_id','=',accountID), ('move_type','=','out_invoice'), ('company_id','=',creditor.id) ]) if invoice: # 创建冲销记录 move_reversal = request.env['account.move.reversal'].with_context( active_model="account.move", active_ids=invoice.id ).sudo().create({ 'date': intakeDate, 'reason': 'Balance Adjustment', 'journal_id': invoice.journal_id.id, }) # 执行冲销 move_reversal.refund_moves() # 搜索生成的冲销条目 refundInvoice = request.env['account.move'].sudo().search([ ('name','=',"/"), ('company_id','=',creditor.id), ('move_type','=','out_refund') ]) if refundInvoice: _logger.info("Refund Invoice Found") # 此处触发错误 refundInvoice.action_post()
自定义授权逻辑
@classmethod def _auth_method_custom_auth(cls): access_token = request.httprequest.headers.get('Authorization') _logger.info(access_token) if not access_token: _logger.info('Access Token Missing') raise BadRequest('Missing Access Token') if access_token.startswith('Bearer '): access_token = access_token[7:] _logger.info(access_token) user_id = request.env["res.users.apikeys"]._check_credentials(scope='odoo.restapi', key=access_token) if not user_id: _logger.info('No user with api key found') raise BadRequest('Access token Invalid') request.update_env(user=user_id)
报错栈
Traceback (most recent call last): File "/home/odoo/src/odoo/odoo/models.py", line 5841, in ensure_one _id, = self._ids ValueError: not enough values to unpack (expected 1, got 0) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/odoo/src/odoo/odoo/http.py", line 2189, in __call__ response = request._serve_db() File "/home/odoo/src/odoo/odoo/http.py", line 1765, in _serve_db return service_model.retrying(self._serve_ir_http, self.env) File "/home/odoo/src/odoo/odoo/service/model.py", line 133, in retrying result = func() File "/home/odoo/src/odoo/odoo/http.py", line 1792, in _serve_ir_http response = self.dispatcher.dispatch(rule.endpoint, args) File "/home/odoo/src/odoo/odoo/http.py", line 1996, in dispatch result = self.request.registry['ir.http']._dispatch(endpoint) File "/home/odoo/src/odoo/addons/website/models/ir_http.py", line 235, in _dispatch response = super()._dispatch(endpoint) File "/home/odoo/src/odoo/odoo/addons/base/models/ir_http.py", line 222, in _dispatch result = endpoint(**request.params) File "/home/odoo/src/odoo/odoo/http.py", line 722, in route_wrapper result = endpoint(self, *args, **params_ok) File "/home/odoo/src/user/account_ext/controllers/main.py", line 411, in update_invoice refundInvoice.action_post() File "/home/odoo/src/odoo/addons/sale/models/account_move.py", line 63, in action_post res = super(AccountMove, self).action_post() File "/home/odoo/src/enterprise/account_accountant/models/account_move.py", line 76, in action_post res = super().action_post() File "/home/odoo/src/odoo/addons/account/models/account_move.py", line 4072, in action_post other_moves._post(soft=False) File "/home/odoo/src/enterprise/sale_subscription/models/account_move.py", line 13, in _post posted_moves = super()._post(soft=soft) File "/home/odoo/src/enterprise/account_asset/models/account_move.py", line 109, in _post posted = super()._post(soft) File "/home/odoo/src/odoo/addons/sale/models/account_move.py", line 99, in _post posted = super()._post(soft) File "/home/odoo/src/enterprise/account_reports/models/account_move.py", line 48, in _post return super()._post(soft) File "/home/odoo/src/enterprise/account_avatax/models/account_move.py", line 15, in _post res = super()._post(soft=soft) File "/home/odoo/src/enterprise/account_invoice_extract/models/account_invoice.py", line 262, in _post posted = super()._post(soft) File "/home/odoo/src/enterprise/account_inter_company_rules/models/account_move.py", line 14, in _post posted = super()._post(soft) File "/home/odoo/src/enterprise/account_external_tax/models/account_move.py", line 53, in _post return super()._post(soft=soft) File "/home/odoo/src/enterprise/account_accountant/models/account_move.py", line 68, in _post posted = super()._post(soft) File "/home/odoo/src/odoo/addons/account/models/account_move.py", line 3876, in _post draft_reverse_moves.reversed_entry_id._reconcile_reversed_moves(draft_reverse_moves, self._context.get('move_reverse_cancel', False)) File "/home/odoo/src/odoo/addons/account/models/account_move.py", line 3694, in _reconcile_reversed_moves lines.with_context(move_reverse_cancel=move_reverse_cancel).reconcile() File "/home/odoo/src/odoo/addons/account/models/account_move_line.py", line 2935, in reconcile return self._reconcile_plan([self]) File "/home/odoo/src/odoo/addons/account/models/account_move_line.py", line 2345, in _reconcile_plan self._reconcile_plan_with_sync(plan_list, all_amls) File "/home/odoo/src/odoo/addons/account/models/account_move_line.py", line 2492, in _reconcile_plan_with_sync exchange_diff_values = exchange_lines_to_fix._prepare_exchange_difference_move_vals( File "/home/odoo/src/odoo/addons/account/models/account_move_line.py", line 2603, in _prepare_exchange_difference_move_vals accounting_exchange_date = journal.with_context(move_date=exchange_date).accounting_date File "/home/odoo/src/odoo/odoo/fields.py", line 1207, in __get__ self.compute_value(recs) File "/home/odoo/src/odoo/odoo/fields.py", line 1389, in compute_value records._compute_field_value(self) File "/home/odoo/src/odoo/addons/mail/models/mail_thread.py", line 424, in _compute_field_value return super()._compute_field_value(field) File "/home/odoo/src/odoo/odoo/models.py", line 4867, in _compute_field_value fields.determine(field.compute, self) File "/home/odoo/src/odoo/odoo/fields.py", line 102, in determine return needle(*args) File "/home/odoo/src/odoo/addons/account/models/account_journal.py", line 366, in _compute_accounting_date journal.accounting_date = temp_move._get_accounting_date(move_date, has_tax) File "/home/odoo/src/odoo/addons/account/models/account_move.py", line 4358, in _get_accounting_date lock_dates = self._get_violated_lock_dates(invoice_date, has_tax) File "/home/odoo/src/odoo/addons/account/models/account_move.py", line 4389, in _get_violated_lock_dates return self.company_id._get_violated_lock_dates(invoice_date, has_tax) File "/home/odoo/src/odoo/addons/account/models/company.py", line 369, in _get_violated_lock_dates self.ensure_one() File "/home/odoo/src/odoo/odoo/models.py", line 5844, in ensure_one raise ValueError("Expected singleton: %s" % self) ValueError: Expected singleton: res.company()
解决方案
通过在调用action_post()时使用with_company()明确指定公司上下文,解决了该问题:
refundInvoice.with_company(invoice.company_id).action_post()
内容的提问来源于stack exchange,提问作者Brandon Huynh
相关产品推荐
相关产品推荐

