/** @odoo-module **/ import { registry } from "@web/core/registry"; import { reactive } from "@odoo/owl"; import { rpc } from "@web/core/network/rpc"; const ENDPOINT_BASE = "/fusion/followup"; export class FollowupService { 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({ partners: [], count: 0, total: 0, statusFilter: null, isLoading: false, isProcessing: false, selectedPartnerId: null, selectedDetail: null, companyId: null, limit: 50, offset: 0, generatedText: null, }); } async loadOverdue(companyId = null) { this.state.companyId = companyId; this.state.isLoading = true; try { const result = await this.rpc(`${ENDPOINT_BASE}/list_overdue`, { status: this.state.statusFilter, limit: this.state.limit, offset: this.state.offset, company_id: companyId, }); this.state.partners = result.partners; this.state.count = result.count; this.state.total = result.total; } finally { this.state.isLoading = false; } } async selectPartner(partnerId) { this.state.selectedPartnerId = partnerId; this.state.selectedDetail = null; this.state.generatedText = null; try { this.state.selectedDetail = await this.rpc(`${ENDPOINT_BASE}/get_partner_detail`, { partner_id: partnerId, }); } catch (err) { this.notification.add(`Failed to load partner detail: ${err.message || err}`, { type: "danger" }); } } async generateText(partnerId, levelId = null, forceRegenerate = false) { this.state.isProcessing = true; try { this.state.generatedText = await this.rpc(`${ENDPOINT_BASE}/generate_text`, { partner_id: partnerId, level_id: levelId, force_regenerate: forceRegenerate, }); return this.state.generatedText; } catch (err) { this.notification.add(`Generate failed: ${err.message || err}`, { type: "danger" }); throw err; } finally { this.state.isProcessing = false; } } async sendFollowup(partnerId, levelId = null, force = false) { this.state.isProcessing = true; try { const result = await this.rpc(`${ENDPOINT_BASE}/send`, { partner_id: partnerId, level_id: levelId, force: force, }); const status = result.status || "unknown"; const type = status === "sent" ? "success" : status.startsWith("paused") ? "warning" : "info"; this.notification.add(`Send result: ${status}`, { type: type }); if (this.state.selectedPartnerId === partnerId) { await this.selectPartner(partnerId); } await this.loadOverdue(this.state.companyId); return result; } catch (err) { this.notification.add(`Send failed: ${err.message || err}`, { type: "danger" }); throw err; } finally { this.state.isProcessing = false; } } async pausePartner(partnerId, untilDate = null) { try { const result = await this.rpc(`${ENDPOINT_BASE}/pause`, { partner_id: partnerId, until_date: untilDate, }); this.notification.add(`Paused until ${result.paused_until}`, { type: "info" }); if (this.state.selectedPartnerId === partnerId) { await this.selectPartner(partnerId); } await this.loadOverdue(this.state.companyId); return result; } catch (err) { this.notification.add(`Pause failed: ${err.message || err}`, { type: "danger" }); throw err; } } async resetPartner(partnerId) { try { const result = await this.rpc(`${ENDPOINT_BASE}/reset`, { partner_id: partnerId, }); this.notification.add(`Reset`, { type: "info" }); if (this.state.selectedPartnerId === partnerId) { await this.selectPartner(partnerId); } await this.loadOverdue(this.state.companyId); return result; } catch (err) { this.notification.add(`Reset failed: ${err.message || err}`, { type: "danger" }); throw err; } } setStatusFilter(status) { this.state.statusFilter = status; this.state.offset = 0; this.loadOverdue(this.state.companyId); } } export const followupService = { dependencies: ["notification"], start(env, services) { return new FollowupService(env, services); }, }; registry.category("services").add("fusion_followup", followupService);