25 UpdateInvoiceRequest,
26 InvoiceFilters,
27 ApiResponse
28} from "./shared/types.ts";
29
70
71// ============================================================================
72// INVOICE API ENDPOINTS
73// ============================================================================
74
75// GET /api/invoices/stats - Dashboard statistics (must come before /:id route)
76app.get("/api/invoices/stats", async (c) => {
77 try {
78 const stats = await getInvoiceStats();
79
80 const response: ApiResponse<typeof stats> = {
81 success: true,
82 data: stats
85 return c.json(response);
86 } catch (error) {
87 const response: ApiResponse<null> = {
88 success: false,
89 error: error.message
93});
94
95// GET /api/invoices - List all invoices with filtering
96app.get("/api/invoices", async (c) => {
97 try {
98 const url = new URL(c.req.url);
120 const invoices = await getAllInvoices(filters);
121
122 const response: ApiResponse<typeof invoices> = {
123 success: true,
124 data: invoices
127 return c.json(response);
128 } catch (error) {
129 const response: ApiResponse<null> = {
130 success: false,
131 error: error.message
135});
136
137// GET /api/invoices/:id - Get specific invoice
138app.get("/api/invoices/:id", async (c) => {
139 try {
140 const id = parseInt(c.req.param('id'));
141 if (isNaN(id)) {
142 const response: ApiResponse<null> = {
143 success: false,
144 error: "Invalid invoice ID"
149 const invoice = await getInvoiceById(id);
150 if (!invoice) {
151 const response: ApiResponse<null> = {
152 success: false,
153 error: "Invoice not found"
156 }
157
158 const response: ApiResponse<typeof invoice> = {
159 success: true,
160 data: invoice
163 return c.json(response);
164 } catch (error) {
165 const response: ApiResponse<null> = {
166 success: false,
167 error: error.message
171});
172
173// POST /api/invoices - Create new invoice
174app.post("/api/invoices", async (c) => {
175 try {
176 const body = await c.req.json();
180
181 if (!vendor_id || !type || !expected_day || !invoice_date) {
182 const response: ApiResponse<null> = {
183 success: false,
184 error: "Missing required fields: vendor_id, type, expected_day, invoice_date"
189 // Validate field values
190 if (!isValidType(type)) {
191 const response: ApiResponse<null> = {
192 success: false,
193 error: `Invalid type. Must be one of: ${CONFIG.INVOICE_TYPES.join(', ')}`
197
198 if (!isValidDay(expected_day)) {
199 const response: ApiResponse<null> = {
200 success: false,
201 error: `Invalid expected_day. Must be one of: ${CONFIG.DAYS_OF_WEEK.join(', ')}`
207 const vendor = await getVendorById(vendor_id);
208 if (!vendor) {
209 const response: ApiResponse<null> = {
210 success: false,
211 error: "Vendor not found"
217 const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
218 if (!dateRegex.test(invoice_date)) {
219 const response: ApiResponse<null> = {
220 success: false,
221 error: "Invalid date format. Use YYYY-MM-DD"
234 const newInvoice = await getInvoiceById(invoiceId);
235
236 const response: ApiResponse<typeof newInvoice> = {
237 success: true,
238 data: newInvoice
241 return c.json(response, 201);
242 } catch (error) {
243 const response: ApiResponse<null> = {
244 success: false,
245 error: error.message
249});
250
251// PUT /api/invoices/:id - Update invoice (mainly status changes)
252app.put("/api/invoices/:id", async (c) => {
253 try {
254 const id = parseInt(c.req.param('id'));
255 if (isNaN(id)) {
256 const response: ApiResponse<null> = {
257 success: false,
258 error: "Invalid invoice ID"
264 const existingInvoice = await getInvoiceById(id);
265 if (!existingInvoice) {
266 const response: ApiResponse<null> = {
267 success: false,
268 error: "Invoice not found"
278 const vendor = await getVendorById(body.vendor_id);
279 if (!vendor) {
280 const response: ApiResponse<null> = {
281 success: false,
282 error: "Vendor not found"
289 if (body.type !== undefined) {
290 if (!isValidType(body.type)) {
291 const response: ApiResponse<null> = {
292 success: false,
293 error: `Invalid type. Must be one of: ${CONFIG.INVOICE_TYPES.join(', ')}`
300 if (body.expected_day !== undefined) {
301 if (!isValidDay(body.expected_day)) {
302 const response: ApiResponse<null> = {
303 success: false,
304 error: `Invalid expected_day. Must be one of: ${CONFIG.DAYS_OF_WEEK.join(', ')}`
311 if (body.status !== undefined) {
312 if (!isValidStatus(body.status)) {
313 const response: ApiResponse<null> = {
314 success: false,
315 error: `Invalid status. Must be one of: ${CONFIG.INVOICE_STATUSES.join(', ')}`
323 const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
324 if (!dateRegex.test(body.invoice_date)) {
325 const response: ApiResponse<null> = {
326 success: false,
327 error: "Invalid date format. Use YYYY-MM-DD"
334 // Check if there are any updates
335 if (Object.keys(updates).length === 0) {
336 const response: ApiResponse<null> = {
337 success: false,
338 error: "No valid fields to update"
343 const success = await updateInvoice(id, updates);
344 if (!success) {
345 const response: ApiResponse<null> = {
346 success: false,
347 error: "Failed to update invoice"
352 const updatedInvoice = await getInvoiceById(id);
353
354 const response: ApiResponse<typeof updatedInvoice> = {
355 success: true,
356 data: updatedInvoice
359 return c.json(response);
360 } catch (error) {
361 const response: ApiResponse<null> = {
362 success: false,
363 error: error.message
367});
368
369// DELETE /api/invoices/:id - Delete invoice
370app.delete("/api/invoices/:id", async (c) => {
371 try {
372 const id = parseInt(c.req.param('id'));
373 if (isNaN(id)) {
374 const response: ApiResponse<null> = {
375 success: false,
376 error: "Invalid invoice ID"
382 const existingInvoice = await getInvoiceById(id);
383 if (!existingInvoice) {
384 const response: ApiResponse<null> = {
385 success: false,
386 error: "Invoice not found"
391 const success = await deleteInvoice(id);
392 if (!success) {
393 const response: ApiResponse<null> = {
394 success: false,
395 error: "Failed to delete invoice"
398 }
399
400 const response: ApiResponse<{ id: number }> = {
401 success: true,
402 data: { id }
405 return c.json(response);
406 } catch (error) {
407 const response: ApiResponse<null> = {
408 success: false,
409 error: error.message
414
415// ============================================================================
416// VENDOR API ENDPOINTS
417// ============================================================================
418
419// GET /api/vendors - List all vendors
420app.get("/api/vendors", async (c) => {
421 try {
422 const vendors = await getAllVendors();
423
424 const response: ApiResponse<typeof vendors> = {
425 success: true,
426 data: vendors
429 return c.json(response);
430 } catch (error) {
431 const response: ApiResponse<null> = {
432 success: false,
433 error: error.message
437});
438
439// GET /api/vendors/:id - Get specific vendor
440app.get("/api/vendors/:id", async (c) => {
441 try {
442 const id = parseInt(c.req.param('id'));
443 if (isNaN(id)) {
444 const response: ApiResponse<null> = {
445 success: false,
446 error: "Invalid vendor ID"
451 const vendor = await getVendorById(id);
452 if (!vendor) {
453 const response: ApiResponse<null> = {
454 success: false,
455 error: "Vendor not found"
458 }
459
460 const response: ApiResponse<typeof vendor> = {
461 success: true,
462 data: vendor
465 return c.json(response);
466 } catch (error) {
467 const response: ApiResponse<null> = {
468 success: false,
469 error: error.message
477// ============================================================================
478
479// POST /api/seed - Seed vendors (for admin use)
480app.post("/api/seed", async (c) => {
481 try {
482 const count = await seedVendors();
483 const newCounts = await getTableCounts();
484
485 const response: ApiResponse<{ seededCount: number; totalCounts: typeof newCounts }> = {
486 success: true,
487 data: {
493 return c.json(response);
494 } catch (error) {
495 const response: ApiResponse<null> = {
496 success: false,
497 error: error.message
501});
502
503// GET /api/test - Test database functionality
504app.get("/api/test", async (c) => {
505 try {
506 const counts = await getTableCounts();
507 const vendors = await getAllVendors();
508
509 const response: ApiResponse<{ counts: typeof counts; vendors: typeof vendors; totalVendors: number }> = {
510 success: true,
511 data: {
518 return c.json(response);
519 } catch (error) {
520 const response: ApiResponse<null> = {
521 success: false,
522 error: error.message