import { db } from "../../db/index"; import { erpConnections, agentTasks, taskExecutions } from "@shared/schema"; import { eq, desc } from "drizzle-orm"; export interface ErpApiClient { testConnection(): Promise; getFinancialData(params?: any): Promise; getInventoryData(params?: any): Promise; getSalesData(params?: any): Promise; getPayables(params?: any): Promise; getReceivables(params?: any): Promise; } export class ArcadiaPlusClient implements ErpApiClient { private baseUrl: string; private apiKey: string; private apiSecret: string; constructor(baseUrl: string, apiKey: string, apiSecret: string) { this.baseUrl = baseUrl.replace(/\/$/, ""); this.apiKey = apiKey; this.apiSecret = apiSecret; } private async request(endpoint: string, method = "GET", body?: any) { const response = await fetch(`${this.baseUrl}${endpoint}`, { method, headers: { "Content-Type": "application/json", "X-API-Key": this.apiKey, "X-API-Secret": this.apiSecret, }, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { throw new Error(`API Error: ${response.status} ${response.statusText}`); } return response.json(); } async testConnection(): Promise { try { await this.request("/api/v1/status"); return true; } catch { return false; } } async getFinancialData(params?: any) { return this.request("/api/v1/financial/balance", "GET"); } async getInventoryData(params?: any) { return this.request("/api/v1/inventory/stock", "GET"); } async getSalesData(params?: any) { return this.request("/api/v1/sales/summary", "GET"); } async getPayables(params?: any) { return this.request("/api/v1/financial/payables", "GET"); } async getReceivables(params?: any) { return this.request("/api/v1/financial/receivables", "GET"); } } export class ArcadiaNextClient implements ErpApiClient { private baseUrl: string; private apiKey: string; private apiSecret: string; constructor(baseUrl: string, apiKey: string, apiSecret: string) { this.baseUrl = baseUrl.replace(/\/$/, ""); this.apiKey = apiKey; this.apiSecret = apiSecret; } private async request(endpoint: string, method = "GET", body?: any) { const response = await fetch(`${this.baseUrl}${endpoint}`, { method, headers: { "Content-Type": "application/json", "Authorization": `token ${this.apiKey}:${this.apiSecret}`, }, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { throw new Error(`API Error: ${response.status} ${response.statusText}`); } return response.json(); } async testConnection(): Promise { try { await this.request("/api/method/frappe.auth.get_logged_user"); return true; } catch { return false; } } async getFinancialData(params?: any) { return this.request("/api/resource/GL Entry?limit_page_length=100"); } async getInventoryData(params?: any) { return this.request("/api/resource/Stock Ledger Entry?limit_page_length=100"); } async getSalesData(params?: any) { return this.request("/api/resource/Sales Invoice?limit_page_length=100"); } async getPayables(params?: any) { return this.request("/api/resource/Purchase Invoice?filters=[[\"outstanding_amount\",\">\",0]]"); } async getReceivables(params?: any) { return this.request("/api/resource/Sales Invoice?filters=[[\"outstanding_amount\",\">\",0]]"); } } export function createErpClient(connection: typeof erpConnections.$inferSelect): ErpApiClient { switch (connection.type) { case "arcadia_plus": return new ArcadiaPlusClient( connection.baseUrl, connection.apiKey || "", connection.apiSecret || "" ); case "arcadia_next": return new ArcadiaNextClient( connection.baseUrl, connection.apiKey || "", connection.apiSecret || "" ); default: throw new Error(`Unknown ERP type: ${connection.type}`); } } export const erpStorage = { async getConnections() { return db.select().from(erpConnections).orderBy(desc(erpConnections.createdAt)); }, async getConnection(id: number) { const [connection] = await db.select().from(erpConnections).where(eq(erpConnections.id, id)); return connection; }, async createConnection(data: Omit) { const [connection] = await db.insert(erpConnections).values(data).returning(); return connection; }, async updateConnection(id: number, data: Partial) { const [connection] = await db.update(erpConnections).set(data).where(eq(erpConnections.id, id)).returning(); return connection; }, async deleteConnection(id: number) { await db.delete(erpConnections).where(eq(erpConnections.id, id)); }, async getTasks() { return db.select().from(agentTasks).orderBy(desc(agentTasks.createdAt)); }, async getTask(id: number) { const [task] = await db.select().from(agentTasks).where(eq(agentTasks.id, id)); return task; }, async createTask(data: Omit) { const [task] = await db.insert(agentTasks).values(data).returning(); return task; }, async updateTask(id: number, data: Partial) { const [task] = await db.update(agentTasks).set(data).where(eq(agentTasks.id, id)).returning(); return task; }, async deleteTask(id: number) { await db.delete(agentTasks).where(eq(agentTasks.id, id)); }, async getTaskExecutions(taskId: number) { return db.select().from(taskExecutions).where(eq(taskExecutions.taskId, taskId)).orderBy(desc(taskExecutions.startedAt)); }, async createTaskExecution(data: Omit) { const [execution] = await db.insert(taskExecutions).values(data).returning(); return execution; }, async updateTaskExecution(id: number, data: Partial) { const [execution] = await db.update(taskExecutions).set(data).where(eq(taskExecutions.id, id)).returning(); return execution; }, };