Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sale_exception/models/sale_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ def sale_check_exception(self):

def action_confirm(self):
if self.detect_exceptions():
exception_map = {sale.id: sale.exception_ids.ids for sale in self}
self.env.cr.rollback()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @pedrobaeza said, a line alone with rollback is not the good way to do.

IMHO, the behavior is totally normal as the 'exceptions' are not technical errors but properties on sale orders that are 'anomalies' to be checked.

As I understand, as sale_loyalty module is not in the same call stack (as no dependency) as this, the coupons are applied even if super() is not called.

The solution should goes IMHO in a glue module between sale_loyalty and sale_exception modules.

Your rollback is too harsh and can have too much corner cases where changes should have been applied

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said in base_exception, you can maybe add in the glue module a constraint if there are coupons.

https://github.com/OCA/server-tools/blob/16.0/base_exception/models/base_exception.py#L100C9-L100C25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rousseldenis I believe that on a method that is supposed to confirm and order (action_confirm), if it's cancelled by any module, it should be canceled by a raise to make a rollback.

They bring the use case of sale_loyalty, but I remember other use cases with other modules that extends action_confirm.

Do you agree?

Other cursor seems to be the good direction but the change is really bigger. Would it be ok for 16? better for 18 or 19?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjscarafia @rousseldenis , @lav-adhoc and I have been doing some tests to combine a new cursor and a raise that prevents a data commit. You can preview the changes here. https://github.com/OCA/sale-workflow/compare/16.0...adhoc-dev:sale-workflow:16.0-h-89839-lav2?expand=1

While it worked, we feel we are adding too many changes in a stable version.

Although the problem occurs with coupons, it's possible that new problems might also arise with payment integrations or electronic invoicing.

So, on one hand, I'd like your opinion on the other approach (does it make sense to implement it in version 16?).

And in newer versions, we can consider a refactoring of the base_exception module in which exceptions are a new type of raise exception with the characteristics we need, and that they write the exceptions with a new cursor.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: self.env.cr.rollback() must not be used in Odoo business methods.

This rolls back the entire current database transaction, not just the changes made by sale_loyalty. Any writes from other modules (audit trails, sequences, computed fields stored during this request cycle) will also be silently lost.

Additionally, after rollback(), the ORM caches (prefetch, field cache, computed values) are stale -- they still reflect the pre-rollback state. Subsequent ORM operations (including the sale.write() below) operate on an inconsistent view, which can cause silent data corruption or MissingError exceptions.

The recommended pattern is to use a new cursor (self.pool.cursor()) to persist the exception data in a separate transaction, then raise an exception to let the ORM naturally roll back the main transaction. See: https://github.com/OCA/l10n-spain/blob/7818a061/l10n_es_aeat_sii_oca/models/sii_mixin.py#L826-L841

for sale in self:
exception_ids = exception_map.get(sale.id, [])
sale.write({"exception_ids": [(6, 0, exception_ids)]})
return self._popup_exceptions()
return super().action_confirm()

Expand Down
Loading