@powerhousedao/contributor-billing 0.1.26 → 0.1.29

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.
@@ -0,0 +1,184 @@
1
+ import type { InvoiceState } from "../../document-models/invoice/index.js";
2
+ /**
3
+ * SAF-T PT (Standard Audit File for Tax - Portugal) Export
4
+ * Format version: 1.04_01
5
+ * Reference: Portaria n.º 302/2016
6
+ */
7
+ export interface SAFTPTExportOptions {
8
+ invoice: InvoiceState;
9
+ filename?: string;
10
+ /**
11
+ * Software certification details (required for certified billing software in Portugal)
12
+ * If not provided, placeholder values will be used
13
+ */
14
+ softwareInfo?: {
15
+ productCompanyTaxID: string;
16
+ softwareCertificateNumber: string;
17
+ productID: string;
18
+ productVersion: string;
19
+ };
20
+ /**
21
+ * ATCUD - Código Único de Documento
22
+ * Required for Portuguese invoices since 2022
23
+ * Format: VALIDATION_CODE-SEQUENCE_NUMBER
24
+ */
25
+ atcud?: string;
26
+ /**
27
+ * Hash and HashControl for digital signature
28
+ * Required for certified software
29
+ */
30
+ hashInfo?: {
31
+ hash: string;
32
+ hashControl: string;
33
+ };
34
+ /**
35
+ * Source/Operator ID
36
+ */
37
+ sourceId?: string;
38
+ /**
39
+ * Economic Activity Code (CAE)
40
+ */
41
+ eacCode?: string;
42
+ /**
43
+ * Tax accounting basis
44
+ * F - Invoicing, C - Accounting, I - Integrated
45
+ */
46
+ taxAccountingBasis?: "F" | "C" | "I" | "S" | "R" | "P";
47
+ /**
48
+ * Tax entity (usually "Global" or "Sede")
49
+ */
50
+ taxEntity?: string;
51
+ /**
52
+ * Fiscal year info - if not provided, derived from invoice date
53
+ */
54
+ fiscalPeriod?: {
55
+ fiscalYear: number;
56
+ startDate: string;
57
+ endDate: string;
58
+ };
59
+ /**
60
+ * Header comment
61
+ */
62
+ headerComment?: string;
63
+ /**
64
+ * Customer ID for internal reference
65
+ */
66
+ customerId?: string;
67
+ /**
68
+ * Tax exemption details (for 0% VAT)
69
+ */
70
+ taxExemption?: {
71
+ reason: string;
72
+ code: string;
73
+ };
74
+ }
75
+ export declare class SAFTPTExporter {
76
+ private invoice;
77
+ private options;
78
+ constructor(options: SAFTPTExportOptions);
79
+ /**
80
+ * Convert the invoice state to SAF-T PT XML format
81
+ * @returns SAF-T PT XML string
82
+ */
83
+ convertInvoiceToSAFTPT(): string;
84
+ /**
85
+ * Generate the Header section
86
+ */
87
+ private generateHeader;
88
+ /**
89
+ * Generate company address block
90
+ */
91
+ private generateCompanyAddress;
92
+ /**
93
+ * Generate the MasterFiles section
94
+ */
95
+ private generateMasterFiles;
96
+ /**
97
+ * Generate customer entry
98
+ */
99
+ private generateCustomer;
100
+ /**
101
+ * Generate products from line items
102
+ */
103
+ private generateProducts;
104
+ /**
105
+ * Generate tax table with all Portuguese tax rates (PT, PT-MA, PT-AC regions)
106
+ * This includes the complete tax table as required by SAF-T PT specification
107
+ */
108
+ private generateTaxTable;
109
+ /**
110
+ * Get Portuguese tax code based on rate
111
+ */
112
+ private getTaxCode;
113
+ /**
114
+ * Generate the SourceDocuments section
115
+ */
116
+ private generateSourceDocuments;
117
+ /**
118
+ * Generate single invoice entry
119
+ */
120
+ private generateInvoice;
121
+ /**
122
+ * Generate invoice line items
123
+ */
124
+ private generateInvoiceLines;
125
+ /**
126
+ * Generate a single invoice line
127
+ */
128
+ private generateInvoiceLine;
129
+ /**
130
+ * Format invoice number for SAF-T PT format
131
+ * Converts "Invoice-123" to "FT 2025/123"
132
+ */
133
+ private formatInvoiceNo;
134
+ /**
135
+ * Extract sequence number from invoice number
136
+ */
137
+ private extractSequenceNumber;
138
+ /**
139
+ * Extract tax ID from legal entity
140
+ */
141
+ private extractTaxId;
142
+ /**
143
+ * Format date as YYYY-MM-DD
144
+ */
145
+ private formatDate;
146
+ /**
147
+ * Format datetime as YYYY-MM-DDTHH:MM:SS
148
+ */
149
+ private formatDateTime;
150
+ /**
151
+ * Get last day of month
152
+ */
153
+ private getLastDayOfMonth;
154
+ /**
155
+ * Escape special XML characters
156
+ */
157
+ private escapeXml;
158
+ /**
159
+ * Export to file
160
+ */
161
+ exportToFile(filename?: string): File;
162
+ /**
163
+ * Trigger download in browser
164
+ */
165
+ downloadSAFTPT(filename?: string): void;
166
+ }
167
+ /**
168
+ * Export an invoice to SAF-T PT format
169
+ * @param options Export options
170
+ * @returns The generated XML file
171
+ */
172
+ export declare function exportToSAFTPT(options: SAFTPTExportOptions): File;
173
+ /**
174
+ * Export and download an invoice as SAF-T PT
175
+ * @param options Export options
176
+ */
177
+ export declare function downloadSAFTPT(options: SAFTPTExportOptions): void;
178
+ /**
179
+ * Convert invoice to SAF-T PT XML string
180
+ * @param options Export options
181
+ * @returns SAF-T PT XML string
182
+ */
183
+ export declare function convertToSAFTPTXml(options: SAFTPTExportOptions): string;
184
+ //# sourceMappingURL=exportSAFTPT.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportSAFTPT.d.ts","sourceRoot":"","sources":["../../../editors/invoice/exportSAFTPT.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,548 @@
1
+ // Helper function to format numbers with appropriate decimal places
2
+ function formatNumber(value, decimals = 2) {
3
+ return value.toFixed(decimals);
4
+ }
5
+ // Helper function to format numbers with high precision for unit prices
6
+ function formatUnitPrice(value) {
7
+ return value.toFixed(7);
8
+ }
9
+ // Portuguese month names
10
+ const PORTUGUESE_MONTHS = [
11
+ "Janeiro",
12
+ "Fevereiro",
13
+ "Março",
14
+ "Abril",
15
+ "Maio",
16
+ "Junho",
17
+ "Julho",
18
+ "Agosto",
19
+ "Setembro",
20
+ "Outubro",
21
+ "Novembro",
22
+ "Dezembro",
23
+ ];
24
+ // Helper function to get Portuguese month name
25
+ function getPortugueseMonth(monthIndex) {
26
+ return PORTUGUESE_MONTHS[monthIndex] || "";
27
+ }
28
+ export class SAFTPTExporter {
29
+ invoice;
30
+ options;
31
+ constructor(options) {
32
+ this.invoice = options.invoice;
33
+ this.options = options;
34
+ }
35
+ /**
36
+ * Convert the invoice state to SAF-T PT XML format
37
+ * @returns SAF-T PT XML string
38
+ */
39
+ convertInvoiceToSAFTPT() {
40
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
41
+ <AuditFile xmlns="urn:OECD:StandardAuditFile-Tax:PT_1.04_01">
42
+ ${this.generateHeader()}
43
+ ${this.generateMasterFiles()}
44
+ ${this.generateSourceDocuments()}
45
+ </AuditFile>`;
46
+ return xml;
47
+ }
48
+ /**
49
+ * Generate the Header section
50
+ */
51
+ generateHeader() {
52
+ const issuer = this.invoice.issuer;
53
+ const taxId = this.extractTaxId(issuer);
54
+ const issueDate = new Date(this.invoice.dateIssued);
55
+ // Derive fiscal period from invoice date if not provided
56
+ const fiscalYear = this.options.fiscalPeriod?.fiscalYear || issueDate.getFullYear();
57
+ const month = issueDate.getMonth() + 1;
58
+ const startDate = this.options.fiscalPeriod?.startDate ||
59
+ `${fiscalYear}-${String(month).padStart(2, "0")}-01`;
60
+ const endDate = this.options.fiscalPeriod?.endDate ||
61
+ this.getLastDayOfMonth(fiscalYear, month);
62
+ const dateCreated = this.formatDate(new Date().toISOString());
63
+ // Default software info from TOConline (Portuguese SAF-T example)
64
+ const softwareInfo = this.options.softwareInfo || {
65
+ productCompanyTaxID: "503692310",
66
+ softwareCertificateNumber: "1662",
67
+ productID: "TOConline/Ordem dos Contabilistas Certificados",
68
+ productVersion: "3.0.4988",
69
+ };
70
+ // Generate HeaderComment with Portuguese month name if not provided
71
+ const portugueseMonth = getPortugueseMonth(issueDate.getMonth());
72
+ const headerComment = this.options.headerComment || `SAF-T ${portugueseMonth} ${fiscalYear}`;
73
+ return `<Header>
74
+ <AuditFileVersion>1.04_01</AuditFileVersion>
75
+ <CompanyID>${this.escapeXml(taxId)}</CompanyID>
76
+ <TaxRegistrationNumber>${this.escapeXml(taxId)}</TaxRegistrationNumber>
77
+ <TaxAccountingBasis>${this.options.taxAccountingBasis || "F"}</TaxAccountingBasis>
78
+ <CompanyName>${this.escapeXml(issuer.name || "")}</CompanyName>
79
+ <BusinessName>${this.escapeXml(issuer.name || "")}</BusinessName>
80
+ ${this.generateCompanyAddress(issuer)}
81
+ <FiscalYear>${fiscalYear}</FiscalYear>
82
+ <StartDate>${startDate}</StartDate>
83
+ <EndDate>${endDate}</EndDate>
84
+ <CurrencyCode>${this.escapeXml(this.invoice.currency || "EUR")}</CurrencyCode>
85
+ <DateCreated>${dateCreated}</DateCreated>
86
+ <TaxEntity>${this.escapeXml(this.options.taxEntity || "Global")}</TaxEntity>
87
+ <ProductCompanyTaxID>${this.escapeXml(softwareInfo.productCompanyTaxID)}</ProductCompanyTaxID>
88
+ <SoftwareCertificateNumber>${this.escapeXml(softwareInfo.softwareCertificateNumber)}</SoftwareCertificateNumber>
89
+ <ProductID>${this.escapeXml(softwareInfo.productID)}</ProductID>
90
+ <ProductVersion>${this.escapeXml(softwareInfo.productVersion)}</ProductVersion>
91
+ <HeaderComment>${this.escapeXml(headerComment)}</HeaderComment>
92
+ ${issuer.contactInfo?.email ? `<Email>${this.escapeXml(issuer.contactInfo.email)}</Email>` : ""}
93
+ </Header>`;
94
+ }
95
+ /**
96
+ * Generate company address block
97
+ */
98
+ generateCompanyAddress(entity) {
99
+ const address = entity.address;
100
+ const addressDetail = [
101
+ address?.streetAddress,
102
+ address?.extendedAddress,
103
+ ].filter(Boolean).join(", ");
104
+ return `<CompanyAddress>
105
+ <AddressDetail>${this.escapeXml(addressDetail || "Desconhecido")}</AddressDetail>
106
+ <City>${this.escapeXml(address?.city || "Desconhecido")}</City>
107
+ <PostalCode>${this.escapeXml(address?.postalCode || "Desconhecido")}</PostalCode>
108
+ <Country>${this.escapeXml(entity.country || "PT")}</Country>
109
+ </CompanyAddress>`;
110
+ }
111
+ /**
112
+ * Generate the MasterFiles section
113
+ */
114
+ generateMasterFiles() {
115
+ return `<MasterFiles>
116
+ ${this.generateCustomer()}
117
+ ${this.generateProducts()}
118
+ ${this.generateTaxTable()}
119
+ </MasterFiles>`;
120
+ }
121
+ /**
122
+ * Generate customer entry
123
+ */
124
+ generateCustomer() {
125
+ const payer = this.invoice.payer;
126
+ const taxId = this.extractTaxId(payer);
127
+ const customerId = this.options.customerId || "1";
128
+ const addressDetail = [
129
+ payer.address?.streetAddress,
130
+ payer.address?.extendedAddress,
131
+ ].filter(Boolean).join("\n") || "Desconhecido";
132
+ return `<Customer>
133
+ <CustomerID>${this.escapeXml(customerId)}</CustomerID>
134
+ <AccountID>Desconhecido</AccountID>
135
+ <CustomerTaxID>${this.escapeXml(taxId || "Consumidor Final")}</CustomerTaxID>
136
+ <CompanyName>${this.escapeXml(payer.name || "Consumidor Final")}</CompanyName>
137
+ <BillingAddress>
138
+ <AddressDetail>${this.escapeXml(addressDetail)}</AddressDetail>
139
+ <City>${this.escapeXml(payer.address?.city || "Desconhecido")}</City>
140
+ <PostalCode>${this.escapeXml(payer.address?.postalCode || "Desconhecido")}</PostalCode>
141
+ <Country>${this.escapeXml(payer.country || "PT")}</Country>
142
+ </BillingAddress>
143
+ <ShipToAddress>
144
+ <AddressDetail>${this.escapeXml(addressDetail)}</AddressDetail>
145
+ <City>${this.escapeXml(payer.address?.city || "Desconhecido")}</City>
146
+ <PostalCode>${this.escapeXml(payer.address?.postalCode || "Desconhecido")}</PostalCode>
147
+ <Country>${this.escapeXml(payer.country || "PT")}</Country>
148
+ </ShipToAddress>
149
+ <SelfBillingIndicator>0</SelfBillingIndicator>
150
+ </Customer>`;
151
+ }
152
+ /**
153
+ * Generate products from line items
154
+ */
155
+ generateProducts() {
156
+ // Create unique product entries from line items
157
+ const productMap = new Map();
158
+ this.invoice.lineItems.forEach((item, index) => {
159
+ const code = String(index + 1).padStart(3, "0");
160
+ if (!productMap.has(item.description)) {
161
+ productMap.set(item.description, {
162
+ code,
163
+ description: item.description,
164
+ });
165
+ }
166
+ });
167
+ // If no products, add a default service product
168
+ if (productMap.size === 0) {
169
+ productMap.set("Serviços", {
170
+ code: "001",
171
+ description: "Prestação de Serviços",
172
+ });
173
+ }
174
+ return Array.from(productMap.values())
175
+ .map((product) => `<Product>
176
+ <ProductType>S</ProductType>
177
+ <ProductCode>${this.escapeXml(product.code)}</ProductCode>
178
+ <ProductDescription>${this.escapeXml(product.description)}</ProductDescription>
179
+ <ProductNumberCode>${this.escapeXml(product.code)}</ProductNumberCode>
180
+ </Product>`)
181
+ .join("\n ");
182
+ }
183
+ /**
184
+ * Generate tax table with all Portuguese tax rates (PT, PT-MA, PT-AC regions)
185
+ * This includes the complete tax table as required by SAF-T PT specification
186
+ */
187
+ generateTaxTable() {
188
+ // Complete Portuguese tax table with all regions (Continental, Madeira, Azores)
189
+ return `<TaxTable>
190
+ <TaxTableEntry>
191
+ <TaxType>IVA</TaxType>
192
+ <TaxCountryRegion>PT</TaxCountryRegion>
193
+ <TaxCode>NOR</TaxCode>
194
+ <Description>Normal</Description>
195
+ <TaxPercentage>23</TaxPercentage>
196
+ </TaxTableEntry>
197
+ <TaxTableEntry>
198
+ <TaxType>IVA</TaxType>
199
+ <TaxCountryRegion>PT</TaxCountryRegion>
200
+ <TaxCode>INT</TaxCode>
201
+ <Description>Intermédia</Description>
202
+ <TaxPercentage>13</TaxPercentage>
203
+ </TaxTableEntry>
204
+ <TaxTableEntry>
205
+ <TaxType>IVA</TaxType>
206
+ <TaxCountryRegion>PT</TaxCountryRegion>
207
+ <TaxCode>RED</TaxCode>
208
+ <Description>Reduzida</Description>
209
+ <TaxPercentage>6</TaxPercentage>
210
+ </TaxTableEntry>
211
+ <TaxTableEntry>
212
+ <TaxType>IVA</TaxType>
213
+ <TaxCountryRegion>PT</TaxCountryRegion>
214
+ <TaxCode>ISE</TaxCode>
215
+ <Description>Isenta</Description>
216
+ <TaxPercentage>0</TaxPercentage>
217
+ </TaxTableEntry>
218
+ <TaxTableEntry>
219
+ <TaxType>IVA</TaxType>
220
+ <TaxCountryRegion>PT-MA</TaxCountryRegion>
221
+ <TaxCode>NOR</TaxCode>
222
+ <Description>Normal</Description>
223
+ <TaxPercentage>22</TaxPercentage>
224
+ </TaxTableEntry>
225
+ <TaxTableEntry>
226
+ <TaxType>IVA</TaxType>
227
+ <TaxCountryRegion>PT-MA</TaxCountryRegion>
228
+ <TaxCode>INT</TaxCode>
229
+ <Description>Intermédia</Description>
230
+ <TaxPercentage>12</TaxPercentage>
231
+ </TaxTableEntry>
232
+ <TaxTableEntry>
233
+ <TaxType>IVA</TaxType>
234
+ <TaxCountryRegion>PT-MA</TaxCountryRegion>
235
+ <TaxCode>ISE</TaxCode>
236
+ <Description>Isenta</Description>
237
+ <TaxPercentage>0</TaxPercentage>
238
+ </TaxTableEntry>
239
+ <TaxTableEntry>
240
+ <TaxType>IVA</TaxType>
241
+ <TaxCountryRegion>PT-AC</TaxCountryRegion>
242
+ <TaxCode>ISE</TaxCode>
243
+ <Description>Isenta</Description>
244
+ <TaxPercentage>0</TaxPercentage>
245
+ </TaxTableEntry>
246
+ <TaxTableEntry>
247
+ <TaxType>IVA</TaxType>
248
+ <TaxCountryRegion>PT-AC</TaxCountryRegion>
249
+ <TaxCode>INT</TaxCode>
250
+ <Description>Intermédia</Description>
251
+ <TaxPercentage>9</TaxPercentage>
252
+ </TaxTableEntry>
253
+ <TaxTableEntry>
254
+ <TaxType>IVA</TaxType>
255
+ <TaxCountryRegion>PT-AC</TaxCountryRegion>
256
+ <TaxCode>RED</TaxCode>
257
+ <Description>Reduzida</Description>
258
+ <TaxPercentage>4</TaxPercentage>
259
+ </TaxTableEntry>
260
+ <TaxTableEntry>
261
+ <TaxType>IVA</TaxType>
262
+ <TaxCountryRegion>PT-AC</TaxCountryRegion>
263
+ <TaxCode>NOR</TaxCode>
264
+ <Description>Normal</Description>
265
+ <TaxPercentage>16</TaxPercentage>
266
+ </TaxTableEntry>
267
+ <TaxTableEntry>
268
+ <TaxType>IVA</TaxType>
269
+ <TaxCountryRegion>PT-MA</TaxCountryRegion>
270
+ <TaxCode>RED</TaxCode>
271
+ <Description>Reduzida</Description>
272
+ <TaxPercentage>4</TaxPercentage>
273
+ </TaxTableEntry>
274
+ </TaxTable>`;
275
+ }
276
+ /**
277
+ * Get Portuguese tax code based on rate
278
+ */
279
+ getTaxCode(rate) {
280
+ if (rate === 0)
281
+ return "ISE";
282
+ if (rate === 6 || rate === 4)
283
+ return "RED";
284
+ if (rate === 13 || rate === 12 || rate === 9)
285
+ return "INT";
286
+ if (rate === 23 || rate === 22 || rate === 16)
287
+ return "NOR";
288
+ return "ISE"; // Default to exempt for unknown rates
289
+ }
290
+ /**
291
+ * Generate the SourceDocuments section
292
+ */
293
+ generateSourceDocuments() {
294
+ const totalCredit = this.invoice.lineItems.reduce((sum, item) => sum + item.totalPriceTaxIncl, 0);
295
+ return `<SourceDocuments>
296
+ <SalesInvoices>
297
+ <NumberOfEntries>1</NumberOfEntries>
298
+ <TotalDebit>0.00</TotalDebit>
299
+ <TotalCredit>${formatNumber(totalCredit)}</TotalCredit>
300
+ ${this.generateInvoice()}
301
+ </SalesInvoices>
302
+ <MovementOfGoods>
303
+ <NumberOfMovementLines>0</NumberOfMovementLines>
304
+ <TotalQuantityIssued>0</TotalQuantityIssued>
305
+ </MovementOfGoods>
306
+ <WorkingDocuments>
307
+ <NumberOfEntries>0</NumberOfEntries>
308
+ <TotalDebit>0.00</TotalDebit>
309
+ <TotalCredit>0.00</TotalCredit>
310
+ </WorkingDocuments>
311
+ <Payments>
312
+ <NumberOfEntries>0</NumberOfEntries>
313
+ <TotalDebit>0.00</TotalDebit>
314
+ <TotalCredit>0.00</TotalCredit>
315
+ </Payments>
316
+ </SourceDocuments>`;
317
+ }
318
+ /**
319
+ * Generate single invoice entry
320
+ */
321
+ generateInvoice() {
322
+ const invoiceDate = this.formatDate(this.invoice.dateIssued);
323
+ const invoiceDateTime = this.formatDateTime(this.invoice.dateIssued);
324
+ const period = new Date(this.invoice.dateIssued).getMonth() + 1;
325
+ // Format invoice number for SAF-T (e.g., "FT 2025/1")
326
+ const invoiceNo = this.formatInvoiceNo(this.invoice.invoiceNo);
327
+ // ATCUD - use provided or generate based on example format (JJBDDFHD-sequence)
328
+ const sequenceNum = this.extractSequenceNumber(this.invoice.invoiceNo);
329
+ const atcud = this.options.atcud || `JJBDDFHD-${sequenceNum}`;
330
+ // Hash info - placeholder values (real hash requires certified software)
331
+ const hashInfo = this.options.hashInfo || {
332
+ hash: "Sn7zDVVOxNcAcW74Yd1b9YBHuwloo5y59NCW0MGBiBkuUz1fpLe8748ODBeFg5++Uzo2qNPfnzTOagpfe1IO3FZ54PJ65RquXuJ3TUc1rJMlEA0O89NGIVPbnbMQbk8CLFBkEolJoqfoqFHHc/GIedejFEyf+KrjhjsmxG/WvI4=",
333
+ hashControl: "1",
334
+ };
335
+ // Default source ID from example
336
+ const sourceId = this.options.sourceId || "919456";
337
+ const customerId = this.options.customerId || "1";
338
+ // Default EAC code from example (63110 = Data processing, hosting and related activities)
339
+ const eacCode = this.options.eacCode || "63110";
340
+ // Calculate totals
341
+ const netTotal = this.invoice.lineItems.reduce((sum, item) => sum + item.totalPriceTaxExcl, 0);
342
+ const grossTotal = this.invoice.lineItems.reduce((sum, item) => sum + item.totalPriceTaxIncl, 0);
343
+ const taxPayable = grossTotal - netTotal;
344
+ return `<Invoice>
345
+ <InvoiceNo>${this.escapeXml(invoiceNo)}</InvoiceNo>
346
+ <ATCUD>${this.escapeXml(atcud)}</ATCUD>
347
+ <DocumentStatus>
348
+ <InvoiceStatus>N</InvoiceStatus>
349
+ <InvoiceStatusDate>${invoiceDateTime}</InvoiceStatusDate>
350
+ <SourceID>${this.escapeXml(sourceId)}</SourceID>
351
+ <SourceBilling>P</SourceBilling>
352
+ </DocumentStatus>
353
+ <Hash>${this.escapeXml(hashInfo.hash)}</Hash>
354
+ <HashControl>${this.escapeXml(hashInfo.hashControl)}</HashControl>
355
+ <Period>${period}</Period>
356
+ <InvoiceDate>${invoiceDate}</InvoiceDate>
357
+ <InvoiceType>FT</InvoiceType>
358
+ <SpecialRegimes>
359
+ <SelfBillingIndicator>0</SelfBillingIndicator>
360
+ <CashVATSchemeIndicator>0</CashVATSchemeIndicator>
361
+ <ThirdPartiesBillingIndicator>0</ThirdPartiesBillingIndicator>
362
+ </SpecialRegimes>
363
+ <SourceID>${this.escapeXml(sourceId)}</SourceID>
364
+ <EACCode>${this.escapeXml(eacCode)}</EACCode>
365
+ <SystemEntryDate>${invoiceDateTime}</SystemEntryDate>
366
+ <CustomerID>${this.escapeXml(customerId)}</CustomerID>
367
+ ${this.generateInvoiceLines()}
368
+ <DocumentTotals>
369
+ <TaxPayable>${formatNumber(taxPayable, 1)}</TaxPayable>
370
+ <NetTotal>${formatNumber(netTotal)}</NetTotal>
371
+ <GrossTotal>${formatNumber(grossTotal)}</GrossTotal>
372
+ </DocumentTotals>
373
+ </Invoice>`;
374
+ }
375
+ /**
376
+ * Generate invoice line items
377
+ */
378
+ generateInvoiceLines() {
379
+ return this.invoice.lineItems
380
+ .map((item, index) => this.generateInvoiceLine(item, index + 1))
381
+ .join("\n ");
382
+ }
383
+ /**
384
+ * Generate a single invoice line
385
+ */
386
+ generateInvoiceLine(item, lineNumber) {
387
+ const taxPointDate = this.formatDate(this.invoice.dateIssued);
388
+ const taxCode = this.getTaxCode(item.taxPercent);
389
+ const productCode = String(lineNumber).padStart(3, "0");
390
+ // Tax exemption info (for 0% VAT) - using default from Portuguese SAF-T example
391
+ const taxExemption = item.taxPercent === 0 ? this.options.taxExemption || {
392
+ reason: "Outras situações de não liquidação do imposto (Exemplos: art",
393
+ code: "M99",
394
+ } : null;
395
+ return `<Line>
396
+ <LineNumber>${lineNumber}</LineNumber>
397
+ <ProductCode>${productCode}</ProductCode>
398
+ <ProductDescription>${this.escapeXml(item.description)}</ProductDescription>
399
+ <Quantity>${formatNumber(item.quantity)}</Quantity>
400
+ <UnitOfMeasure>UN</UnitOfMeasure>
401
+ <UnitPrice>${formatUnitPrice(item.unitPriceTaxExcl)}</UnitPrice>
402
+ <TaxPointDate>${taxPointDate}</TaxPointDate>
403
+ <Description>${this.escapeXml(item.description)}</Description>
404
+ <CreditAmount>${formatNumber(item.totalPriceTaxExcl)}</CreditAmount>
405
+ <Tax>
406
+ <TaxType>IVA</TaxType>
407
+ <TaxCountryRegion>PT</TaxCountryRegion>
408
+ <TaxCode>${taxCode}</TaxCode>
409
+ <TaxPercentage>${item.taxPercent}</TaxPercentage>
410
+ </Tax>
411
+ ${taxExemption ? `<TaxExemptionReason>${this.escapeXml(taxExemption.reason)}</TaxExemptionReason>
412
+ <TaxExemptionCode>${this.escapeXml(taxExemption.code)}</TaxExemptionCode>` : ""}
413
+ </Line>`;
414
+ }
415
+ /**
416
+ * Format invoice number for SAF-T PT format
417
+ * Converts "Invoice-123" to "FT 2025/123"
418
+ */
419
+ formatInvoiceNo(invoiceNo) {
420
+ // Check if already in SAF-T format
421
+ if (/^[A-Z]{2}\s\d{4}\/\d+$/.test(invoiceNo)) {
422
+ return invoiceNo;
423
+ }
424
+ // Extract sequence number
425
+ const sequenceMatch = invoiceNo.match(/\d+/);
426
+ const sequence = sequenceMatch ? sequenceMatch[0] : "1";
427
+ const year = new Date(this.invoice.dateIssued).getFullYear();
428
+ return `FT ${year}/${sequence}`;
429
+ }
430
+ /**
431
+ * Extract sequence number from invoice number
432
+ */
433
+ extractSequenceNumber(invoiceNo) {
434
+ const match = invoiceNo.match(/\d+/);
435
+ return match ? match[0] : "1";
436
+ }
437
+ /**
438
+ * Extract tax ID from legal entity
439
+ */
440
+ extractTaxId(entity) {
441
+ if (!entity?.id)
442
+ return "";
443
+ const taxId = entity.id?.taxId;
444
+ const corpRegId = entity.id?.corpRegId;
445
+ // Remove country prefix if present (e.g., "PT515947504" -> "515947504")
446
+ const id = taxId || corpRegId || "";
447
+ return id.replace(/^[A-Z]{2}/, "");
448
+ }
449
+ /**
450
+ * Format date as YYYY-MM-DD
451
+ */
452
+ formatDate(dateString) {
453
+ if (!dateString)
454
+ return "";
455
+ try {
456
+ const date = new Date(dateString);
457
+ if (isNaN(date.getTime()))
458
+ return "";
459
+ return date.toISOString().split("T")[0];
460
+ }
461
+ catch {
462
+ return "";
463
+ }
464
+ }
465
+ /**
466
+ * Format datetime as YYYY-MM-DDTHH:MM:SS
467
+ */
468
+ formatDateTime(dateString) {
469
+ if (!dateString)
470
+ return "";
471
+ try {
472
+ const date = new Date(dateString);
473
+ if (isNaN(date.getTime()))
474
+ return "";
475
+ return date.toISOString().replace(/\.\d{3}Z$/, "");
476
+ }
477
+ catch {
478
+ return "";
479
+ }
480
+ }
481
+ /**
482
+ * Get last day of month
483
+ */
484
+ getLastDayOfMonth(year, month) {
485
+ const lastDay = new Date(year, month, 0).getDate();
486
+ return `${year}-${String(month).padStart(2, "0")}-${String(lastDay).padStart(2, "0")}`;
487
+ }
488
+ /**
489
+ * Escape special XML characters
490
+ */
491
+ escapeXml(str) {
492
+ return str
493
+ .replace(/&/g, "&amp;")
494
+ .replace(/</g, "&lt;")
495
+ .replace(/>/g, "&gt;")
496
+ .replace(/"/g, "&quot;")
497
+ .replace(/'/g, "&apos;");
498
+ }
499
+ /**
500
+ * Export to file
501
+ */
502
+ exportToFile(filename) {
503
+ const xml = this.convertInvoiceToSAFTPT();
504
+ const finalFilename = filename || this.options.filename || "saft-pt.xml";
505
+ const blob = new Blob([xml], { type: "application/xml" });
506
+ return new File([blob], finalFilename, { type: "application/xml" });
507
+ }
508
+ /**
509
+ * Trigger download in browser
510
+ */
511
+ downloadSAFTPT(filename) {
512
+ const xml = this.convertInvoiceToSAFTPT();
513
+ const finalFilename = filename || this.options.filename || "saft-pt.xml";
514
+ const blob = new Blob([xml], { type: "application/xml" });
515
+ const link = document.createElement("a");
516
+ link.href = URL.createObjectURL(blob);
517
+ link.download = finalFilename;
518
+ document.body.appendChild(link);
519
+ link.click();
520
+ document.body.removeChild(link);
521
+ }
522
+ }
523
+ /**
524
+ * Export an invoice to SAF-T PT format
525
+ * @param options Export options
526
+ * @returns The generated XML file
527
+ */
528
+ export function exportToSAFTPT(options) {
529
+ const exporter = new SAFTPTExporter(options);
530
+ return exporter.exportToFile();
531
+ }
532
+ /**
533
+ * Export and download an invoice as SAF-T PT
534
+ * @param options Export options
535
+ */
536
+ export function downloadSAFTPT(options) {
537
+ const exporter = new SAFTPTExporter(options);
538
+ exporter.downloadSAFTPT();
539
+ }
540
+ /**
541
+ * Convert invoice to SAF-T PT XML string
542
+ * @param options Export options
543
+ * @returns SAF-T PT XML string
544
+ */
545
+ export function convertToSAFTPTXml(options) {
546
+ const exporter = new SAFTPTExporter(options);
547
+ return exporter.convertInvoiceToSAFTPT();
548
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"bankSection.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/legalEntity/bankSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAO3B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAM3E,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,qBAAqB,CAAC,KAAK,CAAC,EAC5B,UAAU,CACX,GAAG;IACF,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmCF,eAAO,MAAM,sBAAsB,qIAoiBlC,CAAC"}
1
+ {"version":3,"file":"bankSection.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/legalEntity/bankSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAO3B,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAO3E,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,qBAAqB,CAAC,KAAK,CAAC,EAC5B,UAAU,CACX,GAAG;IACF,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmCF,eAAO,MAAM,sBAAsB,qIAglBlC,CAAC"}
@@ -1,10 +1,11 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef, useCallback, useState, useEffect, useMemo, } from "react";
3
3
  import { twMerge } from "tailwind-merge";
4
4
  import { CountryForm } from "../components/countryForm.js";
5
5
  import { InputField } from "../components/inputField.js";
6
6
  import { Select } from "@powerhousedao/document-engineering";
7
7
  import { isValidIBAN } from "../validation/validationRules.js";
8
+ import { STATE_PROVINCE_OPTIONS } from "./legalEntity.js";
8
9
  const ACCOUNT_TYPES = ["CHECKING", "SAVINGS", "TRUST"];
9
10
  function flattenBankInput(value) {
10
11
  return {
@@ -129,11 +130,15 @@ export const LegalEntityBankSection = forwardRef(function LegalEntityBankSection
129
130
  // input={localState.city ?? ""}
130
131
  , {
131
132
  // input={localState.city ?? ""}
132
- value: localState.city ?? "", label: "City", placeholder: "City", onBlur: createBlurHandler("city"), handleInputChange: createInputHandler("city"), className: "h-10 w-full text-md mb-2" }), _jsx(InputField
133
- // input={localState.stateProvince ?? ""}
134
- , {
133
+ value: localState.city ?? "", label: "City", placeholder: "City", onBlur: createBlurHandler("city"), handleInputChange: createInputHandler("city"), className: "h-10 w-full text-md mb-2" }), _jsx("div", { className: "space-y-2", children: localState.country === "US" ? (_jsxs(_Fragment, { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "State/Province" }), _jsx(Select, { options: STATE_PROVINCE_OPTIONS, value: localState.stateProvince ?? "", onChange: (value) => {
134
+ createBlurHandler("stateProvince")({
135
+ target: { value: value },
136
+ });
137
+ }, className: "h-10 w-full text-md mb-2", searchable: true })] })) : (_jsx(InputField
135
138
  // input={localState.stateProvince ?? ""}
136
- value: localState.stateProvince ?? "", label: "State/Province", placeholder: "State/Province", onBlur: createBlurHandler("stateProvince"), handleInputChange: createInputHandler("stateProvince"), className: "h-10 w-full text-md mb-2" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(InputField
139
+ , {
140
+ // input={localState.stateProvince ?? ""}
141
+ value: localState.stateProvince ?? "", label: "State/Province", placeholder: "State/Province", onBlur: createBlurHandler("stateProvince"), handleInputChange: createInputHandler("stateProvince"), className: "h-10 w-full text-md mb-2" })) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(InputField
137
142
  // input={localState.postalCode ?? ""}
138
143
  , {
139
144
  // input={localState.postalCode ?? ""}
@@ -182,11 +187,15 @@ export const LegalEntityBankSection = forwardRef(function LegalEntityBankSection
182
187
  // input={localState.cityIntermediary ?? ""}
183
188
  , {
184
189
  // input={localState.cityIntermediary ?? ""}
185
- value: localState.cityIntermediary ?? "", label: "City", placeholder: "City", onBlur: createBlurHandler("cityIntermediary"), handleInputChange: createInputHandler("cityIntermediary"), className: "h-10 w-full text-md mb-2" }), _jsx(InputField
186
- // input={localState.stateProvinceIntermediary ?? ""}
187
- , {
188
- // input={localState.stateProvinceIntermediary ?? ""}
189
- value: localState.stateProvinceIntermediary ?? "", label: "State/Province", placeholder: "State/Province", onBlur: createBlurHandler("stateProvinceIntermediary"), handleInputChange: createInputHandler("stateProvinceIntermediary"), className: "h-10 w-full text-md mb-2" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(InputField
190
+ value: localState.cityIntermediary ?? "", label: "City", placeholder: "City", onBlur: createBlurHandler("cityIntermediary"), handleInputChange: createInputHandler("cityIntermediary"), className: "h-10 w-full text-md mb-2" }), _jsx("div", { className: "space-y-2", children: localState.countryIntermediary === "US" ? (_jsxs(_Fragment, { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "State/Province" }), _jsx(Select, { options: STATE_PROVINCE_OPTIONS, value: localState.stateProvinceIntermediary ?? "", onChange: (value) => {
191
+ createBlurHandler("stateProvinceIntermediary")({
192
+ target: { value: value },
193
+ });
194
+ }, className: "h-10 w-full text-md mb-2", searchable: true })] })) : (_jsx(InputField
195
+ // input={localState.stateProvince ?? ""}
196
+ , {
197
+ // input={localState.stateProvince ?? ""}
198
+ value: localState.stateProvinceIntermediary ?? "", label: "State/Province", placeholder: "State/Province", onBlur: createBlurHandler("stateProvinceIntermediary"), handleInputChange: createInputHandler("stateProvince"), className: "h-10 w-full text-md mb-2" })) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(InputField
190
199
  // input={localState.postalCodeIntermediary ?? ""}
191
200
  , {
192
201
  // input={localState.postalCodeIntermediary ?? ""}
@@ -4,6 +4,10 @@ import type { ValidationResult } from "../validation/validationManager.js";
4
4
  export type EditLegalEntityWalletInput = EditIssuerWalletInput | EditPayerWalletInput;
5
5
  export type EditLegalEntityBankInput = EditIssuerBankInput | EditPayerBankInput;
6
6
  export type EditLegalEntityInput = EditIssuerInput | EditPayerInput;
7
+ export declare const STATE_PROVINCE_OPTIONS: {
8
+ label: string;
9
+ value: string;
10
+ }[];
7
11
  export type LegalEntityMainSectionProps = Omit<ComponentPropsWithRef<"div">, "children"> & {
8
12
  readonly value: EditLegalEntityInput;
9
13
  readonly onChange: (value: EditLegalEntityInput) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"legalEntity.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/legalEntity/legalEntity.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"legalEntity.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/legalEntity/legalEntity.tsx"],"names":[],"mappings":"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"}
@@ -1,11 +1,71 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, {} from "react";
3
3
  import { twMerge } from "tailwind-merge";
4
4
  import { LegalEntityWalletSection } from "./walletSection.js";
5
5
  import { LegalEntityBankSection } from "./bankSection.js";
6
6
  import { CountryForm } from "../components/countryForm.js";
7
7
  import { InputField } from "../components/inputField.js";
8
+ import { Select } from "@powerhousedao/document-engineering";
8
9
  const FieldLabel = ({ children }) => (_jsx("label", { className: "block text-sm font-medium text-gray-700", children: children }));
10
+ export const STATE_PROVINCE_OPTIONS = [
11
+ { label: "Alabama", value: "AL" },
12
+ { label: "Alaska", value: "AK" },
13
+ { label: "Arizona", value: "AZ" },
14
+ { label: "Arkansas", value: "AR" },
15
+ { label: "American Samoa", value: "AS" },
16
+ { label: "California", value: "CA" },
17
+ { label: "Colorado", value: "CO" },
18
+ { label: "Connecticut", value: "CT" },
19
+ { label: "Delaware", value: "DE" },
20
+ { label: "District of Columbia", value: "DC" },
21
+ { label: "Florida", value: "FL" },
22
+ { label: "Georgia", value: "GA" },
23
+ { label: "Guam", value: "GU" },
24
+ { label: "Hawaii", value: "HI" },
25
+ { label: "Idaho", value: "ID" },
26
+ { label: "Illinois", value: "IL" },
27
+ { label: "Indiana", value: "IN" },
28
+ { label: "Iowa", value: "IA" },
29
+ { label: "Kansas", value: "KS" },
30
+ { label: "Kentucky", value: "KY" },
31
+ { label: "Louisiana", value: "LA" },
32
+ { label: "Maine", value: "ME" },
33
+ { label: "Maryland", value: "MD" },
34
+ { label: "Massachusetts", value: "MA" },
35
+ { label: "Michigan", value: "MI" },
36
+ { label: "Minnesota", value: "MN" },
37
+ { label: "Mississippi", value: "MS" },
38
+ { label: "Missouri", value: "MO" },
39
+ { label: "Montana", value: "MT" },
40
+ { label: "Nebraska", value: "NE" },
41
+ { label: "Nevada", value: "NV" },
42
+ { label: "New Hampshire", value: "NH" },
43
+ { label: "New Jersey", value: "NJ" },
44
+ { label: "New Mexico", value: "NM" },
45
+ { label: "New York", value: "NY" },
46
+ { label: "North Carolina", value: "NC" },
47
+ { label: "North Dakota", value: "ND" },
48
+ { label: "Northern Mariana Islands", value: "MP" },
49
+ { label: "Ohio", value: "OH" },
50
+ { label: "Oklahoma", value: "OK" },
51
+ { label: "Oregon", value: "OR" },
52
+ { label: "Pennsylvania", value: "PA" },
53
+ { label: "Puerto Rico", value: "PR" },
54
+ { label: "Rhode Island", value: "RI" },
55
+ { label: "South Carolina", value: "SC" },
56
+ { label: "South Dakota", value: "SD" },
57
+ { label: "Tennessee", value: "TN" },
58
+ { label: "Texas", value: "TX" },
59
+ { label: "Trust Territories", value: "TT" },
60
+ { label: "Utah", value: "UT" },
61
+ { label: "Vermont", value: "VT" },
62
+ { label: "Virgin Islands", value: "VI" },
63
+ { label: "Virginia", value: "VA" },
64
+ { label: "Washington", value: "WA" },
65
+ { label: "West Virginia", value: "WV" },
66
+ { label: "Wisconsin", value: "WI" },
67
+ { label: "Wyoming", value: "WY" },
68
+ ];
9
69
  export const LegalEntityMainSection = (props) => {
10
70
  const { value, onChange, disabled, mainCountryValidation, bankCountryValidation, streetaddressvalidation, cityvalidation, postalcodevalidation, payeremailvalidation, ...divProps } = props;
11
71
  const handleInputChange = (field) => (e) => {
@@ -24,7 +84,11 @@ export const LegalEntityMainSection = (props) => {
24
84
  onChange({ [field]: e.target.value });
25
85
  }
26
86
  };
27
- return (_jsxs("div", { ...divProps, className: twMerge("rounded-lg border border-gray-200 bg-white p-6 mb-2", props.className), children: [_jsx("h3", { className: "mb-4 text-lg font-semibold text-gray-900", children: "Basic Information" }), _jsxs("div", { className: "space-y-6", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.name ?? "", label: "Name", placeholder: "Legal Entity Name", onBlur: handleTextareaBlur("name"), handleInputChange: handleTextareaChange("name"), className: "h-10 w-full text-md mb-2" }) }), _jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.id ?? "", label: "Tax ID / Corp. Reg", placeholder: "332...", onBlur: handleTextareaBlur("id"), handleInputChange: handleTextareaChange("id"), className: "h-10 w-full text-md mb-2" }) }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-4", children: [_jsx(InputField, { value: value.streetAddress ?? "", label: "Address", placeholder: "Street Address", onBlur: handleTextareaBlur("streetAddress"), handleInputChange: handleTextareaChange("streetAddress"), className: "h-10 w-full text-md mb-2", validation: streetaddressvalidation }), _jsx(InputField, { value: value.extendedAddress ?? "", placeholder: "Extended Address", onBlur: handleTextareaBlur("extendedAddress"), handleInputChange: handleTextareaChange("extendedAddress"), className: "h-10 w-full text-md mb-2" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.city ?? "", label: "City", placeholder: "City", onBlur: handleTextareaBlur("city"), handleInputChange: handleTextareaChange("city"), className: "h-10 w-full text-md mb-2", validation: cityvalidation }) }), _jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.stateProvince ?? "", label: "State/Province", placeholder: "State/Province", onBlur: handleTextareaBlur("stateProvince"), handleInputChange: handleTextareaChange("stateProvince"), className: "h-10 w-full text-md mb-2" }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.postalCode ?? "", label: "Postal Code", placeholder: "Postal Code", onBlur: handleTextareaBlur("postalCode"), handleInputChange: handleTextareaChange("postalCode"), className: "h-10 w-full text-md mb-2", validation: postalcodevalidation }) }), _jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { children: "Country" }), _jsx(CountryForm, { country: value.country ?? "", handleInputChange: handleInputChange("country"), handleBlur: handleBlur("country"), className: "h-10 w-full text-md mb-2", validation: mainCountryValidation })] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.email ?? "", label: "Email", placeholder: "Email", onBlur: handleTextareaBlur("email"), handleInputChange: handleTextareaChange("email"), className: "h-10 w-full text-md mb-2", validation: payeremailvalidation }) }), _jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.tel ?? "", label: "Telephone", placeholder: "Telephone", onBlur: handleTextareaBlur("tel"), handleInputChange: handleTextareaChange("tel"), className: "h-10 w-full text-md mb-2" }) })] })] })] }));
87
+ return (_jsxs("div", { ...divProps, className: twMerge("rounded-lg border border-gray-200 bg-white p-6 mb-2", props.className), children: [_jsx("h3", { className: "mb-4 text-lg font-semibold text-gray-900", children: "Basic Information" }), _jsxs("div", { className: "space-y-6", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.name ?? "", label: "Name", placeholder: "Legal Entity Name", onBlur: handleTextareaBlur("name"), handleInputChange: handleTextareaChange("name"), className: "h-10 w-full text-md mb-2" }) }), _jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.id ?? "", label: "Tax ID / Corp. Reg", placeholder: "332...", onBlur: handleTextareaBlur("id"), handleInputChange: handleTextareaChange("id"), className: "h-10 w-full text-md mb-2" }) }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-4", children: [_jsx(InputField, { value: value.streetAddress ?? "", label: "Address", placeholder: "Street Address", onBlur: handleTextareaBlur("streetAddress"), handleInputChange: handleTextareaChange("streetAddress"), className: "h-10 w-full text-md mb-2", validation: streetaddressvalidation }), _jsx(InputField, { value: value.extendedAddress ?? "", placeholder: "Extended Address", onBlur: handleTextareaBlur("extendedAddress"), handleInputChange: handleTextareaChange("extendedAddress"), className: "h-10 w-full text-md mb-2" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.city ?? "", label: "City", placeholder: "City", onBlur: handleTextareaBlur("city"), handleInputChange: handleTextareaChange("city"), className: "h-10 w-full text-md mb-2", validation: cityvalidation }) }), _jsx("div", { className: "space-y-2", children: value.country === "US" ? (_jsxs(_Fragment, { children: [_jsx("label", { className: "mb-2 block text-sm font-medium text-gray-700", children: "State/Province" }), _jsx(Select, { options: STATE_PROVINCE_OPTIONS, value: value.stateProvince ?? "", onChange: (value) => {
88
+ handleBlur("stateProvince")({
89
+ target: { value: value },
90
+ });
91
+ }, className: "h-10 w-full text-md mb-2", searchable: true })] })) : (_jsx(InputField, { value: value.stateProvince ?? "", label: "State/Province", placeholder: "State/Province", onBlur: handleTextareaBlur("stateProvince"), handleInputChange: handleTextareaChange("stateProvince"), className: "h-10 w-full text-md mb-2" })) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.postalCode ?? "", label: "Postal Code", placeholder: "Postal Code", onBlur: handleTextareaBlur("postalCode"), handleInputChange: handleTextareaChange("postalCode"), className: "h-10 w-full text-md mb-2", validation: postalcodevalidation }) }), _jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { children: "Country" }), _jsx(CountryForm, { country: value.country ?? "", handleInputChange: handleInputChange("country"), handleBlur: handleBlur("country"), className: "h-10 w-full text-md mb-2", validation: mainCountryValidation })] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.email ?? "", label: "Email", placeholder: "Email", onBlur: handleTextareaBlur("email"), handleInputChange: handleTextareaChange("email"), className: "h-10 w-full text-md mb-2", validation: payeremailvalidation }) }), _jsx("div", { className: "space-y-2", children: _jsx(InputField, { value: value.tel ?? "", label: "Telephone", placeholder: "Telephone", onBlur: handleTextareaBlur("tel"), handleInputChange: handleTextareaChange("tel"), className: "h-10 w-full text-md mb-2" }) })] })] })] }));
28
92
  };
29
93
  // Helper to flatten LegalEntity to EditLegalEntityInput
30
94
  function flattenLegalEntityToEditInput(legalEntity) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/contributor-billing",
3
3
  "description": "Document models that help contributors of open organisations get paid anonymously for their work on a monthly basis.",
4
- "version": "0.1.26",
4
+ "version": "0.1.29",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [