@voyantjs/finance 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +109 -0
- package/README.md +47 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/routes.d.ts +3603 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +632 -0
- package/dist/schema.d.ts +5722 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +816 -0
- package/dist/service.d.ts +5659 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +1786 -0
- package/dist/validation.d.ts +1316 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +657 -0
- package/package.json +52 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAgEjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAMD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6CA68BtB,CAAA;AAEJ,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAA"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { financeService } from "./service.js";
|
|
3
|
+
import { agingReportQuerySchema, insertBookingGuaranteeSchema, insertBookingItemCommissionSchema, insertBookingItemTaxLineSchema, insertBookingPaymentScheduleSchema, insertCreditNoteLineItemSchema, insertCreditNoteSchema, insertFinanceNoteSchema, insertInvoiceExternalRefSchema, insertInvoiceLineItemSchema, insertInvoiceNumberSeriesSchema, insertInvoiceSchema, insertInvoiceTemplateSchema, insertPaymentAuthorizationSchema, insertPaymentCaptureSchema, insertPaymentInstrumentSchema, insertPaymentSessionSchema, insertPaymentSchema, insertSupplierPaymentSchema, insertTaxRegimeSchema, invoiceListQuerySchema, invoiceNumberSeriesListQuerySchema, invoiceTemplateListQuerySchema, paymentAuthorizationListQuerySchema, paymentCaptureListQuerySchema, paymentInstrumentListQuerySchema, paymentSessionListQuerySchema, profitabilityQuerySchema, renderInvoiceInputSchema, revenueReportQuerySchema, supplierPaymentListQuerySchema, taxRegimeListQuerySchema, updateBookingGuaranteeSchema, updateBookingItemCommissionSchema, updateBookingItemTaxLineSchema, updateBookingPaymentScheduleSchema, updateCreditNoteSchema, updateInvoiceLineItemSchema, updateInvoiceNumberSeriesSchema, updateInvoiceSchema, updateInvoiceTemplateSchema, updatePaymentAuthorizationSchema, updatePaymentCaptureSchema, updatePaymentInstrumentSchema, updatePaymentSessionSchema, updateSupplierPaymentSchema, updateTaxRegimeSchema, createPaymentSessionFromGuaranteeSchema, createPaymentSessionFromInvoiceSchema, createPaymentSessionFromScheduleSchema, applyDefaultBookingPaymentPlanSchema, markPaymentSessionRequiresRedirectSchema, completePaymentSessionSchema, failPaymentSessionSchema, cancelPaymentSessionSchema, expirePaymentSessionSchema, } from "./validation.js";
|
|
4
|
+
// ==========================================================================
|
|
5
|
+
// Finance Routes — method-chained for Hono RPC type inference
|
|
6
|
+
// ==========================================================================
|
|
7
|
+
export const financeRoutes = new Hono()
|
|
8
|
+
// ========================================================================
|
|
9
|
+
// Payment Sessions
|
|
10
|
+
// ========================================================================
|
|
11
|
+
.get("/payment-sessions", async (c) => {
|
|
12
|
+
const query = paymentSessionListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
13
|
+
return c.json(await financeService.listPaymentSessions(c.get("db"), query));
|
|
14
|
+
})
|
|
15
|
+
.post("/payment-sessions", async (c) => {
|
|
16
|
+
return c.json({
|
|
17
|
+
data: await financeService.createPaymentSession(c.get("db"), insertPaymentSessionSchema.parse(await c.req.json())),
|
|
18
|
+
}, 201);
|
|
19
|
+
})
|
|
20
|
+
.get("/payment-sessions/:id", async (c) => {
|
|
21
|
+
const row = await financeService.getPaymentSessionById(c.get("db"), c.req.param("id"));
|
|
22
|
+
if (!row)
|
|
23
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
24
|
+
return c.json({ data: row });
|
|
25
|
+
})
|
|
26
|
+
.patch("/payment-sessions/:id", async (c) => {
|
|
27
|
+
const row = await financeService.updatePaymentSession(c.get("db"), c.req.param("id"), updatePaymentSessionSchema.parse(await c.req.json()));
|
|
28
|
+
if (!row)
|
|
29
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
30
|
+
return c.json({ data: row });
|
|
31
|
+
})
|
|
32
|
+
.post("/payment-sessions/:id/requires-redirect", async (c) => {
|
|
33
|
+
const row = await financeService.markPaymentSessionRequiresRedirect(c.get("db"), c.req.param("id"), markPaymentSessionRequiresRedirectSchema.parse(await c.req.json()));
|
|
34
|
+
if (!row)
|
|
35
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
36
|
+
return c.json({ data: row });
|
|
37
|
+
})
|
|
38
|
+
.post("/payment-sessions/:id/complete", async (c) => {
|
|
39
|
+
const row = await financeService.completePaymentSession(c.get("db"), c.req.param("id"), completePaymentSessionSchema.parse(await c.req.json()));
|
|
40
|
+
if (!row)
|
|
41
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
42
|
+
return c.json({ data: row });
|
|
43
|
+
})
|
|
44
|
+
.post("/payment-sessions/:id/fail", async (c) => {
|
|
45
|
+
const row = await financeService.failPaymentSession(c.get("db"), c.req.param("id"), failPaymentSessionSchema.parse(await c.req.json()));
|
|
46
|
+
if (!row)
|
|
47
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
48
|
+
return c.json({ data: row });
|
|
49
|
+
})
|
|
50
|
+
.post("/payment-sessions/:id/cancel", async (c) => {
|
|
51
|
+
const row = await financeService.cancelPaymentSession(c.get("db"), c.req.param("id"), cancelPaymentSessionSchema.parse(await c.req.json()));
|
|
52
|
+
if (!row)
|
|
53
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
54
|
+
return c.json({ data: row });
|
|
55
|
+
})
|
|
56
|
+
.post("/payment-sessions/:id/expire", async (c) => {
|
|
57
|
+
const row = await financeService.expirePaymentSession(c.get("db"), c.req.param("id"), expirePaymentSessionSchema.parse(await c.req.json()));
|
|
58
|
+
if (!row)
|
|
59
|
+
return c.json({ error: "Payment session not found" }, 404);
|
|
60
|
+
return c.json({ data: row });
|
|
61
|
+
})
|
|
62
|
+
// ========================================================================
|
|
63
|
+
// Payment Instruments
|
|
64
|
+
// ========================================================================
|
|
65
|
+
.get("/payment-instruments", async (c) => {
|
|
66
|
+
const query = paymentInstrumentListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
67
|
+
return c.json(await financeService.listPaymentInstruments(c.get("db"), query));
|
|
68
|
+
})
|
|
69
|
+
.post("/payment-instruments", async (c) => {
|
|
70
|
+
return c.json({
|
|
71
|
+
data: await financeService.createPaymentInstrument(c.get("db"), insertPaymentInstrumentSchema.parse(await c.req.json())),
|
|
72
|
+
}, 201);
|
|
73
|
+
})
|
|
74
|
+
.get("/payment-instruments/:id", async (c) => {
|
|
75
|
+
const row = await financeService.getPaymentInstrumentById(c.get("db"), c.req.param("id"));
|
|
76
|
+
if (!row)
|
|
77
|
+
return c.json({ error: "Payment instrument not found" }, 404);
|
|
78
|
+
return c.json({ data: row });
|
|
79
|
+
})
|
|
80
|
+
.patch("/payment-instruments/:id", async (c) => {
|
|
81
|
+
const row = await financeService.updatePaymentInstrument(c.get("db"), c.req.param("id"), updatePaymentInstrumentSchema.parse(await c.req.json()));
|
|
82
|
+
if (!row)
|
|
83
|
+
return c.json({ error: "Payment instrument not found" }, 404);
|
|
84
|
+
return c.json({ data: row });
|
|
85
|
+
})
|
|
86
|
+
.delete("/payment-instruments/:id", async (c) => {
|
|
87
|
+
const row = await financeService.deletePaymentInstrument(c.get("db"), c.req.param("id"));
|
|
88
|
+
if (!row)
|
|
89
|
+
return c.json({ error: "Payment instrument not found" }, 404);
|
|
90
|
+
return c.json({ success: true });
|
|
91
|
+
})
|
|
92
|
+
// ========================================================================
|
|
93
|
+
// Payment Authorizations
|
|
94
|
+
// ========================================================================
|
|
95
|
+
.get("/payment-authorizations", async (c) => {
|
|
96
|
+
const query = paymentAuthorizationListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
97
|
+
return c.json(await financeService.listPaymentAuthorizations(c.get("db"), query));
|
|
98
|
+
})
|
|
99
|
+
.post("/payment-authorizations", async (c) => {
|
|
100
|
+
return c.json({
|
|
101
|
+
data: await financeService.createPaymentAuthorization(c.get("db"), insertPaymentAuthorizationSchema.parse(await c.req.json())),
|
|
102
|
+
}, 201);
|
|
103
|
+
})
|
|
104
|
+
.get("/payment-authorizations/:id", async (c) => {
|
|
105
|
+
const row = await financeService.getPaymentAuthorizationById(c.get("db"), c.req.param("id"));
|
|
106
|
+
if (!row)
|
|
107
|
+
return c.json({ error: "Payment authorization not found" }, 404);
|
|
108
|
+
return c.json({ data: row });
|
|
109
|
+
})
|
|
110
|
+
.patch("/payment-authorizations/:id", async (c) => {
|
|
111
|
+
const row = await financeService.updatePaymentAuthorization(c.get("db"), c.req.param("id"), updatePaymentAuthorizationSchema.parse(await c.req.json()));
|
|
112
|
+
if (!row)
|
|
113
|
+
return c.json({ error: "Payment authorization not found" }, 404);
|
|
114
|
+
return c.json({ data: row });
|
|
115
|
+
})
|
|
116
|
+
.delete("/payment-authorizations/:id", async (c) => {
|
|
117
|
+
const row = await financeService.deletePaymentAuthorization(c.get("db"), c.req.param("id"));
|
|
118
|
+
if (!row)
|
|
119
|
+
return c.json({ error: "Payment authorization not found" }, 404);
|
|
120
|
+
return c.json({ success: true });
|
|
121
|
+
})
|
|
122
|
+
// ========================================================================
|
|
123
|
+
// Payment Captures
|
|
124
|
+
// ========================================================================
|
|
125
|
+
.get("/payment-captures", async (c) => {
|
|
126
|
+
const query = paymentCaptureListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
127
|
+
return c.json(await financeService.listPaymentCaptures(c.get("db"), query));
|
|
128
|
+
})
|
|
129
|
+
.post("/payment-captures", async (c) => {
|
|
130
|
+
return c.json({
|
|
131
|
+
data: await financeService.createPaymentCapture(c.get("db"), insertPaymentCaptureSchema.parse(await c.req.json())),
|
|
132
|
+
}, 201);
|
|
133
|
+
})
|
|
134
|
+
.get("/payment-captures/:id", async (c) => {
|
|
135
|
+
const row = await financeService.getPaymentCaptureById(c.get("db"), c.req.param("id"));
|
|
136
|
+
if (!row)
|
|
137
|
+
return c.json({ error: "Payment capture not found" }, 404);
|
|
138
|
+
return c.json({ data: row });
|
|
139
|
+
})
|
|
140
|
+
.patch("/payment-captures/:id", async (c) => {
|
|
141
|
+
const row = await financeService.updatePaymentCapture(c.get("db"), c.req.param("id"), updatePaymentCaptureSchema.parse(await c.req.json()));
|
|
142
|
+
if (!row)
|
|
143
|
+
return c.json({ error: "Payment capture not found" }, 404);
|
|
144
|
+
return c.json({ data: row });
|
|
145
|
+
})
|
|
146
|
+
.delete("/payment-captures/:id", async (c) => {
|
|
147
|
+
const row = await financeService.deletePaymentCapture(c.get("db"), c.req.param("id"));
|
|
148
|
+
if (!row)
|
|
149
|
+
return c.json({ error: "Payment capture not found" }, 404);
|
|
150
|
+
return c.json({ success: true });
|
|
151
|
+
})
|
|
152
|
+
// ========================================================================
|
|
153
|
+
// Reports (static paths first)
|
|
154
|
+
// ========================================================================
|
|
155
|
+
// GET /reports/revenue — Revenue by month
|
|
156
|
+
.get("/reports/revenue", async (c) => {
|
|
157
|
+
const query = revenueReportQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
158
|
+
return c.json({ data: await financeService.getRevenueReport(c.get("db"), query) });
|
|
159
|
+
})
|
|
160
|
+
// GET /reports/aging — Outstanding invoices by age buckets
|
|
161
|
+
.get("/reports/aging", async (c) => {
|
|
162
|
+
const query = agingReportQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
163
|
+
return c.json({ data: await financeService.getAgingReport(c.get("db"), query) });
|
|
164
|
+
})
|
|
165
|
+
// GET /reports/profitability — Per-booking margin summary
|
|
166
|
+
.get("/reports/profitability", async (c) => {
|
|
167
|
+
const query = profitabilityQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
168
|
+
return c.json({ data: await financeService.getProfitabilityReport(c.get("db"), query) });
|
|
169
|
+
})
|
|
170
|
+
// ========================================================================
|
|
171
|
+
// Booking Payment Schedules
|
|
172
|
+
// ========================================================================
|
|
173
|
+
.get("/bookings/:bookingId/payment-schedules", async (c) => {
|
|
174
|
+
return c.json({
|
|
175
|
+
data: await financeService.listBookingPaymentSchedules(c.get("db"), c.req.param("bookingId")),
|
|
176
|
+
});
|
|
177
|
+
})
|
|
178
|
+
.post("/bookings/:bookingId/payment-schedules", async (c) => {
|
|
179
|
+
const row = await financeService.createBookingPaymentSchedule(c.get("db"), c.req.param("bookingId"), insertBookingPaymentScheduleSchema.parse(await c.req.json()));
|
|
180
|
+
if (!row) {
|
|
181
|
+
return c.json({ error: "Booking not found" }, 404);
|
|
182
|
+
}
|
|
183
|
+
return c.json({ data: row }, 201);
|
|
184
|
+
})
|
|
185
|
+
.post("/bookings/:bookingId/payment-schedules/default-plan", async (c) => {
|
|
186
|
+
const rows = await financeService.applyDefaultBookingPaymentPlan(c.get("db"), c.req.param("bookingId"), applyDefaultBookingPaymentPlanSchema.parse(await c.req.json()));
|
|
187
|
+
if (!rows) {
|
|
188
|
+
return c.json({ error: "Booking not found" }, 404);
|
|
189
|
+
}
|
|
190
|
+
return c.json({ data: rows }, 201);
|
|
191
|
+
})
|
|
192
|
+
.patch("/bookings/:bookingId/payment-schedules/:scheduleId", async (c) => {
|
|
193
|
+
const row = await financeService.updateBookingPaymentSchedule(c.get("db"), c.req.param("scheduleId"), updateBookingPaymentScheduleSchema.parse(await c.req.json()));
|
|
194
|
+
if (!row) {
|
|
195
|
+
return c.json({ error: "Payment schedule not found" }, 404);
|
|
196
|
+
}
|
|
197
|
+
return c.json({ data: row });
|
|
198
|
+
})
|
|
199
|
+
.post("/bookings/:bookingId/payment-schedules/:scheduleId/payment-session", async (c) => {
|
|
200
|
+
try {
|
|
201
|
+
const row = await financeService.createPaymentSessionFromBookingSchedule(c.get("db"), c.req.param("scheduleId"), createPaymentSessionFromScheduleSchema.parse(await c.req.json()));
|
|
202
|
+
if (!row) {
|
|
203
|
+
return c.json({ error: "Payment schedule not found" }, 404);
|
|
204
|
+
}
|
|
205
|
+
return c.json({ data: row }, 201);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const message = error instanceof Error ? error.message : "Unable to create payment session";
|
|
209
|
+
return c.json({ error: message }, 409);
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
.delete("/bookings/:bookingId/payment-schedules/:scheduleId", async (c) => {
|
|
213
|
+
const row = await financeService.deleteBookingPaymentSchedule(c.get("db"), c.req.param("scheduleId"));
|
|
214
|
+
if (!row) {
|
|
215
|
+
return c.json({ error: "Payment schedule not found" }, 404);
|
|
216
|
+
}
|
|
217
|
+
return c.json({ success: true }, 200);
|
|
218
|
+
})
|
|
219
|
+
// ========================================================================
|
|
220
|
+
// Booking Guarantees
|
|
221
|
+
// ========================================================================
|
|
222
|
+
.get("/bookings/:bookingId/guarantees", async (c) => {
|
|
223
|
+
return c.json({
|
|
224
|
+
data: await financeService.listBookingGuarantees(c.get("db"), c.req.param("bookingId")),
|
|
225
|
+
});
|
|
226
|
+
})
|
|
227
|
+
.post("/bookings/:bookingId/guarantees", async (c) => {
|
|
228
|
+
const row = await financeService.createBookingGuarantee(c.get("db"), c.req.param("bookingId"), insertBookingGuaranteeSchema.parse(await c.req.json()));
|
|
229
|
+
if (!row) {
|
|
230
|
+
return c.json({ error: "Booking not found" }, 404);
|
|
231
|
+
}
|
|
232
|
+
return c.json({ data: row }, 201);
|
|
233
|
+
})
|
|
234
|
+
.post("/bookings/:bookingId/guarantees/:guaranteeId/payment-session", async (c) => {
|
|
235
|
+
try {
|
|
236
|
+
const row = await financeService.createPaymentSessionFromBookingGuarantee(c.get("db"), c.req.param("guaranteeId"), createPaymentSessionFromGuaranteeSchema.parse(await c.req.json()));
|
|
237
|
+
if (!row) {
|
|
238
|
+
return c.json({ error: "Booking guarantee not found" }, 404);
|
|
239
|
+
}
|
|
240
|
+
return c.json({ data: row }, 201);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
const message = error instanceof Error ? error.message : "Unable to create payment session";
|
|
244
|
+
return c.json({ error: message }, 409);
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
.patch("/bookings/:bookingId/guarantees/:guaranteeId", async (c) => {
|
|
248
|
+
const row = await financeService.updateBookingGuarantee(c.get("db"), c.req.param("guaranteeId"), updateBookingGuaranteeSchema.parse(await c.req.json()));
|
|
249
|
+
if (!row) {
|
|
250
|
+
return c.json({ error: "Booking guarantee not found" }, 404);
|
|
251
|
+
}
|
|
252
|
+
return c.json({ data: row });
|
|
253
|
+
})
|
|
254
|
+
.delete("/bookings/:bookingId/guarantees/:guaranteeId", async (c) => {
|
|
255
|
+
const row = await financeService.deleteBookingGuarantee(c.get("db"), c.req.param("guaranteeId"));
|
|
256
|
+
if (!row) {
|
|
257
|
+
return c.json({ error: "Booking guarantee not found" }, 404);
|
|
258
|
+
}
|
|
259
|
+
return c.json({ success: true }, 200);
|
|
260
|
+
})
|
|
261
|
+
// ========================================================================
|
|
262
|
+
// Booking Item Taxes
|
|
263
|
+
// ========================================================================
|
|
264
|
+
.get("/booking-items/:bookingItemId/tax-lines", async (c) => {
|
|
265
|
+
return c.json({
|
|
266
|
+
data: await financeService.listBookingItemTaxLines(c.get("db"), c.req.param("bookingItemId")),
|
|
267
|
+
});
|
|
268
|
+
})
|
|
269
|
+
.post("/booking-items/:bookingItemId/tax-lines", async (c) => {
|
|
270
|
+
const row = await financeService.createBookingItemTaxLine(c.get("db"), c.req.param("bookingItemId"), insertBookingItemTaxLineSchema.parse(await c.req.json()));
|
|
271
|
+
if (!row) {
|
|
272
|
+
return c.json({ error: "Booking item not found" }, 404);
|
|
273
|
+
}
|
|
274
|
+
return c.json({ data: row }, 201);
|
|
275
|
+
})
|
|
276
|
+
.patch("/booking-items/:bookingItemId/tax-lines/:taxLineId", async (c) => {
|
|
277
|
+
const row = await financeService.updateBookingItemTaxLine(c.get("db"), c.req.param("taxLineId"), updateBookingItemTaxLineSchema.parse(await c.req.json()));
|
|
278
|
+
if (!row) {
|
|
279
|
+
return c.json({ error: "Booking item tax line not found" }, 404);
|
|
280
|
+
}
|
|
281
|
+
return c.json({ data: row });
|
|
282
|
+
})
|
|
283
|
+
.delete("/booking-items/:bookingItemId/tax-lines/:taxLineId", async (c) => {
|
|
284
|
+
const row = await financeService.deleteBookingItemTaxLine(c.get("db"), c.req.param("taxLineId"));
|
|
285
|
+
if (!row) {
|
|
286
|
+
return c.json({ error: "Booking item tax line not found" }, 404);
|
|
287
|
+
}
|
|
288
|
+
return c.json({ success: true }, 200);
|
|
289
|
+
})
|
|
290
|
+
// ========================================================================
|
|
291
|
+
// Booking Item Commissions
|
|
292
|
+
// ========================================================================
|
|
293
|
+
.get("/booking-items/:bookingItemId/commissions", async (c) => {
|
|
294
|
+
return c.json({
|
|
295
|
+
data: await financeService.listBookingItemCommissions(c.get("db"), c.req.param("bookingItemId")),
|
|
296
|
+
});
|
|
297
|
+
})
|
|
298
|
+
.post("/booking-items/:bookingItemId/commissions", async (c) => {
|
|
299
|
+
const row = await financeService.createBookingItemCommission(c.get("db"), c.req.param("bookingItemId"), insertBookingItemCommissionSchema.parse(await c.req.json()));
|
|
300
|
+
if (!row) {
|
|
301
|
+
return c.json({ error: "Booking item not found" }, 404);
|
|
302
|
+
}
|
|
303
|
+
return c.json({ data: row }, 201);
|
|
304
|
+
})
|
|
305
|
+
.patch("/booking-items/:bookingItemId/commissions/:commissionId", async (c) => {
|
|
306
|
+
const row = await financeService.updateBookingItemCommission(c.get("db"), c.req.param("commissionId"), updateBookingItemCommissionSchema.parse(await c.req.json()));
|
|
307
|
+
if (!row) {
|
|
308
|
+
return c.json({ error: "Booking item commission not found" }, 404);
|
|
309
|
+
}
|
|
310
|
+
return c.json({ data: row });
|
|
311
|
+
})
|
|
312
|
+
.delete("/booking-items/:bookingItemId/commissions/:commissionId", async (c) => {
|
|
313
|
+
const row = await financeService.deleteBookingItemCommission(c.get("db"), c.req.param("commissionId"));
|
|
314
|
+
if (!row) {
|
|
315
|
+
return c.json({ error: "Booking item commission not found" }, 404);
|
|
316
|
+
}
|
|
317
|
+
return c.json({ success: true }, 200);
|
|
318
|
+
})
|
|
319
|
+
// ========================================================================
|
|
320
|
+
// Supplier Payments
|
|
321
|
+
// ========================================================================
|
|
322
|
+
// GET /supplier-payments — List supplier payments
|
|
323
|
+
.get("/supplier-payments", async (c) => {
|
|
324
|
+
const query = supplierPaymentListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
325
|
+
return c.json(await financeService.listSupplierPayments(c.get("db"), query));
|
|
326
|
+
})
|
|
327
|
+
// POST /supplier-payments — Record supplier payment
|
|
328
|
+
.post("/supplier-payments", async (c) => {
|
|
329
|
+
return c.json({
|
|
330
|
+
data: await financeService.createSupplierPayment(c.get("db"), insertSupplierPaymentSchema.parse(await c.req.json())),
|
|
331
|
+
}, 201);
|
|
332
|
+
})
|
|
333
|
+
// PATCH /supplier-payments/:id — Update supplier payment
|
|
334
|
+
.patch("/supplier-payments/:id", async (c) => {
|
|
335
|
+
const row = await financeService.updateSupplierPayment(c.get("db"), c.req.param("id"), updateSupplierPaymentSchema.parse(await c.req.json()));
|
|
336
|
+
if (!row) {
|
|
337
|
+
return c.json({ error: "Supplier payment not found" }, 404);
|
|
338
|
+
}
|
|
339
|
+
return c.json({ data: row });
|
|
340
|
+
})
|
|
341
|
+
// ========================================================================
|
|
342
|
+
// Invoices CRUD
|
|
343
|
+
// ========================================================================
|
|
344
|
+
// GET /invoices — List invoices
|
|
345
|
+
.get("/invoices", async (c) => {
|
|
346
|
+
const query = invoiceListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
347
|
+
return c.json(await financeService.listInvoices(c.get("db"), query));
|
|
348
|
+
})
|
|
349
|
+
// POST /invoices — Create invoice
|
|
350
|
+
.post("/invoices", async (c) => {
|
|
351
|
+
return c.json({
|
|
352
|
+
data: await financeService.createInvoice(c.get("db"), insertInvoiceSchema.parse(await c.req.json())),
|
|
353
|
+
}, 201);
|
|
354
|
+
})
|
|
355
|
+
// GET /invoices/:id — Get single invoice
|
|
356
|
+
.get("/invoices/:id", async (c) => {
|
|
357
|
+
const row = await financeService.getInvoiceById(c.get("db"), c.req.param("id"));
|
|
358
|
+
if (!row) {
|
|
359
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
360
|
+
}
|
|
361
|
+
return c.json({ data: row });
|
|
362
|
+
})
|
|
363
|
+
// PATCH /invoices/:id — Update invoice
|
|
364
|
+
.patch("/invoices/:id", async (c) => {
|
|
365
|
+
const row = await financeService.updateInvoice(c.get("db"), c.req.param("id"), updateInvoiceSchema.parse(await c.req.json()));
|
|
366
|
+
if (!row) {
|
|
367
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
368
|
+
}
|
|
369
|
+
return c.json({ data: row });
|
|
370
|
+
})
|
|
371
|
+
// DELETE /invoices/:id — Delete invoice (draft only)
|
|
372
|
+
.delete("/invoices/:id", async (c) => {
|
|
373
|
+
const result = await financeService.deleteInvoice(c.get("db"), c.req.param("id"));
|
|
374
|
+
if (result.status === "not_found") {
|
|
375
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
376
|
+
}
|
|
377
|
+
if (result.status === "not_draft") {
|
|
378
|
+
return c.json({ error: "Only draft invoices can be deleted" }, 400);
|
|
379
|
+
}
|
|
380
|
+
return c.json({ success: true }, 200);
|
|
381
|
+
})
|
|
382
|
+
.post("/invoices/:id/payment-session", async (c) => {
|
|
383
|
+
try {
|
|
384
|
+
const row = await financeService.createPaymentSessionFromInvoice(c.get("db"), c.req.param("id"), createPaymentSessionFromInvoiceSchema.parse(await c.req.json()));
|
|
385
|
+
if (!row) {
|
|
386
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
387
|
+
}
|
|
388
|
+
return c.json({ data: row }, 201);
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
const message = error instanceof Error ? error.message : "Unable to create payment session";
|
|
392
|
+
return c.json({ error: message }, 409);
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
// ========================================================================
|
|
396
|
+
// Invoice Line Items
|
|
397
|
+
// ========================================================================
|
|
398
|
+
// GET /invoices/:id/line-items — List line items
|
|
399
|
+
.get("/invoices/:id/line-items", async (c) => {
|
|
400
|
+
return c.json({
|
|
401
|
+
data: await financeService.listInvoiceLineItems(c.get("db"), c.req.param("id")),
|
|
402
|
+
});
|
|
403
|
+
})
|
|
404
|
+
// POST /invoices/:id/line-items — Add line item
|
|
405
|
+
.post("/invoices/:id/line-items", async (c) => {
|
|
406
|
+
const row = await financeService.createInvoiceLineItem(c.get("db"), c.req.param("id"), insertInvoiceLineItemSchema.parse(await c.req.json()));
|
|
407
|
+
if (!row) {
|
|
408
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
409
|
+
}
|
|
410
|
+
return c.json({ data: row }, 201);
|
|
411
|
+
})
|
|
412
|
+
// PATCH /invoices/:id/line-items/:lineId — Update line item
|
|
413
|
+
.patch("/invoices/:id/line-items/:lineId", async (c) => {
|
|
414
|
+
const row = await financeService.updateInvoiceLineItem(c.get("db"), c.req.param("lineId"), updateInvoiceLineItemSchema.parse(await c.req.json()));
|
|
415
|
+
if (!row) {
|
|
416
|
+
return c.json({ error: "Line item not found" }, 404);
|
|
417
|
+
}
|
|
418
|
+
return c.json({ data: row });
|
|
419
|
+
})
|
|
420
|
+
// DELETE /invoices/:id/line-items/:lineId — Delete line item
|
|
421
|
+
.delete("/invoices/:id/line-items/:lineId", async (c) => {
|
|
422
|
+
const row = await financeService.deleteInvoiceLineItem(c.get("db"), c.req.param("lineId"));
|
|
423
|
+
if (!row) {
|
|
424
|
+
return c.json({ error: "Line item not found" }, 404);
|
|
425
|
+
}
|
|
426
|
+
return c.json({ success: true }, 200);
|
|
427
|
+
})
|
|
428
|
+
// ========================================================================
|
|
429
|
+
// Payments
|
|
430
|
+
// ========================================================================
|
|
431
|
+
// GET /invoices/:id/payments — List payments
|
|
432
|
+
.get("/invoices/:id/payments", async (c) => {
|
|
433
|
+
return c.json({ data: await financeService.listPayments(c.get("db"), c.req.param("id")) });
|
|
434
|
+
})
|
|
435
|
+
// POST /invoices/:id/payments — Record payment (transaction)
|
|
436
|
+
.post("/invoices/:id/payments", async (c) => {
|
|
437
|
+
const row = await financeService.createPayment(c.get("db"), c.req.param("id"), insertPaymentSchema.parse(await c.req.json()));
|
|
438
|
+
if (!row) {
|
|
439
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
440
|
+
}
|
|
441
|
+
return c.json({ data: row }, 201);
|
|
442
|
+
})
|
|
443
|
+
// ========================================================================
|
|
444
|
+
// Credit Notes
|
|
445
|
+
// ========================================================================
|
|
446
|
+
// GET /invoices/:id/credit-notes — List credit notes
|
|
447
|
+
.get("/invoices/:id/credit-notes", async (c) => {
|
|
448
|
+
return c.json({
|
|
449
|
+
data: await financeService.listCreditNotes(c.get("db"), c.req.param("id")),
|
|
450
|
+
});
|
|
451
|
+
})
|
|
452
|
+
// POST /invoices/:id/credit-notes — Create credit note
|
|
453
|
+
.post("/invoices/:id/credit-notes", async (c) => {
|
|
454
|
+
const row = await financeService.createCreditNote(c.get("db"), c.req.param("id"), insertCreditNoteSchema.parse(await c.req.json()));
|
|
455
|
+
if (!row) {
|
|
456
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
457
|
+
}
|
|
458
|
+
return c.json({ data: row }, 201);
|
|
459
|
+
})
|
|
460
|
+
// PATCH /invoices/:id/credit-notes/:creditNoteId — Update credit note
|
|
461
|
+
.patch("/invoices/:id/credit-notes/:creditNoteId", async (c) => {
|
|
462
|
+
const row = await financeService.updateCreditNote(c.get("db"), c.req.param("creditNoteId"), updateCreditNoteSchema.parse(await c.req.json()));
|
|
463
|
+
if (!row) {
|
|
464
|
+
return c.json({ error: "Credit note not found" }, 404);
|
|
465
|
+
}
|
|
466
|
+
return c.json({ data: row });
|
|
467
|
+
})
|
|
468
|
+
// ========================================================================
|
|
469
|
+
// Credit Note Line Items
|
|
470
|
+
// ========================================================================
|
|
471
|
+
// GET /invoices/:id/credit-notes/:creditNoteId/line-items — List credit note line items
|
|
472
|
+
.get("/invoices/:id/credit-notes/:creditNoteId/line-items", async (c) => {
|
|
473
|
+
return c.json({
|
|
474
|
+
data: await financeService.listCreditNoteLineItems(c.get("db"), c.req.param("creditNoteId")),
|
|
475
|
+
});
|
|
476
|
+
})
|
|
477
|
+
// POST /invoices/:id/credit-notes/:creditNoteId/line-items — Add credit note line item
|
|
478
|
+
.post("/invoices/:id/credit-notes/:creditNoteId/line-items", async (c) => {
|
|
479
|
+
const row = await financeService.createCreditNoteLineItem(c.get("db"), c.req.param("creditNoteId"), insertCreditNoteLineItemSchema.parse(await c.req.json()));
|
|
480
|
+
if (!row) {
|
|
481
|
+
return c.json({ error: "Credit note not found" }, 404);
|
|
482
|
+
}
|
|
483
|
+
return c.json({ data: row }, 201);
|
|
484
|
+
})
|
|
485
|
+
// ========================================================================
|
|
486
|
+
// Finance Notes
|
|
487
|
+
// ========================================================================
|
|
488
|
+
// GET /invoices/:id/notes — List notes
|
|
489
|
+
.get("/invoices/:id/notes", async (c) => {
|
|
490
|
+
return c.json({ data: await financeService.listNotes(c.get("db"), c.req.param("id")) });
|
|
491
|
+
})
|
|
492
|
+
// POST /invoices/:id/notes — Add note
|
|
493
|
+
.post("/invoices/:id/notes", async (c) => {
|
|
494
|
+
const userId = c.get("userId");
|
|
495
|
+
if (!userId) {
|
|
496
|
+
return c.json({ error: "User ID required to create notes" }, 400);
|
|
497
|
+
}
|
|
498
|
+
const row = await financeService.createNote(c.get("db"), c.req.param("id"), userId, insertFinanceNoteSchema.parse(await c.req.json()));
|
|
499
|
+
if (!row) {
|
|
500
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
501
|
+
}
|
|
502
|
+
return c.json({ data: row }, 201);
|
|
503
|
+
})
|
|
504
|
+
// ========================================================================
|
|
505
|
+
// Invoice Number Series
|
|
506
|
+
// ========================================================================
|
|
507
|
+
.get("/invoice-number-series", async (c) => {
|
|
508
|
+
const query = invoiceNumberSeriesListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
509
|
+
return c.json(await financeService.listInvoiceNumberSeries(c.get("db"), query));
|
|
510
|
+
})
|
|
511
|
+
.post("/invoice-number-series", async (c) => {
|
|
512
|
+
const row = await financeService.createInvoiceNumberSeries(c.get("db"), insertInvoiceNumberSeriesSchema.parse(await c.req.json()));
|
|
513
|
+
return c.json({ data: row }, 201);
|
|
514
|
+
})
|
|
515
|
+
.get("/invoice-number-series/:id", async (c) => {
|
|
516
|
+
const row = await financeService.getInvoiceNumberSeriesById(c.get("db"), c.req.param("id"));
|
|
517
|
+
if (!row)
|
|
518
|
+
return c.json({ error: "Invoice number series not found" }, 404);
|
|
519
|
+
return c.json({ data: row });
|
|
520
|
+
})
|
|
521
|
+
.patch("/invoice-number-series/:id", async (c) => {
|
|
522
|
+
const row = await financeService.updateInvoiceNumberSeries(c.get("db"), c.req.param("id"), updateInvoiceNumberSeriesSchema.parse(await c.req.json()));
|
|
523
|
+
if (!row)
|
|
524
|
+
return c.json({ error: "Invoice number series not found" }, 404);
|
|
525
|
+
return c.json({ data: row });
|
|
526
|
+
})
|
|
527
|
+
.delete("/invoice-number-series/:id", async (c) => {
|
|
528
|
+
const row = await financeService.deleteInvoiceNumberSeries(c.get("db"), c.req.param("id"));
|
|
529
|
+
if (!row)
|
|
530
|
+
return c.json({ error: "Invoice number series not found" }, 404);
|
|
531
|
+
return c.json({ success: true });
|
|
532
|
+
})
|
|
533
|
+
.post("/invoice-number-series/:id/allocate", async (c) => {
|
|
534
|
+
const result = await financeService.allocateInvoiceNumber(c.get("db"), c.req.param("id"));
|
|
535
|
+
if (result.status === "not_found") {
|
|
536
|
+
return c.json({ error: "Invoice number series not found" }, 404);
|
|
537
|
+
}
|
|
538
|
+
if (result.status === "inactive") {
|
|
539
|
+
return c.json({ error: "Invoice number series is inactive" }, 409);
|
|
540
|
+
}
|
|
541
|
+
return c.json({
|
|
542
|
+
data: { sequence: result.sequence, formattedNumber: result.formattedNumber },
|
|
543
|
+
});
|
|
544
|
+
})
|
|
545
|
+
// ========================================================================
|
|
546
|
+
// Invoice Templates
|
|
547
|
+
// ========================================================================
|
|
548
|
+
.get("/invoice-templates", async (c) => {
|
|
549
|
+
const query = invoiceTemplateListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
550
|
+
return c.json(await financeService.listInvoiceTemplates(c.get("db"), query));
|
|
551
|
+
})
|
|
552
|
+
.post("/invoice-templates", async (c) => {
|
|
553
|
+
const row = await financeService.createInvoiceTemplate(c.get("db"), insertInvoiceTemplateSchema.parse(await c.req.json()));
|
|
554
|
+
return c.json({ data: row }, 201);
|
|
555
|
+
})
|
|
556
|
+
.get("/invoice-templates/:id", async (c) => {
|
|
557
|
+
const row = await financeService.getInvoiceTemplateById(c.get("db"), c.req.param("id"));
|
|
558
|
+
if (!row)
|
|
559
|
+
return c.json({ error: "Invoice template not found" }, 404);
|
|
560
|
+
return c.json({ data: row });
|
|
561
|
+
})
|
|
562
|
+
.patch("/invoice-templates/:id", async (c) => {
|
|
563
|
+
const row = await financeService.updateInvoiceTemplate(c.get("db"), c.req.param("id"), updateInvoiceTemplateSchema.parse(await c.req.json()));
|
|
564
|
+
if (!row)
|
|
565
|
+
return c.json({ error: "Invoice template not found" }, 404);
|
|
566
|
+
return c.json({ data: row });
|
|
567
|
+
})
|
|
568
|
+
.delete("/invoice-templates/:id", async (c) => {
|
|
569
|
+
const row = await financeService.deleteInvoiceTemplate(c.get("db"), c.req.param("id"));
|
|
570
|
+
if (!row)
|
|
571
|
+
return c.json({ error: "Invoice template not found" }, 404);
|
|
572
|
+
return c.json({ success: true });
|
|
573
|
+
})
|
|
574
|
+
// ========================================================================
|
|
575
|
+
// Tax Regimes
|
|
576
|
+
// ========================================================================
|
|
577
|
+
.get("/tax-regimes", async (c) => {
|
|
578
|
+
const query = taxRegimeListQuerySchema.parse(Object.fromEntries(new URL(c.req.url).searchParams));
|
|
579
|
+
return c.json(await financeService.listTaxRegimes(c.get("db"), query));
|
|
580
|
+
})
|
|
581
|
+
.post("/tax-regimes", async (c) => {
|
|
582
|
+
const row = await financeService.createTaxRegime(c.get("db"), insertTaxRegimeSchema.parse(await c.req.json()));
|
|
583
|
+
return c.json({ data: row }, 201);
|
|
584
|
+
})
|
|
585
|
+
.get("/tax-regimes/:id", async (c) => {
|
|
586
|
+
const row = await financeService.getTaxRegimeById(c.get("db"), c.req.param("id"));
|
|
587
|
+
if (!row)
|
|
588
|
+
return c.json({ error: "Tax regime not found" }, 404);
|
|
589
|
+
return c.json({ data: row });
|
|
590
|
+
})
|
|
591
|
+
.patch("/tax-regimes/:id", async (c) => {
|
|
592
|
+
const row = await financeService.updateTaxRegime(c.get("db"), c.req.param("id"), updateTaxRegimeSchema.parse(await c.req.json()));
|
|
593
|
+
if (!row)
|
|
594
|
+
return c.json({ error: "Tax regime not found" }, 404);
|
|
595
|
+
return c.json({ data: row });
|
|
596
|
+
})
|
|
597
|
+
.delete("/tax-regimes/:id", async (c) => {
|
|
598
|
+
const row = await financeService.deleteTaxRegime(c.get("db"), c.req.param("id"));
|
|
599
|
+
if (!row)
|
|
600
|
+
return c.json({ error: "Tax regime not found" }, 404);
|
|
601
|
+
return c.json({ success: true });
|
|
602
|
+
})
|
|
603
|
+
// ========================================================================
|
|
604
|
+
// Invoice Renditions & External Refs (nested under invoice)
|
|
605
|
+
// ========================================================================
|
|
606
|
+
.get("/invoices/:id/renditions", async (c) => {
|
|
607
|
+
const rows = await financeService.listInvoiceRenditions(c.get("db"), c.req.param("id"));
|
|
608
|
+
return c.json({ data: rows });
|
|
609
|
+
})
|
|
610
|
+
.post("/invoices/:id/render", async (c) => {
|
|
611
|
+
const input = renderInvoiceInputSchema.parse(await c.req.json());
|
|
612
|
+
const result = await financeService.renderInvoice(c.get("db"), c.req.param("id"), input);
|
|
613
|
+
if (result.status === "not_found")
|
|
614
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
615
|
+
return c.json({ data: result.rendition }, 201);
|
|
616
|
+
})
|
|
617
|
+
.get("/invoices/:id/external-refs", async (c) => {
|
|
618
|
+
const rows = await financeService.listInvoiceExternalRefs(c.get("db"), c.req.param("id"));
|
|
619
|
+
return c.json({ data: rows });
|
|
620
|
+
})
|
|
621
|
+
.post("/invoices/:id/external-refs", async (c) => {
|
|
622
|
+
const row = await financeService.registerInvoiceExternalRef(c.get("db"), c.req.param("id"), insertInvoiceExternalRefSchema.parse(await c.req.json()));
|
|
623
|
+
if (!row)
|
|
624
|
+
return c.json({ error: "Invoice not found" }, 404);
|
|
625
|
+
return c.json({ data: row }, 201);
|
|
626
|
+
})
|
|
627
|
+
.delete("/invoices/:id/external-refs/:refId", async (c) => {
|
|
628
|
+
const row = await financeService.deleteInvoiceExternalRef(c.get("db"), c.req.param("refId"));
|
|
629
|
+
if (!row)
|
|
630
|
+
return c.json({ error: "External ref not found" }, 404);
|
|
631
|
+
return c.json({ success: true });
|
|
632
|
+
});
|