@powerhousedao/contributor-billing 0.0.33 → 0.0.35
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.
- package/dist/document-models/invoice/gen/actions.d.ts +3 -1
- package/dist/document-models/invoice/gen/actions.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/actions.js +1 -0
- package/dist/document-models/invoice/gen/creators.d.ts +1 -0
- package/dist/document-models/invoice/gen/creators.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/creators.js +1 -0
- package/dist/document-models/invoice/gen/document-model.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/document-model.js +167 -50
- package/dist/document-models/invoice/gen/general/actions.d.ts +4 -6
- package/dist/document-models/invoice/gen/general/actions.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/general/creators.d.ts +4 -6
- package/dist/document-models/invoice/gen/general/creators.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/general/creators.js +2 -4
- package/dist/document-models/invoice/gen/general/object.d.ts +3 -5
- package/dist/document-models/invoice/gen/general/object.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/general/object.js +6 -12
- package/dist/document-models/invoice/gen/general/operations.d.ts +3 -5
- package/dist/document-models/invoice/gen/general/operations.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/object.d.ts +3 -1
- package/dist/document-models/invoice/gen/object.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/object.js +8 -1
- package/dist/document-models/invoice/gen/reducer.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/reducer.js +56 -15
- package/dist/document-models/invoice/gen/schema/types.d.ts +89 -23
- package/dist/document-models/invoice/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/schema/zod.d.ts +21 -7
- package/dist/document-models/invoice/gen/schema/zod.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/schema/zod.js +124 -28
- package/dist/document-models/invoice/gen/transitions/actions.d.ts +16 -0
- package/dist/document-models/invoice/gen/transitions/actions.d.ts.map +1 -0
- package/dist/document-models/invoice/gen/transitions/actions.js +1 -0
- package/dist/document-models/invoice/gen/transitions/creators.d.ts +15 -0
- package/dist/document-models/invoice/gen/transitions/creators.d.ts.map +1 -0
- package/dist/document-models/invoice/gen/transitions/creators.js +14 -0
- package/dist/document-models/invoice/gen/transitions/error.d.ts +2 -0
- package/dist/document-models/invoice/gen/transitions/error.d.ts.map +1 -0
- package/dist/document-models/invoice/gen/transitions/error.js +1 -0
- package/dist/document-models/invoice/gen/transitions/object.d.ts +18 -0
- package/dist/document-models/invoice/gen/transitions/object.d.ts.map +1 -0
- package/dist/document-models/invoice/gen/transitions/object.js +40 -0
- package/dist/document-models/invoice/gen/transitions/operations.d.ts +18 -0
- package/dist/document-models/invoice/gen/transitions/operations.d.ts.map +1 -0
- package/dist/document-models/invoice/gen/transitions/operations.js +1 -0
- package/dist/document-models/invoice/gen/utils.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/utils.js +10 -6
- package/dist/document-models/invoice/index.d.ts +14 -4
- package/dist/document-models/invoice/index.d.ts.map +1 -1
- package/dist/document-models/invoice/src/reducers/general.d.ts.map +1 -1
- package/dist/document-models/invoice/src/reducers/general.js +26 -42
- package/dist/document-models/invoice/src/reducers/transitions.d.ts +8 -0
- package/dist/document-models/invoice/src/reducers/transitions.d.ts.map +1 -0
- package/dist/document-models/invoice/src/reducers/transitions.js +162 -0
- package/dist/document-models/invoice/src/tests/general.test.js +10 -31
- package/dist/document-models/invoice/src/tests/transitions.test.d.ts +6 -0
- package/dist/document-models/invoice/src/tests/transitions.test.d.ts.map +1 -0
- package/dist/document-models/invoice/src/tests/transitions.test.js +506 -0
- package/dist/document-models/invoice/utils/statusTransitions.d.ts +13 -0
- package/dist/document-models/invoice/utils/statusTransitions.d.ts.map +1 -0
- package/dist/document-models/invoice/utils/statusTransitions.js +13 -0
- package/dist/editors/billing-statement/lineItemTags/tagMapping.js +64 -64
- package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DriveExplorer.js +3 -2
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts +3 -2
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.js +27 -3
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts +4 -4
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +91 -21
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.js +1 -1
- package/dist/editors/invoice/components/confirmationModal.d.ts +14 -0
- package/dist/editors/invoice/components/confirmationModal.d.ts.map +1 -0
- package/dist/editors/invoice/components/confirmationModal.js +7 -0
- package/dist/editors/invoice/components/selectField.d.ts.map +1 -1
- package/dist/editors/invoice/components/selectField.js +109 -112
- package/dist/editors/invoice/components/statusModalComponents.d.ts +52 -0
- package/dist/editors/invoice/components/statusModalComponents.d.ts.map +1 -0
- package/dist/editors/invoice/components/statusModalComponents.js +78 -0
- package/dist/editors/invoice/editor.d.ts +1 -1
- package/dist/editors/invoice/editor.d.ts.map +1 -1
- package/dist/editors/invoice/editor.js +153 -158
- package/dist/editors/invoice/ingestUBL.d.ts.map +1 -1
- package/dist/editors/invoice/ingestUBL.js +0 -3
- package/dist/editors/invoice/invoiceToGnosis.d.ts.map +1 -1
- package/dist/editors/invoice/invoiceToGnosis.js +4 -3
- package/dist/editors/invoice/lineItemTags/tagMapping.d.ts.map +1 -1
- package/dist/editors/invoice/lineItemTags/tagMapping.js +65 -64
- package/dist/editors/invoice/requestFinance.d.ts +1 -0
- package/dist/editors/invoice/requestFinance.d.ts.map +1 -1
- package/dist/editors/invoice/requestFinance.js +9 -2
- package/dist/editors/invoice/validation/validationHandler.d.ts +4 -0
- package/dist/editors/invoice/validation/validationHandler.d.ts.map +1 -0
- package/dist/editors/invoice/validation/validationHandler.js +117 -0
- package/dist/scripts/contributor-billing/createXeroCsv.d.ts +8 -0
- package/dist/scripts/contributor-billing/createXeroCsv.d.ts.map +1 -0
- package/dist/scripts/contributor-billing/createXeroCsv.js +209 -0
- package/dist/style.css +164 -18
- package/dist/subgraphs/invoice/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/invoice/resolvers.js +92 -22
- package/dist/subgraphs/invoice/schema.d.ts.map +1 -1
- package/dist/subgraphs/invoice/schema.js +181 -57
- package/package.json +1 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ValidationResult } from "./validationManager.js";
|
|
2
|
+
declare const validateStatusBeforeContinue: (newStatus: string, state: any, setInvoiceValidation: (validation: ValidationResult) => void, setWalletValidation: (validation: ValidationResult) => void, setCurrencyValidation: (validation: ValidationResult) => void, setMainCountryValidation: (validation: ValidationResult) => void, setBankCountryValidation: (validation: ValidationResult) => void, setIbanValidation: (validation: ValidationResult) => void, setBicValidation: (validation: ValidationResult) => void, setBankNameValidation: (validation: ValidationResult) => void, setStreetAddressValidation: (validation: ValidationResult) => void, setCityValidation: (validation: ValidationResult) => void, setPostalCodeValidation: (validation: ValidationResult) => void, setPayerEmailValidation: (validation: ValidationResult) => void, setLineItemValidation: (validation: ValidationResult) => void, isFiatCurrency: (currency: string) => boolean) => boolean | undefined;
|
|
3
|
+
export default validateStatusBeforeContinue;
|
|
4
|
+
//# sourceMappingURL=validationHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validationHandler.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/validation/validationHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAoC,MAAM,wBAAwB,CAAC;AAG5F,QAAA,MAAM,4BAA4B,GAC9B,WAAW,MAAM,EACjB,OAAO,GAAG,EACV,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC5D,qBAAqB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC3D,uBAAuB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC7D,0BAA0B,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAChE,0BAA0B,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAChE,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EACzD,kBAAkB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EACxD,uBAAuB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC7D,4BAA4B,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAClE,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EACzD,yBAAyB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC/D,yBAAyB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC/D,uBAAuB,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,EAC7D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,wBAwLhD,CAAA;AAED,eAAe,4BAA4B,CAAC"}
|
|
@@ -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<any>;
|
|
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,GAAG,CAAC,CA+LtG"}
|
|
@@ -0,0 +1,209 @@
|
|
|
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 invoiceState of invoiceStates) {
|
|
62
|
+
const state = invoiceState.global;
|
|
63
|
+
const invoiceId = invoiceState.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 {
|
|
187
|
+
message: `The following invoices have line items missing a 'xero-expense-account' tag: ${[...new Set(missingExpenseTagInvoices)].join(', ')}`,
|
|
188
|
+
missingExpenseTagInvoices: missingExpenseTagInvoices
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Download CSV for all invoices
|
|
192
|
+
const csvLines = [headers.join(',')].concat(allRows.map(row => row.map(value => `"${value}"`).join(',')));
|
|
193
|
+
const csvData = csvLines.join('\n');
|
|
194
|
+
// Download logic (browser)
|
|
195
|
+
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
|
|
196
|
+
const link = document.createElement('a');
|
|
197
|
+
const url = URL.createObjectURL(blob);
|
|
198
|
+
link.setAttribute('href', url);
|
|
199
|
+
link.setAttribute('download', `xero-invoice-export-${new Date().toISOString().split('T')[0]}.csv`);
|
|
200
|
+
link.style.visibility = 'hidden';
|
|
201
|
+
document.body.appendChild(link);
|
|
202
|
+
link.click();
|
|
203
|
+
document.body.removeChild(link);
|
|
204
|
+
// This is the data to be added to ExportData in the state of each invoice
|
|
205
|
+
// console.log(exportDataByInvoice)
|
|
206
|
+
// Return or assign exportDataByInvoice as needed
|
|
207
|
+
// For example, return it if you want to use it elsewhere:
|
|
208
|
+
return exportDataByInvoice;
|
|
209
|
+
}
|