paisa-mcp 0.0.16 → 0.0.18

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/dist/index.js CHANGED
@@ -45732,8 +45732,39 @@ async function backfillPersons(client, crypto3, opts = {}) {
45732
45732
  }
45733
45733
 
45734
45734
  // src/tools/transactions.ts
45735
+ function dedupeKey(date4, description, amount, type) {
45736
+ return `${date4}|${description}|${amount.toFixed(2)}|${type}`;
45737
+ }
45735
45738
  async function importTransactions(client, crypto3, opts) {
45736
- const rows = opts.txns.map((t2) => {
45739
+ let txns = opts.txns;
45740
+ let duplicatesSkipped = 0;
45741
+ if (txns.length > 0) {
45742
+ const dates = txns.map((t2) => t2.date).sort();
45743
+ const existing = [];
45744
+ let cursor;
45745
+ for (let page = 0;page < 100; page++) {
45746
+ const params = {
45747
+ limit: 200,
45748
+ startDate: dates[0],
45749
+ endDate: dates[dates.length - 1]
45750
+ };
45751
+ if (cursor)
45752
+ params.cursor = cursor;
45753
+ const res = await client.get("/api/transactions", params);
45754
+ const batch = res.data ?? [];
45755
+ if (crypto3)
45756
+ await decryptTransactionFields(batch, crypto3.key);
45757
+ existing.push(...batch);
45758
+ if (!res.hasMore || !res.nextCursor || batch.length === 0)
45759
+ break;
45760
+ cursor = res.nextCursor;
45761
+ }
45762
+ const seen = new Set(existing.map((r2) => dedupeKey(String(r2.date), String(r2.description ?? ""), Number(r2.amount), String(r2.type))));
45763
+ const before = txns.length;
45764
+ txns = txns.filter((t2) => !seen.has(dedupeKey(t2.date, t2.description ?? "", t2.amount, t2.type)));
45765
+ duplicatesSkipped = before - txns.length;
45766
+ }
45767
+ const rows = txns.map((t2) => {
45737
45768
  const enrichedData = settlementEnrichedData(t2.description ?? "", t2.type, opts.source);
45738
45769
  return enrichedData ? { ...t2, enrichedData } : { ...t2 };
45739
45770
  });
@@ -45750,7 +45781,7 @@ async function importTransactions(client, crypto3, opts) {
45750
45781
  });
45751
45782
  imported += res.imported ?? 0;
45752
45783
  }
45753
- return { imported };
45784
+ return { imported, duplicatesSkipped };
45754
45785
  }
45755
45786
  async function runRescan(client, crypto3) {
45756
45787
  const EMPTY_BAG2 = {
@@ -45831,12 +45862,22 @@ function registerTransactionTools(server, client, crypto3) {
45831
45862
  type: exports_external.enum(["debit", "credit"]).optional(),
45832
45863
  amountMin: exports_external.number().optional(),
45833
45864
  amountMax: exports_external.number().optional(),
45834
- limit: exports_external.number().optional().describe("Max results (default 50)"),
45835
- offset: exports_external.number().optional()
45865
+ uncategorized: exports_external.boolean().optional().describe("Only transactions with no category (server-side filter)"),
45866
+ limit: exports_external.number().optional().describe("Max results (default 50, max 200)"),
45867
+ cursor: exports_external.string().optional().describe("Opaque cursor from a prior page's nextCursor")
45836
45868
  }
45837
45869
  }, async (params) => {
45838
- const { personName, ...rest } = params;
45870
+ const { personName, categorySlug, ...rest } = params;
45839
45871
  const apiParams = { ...rest };
45872
+ if (apiParams.uncategorized !== true)
45873
+ delete apiParams.uncategorized;
45874
+ if (categorySlug) {
45875
+ const cats = await client.get("/api/categories");
45876
+ const all2 = cats.data.flatMap((c2) => [c2, ...c2.children ?? []]);
45877
+ const match = all2.find((c2) => c2.slug === categorySlug);
45878
+ if (match)
45879
+ apiParams.categoryId = match.id;
45880
+ }
45840
45881
  if (personName && !apiParams.personId) {
45841
45882
  const persons = await loadDecryptedPersons(client, crypto3);
45842
45883
  const match = resolvePersonByName(persons, personName);
@@ -45867,8 +45908,18 @@ function registerTransactionTools(server, client, crypto3) {
45867
45908
  apiParams.personId = match.personId;
45868
45909
  }
45869
45910
  const data = await client.get("/api/transactions", Object.fromEntries(Object.entries(apiParams).filter(([, v2]) => v2 !== undefined).map(([k2, v2]) => [k2, String(v2)])));
45870
- if (crypto3 && data.data)
45911
+ if (crypto3 && data.data) {
45871
45912
  await decryptTransactionFields(data.data, crypto3.key);
45913
+ if (data.data.some((r2) => r2.personId)) {
45914
+ const persons = await loadDecryptedPersons(client, crypto3);
45915
+ const nameById = new Map(persons.map((p2) => [p2.id, p2.name]));
45916
+ for (const r2 of data.data) {
45917
+ const person = r2.person;
45918
+ if (person && nameById.has(person.id))
45919
+ person.name = nameById.get(person.id);
45920
+ }
45921
+ }
45922
+ }
45872
45923
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
45873
45924
  });
45874
45925
  server.registerTool("backfill_persons", {
@@ -46214,5 +46265,5 @@ main().catch((err) => {
46214
46265
  process.exit(1);
46215
46266
  });
46216
46267
 
46217
- //# debugId=F486FC7CCC9FDB0C64756E2164756E21
46268
+ //# debugId=8B15D0182D36A5DF64756E2164756E21
46218
46269
  //# sourceMappingURL=index.js.map