/** @odoo-module **/ import { registry } from "@web/core/registry"; import { reactive } from "@odoo/owl"; import { rpc } from "@web/core/network/rpc"; const ENDPOINT_BASE = "/fusion/assets"; export class AssetsService { constructor(env, services) { this.env = env; // V19: rpc is a standalone import, not a service. this.rpc = rpc; this.notification = services.notification; this.state = reactive({ assets: [], count: 0, total: 0, stateFilter: null, categoryFilter: null, isLoading: false, isProcessing: false, selectedAssetId: null, selectedDetail: null, companyId: null, limit: 50, offset: 0, anomalies: [], }); } async loadAssets(companyId = null) { this.state.companyId = companyId; this.state.isLoading = true; try { const result = await this.rpc(`${ENDPOINT_BASE}/list`, { state: this.state.stateFilter, category_id: this.state.categoryFilter, limit: this.state.limit, offset: this.state.offset, company_id: companyId, }); this.state.assets = result.assets; this.state.count = result.count; this.state.total = result.total; } finally { this.state.isLoading = false; } } async selectAsset(assetId) { this.state.selectedAssetId = assetId; this.state.selectedDetail = null; try { const result = await this.rpc(`${ENDPOINT_BASE}/get_detail`, { asset_id: assetId, }); this.state.selectedDetail = result; } catch (err) { this.notification.add(`Failed to load asset detail: ${err.message || err}`, { type: "danger" }); } } async computeSchedule(assetId, recompute = false) { this.state.isProcessing = true; try { const result = await this.rpc(`${ENDPOINT_BASE}/compute_schedule`, { asset_id: assetId, recompute: recompute, }); this.notification.add(`Schedule computed (${result.lines_created} lines)`, { type: "success" }); if (this.state.selectedAssetId === assetId) { await this.selectAsset(assetId); } return result; } catch (err) { this.notification.add(`Compute failed: ${err.message || err}`, { type: "danger" }); throw err; } finally { this.state.isProcessing = false; } } async postDepreciation(assetId) { this.state.isProcessing = true; try { const result = await this.rpc(`${ENDPOINT_BASE}/post_depreciation`, { asset_id: assetId, }); this.notification.add(`Posted ${result.posted_count} period(s)`, { type: "success" }); if (this.state.selectedAssetId === assetId) { await this.selectAsset(assetId); } return result; } catch (err) { this.notification.add(`Post failed: ${err.message || err}`, { type: "danger" }); throw err; } finally { this.state.isProcessing = false; } } async disposeAsset(assetId, { saleAmount = 0, saleDate = null, salePartnerId = null, disposalType = "sale" } = {}) { this.state.isProcessing = true; try { const result = await this.rpc(`${ENDPOINT_BASE}/dispose`, { asset_id: assetId, sale_amount: saleAmount, sale_date: saleDate, sale_partner_id: salePartnerId, disposal_type: disposalType, }); this.notification.add(`Asset disposed: gain/loss $${result.gain_loss_amount.toFixed(2)}`, { type: "success" }); await this.loadAssets(this.state.companyId); return result; } catch (err) { this.notification.add(`Dispose failed: ${err.message || err}`, { type: "danger" }); throw err; } finally { this.state.isProcessing = false; } } async fetchAnomalies(severity = null) { try { const result = await this.rpc(`${ENDPOINT_BASE}/get_anomalies`, { severity: severity, company_id: this.state.companyId, }); this.state.anomalies = result.anomalies || []; } catch (err) { this.state.anomalies = []; } } async suggestUsefulLife(description, amount = null, partnerName = null) { return await this.rpc(`${ENDPOINT_BASE}/suggest_useful_life`, { description: description, amount: amount, partner_name: partnerName, }); } setStateFilter(state) { this.state.stateFilter = state; this.state.offset = 0; this.loadAssets(this.state.companyId); } } export const assetsService = { dependencies: ["notification"], start(env, services) { return new AssetsService(env, services); }, }; registry.category("services").add("fusion_assets", assetsService);