import type { FastifyPluginAsync } from 'fastify' import { PaginationSchema, TransactionCreateSchema, TransactionLineItemCreateSchema, ApplyDiscountSchema, CompleteTransactionSchema, } from '@lunarfront/shared/schemas' import { TransactionService } from '../../services/transaction.service.js' export const transactionRoutes: FastifyPluginAsync = async (app) => { app.post('/transactions', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => { const parsed = TransactionCreateSchema.safeParse(request.body) if (!parsed.success) { return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } }) } const txn = await TransactionService.create(app.db, parsed.data, request.user.id) request.log.info({ transactionId: txn.id, type: parsed.data.transactionType, userId: request.user.id }, 'Transaction created') return reply.status(201).send(txn) }) app.get('/transactions', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => { const query = request.query as Record const params = PaginationSchema.parse(query) const filters = { status: query.status, transactionType: query.transactionType, locationId: query.locationId, } const result = await TransactionService.list(app.db, params, filters) return reply.send(result) }) app.get('/transactions/:id', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => { const { id } = request.params as { id: string } const txn = await TransactionService.getById(app.db, id) if (!txn) return reply.status(404).send({ error: { message: 'Transaction not found', statusCode: 404 } }) return reply.send(txn) }) app.get('/transactions/:id/receipt', { preHandler: [app.authenticate, app.requirePermission('pos.view')] }, async (request, reply) => { const { id } = request.params as { id: string } const receipt = await TransactionService.getReceipt(app.db, id) return reply.send(receipt) }) app.post('/transactions/:id/line-items', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => { const { id } = request.params as { id: string } const parsed = TransactionLineItemCreateSchema.safeParse(request.body) if (!parsed.success) { return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } }) } const lineItem = await TransactionService.addLineItem(app.db, id, parsed.data) return reply.status(201).send(lineItem) }) app.delete('/transactions/:id/line-items/:lineItemId', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => { const { id, lineItemId } = request.params as { id: string; lineItemId: string } const deleted = await TransactionService.removeLineItem(app.db, id, lineItemId) return reply.send(deleted) }) app.post('/transactions/:id/discounts', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => { const { id } = request.params as { id: string } const parsed = ApplyDiscountSchema.safeParse(request.body) if (!parsed.success) { return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } }) } await TransactionService.applyDiscount(app.db, id, parsed.data, request.user.id) const txn = await TransactionService.getById(app.db, id) return reply.send(txn) }) app.post('/transactions/:id/complete', { preHandler: [app.authenticate, app.requirePermission('pos.edit')] }, async (request, reply) => { const { id } = request.params as { id: string } const parsed = CompleteTransactionSchema.safeParse(request.body) if (!parsed.success) { return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } }) } await TransactionService.complete(app.db, id, parsed.data) const txn = await TransactionService.getById(app.db, id) request.log.info({ transactionId: id, paymentMethod: parsed.data.paymentMethod, userId: request.user.id }, 'Transaction completed') return reply.send(txn) }) app.post('/transactions/:id/void', { preHandler: [app.authenticate, app.requirePermission('pos.admin')] }, async (request, reply) => { const { id } = request.params as { id: string } await TransactionService.void(app.db, id, request.user.id) const txn = await TransactionService.getById(app.db, id) request.log.info({ transactionId: id, voidedBy: request.user.id }, 'Transaction voided') return reply.send(txn) }) }