feat(fusion_accounting_bank_rec): fusion-only batch action bar + reconcile model picker

batch_action_bar exposes bulk Suggest-for-selected and Auto-reconcile-selected
toolbar driven by selectedIds prop and the bank_reconciliation service.
reconcile_model_picker is a quick-pick dropdown over account.reconcile.model
records (rule_type=writeoff_button) including the Fusion AI confidence
threshold; apply path is a state-only stub pending Task 38's dedicated endpoint.

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-04-19 13:03:50 -04:00
parent 99e27cc566
commit 596ecb9e03
5 changed files with 113 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
{
'name': 'Fusion Accounting — Bank Reconciliation',
'version': '19.0.1.0.16',
'version': '19.0.1.0.17',
'category': 'Accounting/Accounting',
'sequence': 28,
'summary': 'Native V19 bank reconciliation widget with AI confidence scoring + behavioural learning.',
@@ -87,6 +87,11 @@ Built by Nexa Systems Inc.
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/ai_suggestion/ai_alternatives_panel.xml',
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/ai_suggestion/ai_reasoning_tooltip.js',
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/ai_suggestion/ai_reasoning_tooltip.xml',
# Fusion-only (Task 35) — batch action bar + reconcile model picker
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/batch_action_bar/batch_action_bar.js',
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/batch_action_bar/batch_action_bar.xml',
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/reconcile_model_picker/reconcile_model_picker.js',
'fusion_accounting_bank_rec/static/src/components/bank_reconciliation/reconcile_model_picker/reconcile_model_picker.xml',
],
},
'installable': True,

View File

@@ -0,0 +1,37 @@
/** @odoo-module **/
import { Component } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class BatchActionBar extends Component {
static template = "fusion_accounting_bank_rec.BatchActionBar";
static props = {
selectedIds: { type: Array, optional: true },
};
setup() {
this.bankRec = useService("fusion_bank_reconciliation");
}
get hasSelection() {
return this.props.selectedIds && this.props.selectedIds.length > 0;
}
get selectionCount() {
return this.props.selectedIds ? this.props.selectedIds.length : 0;
}
async onAutoReconcile() {
if (!this.hasSelection) {
return;
}
await this.bankRec.bulkReconcile(this.props.selectedIds, "auto");
}
async onSuggestForSelected() {
if (!this.hasSelection) {
return;
}
await this.bankRec.suggestMatches(this.props.selectedIds, 3);
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="fusion_accounting_bank_rec.BatchActionBar">
<div class="o_fusion_batch_action_bar d-flex"
style="gap: 0.5rem; padding: 0.75rem; background: #f3f4f6; border-radius: 0.375rem;">
<span class="text-muted">
<t t-esc="selectionCount"/> selected
</span>
<button class="btn_fusion" t-att-disabled="!hasSelection" t-on-click="onSuggestForSelected">
Suggest for selected
</button>
<button class="btn_fusion btn_fusion_primary" t-att-disabled="!hasSelection" t-on-click="onAutoReconcile">
Auto-reconcile selected
</button>
</div>
</t>
</templates>

View File

@@ -0,0 +1,39 @@
/** @odoo-module **/
import { Component, onWillStart, useState } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class ReconcileModelPicker extends Component {
static template = "fusion_accounting_bank_rec.ReconcileModelPicker";
static props = {
statementLineId: { type: Number, optional: true },
};
setup() {
this.orm = useService("orm");
this.bankRec = useService("fusion_bank_reconciliation");
this.state = useState({ models: [], selected: null });
onWillStart(async () => {
const models = await this.orm.searchRead(
"account.reconcile.model",
[["rule_type", "=", "writeoff_button"]],
["id", "name", "fusion_ai_confidence_threshold"],
{ limit: 20 }
);
this.state.models = models;
});
}
onChange(ev) {
const value = parseInt(ev.target.value, 10);
if (Number.isFinite(value)) {
this.onApplyModel(value);
}
}
async onApplyModel(modelId) {
// Phase 1 placeholder: TODO route through dedicated endpoint when Task 38 lands
this.state.selected = modelId;
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="fusion_accounting_bank_rec.ReconcileModelPicker">
<div class="o_fusion_reconcile_model_picker">
<select class="form-select" style="max-width: 240px;"
t-on-change="onChange">
<option value="">— Apply reconcile model —</option>
<option t-foreach="state.models" t-as="m" t-key="m.id" t-att-value="m.id">
<t t-esc="m.name"/>
</option>
</select>
</div>
</t>
</templates>