arcadiasuite/server/erp/index.ts

211 lines
6.2 KiB
TypeScript

import { db } from "../../db/index";
import { erpConnections, agentTasks, taskExecutions } from "@shared/schema";
import { eq, desc } from "drizzle-orm";
export interface ErpApiClient {
testConnection(): Promise<boolean>;
getFinancialData(params?: any): Promise<any>;
getInventoryData(params?: any): Promise<any>;
getSalesData(params?: any): Promise<any>;
getPayables(params?: any): Promise<any>;
getReceivables(params?: any): Promise<any>;
}
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<boolean> {
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<boolean> {
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<typeof erpConnections.$inferInsert, "id" | "createdAt">) {
const [connection] = await db.insert(erpConnections).values(data).returning();
return connection;
},
async updateConnection(id: number, data: Partial<typeof erpConnections.$inferInsert>) {
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<typeof agentTasks.$inferInsert, "id" | "createdAt" | "lastRun" | "nextRun">) {
const [task] = await db.insert(agentTasks).values(data).returning();
return task;
},
async updateTask(id: number, data: Partial<typeof agentTasks.$inferInsert>) {
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<typeof taskExecutions.$inferInsert, "id" | "startedAt">) {
const [execution] = await db.insert(taskExecutions).values(data).returning();
return execution;
},
async updateTaskExecution(id: number, data: Partial<typeof taskExecutions.$inferInsert>) {
const [execution] = await db.update(taskExecutions).set(data).where(eq(taskExecutions.id, id)).returning();
return execution;
},
};