[FIX] account: fix delete invoice rights
From 16.0 any user can delete a customer invoice/vendor bill even if it creates a sequence gap. This commit updates the rights and the warning message: - The deletion confirmation message contains a warning about the sequence gap - Only a Billing Administrator/Accountant can delete customer invoices/vendor bills creating the gap - Also, if the fiduciary mode is on (`quick_edit_mode`) it should be possible to delete invoices/bills regardless of the user group task-3284218 closes odoo/odoo#128556 X-original-commit: 2249f899049ef94b14b083c8db976662b868b026 Related: odoo/enterprise#44128 Signed-off-by: William André (wan) <wan@odoo.com> Signed-off-by: Aliya Tastemirova (alta) <alta@odoo.com>
This commit is contained in:
@@ -13515,6 +13515,14 @@ msgstr ""
|
||||
msgid "This move will be posted at the accounting date: %(date)s"
|
||||
msgstr ""
|
||||
|
||||
#. module: account
|
||||
#. odoo-javascript
|
||||
#: code:addons/account/static/src/components/account_move_form/account_move_form.js:0
|
||||
#: code:addons/account/static/src/components/account_move_list/account_move_list.js:0
|
||||
#, python-format
|
||||
msgid "This operation will create a gap in the sequence."
|
||||
msgstr ""
|
||||
|
||||
#. module: account
|
||||
#. odoo-python
|
||||
#: code:addons/account/models/account_payment.py:0
|
||||
@@ -14944,6 +14952,15 @@ msgstr ""
|
||||
msgid "You cannot delete an item linked to a posted entry."
|
||||
msgstr ""
|
||||
|
||||
#. module: account
|
||||
#. odoo-python
|
||||
#: code:addons/account/models/account_move.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot delete this entry, as it has already consumed a sequence number "
|
||||
"and is not the last one in the chain. You should probably revert it instead."
|
||||
msgstr ""
|
||||
|
||||
#. module: account
|
||||
#. odoo-python
|
||||
#: code:addons/account/models/res_config_settings.py:0
|
||||
|
||||
@@ -2460,6 +2460,29 @@ class AccountMove(models.Model):
|
||||
|
||||
return res
|
||||
|
||||
def check_move_sequence_chain(self):
|
||||
return self.filtered(lambda move: move.name != '/')._is_end_of_seq_chain()
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_forbid_parts_of_chain(self):
|
||||
""" For a user with Billing/Bookkeeper rights, when the fidu mode is deactivated,
|
||||
moves with a sequence number can only be deleted if they are the last element of a chain of sequence.
|
||||
If they are not, deleting them would create a gap. If the user really wants to do this, he still can
|
||||
explicitly empty the 'name' field of the move; but we discourage that practice.
|
||||
If a user is a Billing Administrator/Accountant or if fidu mode is activated, we show a warning,
|
||||
but they can delete the moves even if it creates a sequence gap.
|
||||
"""
|
||||
if not (
|
||||
self.user_has_groups('account.group_account_manager')
|
||||
or self.company_id.quick_edit_mode
|
||||
or self._context.get('force_delete')
|
||||
or self.check_move_sequence_chain()
|
||||
):
|
||||
raise UserError(_(
|
||||
"You cannot delete this entry, as it has already consumed a sequence number and is not the last one in the chain. "
|
||||
"You should probably revert it instead."
|
||||
))
|
||||
|
||||
def unlink(self):
|
||||
self = self.with_context(skip_invoice_sync=True, dynamic_unlink=True) # no need to sync to delete everything
|
||||
self.line_ids.unlink()
|
||||
|
||||
@@ -6,6 +6,21 @@ import { Notebook } from "@web/core/notebook/notebook";
|
||||
import { formView } from "@web/views/form/form_view";
|
||||
import { FormCompiler } from "@web/views/form/form_compiler";
|
||||
import { FormRenderer } from "@web/views/form/form_renderer";
|
||||
import { FormController } from '@web/views/form/form_controller';
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
export class AccountMoveController extends FormController {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.account_move_service = useService("account_move");
|
||||
}
|
||||
|
||||
async deleteRecord() {
|
||||
if ( !await this.account_move_service.addDeletionDialog(this, this.model.root.resId)) {
|
||||
return super.deleteRecord(...arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class AccountMoveFormNotebook extends Notebook {
|
||||
async changeTabTo(page_id) {
|
||||
@@ -56,6 +71,7 @@ export const AccountMoveFormView = {
|
||||
...formView,
|
||||
Renderer: AccountMoveFormRenderer,
|
||||
Compiler: AccountMoveFormCompiler,
|
||||
Controller: AccountMoveController,
|
||||
};
|
||||
|
||||
registry.category("views").add("account_move_form", AccountMoveFormView);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
|
||||
import { escape } from "@web/core/utils/strings";
|
||||
import { markup } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
|
||||
export const accountMove = {
|
||||
dependencies: ["dialog", "orm"],
|
||||
start(env, { dialog, orm }) {
|
||||
return {
|
||||
async addDeletionDialog(component, moveIds) {
|
||||
const isMoveEndOfChain = await orm.call('account.move', 'check_move_sequence_chain', [moveIds]);
|
||||
if (!isMoveEndOfChain) {
|
||||
const message = env._t("This operation will create a gap in the sequence.");
|
||||
const confirmationDialogProps = component.deleteConfirmationDialogProps;
|
||||
confirmationDialogProps.body = markup(`<div class="text-danger">${escape(message)}</div>${escape(confirmationDialogProps.body)}`);
|
||||
dialog.add(ConfirmationDialog, confirmationDialogProps);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("services").add("account_move", accountMove);
|
||||
@@ -14,7 +14,7 @@ import { KanbanRecord } from "@web/views/kanban/kanban_record";
|
||||
import { FileUploader } from "@web/views/fields/file_handler";
|
||||
import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
|
||||
|
||||
const { Component, useState } = owl;
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
export class AccountFileUploader extends Component {
|
||||
setup() {
|
||||
@@ -135,15 +135,28 @@ AccountMoveUploadListRenderer.components = {
|
||||
AccountDropZone,
|
||||
};
|
||||
|
||||
export class AccountMoveUploadListController extends ListController {}
|
||||
AccountMoveUploadListController.components = {
|
||||
export class AccountMoveListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.account_move_service = useService("account_move");
|
||||
}
|
||||
|
||||
async onDeleteSelectedRecords() {
|
||||
const selectedResIds = await this.getSelectedResIds();
|
||||
if (!await this.account_move_service.addDeletionDialog(this, selectedResIds)) {
|
||||
return super.onDeleteSelectedRecords(...arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AccountMoveListController.components = {
|
||||
...ListController.components,
|
||||
AccountFileUploader,
|
||||
};
|
||||
|
||||
export const AccountMoveUploadListView = {
|
||||
...listView,
|
||||
Controller: AccountMoveUploadListController,
|
||||
Controller: AccountMoveListController,
|
||||
Renderer: AccountMoveUploadListRenderer,
|
||||
buttonTemplate: "account.ListView.Buttons",
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { AccountMoveFormRenderer } from "@account/components/account_move_form/account_move_form";
|
||||
import { accountMove as accountMoveService} from '@account/components/account_move_service/account_move_service';
|
||||
|
||||
QUnit.module("Views", {}, function (hooks) {
|
||||
QUnit.module('MoveFormView');
|
||||
@@ -25,6 +26,9 @@ QUnit.module("Views", {}, function (hooks) {
|
||||
};
|
||||
const { click, openView } = await start({
|
||||
serverData: { views },
|
||||
services: {
|
||||
'account_move': accountMoveService,
|
||||
}
|
||||
});
|
||||
patchWithCleanup(AccountMoveFormRenderer.prototype, {
|
||||
saveBeforeTabChange() {
|
||||
|
||||
Reference in New Issue
Block a user