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" } = {} ) => { // This code implements version 2.2! But the guidelines require that this stays fixed as 2.0 ;) const VERSION = "0200"; const CODING = 1; // utf-8 const LINE_BREAK = "\n"; 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(LINE_BREAK); 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}
); // Important: pt-px was added because: break-inside-avoid with the top-border results in a wrong pagebreak in chrome, // the top-border remained on the first page and only the rest of the invoice was on the second page. // AND: this only happend if scroll position was at the bottom. // FIXME: Chrome should fix the break-inside-avoid calculation, when chrome has fixed their issue the pt-px can be removed. 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;