Finished
66 }
77}
88
9// Constants for formatting
10const DATE_PADDING = 10;
11const DESCRIPTION_PADDING = 25;
12const CHANGE_PADDING = 13;
13const DESCRIPTION_MAX_LENGTH = 22;
14const CENTS_PER_DOLLAR = 100;
15
16// Date formatting functions
17function formatDateUS(date) {
18 const month = (date.getMonth() + 1).toString().padStart(2, "0");
19 const day = date.getDate().toString().padStart(2, "0");
20 const year = date.getFullYear();
21 return `${month}/${day}/${year}`;
22}
23
24function formatDateDutch(date) {
25 const day = date.getDate().toString().padStart(2, "0");
26 const month = (date.getMonth() + 1).toString().padStart(2, "0");
27 const year = date.getFullYear();
28 return `${day}-${month}-${year}`;
29}
30
31// Description formatting function
32function formatDescription(description) {
33 return description.length > DESCRIPTION_PADDING
34 ? `${description.substring(0, DESCRIPTION_MAX_LENGTH)}...`
35 : description.padEnd(DESCRIPTION_PADDING, " ");
36}
37
38// Currency formatting functions
39function formatCurrencyUS(change, currency) {
40 const formattingOptions = {
41 style: "currency",
42 currency: currency,
43 minimumFractionDigits: 2,
44 maximumFractionDigits: 2,
45 };
46
47 if (change < 0) {
48 return `(${Math.abs(change / CENTS_PER_DOLLAR).toLocaleString("en-US", formattingOptions)})`;
49 } else {
50 return `${(change / CENTS_PER_DOLLAR).toLocaleString("en-US", formattingOptions)} `;
51 }
52}
53
54function formatCurrencyDutch(change, currency) {
55 const formattingOptions = {
56 style: "currency",
57 currency: currency,
58 currencyDisplay: "narrowSymbol",
59 minimumFractionDigits: 2,
60 maximumFractionDigits: 2,
61 };
62
63 return `${(change / CENTS_PER_DOLLAR).toLocaleString("nl-NL", formattingOptions)} `;
64}
65
66// Header generation functions
67function generateHeaderUS() {
68 return (
69 "Date".padEnd(DATE_PADDING, " ") +
70 " | " +
71 "Description".padEnd(DESCRIPTION_PADDING, " ") +
72 " | " +
73 "Change".padEnd(CHANGE_PADDING, " ") +
74 "\n"
75 );
76}
77
78function generateHeaderDutch() {
79 return (
80 "Datum".padEnd(DATE_PADDING, " ") +
81 " | " +
82 "Omschrijving".padEnd(DESCRIPTION_PADDING, " ") +
83 " | " +
84 "Verandering".padEnd(CHANGE_PADDING, " ") +
85 "\n"
86 );
87}
88
89// Sorting function
90function sortEntries(entries) {
91 entries.sort(
92 (a, b) =>
93 a.date - b.date ||
94 a.change - b.change ||
95 a.description.localeCompare(b.description),
96 );
97}
98
999/**
10100 * @param {string} date
11101 * @param {string} description
28118 */
29119export function formatEntries(currency, locale, entries) {
30120 let table = '';
121
122 // Sort entries
123 entries.sort(
124 (a, b) =>
125 a.date - b.date ||
126 a.change - b.change ||
127 a.description.localeCompare(b.description),
128 );
129
31130 if (locale === 'en-US') {
32 // Generate Header Row
33 table +=
34 'Date'.padEnd(10, ' ') +
35 ' | ' +
36 'Description'.padEnd(25, ' ') +
37 ' | ' +
38 'Change'.padEnd(13, ' ') +
39 '\n';
40
41 // Sort entries
42 entries.sort(
43 (a, b) =>
44 a.date - b.date ||
45 a.change - b.change ||
46 a.description.localeCompare(b.description),
47 );
48
49 entries.forEach((entry) => {
50 // Write entry date to table
51 const dateStr = `${(entry.date.getMonth() + 1)
52 .toString()
53 .padStart(2, '0')}/${entry.date
54 .getDate()
55 .toString()
56 .padStart(2, '0')}/${entry.date.getFullYear()}`;
57 table += `${dateStr} | `;
58
59 // Write entry description to table
60 const truncatedDescription =
61 entry.description.length > 25
62 ? `${entry.description.substring(0, 22)}...`
63 : entry.description.padEnd(25, ' ');
64 table += `${truncatedDescription} | `;
65
66 // Write entry change to table
67 let changeStr = '';
68 if (currency === 'USD') {
69 let formatingOptions = {
70 style: 'currency',
71 currency: 'USD',
72 //currencySign: 'accounting',
73 minimumFractionDigits: 2,
74 maximumFractionDigits: 2,
75 };
76 if (entry.change < 0) {
77 changeStr = `(${Math.abs(entry.change / 100).toLocaleString(
78 'en-US',
79 formatingOptions,
80 )})`;
81 } else {
82 changeStr = `${(entry.change / 100).toLocaleString(
83 'en-US',
84 formatingOptions,
85 )} `;
86 }
87 } else if (currency === 'EUR') {
88 let formatingOptions = {
89 style: 'currency',
90 currency: 'EUR',
91 minimumFractionDigits: 2,
92 maximumFractionDigits: 2,
93 };
94 if (entry.change < 0) {
95 changeStr = `(${Math.abs(entry.change / 100).toLocaleString(
96 'en-US',
97 formatingOptions,
98 )})`;
99 } else {
100 changeStr = `${(entry.change / 100).toLocaleString(
101 'en-US',
102 formatingOptions,
103 )} `;
104 }
105 }
106 table += changeStr.padStart(13, ' ');
107 table += '\n';
108 });
131 table = buildTableUS(entries, currency);
109132 } else if (locale === 'nl-NL') {
110 // Generate Header Row
111 table +=
112 'Datum'.padEnd(10, ' ') +
113 ' | ' +
114 'Omschrijving'.padEnd(25, ' ') +
115 ' | ' +
116 'Verandering'.padEnd(13, ' ') +
117 '\n';
118
119 // Sort entries
120 entries.sort(
121 (a, b) =>
122 a.date - b.date ||
123 a.change - b.change ||
124 a.description.localeCompare(b.description),
125 );
126
127 entries.forEach((entry) => {
128 // Write entry date to table
129 const dateStr = `${entry.date.getDate().toString().padStart(2, '0')}-${(
130 entry.date.getMonth() + 1
131 )
132 .toString()
133 .padStart(2, '0')}-${entry.date.getFullYear()}`;
134 table += `${dateStr} | `;
135
136 // Write entry description to table
137 const truncatedDescription =
138 entry.description.length > 25
139 ? `${entry.description.substring(0, 22)}...`
140 : entry.description.padEnd(25, ' ');
141 table += `${truncatedDescription} | `;
142
143 // Write entry change to table
144 let changeStr = '';
145 if (currency === 'USD') {
146 let formatingOptions = {
147 style: 'currency',
148 currency: 'USD',
149 currencyDisplay: 'narrowSymbol',
150 minimumFractionDigits: 2,
151 maximumFractionDigits: 2,
152 };
153 changeStr = `${(entry.change / 100).toLocaleString(
154 'nl-NL',
155 formatingOptions,
156 )} `;
157 } else if (currency === 'EUR') {
158 let formatingOptions = {
159 style: 'currency',
160 currency: 'EUR',
161 currencyDisplay: 'narrowSymbol',
162 minimumFractionDigits: 2,
163 maximumFractionDigits: 2,
164 };
165 changeStr = `${(entry.change / 100).toLocaleString(
166 'nl-NL',
167 formatingOptions,
168 )} `;
169 }
170 table += changeStr.padStart(13, ' ');
171 table += '\n';
172 });
133 table = buildTableDutch(entries, currency);
173134 }
135
174136 return table.replace(/\n$/, '');
175137}
138
139function buildTableUS(entries, currency) {
140 let table = generateHeaderUS();
141
142 entries.forEach((entry) => {
143 // Write entry date to table
144 const dateStr = formatDateUS(entry.date);
145 table += `${dateStr} | `;
146
147 // Write entry description to table
148 const truncatedDescription = formatDescription(entry.description);
149 table += `${truncatedDescription} | `;
150
151 // Write entry change to table
152 const changeStr = formatCurrencyUS(entry.change, currency);
153 table += changeStr.padStart(CHANGE_PADDING, " ");
154 table += "\n";
155 });
156
157 return table;
158}
159
160function buildTableDutch(entries, currency) {
161 let table = generateHeaderDutch();
162
163 entries.forEach((entry) => {
164 // Write entry date to table
165 const dateStr = formatDateDutch(entry.date);
166 table += `${dateStr} | `;
167
168 // Write entry description to table
169 const truncatedDescription = formatDescription(entry.description);
170 table += `${truncatedDescription} | `;
171
172 // Write entry change to table
173 const changeStr = formatCurrencyDutch(entry.change, currency);
174 table += changeStr.padStart(CHANGE_PADDING, " ");
175 table += "\n";
176 });
177
178 return table;
179}
Test NameStatus
empty ledger
Pass
one entry
Pass
credit and debit
Pass
final order tie breaker is change
Pass
overlong description is truncated
Pass
euros
Pass
Dutch locale
Pass
Dutch locale and euros
Pass
Dutch negative number with 3 digits before decimal point
Pass
American negative number with 3 digits before decimal point
Pass
multiple entries on same date ordered by description
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.