Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions pages/api/invoices/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { setSession } from 'utils/setSession'
import { parseCreateInvoicePOSTRequest } from 'utils/validators'
import { RESPONSE_MESSAGES } from 'constants/index'
import { createInvoice } from 'services/invoiceService'

export default async (
req: any,
res: any
): Promise<void> => {
await setSession(req, res, true)
const session = req.session
if (req.method === 'POST') {
try {
const parsedValues = parseCreateInvoicePOSTRequest({
...req.body,
userId: session.userId
})
const invoice = await createInvoice(parsedValues)
res.status(200).json({
invoice
})
} catch (err: any) {
switch (err.message) {
case RESPONSE_MESSAGES.USER_ID_NOT_PROVIDED_400.message:
res.status(400).json(RESPONSE_MESSAGES.USER_ID_NOT_PROVIDED_400)
break
default:
res.status(500).json({ statusCode: 500, message: err.message })
}
}
}
}
24 changes: 24 additions & 0 deletions prisma/migrations/20250512173910_add_invoice/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- CreateTable
CREATE TABLE `Invoice` (
`id` VARCHAR(191) NOT NULL,
`userId` VARCHAR(191) NOT NULL,
`invoiceNumber` VARCHAR(191) NOT NULL,
`transactionId` VARCHAR(191) NOT NULL,
`amount` DECIMAL(65, 30) NOT NULL,
`description` VARCHAR(191) NOT NULL,
`recipientName` VARCHAR(191) NOT NULL,
`recipientAddress` VARCHAR(191) NOT NULL,
`customerName` VARCHAR(191) NOT NULL,
`customerAddress` VARCHAR(191) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,

UNIQUE INDEX `Invoice_invoiceNumber_userId_unique_constraint`(`invoiceNumber`, `userId`),
PRIMARY KEY (`id`, `userId`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- AddForeignKey
ALTER TABLE `Invoice` ADD CONSTRAINT `Invoice_transactionId_fkey` FOREIGN KEY (`transactionId`) REFERENCES `Transaction`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE `Invoice` ADD CONSTRAINT `Invoice_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `UserProfile`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
26 changes: 26 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ model Network {
prices Price[]
}


model Transaction {
id String @id @default(dbgenerated("(uuid())"))
hash String @db.VarChar(255)
Expand All @@ -74,6 +75,8 @@ model Transaction {
opReturn String @db.LongText @default("")
address Address @relation(fields: [addressId], references: [id], onDelete: Cascade, onUpdate: Cascade)
prices PricesOnTransactions[]
invoices Invoice[]

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

Expand Down Expand Up @@ -166,6 +169,7 @@ model UserProfile {
lastSentVerificationEmailAt DateTime?
wallets WalletsOnUserProfile[]
addresses AddressesOnUserProfiles[]
invoices Invoice[]
preferredCurrencyId Int @default(1)
preferredTimezone String @db.VarChar(255)@default("")

Expand Down Expand Up @@ -241,3 +245,25 @@ model OrganizationInvite {
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
}

model Invoice {
id String @default(uuid())
userId String
invoiceNumber String
transactionId String
transaction Transaction @relation(fields: [transactionId], references: [id], onUpdate: Cascade, onDelete: Restrict)
userProfile UserProfile @relation(fields: [userId], references: [id], onUpdate: Cascade, onDelete: Restrict)

amount Decimal
description String
recipientName String
recipientAddress String
customerName String
customerAddress String

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@id([id, userId])
@@unique([invoiceNumber, userId], map: "Invoice_invoiceNumber_userId_unique_constraint")
}
37 changes: 37 additions & 0 deletions services/invoiceService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import prisma from 'prisma/clientInstance'
import { Invoice } from '@prisma/client'
interface CreateInvoiceParams {
userId: string
transactionId: string
amount: number
description: string
recipientName: string
recipientAddress: string
customerName: string
customerAddress: string
}

export async function createInvoice (params: CreateInvoiceParams): Promise<Invoice> {
const year = new Date().getFullYear()

const latestInvoiceNumber = await prisma.invoice.findFirst({
where: {
invoiceNumber: {
startsWith: `${year}-`
}
},
orderBy: {
invoiceNumber: 'desc'
}
})
const nextNumber = (latestInvoiceNumber != null) ? parseInt(latestInvoiceNumber.invoiceNumber.split('-')[1]) + 1 : 1

const invoiceNumber = `${year}-${String(nextNumber).padStart(3, '0')}`

return await prisma.invoice.create({
data: {
invoiceNumber,
...params
}
})
}
21 changes: 21 additions & 0 deletions utils/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,24 @@ export const parseUpdateUserTimezonePUTRequest = function (params: UpdateUserTim

return { timezone: params.timezone }
}

export interface CreateinvoicePOSTParameters {
userId: string
transactionId: string
amount: number
description: string
recipientName: string
recipientAddress: string
customerName: string
customerAddress: string
}

export const parseCreateInvoicePOSTRequest = function (params: CreateinvoicePOSTParameters): CreateinvoicePOSTParameters {
let description = params.description
if (params.userId === '' || params.userId === undefined) throw new Error(RESPONSE_MESSAGES.USER_ID_NOT_PROVIDED_400.message)
if (description === undefined) description = ''
return {
...params,
description
}
}