feat(fusion_accounting_assets): ai_useful_life_panel + anomaly_strip components
Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
'name': 'Fusion Accounting Assets',
|
'name': 'Fusion Accounting Assets',
|
||||||
'version': '19.0.1.0.25',
|
'version': '19.0.1.0.26',
|
||||||
'category': 'Accounting/Accounting',
|
'category': 'Accounting/Accounting',
|
||||||
'summary': 'AI-augmented asset management with depreciation schedules.',
|
'summary': 'AI-augmented asset management with depreciation schedules.',
|
||||||
'description': """
|
'description': """
|
||||||
@@ -52,6 +52,10 @@ menu hides; the engine + AI tools remain available for the chat.
|
|||||||
'fusion_accounting_assets/static/src/components/depreciation_board/depreciation_board.xml',
|
'fusion_accounting_assets/static/src/components/depreciation_board/depreciation_board.xml',
|
||||||
'fusion_accounting_assets/static/src/components/disposal_dialog/disposal_dialog.js',
|
'fusion_accounting_assets/static/src/components/disposal_dialog/disposal_dialog.js',
|
||||||
'fusion_accounting_assets/static/src/components/disposal_dialog/disposal_dialog.xml',
|
'fusion_accounting_assets/static/src/components/disposal_dialog/disposal_dialog.xml',
|
||||||
|
'fusion_accounting_assets/static/src/components/ai_useful_life_panel/ai_useful_life_panel.js',
|
||||||
|
'fusion_accounting_assets/static/src/components/ai_useful_life_panel/ai_useful_life_panel.xml',
|
||||||
|
'fusion_accounting_assets/static/src/components/anomaly_strip/anomaly_strip.js',
|
||||||
|
'fusion_accounting_assets/static/src/components/anomaly_strip/anomaly_strip.xml',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { Component, useState } from "@odoo/owl";
|
||||||
|
import { useService } from "@web/core/utils/hooks";
|
||||||
|
|
||||||
|
export class AiUsefulLifePanel extends Component {
|
||||||
|
static template = "fusion_accounting_assets.AiUsefulLifePanel";
|
||||||
|
static props = {
|
||||||
|
description: { type: String, optional: true },
|
||||||
|
amount: { type: Number, optional: true },
|
||||||
|
onSelect: { type: Function, optional: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.assets = useService("fusion_assets");
|
||||||
|
this.state = useState({
|
||||||
|
suggestion: null,
|
||||||
|
isLoading: false,
|
||||||
|
descInput: this.props.description || '',
|
||||||
|
amountInput: this.props.amount || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async onSuggest() {
|
||||||
|
this.state.isLoading = true;
|
||||||
|
try {
|
||||||
|
this.state.suggestion = await this.assets.suggestUsefulLife(
|
||||||
|
this.state.descInput,
|
||||||
|
parseFloat(this.state.amountInput) || null,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.state.isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUseSuggestion() {
|
||||||
|
if (this.state.suggestion && this.props.onSelect) {
|
||||||
|
this.props.onSelect(this.state.suggestion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="fusion_accounting_assets.AiUsefulLifePanel">
|
||||||
|
<div style="background: white; padding: 1rem; border: 1px solid #e5e7eb; border-radius: 0.5rem;">
|
||||||
|
<h5>AI Suggest Useful Life</h5>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label>Description</label>
|
||||||
|
<input class="form-control" t-att-value="state.descInput"
|
||||||
|
t-on-input="(ev) => state.descInput = ev.target.value"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label>Amount</label>
|
||||||
|
<input type="number" class="form-control" t-att-value="state.amountInput"
|
||||||
|
t-on-input="(ev) => state.amountInput = ev.target.value"/>
|
||||||
|
</div>
|
||||||
|
<button class="btn_asset primary" t-on-click="onSuggest"
|
||||||
|
t-att-disabled="state.isLoading">
|
||||||
|
<t t-if="state.isLoading">Asking AI...</t>
|
||||||
|
<t t-else="">Suggest</t>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div t-if="state.suggestion" class="mt-3 p-2"
|
||||||
|
style="background: #eff6ff; border-radius: 0.25rem;">
|
||||||
|
<div><strong>Suggested life:</strong> <t t-esc="state.suggestion.useful_life_years"/> years</div>
|
||||||
|
<div><strong>Method:</strong> <t t-esc="state.suggestion.depreciation_method"/></div>
|
||||||
|
<div class="text-muted small">
|
||||||
|
<em><t t-esc="state.suggestion.rationale"/></em>
|
||||||
|
(confidence: <t t-esc="(state.suggestion.confidence * 100).toFixed(0)"/>%)
|
||||||
|
</div>
|
||||||
|
<button class="btn_asset mt-2" t-if="props.onSelect" t-on-click="onUseSuggestion">
|
||||||
|
Use This
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { Component } from "@odoo/owl";
|
||||||
|
|
||||||
|
export class AnomalyStrip extends Component {
|
||||||
|
static template = "fusion_accounting_assets.AnomalyStrip";
|
||||||
|
static props = {
|
||||||
|
anomaly: { type: Object },
|
||||||
|
};
|
||||||
|
|
||||||
|
formatNumber(n) {
|
||||||
|
if (n === null || n === undefined) return "";
|
||||||
|
return new Intl.NumberFormat(undefined, {
|
||||||
|
minimumFractionDigits: 0, maximumFractionDigits: 1,
|
||||||
|
}).format(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="fusion_accounting_assets.AnomalyStrip">
|
||||||
|
<div class="o_fusion_anomaly_strip" t-att-data-severity="props.anomaly.severity">
|
||||||
|
<strong>
|
||||||
|
<t t-esc="props.anomaly.asset_name || 'Asset'"/>
|
||||||
|
</strong>
|
||||||
|
<span class="ms-2">
|
||||||
|
<t t-esc="props.anomaly.anomaly_type.replace('_', ' ')"/>:
|
||||||
|
<t t-esc="formatNumber(props.anomaly.variance_pct)"/>%
|
||||||
|
</span>
|
||||||
|
<span class="ms-3 text-muted">
|
||||||
|
<t t-esc="props.anomaly.detail"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
Reference in New Issue
Block a user