import { eq } from "drizzle-orm"; import { Hono } from "hono"; import type { Db } from "../db/client.js"; import { glAccounts } from "../db/schema/index.js"; import type { AppBindings } from "../auth/middleware.js"; import { errorJson } from "../http.js"; import { asString, iso, isUuid, parseJsonBody, requireCan } from "./helpers.js"; function toGlAccount(row: typeof glAccounts.$inferSelect) { return { id: row.id, code: row.code, name: row.name, createdAt: iso(row.createdAt), updatedAt: iso(row.updatedAt), }; } export function createGlAccountRoutes(handle: Db) { const routes = new Hono(); routes.get("/gl-accounts", async (c) => { const denied = requireCan(c, "read:invoices"); if (denied) return denied; const rows = await handle.db.select().from(glAccounts); return c.json({ items: rows.map(toGlAccount) }); }); routes.get("/gl-accounts/:id", async (c) => { const denied = requireCan(c, "read:invoices"); if (denied) return denied; const id = c.req.param("id"); if (!isUuid(id)) return errorJson(c, 400, "VALIDATION_ERROR", "Invalid GL account id."); const row = await handle.db.query.glAccounts.findFirst({ where: eq(glAccounts.id, id) }); if (!row) return errorJson(c, 404, "NOT_FOUND", "GL account not found."); return c.json(toGlAccount(row)); }); routes.post("/gl-accounts", async (c) => { const denied = requireCan(c, "admin:settings"); if (denied) return denied; const body = await parseJsonBody(c); const code = asString(body.code).trim(); const name = asString(body.name).trim(); if (!code || !name) return errorJson(c, 400, "VALIDATION_ERROR", "Code and name are required."); const [row] = await handle.db.insert(glAccounts).values({ code, name }).returning(); return c.json(toGlAccount(row), 201); }); routes.patch("/gl-accounts/:id", async (c) => { const denied = requireCan(c, "admin:settings"); if (denied) return denied; const id = c.req.param("id"); if (!isUuid(id)) return errorJson(c, 400, "VALIDATION_ERROR", "Invalid GL account id."); const existing = await handle.db.query.glAccounts.findFirst({ where: eq(glAccounts.id, id) }); if (!existing) return errorJson(c, 404, "NOT_FOUND", "GL account not found."); const body = await parseJsonBody(c); const patch: Partial = { updatedAt: new Date() }; if (body.code !== undefined) { const code = asString(body.code).trim(); if (!code) return errorJson(c, 400, "VALIDATION_ERROR", "Code is required."); patch.code = code; } if (body.name !== undefined) { const name = asString(body.name).trim(); if (!name) return errorJson(c, 400, "VALIDATION_ERROR", "Name is required."); patch.name = name; } const [row] = await handle.db .update(glAccounts) .set(patch) .where(eq(glAccounts.id, id)) .returning(); return c.json(toGlAccount(row)); }); return routes; }