Skip to content

Commit

Permalink
feat: add support for down payments
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanvanherwijnen committed Aug 9, 2024
1 parent 06a1b84 commit 7575f41
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 21 deletions.
2 changes: 2 additions & 0 deletions packages/api/src/trpc/admin/invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
55 changes: 55 additions & 0 deletions packages/api/src/trpc/public/invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/zod/invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
26 changes: 24 additions & 2 deletions packages/app/src/components/invoice/InvoiceForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@
lazy-rules
name="numberPrefix"
/> -->
<q-input
:model-value="modelValue.requiredDownPaymentAmount / 100"
:label="lang.invoice.fields.requiredDownPaymentAmount"
class="col-12 col-md-4"
:prefix="currencySymbols[modelValue.currency]"
:lang="$q.lang.isoName"
type="number"
step="0.01"
@update:model-value="
modelValue.requiredDownPaymentAmount = Math.round(
Number($event) * 100
)
"
/>
<q-input
v-model="modelValue.projectId"
class="col-md-4 col-12"
Expand Down Expand Up @@ -158,7 +172,7 @@ import {
InvoiceLineRow,
InvoiceDiscountSurchargeRow
} from '@modular-api/quasar-components/checkout'
import { QForm, extend } from 'quasar'
import { QForm, extend, useQuasar } from 'quasar'
import { ResponsiveDialog } from '@simsustech/quasar-components'
import NumberPrefixSelect from '../numberPrefix/NumberPrefixSelect.vue'
import { NumberPrefix, Invoice, Company } from '@slimfact/api/zod'
Expand Down Expand Up @@ -203,6 +217,8 @@ const emit = defineEmits<{
): void
}>()
const $q = useQuasar()
const initialValue: Invoice = {
companyId: NaN,
clientId: NaN,
Expand All @@ -214,7 +230,8 @@ const initialValue: Invoice = {
discounts: [],
surcharges: [],
paymentTermDays: 14,
projectId: null
projectId: null,
requiredDownPaymentAmount: 0
}
const { filteredCompanies } = toRefs(props)
Expand Down Expand Up @@ -302,6 +319,11 @@ watch(
}
)
const currencySymbols = ref({
EUR: '',
USD: '$'
})
const functions = ref({
submit,
setValue
Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/lang/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/lang/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface Language {
notes: string
projectId: string
status: string
requiredDownPaymentAmount: string
}
status: {
concept: string
Expand Down Expand Up @@ -224,6 +225,7 @@ export interface Language {
addPayment: string
amountDue: string
amountPaid: string
downPayment: string
fields: {
transactionReference: string
description: string
Expand Down
4 changes: 3 additions & 1 deletion packages/app/src/lang/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -202,6 +203,7 @@ const lang: Language = {
addPayment: 'Betaling toevoegen',
amountDue: 'Te betalen',
amountPaid: 'Betaald',
downPayment: 'Aanbetaling',
fields: {
transactionReference: 'Transactie referentie',
description: 'Omschrijving'
Expand Down
81 changes: 64 additions & 17 deletions packages/app/src/pages/InvoicePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,36 @@
</q-list>
</q-menu>
</q-btn>

<q-btn
v-if="
invoice &&
[InvoiceStatus.OPEN, InvoiceStatus.BILL].includes(invoice.status) &&
invoice.requiredDownPaymentAmount &&
invoice.requiredDownPaymentAmount > (invoice.amountPaid || 0)
"
icon="payment"
:label="`${lang.payment.downPayment} ${format(invoice.requiredDownPaymentAmount - (invoice.amountPaid || 0))}`"
color="primary"
>
<q-menu>
<q-list>
<q-item
v-if="configuration.PAYMENT_HANDLERS.ideal"
clickable
@click="payDownPaymentWithIdeal"
>
<q-item-section avatar>
<q-icon name="fa-brands fa-ideal" />
</q-item-section>
<q-item-section>
<q-item-label> iDEAL </q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>

<q-btn
v-if="
invoice &&
Expand Down Expand Up @@ -85,23 +115,27 @@
/>
</div>

<div class="row justify-center">
<div v-if="invoice?.amountPaid && invoice?.amountPaid > 0" class="no-print">
{{ lang.payment.amountPaid }}:
<price
:model-value="invoice.amountPaid"
:currency="invoice.currency"
:locale="invoice.locale"
/>
</div>
<div v-if="invoice?.amountDue && invoice?.amountDue > 0" class="no-print">
{{ lang.payment.amountDue }}:
<price
:model-value="invoice.amountDue"
:currency="invoice.currency"
:locale="invoice.locale"
/>
</div>
<div
v-if="invoice?.amountPaid && invoice?.amountPaid > 0"
class="row justify-center no-print"
>
{{ lang.payment.amountPaid }}:
<price
:model-value="invoice.amountPaid"
:currency="invoice.currency"
:locale="invoice.locale"
/>
</div>
<div
v-if="invoice?.amountDue && invoice?.amountDue > 0"
class="row justify-center no-print"
>
{{ lang.payment.amountDue }}:
<price
:model-value="invoice.amountDue"
:currency="invoice.currency"
:locale="invoice.locale"
/>
</div>

<div v-if="invoice" class="row justify-center">
Expand Down Expand Up @@ -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: {
Expand Down

0 comments on commit 7575f41

Please sign in to comment.