[FIX] account: block deletion of used tax groups#245243
[FIX] account: block deletion of used tax groups#245243smjo-odoo wants to merge 2 commits intoodoo:saas-18.3from
Conversation
f751df1 to
ba2d5cf
Compare
ba2d5cf to
cf820cc
Compare
| return '' | ||
| return html2plaintext(self.description) | ||
|
|
||
| @api.ondelete(at_uninstall=False) |
There was a problem hiding this comment.
We have an ondelete="restrict" on tax_line_id. Would not an ondelete="restrict" on tax_ids (on account_move_line) do the same trick? Why this choice to pass through is_used?
There was a problem hiding this comment.
Would not this avoid the enterprise PR as well?
There was a problem hiding this comment.
Why this choice to pass through is_used?
@jco-odoo, the reason for using is_used is that the tax group might be used in different modules, like sale, PoS, purchase, etc. We have the _hook_compute_is_used method that computes the IDs of taxes used in other modules and returns a set of IDs of taxes from that input set that are used in transactions.
If we use ondelete='restrict' on tax_ids in account.move.line, It will only prevent the deletion in invoicing module and we would still be able to delete tax groups used in other modules.
If the is_used field can be used for preventing the modification of taxes. Why should we not use the same field to prevent the deletion of used tax groups ?
There was a problem hiding this comment.
Would not this avoid the enterprise PR as well?
The enterprise PR would still be needed because in that particular test case, the goal is to create a bill with "no tax", and not to delete/unlink the tax. This is the reason why we used Command.clear() instead of unlink() on the tax_ids.
There was a problem hiding this comment.
Hello @jco-odoo Do we have any update on this PR ?
There was a problem hiding this comment.
@smjo-odoo I had not checked that far and that is_used seems to be in handy for it. I see that the ondelete='restrict' is also not on some other models, where we could have simply put it. (hr.expense, purchase.order.line, sale.order.line) But e.g. the method is not inherited for sale.order.line... Also, such changes are huge in 18.3 for still solving a corner case (although it is not 18 stable), but it can be a good idea to enforce it this way.
I see @aboo-odoo worked a lot on the is_used, so maybe he can already tell why it is not on sale order line or if it can be a good approach for it.
There was a problem hiding this comment.
The initial idea behind is_used was to check if a tax was used to restrict/track modification of used taxes (restriction has been removed as per FP request). I don't recall, nor see any reason anywhere on why the is_used is not applied to SOs and SOLs - maybe this was said during a discussion with TSB but not written anywhere.
However, it seems that here you are making it so that any taxes (not just tax groups) cannot be deleted after it as been used. If this is the case, I'm pretty sure this should be further discussed and validated.
edit: is_used is quite heavy to compute but I think it's okay to use it to check whether or not a tax should or not be deleted (again - if it has been functionally agreed upon)
There was a problem hiding this comment.
@jco-odoo Should we continue with the is_used approach ?
There was a problem hiding this comment.
However, it seems that here you are making it so that any taxes (not just tax groups) cannot be deleted after it as been used. If this is the case, I'm pretty sure this should be further discussed and validated.
@aboo-odoo Would it be fine to use context(force_delete=True) to allow a forced deletion?
This way, we could bypass the blocking error when intentionally force-deleting a group of taxes, while still keeping the restriction in normal cases.
There was a problem hiding this comment.
edit:
is_usedis quite heavy to compute but I think it's okay to use it to check whether or not a tax should or not be deleted (again - if it has been functionally agreed upon)
Especially since the SQL logic is flawed.
SELECT id
FROM account_tax
WHERE EXISTS(
SELECT 1
FROM account_move_line_account_tax_rel AS line
WHERE account_tax_id IN %s
AND account_tax.id = line.account_tax_id
)Should be
SELECT id
FROM account_tax
WHERE EXISTS(
SELECT 1
FROM account_move_line_account_tax_rel AS line
WHERE account_tax.id = line.account_tax_id
) AND id IN %sSame for all the similar queries of course.
william-andre
left a comment
There was a problem hiding this comment.
Using is_used is fine for me; it makes sense to protect the taxes against edition and deletion the same way.
| return '' | ||
| return html2plaintext(self.description) | ||
|
|
||
| @api.ondelete(at_uninstall=False) |
There was a problem hiding this comment.
edit:
is_usedis quite heavy to compute but I think it's okay to use it to check whether or not a tax should or not be deleted (again - if it has been functionally agreed upon)
Especially since the SQL logic is flawed.
SELECT id
FROM account_tax
WHERE EXISTS(
SELECT 1
FROM account_move_line_account_tax_rel AS line
WHERE account_tax_id IN %s
AND account_tax.id = line.account_tax_id
)Should be
SELECT id
FROM account_tax
WHERE EXISTS(
SELECT 1
FROM account_move_line_account_tax_rel AS line
WHERE account_tax.id = line.account_tax_id
) AND id IN %sSame for all the similar queries of course.
|
@william-andre @smjo-odoo I think the _is_used makes sense too, but I want to confirm with PO as it changes the behaviour in stable. |
cf820cc to
bad65bb
Compare
| tuple(self.ids), | ||
| WHERE account_tax.id = line.account_tax_id | ||
| ) AND id IN %s | ||
| """, tuple(self.ids), |
There was a problem hiding this comment.
The simplest would be to not use _is_used but only put this one in a separate method and call it. In principle, the tax is mainly important for accounting (in principle you would still have the tax tags but it is more difficult to track where they come from then) and if it gets deleted on expense, ... it is less of a problem. (discussed with PO) If we would keep the _is_used, we would need a very precise error message to tell where the user would need to go to delete the tax. Doing this is also better in stable that the ondelete restrict as we would need an update of the module for that. (so only the tax_ids on account.move.line but use this query instead of the ondelete restrict)
There was a problem hiding this comment.
You can simply add smart buttons on the tax to jump to the different related models.
In the meantime, this fix is alright; even if it could be improved.
There was a problem hiding this comment.
That would be a lot of buttons just to tell where it is used...
There was a problem hiding this comment.
You're right, but I don't think that it matters that much...
| tuple(self.ids), | ||
| WHERE account_tax.id = line.account_tax_id | ||
| ) AND id IN %s | ||
| """, tuple(self.ids), |
There was a problem hiding this comment.
You can simply add smart buttons on the tax to jump to the different related models.
In the meantime, this fix is alright; even if it could be improved.
Before this commit: - Group of taxes can be deleted by a user, even if they are used. After this commit: - If a user tries to delete a group of taxes in use, a validation error is raised. - Fixed a test case in POS and deactivated the tax instead of deleting the tax. Task: 5472834
Problem: - Tax usage queries filter inside subqueries (`account_tax_id IN %s`), causing unnecessary scans of the account_tax table. Solution: - moved `id IN %s` to outer query to restrict records earlier and reduce unnecessary scanning of account_tax table. task-5472834
bad65bb to
f05ac78
Compare
|
@robodoo r+ rebase-ff |
|
Merge method set to rebase and fast-forward. |

Before this PR:
After this PR:
If a user tries to delete a group of taxes in use, a validation
error is raised.
Fixed a test case in POS and deactivated the tax instead of
deleting the tax.
Related PR: https://github.com/odoo/enterprise/pull/105174
task-5472834