@powerhousedao/contributor-billing 0.0.33 → 0.0.34

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 (95) hide show
  1. package/dist/document-models/invoice/gen/actions.d.ts +3 -1
  2. package/dist/document-models/invoice/gen/actions.d.ts.map +1 -1
  3. package/dist/document-models/invoice/gen/actions.js +1 -0
  4. package/dist/document-models/invoice/gen/creators.d.ts +1 -0
  5. package/dist/document-models/invoice/gen/creators.d.ts.map +1 -1
  6. package/dist/document-models/invoice/gen/creators.js +1 -0
  7. package/dist/document-models/invoice/gen/document-model.d.ts.map +1 -1
  8. package/dist/document-models/invoice/gen/document-model.js +167 -50
  9. package/dist/document-models/invoice/gen/general/actions.d.ts +4 -6
  10. package/dist/document-models/invoice/gen/general/actions.d.ts.map +1 -1
  11. package/dist/document-models/invoice/gen/general/creators.d.ts +4 -6
  12. package/dist/document-models/invoice/gen/general/creators.d.ts.map +1 -1
  13. package/dist/document-models/invoice/gen/general/creators.js +2 -4
  14. package/dist/document-models/invoice/gen/general/object.d.ts +3 -5
  15. package/dist/document-models/invoice/gen/general/object.d.ts.map +1 -1
  16. package/dist/document-models/invoice/gen/general/object.js +6 -12
  17. package/dist/document-models/invoice/gen/general/operations.d.ts +3 -5
  18. package/dist/document-models/invoice/gen/general/operations.d.ts.map +1 -1
  19. package/dist/document-models/invoice/gen/object.d.ts +3 -1
  20. package/dist/document-models/invoice/gen/object.d.ts.map +1 -1
  21. package/dist/document-models/invoice/gen/object.js +8 -1
  22. package/dist/document-models/invoice/gen/reducer.d.ts.map +1 -1
  23. package/dist/document-models/invoice/gen/reducer.js +56 -15
  24. package/dist/document-models/invoice/gen/schema/types.d.ts +89 -23
  25. package/dist/document-models/invoice/gen/schema/types.d.ts.map +1 -1
  26. package/dist/document-models/invoice/gen/schema/zod.d.ts +21 -7
  27. package/dist/document-models/invoice/gen/schema/zod.d.ts.map +1 -1
  28. package/dist/document-models/invoice/gen/schema/zod.js +124 -28
  29. package/dist/document-models/invoice/gen/transitions/actions.d.ts +16 -0
  30. package/dist/document-models/invoice/gen/transitions/actions.d.ts.map +1 -0
  31. package/dist/document-models/invoice/gen/transitions/actions.js +1 -0
  32. package/dist/document-models/invoice/gen/transitions/creators.d.ts +15 -0
  33. package/dist/document-models/invoice/gen/transitions/creators.d.ts.map +1 -0
  34. package/dist/document-models/invoice/gen/transitions/creators.js +14 -0
  35. package/dist/document-models/invoice/gen/transitions/error.d.ts +2 -0
  36. package/dist/document-models/invoice/gen/transitions/error.d.ts.map +1 -0
  37. package/dist/document-models/invoice/gen/transitions/error.js +1 -0
  38. package/dist/document-models/invoice/gen/transitions/object.d.ts +18 -0
  39. package/dist/document-models/invoice/gen/transitions/object.d.ts.map +1 -0
  40. package/dist/document-models/invoice/gen/transitions/object.js +40 -0
  41. package/dist/document-models/invoice/gen/transitions/operations.d.ts +18 -0
  42. package/dist/document-models/invoice/gen/transitions/operations.d.ts.map +1 -0
  43. package/dist/document-models/invoice/gen/transitions/operations.js +1 -0
  44. package/dist/document-models/invoice/gen/utils.d.ts.map +1 -1
  45. package/dist/document-models/invoice/gen/utils.js +10 -6
  46. package/dist/document-models/invoice/index.d.ts +14 -4
  47. package/dist/document-models/invoice/index.d.ts.map +1 -1
  48. package/dist/document-models/invoice/src/reducers/general.d.ts.map +1 -1
  49. package/dist/document-models/invoice/src/reducers/general.js +26 -42
  50. package/dist/document-models/invoice/src/reducers/transitions.d.ts +8 -0
  51. package/dist/document-models/invoice/src/reducers/transitions.d.ts.map +1 -0
  52. package/dist/document-models/invoice/src/reducers/transitions.js +162 -0
  53. package/dist/document-models/invoice/src/tests/general.test.js +10 -31
  54. package/dist/document-models/invoice/src/tests/transitions.test.d.ts +6 -0
  55. package/dist/document-models/invoice/src/tests/transitions.test.d.ts.map +1 -0
  56. package/dist/document-models/invoice/src/tests/transitions.test.js +506 -0
  57. package/dist/document-models/invoice/utils/statusTransitions.d.ts +13 -0
  58. package/dist/document-models/invoice/utils/statusTransitions.d.ts.map +1 -0
  59. package/dist/document-models/invoice/utils/statusTransitions.js +13 -0
  60. package/dist/editors/billing-statement/lineItemTags/tagMapping.js +64 -64
  61. package/dist/editors/contributor-billing/components/DriveExplorer.js +1 -1
  62. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts +3 -2
  63. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts.map +1 -1
  64. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.js +26 -3
  65. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
  66. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +11 -3
  67. package/dist/editors/invoice/components/confirmationModal.d.ts +14 -0
  68. package/dist/editors/invoice/components/confirmationModal.d.ts.map +1 -0
  69. package/dist/editors/invoice/components/confirmationModal.js +7 -0
  70. package/dist/editors/invoice/components/selectField.d.ts.map +1 -1
  71. package/dist/editors/invoice/components/selectField.js +109 -112
  72. package/dist/editors/invoice/components/statusModalComponents.d.ts +52 -0
  73. package/dist/editors/invoice/components/statusModalComponents.d.ts.map +1 -0
  74. package/dist/editors/invoice/components/statusModalComponents.js +78 -0
  75. package/dist/editors/invoice/editor.d.ts +1 -1
  76. package/dist/editors/invoice/editor.d.ts.map +1 -1
  77. package/dist/editors/invoice/editor.js +153 -158
  78. package/dist/editors/invoice/ingestUBL.d.ts.map +1 -1
  79. package/dist/editors/invoice/ingestUBL.js +0 -3
  80. package/dist/editors/invoice/invoiceToGnosis.d.ts.map +1 -1
  81. package/dist/editors/invoice/invoiceToGnosis.js +4 -3
  82. package/dist/editors/invoice/lineItemTags/tagMapping.d.ts.map +1 -1
  83. package/dist/editors/invoice/lineItemTags/tagMapping.js +65 -64
  84. package/dist/editors/invoice/validation/validationHandler.d.ts +4 -0
  85. package/dist/editors/invoice/validation/validationHandler.d.ts.map +1 -0
  86. package/dist/editors/invoice/validation/validationHandler.js +117 -0
  87. package/dist/scripts/contributor-billing/createXeroCsv.d.ts +8 -0
  88. package/dist/scripts/contributor-billing/createXeroCsv.d.ts.map +1 -0
  89. package/dist/scripts/contributor-billing/createXeroCsv.js +206 -0
  90. package/dist/style.css +181 -18
  91. package/dist/subgraphs/invoice/resolvers.d.ts.map +1 -1
  92. package/dist/subgraphs/invoice/resolvers.js +92 -22
  93. package/dist/subgraphs/invoice/schema.d.ts.map +1 -1
  94. package/dist/subgraphs/invoice/schema.js +181 -57
  95. package/package.json +1 -1
@@ -0,0 +1,117 @@
1
+ import { validateField } from "./validationManager.js";
2
+ import { toast } from "@powerhousedao/design-system";
3
+ const validateStatusBeforeContinue = (newStatus, state, setInvoiceValidation, setWalletValidation, setCurrencyValidation, setMainCountryValidation, setBankCountryValidation, setIbanValidation, setBicValidation, setBankNameValidation, setStreetAddressValidation, setCityValidation, setPostalCodeValidation, setPayerEmailValidation, setLineItemValidation, isFiatCurrency) => {
4
+ if (newStatus === "PAYMENTSCHEDULED" || newStatus === "ISSUED") {
5
+ const context = {
6
+ currency: state.currency,
7
+ currentStatus: state.status,
8
+ targetStatus: newStatus === "PAYMENTSCHEDULED" ? "ISSUED" : "ISSUED",
9
+ };
10
+ // Collect all validation errors
11
+ const validationErrors = [];
12
+ // Validate invoice number
13
+ const invoiceValidation = validateField("invoiceNo", state.invoiceNo, context);
14
+ setInvoiceValidation(invoiceValidation);
15
+ if (invoiceValidation && !invoiceValidation.isValid) {
16
+ validationErrors.push(invoiceValidation);
17
+ }
18
+ // Validate wallet address if currency is crypto
19
+ if (!isFiatCurrency(state.currency)) {
20
+ const walletValidation = validateField("address", state.issuer.paymentRouting?.wallet?.address ?? "", context);
21
+ setWalletValidation(walletValidation);
22
+ if (walletValidation && !walletValidation.isValid) {
23
+ validationErrors.push(walletValidation);
24
+ }
25
+ }
26
+ // Validate currency
27
+ const currencyValidation = validateField("currency", state.currency, context);
28
+ setCurrencyValidation(currencyValidation);
29
+ if (currencyValidation && !currencyValidation.isValid) {
30
+ validationErrors.push(currencyValidation);
31
+ }
32
+ // Validate main country
33
+ const mainCountry = state.issuer.country ?? "";
34
+ const mainCountryValidation = validateField("mainCountry", mainCountry, context);
35
+ setMainCountryValidation(mainCountryValidation);
36
+ if (mainCountryValidation && !mainCountryValidation.isValid) {
37
+ validationErrors.push(mainCountryValidation);
38
+ }
39
+ // Validate bank country
40
+ const bankCountry = state.issuer.paymentRouting?.bank?.address?.country ?? "";
41
+ const bankCountryValidation = validateField("bankCountry", bankCountry, context);
42
+ setBankCountryValidation(bankCountryValidation);
43
+ if (bankCountryValidation && !bankCountryValidation.isValid) {
44
+ validationErrors.push(bankCountryValidation);
45
+ }
46
+ // Validate EUR&GBP IBAN account number
47
+ const ibanValidation = validateField("accountNum", state.issuer.paymentRouting?.bank?.accountNum, context);
48
+ setIbanValidation(ibanValidation);
49
+ if (ibanValidation && !ibanValidation.isValid) {
50
+ validationErrors.push(ibanValidation);
51
+ }
52
+ // Validate BIC number
53
+ const bicValidation = validateField("bicNumber", state.issuer.paymentRouting?.bank?.BIC, context);
54
+ setBicValidation(bicValidation);
55
+ if (bicValidation && !bicValidation.isValid) {
56
+ validationErrors.push(bicValidation);
57
+ }
58
+ // Validate bank name
59
+ const bankNameValidation = validateField("bankName", state.issuer.paymentRouting?.bank?.name, context);
60
+ setBankNameValidation(bankNameValidation);
61
+ if (bankNameValidation && !bankNameValidation.isValid) {
62
+ validationErrors.push(bankNameValidation);
63
+ }
64
+ // Validate street address
65
+ const streetAddressValidation = validateField("streetAddress", state.issuer.address?.streetAddress, context);
66
+ setStreetAddressValidation(streetAddressValidation);
67
+ if (streetAddressValidation && !streetAddressValidation.isValid) {
68
+ validationErrors.push(streetAddressValidation);
69
+ }
70
+ // Validate city
71
+ const cityValidation = validateField("city", state.issuer.address?.city, context);
72
+ setCityValidation(cityValidation);
73
+ if (cityValidation && !cityValidation.isValid) {
74
+ validationErrors.push(cityValidation);
75
+ }
76
+ // Validate postal code
77
+ const postalCodeValidation = validateField("postalCode", state.issuer.address?.postalCode, context);
78
+ setPostalCodeValidation(postalCodeValidation);
79
+ if (postalCodeValidation && !postalCodeValidation.isValid) {
80
+ validationErrors.push(postalCodeValidation);
81
+ }
82
+ // Validate payer email
83
+ const payerEmailValidation = validateField("email", state.payer.contactInfo?.email, context);
84
+ setPayerEmailValidation(payerEmailValidation);
85
+ if (payerEmailValidation && !payerEmailValidation.isValid) {
86
+ validationErrors.push(payerEmailValidation);
87
+ }
88
+ // Validate line items
89
+ const lineItemValidation = validateField("lineItem", state.lineItems, context);
90
+ setLineItemValidation(lineItemValidation);
91
+ if (lineItemValidation && !lineItemValidation.isValid) {
92
+ validationErrors.push(lineItemValidation);
93
+ }
94
+ if (newStatus === "PAYMENTSCHEDULED" &&
95
+ !isFiatCurrency(state.currency) &&
96
+ state.issuer.paymentRouting?.wallet?.chainName === "") {
97
+ validationErrors.push({
98
+ message: "Select currency and chain before accepting invoice",
99
+ severity: "warning",
100
+ isValid: false,
101
+ });
102
+ }
103
+ // If there are any validation errors, show them and return
104
+ if (validationErrors.length > 0) {
105
+ validationErrors.forEach((error) => {
106
+ toast(error.message, {
107
+ type: error.severity === "error" ? "error" : "warning",
108
+ });
109
+ });
110
+ return true;
111
+ }
112
+ else {
113
+ return false;
114
+ }
115
+ }
116
+ };
117
+ export default validateStatusBeforeContinue;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Fetches the exchange rate from `from` currency to `to` currency for a given date using Frankfurter API.
3
+ * Returns 1 for unsupported currencies or errors.
4
+ * See: https://frankfurter.dev/
5
+ */
6
+ export declare function getExchangeRate(date: string, from: string, to: string): Promise<number>;
7
+ export declare function exportInvoicesToXeroCSV(invoiceStates: any[], baseCurrency: string): Promise<void>;
8
+ //# sourceMappingURL=createXeroCsv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createXeroCsv.d.ts","sourceRoot":"","sources":["../../../scripts/contributor-billing/createXeroCsv.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC7F;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA+LvG"}
@@ -0,0 +1,206 @@
1
+ // Simple in-memory cache: { '2024-06-10|USD|CHF': 0.91 }
2
+ const exchangeRateCache = {};
3
+ /**
4
+ * Fetches the exchange rate from `from` currency to `to` currency for a given date using Frankfurter API.
5
+ * Returns 1 for unsupported currencies or errors.
6
+ * See: https://frankfurter.dev/
7
+ */
8
+ export async function getExchangeRate(date, from, to) {
9
+ if (!date || !from || !to || from === to)
10
+ return 1;
11
+ // Use the invoice currency as base, CHF as symbol
12
+ let effectiveTo = from;
13
+ if (from === 'DAI' || from === 'USDS') {
14
+ effectiveTo = 'USD';
15
+ }
16
+ const formattedDate = date.split('T')[0];
17
+ const cacheKey = `${formattedDate}|${effectiveTo}|${to}`;
18
+ if (exchangeRateCache[cacheKey] !== undefined) {
19
+ return exchangeRateCache[cacheKey];
20
+ }
21
+ try {
22
+ // base=effectiveTo (invoice currency), symbols=CHF
23
+ const url = `https://api.frankfurter.dev/v1/${formattedDate}?base=${effectiveTo}&symbols=${to}`;
24
+ console.log('Fetching from Frankfurter URL:', url);
25
+ const res = await fetch(url);
26
+ if (!res.ok) {
27
+ throw new Error(`Failed to fetch Frankfurter exchange rate: ${res.status} ${res.statusText}`);
28
+ }
29
+ const data = await res.json();
30
+ const rate = data?.rates?.[to];
31
+ if (typeof rate !== 'number') {
32
+ throw new Error(`Frankfurter exchange rate for ${effectiveTo} to CHF on ${formattedDate} not found in response`);
33
+ }
34
+ exchangeRateCache[cacheKey] = rate;
35
+ return rate;
36
+ }
37
+ catch (err) {
38
+ console.error('Frankfurter ForEx API error:', err);
39
+ return 1; // Fallback to 1:1 on error
40
+ }
41
+ }
42
+ export async function exportInvoicesToXeroCSV(invoiceStates, baseCurrency) {
43
+ const headers = [
44
+ 'Narration',
45
+ 'Date',
46
+ 'Description',
47
+ 'AccountCode',
48
+ 'TaxRate',
49
+ 'Amount',
50
+ 'TrackingName1',
51
+ 'TrackingOption1',
52
+ 'TrackingName2',
53
+ 'TrackingOption2',
54
+ 'Invoice currency',
55
+ 'FX Rate Used',
56
+ ];
57
+ const allRows = [];
58
+ const exportDataByInvoice = {};
59
+ const exportTimestamp = new Date().toISOString();
60
+ const missingExpenseTagInvoices = [];
61
+ for (let state of invoiceStates) {
62
+ state = state.global;
63
+ const invoiceId = state.id || Math.random().toString(36).slice(2); // fallback if no id
64
+ const invoiceName = state.name || invoiceId;
65
+ const items = state.lineItems || [];
66
+ const dateIssued = state.dateIssued || '';
67
+ let datePaid = state.paymentDate || '';
68
+ if (datePaid.includes('T')) {
69
+ datePaid = datePaid.split('T')[0];
70
+ }
71
+ const narration = `${state.issuer?.name || 'Supplier'}, invoice ${state.invoiceNo || ''}`;
72
+ const currency = state.currency || 'USD';
73
+ const invoiceAmount = Number(state.totalPriceTaxIncl || 0);
74
+ let effectiveCurrency = currency;
75
+ if (currency === 'DAI' || currency === 'USDS') {
76
+ effectiveCurrency = 'USD';
77
+ }
78
+ // Check if any line item is missing a valid xero-expense-account tag
79
+ const hasMissingExpenseTag = items.some((item) => {
80
+ const expenseTag = (item.lineItemTag || []).find((tag) => tag.dimension === "xero-expense-account");
81
+ return !expenseTag || !expenseTag.label;
82
+ });
83
+ if (hasMissingExpenseTag) {
84
+ missingExpenseTagInvoices.push(invoiceName);
85
+ continue; // Skip this invoice entirely
86
+ }
87
+ // Fetch exchange rates
88
+ const [rateOnIssue,
89
+ //rateOnPayment
90
+ ] = await Promise.all([
91
+ getExchangeRate(dateIssued, effectiveCurrency, baseCurrency),
92
+ //getExchangeRate(datePaid, effectiveCurrency, 'CHF')
93
+ ]);
94
+ const amountAtIssue = invoiceAmount * rateOnIssue;
95
+ //const amountAtPayment = invoiceAmount * rateOnPayment;
96
+ //const realisedGain = amountAtPayment - amountAtIssue;
97
+ // Collect rows for this invoice
98
+ const invoiceRows = [];
99
+ // --- ISSUE DATE JOURNALS ---
100
+ if (amountAtIssue) {
101
+ invoiceRows.push([
102
+ narration,
103
+ dateIssued,
104
+ 'Accounts Payable',
105
+ '802',
106
+ 'Tax Exempt (0%)',
107
+ `-${amountAtIssue.toFixed(2)}`,
108
+ '', '', '', '',
109
+ currency,
110
+ rateOnIssue.toString()
111
+ ]);
112
+ }
113
+ items.forEach((item) => {
114
+ const expenseTag = (item.lineItemTag || []).find((tag) => tag.dimension === "xero-expense-account");
115
+ const description = expenseTag.label;
116
+ const accountCode = expenseTag.value?.toString() || item.accountCode?.toString() || '';
117
+ const taxRate = 'Tax Exempt (0%)';
118
+ const itemAmount = (item.totalPriceTaxIncl || 0) * rateOnIssue;
119
+ invoiceRows.push([
120
+ narration,
121
+ dateIssued,
122
+ description,
123
+ accountCode,
124
+ taxRate,
125
+ itemAmount.toFixed(2),
126
+ '', '', '', '',
127
+ currency,
128
+ rateOnIssue.toString()
129
+ ]);
130
+ });
131
+ // --- PAYMENT DATE JOURNALS (commented out) ---
132
+ /*
133
+ if (datePaid && amountAtPayment) {
134
+ // Accounts Payable (positive, at payment date)
135
+ rows.push([
136
+ narration,
137
+ datePaid,
138
+ 'Accounts Payable',
139
+ '802',
140
+ 'Tax Exempt (0%)',
141
+ amountAtIssue.toFixed(2),
142
+ '', '', state.txnHash || '', '',
143
+ currency,
144
+ rateOnPayment.toString()
145
+ ]);
146
+ // Wallet DAI (negative, at payment date)
147
+ rows.push([
148
+ narration,
149
+ datePaid,
150
+ 'Wallet DAI',
151
+ '',
152
+ 'Tax Exempt (0%)',
153
+ -amountAtPayment.toFixed(2),
154
+ '', '', state.txnHash || '', '',
155
+ currency,
156
+ rateOnPayment.toString()
157
+ ]);
158
+ // Realised Currency Gains (if any, at payment date)
159
+ if (Math.abs(realisedGain) > 0.01) {
160
+ rows.push([
161
+ narration,
162
+ datePaid,
163
+ 'Realised Currency Gains',
164
+ '499',
165
+ 'Tax Exempt (0%)',
166
+ realisedGain.toFixed(2),
167
+ '', '', state.txnHash || '', '',
168
+ currency,
169
+ rateOnPayment.toString()
170
+ ]);
171
+ }
172
+ }
173
+ */
174
+ // Add to allRows for CSV download
175
+ allRows.push(...invoiceRows);
176
+ // Store export data for this invoice
177
+ exportDataByInvoice[invoiceId] = {
178
+ exportTimestamp,
179
+ exportedLines: [headers, ...invoiceRows]
180
+ };
181
+ // Optionally, assign to the invoice state here if you want to mutate it directly:
182
+ // state.exportData = exportDataByInvoice[invoiceId];
183
+ }
184
+ // If any invoices are missing expense tags, throw an error
185
+ if (missingExpenseTagInvoices.length > 0) {
186
+ throw new Error(`The following invoices have line items missing a 'xero-expense-account' tag: ${[...new Set(missingExpenseTagInvoices)].join(', ')}`);
187
+ }
188
+ // Download CSV for all invoices
189
+ const csvLines = [headers.join(',')].concat(allRows.map(row => row.map(value => `"${value}"`).join(',')));
190
+ const csvData = csvLines.join('\n');
191
+ // Download logic (browser)
192
+ const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
193
+ const link = document.createElement('a');
194
+ const url = URL.createObjectURL(blob);
195
+ link.setAttribute('href', url);
196
+ link.setAttribute('download', `xero-invoice-export-${new Date().toISOString().split('T')[0]}.csv`);
197
+ link.style.visibility = 'hidden';
198
+ document.body.appendChild(link);
199
+ link.click();
200
+ document.body.removeChild(link);
201
+ // This is the data to be added to ExportData in the state of each invoice
202
+ console.log(exportDataByInvoice);
203
+ // Return or assign exportDataByInvoice as needed
204
+ // For example, return it if you want to use it elsewhere:
205
+ // return exportDataByInvoice;
206
+ }