From 74739a2a7a0894dcc6123e3f901591cfe80a6d83 Mon Sep 17 00:00:00 2001 From: Katja Lutz Date: Wed, 22 Jun 2022 21:39:18 +0200 Subject: [PATCH] feat: implement swiss invoice component --- src/components/SwissInvoice.tsx | 221 ++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 src/components/SwissInvoice.tsx diff --git a/src/components/SwissInvoice.tsx b/src/components/SwissInvoice.tsx new file mode 100644 index 0000000..6ca0fb5 --- /dev/null +++ b/src/components/SwissInvoice.tsx @@ -0,0 +1,221 @@ +import { Component, FlowComponent, JSX, Show } from "solid-js"; +import Address, { AddressData } from "./Address"; +import SwissQrCode from "./SwissQrCode"; + +export type InvoiceData = { + iban: string; + amount: number; + amountBeforeTax: number; + tax: number; + currency: string; + message?: string; + reference?: string; + referenceType?: "QRR" | "SCOR" | "NON"; + creditor: AddressData; + debtor?: AddressData; +}; + +export const formatAmount = (amount: number) => { + return amount + .toLocaleString("de-CH", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }) + .replaceAll("’", " "); +}; + +const spaceEveryX = (text: string, x = 4, reverse = false) => { + text = text.replaceAll(" ", ""); + + let result: string[] = []; + + let chars = text.split(""); + + if (reverse) { + chars = chars.reverse(); + } + + chars.forEach(function (v, i) { + if (i % x === 0) { + result.push(" "); + } + result.push(v); + }); + + if (reverse) { + result = result.reverse(); + } + + return result.join(""); +}; + +const encodeSwissQrInvoice = ( + invoiceData: InvoiceData, + { type = "SPC" } = {} +) => { + const VERSION = "0220"; // 2.20 + const CODING = 1; // utf-8 + const END_INDICATOR = "EPD"; + + const creditorType = invoiceData.creditor.type || "S"; + const debtorType = invoiceData.debtor?.type || "S"; + + const header: string[] = [type, VERSION, CODING.toString()]; + const creditor: string[] = [ + invoiceData.iban.replaceAll(" ", ""), + creditorType, + invoiceData.creditor.name, + invoiceData.creditor.line1 || "", + invoiceData.creditor.line2 || "", + invoiceData.creditor.type === "S" + ? (invoiceData.creditor.zip || 0).toString() + : "", + creditorType ? invoiceData.creditor.city || "" : "", + invoiceData.creditor.country, + ]; + const ultimateCreditor: string[] = [ + "", // type + "", // name + "", // line 1 + "", // line 2 + "", // zip + "", // city + "", // country + ]; + const amount: string[] = [ + invoiceData.amount.toFixed(2), + invoiceData.currency, + ]; + const debtor: string[] = [ + debtorType ? invoiceData.debtor?.type || "" : "", + invoiceData.debtor?.name || "", + invoiceData.debtor?.line1 || "", + invoiceData.debtor?.line2 || "", + (invoiceData.debtor?.zip || "").toString(), + invoiceData.debtor?.city || "", + invoiceData.debtor?.country || "", + ]; + const referenceType = !invoiceData.reference + ? "NON" + : invoiceData.referenceType || + (invoiceData.reference.startsWith("RF") ? "SCOR" : "QRR"); + const reference: string[] = [ + referenceType, + (invoiceData.reference || "").replaceAll(" ", ""), + ]; + + const invoice = [ + header, + creditor, + ultimateCreditor, + amount, + debtor, + reference, + invoiceData.message || "", + END_INDICATOR, + ] + .flat() + .join("\n"); + + return invoice; +}; + +const SwissInvoice: Component< + { value: InvoiceData } & JSX.HTMLAttributes +> = (props) => { + const HeaderEz: FlowComponent = (props) => ( +
{props.children}
+ ); + const HeaderZ: FlowComponent = (props) => ( +
{props.children}
+ ); + const HeaderE: FlowComponent = (props) => ( +
{props.children}
+ ); + + return ( +
+
+
+ Empfangsschein +
+
+ Konto / Zahlbar an +
{spaceEveryX(props.value.iban)}
+
+
+ +
+ Referenz +
{spaceEveryX(props.value.reference!)}
+
+
+ +
+ Zahlbar durch +
+
+
+
+
+
+
+ Währung + Betrag +
{props.value.currency}
+
{formatAmount(props.value.amount)}
+
+
+ Annahmestelle +
+
+ +
+ Zahlteil + +
+
+
+ Währung + Betrag +
{props.value.currency}
+
{formatAmount(props.value.amount)}
+
+
+ +
+ +
+
+ Konto / Zahlbar an +
{spaceEveryX(props.value.iban)}
+
+
+ +
+ Referenz +
{spaceEveryX(props.value.reference!)}
+
+
+ +
+ Zusätzliche Informationen +
{props.value.message}
+
+
+ +
+ Zahlbar durch +
+
+
+
+
+
+ ); +}; + +export default SwissInvoice;