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',
|
||||
'version': '19.0.1.0.13',
|
||||
'version': '19.0.1.0.14',
|
||||
'category': 'Accounting/Accounting',
|
||||
'sequence': 28,
|
||||
'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/apply_amount/apply_amount.js',
|
||||
'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,
|
||||
|
||||
@@ -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