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