hledger-lsp 0.2.0 → 0.2.2
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/out/features/codeActions.d.ts.map +1 -1
- package/out/features/codeActions.js +5 -2
- package/out/features/codeActions.js.map +1 -1
- package/out/features/codeLens.d.ts.map +1 -1
- package/out/features/codeLens.js +4 -1
- package/out/features/codeLens.js.map +1 -1
- package/out/features/foldingRanges.d.ts.map +1 -1
- package/out/features/foldingRanges.js +4 -1
- package/out/features/foldingRanges.js.map +1 -1
- package/out/features/formatter.d.ts +7 -10
- package/out/features/formatter.d.ts.map +1 -1
- package/out/features/formatter.js +124 -265
- package/out/features/formatter.js.map +1 -1
- package/out/features/inlayHints.d.ts +11 -10
- package/out/features/inlayHints.d.ts.map +1 -1
- package/out/features/inlayHints.js +131 -126
- package/out/features/inlayHints.js.map +1 -1
- package/out/features/semanticTokens.js +6 -6
- package/out/features/semanticTokens.js.map +1 -1
- package/out/features/symbols.d.ts.map +1 -1
- package/out/features/symbols.js +5 -2
- package/out/features/symbols.js.map +1 -1
- package/out/features/validator.d.ts.map +1 -1
- package/out/features/validator.js +32 -19
- package/out/features/validator.js.map +1 -1
- package/out/parser/ast.js +6 -6
- package/out/parser/ast.js.map +1 -1
- package/out/server.js +108 -39
- package/out/server.js.map +1 -1
- package/out/utils/amountFormatter.d.ts +31 -11
- package/out/utils/amountFormatter.d.ts.map +1 -1
- package/out/utils/amountFormatter.js +147 -70
- package/out/utils/amountFormatter.js.map +1 -1
- package/out/utils/runningBalanceCalculator.d.ts +44 -0
- package/out/utils/runningBalanceCalculator.d.ts.map +1 -0
- package/out/utils/runningBalanceCalculator.js +163 -0
- package/out/utils/runningBalanceCalculator.js.map +1 -0
- package/package.json +1 -1
|
@@ -4,88 +4,165 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.formatAmount = formatAmount;
|
|
7
|
+
exports.getAmountLayout = getAmountLayout;
|
|
8
|
+
exports.renderAmountLayout = renderAmountLayout;
|
|
9
|
+
function formatAmount(quantity, commodity, parsed, options) {
|
|
10
|
+
// Construct a temporary Amount object to reuse the unified layout logic
|
|
11
|
+
const amount = {
|
|
12
|
+
quantity,
|
|
13
|
+
commodity
|
|
14
|
+
// We don't have the full amount object with format/source here, but that's fine for this use case
|
|
15
|
+
// format property is undefined, so it will fall back to commodity format
|
|
16
|
+
};
|
|
17
|
+
// Safe defaults for options if not provided
|
|
18
|
+
const opts = {
|
|
19
|
+
indentation: 4,
|
|
20
|
+
maxAccountWidth: 42,
|
|
21
|
+
maxCommodityWidth: 4,
|
|
22
|
+
maxAmountWidth: 12,
|
|
23
|
+
minSpacing: 2,
|
|
24
|
+
decimalAlignColumn: 52,
|
|
25
|
+
assertionDecimalAlignColumn: 70,
|
|
26
|
+
signPosition: 'after-symbol',
|
|
27
|
+
...options
|
|
28
|
+
};
|
|
29
|
+
const layout = getAmountLayout(amount, parsed, opts);
|
|
30
|
+
return renderAmountLayout(layout);
|
|
31
|
+
}
|
|
7
32
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @param quantity - The numeric amount
|
|
11
|
-
* @param commodity - The commodity symbol/name
|
|
12
|
-
* @param parsed - ParsedDocument to look up commodity format information
|
|
13
|
-
* @param options - Formatting options (e.g. sign position)
|
|
14
|
-
* @returns Formatted amount string (e.g., "$50.00", "50.00 USD")
|
|
33
|
+
* Calculate the layout components for an amount
|
|
15
34
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const spaceBetween = format.spaceBetween ?? true;
|
|
28
|
-
const space = spaceBetween ? ' ' : '';
|
|
29
|
-
// Use precision from format, or default to 2
|
|
30
|
-
const precision = format.precision ?? 2;
|
|
31
|
-
// Manual formatting to respect separators
|
|
32
|
-
const decimalMark = format.decimalMark || '.';
|
|
33
|
-
const thousandsSeparator = format.thousandsSeparator; // can be null/undefined
|
|
34
|
-
// Get basic fixed string (e.g. "1000.00") with dot decimal
|
|
35
|
-
const baseFixed = absQuantity.toFixed(precision);
|
|
36
|
-
const [integerPart, decimalPart] = baseFixed.split('.');
|
|
37
|
-
let formattedInteger = integerPart;
|
|
38
|
-
if (thousandsSeparator) {
|
|
39
|
-
// split into groups of 3
|
|
40
|
-
const groups = [];
|
|
41
|
-
for (let i = integerPart.length; i > 0; i -= 3) {
|
|
42
|
-
groups.unshift(integerPart.substring(Math.max(0, i - 3), i));
|
|
43
|
-
}
|
|
44
|
-
formattedInteger = groups.join(thousandsSeparator);
|
|
35
|
+
function getAmountLayout(amount, parsed, options) {
|
|
36
|
+
const commodity = parsed.commodities.get(amount.commodity);
|
|
37
|
+
let format = {};
|
|
38
|
+
let declaredPrecision = undefined;
|
|
39
|
+
if (commodity && commodity.format) {
|
|
40
|
+
format = commodity.format;
|
|
41
|
+
const formatPrecision = commodity.format.precision;
|
|
42
|
+
// Note: precision can be undefined (not set), null (no decimal), or a number
|
|
43
|
+
// Only set declaredPrecision if it's a number
|
|
44
|
+
if (formatPrecision !== null && formatPrecision !== undefined) {
|
|
45
|
+
declaredPrecision = formatPrecision;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
}
|
|
48
|
+
else if (amount.format) {
|
|
49
|
+
format = amount.format;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// If no format found, check some defaults or use empty
|
|
53
|
+
if (!commodity && !amount.format) {
|
|
54
|
+
// Default for any undeclared commodity: Symbol on left, no space, precision 2
|
|
55
|
+
if (amount.commodity) {
|
|
56
|
+
format = {
|
|
57
|
+
symbolOnLeft: true,
|
|
58
|
+
spaceBetween: false,
|
|
59
|
+
symbol: amount.commodity,
|
|
60
|
+
precision: 2
|
|
61
|
+
};
|
|
62
|
+
declaredPrecision = 2;
|
|
57
63
|
}
|
|
58
|
-
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Determine the actual precision to use based on the rules:
|
|
67
|
+
// 1. Never reduce precision if posting has higher precision than declared
|
|
68
|
+
// 2. Add zeros to match declared precision when actual < declared
|
|
69
|
+
// 3. Don't change formatting when commodity is not declared
|
|
70
|
+
// Note: precision can be undefined (not set), null (no decimal), or a number
|
|
71
|
+
const amountFormatPrecision = amount.format?.precision;
|
|
72
|
+
const actualPrecision = (amountFormatPrecision !== null && amountFormatPrecision !== undefined) ? amountFormatPrecision : undefined;
|
|
73
|
+
let targetPrecision = undefined;
|
|
74
|
+
if (declaredPrecision !== undefined) {
|
|
75
|
+
// Commodity is declared - use max of actual and declared precision
|
|
76
|
+
if (actualPrecision !== undefined) {
|
|
77
|
+
targetPrecision = Math.max(actualPrecision, declaredPrecision);
|
|
78
|
+
}
|
|
79
|
+
else if (amountFormatPrecision === null && commodity?.declared !== true) {
|
|
80
|
+
// Amount has explicit null precision (no decimal) - preserve it
|
|
81
|
+
// UNLESS the commodity is declared (via commodity directive), in which case use declared precision
|
|
82
|
+
targetPrecision = undefined;
|
|
59
83
|
}
|
|
60
84
|
else {
|
|
61
|
-
|
|
85
|
+
targetPrecision = declaredPrecision;
|
|
62
86
|
}
|
|
63
87
|
}
|
|
64
|
-
else
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (quantity < 0) {
|
|
72
|
-
const signPos = options?.signPosition || 'after-symbol';
|
|
73
|
-
if (signPos === 'before-symbol') {
|
|
74
|
-
return sign + commodity + formattedNumber;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
return commodity + sign + formattedNumber;
|
|
78
|
-
}
|
|
88
|
+
else {
|
|
89
|
+
// Commodity is not declared - preserve original precision
|
|
90
|
+
targetPrecision = actualPrecision;
|
|
91
|
+
// Fallback for formatAmount heuristic cases (e.g. "123.456" with no commodity)
|
|
92
|
+
if (targetPrecision === undefined && !amount.format && !commodity?.format) {
|
|
93
|
+
if (!amount.commodity) {
|
|
94
|
+
targetPrecision = 2;
|
|
79
95
|
}
|
|
80
|
-
return sign + commodity + formattedNumber;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
return sign + formattedNumber + ' ' + commodity;
|
|
84
96
|
}
|
|
85
97
|
}
|
|
98
|
+
const symbolOnLeft = format.symbolOnLeft || false;
|
|
99
|
+
// Fallback for spaceBetween: default to true for right-side symbols (most commodities), false for left-side ($)
|
|
100
|
+
const spaceBetweenCommodityAndAmount = format.spaceBetween ?? (symbolOnLeft ? false : true);
|
|
101
|
+
const negativeSignBefore = symbolOnLeft && options?.signPosition === 'before-symbol';
|
|
102
|
+
// Calculate string values using rounding logic
|
|
103
|
+
const absQuantity = Math.abs(amount.quantity);
|
|
104
|
+
const rawString = targetPrecision !== undefined ? absQuantity.toFixed(targetPrecision) : absQuantity.toString();
|
|
105
|
+
const parts = rawString.split('.');
|
|
106
|
+
const integerPart = parts[0];
|
|
107
|
+
const decimalPart = parts[1] || '';
|
|
108
|
+
// Format Integer
|
|
109
|
+
let amountIntegerString = integerPart;
|
|
110
|
+
if (format.thousandsSeparator) {
|
|
111
|
+
const regex = /\B(?=(\d{3})+(?!\d))/g;
|
|
112
|
+
amountIntegerString = integerPart.replace(regex, format.thousandsSeparator);
|
|
113
|
+
}
|
|
114
|
+
// Format Decimal
|
|
115
|
+
// decimalPart is already correct from toFixed or toString
|
|
116
|
+
const amountDecimalString = decimalPart;
|
|
117
|
+
return {
|
|
118
|
+
commodityBefore: symbolOnLeft ? format?.symbol || amount.commodity || '' : '',
|
|
119
|
+
isNegative: amount.quantity < 0,
|
|
120
|
+
negativeSignBefore,
|
|
121
|
+
amountIntegerString,
|
|
122
|
+
amountDecimalString,
|
|
123
|
+
demicalMark: (targetPrecision !== undefined && targetPrecision > 0) || amountDecimalString.length > 0 ? (format.decimalMark || '.') : '',
|
|
124
|
+
spaceBetweenCommodityAndAmount,
|
|
125
|
+
commodityAfter: !symbolOnLeft ? format?.symbol || amount.commodity || '' : ''
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Render the layout into a string, optionally using provided widths for alignment/padding.
|
|
130
|
+
* If widths are not provided, segments are joined without extra padding.
|
|
131
|
+
*/
|
|
132
|
+
function renderAmountLayout(layout, widths) {
|
|
133
|
+
// If no widths provided, create minimal widths based on content
|
|
134
|
+
const effectiveWidths = widths || {
|
|
135
|
+
commodityBefore: layout.commodityBefore.length,
|
|
136
|
+
spaceBetweenCommodityBeforeAndAmount: (layout.spaceBetweenCommodityAndAmount && layout.commodityBefore) ? 1 : 0,
|
|
137
|
+
negativeSign: layout.isNegative ? 1 : 0,
|
|
138
|
+
integer: layout.amountIntegerString.length,
|
|
139
|
+
decimalMark: layout.demicalMark.length,
|
|
140
|
+
decimal: layout.amountDecimalString.length,
|
|
141
|
+
spaceBetweenAmountAndCommodityAfter: (layout.spaceBetweenCommodityAndAmount && layout.commodityAfter) ? 1 : 0,
|
|
142
|
+
commodityAfter: layout.commodityAfter.length
|
|
143
|
+
};
|
|
144
|
+
let result = '';
|
|
145
|
+
if (layout.negativeSignBefore) {
|
|
146
|
+
result += layout.isNegative ? '-'.padStart(effectiveWidths.negativeSign, ' ') : ' '.repeat(effectiveWidths.negativeSign);
|
|
147
|
+
result += layout.commodityBefore.padStart(effectiveWidths.commodityBefore, ' ');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
result += layout.commodityBefore.padStart(effectiveWidths.commodityBefore, ' ');
|
|
151
|
+
result += layout.isNegative ? '-'.padStart(effectiveWidths.negativeSign, ' ') : ' '.repeat(effectiveWidths.negativeSign);
|
|
152
|
+
}
|
|
153
|
+
result += ' '.repeat(effectiveWidths.spaceBetweenCommodityBeforeAndAmount);
|
|
154
|
+
result += layout.amountIntegerString.padStart(effectiveWidths.integer, ' ');
|
|
155
|
+
if (layout.demicalMark) {
|
|
156
|
+
result += layout.demicalMark.padEnd(effectiveWidths.decimalMark, ' ');
|
|
157
|
+
result += layout.amountDecimalString.padEnd(effectiveWidths.decimal, ' ');
|
|
158
|
+
}
|
|
86
159
|
else {
|
|
87
|
-
//
|
|
88
|
-
|
|
160
|
+
// If there is no decimal mark in this specific amount, we still need to respect
|
|
161
|
+
// the reserved space for alignment if it exists.
|
|
162
|
+
result += ' '.repeat(effectiveWidths.decimalMark + effectiveWidths.decimal);
|
|
89
163
|
}
|
|
164
|
+
result += ' '.repeat(effectiveWidths.spaceBetweenAmountAndCommodityAfter);
|
|
165
|
+
result += layout.commodityAfter.padEnd(effectiveWidths.commodityAfter, ' ');
|
|
166
|
+
return result;
|
|
90
167
|
}
|
|
91
168
|
//# sourceMappingURL=amountFormatter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"amountFormatter.js","sourceRoot":"","sources":["../../src/utils/amountFormatter.ts"],"names":[],"mappings":";AAAA;;GAEG;;
|
|
1
|
+
{"version":3,"file":"amountFormatter.js","sourceRoot":"","sources":["../../src/utils/amountFormatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running balance calculator for hledger transactions
|
|
3
|
+
*
|
|
4
|
+
* Calculates running balances for all accounts across all transactions,
|
|
5
|
+
* taking into account:
|
|
6
|
+
* - Chronological ordering (by date)
|
|
7
|
+
* - Multiple commodities per account
|
|
8
|
+
* - Explicit and inferred amounts
|
|
9
|
+
* - Transactions from multiple files (via includes)
|
|
10
|
+
*/
|
|
11
|
+
import { Transaction, ParsedDocument } from '../types';
|
|
12
|
+
/**
|
|
13
|
+
* Result of running balance calculation
|
|
14
|
+
* Maps: transaction index -> posting index -> commodity -> balance
|
|
15
|
+
*/
|
|
16
|
+
export type RunningBalanceMap = Map<number, Map<number, Map<string, number>>>;
|
|
17
|
+
/**
|
|
18
|
+
* Account balances
|
|
19
|
+
* Maps: account name -> commodity -> balance
|
|
20
|
+
*/
|
|
21
|
+
export type AccountBalanceMap = Map<string, Map<string, number>>;
|
|
22
|
+
/**
|
|
23
|
+
* Calculate running balances for all transactions in a parsed document
|
|
24
|
+
*
|
|
25
|
+
* This function:
|
|
26
|
+
* 1. Sorts transactions chronologically by date
|
|
27
|
+
* 2. Processes each posting to update account balances
|
|
28
|
+
* 3. Returns a map of balances after each posting
|
|
29
|
+
*
|
|
30
|
+
* @param parsed The parsed document containing all transactions
|
|
31
|
+
* @returns A map of transaction index -> posting index -> commodity -> balance
|
|
32
|
+
*/
|
|
33
|
+
export declare function calculateRunningBalances(parsed: ParsedDocument): RunningBalanceMap;
|
|
34
|
+
/**
|
|
35
|
+
* Calculate running balances for balance assertion validation
|
|
36
|
+
*
|
|
37
|
+
* This is a simplified version that only tracks account balances,
|
|
38
|
+
* suitable for validating balance assertions.
|
|
39
|
+
*
|
|
40
|
+
* @param transactions List of transactions (will be sorted by date internally)
|
|
41
|
+
* @returns A map of account name -> commodity -> balance
|
|
42
|
+
*/
|
|
43
|
+
export declare function calculateAccountBalances(transactions: Transaction[]): AccountBalanceMap;
|
|
44
|
+
//# sourceMappingURL=runningBalanceCalculator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runningBalanceCalculator.d.ts","sourceRoot":"","sources":["../../src/utils/runningBalanceCalculator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;AAE9E;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEjE;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAsElF;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,iBAAiB,CA0BvF"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Running balance calculator for hledger transactions
|
|
4
|
+
*
|
|
5
|
+
* Calculates running balances for all accounts across all transactions,
|
|
6
|
+
* taking into account:
|
|
7
|
+
* - Chronological ordering (by date)
|
|
8
|
+
* - Multiple commodities per account
|
|
9
|
+
* - Explicit and inferred amounts
|
|
10
|
+
* - Transactions from multiple files (via includes)
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.calculateRunningBalances = calculateRunningBalances;
|
|
14
|
+
exports.calculateAccountBalances = calculateAccountBalances;
|
|
15
|
+
/**
|
|
16
|
+
* Calculate running balances for all transactions in a parsed document
|
|
17
|
+
*
|
|
18
|
+
* This function:
|
|
19
|
+
* 1. Sorts transactions chronologically by date
|
|
20
|
+
* 2. Processes each posting to update account balances
|
|
21
|
+
* 3. Returns a map of balances after each posting
|
|
22
|
+
*
|
|
23
|
+
* @param parsed The parsed document containing all transactions
|
|
24
|
+
* @returns A map of transaction index -> posting index -> commodity -> balance
|
|
25
|
+
*/
|
|
26
|
+
function calculateRunningBalances(parsed) {
|
|
27
|
+
const result = new Map();
|
|
28
|
+
// Track global account balances across all transactions
|
|
29
|
+
const accountBalances = new Map();
|
|
30
|
+
// Sort transactions by date to calculate balances in chronological order
|
|
31
|
+
// This is critical when transactions come from multiple files via includes
|
|
32
|
+
const sortedTransactions = [...parsed.transactions].sort((a, b) => {
|
|
33
|
+
return a.date.localeCompare(b.date);
|
|
34
|
+
});
|
|
35
|
+
// Create a map from sorted index to original index
|
|
36
|
+
const sortedToOriginalIndex = new Map();
|
|
37
|
+
sortedTransactions.forEach((tx, sortedIdx) => {
|
|
38
|
+
const originalIdx = parsed.transactions.indexOf(tx);
|
|
39
|
+
sortedToOriginalIndex.set(sortedIdx, originalIdx);
|
|
40
|
+
});
|
|
41
|
+
for (let sortedIdx = 0; sortedIdx < sortedTransactions.length; sortedIdx++) {
|
|
42
|
+
const transaction = sortedTransactions[sortedIdx];
|
|
43
|
+
const txIndex = sortedToOriginalIndex.get(sortedIdx);
|
|
44
|
+
const postingBalances = new Map();
|
|
45
|
+
// Get inferred amounts from the transaction's AST (if available)
|
|
46
|
+
const inferredAmounts = getInferredAmountsFromTransaction(transaction);
|
|
47
|
+
for (let postingIndex = 0; postingIndex < transaction.postings.length; postingIndex++) {
|
|
48
|
+
const posting = transaction.postings[postingIndex];
|
|
49
|
+
const account = posting.account;
|
|
50
|
+
// Get amount (either explicit or inferred)
|
|
51
|
+
let amountMap;
|
|
52
|
+
if (posting.amount) {
|
|
53
|
+
// Explicit amount
|
|
54
|
+
const commodity = posting.amount.commodity || '';
|
|
55
|
+
amountMap = new Map([[commodity, posting.amount.quantity]]);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Inferred amount
|
|
59
|
+
amountMap = inferredAmounts.get(postingIndex);
|
|
60
|
+
}
|
|
61
|
+
if (amountMap) {
|
|
62
|
+
// Update balance for each commodity in this posting
|
|
63
|
+
for (const [commodity, quantity] of amountMap.entries()) {
|
|
64
|
+
// Get or create account balance map
|
|
65
|
+
if (!accountBalances.has(account)) {
|
|
66
|
+
accountBalances.set(account, new Map());
|
|
67
|
+
}
|
|
68
|
+
const commodityBalances = accountBalances.get(account);
|
|
69
|
+
// Update balance
|
|
70
|
+
const currentBalance = commodityBalances.get(commodity) || 0;
|
|
71
|
+
const newBalance = currentBalance + quantity;
|
|
72
|
+
commodityBalances.set(commodity, newBalance);
|
|
73
|
+
// Store this posting's resulting balance
|
|
74
|
+
if (!postingBalances.has(postingIndex)) {
|
|
75
|
+
postingBalances.set(postingIndex, new Map());
|
|
76
|
+
}
|
|
77
|
+
postingBalances.get(postingIndex).set(commodity, newBalance);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
result.set(txIndex, postingBalances);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Calculate running balances for balance assertion validation
|
|
87
|
+
*
|
|
88
|
+
* This is a simplified version that only tracks account balances,
|
|
89
|
+
* suitable for validating balance assertions.
|
|
90
|
+
*
|
|
91
|
+
* @param transactions List of transactions (will be sorted by date internally)
|
|
92
|
+
* @returns A map of account name -> commodity -> balance
|
|
93
|
+
*/
|
|
94
|
+
function calculateAccountBalances(transactions) {
|
|
95
|
+
const balances = new Map();
|
|
96
|
+
// Sort transactions by date to calculate balances in chronological order
|
|
97
|
+
const sortedTransactions = [...transactions].sort((a, b) => {
|
|
98
|
+
return a.date.localeCompare(b.date);
|
|
99
|
+
});
|
|
100
|
+
for (const transaction of sortedTransactions) {
|
|
101
|
+
for (const posting of transaction.postings) {
|
|
102
|
+
// Update running balance
|
|
103
|
+
// Note: Balance assertions check the ORIGINAL commodity (amount.commodity),
|
|
104
|
+
// not the cost commodity. So we always update balance in the amount's commodity.
|
|
105
|
+
if (posting.amount) {
|
|
106
|
+
const accountBalances = balances.get(posting.account) || new Map();
|
|
107
|
+
const commodity = posting.amount.commodity || '';
|
|
108
|
+
const currentBalance = accountBalances.get(commodity) || 0;
|
|
109
|
+
// Always use the original amount quantity for balance tracking,
|
|
110
|
+
// regardless of whether there's a cost notation
|
|
111
|
+
accountBalances.set(commodity, currentBalance + posting.amount.quantity);
|
|
112
|
+
balances.set(posting.account, accountBalances);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return balances;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get inferred amounts from a transaction
|
|
120
|
+
*
|
|
121
|
+
* This extracts inferred amounts that were calculated during parsing
|
|
122
|
+
* and stored in the transaction's AST.
|
|
123
|
+
*
|
|
124
|
+
* @param transaction The transaction to extract inferred amounts from
|
|
125
|
+
* @returns A map of posting index -> commodity -> amount
|
|
126
|
+
*/
|
|
127
|
+
function getInferredAmountsFromTransaction(transaction) {
|
|
128
|
+
const result = new Map();
|
|
129
|
+
// If the transaction has an AST with inferred amounts, use those
|
|
130
|
+
if (transaction.ast) {
|
|
131
|
+
const ast = transaction.ast;
|
|
132
|
+
// Look for inferred amounts in the AST
|
|
133
|
+
// The AST should have calculated these during parsing
|
|
134
|
+
if (ast.inferredAmounts) {
|
|
135
|
+
return ast.inferredAmounts;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// If no AST or no inferred amounts, calculate them manually
|
|
139
|
+
// This is a fallback for transactions without AST data
|
|
140
|
+
const explicitPostings = transaction.postings.filter(p => p.amount && !p.amount.inferred);
|
|
141
|
+
const implicitPostings = transaction.postings.filter(p => !p.amount || p.amount.inferred);
|
|
142
|
+
if (implicitPostings.length === 1 && explicitPostings.length > 0) {
|
|
143
|
+
// Calculate the inferred amount as the negative sum of explicit amounts
|
|
144
|
+
const sums = new Map();
|
|
145
|
+
for (const posting of explicitPostings) {
|
|
146
|
+
if (posting.amount) {
|
|
147
|
+
const commodity = posting.amount.commodity || '';
|
|
148
|
+
const currentSum = sums.get(commodity) || 0;
|
|
149
|
+
sums.set(commodity, currentSum + posting.amount.quantity);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Negate the sums to get the inferred amount
|
|
153
|
+
const inferredAmountMap = new Map();
|
|
154
|
+
for (const [commodity, sum] of sums.entries()) {
|
|
155
|
+
inferredAmountMap.set(commodity, -sum);
|
|
156
|
+
}
|
|
157
|
+
// Find the index of the implicit posting
|
|
158
|
+
const implicitIndex = transaction.postings.indexOf(implicitPostings[0]);
|
|
159
|
+
result.set(implicitIndex, inferredAmountMap);
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=runningBalanceCalculator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runningBalanceCalculator.js","sourceRoot":"","sources":["../../src/utils/runningBalanceCalculator.ts"],"names":[],"mappings":"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"}
|