diff --git a/packages/api/src/setup.ts b/packages/api/src/setup.ts index c08090c..aae6945 100644 --- a/packages/api/src/setup.ts +++ b/packages/api/src/setup.ts @@ -14,9 +14,11 @@ import { createInvoiceHandler, createMolliePaymentHandler, createBankTransferPaymentHandler, - createSmartpinPaymentHandler + createSmartpinPaymentHandler, + createPinPaymentHandler } from '@modular-api/fastify-checkout' import { initialize } from './pgboss.js' +import type { ClientMetadata } from 'oidc-provider' const getString = (str: string) => str const host = getString(__HOST__) @@ -82,6 +84,11 @@ export default async function (fastify: FastifyInstance) { kysely }) + const pinPaymentHandler = createPinPaymentHandler({ + fastify, + kysely + }) + let molliePaymentHandler: | CheckoutPluginOptionsPaymentHandlers['mollie'] | undefined @@ -156,11 +163,12 @@ export default async function (fastify: FastifyInstance) { mollie: molliePaymentHandler, cash: cashPaymentHandler, bankTransfer: bankTransferPaymentHandler, - smartpin: smartpinPaymentHandler + smartpin: smartpinPaymentHandler, + pin: pinPaymentHandler } }) - const clients = [ + const clients: ClientMetadata[] = [ { client_id: env.read('OIDC_CLIENT_ID') || diff --git a/packages/app/src/components/invoice/InvoiceExpansionItem.vue b/packages/app/src/components/invoice/InvoiceExpansionItem.vue index 620f97b..b3c445b 100644 --- a/packages/app/src/components/invoice/InvoiceExpansionItem.vue +++ b/packages/app/src/components/invoice/InvoiceExpansionItem.vue @@ -241,7 +241,9 @@ modelValue.status ) && modelValue.amountDue && - (onAddPaymentCash || onAddPaymentBankTransfer) + (onAddPaymentCash || + onAddPaymentBankTransfer || + onAddPaymentPin) " clickable > @@ -284,6 +286,21 @@ + + + + + + + {{ lang.payment.methods.pin }} + + + void } ): void + ( + e: 'addPaymentPin', + { + data, + done + }: { + data: Invoice + done: (success?: boolean) => void + } + ): void ( e: 'sendReceipt', { @@ -638,6 +666,12 @@ const addPaymentIdeal = (data: Invoice) => { } emit('addPaymentIdeal', { data: data, done }) } +const addPaymentPin = (data: Invoice) => { + function done() { + // + } + emit('addPaymentPin', { data: data, done }) +} const sendReceipt = (data: Invoice) => { function done() { // diff --git a/packages/app/src/lang/en-US.ts b/packages/app/src/lang/en-US.ts index 2018df2..5a4e26f 100644 --- a/packages/app/src/lang/en-US.ts +++ b/packages/app/src/lang/en-US.ts @@ -218,7 +218,8 @@ const lang: Language = { methods: { cash: 'Cash', bankTransfer: 'Bank transfer', - ideal: 'iDEAL' + ideal: 'iDEAL', + pin: 'PIN' }, messages: { scanQrOrUseInformationBelow: diff --git a/packages/app/src/lang/index.ts b/packages/app/src/lang/index.ts index 9cb370f..92b43f4 100644 --- a/packages/app/src/lang/index.ts +++ b/packages/app/src/lang/index.ts @@ -243,6 +243,7 @@ export interface Language { cash: string bankTransfer: string ideal: string + pin: string } messages: { scanQrOrUseInformationBelow: string diff --git a/packages/app/src/lang/nl.ts b/packages/app/src/lang/nl.ts index 21d3383..a1ceb5e 100644 --- a/packages/app/src/lang/nl.ts +++ b/packages/app/src/lang/nl.ts @@ -220,7 +220,8 @@ const lang: Language = { methods: { cash: 'Contant', bankTransfer: 'Bank overschrijving', - ideal: 'iDEAL' + ideal: 'iDEAL', + pin: 'PIN' }, messages: { scanQrOrUseInformationBelow: diff --git a/packages/app/src/pages/admin/BillsPage.vue b/packages/app/src/pages/admin/BillsPage.vue index 8023077..20b2381 100644 --- a/packages/app/src/pages/admin/BillsPage.vue +++ b/packages/app/src/pages/admin/BillsPage.vue @@ -41,6 +41,7 @@ @update="openUpdateDialog" @send-receipt="($event) => openSendBillDialog('receipt')!($event)" @add-payment-cash="openAddCashPaymentDialog" + @add-payment-pin="openAddPinPaymentDialog" @cancel="openCancelDialog" /> @@ -109,7 +110,8 @@ import { InvoiceStatus } from '@slimfact/api/zod' import { useQuasar, QSelect } from 'quasar' import CompanySelect from '../../components/company/CompanySelect.vue' import ClientSelect from '../../components/client/ClientSelect.vue' -import PriceInputDialog from 'src/components/PriceInputDialog.vue' +import PriceInputDialog from '../../components/PriceInputDialog.vue' +import AddPaymentDialog from '../../components/AddPaymentDialog.vue' const { useQuery, useMutation } = await createUseTrpc() @@ -290,6 +292,48 @@ const openAddCashPaymentDialog: InstanceType< }) } +const openAddPinPaymentDialog: InstanceType< + typeof InvoiceExpansionItem +>['$props']['onMarkPaid'] = async ({ data, done }) => { + const format = (value: number) => + Intl.NumberFormat($q.lang.isoName, { + maximumFractionDigits: 2, + style: 'currency', + currency: data.currency + }).format(value / 100) + return $q + .dialog({ + component: AddPaymentDialog, + componentProps: { + message: lang.value.invoice.messages.addBankTransferPayment({ + clientDetails: data.clientDetails, + totalIncludingTax: format(data.totalIncludingTax) + }), + currency: data.currency + } + }) + .onOk(async ({ amount, transactionReference }) => { + const result = useMutation('admin.addPaymentToInvoice', { + args: { + id: data.id, + payment: { + amount, + currency: data.currency, + description: new Date().toISOString().slice(0, 10), + transactionReference, + method: PaymentMethod.pin + } + }, + immediate: true + }) + await result.immediatePromise + + if (!result.error.value) { + await execute() + } + }) +} + const openCancelDialog: InstanceType< typeof InvoiceExpansionItem >['$props']['onCancel'] = async ({ data, done }) => { diff --git a/packages/app/src/pages/admin/InvoicesPage.vue b/packages/app/src/pages/admin/InvoicesPage.vue index aff00cc..67d61cc 100644 --- a/packages/app/src/pages/admin/InvoicesPage.vue +++ b/packages/app/src/pages/admin/InvoicesPage.vue @@ -47,6 +47,7 @@ @cancel="openCancelDialog" @add-payment-cash="openAddCashPaymentDialog" @add-payment-bank-transfer="openAddBankTransferPaymentDialog" + @add-payment-pin="openAddPinPaymentDialog" /> @@ -414,6 +415,48 @@ const openAddBankTransferPaymentDialog: InstanceType< } }) } + +const openAddPinPaymentDialog: InstanceType< + typeof InvoiceExpansionItem +>['$props']['onMarkPaid'] = async ({ data, done }) => { + const format = (value: number) => + Intl.NumberFormat($q.lang.isoName, { + maximumFractionDigits: 2, + style: 'currency', + currency: data.currency + }).format(value / 100) + return $q + .dialog({ + component: AddPaymentDialog, + componentProps: { + message: lang.value.invoice.messages.addBankTransferPayment({ + clientDetails: data.clientDetails, + totalIncludingTax: format(data.totalIncludingTax) + }), + currency: data.currency + } + }) + .onOk(async ({ amount, transactionReference }) => { + const result = useMutation('admin.addPaymentToInvoice', { + args: { + id: data.id, + payment: { + amount, + currency: data.currency, + description: new Date().toISOString().slice(0, 10), + transactionReference, + method: PaymentMethod.pin + } + }, + immediate: true + }) + await result.immediatePromise + + if (!result.error.value) { + await execute() + } + }) +} // const openMarkPaidDialog: InstanceType< // typeof InvoiceExpansionItem // >['$props']['onMarkPaid'] = async ({ data, done }) => {