feat(fusion_accounting_bank_rec): mirror Enterprise OWL batch 3 (dialog components)
Mirrors 2 OWL components (3 files each) from account_accountant for Phase 1 structural parity: - bankrec_form_dialog/ (full-form dialog for advanced editing, including BankRecEditLineFormController with the To-Review hotkey button) - search_dialog/ (BankRecSelectCreateDialog for finding additional matches, plus the bank_rec_dialog_list view registration) Renames applied per spec. Notes: - View registry IDs prefixed: `fusion_bankrec_edit_line`, `fusion_bank_rec_dialog_list`. - Button template renamed `accountant.BankRecFormDialog.buttons` -> `fusion_accounting_bank_rec.BankRecFormDialog.buttons`. Manifest version bumped to 19.0.1.0.14. Module upgrade succeeds, 134 logical tests still pass. Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
'name': 'Fusion Accounting — Bank Reconciliation',
|
'name': 'Fusion Accounting — Bank Reconciliation',
|
||||||
'version': '19.0.1.0.13',
|
'version': '19.0.1.0.14',
|
||||||
'category': 'Accounting/Accounting',
|
'category': 'Accounting/Accounting',
|
||||||
'sequence': 28,
|
'sequence': 28,
|
||||||
'summary': 'Native V19 bank reconciliation widget with AI confidence scoring + behavioural learning.',
|
'summary': 'Native V19 bank reconciliation widget with AI confidence scoring + behavioural learning.',
|
||||||
@@ -68,6 +68,13 @@ Built by Nexa Systems Inc.
|
|||||||
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/list_view/list_view_many2one_multi_edit.xml',
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/list_view/list_view_many2one_multi_edit.xml',
|
||||||
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/apply_amount/apply_amount.js',
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/apply_amount/apply_amount.js',
|
||||||
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/apply_amount/apply_amount.xml',
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/apply_amount/apply_amount.xml',
|
||||||
|
# Batch 3 (Task 32) — dialog components
|
||||||
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/bankrec_form_dialog/bankrec_form_dialog.js',
|
||||||
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/bankrec_form_dialog/bankrec_form_dialog.xml',
|
||||||
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/search_dialog/search_dialog.js',
|
||||||
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/search_dialog/search_dialog.xml',
|
||||||
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/search_dialog/search_dialog_list.js',
|
||||||
|
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/search_dialog/search_dialog_list.xml',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrored from
|
||||||
|
* `account_accountant/.../bankrec_form_dialog/bankrec_form_dialog.js`.
|
||||||
|
* Phase 1 structural parity.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FormController } from "@web/views/form/form_controller";
|
||||||
|
import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog";
|
||||||
|
import { formView } from "@web/views/form/form_view";
|
||||||
|
import { onWillStart } from "@odoo/owl";
|
||||||
|
import { registry } from "@web/core/registry";
|
||||||
|
import { user } from "@web/core/user";
|
||||||
|
|
||||||
|
export class BankRecFormDialog extends FormViewDialog {
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
Object.assign(this.viewProps, {
|
||||||
|
buttonTemplate: "fusion_accounting_bank_rec.BankRecFormDialog.buttons",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BankRecEditLineFormController extends FormController {
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
this.isReviewed = this.props.context.is_reviewed;
|
||||||
|
onWillStart(async () => {
|
||||||
|
this.userCanReview = await user.hasGroup("account.group_account_user");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async toReviewButtonClicked(params = {}) {
|
||||||
|
await this.orm.call("account.move", "set_moves_checked", [
|
||||||
|
this.model.root.data.move_id.id,
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
return this.saveButtonClicked(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bankRecEditLineFormController = {
|
||||||
|
...formView,
|
||||||
|
Controller: BankRecEditLineFormController,
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.category("views").add("fusion_bankrec_edit_line", bankRecEditLineFormController);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="fusion_accounting_bank_rec.BankRecFormDialog.buttons" t-inherit="web.FormViewDialog.ToOne.buttons" t-inherit-mode="primary">
|
||||||
|
<xpath expr="//button[hasclass('o_form_button_save')]" position="after">
|
||||||
|
<button
|
||||||
|
t-if="userCanReview and this.isReviewed"
|
||||||
|
class="btn btn-info"
|
||||||
|
t-on-click.stop="() => this.toReviewButtonClicked({closable: true})"
|
||||||
|
data-hotkey="q">
|
||||||
|
<span>To Review</span>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrored from
|
||||||
|
* `account_accountant/.../search_dialog/search_dialog.js`.
|
||||||
|
* Phase 1 structural parity.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog";
|
||||||
|
import { formatMonetary } from "@web/views/fields/formatters";
|
||||||
|
import { useService } from "@web/core/utils/hooks";
|
||||||
|
|
||||||
|
const { DateTime } = luxon;
|
||||||
|
|
||||||
|
export class BankRecSelectCreateDialog extends SelectCreateDialog {
|
||||||
|
static template = "fusion_accounting_bank_rec.BankRecSelectCreateDialog";
|
||||||
|
static props = {
|
||||||
|
...SelectCreateDialog.props,
|
||||||
|
suspenseAccountLine: Object,
|
||||||
|
reference: String,
|
||||||
|
date: DateTime,
|
||||||
|
size: { type: String, optional: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
...SelectCreateDialog.defaultProps,
|
||||||
|
size: "lg",
|
||||||
|
};
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
this.orm = useService("orm");
|
||||||
|
this.ui = useService("ui");
|
||||||
|
this.state.remainingAmount = this.suspenseAccountLine.amount_currency;
|
||||||
|
this.state.hideRemainingAmount = false;
|
||||||
|
|
||||||
|
this.baseViewProps.onSelectionChanged = (resIds, selectedLines) => {
|
||||||
|
this.state.resIds = resIds;
|
||||||
|
this.changeInSelectedMoveLine(selectedLines);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeInSelectedMoveLine(selectedLines) {
|
||||||
|
if (!selectedLines?.length) {
|
||||||
|
this.state.remainingAmount = this.suspenseAccountLine.amount_currency;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedLinesSum = 0;
|
||||||
|
this.state.hideRemainingAmount = false;
|
||||||
|
if (
|
||||||
|
this.suspenseAccountLine.currency_id.id !==
|
||||||
|
this.suspenseAccountLine.company_currency_id.id
|
||||||
|
) {
|
||||||
|
const selectedLineCurrencies = selectedLines.map((line) => line.currency_id);
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedLineCurrencies.length !== 1 ||
|
||||||
|
(selectedLineCurrencies.length === 1 &&
|
||||||
|
selectedLineCurrencies[0] !== this.suspenseAccountLine.currency_id.id)
|
||||||
|
) {
|
||||||
|
this.state.hideRemainingAmount = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
selectedLinesSum = selectedLines.reduce((sum, line) => {
|
||||||
|
return sum + line.amount_residual_currency;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedLinesSum = selectedLines.reduce((sum, line) => {
|
||||||
|
return sum + line.amount_residual;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
this.state.remainingAmount = this.suspenseAccountLine.amount_currency + selectedLinesSum;
|
||||||
|
}
|
||||||
|
|
||||||
|
get suspenseAccountLine() {
|
||||||
|
return this.props?.suspenseAccountLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
get remainingAmountFormatted() {
|
||||||
|
return formatMonetary(this.state.remainingAmount, {
|
||||||
|
currencyId: this.suspenseAccountLine.currency_id.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get formattedStatementLineDate() {
|
||||||
|
return this.props.date?.toLocaleString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="fusion_accounting_bank_rec.BankRecSelectCreateDialog" t-inherit="web.SelectCreateDialog" t-inherit-mode="primary">
|
||||||
|
<xpath expr="//Dialog" position="attributes">
|
||||||
|
<attribute name="size">props.size</attribute>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//button[hasclass('o_form_button_cancel')]" position="after">
|
||||||
|
<div t-if="!this.ui.isSmall" class="d-flex align-items-center flex-grow-1 flex-shrink-1 flex-basis-0 gap-2 min-w-0 justify-content-between" name="bank_reconciliation_info">
|
||||||
|
<span t-esc="formattedStatementLineDate"/>
|
||||||
|
<div class="text-truncate" t-esc="props.reference"/>
|
||||||
|
<div class="text-nowrap text-end" name="remaining_amount">
|
||||||
|
<span class="text-muted">Balance: </span>
|
||||||
|
<t t-if="!this.state.hideRemainingAmount" t-esc="remainingAmountFormatted"/>
|
||||||
|
<t t-else=""> / </t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrored from
|
||||||
|
* `account_accountant/.../search_dialog/search_dialog_list.js`.
|
||||||
|
* Phase 1 structural parity.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ListController } from "@web/views/list/list_controller";
|
||||||
|
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||||
|
import { listView } from "@web/views/list/list_view";
|
||||||
|
import { registry } from "@web/core/registry";
|
||||||
|
import { useService } from "@web/core/utils/hooks";
|
||||||
|
|
||||||
|
export class BankRecReconcileDialogListController extends ListController {
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
this.orm = useService("orm");
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSelectionChanged() {
|
||||||
|
const resIds = await this.model.root.getResIds(true);
|
||||||
|
if (!resIds.length) {
|
||||||
|
this.props.onSelectionChanged(resIds, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedLines;
|
||||||
|
// When being in the list view with more elements than the limit and
|
||||||
|
// doing a select all, the user can select more elements than the
|
||||||
|
// limit. In this case the isDomainSelected is True.
|
||||||
|
if (this.isDomainSelected) {
|
||||||
|
const { resModel, context } = this.model.root._config;
|
||||||
|
selectedLines = await this.orm.read(
|
||||||
|
resModel,
|
||||||
|
resIds,
|
||||||
|
["amount_residual", "amount_residual_currency", "currency_id"],
|
||||||
|
{ context }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
selectedLines = Object.values(this.model.root.records)
|
||||||
|
.filter((record) => resIds.includes(record._config.resId))
|
||||||
|
.map((record) => {
|
||||||
|
const data = record.data;
|
||||||
|
return {
|
||||||
|
amount_residual: data.amount_residual,
|
||||||
|
amount_residual_currency: data.amount_residual_currency,
|
||||||
|
currency_id: data.currency_id.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.props.onSelectionChanged(resIds, selectedLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BankRecReconcileDialogListRenderer extends ListRenderer {
|
||||||
|
static template = "fusion_accounting_bank_rec.BankRecReconcileDialogListRenderer";
|
||||||
|
static recordRowTemplate =
|
||||||
|
"fusion_accounting_bank_rec.BankRecReconcileDialogListRenderer.RecordRow";
|
||||||
|
|
||||||
|
async openMoveView(record) {
|
||||||
|
this.env.services.action.doAction({
|
||||||
|
type: "ir.actions.act_window",
|
||||||
|
res_model: "account.move",
|
||||||
|
res_id: record.data.move_id.id,
|
||||||
|
views: [[false, "form"]],
|
||||||
|
target: "current",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bankRecReconcileDialogListRenderer = {
|
||||||
|
...listView,
|
||||||
|
Renderer: BankRecReconcileDialogListRenderer,
|
||||||
|
Controller: BankRecReconcileDialogListController,
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.category("views").add("fusion_bank_rec_dialog_list", bankRecReconcileDialogListRenderer);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="fusion_accounting_bank_rec.BankRecReconcileDialogListRenderer" t-inherit="web.ListRenderer" t-inherit-mode="primary">
|
||||||
|
<xpath expr="//th[@t-if='hasOpenFormViewColumn']" position="replace">
|
||||||
|
<th class="o_list_open_form_view w-print-0 p-print-0"/>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="fusion_accounting_bank_rec.BankRecReconcileDialogListRenderer.RecordRow" t-inherit="web.ListRenderer.RecordRow" t-inherit-mode="primary">
|
||||||
|
<xpath expr="//t[@t-if='hasOpenFormViewColumn']" position="replace">
|
||||||
|
<td class="o_list_record_open_form_view w-print-0 p-print-0 text-center"
|
||||||
|
t-custom-click.stop="() => this.openMoveView(record)"
|
||||||
|
>
|
||||||
|
<button class="btn btn-link align-top text-end"
|
||||||
|
name="Open in form view"
|
||||||
|
aria-label="Open in form view"
|
||||||
|
>View</button>
|
||||||
|
</td>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
Reference in New Issue
Block a user