@mulmoclaude/accounting-plugin 2.1.0 → 3.0.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 (50) hide show
  1. package/README.md +37 -0
  2. package/dist/server/context.d.ts +45 -3
  3. package/dist/server/context.d.ts.map +1 -1
  4. package/dist/server/eventPublisher.d.ts +9 -4
  5. package/dist/server/eventPublisher.d.ts.map +1 -1
  6. package/dist/server/index.d.ts +1 -0
  7. package/dist/server/index.d.ts.map +1 -1
  8. package/dist/server/router.d.ts +27 -3
  9. package/dist/server/router.d.ts.map +1 -1
  10. package/dist/server/snapshotCache.d.ts +3 -3
  11. package/dist/server/snapshotCache.d.ts.map +1 -1
  12. package/dist/server.cjs +149 -80
  13. package/dist/server.cjs.map +1 -1
  14. package/dist/server.js +149 -80
  15. package/dist/server.js.map +1 -1
  16. package/dist/shared/api.d.ts +12 -0
  17. package/dist/shared/api.d.ts.map +1 -1
  18. package/dist/shared/channels.d.ts +22 -2
  19. package/dist/shared/channels.d.ts.map +1 -1
  20. package/dist/{shared-BZAAF-I4.js → shared-BccLo0Ux.js} +39 -5
  21. package/dist/shared-BccLo0Ux.js.map +1 -0
  22. package/dist/{shared-BpFVwa6Y.cjs → shared-Dgt-i23T.cjs} +50 -4
  23. package/dist/shared-Dgt-i23T.cjs.map +1 -0
  24. package/dist/shared.cjs +3 -1
  25. package/dist/shared.js +2 -2
  26. package/dist/style.css +19 -10
  27. package/dist/vue/View.vue.d.ts +5 -0
  28. package/dist/vue/View.vue.d.ts.map +1 -1
  29. package/dist/vue/api.d.ts +132 -100
  30. package/dist/vue/api.d.ts.map +1 -1
  31. package/dist/vue/components/AccountsModal.vue.d.ts.map +1 -1
  32. package/dist/vue/components/BalanceSheet.vue.d.ts.map +1 -1
  33. package/dist/vue/components/BookSettings.vue.d.ts.map +1 -1
  34. package/dist/vue/components/JournalEntryForm.vue.d.ts.map +1 -1
  35. package/dist/vue/components/JournalList.vue.d.ts.map +1 -1
  36. package/dist/vue/components/Ledger.vue.d.ts.map +1 -1
  37. package/dist/vue/components/NewBookForm.vue.d.ts.map +1 -1
  38. package/dist/vue/components/OpeningBalancesForm.vue.d.ts.map +1 -1
  39. package/dist/vue/components/ProfitLoss.vue.d.ts.map +1 -1
  40. package/dist/vue/hostContext.d.ts +15 -0
  41. package/dist/vue/hostContext.d.ts.map +1 -1
  42. package/dist/vue/useAccountingChannel.d.ts +13 -3
  43. package/dist/vue/useAccountingChannel.d.ts.map +1 -1
  44. package/dist/vue.cjs +227 -98
  45. package/dist/vue.cjs.map +1 -1
  46. package/dist/vue.js +228 -99
  47. package/dist/vue.js.map +1 -1
  48. package/package.json +4 -3
  49. package/dist/shared-BZAAF-I4.js.map +0 -1
  50. package/dist/shared-BpFVwa6Y.cjs.map +0 -1
package/dist/vue.js CHANGED
@@ -1,5 +1,5 @@
1
- import { A as resolveFiscalYearEnd, C as currentFiscalYearRange, E as fiscalYearEndMonthLabel, F as isUnknownArray, H as ACCOUNTING_API, I as ACCOUNTING_BOOKS_CHANNEL, L as BOOK_EVENT_KINDS, M as errorMessage, O as previousFiscalYearRange, P as isRecord, R as bookChannel, S as FISCAL_YEAR_END_MONTHS, U as ACCOUNTING_ACTIONS, _ as SUPPORTED_COUNTRY_CODES, a as localDateString, b as localizedCountryName, c as previousMonthString, d as formatAmountNumeric, i as lastMonthOfPreviousQuarterString, k as previousQuarterRange, l as SUPPORTED_CURRENCY_CODES, m as localizedCurrencyName, o as localMonthString, p as inputStepFor, r as decemberOfPreviousYearString, u as formatAmount, v as countryHasFeature, w as currentQuarterRange, y as isSupportedCountryCode, z as ACCOUNT_TYPES } from "./shared-BZAAF-I4.js";
2
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, onMounted, onUnmounted, openBlock, reactive, ref, renderList, toDisplayString, unref, vModelSelect, vModelText, watch, withDirectives, withKeys, withModifiers } from "vue";
1
+ import { A as resolveFiscalYearEnd, B as ACCOUNT_TYPES, C as currentFiscalYearRange, E as fiscalYearEndMonthLabel, F as isUnknownArray, G as ACCOUNTING_ACTIONS, L as BOOK_EVENT_KINDS, M as errorMessage, O as previousFiscalYearRange, P as isRecord, R as bookChannel, S as FISCAL_YEAR_END_MONTHS, U as ACCOUNTING_API, W as ACCOUNTING_PROJECT_FIELD, _ as SUPPORTED_COUNTRY_CODES, a as localDateString, b as localizedCountryName, c as previousMonthString, d as formatAmountNumeric, i as lastMonthOfPreviousQuarterString, k as previousQuarterRange, l as SUPPORTED_CURRENCY_CODES, m as localizedCurrencyName, o as localMonthString, p as inputStepFor, r as decemberOfPreviousYearString, u as formatAmount, v as countryHasFeature, w as currentQuarterRange, y as isSupportedCountryCode, z as booksChannel } from "./shared-BccLo0Ux.js";
2
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, nextTick, normalizeClass, onMounted, onUnmounted, openBlock, provide, reactive, ref, renderList, toDisplayString, unref, vModelSelect, vModelText, watch, withDirectives, withKeys, withModifiers } from "vue";
3
3
  import { createPluginI18n } from "@mulmoclaude/core/plugin-vue/i18n";
4
4
  //#region src/vue/hostContext.ts
5
5
  var ctx = null;
@@ -22,6 +22,12 @@ function hostSubscribe(channel, handler) {
22
22
  function hostLocaleTag() {
23
23
  return requireCtx().localeTag();
24
24
  }
25
+ /** The host's opaque project id for the mounted surface, or `null` when
26
+ * the host serves a single root (MulmoClaude) or the surface belongs to
27
+ * its default one. */
28
+ function hostProjectScope() {
29
+ return ctx?.projectScope?.() ?? null;
30
+ }
25
31
  //#endregion
26
32
  //#region src/vue/lang/index.ts
27
33
  /** The plugin's i18n composable — a drop-in for vue-i18n's `useI18n()` over the
@@ -1722,78 +1728,163 @@ var useAccountingI18n = createPluginI18n({
1722
1728
  //#region src/vue/api.ts
1723
1729
  var DISPATCH_URL = ACCOUNTING_API.dispatch.path;
1724
1730
  var DISPATCH_METHOD = ACCOUNTING_API.dispatch.method;
1725
- function call(action, args = {}) {
1726
- return hostApiCall(DISPATCH_URL, {
1727
- method: DISPATCH_METHOD,
1728
- body: {
1729
- action,
1730
- ...args
1731
- }
1732
- });
1733
- }
1734
- function getBooks() {
1735
- return call(ACCOUNTING_ACTIONS.getBooks);
1736
- }
1737
- function createBook(input) {
1738
- return call(ACCOUNTING_ACTIONS.createBook, input);
1739
- }
1740
- function updateBook(input) {
1741
- return call(ACCOUNTING_ACTIONS.updateBook, input);
1742
- }
1743
- function deleteBook(bookId) {
1744
- return call(ACCOUNTING_ACTIONS.deleteBook, {
1745
- bookId,
1746
- confirm: true
1747
- });
1748
- }
1749
- function getAccounts(bookId) {
1750
- return call(ACCOUNTING_ACTIONS.getAccounts, { bookId });
1751
- }
1752
- function upsertAccount(account, bookId) {
1753
- return call(ACCOUNTING_ACTIONS.upsertAccount, {
1754
- account,
1755
- bookId
1756
- });
1757
- }
1758
- function addEntries(input) {
1759
- return call(ACCOUNTING_ACTIONS.addEntries, input);
1760
- }
1761
- function voidEntry(input) {
1762
- return call(ACCOUNTING_ACTIONS.voidEntry, input);
1731
+ /** The one call every helper goes through, bound to a scope RESOLVER
1732
+ * rather than a fixed scope, so the host-scoped client tracks a host
1733
+ * that changes its active project while a pinned one does not. Absent
1734
+ * (single-root host) every request body is byte-identical to what it
1735
+ * was before scopes existed. */
1736
+ function makeCall(getScope) {
1737
+ return function call(action, args = {}) {
1738
+ const project = getScope();
1739
+ const scopeField = project ? { [ACCOUNTING_PROJECT_FIELD]: project } : {};
1740
+ return hostApiCall(DISPATCH_URL, {
1741
+ method: DISPATCH_METHOD,
1742
+ body: {
1743
+ action,
1744
+ ...args,
1745
+ ...scopeField
1746
+ }
1747
+ });
1748
+ };
1763
1749
  }
1764
- function getJournalEntries(input) {
1765
- return call(ACCOUNTING_ACTIONS.getJournalEntries, input);
1750
+ /** Books — the list and its lifecycle. */
1751
+ function booksApi(call) {
1752
+ function getBooks() {
1753
+ return call(ACCOUNTING_ACTIONS.getBooks);
1754
+ }
1755
+ function createBook(input) {
1756
+ return call(ACCOUNTING_ACTIONS.createBook, input);
1757
+ }
1758
+ function updateBook(input) {
1759
+ return call(ACCOUNTING_ACTIONS.updateBook, input);
1760
+ }
1761
+ function deleteBook(bookId) {
1762
+ return call(ACCOUNTING_ACTIONS.deleteBook, {
1763
+ bookId,
1764
+ confirm: true
1765
+ });
1766
+ }
1767
+ return {
1768
+ getBooks,
1769
+ createBook,
1770
+ updateBook,
1771
+ deleteBook
1772
+ };
1766
1773
  }
1767
- function getOpeningBalances(bookId) {
1768
- return call(ACCOUNTING_ACTIONS.getOpeningBalances, { bookId });
1774
+ /** The chart of accounts. */
1775
+ function accountsApi(call) {
1776
+ function getAccounts(bookId) {
1777
+ return call(ACCOUNTING_ACTIONS.getAccounts, { bookId });
1778
+ }
1779
+ function upsertAccount(account, bookId) {
1780
+ return call(ACCOUNTING_ACTIONS.upsertAccount, {
1781
+ account,
1782
+ bookId
1783
+ });
1784
+ }
1785
+ return {
1786
+ getAccounts,
1787
+ upsertAccount
1788
+ };
1769
1789
  }
1770
- function setOpeningBalances(input) {
1771
- return call(ACCOUNTING_ACTIONS.setOpeningBalances, input);
1790
+ /** Journal entries and the opening balance. */
1791
+ function journalApi(call) {
1792
+ function addEntries(input) {
1793
+ return call(ACCOUNTING_ACTIONS.addEntries, input);
1794
+ }
1795
+ function voidEntry(input) {
1796
+ return call(ACCOUNTING_ACTIONS.voidEntry, input);
1797
+ }
1798
+ function getJournalEntries(input) {
1799
+ return call(ACCOUNTING_ACTIONS.getJournalEntries, input);
1800
+ }
1801
+ function getOpeningBalances(bookId) {
1802
+ return call(ACCOUNTING_ACTIONS.getOpeningBalances, { bookId });
1803
+ }
1804
+ function setOpeningBalances(input) {
1805
+ return call(ACCOUNTING_ACTIONS.setOpeningBalances, input);
1806
+ }
1807
+ return {
1808
+ addEntries,
1809
+ voidEntry,
1810
+ getJournalEntries,
1811
+ getOpeningBalances,
1812
+ setOpeningBalances
1813
+ };
1772
1814
  }
1773
- function getBalanceSheet(period, bookId) {
1774
- return call(ACCOUNTING_ACTIONS.getReport, {
1775
- kind: "balance",
1776
- period,
1777
- bookId
1778
- });
1815
+ /** Reports, series, and snapshot maintenance. */
1816
+ function reportsApi(call) {
1817
+ function getBalanceSheet(period, bookId) {
1818
+ return call(ACCOUNTING_ACTIONS.getReport, {
1819
+ kind: "balance",
1820
+ period,
1821
+ bookId
1822
+ });
1823
+ }
1824
+ function getProfitLoss(period, bookId) {
1825
+ return call(ACCOUNTING_ACTIONS.getReport, {
1826
+ kind: "pl",
1827
+ period,
1828
+ bookId
1829
+ });
1830
+ }
1831
+ function getLedger(accountCode, period, bookId) {
1832
+ return call(ACCOUNTING_ACTIONS.getReport, {
1833
+ kind: "ledger",
1834
+ accountCode,
1835
+ period,
1836
+ bookId
1837
+ });
1838
+ }
1839
+ function getTimeSeries(input) {
1840
+ return call(ACCOUNTING_ACTIONS.getTimeSeries, { ...input });
1841
+ }
1842
+ function rebuildSnapshots(bookId) {
1843
+ return call(ACCOUNTING_ACTIONS.rebuildSnapshots, { bookId });
1844
+ }
1845
+ return {
1846
+ getBalanceSheet,
1847
+ getProfitLoss,
1848
+ getLedger,
1849
+ getTimeSeries,
1850
+ rebuildSnapshots
1851
+ };
1779
1852
  }
1780
- function getProfitLoss(period, bookId) {
1781
- return call(ACCOUNTING_ACTIONS.getReport, {
1782
- kind: "pl",
1783
- period,
1784
- bookId
1785
- });
1853
+ /** Build an api client bound to ONE project scope. */
1854
+ function makeApi(getScope) {
1855
+ const call = makeCall(getScope);
1856
+ return {
1857
+ ...booksApi(call),
1858
+ ...accountsApi(call),
1859
+ ...journalApi(call),
1860
+ ...reportsApi(call)
1861
+ };
1786
1862
  }
1787
- function getLedger(accountCode, period, bookId) {
1788
- return call(ACCOUNTING_ACTIONS.getReport, {
1789
- kind: "ledger",
1790
- accountCode,
1791
- period,
1792
- bookId
1793
- });
1863
+ /** Follows whatever project the HOST currently considers active. Used
1864
+ * by any surface mounted outside a scoped tree, and by every
1865
+ * single-root host, where the scope is always `null`. */
1866
+ var hostScopedApi = makeApi(hostProjectScope);
1867
+ var API_KEY = Symbol("accounting-api");
1868
+ /** Bind a component tree to the project its surface belongs to.
1869
+ *
1870
+ * A card was opened against the project its server envelope named
1871
+ * (`OpenAppPayload.scope`). If its requests kept resolving the host's
1872
+ * CURRENT project instead, switching projects would silently repoint
1873
+ * an already-open card's reads and writes at another project's books —
1874
+ * same bookId, different company. Call this once in the mounted app's
1875
+ * setup, passing a GETTER: the surface's own scope can change when it
1876
+ * is retargeted at another card, and the tree must follow the card it
1877
+ * is showing rather than the one it first showed. */
1878
+ function provideAccountingApi(getScope) {
1879
+ const api = makeApi(getScope);
1880
+ provide(API_KEY, api);
1881
+ return api;
1794
1882
  }
1795
- function rebuildSnapshots(bookId) {
1796
- return call(ACCOUNTING_ACTIONS.rebuildSnapshots, { bookId });
1883
+ /** The api client for the surrounding tree, falling back to the
1884
+ * host-scoped one for a surface nobody bound (a single-root host's
1885
+ * every surface). */
1886
+ function useAccountingApi() {
1887
+ return inject(API_KEY, hostScopedApi);
1797
1888
  }
1798
1889
  //#endregion
1799
1890
  //#region src/vue/components/NewBookForm.vue?vue&type=script&setup=true&lang.ts
@@ -1840,6 +1931,7 @@ var NewBookForm_default = /* @__PURE__ */ defineComponent({
1840
1931
  },
1841
1932
  emits: ["cancel", "created"],
1842
1933
  setup(__props, { emit: __emit }) {
1934
+ const api = useAccountingApi();
1843
1935
  const { t, locale } = useAccountingI18n();
1844
1936
  function regionFromLocaleTag(tag) {
1845
1937
  try {
@@ -1893,7 +1985,7 @@ var NewBookForm_default = /* @__PURE__ */ defineComponent({
1893
1985
  error.value = null;
1894
1986
  try {
1895
1987
  const pickedCountry = country.value === "" ? void 0 : country.value;
1896
- const result = await createBook({
1988
+ const result = await api.createBook({
1897
1989
  name: name.value.trim(),
1898
1990
  currency: currency.value,
1899
1991
  country: pickedCountry,
@@ -2592,6 +2684,7 @@ var AccountsModal_default = /* @__PURE__ */ defineComponent({
2592
2684
  },
2593
2685
  emits: ["close", "changed"],
2594
2686
  setup(__props, { emit: __emit }) {
2687
+ const api = useAccountingApi();
2595
2688
  const { t } = useAccountingI18n();
2596
2689
  const props = __props;
2597
2690
  const emit = __emit;
@@ -2703,7 +2796,7 @@ var AccountsModal_default = /* @__PURE__ */ defineComponent({
2703
2796
  if (!isNew) {
2704
2797
  if (props.accounts.find((entry) => entry.code === account.code)?.active === false) account.active = false;
2705
2798
  }
2706
- const result = await upsertAccount(account, props.bookId);
2799
+ const result = await api.upsertAccount(account, props.bookId);
2707
2800
  if (!result.ok) {
2708
2801
  error.value = result.error;
2709
2802
  return;
@@ -2731,7 +2824,7 @@ var AccountsModal_default = /* @__PURE__ */ defineComponent({
2731
2824
  };
2732
2825
  if (account.note !== void 0 && account.note.length > 0) next.note = account.note;
2733
2826
  next.active = !willDeactivate;
2734
- const result = await upsertAccount(next, props.bookId);
2827
+ const result = await api.upsertAccount(next, props.bookId);
2735
2828
  if (!result.ok) {
2736
2829
  toggleError.value = result.error;
2737
2830
  return;
@@ -2926,6 +3019,7 @@ var JournalEntryForm_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
2926
3019
  },
2927
3020
  emits: ["submitted", "cancel"],
2928
3021
  setup(__props, { emit: __emit }) {
3022
+ const api = useAccountingApi();
2929
3023
  const { t } = useAccountingI18n();
2930
3024
  const props = __props;
2931
3025
  const emit = __emit;
@@ -3032,7 +3126,7 @@ var JournalEntryForm_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
3032
3126
  const editingId = props.entryToEdit?.id;
3033
3127
  if (editingId) {
3034
3128
  editAttempted.value = true;
3035
- const voidResult = await voidEntry({
3129
+ const voidResult = await api.voidEntry({
3036
3130
  bookId: props.bookId,
3037
3131
  entryId: editingId,
3038
3132
  reason: t("pluginAccounting.entryForm.editVoidReason")
@@ -3042,7 +3136,7 @@ var JournalEntryForm_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
3042
3136
  return;
3043
3137
  }
3044
3138
  }
3045
- const result = await addEntries({
3139
+ const result = await api.addEntries({
3046
3140
  bookId: props.bookId,
3047
3141
  entries: [{
3048
3142
  date: date.value,
@@ -3233,7 +3327,7 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
3233
3327
  };
3234
3328
  //#endregion
3235
3329
  //#region src/vue/components/JournalEntryForm.vue
3236
- var JournalEntryForm_default = /*#__PURE__*/ _plugin_vue_export_helper_default(JournalEntryForm_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-d5dcae53"]]);
3330
+ var JournalEntryForm_default = /*#__PURE__*/ _plugin_vue_export_helper_default(JournalEntryForm_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-8624a7bf"]]);
3237
3331
  //#endregion
3238
3332
  //#region src/vue/components/JournalList.vue?vue&type=script&setup=true&lang.ts
3239
3333
  var _hoisted_1$8 = { class: "flex flex-col h-full gap-3" };
@@ -3345,6 +3439,7 @@ var JournalList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
3345
3439
  },
3346
3440
  emits: ["editOpening", "preselectConsumed"],
3347
3441
  setup(__props, { emit: __emit }) {
3442
+ const api = useAccountingApi();
3348
3443
  const { t } = useAccountingI18n();
3349
3444
  const props = __props;
3350
3445
  const emit = __emit;
@@ -3442,7 +3537,7 @@ var JournalList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
3442
3537
  loading.value = true;
3443
3538
  error.value = null;
3444
3539
  try {
3445
- const result = await getJournalEntries({
3540
+ const result = await api.getJournalEntries({
3446
3541
  bookId: props.bookId,
3447
3542
  from: range.value.from || void 0,
3448
3543
  to: range.value.to || void 0,
@@ -3473,7 +3568,7 @@ var JournalList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
3473
3568
  const reason = window.prompt(t("pluginAccounting.journalList.voidReason"));
3474
3569
  if (reason === null) return;
3475
3570
  try {
3476
- const result = await voidEntry({
3571
+ const result = await api.voidEntry({
3477
3572
  entryId: entry.id,
3478
3573
  reason: reason || void 0,
3479
3574
  bookId: props.bookId
@@ -3665,7 +3760,7 @@ var JournalList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
3665
3760
  ]);
3666
3761
  };
3667
3762
  }
3668
- }), [["__scopeId", "data-v-8ec9519f"]]);
3763
+ }), [["__scopeId", "data-v-6d11cf47"]]);
3669
3764
  //#endregion
3670
3765
  //#region src/vue/components/OpeningBalancesForm.vue?vue&type=script&setup=true&lang.ts
3671
3766
  var _hoisted_1$7 = { class: "flex items-center justify-between gap-2" };
@@ -3738,6 +3833,7 @@ var OpeningBalancesForm_default = /*#__PURE__*/ _plugin_vue_export_helper_defaul
3738
3833
  },
3739
3834
  emits: ["submitted"],
3740
3835
  setup(__props, { emit: __emit }) {
3836
+ const api = useAccountingApi();
3741
3837
  const { t } = useAccountingI18n();
3742
3838
  const props = __props;
3743
3839
  const emit = __emit;
@@ -3816,7 +3912,7 @@ var OpeningBalancesForm_default = /*#__PURE__*/ _plugin_vue_export_helper_defaul
3816
3912
  async function loadExisting() {
3817
3913
  const token = beginLoad();
3818
3914
  const next = freshRows();
3819
- const result = await getOpeningBalances(props.bookId);
3915
+ const result = await api.getOpeningBalances(props.bookId);
3820
3916
  if (!isCurrentLoad(token)) return;
3821
3917
  if (!result.ok) {
3822
3918
  existing.value = null;
@@ -3839,7 +3935,7 @@ var OpeningBalancesForm_default = /*#__PURE__*/ _plugin_vue_export_helper_defaul
3839
3935
  error.value = null;
3840
3936
  successMessage.value = null;
3841
3937
  try {
3842
- const result = await setOpeningBalances({
3938
+ const result = await api.setOpeningBalances({
3843
3939
  bookId: props.bookId,
3844
3940
  asOfDate: asOfDate.value,
3845
3941
  lines: toApiLines()
@@ -3950,7 +4046,7 @@ var OpeningBalancesForm_default = /*#__PURE__*/ _plugin_vue_export_helper_defaul
3950
4046
  }, null, 8, ["book-id", "accounts"])) : createCommentVNode("", true)], 64);
3951
4047
  };
3952
4048
  }
3953
- }), [["__scopeId", "data-v-eb577d9f"]]);
4049
+ }), [["__scopeId", "data-v-08e2a48f"]]);
3954
4050
  //#endregion
3955
4051
  //#region src/vue/components/AccountsList.vue?vue&type=script&setup=true&lang.ts
3956
4052
  var _hoisted_1$6 = {
@@ -4111,6 +4207,7 @@ var Ledger_default = /* @__PURE__ */ defineComponent({
4111
4207
  preselectAccountCode: {}
4112
4208
  },
4113
4209
  setup(__props) {
4210
+ const api = useAccountingApi();
4114
4211
  const { t } = useAccountingI18n();
4115
4212
  const props = __props;
4116
4213
  const accountCode = ref("");
@@ -4150,7 +4247,7 @@ var Ledger_default = /* @__PURE__ */ defineComponent({
4150
4247
  loading.value = true;
4151
4248
  error.value = null;
4152
4249
  try {
4153
- const result = await getLedger(accountCode.value, periodFromRange(range.value), props.bookId);
4250
+ const result = await api.getLedger(accountCode.value, periodFromRange(range.value), props.bookId);
4154
4251
  if (!isCurrent(token)) return;
4155
4252
  if (!result.ok) {
4156
4253
  error.value = result.error;
@@ -4292,6 +4389,7 @@ var BalanceSheet_default = /* @__PURE__ */ defineComponent({
4292
4389
  },
4293
4390
  emits: ["selectAccount"],
4294
4391
  setup(__props, { emit: __emit }) {
4392
+ const api = useAccountingApi();
4295
4393
  const { t } = useAccountingI18n();
4296
4394
  const props = __props;
4297
4395
  const emit = __emit;
@@ -4348,7 +4446,7 @@ var BalanceSheet_default = /* @__PURE__ */ defineComponent({
4348
4446
  loading.value = true;
4349
4447
  error.value = null;
4350
4448
  try {
4351
- const result = await getBalanceSheet({
4449
+ const result = await api.getBalanceSheet({
4352
4450
  kind: "month",
4353
4451
  period: period.value
4354
4452
  }, props.bookId);
@@ -4483,6 +4581,7 @@ var ProfitLoss_default = /* @__PURE__ */ defineComponent({
4483
4581
  },
4484
4582
  emits: ["selectAccount"],
4485
4583
  setup(__props, { emit: __emit }) {
4584
+ const api = useAccountingApi();
4486
4585
  const { t } = useAccountingI18n();
4487
4586
  const props = __props;
4488
4587
  const emit = __emit;
@@ -4507,10 +4606,12 @@ var ProfitLoss_default = /* @__PURE__ */ defineComponent({
4507
4606
  loading.value = true;
4508
4607
  error.value = null;
4509
4608
  try {
4510
- const result = await getProfitLoss({
4609
+ const fromBound = range.value.from || "0000-01-01";
4610
+ const toBound = range.value.to || "9999-12-31";
4611
+ const result = await api.getProfitLoss({
4511
4612
  kind: "range",
4512
- from: range.value.from || "0000-01-01",
4513
- to: range.value.to || "9999-12-31"
4613
+ from: fromBound,
4614
+ to: toBound
4514
4615
  }, props.bookId);
4515
4616
  if (!isCurrent(token)) return;
4516
4617
  if (!result.ok) {
@@ -4654,6 +4755,7 @@ var BookSettings_default = /* @__PURE__ */ defineComponent({
4654
4755
  },
4655
4756
  emits: ["deleted", "books-changed"],
4656
4757
  setup(__props, { emit: __emit }) {
4758
+ const api = useAccountingApi();
4657
4759
  const { t, locale } = useAccountingI18n();
4658
4760
  const props = __props;
4659
4761
  const emit = __emit;
@@ -4690,7 +4792,7 @@ var BookSettings_default = /* @__PURE__ */ defineComponent({
4690
4792
  rebuildOk.value = null;
4691
4793
  rebuildError.value = null;
4692
4794
  try {
4693
- const result = await rebuildSnapshots(props.bookId);
4795
+ const result = await api.rebuildSnapshots(props.bookId);
4694
4796
  if (!result.ok) {
4695
4797
  rebuildError.value = result.error;
4696
4798
  return;
@@ -4708,7 +4810,7 @@ var BookSettings_default = /* @__PURE__ */ defineComponent({
4708
4810
  try {
4709
4811
  const rawCountry = selectedCountry.value;
4710
4812
  const country = rawCountry === "" || isSupportedCountryCode(rawCountry) ? rawCountry : "";
4711
- const result = await updateBook({
4813
+ const result = await api.updateBook({
4712
4814
  bookId: props.bookId,
4713
4815
  name: selectedName.value.trim(),
4714
4816
  country,
@@ -4729,7 +4831,7 @@ var BookSettings_default = /* @__PURE__ */ defineComponent({
4729
4831
  deleting.value = true;
4730
4832
  deleteError.value = null;
4731
4833
  try {
4732
- const result = await deleteBook(props.bookId);
4834
+ const result = await api.deleteBook(props.bookId);
4733
4835
  if (!result.ok) {
4734
4836
  deleteError.value = result.error;
4735
4837
  return;
@@ -4860,7 +4962,20 @@ function toBookChannelPayload(data) {
4860
4962
  period
4861
4963
  };
4862
4964
  }
4863
- function useAccountingChannel(bookId, onPayload) {
4965
+ /** Resolves the scope REACTIVELY, so a subscription re-binds instead of
4966
+ * sitting on a channel that stopped being the caller's. A host whose
4967
+ * `projectScope` getter reads reactive state gets that for free on the
4968
+ * host-following path too; one that does not simply never changes. */
4969
+ function resolveScope(scope) {
4970
+ return computed(() => scope === void 0 ? hostProjectScope() : typeof scope === "function" ? scope() : scope);
4971
+ }
4972
+ /** `scope` binds the subscription to one project — pass the scope the
4973
+ * surface was opened for. Omit it and the channel follows whatever
4974
+ * project the host currently considers active, which is right for a
4975
+ * surface the host itself scopes and always `null` on a single-root
4976
+ * host. */
4977
+ function useAccountingChannel(bookId, onPayload, scope) {
4978
+ const channelScope = resolveScope(scope);
4864
4979
  const version = ref(0);
4865
4980
  let unsubscribe = null;
4866
4981
  function bind(nextBookId) {
@@ -4868,13 +4983,13 @@ function useAccountingChannel(bookId, onPayload) {
4868
4983
  unsubscribe = null;
4869
4984
  version.value = 0;
4870
4985
  if (!nextBookId) return;
4871
- unsubscribe = hostSubscribe(bookChannel(nextBookId), (data) => {
4986
+ unsubscribe = hostSubscribe(bookChannel(nextBookId, channelScope.value), (data) => {
4872
4987
  version.value += 1;
4873
4988
  const event = toBookChannelPayload(data);
4874
4989
  if (event) onPayload?.(event);
4875
4990
  });
4876
4991
  }
4877
- watch(bookId, bind, { immediate: true });
4992
+ watch([bookId, channelScope], ([nextBookId]) => bind(nextBookId), { immediate: true });
4878
4993
  onUnmounted(() => {
4879
4994
  unsubscribe?.();
4880
4995
  unsubscribe = null;
@@ -4883,10 +4998,19 @@ function useAccountingChannel(bookId, onPayload) {
4883
4998
  }
4884
4999
  /** Subscribe to "the list of books changed" events. Use in
4885
5000
  * BookSwitcher.vue to refetch the dropdown contents when a sibling
4886
- * tab adds / deletes a book. */
4887
- function useAccountingBooksChannel(onChange) {
4888
- const unsubscribe = hostSubscribe(ACCOUNTING_BOOKS_CHANNEL, onChange);
4889
- onUnmounted(() => unsubscribe());
5001
+ * tab adds / deletes a book. Re-binds on a scope change, like
5002
+ * `useAccountingChannel`. */
5003
+ function useAccountingBooksChannel(onChange, scope) {
5004
+ const channelScope = resolveScope(scope);
5005
+ let unsubscribe = null;
5006
+ watch(channelScope, (nextScope) => {
5007
+ unsubscribe?.();
5008
+ unsubscribe = hostSubscribe(booksChannel(nextScope), onChange);
5009
+ }, { immediate: true });
5010
+ onUnmounted(() => {
5011
+ unsubscribe?.();
5012
+ unsubscribe = null;
5013
+ });
4890
5014
  }
4891
5015
  //#endregion
4892
5016
  //#region src/vue/View.vue?vue&type=script&setup=true&lang.ts
@@ -4990,6 +5114,11 @@ var View_default = /* @__PURE__ */ defineComponent({
4990
5114
  return TAB_KEYS.some((key) => key === value);
4991
5115
  }
4992
5116
  const initialPayload = computed(() => props.selectedResult?.data ?? props.selectedResult?.jsonData ?? {});
5117
+ const cardScope = computed(() => {
5118
+ const payload = initialPayload.value;
5119
+ return "scope" in payload ? payload.scope ?? null : hostProjectScope();
5120
+ });
5121
+ const api = provideAccountingApi(() => cardScope.value);
4993
5122
  const initialTab = computed(() => isTabKey(initialPayload.value.initialTab) ? initialPayload.value.initialTab : "journal");
4994
5123
  const currentTab = ref(initialTab.value);
4995
5124
  const books = ref([]);
@@ -5008,8 +5137,8 @@ var View_default = /* @__PURE__ */ defineComponent({
5008
5137
  const activeCurrency = computed(() => activeBook.value?.currency ?? "USD");
5009
5138
  const activeCountry = computed(() => activeBook.value?.country);
5010
5139
  const activeFiscalYearEnd = computed(() => activeBook.value?.fiscalYearEnd);
5011
- const { version: bookVersion } = useAccountingChannel(activeBookId);
5012
- useAccountingBooksChannel(() => void refetchBooks());
5140
+ const { version: bookVersion } = useAccountingChannel(activeBookId, void 0, () => cardScope.value);
5141
+ useAccountingBooksChannel(() => void refetchBooks(), () => cardScope.value);
5013
5142
  function pickInitialBookId() {
5014
5143
  const [firstBook] = books.value;
5015
5144
  if (!firstBook) return null;
@@ -5022,7 +5151,7 @@ var View_default = /* @__PURE__ */ defineComponent({
5022
5151
  bookLoadError.value = null;
5023
5152
  const previousActive = activeBook.value;
5024
5153
  try {
5025
- const result = await getBooks();
5154
+ const result = await api.getBooks();
5026
5155
  if (!result.ok) {
5027
5156
  bookLoadError.value = result.error;
5028
5157
  return;
@@ -5062,7 +5191,7 @@ var View_default = /* @__PURE__ */ defineComponent({
5062
5191
  accounts.value = [];
5063
5192
  return;
5064
5193
  }
5065
- const result = await getAccounts(activeBookId.value);
5194
+ const result = await api.getAccounts(activeBookId.value);
5066
5195
  if (!result.ok) return;
5067
5196
  accounts.value = result.data.accounts;
5068
5197
  }
@@ -5072,7 +5201,7 @@ var View_default = /* @__PURE__ */ defineComponent({
5072
5201
  activeOpeningDate.value = void 0;
5073
5202
  return;
5074
5203
  }
5075
- const result = await getOpeningBalances(activeBookId.value);
5204
+ const result = await api.getOpeningBalances(activeBookId.value);
5076
5205
  if (!result.ok) return;
5077
5206
  hasOpening.value = result.data.opening !== null;
5078
5207
  activeOpeningDate.value = result.data.opening?.date;