Skip to content

Commit c82b6b3

Browse files
authored
feat: Implement auto-incrementing invoice numbers (#43)
1 parent a61f0e1 commit c82b6b3

File tree

5 files changed

+53
-1
lines changed

5 files changed

+53
-1
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 { getInvoiceCount } 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 invoiceCount = await getInvoiceCount(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+
invoiceCount={invoiceCount}
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 { getInvoiceCount } 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 invoiceCount = await getInvoiceCount(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+
invoiceCount={invoiceCount}
4145
/>
4246
</main>
4347
<Footer />

src/components/invoice-creator.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { InvoiceForm } from "@/components/invoice-form";
44
import { InvoicePreview } from "@/components/invoice-preview";
55
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
6+
import { generateInvoiceNumber } from "@/lib/invoice/client";
67
import {
78
type InvoiceFormValues,
89
invoiceFormSchema,
@@ -23,11 +24,13 @@ interface InvoiceCreatorProps {
2324
name: string;
2425
email: string;
2526
};
27+
invoiceCount: string;
2628
}
2729

2830
export function InvoiceCreator({
2931
recipientDetails,
3032
currentUser,
33+
invoiceCount,
3134
}: InvoiceCreatorProps) {
3235
const router = useRouter();
3336
const isInvoiceMe = !!recipientDetails?.userId;
@@ -52,7 +55,7 @@ export function InvoiceCreator({
5255
const form = useForm<InvoiceFormValues>({
5356
resolver: zodResolver(invoiceFormSchema),
5457
defaultValues: {
55-
invoiceNumber: "",
58+
invoiceNumber: generateInvoiceNumber(invoiceCount),
5659
dueDate: "",
5760
creatorName: !isInvoiceMe ? (currentUser?.name ?? "") : "",
5861
creatorEmail: !isInvoiceMe ? (currentUser?.email ?? "") : "",

src/lib/invoice/client.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export const generateInvoiceNumber = (invoiceCount: string) => {
2+
const now = new Date();
3+
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
4+
5+
const formatter = new Intl.DateTimeFormat(undefined, {
6+
year: "numeric",
7+
month: "2-digit",
8+
timeZone: userTimeZone,
9+
});
10+
11+
const [{ value: month }, , { value: year }] = formatter.formatToParts(now);
12+
13+
return `${year}${month}-${invoiceCount}`;
14+
};

src/lib/invoice/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 getInvoiceCount = 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 invoiceCount = String(invoicesCountThisMonth[0].count + 1).padStart(
22+
4,
23+
"0",
24+
);
25+
26+
return invoiceCount;
27+
};

0 commit comments

Comments
 (0)