payload-clienthub 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +90 -0
  2. package/dist/components/invoice-pdf.d.ts +60 -0
  3. package/dist/components/invoice-pdf.d.ts.map +1 -0
  4. package/dist/components/invoice-pdf.js +254 -0
  5. package/dist/components/invoice-pdf.js.map +1 -0
  6. package/dist/components/swiss-qr-bill.d.ts +74 -0
  7. package/dist/components/swiss-qr-bill.d.ts.map +1 -0
  8. package/dist/components/swiss-qr-bill.js +326 -0
  9. package/dist/components/swiss-qr-bill.js.map +1 -0
  10. package/dist/const.d.ts +12 -0
  11. package/dist/const.d.ts.map +1 -0
  12. package/dist/const.js +12 -0
  13. package/dist/const.js.map +1 -0
  14. package/dist/endpoints/process-all-clients.d.ts +4 -0
  15. package/dist/endpoints/process-all-clients.d.ts.map +1 -0
  16. package/dist/endpoints/process-all-clients.js +55 -0
  17. package/dist/endpoints/process-all-clients.js.map +1 -0
  18. package/dist/entities.d.ts +16 -0
  19. package/dist/entities.d.ts.map +1 -0
  20. package/dist/entities.js +425 -0
  21. package/dist/entities.js.map +1 -0
  22. package/dist/exports/client.d.ts +2 -0
  23. package/dist/exports/client.d.ts.map +1 -0
  24. package/dist/exports/client.js +2 -0
  25. package/dist/exports/client.js.map +1 -0
  26. package/dist/exports/rsc.d.ts +2 -0
  27. package/dist/exports/rsc.d.ts.map +1 -0
  28. package/dist/exports/rsc.js +2 -0
  29. package/dist/exports/rsc.js.map +1 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +38 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/payload-types.d.ts +459 -0
  35. package/dist/payload-types.d.ts.map +1 -0
  36. package/dist/translations.d.ts +183 -0
  37. package/dist/translations.d.ts.map +1 -0
  38. package/dist/translations.js +242 -0
  39. package/dist/translations.js.map +1 -0
  40. package/dist/types.d.ts +3 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/utils/authentication.d.ts +3 -0
  43. package/dist/utils/authentication.d.ts.map +1 -0
  44. package/dist/utils/authentication.js +5 -0
  45. package/dist/utils/authentication.js.map +1 -0
  46. package/dist/utils/process-client-invoice.d.ts +13 -0
  47. package/dist/utils/process-client-invoice.d.ts.map +1 -0
  48. package/dist/utils/process-client-invoice.js +193 -0
  49. package/dist/utils/process-client-invoice.js.map +1 -0
  50. package/package.json +84 -0
@@ -0,0 +1,242 @@
1
+ const u = {
2
+ de: {
3
+ invoice: {
4
+ title: "Rechnung",
5
+ invoiceNumber: "Rechnungsnr.",
6
+ date: "Datum",
7
+ paymentTerms: "Zahlbar bis",
8
+ paymentTermsValue: "14 Tage nach Empfang",
9
+ greetingBody: "Gerne stelle ich folgende Leistung in Rechnung.",
10
+ tableService: "Service",
11
+ tableCost: "Kosten",
12
+ tableTotal: "Total",
13
+ invoiceMessage: "Rechnung {{invoiceNumber}}",
14
+ footerCreditor: "Rechnungssteller",
15
+ footerContact: "Kontaktperson",
16
+ footerBank: "Bankverbindung",
17
+ qrPaymentPart: "Zahlteil",
18
+ qrReceipt: "Empfangsschein",
19
+ qrAccount: "Konto / Zahlbar an",
20
+ qrReference: "Referenz",
21
+ qrAdditionalInfo: "Zusätzliche Informationen",
22
+ qrPayableBy: "Zahlbar durch",
23
+ qrPayableByBlank: "Zahlbar durch (Name/Adresse)",
24
+ qrCurrency: "Währung",
25
+ qrAmount: "Betrag",
26
+ qrAcceptancePoint: "Annahmestelle",
27
+ emailSubject: "Rechnung {{invoiceNumber}}",
28
+ emailBody: "anbei die Rechnung für unsere Dienstleistungen.",
29
+ emailQuestion: "Bei Fragen stehen wir Ihnen gerne zur Verfügung.",
30
+ emailClosing: "Beste Grüsse,"
31
+ }
32
+ },
33
+ fr: {
34
+ invoice: {
35
+ title: "Facture",
36
+ invoiceNumber: "N° de facture",
37
+ date: "Date",
38
+ paymentTerms: "Payable jusqu'au",
39
+ paymentTermsValue: "14 jours après réception",
40
+ greetingBody: "Je vous prie de trouver ci-joint la facture pour nos services.",
41
+ tableService: "Service",
42
+ tableCost: "Coût",
43
+ tableTotal: "Total",
44
+ invoiceMessage: "Facture {{invoiceNumber}}",
45
+ footerCreditor: "Émetteur",
46
+ footerContact: "Contact",
47
+ footerBank: "Coordonnées bancaires",
48
+ qrPaymentPart: "Section paiement",
49
+ qrReceipt: "Récépissé",
50
+ qrAccount: "Compte / Payable à",
51
+ qrReference: "Référence",
52
+ qrAdditionalInfo: "Informations supplémentaires",
53
+ qrPayableBy: "Payable par",
54
+ qrPayableByBlank: "Payable par (nom/adresse)",
55
+ qrCurrency: "Monnaie",
56
+ qrAmount: "Montant",
57
+ qrAcceptancePoint: "Point de dépôt",
58
+ emailSubject: "Facture {{invoiceNumber}}",
59
+ emailBody: "veuillez trouver ci-joint la facture pour nos services.",
60
+ emailQuestion: "N'hésitez pas à nous contacter pour toute question.",
61
+ emailClosing: "Cordialement,"
62
+ }
63
+ },
64
+ it: {
65
+ invoice: {
66
+ title: "Fattura",
67
+ invoiceNumber: "N. fattura",
68
+ date: "Data",
69
+ paymentTerms: "Pagabile entro",
70
+ paymentTermsValue: "14 giorni dal ricevimento",
71
+ greetingBody: "Le invio in allegato la fattura per i nostri servizi.",
72
+ tableService: "Servizio",
73
+ tableCost: "Costo",
74
+ tableTotal: "Totale",
75
+ invoiceMessage: "Fattura {{invoiceNumber}}",
76
+ footerCreditor: "Emittente",
77
+ footerContact: "Contatto",
78
+ footerBank: "Coordinate bancarie",
79
+ qrPaymentPart: "Sezione pagamento",
80
+ qrReceipt: "Ricevuta",
81
+ qrAccount: "Conto / Pagabile a",
82
+ qrReference: "Riferimento",
83
+ qrAdditionalInfo: "Informazioni supplementari",
84
+ qrPayableBy: "Pagabile da",
85
+ qrPayableByBlank: "Pagabile da (nome/indirizzo)",
86
+ qrCurrency: "Valuta",
87
+ qrAmount: "Importo",
88
+ qrAcceptancePoint: "Punto di accettazione",
89
+ emailSubject: "Fattura {{invoiceNumber}}",
90
+ emailBody: "in allegato la fattura per i nostri servizi.",
91
+ emailQuestion: "Per qualsiasi domanda, non esiti a contattarci.",
92
+ emailClosing: "Cordiali saluti,"
93
+ }
94
+ },
95
+ en: {
96
+ invoice: {
97
+ title: "Invoice",
98
+ invoiceNumber: "Invoice No.",
99
+ date: "Date",
100
+ paymentTerms: "Due by",
101
+ paymentTermsValue: "14 days upon receipt",
102
+ greetingBody: "Please find attached the invoice for our services.",
103
+ tableService: "Service",
104
+ tableCost: "Cost",
105
+ tableTotal: "Total",
106
+ invoiceMessage: "Invoice {{invoiceNumber}}",
107
+ footerCreditor: "Biller",
108
+ footerContact: "Contact",
109
+ footerBank: "Bank details",
110
+ qrPaymentPart: "Payment part",
111
+ qrReceipt: "Receipt",
112
+ qrAccount: "Account / Payable to",
113
+ qrReference: "Reference",
114
+ qrAdditionalInfo: "Additional information",
115
+ qrPayableBy: "Payable by",
116
+ qrPayableByBlank: "Payable by (name/address)",
117
+ qrCurrency: "Currency",
118
+ qrAmount: "Amount",
119
+ qrAcceptancePoint: "Acceptance point",
120
+ emailSubject: "Invoice {{invoiceNumber}}",
121
+ emailBody: "Please find attached the invoice for our services.",
122
+ emailQuestion: "If you have any questions, don't hesitate to contact us.",
123
+ emailClosing: "Best regards,"
124
+ }
125
+ }
126
+ }, m = {
127
+ de: {
128
+ mr: "Herr",
129
+ ms: "Frau",
130
+ miss: "Frau",
131
+ mx: "",
132
+ dr: "Dr.",
133
+ prof: "Prof."
134
+ },
135
+ fr: {
136
+ mr: "Monsieur",
137
+ ms: "Madame",
138
+ miss: "Mademoiselle",
139
+ mx: "",
140
+ dr: "Dr",
141
+ prof: "Prof."
142
+ },
143
+ it: {
144
+ mr: "Signor",
145
+ ms: "Signora",
146
+ miss: "Signorina",
147
+ mx: "",
148
+ dr: "Dott.",
149
+ prof: "Prof."
150
+ },
151
+ en: {
152
+ mr: "Mr.",
153
+ ms: "Ms.",
154
+ miss: "Miss",
155
+ mx: "Mx.",
156
+ dr: "Dr.",
157
+ prof: "Prof."
158
+ }
159
+ }, f = {
160
+ de: { male: "Sehr geehrter", female: "Sehr geehrte", neutral: "Guten Tag" },
161
+ fr: { male: "Cher", female: "Chère", neutral: "Bonjour" },
162
+ it: { male: "Gentile", female: "Gentile", neutral: "Gentile" },
163
+ en: { male: "Dear", female: "Dear", neutral: "Dear" }
164
+ }, d = {
165
+ de: { male: "Lieber", female: "Liebe", neutral: "Hallo" },
166
+ fr: { male: "Cher", female: "Chère", neutral: "Salut" },
167
+ it: { male: "Caro", female: "Cara", neutral: "Ciao" },
168
+ en: { male: "Hi", female: "Hi", neutral: "Hi" }
169
+ };
170
+ function b(t) {
171
+ switch (t) {
172
+ case "mr":
173
+ return "male";
174
+ case "ms":
175
+ case "miss":
176
+ return "female";
177
+ default:
178
+ return "neutral";
179
+ }
180
+ }
181
+ function s(t) {
182
+ return u[t].invoice;
183
+ }
184
+ function g(t, a = {}) {
185
+ const e = s(t);
186
+ return {
187
+ emailSubject: (e.emailSubject ?? "").replace(
188
+ "{{invoiceNumber}}",
189
+ a.invoiceNumber ?? ""
190
+ ),
191
+ emailBody: e.emailBody,
192
+ emailQuestion: e.emailQuestion,
193
+ emailClosing: e.emailClosing
194
+ };
195
+ }
196
+ function p(t, a = {}) {
197
+ const e = s(t);
198
+ return {
199
+ title: e.title,
200
+ invoiceNumber: e.invoiceNumber,
201
+ date: e.date,
202
+ paymentTerms: e.paymentTerms,
203
+ paymentTermsValue: e.paymentTermsValue,
204
+ greetingBody: e.greetingBody,
205
+ tableService: e.tableService,
206
+ tableCost: e.tableCost,
207
+ tableTotal: e.tableTotal,
208
+ invoiceMessage: (e.invoiceMessage ?? "").replace(
209
+ "{{invoiceNumber}}",
210
+ a.invoiceNumber ?? ""
211
+ ),
212
+ footerCreditor: e.footerCreditor,
213
+ footerContact: e.footerContact,
214
+ footerBank: e.footerBank,
215
+ qrBill: {
216
+ paymentPart: e.qrPaymentPart,
217
+ receipt: e.qrReceipt,
218
+ account: e.qrAccount,
219
+ reference: e.qrReference,
220
+ additionalInfo: e.qrAdditionalInfo,
221
+ payableBy: e.qrPayableBy,
222
+ payableByBlank: e.qrPayableByBlank,
223
+ currency: e.qrCurrency,
224
+ amount: e.qrAmount,
225
+ acceptancePoint: e.qrAcceptancePoint
226
+ }
227
+ };
228
+ }
229
+ function v(t) {
230
+ const { language: a, salutation: e, formality: o, firstname: l, lastname: r } = t, c = b(e);
231
+ if (o === "informal")
232
+ return `${d[a][c]} ${l},`;
233
+ const n = f[a][c], i = m[a][e];
234
+ return e === "dr" || e === "prof" ? `${n} ${i} ${r},` : i ? `${n} ${i} ${r},` : `${n} ${l} ${r},`;
235
+ }
236
+ export {
237
+ v as formatGreeting,
238
+ g as getInvoiceEmailStrings,
239
+ p as getInvoicePdfLabels,
240
+ u as invoiceTranslations
241
+ };
242
+ //# sourceMappingURL=translations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.js","sources":["../src/translations.ts"],"sourcesContent":["// ============================================\n// TRANSLATIONS FOR PAYLOAD CONFIG\n// ============================================\n\nimport type { Client } from '@/payload-types';\n\n/** Invoice translations to register with Payload's i18n config */\nexport const invoiceTranslations = {\n de: {\n invoice: {\n title: 'Rechnung',\n invoiceNumber: 'Rechnungsnr.',\n date: 'Datum',\n paymentTerms: 'Zahlbar bis',\n paymentTermsValue: '14 Tage nach Empfang',\n greetingBody: 'Gerne stelle ich folgende Leistung in Rechnung.',\n tableService: 'Service',\n tableCost: 'Kosten',\n tableTotal: 'Total',\n invoiceMessage: 'Rechnung {{invoiceNumber}}',\n footerCreditor: 'Rechnungssteller',\n footerContact: 'Kontaktperson',\n footerBank: 'Bankverbindung',\n qrPaymentPart: 'Zahlteil',\n qrReceipt: 'Empfangsschein',\n qrAccount: 'Konto / Zahlbar an',\n qrReference: 'Referenz',\n qrAdditionalInfo: 'Zusätzliche Informationen',\n qrPayableBy: 'Zahlbar durch',\n qrPayableByBlank: 'Zahlbar durch (Name/Adresse)',\n qrCurrency: 'Währung',\n qrAmount: 'Betrag',\n qrAcceptancePoint: 'Annahmestelle',\n emailSubject: 'Rechnung {{invoiceNumber}}',\n emailBody: 'anbei die Rechnung für unsere Dienstleistungen.',\n emailQuestion: 'Bei Fragen stehen wir Ihnen gerne zur Verfügung.',\n emailClosing: 'Beste Grüsse,',\n },\n },\n fr: {\n invoice: {\n title: 'Facture',\n invoiceNumber: 'N° de facture',\n date: 'Date',\n paymentTerms: \"Payable jusqu'au\",\n paymentTermsValue: '14 jours après réception',\n greetingBody:\n 'Je vous prie de trouver ci-joint la facture pour nos services.',\n tableService: 'Service',\n tableCost: 'Coût',\n tableTotal: 'Total',\n invoiceMessage: 'Facture {{invoiceNumber}}',\n footerCreditor: 'Émetteur',\n footerContact: 'Contact',\n footerBank: 'Coordonnées bancaires',\n qrPaymentPart: 'Section paiement',\n qrReceipt: 'Récépissé',\n qrAccount: 'Compte / Payable à',\n qrReference: 'Référence',\n qrAdditionalInfo: 'Informations supplémentaires',\n qrPayableBy: 'Payable par',\n qrPayableByBlank: 'Payable par (nom/adresse)',\n qrCurrency: 'Monnaie',\n qrAmount: 'Montant',\n qrAcceptancePoint: 'Point de dépôt',\n emailSubject: 'Facture {{invoiceNumber}}',\n emailBody: 'veuillez trouver ci-joint la facture pour nos services.',\n emailQuestion: \"N'hésitez pas à nous contacter pour toute question.\",\n emailClosing: 'Cordialement,',\n },\n },\n it: {\n invoice: {\n title: 'Fattura',\n invoiceNumber: 'N. fattura',\n date: 'Data',\n paymentTerms: 'Pagabile entro',\n paymentTermsValue: '14 giorni dal ricevimento',\n greetingBody: 'Le invio in allegato la fattura per i nostri servizi.',\n tableService: 'Servizio',\n tableCost: 'Costo',\n tableTotal: 'Totale',\n invoiceMessage: 'Fattura {{invoiceNumber}}',\n footerCreditor: 'Emittente',\n footerContact: 'Contatto',\n footerBank: 'Coordinate bancarie',\n qrPaymentPart: 'Sezione pagamento',\n qrReceipt: 'Ricevuta',\n qrAccount: 'Conto / Pagabile a',\n qrReference: 'Riferimento',\n qrAdditionalInfo: 'Informazioni supplementari',\n qrPayableBy: 'Pagabile da',\n qrPayableByBlank: 'Pagabile da (nome/indirizzo)',\n qrCurrency: 'Valuta',\n qrAmount: 'Importo',\n qrAcceptancePoint: 'Punto di accettazione',\n emailSubject: 'Fattura {{invoiceNumber}}',\n emailBody: 'in allegato la fattura per i nostri servizi.',\n emailQuestion: 'Per qualsiasi domanda, non esiti a contattarci.',\n emailClosing: 'Cordiali saluti,',\n },\n },\n en: {\n invoice: {\n title: 'Invoice',\n invoiceNumber: 'Invoice No.',\n date: 'Date',\n paymentTerms: 'Due by',\n paymentTermsValue: '14 days upon receipt',\n greetingBody: 'Please find attached the invoice for our services.',\n tableService: 'Service',\n tableCost: 'Cost',\n tableTotal: 'Total',\n invoiceMessage: 'Invoice {{invoiceNumber}}',\n footerCreditor: 'Biller',\n footerContact: 'Contact',\n footerBank: 'Bank details',\n qrPaymentPart: 'Payment part',\n qrReceipt: 'Receipt',\n qrAccount: 'Account / Payable to',\n qrReference: 'Reference',\n qrAdditionalInfo: 'Additional information',\n qrPayableBy: 'Payable by',\n qrPayableByBlank: 'Payable by (name/address)',\n qrCurrency: 'Currency',\n qrAmount: 'Amount',\n qrAcceptancePoint: 'Acceptance point',\n emailSubject: 'Invoice {{invoiceNumber}}',\n emailBody: 'Please find attached the invoice for our services.',\n emailQuestion: \"If you have any questions, don't hesitate to contact us.\",\n emailClosing: 'Best regards,',\n },\n },\n};\n\n// ============================================\n// SALUTATION & GREETING HELPERS\n// ============================================\n\nconst salutationTitles = {\n de: {\n mr: 'Herr',\n ms: 'Frau',\n miss: 'Frau',\n mx: '',\n dr: 'Dr.',\n prof: 'Prof.',\n },\n fr: {\n mr: 'Monsieur',\n ms: 'Madame',\n miss: 'Mademoiselle',\n mx: '',\n dr: 'Dr',\n prof: 'Prof.',\n },\n it: {\n mr: 'Signor',\n ms: 'Signora',\n miss: 'Signorina',\n mx: '',\n dr: 'Dott.',\n prof: 'Prof.',\n },\n en: {\n mr: 'Mr.',\n ms: 'Ms.',\n miss: 'Miss',\n mx: 'Mx.',\n dr: 'Dr.',\n prof: 'Prof.',\n },\n} as const;\n\nconst formalGreetingPrefix = {\n de: { male: 'Sehr geehrter', female: 'Sehr geehrte', neutral: 'Guten Tag' },\n fr: { male: 'Cher', female: 'Chère', neutral: 'Bonjour' },\n it: { male: 'Gentile', female: 'Gentile', neutral: 'Gentile' },\n en: { male: 'Dear', female: 'Dear', neutral: 'Dear' },\n} as const;\n\nconst informalGreetingPrefix = {\n de: { male: 'Lieber', female: 'Liebe', neutral: 'Hallo' },\n fr: { male: 'Cher', female: 'Chère', neutral: 'Salut' },\n it: { male: 'Caro', female: 'Cara', neutral: 'Ciao' },\n en: { male: 'Hi', female: 'Hi', neutral: 'Hi' },\n} as const;\n\nfunction getGender(\n salutation: Client['contact']['salutation'],\n): 'male' | 'female' | 'neutral' {\n switch (salutation) {\n case 'mr':\n return 'male';\n case 'ms':\n case 'miss':\n return 'female';\n default:\n return 'neutral';\n }\n}\n\n/**\n * Get invoice strings in the given language.\n *\n * IMPORTANT: Use this instead of `req.t(\"invoice:*\", { lng })` because\n * Payload's t() function does not reliably respect the `lng` option to\n * override the request locale. This direct lookup ensures correct language.\n */\nfunction getInvoiceStrings(\n lng: Client['contact']['language'],\n): (typeof invoiceTranslations)['en']['invoice'] {\n return invoiceTranslations[lng].invoice;\n}\n\n/**\n * Get invoice email copy in the given language.\n * Use this for outgoing emails so body/question/closing match the client's language\n * (req.t uses request locale, which may differ when e.g. cron runs in default locale).\n */\nexport function getInvoiceEmailStrings(\n lng: Client['contact']['language'],\n vars: { invoiceNumber?: string } = {},\n): {\n emailSubject: string;\n emailBody: string;\n emailQuestion: string;\n emailClosing: string;\n} {\n const inv = getInvoiceStrings(lng);\n const subject = (inv.emailSubject ?? '').replace(\n '{{invoiceNumber}}',\n vars.invoiceNumber ?? '',\n );\n return {\n emailSubject: subject,\n emailBody: inv.emailBody,\n emailQuestion: inv.emailQuestion,\n emailClosing: inv.emailClosing,\n };\n}\n\n/**\n * Get invoice PDF labels in the given language.\n * Use this for PDF generation to ensure correct language regardless of request locale.\n */\nexport function getInvoicePdfLabels(\n lng: Client['contact']['language'],\n vars: { invoiceNumber?: string } = {},\n): {\n title: string;\n invoiceNumber: string;\n date: string;\n paymentTerms: string;\n paymentTermsValue: string;\n greetingBody: string;\n tableService: string;\n tableCost: string;\n tableTotal: string;\n invoiceMessage: string;\n footerCreditor: string;\n footerContact: string;\n footerBank: string;\n qrBill: {\n paymentPart: string;\n receipt: string;\n account: string;\n reference: string;\n additionalInfo: string;\n payableBy: string;\n payableByBlank: string;\n currency: string;\n amount: string;\n acceptancePoint: string;\n };\n} {\n const inv = getInvoiceStrings(lng);\n return {\n title: inv.title,\n invoiceNumber: inv.invoiceNumber,\n date: inv.date,\n paymentTerms: inv.paymentTerms,\n paymentTermsValue: inv.paymentTermsValue,\n greetingBody: inv.greetingBody,\n tableService: inv.tableService,\n tableCost: inv.tableCost,\n tableTotal: inv.tableTotal,\n invoiceMessage: (inv.invoiceMessage ?? '').replace(\n '{{invoiceNumber}}',\n vars.invoiceNumber ?? '',\n ),\n footerCreditor: inv.footerCreditor,\n footerContact: inv.footerContact,\n footerBank: inv.footerBank,\n qrBill: {\n paymentPart: inv.qrPaymentPart,\n receipt: inv.qrReceipt,\n account: inv.qrAccount,\n reference: inv.qrReference,\n additionalInfo: inv.qrAdditionalInfo,\n payableBy: inv.qrPayableBy,\n payableByBlank: inv.qrPayableByBlank,\n currency: inv.qrCurrency,\n amount: inv.qrAmount,\n acceptancePoint: inv.qrAcceptancePoint,\n },\n };\n}\n\n/** Format personalized greeting based on contact preferences */\nexport function formatGreeting(opts: {\n language: Client['contact']['language'];\n salutation: Client['contact']['salutation'];\n formality: Client['contact']['formality'];\n firstname: string;\n lastname: string;\n}): string {\n const { language, salutation, formality, firstname, lastname } = opts;\n const gender = getGender(salutation);\n\n if (formality === 'informal') {\n const prefix = informalGreetingPrefix[language][gender];\n return `${prefix} ${firstname},`;\n }\n\n const prefix = formalGreetingPrefix[language][gender];\n const title = salutationTitles[language][salutation];\n\n if (salutation === 'dr' || salutation === 'prof') {\n return `${prefix} ${title} ${lastname},`;\n }\n\n if (title) {\n return `${prefix} ${title} ${lastname},`;\n }\n\n return `${prefix} ${firstname} ${lastname},`;\n}\n"],"names":["invoiceTranslations","salutationTitles","formalGreetingPrefix","informalGreetingPrefix","getGender","salutation","getInvoiceStrings","lng","getInvoiceEmailStrings","vars","inv","getInvoicePdfLabels","formatGreeting","opts","language","formality","firstname","lastname","gender","prefix","title"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ClienthubPluginOptions } from '.';
2
+ export type ResolvedPluginOptions<K extends keyof ClienthubPluginOptions> = Pick<Required<ClienthubPluginOptions>, K>;
3
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,GAAG,CAAC;AAEhD,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,MAAM,sBAAsB,IACtE,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { PayloadRequest } from 'payload';
2
+ export declare const getAuthentication: (req: PayloadRequest, cronSecret: string) => "cron" | "user" | null;
3
+ //# sourceMappingURL=authentication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../src/utils/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,iBAAiB,QAAS,cAAc,cAAc,MAAM,2BAMxE,CAAC"}
@@ -0,0 +1,5 @@
1
+ const u = (e, t) => e.headers.get("authorization") === `Bearer ${t}` ? "cron" : e.user !== null ? "user" : null;
2
+ export {
3
+ u as getAuthentication
4
+ };
5
+ //# sourceMappingURL=authentication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication.js","sources":["../../src/utils/authentication.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload';\n\nexport const getAuthentication = (req: PayloadRequest, cronSecret: string) => {\n const authHeader = req.headers.get('authorization');\n const isValidCronRequest = authHeader === `Bearer ${cronSecret}`;\n if (isValidCronRequest) return 'cron';\n if (req.user !== null) return 'user';\n return null;\n};\n"],"names":["getAuthentication","req","cronSecret"],"mappings":"AAEO,MAAMA,IAAoB,CAACC,GAAqBC,MAClCD,EAAI,QAAQ,IAAI,eAAe,MACR,UAAUC,CAAU,KAC/B,SAC3BD,EAAI,SAAS,OAAa,SACvB;"}
@@ -0,0 +1,13 @@
1
+ import { PayloadRequest } from 'payload';
2
+ import { Client, PluginSettings, Service } from '../payload-types';
3
+ import { ResolvedPluginOptions } from '../types';
4
+ export declare const processClientInvoice: (req: PayloadRequest, { client, services, settings, }: {
5
+ client: Client;
6
+ services: Service[];
7
+ settings: PluginSettings;
8
+ }, options: ResolvedPluginOptions<"onError" | "cronSecret">) => Promise<{
9
+ client: string;
10
+ invoiceNumber: string;
11
+ error?: string;
12
+ } | null>;
13
+ //# sourceMappingURL=process-client-invoice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-client-invoice.d.ts","sourceRoot":"","sources":["../../src/utils/process-client-invoice.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAQ,cAAc,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAW,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AASrD,eAAO,MAAM,oBAAoB,QAC1B,cAAc,mCAKhB;IACD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,cAAc,CAAC;CAC1B,WACQ,qBAAqB,CAAC,SAAS,GAAG,YAAY,CAAC,KACvD,OAAO,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,IAAI,CAwPP,CAAC"}
@@ -0,0 +1,193 @@
1
+ import y from "dayjs";
2
+ import { generateInvoicePdf as D } from "../components/invoice-pdf.js";
3
+ import { formatGreeting as I, getInvoicePdfLabels as $, getInvoiceEmailStrings as w } from "../translations.js";
4
+ import { getAuthentication as A } from "./authentication.js";
5
+ const E = async (c, {
6
+ client: t,
7
+ services: g,
8
+ settings: n
9
+ }, p) => {
10
+ const r = y(), u = A(c, p.cronSecret) === "cron", l = [];
11
+ for (const e of g) {
12
+ if (y(e.startDate).isAfter(r)) continue;
13
+ if (e.recurrence === "one-time") {
14
+ e.lastInvoicedAt === null && l.push({
15
+ service: e,
16
+ periods: [r.format("DD.MM.YYYY")]
17
+ });
18
+ continue;
19
+ }
20
+ let o = y(e.lastInvoicedAt ?? e.startDate);
21
+ const f = [];
22
+ for (; o.isBefore(r); )
23
+ switch (e.recurrence) {
24
+ case "monthly":
25
+ if (o = o.add(1, "month"), o.isAfter(r)) break;
26
+ f.push(o.format("MMM"));
27
+ break;
28
+ case "yearly":
29
+ if (o = o.add(1, "year"), o.isAfter(r)) break;
30
+ f.push(o.format("YYYY"));
31
+ break;
32
+ }
33
+ f.length !== 0 && l.push({
34
+ service: e,
35
+ periods: f
36
+ });
37
+ }
38
+ if (l.length === 0) return null;
39
+ const b = t.contact.language, v = I(t.contact), i = `${t.code}${r.format("YYMM")}`, Y = l.reduce(
40
+ (e, { service: m, periods: o }) => e + m.cost * o.length,
41
+ 0
42
+ ), a = $(b, { invoiceNumber: i }), s = await D({
43
+ invoiceNumber: i,
44
+ date: r.format("DD.MM.YYYY"),
45
+ paymentTerms: a.paymentTermsValue,
46
+ client: {
47
+ name: t.name,
48
+ address: t.address,
49
+ zip: t.zip,
50
+ city: t.city,
51
+ country: t.country
52
+ },
53
+ company: {
54
+ name: n.company.name,
55
+ address: n.company.address,
56
+ website: n.company.website,
57
+ uid: n.company.uid,
58
+ contact: n.contact,
59
+ bank: n.bank,
60
+ zip: n.company.zip,
61
+ city: n.company.city,
62
+ country: n.company.country,
63
+ logo: n.company.logo
64
+ },
65
+ items: l.flatMap(
66
+ ({ service: e, periods: m }) => m.map((o) => ({
67
+ description: `${e.name} (${o})`,
68
+ cost: e.cost
69
+ }))
70
+ ),
71
+ total: Y,
72
+ message: a.invoiceMessage,
73
+ labels: {
74
+ title: a.title,
75
+ invoiceNumber: a.invoiceNumber,
76
+ date: a.date,
77
+ paymentTerms: a.paymentTerms,
78
+ greeting: v,
79
+ greetingBody: a.greetingBody,
80
+ tableService: a.tableService,
81
+ tableCost: a.tableCost,
82
+ tableTotal: a.tableTotal,
83
+ footerCreditor: a.footerCreditor,
84
+ footerContact: a.footerContact,
85
+ footerBank: a.footerBank,
86
+ qrBill: a.qrBill
87
+ }
88
+ }).then(
89
+ (e) => ({
90
+ data: e,
91
+ size: e.length,
92
+ name: `${a.title}_${i}.pdf`,
93
+ mimetype: "application/pdf"
94
+ })
95
+ ).catch((e) => (p.onError?.(e, {
96
+ operation: "generateInvoicePdf",
97
+ metadata: {
98
+ invoiceNumber: i,
99
+ clientName: t.name,
100
+ clientId: t.id
101
+ }
102
+ }), null));
103
+ if (s === null)
104
+ return {
105
+ client: t.name,
106
+ invoiceNumber: i,
107
+ error: "Failed to generate PDF"
108
+ };
109
+ const h = await c.payload.create({
110
+ collection: "invoice-pdfs",
111
+ data: {},
112
+ file: s,
113
+ overrideAccess: u,
114
+ req: c
115
+ }), M = {
116
+ invoiceNumber: i,
117
+ client: t.id,
118
+ items: l.flatMap(
119
+ ({ service: e, periods: m }) => m.map(() => ({
120
+ service: e.id,
121
+ cost: e.cost
122
+ }))
123
+ ),
124
+ total: Y,
125
+ date: r.format("YYYY-MM-DD"),
126
+ pdf: h.id
127
+ };
128
+ let d = !1;
129
+ try {
130
+ const e = w(b, { invoiceNumber: i }), m = `${v}
131
+
132
+ ${e.emailBody}
133
+
134
+ ${e.emailQuestion}
135
+
136
+ ${e.emailClosing}
137
+ ${n.company.name}`;
138
+ await c.payload.sendEmail({
139
+ to: t.contact.email,
140
+ subject: e.emailSubject,
141
+ text: m,
142
+ attachments: [
143
+ {
144
+ filename: s.name,
145
+ content: s.data
146
+ }
147
+ ]
148
+ }), d = !0;
149
+ } catch (e) {
150
+ p.onError?.(e, {
151
+ operation: "sendInvoiceEmail",
152
+ metadata: {
153
+ invoiceNumber: i,
154
+ clientName: t.name,
155
+ clientId: t.id,
156
+ clientEmail: t.contact.email
157
+ }
158
+ });
159
+ }
160
+ if (await c.payload.create({
161
+ collection: "invoices",
162
+ data: {
163
+ ...M,
164
+ status: d ? "sent" : "generated",
165
+ sentAt: d ? r.toISOString() : void 0
166
+ },
167
+ overrideAccess: u,
168
+ req: c
169
+ }), !d)
170
+ return {
171
+ client: t.name,
172
+ invoiceNumber: i,
173
+ error: "Failed to send email"
174
+ };
175
+ for (const { service: e } of l)
176
+ await c.payload.update({
177
+ collection: "services",
178
+ id: e.id,
179
+ data: {
180
+ lastInvoicedAt: r.format("YYYY-MM-DD")
181
+ },
182
+ overrideAccess: u,
183
+ req: c
184
+ });
185
+ return {
186
+ client: t.name,
187
+ invoiceNumber: i
188
+ };
189
+ };
190
+ export {
191
+ E as processClientInvoice
192
+ };
193
+ //# sourceMappingURL=process-client-invoice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-client-invoice.js","sources":["../../src/utils/process-client-invoice.ts"],"sourcesContent":["import dayjs from 'dayjs';\n\nimport type { File, PayloadRequest } from 'payload';\nimport type { Client, Invoice, PluginSettings, Service } from '@/payload-types';\nimport type { ResolvedPluginOptions } from '@/types';\nimport { generateInvoicePdf } from '../components/invoice-pdf';\nimport {\n formatGreeting,\n getInvoiceEmailStrings,\n getInvoicePdfLabels,\n} from '../translations';\nimport { getAuthentication } from './authentication';\n\nexport const processClientInvoice = async (\n req: PayloadRequest,\n {\n client,\n services,\n settings,\n }: {\n client: Client;\n services: Service[];\n settings: PluginSettings;\n },\n options: ResolvedPluginOptions<'onError' | 'cronSecret'>,\n): Promise<{\n client: string;\n invoiceNumber: string;\n error?: string;\n} | null> => {\n const now = dayjs();\n const overrideAccess = getAuthentication(req, options.cronSecret) === 'cron';\n\n const billableItems: Array<{\n service: Pick<Service, 'id' | 'name' | 'cost' | 'recurrence'>;\n periods: string[];\n }> = [];\n\n // MARK: Gather billable items\n for (const service of services) {\n const startDate = dayjs(service.startDate);\n if (startDate.isAfter(now)) continue;\n\n if (service.recurrence === 'one-time') {\n if (service.lastInvoicedAt === null) {\n billableItems.push({\n service,\n periods: [now.format('DD.MM.YYYY')],\n });\n }\n continue;\n }\n\n let lastInvoicedAt = dayjs(service.lastInvoicedAt ?? service.startDate);\n\n const periods: string[] = [];\n\n while (lastInvoicedAt.isBefore(now)) {\n switch (service.recurrence) {\n case 'monthly':\n lastInvoicedAt = lastInvoicedAt.add(1, 'month');\n if (lastInvoicedAt.isAfter(now)) break;\n periods.push(lastInvoicedAt.format('MMM'));\n break;\n case 'yearly':\n lastInvoicedAt = lastInvoicedAt.add(1, 'year');\n if (lastInvoicedAt.isAfter(now)) break;\n periods.push(lastInvoicedAt.format('YYYY'));\n break;\n }\n }\n if (periods.length === 0) continue;\n billableItems.push({\n service,\n periods,\n });\n }\n\n if (billableItems.length === 0) return null;\n\n // MARK: i18n – contact preferences\n // Use direct translation lookup instead of req.t() because Payload's t()\n // doesn't reliably respect the { lng } option to override request locale.\n const lng = client.contact.language;\n const greeting = formatGreeting(client.contact);\n\n // MARK: Generate PDF\n\n const invoiceNumber = `${client.code}${now.format('YYMM')}`;\n const total = billableItems.reduce(\n (sum, { service, periods }) => sum + service.cost * periods.length,\n 0,\n );\n\n // Get labels in client's language with invoice number for interpolated strings\n const labels = getInvoicePdfLabels(lng, { invoiceNumber });\n\n const pdfFile = await generateInvoicePdf({\n invoiceNumber,\n date: now.format('DD.MM.YYYY'),\n paymentTerms: labels.paymentTermsValue,\n client: {\n name: client.name,\n address: client.address,\n zip: client.zip,\n city: client.city,\n country: client.country,\n },\n company: {\n name: settings.company.name,\n address: settings.company.address,\n website: settings.company.website,\n uid: settings.company.uid,\n contact: settings.contact,\n bank: settings.bank,\n zip: settings.company.zip,\n city: settings.company.city,\n country: settings.company.country,\n logo: settings.company.logo,\n },\n items: billableItems.flatMap(({ service, periods }) =>\n periods.map((period) => ({\n description: `${service.name} (${period})`,\n cost: service.cost,\n })),\n ),\n total,\n message: labels.invoiceMessage,\n labels: {\n title: labels.title,\n invoiceNumber: labels.invoiceNumber,\n date: labels.date,\n paymentTerms: labels.paymentTerms,\n greeting,\n greetingBody: labels.greetingBody,\n tableService: labels.tableService,\n tableCost: labels.tableCost,\n tableTotal: labels.tableTotal,\n footerCreditor: labels.footerCreditor,\n footerContact: labels.footerContact,\n footerBank: labels.footerBank,\n qrBill: labels.qrBill,\n },\n })\n .then(\n (data): File => ({\n data,\n size: data.length,\n name: `${labels.title}_${invoiceNumber}.pdf`,\n mimetype: 'application/pdf',\n }),\n )\n .catch((error) => {\n options.onError?.(error as Error, {\n operation: 'generateInvoicePdf',\n metadata: {\n invoiceNumber,\n clientName: client.name,\n clientId: client.id,\n },\n });\n return null;\n });\n\n if (pdfFile === null)\n return {\n client: client.name,\n invoiceNumber,\n error: 'Failed to generate PDF',\n };\n\n // START TRANSACTION\n\n // MARK: Store PDF\n\n const mediaDoc = await req.payload.create({\n collection: 'invoice-pdfs',\n data: {},\n file: pdfFile,\n overrideAccess,\n req,\n });\n\n const invoiceData: Pick<\n Invoice,\n 'invoiceNumber' | 'client' | 'items' | 'total' | 'date' | 'pdf'\n > = {\n invoiceNumber,\n client: client.id,\n items: billableItems.flatMap(({ service, periods }) =>\n periods.map(() => ({\n service: service.id,\n cost: service.cost,\n })),\n ),\n total,\n date: now.format('YYYY-MM-DD'),\n pdf: mediaDoc.id,\n };\n\n // MARK: Create Invoice\n\n // MARK: Send Email\n\n let emailSent = false;\n\n try {\n const emailStrings = getInvoiceEmailStrings(lng, { invoiceNumber });\n const text = `${greeting}\n\n${emailStrings.emailBody}\n\n${emailStrings.emailQuestion}\n\n${emailStrings.emailClosing}\n${settings.company.name}`;\n await req.payload.sendEmail({\n to: client.contact.email,\n subject: emailStrings.emailSubject,\n text,\n attachments: [\n {\n filename: pdfFile.name,\n content: pdfFile.data,\n },\n ],\n });\n emailSent = true;\n } catch (error) {\n options.onError?.(error as Error, {\n operation: 'sendInvoiceEmail',\n metadata: {\n invoiceNumber,\n clientName: client.name,\n clientId: client.id,\n clientEmail: client.contact.email,\n },\n });\n }\n\n await req.payload.create({\n collection: 'invoices',\n data: {\n ...invoiceData,\n status: emailSent ? 'sent' : 'generated',\n sentAt: emailSent ? now.toISOString() : undefined,\n },\n overrideAccess,\n req,\n });\n\n if (!emailSent) {\n return {\n client: client.name,\n invoiceNumber,\n error: 'Failed to send email',\n };\n }\n\n // MARK: Mark Services as Invoiced\n\n for (const { service } of billableItems) {\n await req.payload.update({\n collection: 'services',\n id: service.id,\n data: {\n lastInvoicedAt: now.format('YYYY-MM-DD'),\n },\n overrideAccess,\n req,\n });\n }\n\n return {\n client: client.name,\n invoiceNumber,\n };\n};\n"],"names":["processClientInvoice","req","client","services","settings","options","now","dayjs","overrideAccess","getAuthentication","billableItems","service","lastInvoicedAt","periods","lng","greeting","formatGreeting","invoiceNumber","total","sum","labels","getInvoicePdfLabels","pdfFile","generateInvoicePdf","period","data","error","mediaDoc","invoiceData","emailSent","emailStrings","getInvoiceEmailStrings","text"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "payload-clienthub",
3
+ "version": "0.0.1",
4
+ "description": "Payload CMS plugin for client management, service tracking, and invoicing.",
5
+ "keywords": [
6
+ "payload"
7
+ ],
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/davincicoding-org/payload-plugins.git",
11
+ "directory": "packages/clienthub"
12
+ },
13
+ "license": "MIT",
14
+ "author": "DAVINCI CODING GmbH",
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./rsc": {
23
+ "types": "./dist/exports/rsc.d.ts",
24
+ "import": "./dist/exports/rsc.js",
25
+ "default": "./dist/exports/rsc.js"
26
+ },
27
+ "./client": {
28
+ "types": "./dist/exports/client.d.ts",
29
+ "import": "./dist/exports/client.js",
30
+ "default": "./dist/exports/client.js"
31
+ },
32
+ "./styles.css": "./dist/styles.css"
33
+ },
34
+ "main": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "prebuild": "pnpm typecheck",
41
+ "build": "plugin-build",
42
+ "clean": "rm -rf dist && rm -rf node_modules",
43
+ "dev": "plugin-build --watch",
44
+ "generate:types": "generate-types",
45
+ "lint": "biome check .",
46
+ "lint:fix": "biome check --write .",
47
+ "test": "vitest run",
48
+ "test:watch": "vitest",
49
+ "typecheck": "tsc --noEmit"
50
+ },
51
+ "dependencies": {
52
+ "@react-pdf/renderer": "^4.3.2",
53
+ "dayjs": "^1.11.19",
54
+ "qrcode": "^1.5.4"
55
+ },
56
+ "devDependencies": {
57
+ "@payloadcms/ui": "catalog:payload-stack",
58
+ "@repo/common": "workspace:*",
59
+ "@types/node": "^22.5.4",
60
+ "@types/qrcode": "^1.5.6",
61
+ "@types/react": "catalog:payload-stack",
62
+ "@types/react-dom": "catalog:payload-stack",
63
+ "next": "catalog:payload-stack",
64
+ "payload": "catalog:payload-stack",
65
+ "react": "catalog:payload-stack",
66
+ "react-dom": "catalog:payload-stack",
67
+ "typescript": "catalog:payload-stack",
68
+ "vite": "catalog:vite",
69
+ "vitest": "^3.1.2"
70
+ },
71
+ "peerDependencies": {
72
+ "@payloadcms/next": ">=3.72.0",
73
+ "@payloadcms/ui": ">=3.72.0",
74
+ "next": ">=15.2.3",
75
+ "payload": ">=3.72.0"
76
+ },
77
+ "engines": {
78
+ "node": "^18.20.2 || >=20.9.0",
79
+ "pnpm": "^9 || ^10"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public"
83
+ }
84
+ }