1 | import { NextRequest, NextResponse } from 'next/server';
|
---|
2 | import { invoiceSchema, updateInvoiceSchema } from 'src/schemas';
|
---|
3 | import prisma from 'src/lib/prisma';
|
---|
4 | import { authenticateRequest } from 'src/lib/auth-middleware';
|
---|
5 | import { Prisma } from '@prisma/client';
|
---|
6 |
|
---|
7 | export async function GET(request: NextRequest, { params }: { params: { id: string } }) {
|
---|
8 | try {
|
---|
9 | // Validate ID format
|
---|
10 | if (!params.id || !/^[0-9a-fA-F-]+$/.test(params.id)) {
|
---|
11 | return NextResponse.json({ error: 'Invalid invoice ID format' }, { status: 400 });
|
---|
12 | }
|
---|
13 |
|
---|
14 | // Authenticate request
|
---|
15 | const authResult = await authenticateRequest(request);
|
---|
16 | if (authResult instanceof NextResponse) {
|
---|
17 | return authResult;
|
---|
18 | }
|
---|
19 | const { userId } = authResult;
|
---|
20 |
|
---|
21 | // Fetch invoice with user check
|
---|
22 | const invoice = await prisma.invoice.findFirst({
|
---|
23 | where: {
|
---|
24 | id: params.id,
|
---|
25 | // invoiceFromId: userId,
|
---|
26 | },
|
---|
27 | include: {
|
---|
28 | invoiceFrom: true,
|
---|
29 | invoiceTo: true,
|
---|
30 | items: true,
|
---|
31 | },
|
---|
32 | });
|
---|
33 |
|
---|
34 | if (!invoice) {
|
---|
35 | return NextResponse.json({ error: 'Invoice not found or access denied' }, { status: 404 });
|
---|
36 | }
|
---|
37 |
|
---|
38 | return NextResponse.json(invoice);
|
---|
39 | } catch (error) {
|
---|
40 | console.error('Error fetching invoice:', error);
|
---|
41 |
|
---|
42 | if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
---|
43 | return NextResponse.json({ error: 'Database error occurred' }, { status: 500 });
|
---|
44 | }
|
---|
45 |
|
---|
46 | return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
---|
47 | }
|
---|
48 | }
|
---|
49 |
|
---|
50 | export async function PATCH(request: NextRequest, { params }: { params: { id: string } }) {
|
---|
51 | try {
|
---|
52 | // Validate ID format
|
---|
53 | if (!params.id || !/^[0-9a-fA-F-]+$/.test(params.id)) {
|
---|
54 | return NextResponse.json({ error: 'Invalid invoice ID format' }, { status: 400 });
|
---|
55 | }
|
---|
56 |
|
---|
57 | // Authenticate request
|
---|
58 | const authResult = await authenticateRequest(request);
|
---|
59 | if (authResult instanceof NextResponse) {
|
---|
60 | return authResult;
|
---|
61 | }
|
---|
62 | const { userId } = authResult;
|
---|
63 |
|
---|
64 | // Parse and validate request body
|
---|
65 | const body = await request.json();
|
---|
66 |
|
---|
67 | const validation = updateInvoiceSchema.partial().safeParse(body);
|
---|
68 |
|
---|
69 | if (!validation.success) {
|
---|
70 | return NextResponse.json(
|
---|
71 | { error: 'Invalid invoice data', details: validation.error.format() },
|
---|
72 | { status: 400 }
|
---|
73 | );
|
---|
74 | }
|
---|
75 |
|
---|
76 | // Verify invoice exists and belongs to user
|
---|
77 | const existingInvoice = await prisma.invoice.findFirst({
|
---|
78 | where: {
|
---|
79 | id: params.id,
|
---|
80 | // invoiceFromId: userId,
|
---|
81 | },
|
---|
82 | });
|
---|
83 |
|
---|
84 | if (!existingInvoice) {
|
---|
85 | return NextResponse.json({ error: 'Invoice not found or access denied' }, { status: 404 });
|
---|
86 | }
|
---|
87 |
|
---|
88 | // Update invoice and related data
|
---|
89 | const updatedInvoice = await prisma.$transaction(async (tx) => {
|
---|
90 | // Conditionally delete and recreate items only if they are provided
|
---|
91 | if (validation.data.items) {
|
---|
92 | await tx.lineItem.deleteMany({
|
---|
93 | where: { invoiceId: params.id },
|
---|
94 | });
|
---|
95 | }
|
---|
96 |
|
---|
97 | // Update the invoice and create new items if provided
|
---|
98 | return tx.invoice.update({
|
---|
99 | where: { id: params.id },
|
---|
100 | data: {
|
---|
101 | invoiceNumber: validation.data.invoiceNumber,
|
---|
102 | issueDate: validation.data.issueDate,
|
---|
103 | dueDate: validation.data.dueDate,
|
---|
104 | status: validation.data.status,
|
---|
105 | currency: validation.data.currency,
|
---|
106 | quantityType: validation.data.quantityType,
|
---|
107 | subTotal: validation.data.subTotal,
|
---|
108 | month: validation.data.month,
|
---|
109 | totalAmount: validation.data.totalAmount,
|
---|
110 | discount: validation.data.discount,
|
---|
111 | taxes: validation.data.taxes,
|
---|
112 | pdfRef: validation.data.pdfRef,
|
---|
113 | invoiceTo: {
|
---|
114 | update: validation.data.invoiceTo,
|
---|
115 | },
|
---|
116 | items: validation.data.items
|
---|
117 | ? {
|
---|
118 | create: validation.data.items.map((item) => ({
|
---|
119 | ...item,
|
---|
120 | service: {
|
---|
121 | connect: { id: item.service.id },
|
---|
122 | },
|
---|
123 | })),
|
---|
124 | }
|
---|
125 | : undefined,
|
---|
126 | },
|
---|
127 | include: {
|
---|
128 | invoiceFrom: true,
|
---|
129 | invoiceTo: true,
|
---|
130 | items: true,
|
---|
131 | },
|
---|
132 | });
|
---|
133 | });
|
---|
134 |
|
---|
135 | return NextResponse.json(updatedInvoice);
|
---|
136 | } catch (error) {
|
---|
137 | console.error('Error updating invoice:', error);
|
---|
138 |
|
---|
139 | if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
---|
140 | return NextResponse.json({ error: 'Database error occurred' }, { status: 500 });
|
---|
141 | }
|
---|
142 |
|
---|
143 | return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
---|
144 | }
|
---|
145 | }
|
---|
146 |
|
---|
147 | export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) {
|
---|
148 | try {
|
---|
149 | // Validate ID format
|
---|
150 | if (!params.id || !/^[0-9a-fA-F-]+$/.test(params.id)) {
|
---|
151 | return NextResponse.json({ error: 'Invalid invoice ID format' }, { status: 400 });
|
---|
152 | }
|
---|
153 |
|
---|
154 | // Authenticate request
|
---|
155 | const authResult = await authenticateRequest(request);
|
---|
156 | if (authResult instanceof NextResponse) {
|
---|
157 | return authResult;
|
---|
158 | }
|
---|
159 | const { userId } = authResult;
|
---|
160 | console.log('userId', userId);
|
---|
161 |
|
---|
162 | // Verify invoice exists and belongs to user
|
---|
163 | const existingInvoice = await prisma.invoice.findFirst({
|
---|
164 | where: {
|
---|
165 | id: params.id,
|
---|
166 | },
|
---|
167 | });
|
---|
168 |
|
---|
169 | if (!existingInvoice) {
|
---|
170 | return NextResponse.json({ error: 'Invoice not found or access denied' }, { status: 404 });
|
---|
171 | }
|
---|
172 |
|
---|
173 | // Delete invoice and related items in a transaction
|
---|
174 | await prisma.$transaction(async (tx) => {
|
---|
175 | // Delete related items first
|
---|
176 | await tx.lineItem.deleteMany({
|
---|
177 | where: { invoiceId: params.id },
|
---|
178 | });
|
---|
179 |
|
---|
180 | // Delete the invoice
|
---|
181 | await tx.invoice.delete({
|
---|
182 | where: { id: params.id },
|
---|
183 | });
|
---|
184 | });
|
---|
185 |
|
---|
186 | return NextResponse.json({ message: 'Invoice deleted successfully' }, { status: 200 });
|
---|
187 | } catch (error) {
|
---|
188 | console.error('Error deleting invoice:', error);
|
---|
189 |
|
---|
190 | if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
---|
191 | if (error.code === 'P2025') {
|
---|
192 | return NextResponse.json({ error: 'Invoice not found' }, { status: 404 });
|
---|
193 | }
|
---|
194 | return NextResponse.json({ error: 'Database error occurred' }, { status: 500 });
|
---|
195 | }
|
---|
196 |
|
---|
197 | return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
---|
198 | }
|
---|
199 | }
|
---|