| 6 | 34 | } |
| 7 | 35 | } |
| 8 | 36 | |
| 37 | // Helper functions for formatting |
| 38 | function formatDate(entry, locale) { |
| 39 | const date = entry.date; |
| 40 | const format = DATE_FORMATS[locale]; |
| 41 | |
| 42 | const month = (date.getMonth() + 1).toString().padStart(2, '0'); |
| 43 | const day = date.getDate().toString().padStart(2, '0'); |
| 44 | const year = date.getFullYear(); |
| 45 | |
| 46 | if (locale === 'en-US') { |
| 47 | return `${month}${format.separator}${day}${format.separator}${year}`; |
| 48 | } else { // nl-NL |
| 49 | return `${day}${format.separator}${month}${format.separator}${year}`; |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | function formatDescription(description) { |
| 54 | if (description.length > DESCRIPTION_MAX_LENGTH) { |
| 55 | return `${description.substring(0, DESCRIPTION_TRUNCATE_TO)}...`; |
| 56 | } |
| 57 | return description.padEnd(DESCRIPTION_MAX_LENGTH, ' '); |
| 58 | } |
| 59 | |
| 60 | function getCurrencyFormattingOptions(currency, locale) { |
| 61 | const baseOptions = { |
| 62 | style: 'currency', |
| 63 | minimumFractionDigits: 2, |
| 64 | maximumFractionDigits: 2, |
| 65 | }; |
| 66 | |
| 67 | if (locale === 'en-US') { |
| 68 | return { |
| 69 | ...baseOptions, |
| 70 | currency: currency, |
| 71 | }; |
| 72 | } else { // nl-NL |
| 73 | return { |
| 74 | ...baseOptions, |
| 75 | currency: currency, |
| 76 | currencyDisplay: 'narrowSymbol', |
| 77 | }; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | function formatCurrencyAmount(change, currency, locale) { |
| 82 | const options = getCurrencyFormattingOptions(currency, locale); |
| 83 | const amount = change / 100; |
| 84 | |
| 85 | if (locale === 'en-US') { |
| 86 | if (change < 0) { |
| 87 | return `(${Math.abs(amount).toLocaleString('en-US', options)})`; |
| 88 | } else { |
| 89 | return `${amount.toLocaleString('en-US', options)} `; |
| 90 | } |
| 91 | } else { // nl-NL |
| 92 | return `${amount.toLocaleString('nl-NL', options)} `; |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | function sortEntries(entries) { |
| 97 | return entries.sort((a, b) => |
| 98 | a.date - b.date || |
| 99 | a.change - b.change || |
| 100 | a.description.localeCompare(b.description) |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | function generateHeader(locale) { |
| 105 | const headers = HEADERS[locale]; |
| 106 | return ( |
| 107 | headers.date.padEnd(COLUMN_WIDTHS.date, ' ') + |
| 108 | ' | ' + |
| 109 | headers.description.padEnd(COLUMN_WIDTHS.description, ' ') + |
| 110 | ' | ' + |
| 111 | headers.change.padEnd(COLUMN_WIDTHS.change, ' ') |
| 112 | ); |
| 113 | } |
| 114 | |
| 9 | 115 | export function createEntry(date, description, change) { |
| 10 | 116 | let entry = new LedgerEntry(); |
| 11 | 117 | entry.date = new Date(date); |
| 15 | 121 | } |
| 16 | 122 | |
| 17 | 123 | export function formatEntries(currency, locale, entries) { |
| 18 | | let table = ''; |
| 19 | | if (locale === 'en-US') { |
| 20 | | // Generate Header Row |
| 21 | | table += |
| 22 | | 'Date'.padEnd(10, ' ') + |
| 23 | | ' | ' + |
| 24 | | 'Description'.padEnd(25, ' ') + |
| 25 | | ' | ' + |
| 26 | | 'Change'.padEnd(13, ' ') + |
| 27 | | '\n'; |
| 28 | | |
| 29 | | // Sort entries |
| 30 | | entries.sort( |
| 31 | | (a, b) => |
| 32 | | a.date - b.date || |
| 33 | | a.change - b.change || |
| 34 | | a.description.localeCompare(b.description), |
| 35 | | ); |
| 36 | | |
| 37 | | entries.forEach((entry) => { |
| 38 | | // Write entry date to table |
| 39 | | const dateStr = `${(entry.date.getMonth() + 1) |
| 40 | | .toString() |
| 41 | | .padStart(2, '0')}/${entry.date |
| 42 | | .getDate() |
| 43 | | .toString() |
| 44 | | .padStart(2, '0')}/${entry.date.getFullYear()}`; |
| 45 | | table += `${dateStr} | `; |
| 46 | | |
| 47 | | // Write entry description to table |
| 48 | | const truncatedDescription = |
| 49 | | entry.description.length > 25 |
| 50 | | ? `${entry.description.substring(0, 22)}...` |
| 51 | | : entry.description.padEnd(25, ' '); |
| 52 | | table += `${truncatedDescription} | `; |
| 53 | | |
| 54 | | // Write entry change to table |
| 55 | | let changeStr = ''; |
| 56 | | if (currency === 'USD') { |
| 57 | | let formatingOptions = { |
| 58 | | style: 'currency', |
| 59 | | currency: 'USD', |
| 60 | | //currencySign: 'accounting', |
| 61 | | minimumFractionDigits: 2, |
| 62 | | maximumFractionDigits: 2, |
| 63 | | }; |
| 64 | | if (entry.change < 0) { |
| 65 | | changeStr = `(${Math.abs(entry.change / 100).toLocaleString( |
| 66 | | 'en-US', |
| 67 | | formatingOptions, |
| 68 | | )})`; |
| 69 | | } else { |
| 70 | | changeStr = `${(entry.change / 100).toLocaleString( |
| 71 | | 'en-US', |
| 72 | | formatingOptions, |
| 73 | | )} `; |
| 74 | | } |
| 75 | | } else if (currency === 'EUR') { |
| 76 | | let formatingOptions = { |
| 77 | | style: 'currency', |
| 78 | | currency: 'EUR', |
| 79 | | minimumFractionDigits: 2, |
| 80 | | maximumFractionDigits: 2, |
| 81 | | }; |
| 82 | | if (entry.change < 0) { |
| 83 | | changeStr = `(${Math.abs(entry.change / 100).toLocaleString( |
| 84 | | 'en-US', |
| 85 | | formatingOptions, |
| 86 | | )})`; |
| 87 | | } else { |
| 88 | | changeStr = `${(entry.change / 100).toLocaleString( |
| 89 | | 'en-US', |
| 90 | | formatingOptions, |
| 91 | | )} `; |
| 92 | | } |
| 93 | | } |
| 94 | | table += changeStr.padStart(13, ' '); |
| 95 | | table += '\n'; |
| 96 | | }); |
| 97 | | } else if (locale === 'nl-NL') { |
| 98 | | // Generate Header Row |
| 99 | | table += |
| 100 | | 'Datum'.padEnd(10, ' ') + |
| 101 | | ' | ' + |
| 102 | | 'Omschrijving'.padEnd(25, ' ') + |
| 103 | | ' | ' + |
| 104 | | 'Verandering'.padEnd(13, ' ') + |
| 105 | | '\n'; |
| 106 | | |
| 107 | | // Sort entries |
| 108 | | entries.sort( |
| 109 | | (a, b) => |
| 110 | | a.date - b.date || |
| 111 | | a.change - b.change || |
| 112 | | a.description.localeCompare(b.description), |
| 113 | | ); |
| 114 | | |
| 115 | | entries.forEach((entry) => { |
| 116 | | // Write entry date to table |
| 117 | | const dateStr = `${entry.date.getDate().toString().padStart(2, '0')}-${( |
| 118 | | entry.date.getMonth() + 1 |
| 119 | | ) |
| 120 | | .toString() |
| 121 | | .padStart(2, '0')}-${entry.date.getFullYear()}`; |
| 122 | | table += `${dateStr} | `; |
| 123 | | |
| 124 | | // Write entry description to table |
| 125 | | const truncatedDescription = |
| 126 | | entry.description.length > 25 |
| 127 | | ? `${entry.description.substring(0, 22)}...` |
| 128 | | : entry.description.padEnd(25, ' '); |
| 129 | | table += `${truncatedDescription} | `; |
| 130 | | |
| 131 | | // Write entry change to table |
| 132 | | let changeStr = ''; |
| 133 | | if (currency === 'USD') { |
| 134 | | let formatingOptions = { |
| 135 | | style: 'currency', |
| 136 | | currency: 'USD', |
| 137 | | currencyDisplay: 'narrowSymbol', |
| 138 | | minimumFractionDigits: 2, |
| 139 | | maximumFractionDigits: 2, |
| 140 | | }; |
| 141 | | changeStr = `${(entry.change / 100).toLocaleString( |
| 142 | | 'nl-NL', |
| 143 | | formatingOptions, |
| 144 | | )} `; |
| 145 | | } else if (currency === 'EUR') { |
| 146 | | let formatingOptions = { |
| 147 | | style: 'currency', |
| 148 | | currency: 'EUR', |
| 149 | | currencyDisplay: 'narrowSymbol', |
| 150 | | minimumFractionDigits: 2, |
| 151 | | maximumFractionDigits: 2, |
| 152 | | }; |
| 153 | | changeStr = `${(entry.change / 100).toLocaleString( |
| 154 | | 'nl-NL', |
| 155 | | formatingOptions, |
| 156 | | )} `; |
| 157 | | } |
| 158 | | table += changeStr.padStart(13, ' '); |
| 159 | | table += '\n'; |
| 160 | | }); |
| 161 | | } |
| 124 | // Generate header |
| 125 | let table = generateHeader(locale) + '\n'; |
| 126 | |
| 127 | // Sort entries |
| 128 | const sortedEntries = sortEntries(entries); |
| 129 | |
| 130 | // Format each entry |
| 131 | sortedEntries.forEach((entry) => { |
| 132 | const dateStr = formatDate(entry, locale); |
| 133 | const descriptionStr = formatDescription(entry.description); |
| 134 | const changeStr = formatCurrencyAmount(entry.change, currency, locale); |
| 135 | |
| 136 | table += `${dateStr} | ${descriptionStr} | ${changeStr.padStart(COLUMN_WIDTHS.change, ' ')}\n`; |
| 137 | }); |
| 138 | |
| 162 | 139 | return table.replace(/\n$/, ''); |
| 163 | 140 | } |