Skip to content

Commit

Permalink
Merge pull request #68 from LinuCC/main
Browse files Browse the repository at this point in the history
Handle negative signs in front of currencies
  • Loading branch information
tgrosinger authored May 22, 2024
2 parents 7d35a97 + 4bb3fc2 commit b59e3c4
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ data.json

# Generated Files

grammar/ledger.ts
grammar/ledger.ts
grammar/ledger.ts-e
11 changes: 7 additions & 4 deletions grammar/ledger.ne
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@
check: { match: /\([0-9]+\)[ \t]+/, value: (s:string) => s.trim().slice(1, -1) },
ws: /[ \t]+/,
reconciled: /[!*]/,
payee: { match: /[^!*;#|\n]+/, value: (s:string) => s.trim() },
payee: { match: /[^!*;#|\n][^!;#|\n]+/, value: (s:string) => s.trim() },
comment: { match: /[;#|][^\n]+/, value: (s:string) => s.slice(1).trim() },
newline: { match: '\n', lineBreaks: true, next: 'expenseLine'},
},
expenseLine: {
newline: { match: '\n', lineBreaks: true },
ws: /[ \t]+/,
number: { match: /-?[0-9.,]+/, value: (s:string) => s.replace(/,/g, '') },
absoluteNumber: { match: /[0-9.,]+/, value: (s:string) => s.replace(/,/g, '') },
numberSign: /-/,
currency: /[$£₤€₳₿₹¥¥₩Р₱₽₴₫]/, // Note: Р != P
reconciled: /[!*]/,
comment: { match: /[;#|][^\n]+/, value: (s:string) => s.slice(1).trim() },
assertion: {match: /==?\*?/},
account: { match: /[^$£₤€₳₿₹¥¥₩Р₱₽₴₫;#|\n]+/, value: (s:string) => s.trim() },
account: { match: /[^$£₤€₳₿₹¥¥₩Р₱₽₴₫;#|\n\-]+/, value: (s:string) => s.trim() },
},
alias: {
account: { match: /[a-zA-Z0-9: ]+/, value: (s:string) => s.trim() },
Expand Down Expand Up @@ -102,5 +103,7 @@ expenseline ->
balance -> %ws:* %assertion %ws:+ amount {% (d) => {return {}} %}
reconciled -> %reconciled %ws:+ {% ([r,]) => r.value %}
alias -> "alias" %account %equal %account {% ([,l,,r]) => { return { blockLine: l.line, left: l.value, right: r.value } } %}
amount -> %currency %number {% ([c,a]) => { return {currency: c.value, amount: parseFloat(a.value)} } %}
amount -> %currency %absoluteNumber {% ([c,a]) => { return {currency: c.value, amount: parseFloat(a.value)} } %}
amount -> %currency %numberSign %absoluteNumber {% ([c,ns,a]) => { return {currency: c.value, amount: parseFloat(ns.value + a.value)} } %}
amount -> %numberSign %currency %absoluteNumber {% ([ns,c,a]) => { return {currency: c.value, amount: parseFloat(ns.value + a.value)} } %}
check -> %check {% ([c]) => parseFloat(c.value) %}
87 changes: 86 additions & 1 deletion tests/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,54 @@ describe('parsing a ledger file', () => {
expect(txCache.transactions).toHaveLength(1);
expect(txCache.transactions[0]).toEqual(expected);
});
test('when the sign is in front of the currency', () => {
const contents = `2021/04/20 Obsidian
e:Spending Money
b:CreditUnion -$20.00`;
const txCache = parse(contents, settings);
const expected: EnhancedTransaction = {
type: 'tx',
blockLine: 1,
block: {
firstLine: 0,
lastLine: 2,
block: contents,
},
value: {
date: '2021/04/20',
payee: 'Obsidian',
expenselines: [
{
account: 'e:Spending Money',
dealiasedAccount: 'e:Spending Money',
amount: 20,
currency: '$',
reconcile: '',
},
{
account: 'b:CreditUnion',
dealiasedAccount: 'b:CreditUnion',
amount: -20,
currency: '$',
reconcile: '',
},
],
},
};
expect(txCache.transactions).toHaveLength(1);
expect(txCache.transactions[0]).toEqual(expected);
});
test('when the sign is in front of the currency as well as in the amount', () => {
// ledger-cli combines the signs in front of the currency and in front of the amount to make it positive again
// (result for `-$-20.00` is positive, e.g. 20$).
// We would have previously parsed this as a negative amount (account name would be 'b:CreditUnion -' though).
// For now lets just not allow double signs to surface this inconsistency hoping that this is an edge case.
const contents = `2021/04/20 Obsidian
e:Spending Money
b:CreditUnion -$-20.00`;
const txCache = parse(contents, settings);
expect(txCache.transactions).toHaveLength(0);
});
test('when the middle expense has no amount', () => {
const contents = `2021/04/20 Obsidian
e:Spending Money $20.00
Expand Down Expand Up @@ -498,6 +546,43 @@ describe('parsing a ledger file', () => {
expect(txCache.transactions).toHaveLength(1);
expect(txCache.transactions[0]).toEqual(expected);
});
test('Stars in payee are supported', () => {
const contents = `2021/04/21 CardNr.*******1234
e:Spending Money
b:CreditUnion -$20.00`;
const txCache = parse(contents, settings);
const expected: EnhancedTransaction = {
type: 'tx',
blockLine: 1,
block: {
firstLine: 0,
lastLine: 2,
block: contents,
},
value: {
date: '2021/04/21',
payee: 'CardNr.*******1234',
expenselines: [
{
account: 'e:Spending Money',
dealiasedAccount: 'e:Spending Money',
amount: 20,
currency: '$',
reconcile: '',
},
{
account: 'b:CreditUnion',
dealiasedAccount: 'b:CreditUnion',
amount: -20,
currency: '$',
reconcile: '',
},
],
},
};
expect(txCache.transactions).toHaveLength(1);
expect(txCache.transactions[0]).toEqual(expected);
});
test('A parsing error tags the result accordingly', () => {
const contents = `2021/04/20 Obsidian
e:Spending Money $20.00
Expand Down Expand Up @@ -875,7 +960,7 @@ alias c=Credit
],
},
};

expect(txCache.parsingErrors).toHaveLength(0);
expect(txCache.transactions).toHaveLength(1);
expect(txCache.transactions[0]).toEqual(expected);
Expand Down

0 comments on commit b59e3c4

Please sign in to comment.