@fuzzle/opencode-accountant 0.16.5 → 0.16.6-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 +73 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17850,7 +17850,7 @@ function extractMetadata2(detection) {
17850
17850
  fromDate: metadata["from-date"],
17851
17851
  untilDate: metadata["until-date"],
17852
17852
  openingBalance: metadata["opening-balance"],
17853
- closingBalance: metadata["closing-balance"]
17853
+ closingBalance: metadata["closing-balance"] ? parseFloat(metadata["closing-balance"]).toFixed(2) : undefined
17854
17854
  };
17855
17855
  }
17856
17856
  function executeMoves(plannedMoves, config2, unrecognizedDir, directory) {
@@ -26903,6 +26903,16 @@ function generateIbkrBuyEntry(trade, logger) {
26903
26903
  account: `assets:broker:ibkr:${trade.currency.toLowerCase()}`,
26904
26904
  amount: formatAmount(-cashOut, trade.currency)
26905
26905
  });
26906
+ const computedNetCents = Math.round(totalCost * 100) + Math.round(fees * 100);
26907
+ const brokerNetCents = Math.round(cashOut * 100);
26908
+ const roundingCents = brokerNetCents - computedNetCents;
26909
+ if (roundingCents !== 0) {
26910
+ postings.push({
26911
+ account: "equity:rounding",
26912
+ amount: formatAmount(roundingCents / 100, trade.currency)
26913
+ });
26914
+ logger?.debug(`Rounding adjustment: ${roundingCents / 100} ${trade.currency}`);
26915
+ }
26906
26916
  let entry = `${trade.date} ${description}
26907
26917
  `;
26908
26918
  entry += ` ; ibkr:account:${trade.account}
@@ -26952,6 +26962,16 @@ function generateIbkrSellEntry(trade, consumed, logger) {
26952
26962
  amount: formatAmount(-capitalGain, trade.currency)
26953
26963
  });
26954
26964
  }
26965
+ const computedNetCents = Math.round(saleProceeds * 100) - Math.round(fees * 100);
26966
+ const brokerNetCents = Math.round(Math.abs(cashIn) * 100);
26967
+ const roundingCents = computedNetCents - brokerNetCents;
26968
+ if (roundingCents !== 0) {
26969
+ postings.push({
26970
+ account: "equity:rounding",
26971
+ amount: formatAmount(roundingCents / 100, trade.currency)
26972
+ });
26973
+ logger?.debug(`Rounding adjustment: ${roundingCents / 100} ${trade.currency}`);
26974
+ }
26955
26975
  let entry = `${trade.date} ${description}
26956
26976
  `;
26957
26977
  entry += ` ; ibkr:account:${trade.account}
@@ -26981,6 +27001,16 @@ function generateIbkrDividendEntry(dividend, logger) {
26981
27001
  account: `income:dividends:${dividend.symbol}`,
26982
27002
  amount: formatAmount(-dividend.grossAmount, dividend.currency)
26983
27003
  });
27004
+ const computedNetCents = Math.round(dividend.grossAmount * 100) - Math.round(dividend.withholdingTax * 100);
27005
+ const brokerNetCents = Math.round(dividend.netAmount * 100);
27006
+ const roundingCents = brokerNetCents - computedNetCents;
27007
+ if (roundingCents !== 0) {
27008
+ postings.push({
27009
+ account: "equity:rounding",
27010
+ amount: formatAmount(roundingCents / 100, dividend.currency)
27011
+ });
27012
+ logger?.debug(`Rounding adjustment: ${roundingCents / 100} ${dividend.currency}`);
27013
+ }
26984
27014
  let entry = `${dividend.date} ${description}
26985
27015
  `;
26986
27016
  entry += ` ; ibkr:account:${dividend.account}
@@ -26997,6 +27027,7 @@ var TAX_TYPE = "Foreign Tax Withholding";
26997
27027
  var ADJUSTMENT_TYPE = "Adjustment";
26998
27028
  var FOREX_TYPE = "Forex Trade Component";
26999
27029
  var DEPOSIT_TYPE = "Deposit";
27030
+ var round2 = (n) => Math.round(n * 100) / 100;
27000
27031
  function parseIbkrCsv(content) {
27001
27032
  const transactions = [];
27002
27033
  const result = import_papaparse3.default.parse(content, {
@@ -27102,6 +27133,8 @@ async function preprocessIbkr(csvPath, directory, currency, year, lotInventoryPa
27102
27133
  const content = fs22.readFileSync(csvPath, "utf-8");
27103
27134
  const transactions = parseIbkrCsv(content);
27104
27135
  logger?.info(`Parsed ${transactions.length} IBKR transactions from ${path17.basename(csvPath)}`);
27136
+ let rawSum = 0;
27137
+ let roundedSum = 0;
27105
27138
  const deposits = [];
27106
27139
  const trades = [];
27107
27140
  const dividendTxns = [];
@@ -27224,6 +27257,7 @@ async function preprocessIbkr(csvPath, directory, currency, year, lotInventoryPa
27224
27257
  const commodityJournalPath = path17.join(directory, "ledger", "investments", "commodities.journal");
27225
27258
  ensureCommodityDeclarations(commodityJournalPath, tradedSymbols, logger);
27226
27259
  }
27260
+ usedAccounts.add("equity:rounding");
27227
27261
  if (usedAccounts.size > 0) {
27228
27262
  const accountJournalPath = path17.join(directory, "ledger", "investments", "accounts.journal");
27229
27263
  ensureInvestmentAccountDeclarations(accountJournalPath, usedAccounts, logger);
@@ -27232,29 +27266,52 @@ async function preprocessIbkr(csvPath, directory, currency, year, lotInventoryPa
27232
27266
  logger?.info(`Generated IBKR journal: ${journalPath} with ${journalEntries.length} entries`);
27233
27267
  }
27234
27268
  const simpleTransactions = [...deposits, ...adjustments, ...forexTxns];
27269
+ for (const d of simpleTransactions) {
27270
+ rawSum += d.netAmount;
27271
+ roundedSum += round2(d.netAmount);
27272
+ }
27273
+ const csvRows = simpleTransactions.map((d) => [
27274
+ d.date,
27275
+ d.account,
27276
+ `"${d.description.replace(/"/g, '""')}"`,
27277
+ d.transactionType,
27278
+ d.symbol,
27279
+ d.quantity || "",
27280
+ d.price || "",
27281
+ d.priceCurrency,
27282
+ d.grossAmount ? round2(d.grossAmount) : "",
27283
+ d.commission ? round2(d.commission) : "",
27284
+ round2(d.netAmount)
27285
+ ].join(","));
27286
+ const roundingAdj = round2(round2(rawSum) - roundedSum);
27287
+ if (roundingAdj !== 0 && Math.abs(roundingAdj) <= 0.05) {
27288
+ const lastDate = transactions[transactions.length - 1]?.date ?? "";
27289
+ const account = transactions[0]?.account ?? "";
27290
+ csvRows.push([
27291
+ lastDate,
27292
+ account,
27293
+ '"Rounding adjustment"',
27294
+ "Rounding",
27295
+ "",
27296
+ "",
27297
+ "",
27298
+ "",
27299
+ "",
27300
+ "",
27301
+ roundingAdj
27302
+ ].join(","));
27303
+ logger?.info(`Added rounding adjustment: ${roundingAdj} CHF`);
27304
+ }
27235
27305
  let simpleTransactionsCsvPath = null;
27236
- if (simpleTransactions.length > 0) {
27306
+ if (csvRows.length > 0) {
27237
27307
  const filteredCsvPath = path17.join(csvDir, `${csvBasename}-filtered.csv`);
27238
27308
  const csvHeader = "Date,Account,Description,Transaction Type,Symbol,Quantity,Price,Price Currency,Gross Amount,Commission,Net Amount";
27239
- const csvRows = simpleTransactions.map((d) => [
27240
- d.date,
27241
- d.account,
27242
- `"${d.description.replace(/"/g, '""')}"`,
27243
- d.transactionType,
27244
- d.symbol,
27245
- d.quantity || "",
27246
- d.price || "",
27247
- d.priceCurrency,
27248
- d.grossAmount ? Math.round(d.grossAmount * 100) / 100 : "",
27249
- d.commission ? Math.round(d.commission * 100) / 100 : "",
27250
- Math.round(d.netAmount * 100) / 100
27251
- ].join(","));
27252
27309
  fs22.writeFileSync(filteredCsvPath, csvHeader + `
27253
27310
  ` + csvRows.join(`
27254
27311
  `) + `
27255
27312
  `);
27256
27313
  simpleTransactionsCsvPath = filteredCsvPath;
27257
- logger?.info(`Generated filtered CSV: ${filteredCsvPath} (${simpleTransactions.length} rows)`);
27314
+ logger?.info(`Generated filtered CSV: ${filteredCsvPath} (${csvRows.length} rows)`);
27258
27315
  }
27259
27316
  const dividendCount = dividendGroups.length;
27260
27317
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzzle/opencode-accountant",
3
- "version": "0.16.5",
3
+ "version": "0.16.6-next.1",
4
4
  "description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
5
5
  "author": {
6
6
  "name": "ali bengali",