@fuzzle/opencode-accountant 0.10.8 → 0.11.0

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.
Files changed (2) hide show
  1. package/dist/index.js +69 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2739,6 +2739,19 @@ var init_agentLoader = __esm(() => {
2739
2739
  ]);
2740
2740
  });
2741
2741
 
2742
+ // src/utils/journalMatchers.ts
2743
+ function extractAccount(line, pattern) {
2744
+ const match = line.match(pattern);
2745
+ return match ? match[1].trim() : null;
2746
+ }
2747
+ var JOURNAL_ACCOUNT_DECL, RULES_ACCOUNT_DIRECTIVE, RULES_ACCOUNT2_DIRECTIVE, TX_HEADER_PATTERN;
2748
+ var init_journalMatchers = __esm(() => {
2749
+ JOURNAL_ACCOUNT_DECL = /^account\s+(.+)$/;
2750
+ RULES_ACCOUNT_DIRECTIVE = /^account\d+\s+(.+)$/;
2751
+ RULES_ACCOUNT2_DIRECTIVE = /^\s*account2\s+(.+)$/;
2752
+ TX_HEADER_PATTERN = /^(\d{4})-(\d{2}-\d{2})(\s+(.+))?$/;
2753
+ });
2754
+
2742
2755
  // node_modules/papaparse/papaparse.js
2743
2756
  var require_papaparse = __commonJS((exports, module) => {
2744
2757
  (function(root, factory) {
@@ -4258,19 +4271,6 @@ var require_brace_expansion = __commonJS((exports, module) => {
4258
4271
  }
4259
4272
  });
4260
4273
 
4261
- // src/utils/journalMatchers.ts
4262
- function extractAccount(line, pattern) {
4263
- const match2 = line.match(pattern);
4264
- return match2 ? match2[1].trim() : null;
4265
- }
4266
- var JOURNAL_ACCOUNT_DECL, RULES_ACCOUNT_DIRECTIVE, RULES_ACCOUNT2_DIRECTIVE, TX_HEADER_PATTERN;
4267
- var init_journalMatchers = __esm(() => {
4268
- JOURNAL_ACCOUNT_DECL = /^account\s+(.+)$/;
4269
- RULES_ACCOUNT_DIRECTIVE = /^account\d+\s+(.+)$/;
4270
- RULES_ACCOUNT2_DIRECTIVE = /^\s*account2\s+(.+)$/;
4271
- TX_HEADER_PATTERN = /^(\d{4})-(\d{2}-\d{2})(\s+(.+))?$/;
4272
- });
4273
-
4274
4274
  // src/utils/accountSuggester.ts
4275
4275
  var exports_accountSuggester = {};
4276
4276
  __export(exports_accountSuggester, {
@@ -17012,6 +17012,7 @@ function ensureDirectory(dirPath) {
17012
17012
  }
17013
17013
 
17014
17014
  // src/utils/journalUtils.ts
17015
+ init_journalMatchers();
17015
17016
  function extractDateFromPriceLine(line) {
17016
17017
  return line.split(" ")[1];
17017
17018
  }
@@ -17097,6 +17098,37 @@ function ensureCommodityDeclarations(commodityJournalPath, symbols, logger) {
17097
17098
  logger?.info(`Commodity declarations: added ${missing.length} (${missing.join(", ")})`);
17098
17099
  return { added: missing.sort(), updated: true };
17099
17100
  }
17101
+ function ensureInvestmentAccountDeclarations(accountJournalPath, accounts, logger) {
17102
+ const existing = new Set;
17103
+ if (fs4.existsSync(accountJournalPath)) {
17104
+ const content2 = fs4.readFileSync(accountJournalPath, "utf-8");
17105
+ for (const line of content2.split(`
17106
+ `)) {
17107
+ const match = line.trim().match(JOURNAL_ACCOUNT_DECL);
17108
+ if (match) {
17109
+ existing.add(match[1]);
17110
+ }
17111
+ }
17112
+ }
17113
+ const missing = [];
17114
+ for (const account of accounts) {
17115
+ if (!existing.has(account)) {
17116
+ missing.push(account);
17117
+ existing.add(account);
17118
+ }
17119
+ }
17120
+ if (missing.length === 0) {
17121
+ return { added: [], updated: false };
17122
+ }
17123
+ const sorted = Array.from(existing).sort((a, b) => a.localeCompare(b));
17124
+ const content = sorted.map((a) => `account ${a}`).join(`
17125
+ `) + `
17126
+ `;
17127
+ ensureDirectory(path3.dirname(accountJournalPath));
17128
+ fs4.writeFileSync(accountJournalPath, content);
17129
+ logger?.info(`Account declarations: added ${missing.length} (${missing.join(", ")})`);
17130
+ return { added: missing.sort(), updated: true };
17131
+ }
17100
17132
 
17101
17133
  // src/utils/dateUtils.ts
17102
17134
  function formatDateISO(date5) {
@@ -25033,7 +25065,7 @@ function removeLots(inventory, symbol2, logger) {
25033
25065
  return [];
25034
25066
  }
25035
25067
  const removedLots = [...lots];
25036
- delete inventory[symbol2];
25068
+ inventory[symbol2] = [];
25037
25069
  const totalQuantity = removedLots.reduce((sum, lot) => sum + lot.quantity, 0);
25038
25070
  const totalCost = removedLots.reduce((sum, lot) => sum + lot.quantity * lot.costBasis, 0);
25039
25071
  logger?.info(`Removed ${removedLots.length} lots for ${symbol2}: ${totalQuantity} shares, cost basis ${totalCost}`);
@@ -25202,17 +25234,18 @@ function generateWorthlessEntry(action, removedLots, logger) {
25202
25234
  `;
25203
25235
  return entry;
25204
25236
  }
25205
- function generateMultiWayMergerEntry(group, crossCurrencyOutgoingSymbols, logger) {
25237
+ function generateMultiWayMergerEntry(group, crossCurrencyOutgoingSymbols, crossCurrencyOutgoingIsins, logger) {
25206
25238
  const date5 = formatDate(group.date);
25207
25239
  const outSymbols = crossCurrencyOutgoingSymbols ?? group.outgoing.map((a) => a.symbol);
25208
25240
  const inSymbols = group.incoming.map((a) => a.symbol);
25209
- const description = escapeDescription(`Merger: ${outSymbols.join(" + ")} -> ${inSymbols.join(" + ")}`);
25241
+ const descriptionParts = outSymbols.join(" + ");
25242
+ const description = escapeDescription(inSymbols.length > 0 ? `Merger: ${descriptionParts} -> ${inSymbols.join(" + ")}` : `Merger: ${descriptionParts}`);
25210
25243
  logger?.debug(`Generating multi-way merger entry: ${outSymbols.join(", ")} -> ${inSymbols.join(", ")}`);
25211
25244
  let entry = `${date5} ${description}
25212
25245
  `;
25213
25246
  entry += ` ; swissquote:order:${group.orderNum}
25214
25247
  `;
25215
- const oldIsins = group.outgoing.map((a) => a.isin).filter(Boolean);
25248
+ const oldIsins = (crossCurrencyOutgoingIsins ?? group.outgoing.map((a) => a.isin)).filter(Boolean);
25216
25249
  const newIsins = group.incoming.map((a) => a.isin).filter(Boolean);
25217
25250
  if (oldIsins.length > 0 || newIsins.length > 0) {
25218
25251
  entry += ` ; Old ISIN: ${oldIsins.join(", ") || "n/a"}, New ISINs: ${newIsins.join(", ") || "n/a"}
@@ -25514,7 +25547,7 @@ function processMultiWayMerger(group, inventory, lotInventoryPath, projectDir, l
25514
25547
  inventory[inc.symbol].push(lot);
25515
25548
  logger?.info(`Cross-currency merger incoming: ${absQty} ${inc.symbol} @ ${costBasisPerUnit.toFixed(2)} ${pendingState.currency}`);
25516
25549
  }
25517
- const entry2 = generateMultiWayMergerEntry(group, pendingState.outgoingSymbols, logger);
25550
+ const entry2 = generateMultiWayMergerEntry(group, pendingState.outgoingSymbols, pendingState.outgoingIsins, logger);
25518
25551
  entries.push(entry2);
25519
25552
  removePendingMerger(projectDir, lotInventoryPath, group.key, logger);
25520
25553
  } else {
@@ -25545,9 +25578,12 @@ function processMultiWayMerger(group, inventory, lotInventoryPath, projectDir, l
25545
25578
  date: group.date,
25546
25579
  orderNum: group.orderNum,
25547
25580
  outgoingSymbols,
25581
+ outgoingIsins: group.outgoing.map((a) => a.isin),
25548
25582
  totalCostBasis,
25549
25583
  currency: outgoingCurrency || "CAD"
25550
25584
  }, logger);
25585
+ const entry2 = generateMultiWayMergerEntry(group, undefined, undefined, logger);
25586
+ entries.push(entry2);
25551
25587
  logger?.info(`Cross-currency merger outgoing: ${outgoingSymbols.join(", ")} -> pending (cost basis: ${totalCostBasis.toFixed(2)})`);
25552
25588
  return entries;
25553
25589
  }
@@ -25576,7 +25612,7 @@ function processMultiWayMerger(group, inventory, lotInventoryPath, projectDir, l
25576
25612
  inventory[inc.symbol].push(lot);
25577
25613
  logger?.debug(`Merger incoming: added ${absQty} ${inc.symbol} @ ${costBasisPerUnit.toFixed(2)}`);
25578
25614
  }
25579
- const entry = generateMultiWayMergerEntry(group, undefined, logger);
25615
+ const entry = generateMultiWayMergerEntry(group, undefined, undefined, logger);
25580
25616
  entries.push(entry);
25581
25617
  return entries;
25582
25618
  }
@@ -25830,6 +25866,20 @@ async function preprocessSwissquote(csvPath, projectDir, currency, year, lotInve
25830
25866
  if (stockSymbols.size > 0) {
25831
25867
  const commodityJournalPath = path13.join(projectDir, "ledger", "investments", "commodities.journal");
25832
25868
  ensureCommodityDeclarations(commodityJournalPath, stockSymbols, logger);
25869
+ const accountJournalPath = path13.join(projectDir, "ledger", "investments", "accounts.journal");
25870
+ const investmentAccounts = new Set;
25871
+ for (const symbol2 of stockSymbols) {
25872
+ investmentAccounts.add(`assets:investments:stocks:${symbol2}`);
25873
+ investmentAccounts.add(`income:dividends:${symbol2}`);
25874
+ }
25875
+ investmentAccounts.add(`assets:broker:swissquote:${currency.toLowerCase()}`);
25876
+ investmentAccounts.add("expenses:fees:trading:swissquote");
25877
+ investmentAccounts.add("expenses:taxes:withholding");
25878
+ investmentAccounts.add("income:capital-gains:realized");
25879
+ investmentAccounts.add("income:capital-gains:rights-distribution");
25880
+ investmentAccounts.add("expenses:losses:capital");
25881
+ investmentAccounts.add("equity:conversion");
25882
+ ensureInvestmentAccountDeclarations(accountJournalPath, investmentAccounts, logger);
25833
25883
  }
25834
25884
  logger?.logResult({
25835
25885
  totalRows: stats.totalRows,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzzle/opencode-accountant",
3
- "version": "0.10.8",
3
+ "version": "0.11.0",
4
4
  "description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
5
5
  "author": {
6
6
  "name": "ali bengali",