@fuzzle/opencode-accountant 0.12.2-next.1 → 0.13.0-next.1

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 +49 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24745,8 +24745,18 @@ function generateBtcPurchaseJournal(fiatCsvPaths, btcCsvPath, yearJournalPath, l
24745
24745
  }
24746
24746
  const newEntries = [];
24747
24747
  let skippedDuplicates = 0;
24748
+ const accountsUsed = new Set;
24748
24749
  const sortedMatches = [...matches].sort((a, b) => a.fiatRow.date.getTime() - b.fiatRow.date.getTime());
24749
24750
  for (const match2 of sortedMatches) {
24751
+ const fiat = match2.fiatRow.currency.toLowerCase();
24752
+ const pair = `${fiat}-btc`;
24753
+ accountsUsed.add(`assets:bank:revolut:${fiat}`);
24754
+ accountsUsed.add(`equity:conversion:${pair}:${fiat}`);
24755
+ accountsUsed.add(`equity:conversion:${pair}:btc`);
24756
+ accountsUsed.add("assets:bank:revolut:btc");
24757
+ if (match2.btcRow.fees.amount > 0) {
24758
+ accountsUsed.add("expenses:fees:btc");
24759
+ }
24750
24760
  if (isDuplicate(match2, journalContent)) {
24751
24761
  skippedDuplicates++;
24752
24762
  logger?.debug(`Skipping duplicate: ${match2.fiatRow.dateStr} ${formatAmount(match2.fiatRow.amount)} ${match2.fiatRow.currency}`);
@@ -24770,7 +24780,8 @@ function generateBtcPurchaseJournal(fiatCsvPaths, btcCsvPath, yearJournalPath, l
24770
24780
  entriesAdded: newEntries.length,
24771
24781
  skippedDuplicates,
24772
24782
  unmatchedFiat,
24773
- unmatchedBtc
24783
+ unmatchedBtc,
24784
+ accountsUsed
24774
24785
  };
24775
24786
  }
24776
24787
 
@@ -26054,7 +26065,13 @@ function generateCurrencyExchangeJournal(fiatCsvPaths, yearJournalPath, logger)
26054
26065
  }
26055
26066
  if (rowsByCsv.size < 2) {
26056
26067
  logger?.info("Need at least 2 CSVs with EXCHANGE rows to match pairs, skipping");
26057
- return { matchCount: 0, entriesAdded: 0, skippedDuplicates: 0, matchedRowIndices: new Map };
26068
+ return {
26069
+ matchCount: 0,
26070
+ entriesAdded: 0,
26071
+ skippedDuplicates: 0,
26072
+ matchedRowIndices: new Map,
26073
+ accountsUsed: new Set
26074
+ };
26058
26075
  }
26059
26076
  logger?.info("Matching exchange pairs across CSVs...");
26060
26077
  const matches = matchExchangePairs(rowsByCsv);
@@ -26065,8 +26082,16 @@ function generateCurrencyExchangeJournal(fiatCsvPaths, yearJournalPath, logger)
26065
26082
  }
26066
26083
  const newEntries = [];
26067
26084
  let skippedDuplicates = 0;
26085
+ const accountsUsed = new Set;
26068
26086
  const sortedMatches = [...matches].sort((a, b) => a.source.date.getTime() - b.source.date.getTime());
26069
26087
  for (const match2 of sortedMatches) {
26088
+ const source = match2.source.currency.toLowerCase();
26089
+ const target = match2.target.currency.toLowerCase();
26090
+ const pair = `${source}-${target}`;
26091
+ accountsUsed.add(`assets:bank:revolut:${source}`);
26092
+ accountsUsed.add(`equity:conversion:${pair}:${source}`);
26093
+ accountsUsed.add(`equity:conversion:${pair}:${target}`);
26094
+ accountsUsed.add(`assets:bank:revolut:${target}`);
26070
26095
  if (isDuplicate2(match2, journalContent)) {
26071
26096
  skippedDuplicates++;
26072
26097
  logger?.debug(`Skipping duplicate: ${match2.source.dateStr} ${formatAmount3(match2.source.amount)} ${match2.source.currency} \u2192 ${formatAmount3(match2.target.amount)} ${match2.target.currency}`);
@@ -26098,7 +26123,8 @@ function generateCurrencyExchangeJournal(fiatCsvPaths, yearJournalPath, logger)
26098
26123
  matchCount: matches.length,
26099
26124
  entriesAdded: newEntries.length,
26100
26125
  skippedDuplicates,
26101
- matchedRowIndices
26126
+ matchedRowIndices,
26127
+ accountsUsed
26102
26128
  };
26103
26129
  }
26104
26130
 
@@ -26270,6 +26296,9 @@ async function executeBtcPurchaseStep(context, contextIds, logger) {
26270
26296
  const year = yearMatch ? parseInt(yearMatch[1], 10) : new Date().getFullYear();
26271
26297
  const yearJournalPath = ensureYearJournalExists(context.directory, year);
26272
26298
  const result = generateBtcPurchaseJournal(fiatCsvPaths, btcCsvPath, yearJournalPath, logger);
26299
+ for (const account of result.accountsUsed) {
26300
+ context.generatedAccounts.add(account);
26301
+ }
26273
26302
  const message = result.entriesAdded > 0 ? `Generated ${result.entriesAdded} BTC purchase entries (${result.matchCount} matched, ${result.skippedDuplicates} skipped)` : `No new BTC purchase entries (${result.matchCount} matched, ${result.skippedDuplicates} duplicates)`;
26274
26303
  logger?.logStep("BTC Purchases", "success", message);
26275
26304
  logger?.endSection();
@@ -26304,6 +26333,9 @@ async function executeCurrencyExchangeStep(context, contextIds, logger) {
26304
26333
  const yearJournalPath = ensureYearJournalExists(context.directory, year);
26305
26334
  const fiatCsvPaths = fiatContexts.map((c) => c.csvPath);
26306
26335
  const result = generateCurrencyExchangeJournal(fiatCsvPaths, yearJournalPath, logger);
26336
+ for (const account of result.accountsUsed) {
26337
+ context.generatedAccounts.add(account);
26338
+ }
26307
26339
  if (result.matchedRowIndices.size > 0) {
26308
26340
  for (const { contextId, csvPath } of fiatContexts) {
26309
26341
  const indices = result.matchedRowIndices.get(csvPath);
@@ -26706,7 +26738,8 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
26706
26738
  options,
26707
26739
  configLoader,
26708
26740
  hledgerExecutor,
26709
- result
26741
+ result,
26742
+ generatedAccounts: new Set
26710
26743
  };
26711
26744
  try {
26712
26745
  const contextIds = await executeClassifyStep(context, logger);
@@ -26719,6 +26752,18 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
26719
26752
  await executeBtcPurchaseStep(context, contextIds, logger);
26720
26753
  await executeCurrencyExchangeStep(context, contextIds, logger);
26721
26754
  await executeSwissquotePreprocessStep(context, contextIds, logger);
26755
+ if (context.generatedAccounts.size > 0) {
26756
+ const firstCtx = contextIds.map((id) => loadContext(context.directory, id)).find((c) => c.provider === "revolut");
26757
+ if (firstCtx) {
26758
+ const yearMatch = firstCtx.filePath.match(/(\d{4})-\d{2}-\d{2}/);
26759
+ const year = yearMatch ? parseInt(yearMatch[1], 10) : new Date().getFullYear();
26760
+ const yearJournalPath = ensureYearJournalExists(context.directory, year);
26761
+ const declResult = ensureAccountDeclarations(yearJournalPath, context.generatedAccounts);
26762
+ if (declResult.added.length > 0) {
26763
+ logger.info(`Declared ${declResult.added.length} account(s) from generated entries: ${declResult.added.join(", ")}`);
26764
+ }
26765
+ }
26766
+ }
26722
26767
  const importConfig = loadImportConfig(context.directory);
26723
26768
  const orderedContextIds = [...contextIds].sort((a, b) => {
26724
26769
  const ctxA = loadContext(context.directory, a);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzzle/opencode-accountant",
3
- "version": "0.12.2-next.1",
3
+ "version": "0.13.0-next.1",
4
4
  "description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
5
5
  "author": {
6
6
  "name": "ali bengali",