From 7575f41a706b18be4f76d971b3798cc25f4bcd73 Mon Sep 17 00:00:00 2001 From: Stefan van Herwijnen Date: Fri, 9 Aug 2024 12:09:43 +0200 Subject: [PATCH] feat: add support for down payments --- packages/api/src/trpc/admin/invoices.ts | 2 + packages/api/src/trpc/public/invoices.ts | 55 +++++++++++++ packages/api/src/zod/invoice.ts | 1 + .../src/components/invoice/InvoiceForm.vue | 26 +++++- packages/app/src/lang/en-US.ts | 4 +- packages/app/src/lang/index.ts | 2 + packages/app/src/lang/nl.ts | 4 +- packages/app/src/pages/InvoicePage.vue | 81 +++++++++++++++---- 8 files changed, 154 insertions(+), 21 deletions(-) diff --git a/packages/api/src/trpc/admin/invoices.ts b/packages/api/src/trpc/admin/invoices.ts index 363a58e..c23635c 100644 --- a/packages/api/src/trpc/admin/invoices.ts +++ b/packages/api/src/trpc/admin/invoices.ts @@ -144,6 +144,7 @@ export const adminInvoiceRoutes = ({ locale: input.locale, notes: input.notes, projectId: input.projectId, + requiredDownPaymentAmount: input.requiredDownPaymentAmount, status: input.status, companyId: companyDetails.id, clientId: clientDetails.id @@ -206,6 +207,7 @@ export const adminInvoiceRoutes = ({ locale: input.locale, notes: input.notes, projectId: input.projectId, + requiredDownPaymentAmount: input.requiredDownPaymentAmount, status: input.status, companyId: companyDetails.id, clientId: clientDetails.id diff --git a/packages/api/src/trpc/public/invoices.ts b/packages/api/src/trpc/public/invoices.ts index 297b2e2..79a8d79 100644 --- a/packages/api/src/trpc/public/invoices.ts +++ b/packages/api/src/trpc/public/invoices.ts @@ -106,6 +106,61 @@ export const publicInvoiceRoutes = ({ code: 'BAD_REQUEST' }) }), + payDownPaymentWithIdeal: procedure + .input(z.object({ uuid: z.string() })) + .mutation(async ({ input }) => { + if (fastify.checkout?.invoiceHandler) { + const { uuid } = input + const invoice = await fastify.checkout.invoiceHandler.getInvoice({ + uuid, + options: { + withAmountPaid: true, + withAmountDue: true + } + }) + if ( + (invoice?.status === InvoiceStatus.OPEN || + invoice?.status === InvoiceStatus.BILL) && + invoice.requiredDownPaymentAmount && + invoice.requiredDownPaymentAmount > (invoice.amountPaid || 0) + ) { + if (fastify.checkout.paymentHandlers?.mollie) { + try { + const paymentResult = + await fastify.checkout.invoiceHandler.addPaymentToInvoice({ + uuid, + payment: { + amount: + invoice.requiredDownPaymentAmount - + (invoice.amountPaid || 0), + currency: invoice.currency, + description: invoice.number + ? `${invoice.numberPrefix}${invoice.number}` + : invoice.uuid, + method: PaymentMethod.ideal, + invoiceId: invoice.id, + redirectUrl + } + }) + if (paymentResult.success) { + return paymentResult.checkoutUrl + } else { + fastify.log.error(paymentResult.errorMessage) + } + } catch (e) { + fastify.log.error(e) + throw new TRPCError({ + code: 'BAD_REQUEST', + message: e as string + }) + } + } + } + } + throw new TRPCError({ + code: 'BAD_REQUEST' + }) + }), payWithSmartpin: procedure .input(z.object({ uuid: z.string() })) .mutation(async ({ input }) => { diff --git a/packages/api/src/zod/invoice.ts b/packages/api/src/zod/invoice.ts index d02ff76..09130d2 100644 --- a/packages/api/src/zod/invoice.ts +++ b/packages/api/src/zod/invoice.ts @@ -34,6 +34,7 @@ export const invoiceValidation = { numberPrefix: z.string().optional().nullable(), numberPrefixTemplate: z.string(), paymentTermDays: z.number(), + requiredDownPaymentAmount: z.number(), lines: invoiceLineValidation.array(), discounts: invoiceDiscountSurchargeValidation.array().optional().nullable(), surcharges: invoiceDiscountSurchargeValidation.array().optional().nullable(), diff --git a/packages/app/src/components/invoice/InvoiceForm.vue b/packages/app/src/components/invoice/InvoiceForm.vue index 418fff9..ed1a0ab 100644 --- a/packages/app/src/components/invoice/InvoiceForm.vue +++ b/packages/app/src/components/invoice/InvoiceForm.vue @@ -62,6 +62,20 @@ lazy-rules name="numberPrefix" /> --> + () +const $q = useQuasar() + const initialValue: Invoice = { companyId: NaN, clientId: NaN, @@ -214,7 +230,8 @@ const initialValue: Invoice = { discounts: [], surcharges: [], paymentTermDays: 14, - projectId: null + projectId: null, + requiredDownPaymentAmount: 0 } const { filteredCompanies } = toRefs(props) @@ -302,6 +319,11 @@ watch( } ) +const currencySymbols = ref({ + EUR: '€', + USD: '$' +}) + const functions = ref({ submit, setValue diff --git a/packages/app/src/lang/en-US.ts b/packages/app/src/lang/en-US.ts index 0b78ce8..c9fe140 100644 --- a/packages/app/src/lang/en-US.ts +++ b/packages/app/src/lang/en-US.ts @@ -111,7 +111,8 @@ const lang: Language = { paymentTermDays: 'Payment term in days', notes: 'Notes', projectId: 'Project ID', - status: 'Status' + status: 'Status', + requiredDownPaymentAmount: 'Required down payment' }, status: { concept: 'Concept', @@ -200,6 +201,7 @@ const lang: Language = { addPayment: 'Add payment', amountDue: 'Amount due', amountPaid: 'Amount paid', + downPayment: 'Down payment', fields: { transactionReference: 'Transaction reference', description: 'Description' diff --git a/packages/app/src/lang/index.ts b/packages/app/src/lang/index.ts index ec4405c..5e6cd38 100644 --- a/packages/app/src/lang/index.ts +++ b/packages/app/src/lang/index.ts @@ -111,6 +111,7 @@ export interface Language { notes: string projectId: string status: string + requiredDownPaymentAmount: string } status: { concept: string @@ -224,6 +225,7 @@ export interface Language { addPayment: string amountDue: string amountPaid: string + downPayment: string fields: { transactionReference: string description: string diff --git a/packages/app/src/lang/nl.ts b/packages/app/src/lang/nl.ts index 0b1117b..5f70bae 100644 --- a/packages/app/src/lang/nl.ts +++ b/packages/app/src/lang/nl.ts @@ -112,7 +112,8 @@ const lang: Language = { paymentTermDays: 'Betalingstermijn in dagen', notes: 'Notities', projectId: 'Project ID', - status: 'Status' + status: 'Status', + requiredDownPaymentAmount: 'Vereiste aanbetaling' }, status: { concept: 'Concept', @@ -202,6 +203,7 @@ const lang: Language = { addPayment: 'Betaling toevoegen', amountDue: 'Te betalen', amountPaid: 'Betaald', + downPayment: 'Aanbetaling', fields: { transactionReference: 'Transactie referentie', description: 'Omschrijving' diff --git a/packages/app/src/pages/InvoicePage.vue b/packages/app/src/pages/InvoicePage.vue index a37b946..beb11ed 100644 --- a/packages/app/src/pages/InvoicePage.vue +++ b/packages/app/src/pages/InvoicePage.vue @@ -57,6 +57,36 @@ + + + + + + + + + + iDEAL + + + + + + -
- {{ lang.payment.amountPaid }}: - -
-
- {{ lang.payment.amountDue }}: - -
+
+ {{ lang.payment.amountPaid }}: + +
+
+ {{ lang.payment.amountDue }}: +
@@ -234,6 +268,19 @@ const payWithIdeal = async () => { if (result.data.value) window.location.href = result.data.value } +const payDownPaymentWithIdeal = async () => { + const result = useMutation('public.payDownPaymentWithIdeal', { + args: { + uuid: uuid.value + }, + immediate: true + }) + + await result.immediatePromise + + if (result.data.value) window.location.href = result.data.value +} + const payWithSmartpin = async () => { const result = useMutation('public.payWithSmartpin', { args: {