Skip to content

Commit a6e7d34

Browse files
committed
feat: Implement invoice number generation and integrate into invoice creation and display
1 parent a61f0e1 commit a6e7d34

File tree

5 files changed

+47
-2
lines changed

5 files changed

+47
-2
lines changed

src/app/i/[id]/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { BackgroundWrapper } from "@/components/background-wrapper";
22
import { Footer } from "@/components/footer";
33
import { Header } from "@/components/header";
44
import { InvoiceCreator } from "@/components/invoice-creator";
5+
import { generateInvoiceNumber } from "@/lib/invoice";
56
import { api } from "@/trpc/server";
67
import { ArrowLeft } from "lucide-react";
78
import type { Metadata } from "next";
@@ -24,6 +25,8 @@ export default async function InvoiceMePage({
2425
notFound();
2526
}
2627

28+
const invoiceNumber = await generateInvoiceNumber(invoiceMeLink.user.id);
29+
2730
return (
2831
<BackgroundWrapper
2932
topGradient={{ from: "purple-100", to: "purple-200" }}
@@ -49,6 +52,7 @@ export default async function InvoiceMePage({
4952
clientEmail: invoiceMeLink.user.email ?? "",
5053
userId: invoiceMeLink.user.id,
5154
}}
55+
invoiceNumber={invoiceNumber}
5256
/>
5357
</main>
5458
<Footer />

src/app/invoices/create/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { BackgroundWrapper } from "@/components/background-wrapper";
22
import { Footer } from "@/components/footer";
33
import { Header } from "@/components/header";
44
import { InvoiceCreator } from "@/components/invoice-creator";
5+
import { generateInvoiceNumber } from "@/lib/invoice";
56
import { getCurrentSession } from "@/server/auth";
67
import { ArrowLeft } from "lucide-react";
78
import Link from "next/link";
@@ -14,6 +15,8 @@ export default async function CreateInvoicePage() {
1415
redirect("/");
1516
}
1617

18+
const invoiceNumber = await generateInvoiceNumber(user.id);
19+
1720
return (
1821
<BackgroundWrapper
1922
topGradient={{ from: "purple-100", to: "purple-200" }}
@@ -38,6 +41,7 @@ export default async function CreateInvoicePage() {
3841
email: user.email ?? "",
3942
name: user.name ?? "",
4043
}}
44+
invoiceNumber={invoiceNumber}
4145
/>
4246
</main>
4347
<Footer />

src/components/invoice-creator.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ interface InvoiceCreatorProps {
2323
name: string;
2424
email: string;
2525
};
26+
invoiceNumber?: string;
2627
}
2728

2829
export function InvoiceCreator({
2930
recipientDetails,
3031
currentUser,
32+
invoiceNumber,
3133
}: InvoiceCreatorProps) {
3234
const router = useRouter();
3335
const isInvoiceMe = !!recipientDetails?.userId;
@@ -52,7 +54,7 @@ export function InvoiceCreator({
5254
const form = useForm<InvoiceFormValues>({
5355
resolver: zodResolver(invoiceFormSchema),
5456
defaultValues: {
55-
invoiceNumber: "",
57+
invoiceNumber: invoiceNumber,
5658
dueDate: "",
5759
creatorName: !isInvoiceMe ? (currentUser?.name ?? "") : "",
5860
creatorEmail: !isInvoiceMe ? (currentUser?.email ?? "") : "",

src/components/invoice-form.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ export function InvoiceForm({
5151
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
5252
<div className="space-y-2">
5353
<Label htmlFor="invoiceNumber">Invoice Number</Label>
54-
<Input {...form.register("invoiceNumber")} placeholder="INV-001" />
54+
<Input
55+
disabled
56+
{...form.register("invoiceNumber")}
57+
placeholder="INV-001"
58+
/>
5559
{form.formState.errors.invoiceNumber && (
5660
<p className="text-sm text-red-500">
5761
{form.formState.errors.invoiceNumber.message}

src/lib/invoice/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { db } from "@/server/db";
2+
import { requestTable } from "@/server/db/schema";
3+
import { and, count, eq, gte } from "drizzle-orm";
4+
5+
export const generateInvoiceNumber = async (userId: string) => {
6+
const invoicesCountThisMonth = await db
7+
.select({
8+
count: count(),
9+
})
10+
.from(requestTable)
11+
.where(
12+
and(
13+
eq(requestTable.userId, userId),
14+
gte(
15+
requestTable.createdAt,
16+
new Date(new Date().getFullYear(), new Date().getMonth(), 1),
17+
),
18+
),
19+
);
20+
21+
const now = new Date();
22+
const year = now.getFullYear();
23+
const month = String(now.getMonth() + 1).padStart(2, "0");
24+
const invoiceCount = String(invoicesCountThisMonth[0].count + 1).padStart(
25+
4,
26+
"0",
27+
); // Pad with zeros to 4 digits
28+
29+
const invoiceNumber = `${year}${month}-${invoiceCount}`;
30+
return invoiceNumber;
31+
};

0 commit comments

Comments
 (0)