diff --git a/backend/src/app.ts b/backend/src/app.ts index e519272..219bab5 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -144,7 +144,20 @@ app.use(route.post('/invoice', async (ctx) => { })) app.use(route.get('/invoice/INV-:date(\\d{8})-:suffix(\\d+)', async (ctx, invoiceId, suffix) => { - ctx.body = `Invoice ID: INV-${invoiceId}-${suffix}` + // 请求头验证 bearer token + const bearerToken = ctx.request.headers['authorization']?.split(' ') + if (!bearerToken) throw new HttpError(ErrorDescEnum.unauthorized, 401) + if (bearerToken[0] !== 'Bearer') throw new HttpError(ErrorDescEnum.unauthorized, 401) + if (!bearerToken[1]) throw new HttpError(ErrorDescEnum.unauthorized, 401) + await func.verifyBearerToken(bearerToken[1]) + + // 验证 invoiceId 格式 + const date = invoiceId.slice(0, 4) + '-' + invoiceId.slice(4, 6) + '-' + invoiceId.slice(6, 8) + + // 获取特定收据 + const invoice = await func.getSpecificInvoice(ctx.prisma, date, parseInt(suffix)) + console.log(invoice.exportJSON()) + ctx.body = invoice.exportJSON() })) const port = parseInt(process.env.PORT ?? '3000') diff --git a/backend/src/classes/Invoice.ts b/backend/src/classes/Invoice.ts index 4f530aa..0b747b1 100644 --- a/backend/src/classes/Invoice.ts +++ b/backend/src/classes/Invoice.ts @@ -19,6 +19,8 @@ class Invoice { this.suffixCode = suffixCode // 设置签发时间 this.issueTime = issueDate + // 设置为当天零点零秒 + this.issueTime.setHours(0, 0, 0, 0) // 设置账单周期 console.log(period) // 首先验证周期有两个日期,且第一个日期早于第二个日期 @@ -154,6 +156,23 @@ class Invoice { })) } } + + /** + * 导出为 JSON + * @returns JSON 格式的收据 + */ + exportJSON() { + return { + invoiceNumber: `INV-${this.issueTime.getFullYear()}${(this.issueTime.getMonth() + 1).toString().padStart(2, '0')}${this.issueTime.getDate().toString().padStart(2, '0')}-${this.suffixCode}`, + periodStart: this.period[0], + periodEnd: this.period[1], + items: this.items.map(item => item.exportJSON()), + dueDate: this.dueDate, + payerInfoSnap: this.payer, + payerId: this.payer.id, + note: this.note + } + } } export default Invoice \ No newline at end of file diff --git a/backend/src/classes/InvoiceItem.ts b/backend/src/classes/InvoiceItem.ts index 99fcd46..a941174 100644 --- a/backend/src/classes/InvoiceItem.ts +++ b/backend/src/classes/InvoiceItem.ts @@ -48,6 +48,19 @@ class InvoiceItem { getSubtotal() { return (this.unitPrice ? Number(this.unitPrice) : 0) * (this.quantity ?? 0); } + + /** + * 导出为 JSON 对象。 + */ + exportJSON() { + return { + id: this.id, + description: this.description, + quantity: this.quantity, + unit: this.unit, + unitPrice: this.unitPrice?.toNumber() + } + } } export default InvoiceItem \ No newline at end of file diff --git a/backend/src/func/getSpecificInvoice.ts b/backend/src/func/getSpecificInvoice.ts new file mode 100644 index 0000000..14a7811 --- /dev/null +++ b/backend/src/func/getSpecificInvoice.ts @@ -0,0 +1,44 @@ +import { PrismaClient } from '@prisma/client' +import dayjs from 'dayjs' +import { ErrorDescEnum, HttpError } from '../classes/HttpError' +import Invoice from '../classes/Invoice' +import InvoiceItem from '../classes/InvoiceItem' + +export default async (prisma: PrismaClient, invoiceIssueDateString: string, invoiceSuffix: number) => { + const invoiceIssueDate = dayjs(invoiceIssueDateString, 'YYYYMMDD').toDate() + + const invoice = await prisma.invoice.findUnique({ + where: { + invoice_date_invoice_suffix_code: { + invoice_date: invoiceIssueDate, + invoice_suffix_code: invoiceSuffix + } + } + }) + console.log(invoiceIssueDate, invoiceSuffix) + console.log(invoice) + if (!invoice) throw new HttpError(ErrorDescEnum.related_item_not_found, 404) + + const items = await prisma.invoiceItem.findMany({ + where: { + invoice_suffix_code: invoiceSuffix, + invoice_date: invoiceIssueDate + } + }) + + const payer = { + id: invoice.payer_id, + name: invoice.invoice_payer_name, + address: invoice.invoice_payer_address, + abn: invoice.invoice_payer_abn ?? undefined + } + + const invoiceItems = items.map(item => { + return new InvoiceItem(item.item_id, item.item_description, item.item_quantity, item.item_unit, item.item_unit_price) + }) + + const invoiceInstance = new Invoice(invoice.invoice_suffix_code, invoice.invoice_date, [invoice.invoice_billing_period_start, invoice.invoice_billing_period_end], invoiceItems, invoice.invoice_due_date, payer) + if (invoice.invoice_note) await invoiceInstance.setNote(invoice.invoice_note) + + return invoiceInstance +} \ No newline at end of file diff --git a/backend/src/func/index.ts b/backend/src/func/index.ts index 4e38775..bde06ae 100644 --- a/backend/src/func/index.ts +++ b/backend/src/func/index.ts @@ -1,9 +1,11 @@ import createPayer from "./createPayer" import issueInvoice from "./issueInvoice" import verifyBearerToken from "./verifyBearerToken" +import getSpecificInvoice from "./getSpecificInvoice" export default { createPayer, issueInvoice, - verifyBearerToken + verifyBearerToken, + getSpecificInvoice } \ No newline at end of file