@mohasinac/appkit 4.62.1 → 4.63.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 (263) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
  3. package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
  4. package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
  5. package/dist/_internal/server/features/ads/booking.d.ts +118 -0
  6. package/dist/_internal/server/features/ads/booking.js +326 -0
  7. package/dist/_internal/server/features/checkout/actions.d.ts +51 -11
  8. package/dist/_internal/server/features/checkout/actions.js +335 -80
  9. package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
  10. package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
  11. package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
  12. package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
  13. package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
  14. package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
  15. package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
  16. package/dist/_internal/server/features/dashboard/attention.js +183 -0
  17. package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
  18. package/dist/_internal/server/features/dashboard/data.js +82 -0
  19. package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
  20. package/dist/_internal/server/features/inventory/reservations.js +273 -0
  21. package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
  22. package/dist/_internal/server/features/offers/destinations.js +65 -0
  23. package/dist/_internal/server/features/orders/adapters.js +21 -0
  24. package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
  25. package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
  26. package/dist/_internal/server/features/orders/balance.d.ts +89 -0
  27. package/dist/_internal/server/features/orders/balance.js +256 -0
  28. package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
  29. package/dist/_internal/server/features/pricing/actions.js +144 -0
  30. package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
  31. package/dist/_internal/server/features/shipping/book-split.js +130 -0
  32. package/dist/_internal/server/features/shipping/book.d.ts +61 -0
  33. package/dist/_internal/server/features/shipping/book.js +176 -0
  34. package/dist/_internal/server/features/shipping/courier-block.js +26 -1
  35. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  36. package/dist/_internal/server/features/site-settings/adapters.js +14 -0
  37. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  38. package/dist/_internal/server/features/stores/adapters.js +14 -0
  39. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  40. package/dist/_internal/server/functions/scheduled.js +57 -1
  41. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +2 -0
  42. package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
  43. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +4 -1
  44. package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
  45. package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
  46. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -0
  47. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  48. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  49. package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
  50. package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
  51. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  52. package/dist/_internal/shared/actions/action-registry.js +268 -0
  53. package/dist/_internal/shared/checkout/order-math.d.ts +31 -2
  54. package/dist/_internal/shared/checkout/order-math.js +22 -36
  55. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  56. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  57. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
  58. package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
  59. package/dist/_internal/shared/checkout/split.d.ts +0 -12
  60. package/dist/_internal/shared/checkout/split.js +22 -2
  61. package/dist/_internal/shared/content/disclaimers.js +7 -7
  62. package/dist/_internal/shared/content/published-values.d.ts +6 -1
  63. package/dist/_internal/shared/content/published-values.js +8 -0
  64. package/dist/_internal/shared/features/admin/integrations.d.ts +32 -2
  65. package/dist/_internal/shared/features/admin/integrations.js +19 -9
  66. package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
  67. package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
  68. package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
  69. package/dist/_internal/shared/features/emi/choice.js +69 -0
  70. package/dist/_internal/shared/features/emi/group.d.ts +84 -0
  71. package/dist/_internal/shared/features/emi/group.js +126 -0
  72. package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
  73. package/dist/_internal/shared/features/ledger/posting.js +165 -0
  74. package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
  75. package/dist/_internal/shared/features/ledger/reports.js +127 -0
  76. package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
  77. package/dist/_internal/shared/features/ledger/rules.js +221 -0
  78. package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
  79. package/dist/_internal/shared/features/offers/coordination.js +80 -0
  80. package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
  81. package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
  82. package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
  83. package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
  84. package/dist/_internal/shared/features/points/accepts.d.ts +1 -8
  85. package/dist/_internal/shared/features/points/accepts.js +12 -6
  86. package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
  87. package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
  88. package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
  89. package/dist/_internal/shared/features/shipping/parcel.js +139 -0
  90. package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
  91. package/dist/_internal/shared/features/site-settings/fees.js +12 -8
  92. package/dist/_internal/shared/fees/explain.d.ts +81 -0
  93. package/dist/_internal/shared/fees/explain.js +101 -0
  94. package/dist/_internal/shared/history/index.d.ts +1 -1
  95. package/dist/_internal/shared/history/index.js +1 -1
  96. package/dist/_internal/shared/history/types.d.ts +7 -1
  97. package/dist/_internal/shared/history/types.js +25 -0
  98. package/dist/client.d.ts +11 -1
  99. package/dist/client.js +13 -0
  100. package/dist/constants/api-endpoints.d.ts +72 -0
  101. package/dist/constants/api-endpoints.js +41 -0
  102. package/dist/constants/field-names.d.ts +41 -0
  103. package/dist/constants/field-names.js +47 -0
  104. package/dist/features/account/components/UserOrdersView.js +28 -2
  105. package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
  106. package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
  107. package/dist/features/admin/components/AdminOrderEditorView.d.ts +16 -1
  108. package/dist/features/admin/components/AdminOrderEditorView.js +72 -3
  109. package/dist/features/admin/components/AdminOrdersView.js +20 -1
  110. package/dist/features/admin/schemas/firestore.d.ts +32 -0
  111. package/dist/features/ads/schemas/booking-write.d.ts +157 -0
  112. package/dist/features/ads/schemas/booking-write.js +88 -0
  113. package/dist/features/ads/schemas/creative-form.d.ts +18 -0
  114. package/dist/features/ads/schemas/creative-form.js +51 -0
  115. package/dist/features/ads/schemas/firestore.d.ts +140 -0
  116. package/dist/features/ads/schemas/firestore.js +109 -0
  117. package/dist/features/auctions/repository/bid.repository.js +3 -1
  118. package/dist/features/audit-log/schemas/firestore.d.ts +110 -0
  119. package/dist/features/audit-log/schemas/firestore.js +74 -0
  120. package/dist/features/auth/actions/profile-actions.d.ts +69 -1
  121. package/dist/features/auth/actions/profile-actions.js +62 -1
  122. package/dist/features/auth/schemas/firestore.d.ts +16 -2
  123. package/dist/features/auth/schemas/firestore.js +2 -1
  124. package/dist/features/blog/actions/blog-actions.d.ts +6 -0
  125. package/dist/features/blog/actions/blog-actions.js +17 -2
  126. package/dist/features/blog/api/route.js +4 -1
  127. package/dist/features/blog/columns/index.js +5 -0
  128. package/dist/features/blog/components/BlogIndexPageView.js +2 -1
  129. package/dist/features/blog/repository/blog.repository.d.ts +4 -0
  130. package/dist/features/blog/repository/blog.repository.js +5 -0
  131. package/dist/features/blog/schemas/blog-form.d.ts +3 -0
  132. package/dist/features/blog/schemas/blog-form.js +1 -0
  133. package/dist/features/blog/schemas/firestore.d.ts +38 -1
  134. package/dist/features/blog/schemas/firestore.js +23 -0
  135. package/dist/features/blog/schemas/index.d.ts +6 -0
  136. package/dist/features/blog/schemas/index.js +2 -0
  137. package/dist/features/blog/types/index.d.ts +2 -0
  138. package/dist/features/cart/components/CartPriceBreakdown.d.ts +12 -1
  139. package/dist/features/cart/components/CartPriceBreakdown.js +24 -5
  140. package/dist/features/cart/repository/cart.repository.d.ts +14 -0
  141. package/dist/features/cart/repository/cart.repository.js +20 -0
  142. package/dist/features/cart/schemas/firestore.d.ts +24 -0
  143. package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
  144. package/dist/features/dashboard/components/DashboardHubView.js +56 -0
  145. package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
  146. package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
  147. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
  148. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
  149. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +24 -0
  150. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +28 -0
  151. package/dist/features/dispute-reasons/schemas/firestore.d.ts +3 -5
  152. package/dist/features/events/components/EventSaleBanner.js +1 -1
  153. package/dist/features/faq/components/FAQPageContent.js +2 -2
  154. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  155. package/dist/features/homepage/schemas/index.d.ts +8 -8
  156. package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
  157. package/dist/features/inventory/schemas/stock-reservation.js +103 -0
  158. package/dist/features/layout/AppLayoutShell.d.ts +9 -1
  159. package/dist/features/layout/AppLayoutShell.js +12 -4
  160. package/dist/features/layout/BottomActions.js +2 -1
  161. package/dist/features/layout/BottomNavbar.d.ts +10 -2
  162. package/dist/features/layout/SideActionDock.js +52 -2
  163. package/dist/features/layout/TitleBarLayout.js +13 -4
  164. package/dist/features/layout/TitleBarLayout.style.css +59 -0
  165. package/dist/features/layout/useCtaReveal.d.ts +16 -1
  166. package/dist/features/layout/useCtaReveal.js +17 -26
  167. package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
  168. package/dist/features/ledger/repository/journal.repository.js +312 -0
  169. package/dist/features/ledger/schemas/expenses.d.ts +87 -0
  170. package/dist/features/ledger/schemas/expenses.js +27 -0
  171. package/dist/features/ledger/schemas/firestore.d.ts +183 -0
  172. package/dist/features/ledger/schemas/firestore.js +69 -0
  173. package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
  174. package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
  175. package/dist/features/orders/components/RaiseDisputeForm.js +19 -11
  176. package/dist/features/orders/repository/orders.repository.d.ts +28 -1
  177. package/dist/features/orders/repository/orders.repository.js +60 -6
  178. package/dist/features/orders/schemas/balance.d.ts +40 -0
  179. package/dist/features/orders/schemas/balance.js +47 -0
  180. package/dist/features/orders/schemas/firestore.d.ts +202 -0
  181. package/dist/features/orders/schemas/firestore.js +43 -0
  182. package/dist/features/orders/schemas/index.d.ts +2 -2
  183. package/dist/features/orders/types/index.d.ts +21 -0
  184. package/dist/features/payments/repository/payout.repository.js +3 -2
  185. package/dist/features/payments/schemas/firestore.d.ts +92 -1
  186. package/dist/features/payments/schemas/firestore.js +23 -0
  187. package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
  188. package/dist/features/pricing/components/AdminPricingView.js +157 -0
  189. package/dist/features/products/schemas/firestore.d.ts +26 -1
  190. package/dist/features/products/schemas/firestore.js +2 -0
  191. package/dist/features/products/schemas/index.d.ts +2 -2
  192. package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
  193. package/dist/features/reviews/repository/reviews.repository.js +16 -0
  194. package/dist/features/scams/repository/scammer.repository.js +2 -1
  195. package/dist/features/scams/schemas/firestore.d.ts +2 -1
  196. package/dist/features/scams/schemas/firestore.js +2 -1
  197. package/dist/features/seller/actions/offer-actions.js +56 -0
  198. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  199. package/dist/features/seller/actions/seller-actions.js +4 -0
  200. package/dist/features/seller/components/DispatchForm.d.ts +11 -0
  201. package/dist/features/seller/components/DispatchForm.js +190 -0
  202. package/dist/features/seller/components/SellerOrdersView.js +94 -2
  203. package/dist/features/seller/components/index.d.ts +1 -0
  204. package/dist/features/seller/components/index.js +1 -0
  205. package/dist/features/seller/repository/offer.repository.js +4 -3
  206. package/dist/features/seller/schemas/firestore.d.ts +23 -2
  207. package/dist/features/seller/schemas/firestore.js +3 -2
  208. package/dist/features/seller/schemas/index.d.ts +4 -4
  209. package/dist/features/seller/schemas/order-forms.d.ts +61 -4
  210. package/dist/features/seller/schemas/order-forms.js +41 -8
  211. package/dist/features/shipments/schemas/firestore.d.ts +25 -0
  212. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +39 -0
  213. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +55 -0
  214. package/dist/features/shipping-boxes/schemas/firestore.d.ts +2 -4
  215. package/dist/features/stores/repository/store.repository.d.ts +18 -0
  216. package/dist/features/stores/repository/store.repository.js +27 -1
  217. package/dist/features/stores/schemas/firestore.d.ts +41 -0
  218. package/dist/features/stores/schemas/firestore.js +20 -0
  219. package/dist/features/stores/schemas/index.d.ts +6 -6
  220. package/dist/features/support/repository/support.repository.js +72 -6
  221. package/dist/features/support/schemas/firestore.d.ts +17 -0
  222. package/dist/features/support/schemas/index.d.ts +2 -2
  223. package/dist/index.d.ts +5 -0
  224. package/dist/index.js +19 -0
  225. package/dist/next/routing/route-map.d.ts +92 -0
  226. package/dist/next/routing/route-map.js +53 -0
  227. package/dist/react/contexts/SessionContext.d.ts +17 -1
  228. package/dist/schemas/registry.d.ts +22 -16
  229. package/dist/security/pii-schemas.d.ts +1 -1
  230. package/dist/security/pii-schemas.js +45 -0
  231. package/dist/server-entry.d.ts +1 -1
  232. package/dist/server-entry.js +10 -1
  233. package/dist/server.d.ts +29 -1
  234. package/dist/server.js +158 -0
  235. package/dist/styles.css +349 -1
  236. package/dist/tailwind-utilities.css +1 -1
  237. package/dist/ui/components/Button.d.ts +10 -1
  238. package/dist/ui/components/Button.js +25 -3
  239. package/dist/ui/components/Chart.d.ts +63 -0
  240. package/dist/ui/components/Chart.js +169 -0
  241. package/dist/ui/components/Chart.style.css +214 -0
  242. package/dist/ui/components/Disclaimer.js +10 -1
  243. package/dist/ui/components/OverflowRow.js +54 -2
  244. package/dist/ui/components/OverflowRow.style.css +38 -0
  245. package/dist/ui/components/RichTextEditor.js +99 -8
  246. package/dist/ui/components/chart-spec.d.ts +68 -0
  247. package/dist/ui/components/chart-spec.js +140 -0
  248. package/dist/ui/components/index.style.css +1 -0
  249. package/dist/ui/components/surface-tokens.d.ts +1 -1
  250. package/dist/ui/components/surface-tokens.js +9 -0
  251. package/dist/ui/components/toolbar-collapse.d.ts +13 -0
  252. package/dist/ui/components/toolbar-collapse.js +10 -1
  253. package/dist/ui/index.d.ts +6 -0
  254. package/dist/ui/index.js +3 -0
  255. package/dist/ui/rich-text/RichText.d.ts +10 -1
  256. package/dist/ui/rich-text/RichText.js +33 -2
  257. package/dist/ui/rich-text/RichText.style.css +35 -0
  258. package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
  259. package/dist/ui/rich-text/RichTextRenderer.js +34 -2
  260. package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
  261. package/dist/ui/rich-text/chart-blocks.js +87 -0
  262. package/package.json +1 -1
  263. package/scripts/seed-cli.mjs +3 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The posting rules — pure, so they can be tested by an audit rather than by
3
+ * a `*.test.ts` the project forbids.
4
+ *
5
+ * Nothing here touches Firestore. `assertBalanced` and `buildReversal` are
6
+ * the two decisions that must never be wrong, and keeping them pure is what
7
+ * lets `verify-posting-rules.mjs` exercise them directly — the same shape as
8
+ * `verify-shipment-allocation-guard.mjs`, which reproduces a real failure
9
+ * against the real data shape and carries negative controls.
10
+ */
11
+ import type { JournalLine, JournalSourceEvent } from "../../../../features/ledger/schemas/firestore";
12
+ /**
13
+ * `YYYY-MM` for a date, in the ONE place that derives it.
14
+ *
15
+ * 🛑 **LOCAL, not UTC, and the difference is a GST return.** A sale at 02:00
16
+ * IST on 1 November is 20:30 UTC on 31 October, so `getUTCMonth()` files it
17
+ * under the wrong month — and the month is exactly what a GSTR-1 period is.
18
+ * Every evening sale in India would land in the previous return.
19
+ *
20
+ * Local is correct because `vercel.json` pins functions to `bom1` and the
21
+ * Firebase region is `asia-south1`, so "local" IS IST on every server that
22
+ * runs this. It also matches `financialYearLabel`, which uses `getMonth()`
23
+ * for the same reason and whose boundary error would be unrecoverable —
24
+ * these two must agree or a March entry could sit in one FY and one period
25
+ * that disagree about which year it belongs to.
26
+ *
27
+ * 🛑 It therefore DISAGREES with nothing by accident but with
28
+ * `payPeriodFor`'s UTC-free local midnight by design — both are local; if
29
+ * either is ever moved to UTC, move both, and expect the invoice series to
30
+ * need the same treatment.
31
+ */
32
+ export declare function periodOf(date: Date): string;
33
+ /**
34
+ * 🛑 Money comparison needs a TOLERANCE, and omitting one is the same bug
35
+ * Root Cause #92 cost a million invocations a day.
36
+ *
37
+ * Every leg here is a rupee amount with at most two decimals, and the
38
+ * arithmetic that produces them (a GST split, a pro-rata allocation) is
39
+ * floating point — `0.1 + 0.2 !== 0.3`. An exact `===` on the two totals
40
+ * would refuse a correct entry whose halves differ in the last bit, and the
41
+ * refusal would look like a posting-rule bug rather than a float.
42
+ *
43
+ * Half of the smallest representable unit (0.005): tight enough that a real
44
+ * 0.01 imbalance still fails, loose enough that no accumulation of
45
+ * two-decimal values can reach it.
46
+ */
47
+ export declare const BALANCE_TOLERANCE = 0.005;
48
+ export interface PostingInput {
49
+ entryDate: Date;
50
+ sourceEvent: JournalSourceEvent;
51
+ sourceId?: string;
52
+ narration: string;
53
+ lines: JournalLine[];
54
+ }
55
+ export interface BalancedPosting extends PostingInput {
56
+ period: string;
57
+ totalDebit: number;
58
+ totalCredit: number;
59
+ }
60
+ export declare class UnbalancedEntryError extends Error {
61
+ constructor(message: string);
62
+ }
63
+ /**
64
+ * Validate a posting and compute its totals, or refuse it.
65
+ *
66
+ * 🛑 **Refuses rather than correcting.** A plausible repair — dropping a
67
+ * zero line, inferring the missing side from the difference — is how a wrong
68
+ * posting rule gets a correct-looking entry written for it, and the books
69
+ * then balance while saying something false. An unbalanced entry is always a
70
+ * bug in the rule that built it, and the only safe response is to not write.
71
+ *
72
+ * The five checks, each of which has a distinct failure it prevents:
73
+ */
74
+ export declare function assertBalanced(input: PostingInput): BalancedPosting;
75
+ /**
76
+ * The reversing entry for a posting — every leg's sides swapped.
77
+ *
78
+ * 🛑 **Swapped, never negated.** `debit: -100` would balance arithmetically
79
+ * and is wrong in every other way: it breaks the non-negative rule above, it
80
+ * makes a trial balance sum to the right number through cancelling errors
81
+ * rather than through correct postings, and a ledger export to Tally would
82
+ * reject it. A credit of 100 against a debit of 100 is what a reversal IS.
83
+ *
84
+ * 🛑 **The reversal's `entryDate` is the CALLER's, not the original's.** A
85
+ * correction discovered in November for an October entry posts in November
86
+ * when October is locked — that is the whole point of the lock, and
87
+ * back-dating the reversal to the original would silently reopen a filed
88
+ * period. The link between them is `reversesEntryId`, not a shared date.
89
+ */
90
+ export declare function buildReversal(original: {
91
+ id: string;
92
+ lines: JournalLine[];
93
+ narration: string;
94
+ }, at: Date, reason: string): PostingInput;
95
+ /**
96
+ * Where a late correction must post.
97
+ *
98
+ * 🛑 Returns the entry's OWN period when it is open, and the caller's
99
+ * fallback when it is not — it never silently moves a posting. A rule that
100
+ * quietly re-dated anything landing in a closed period would make the lock
101
+ * invisible, and the first anyone knew of it would be a GST return that no
102
+ * longer reconciles to the books it was filed from.
103
+ */
104
+ export declare function resolvePostingPeriod(wanted: string, isLocked: (period: string) => boolean, openFallback: string): {
105
+ period: string;
106
+ deferred: boolean;
107
+ };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * The posting rules — pure, so they can be tested by an audit rather than by
3
+ * a `*.test.ts` the project forbids.
4
+ *
5
+ * Nothing here touches Firestore. `assertBalanced` and `buildReversal` are
6
+ * the two decisions that must never be wrong, and keeping them pure is what
7
+ * lets `verify-posting-rules.mjs` exercise them directly — the same shape as
8
+ * `verify-shipment-allocation-guard.mjs`, which reproduces a real failure
9
+ * against the real data shape and carries negative controls.
10
+ */
11
+ /**
12
+ * `YYYY-MM` for a date, in the ONE place that derives it.
13
+ *
14
+ * 🛑 **LOCAL, not UTC, and the difference is a GST return.** A sale at 02:00
15
+ * IST on 1 November is 20:30 UTC on 31 October, so `getUTCMonth()` files it
16
+ * under the wrong month — and the month is exactly what a GSTR-1 period is.
17
+ * Every evening sale in India would land in the previous return.
18
+ *
19
+ * Local is correct because `vercel.json` pins functions to `bom1` and the
20
+ * Firebase region is `asia-south1`, so "local" IS IST on every server that
21
+ * runs this. It also matches `financialYearLabel`, which uses `getMonth()`
22
+ * for the same reason and whose boundary error would be unrecoverable —
23
+ * these two must agree or a March entry could sit in one FY and one period
24
+ * that disagree about which year it belongs to.
25
+ *
26
+ * 🛑 It therefore DISAGREES with nothing by accident but with
27
+ * `payPeriodFor`'s UTC-free local midnight by design — both are local; if
28
+ * either is ever moved to UTC, move both, and expect the invoice series to
29
+ * need the same treatment.
30
+ */
31
+ export function periodOf(date) {
32
+ const y = date.getFullYear();
33
+ const m = String(date.getMonth() + 1).padStart(2, "0");
34
+ return `${y}-${m}`;
35
+ }
36
+ /**
37
+ * 🛑 Money comparison needs a TOLERANCE, and omitting one is the same bug
38
+ * Root Cause #92 cost a million invocations a day.
39
+ *
40
+ * Every leg here is a rupee amount with at most two decimals, and the
41
+ * arithmetic that produces them (a GST split, a pro-rata allocation) is
42
+ * floating point — `0.1 + 0.2 !== 0.3`. An exact `===` on the two totals
43
+ * would refuse a correct entry whose halves differ in the last bit, and the
44
+ * refusal would look like a posting-rule bug rather than a float.
45
+ *
46
+ * Half of the smallest representable unit (0.005): tight enough that a real
47
+ * 0.01 imbalance still fails, loose enough that no accumulation of
48
+ * two-decimal values can reach it.
49
+ */
50
+ export const BALANCE_TOLERANCE = 0.005;
51
+ export class UnbalancedEntryError extends Error {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = "UnbalancedEntryError";
55
+ }
56
+ }
57
+ /**
58
+ * Validate a posting and compute its totals, or refuse it.
59
+ *
60
+ * 🛑 **Refuses rather than correcting.** A plausible repair — dropping a
61
+ * zero line, inferring the missing side from the difference — is how a wrong
62
+ * posting rule gets a correct-looking entry written for it, and the books
63
+ * then balance while saying something false. An unbalanced entry is always a
64
+ * bug in the rule that built it, and the only safe response is to not write.
65
+ *
66
+ * The five checks, each of which has a distinct failure it prevents:
67
+ */
68
+ export function assertBalanced(input) {
69
+ const { lines } = input;
70
+ // 1. A single-legged entry is not double entry. One line can never balance,
71
+ // and the commonest way to get one is a rule that forgot its credit.
72
+ if (lines.length < 2) {
73
+ throw new UnbalancedEntryError(`A journal entry needs at least two lines; got ${lines.length}.`);
74
+ }
75
+ let totalDebit = 0;
76
+ let totalCredit = 0;
77
+ for (const [i, line] of lines.entries()) {
78
+ const d = line.debit ?? 0;
79
+ const c = line.credit ?? 0;
80
+ // 2. NEGATIVE amounts are refused outright. A negative debit is a credit
81
+ // written the wrong way round, and allowing it means every report has
82
+ // to handle a sign it should never see — plus the two totals could
83
+ // then balance while both sides are nonsense.
84
+ if (d < 0 || c < 0) {
85
+ throw new UnbalancedEntryError(`Line ${i} (${line.accountCode}) has a negative amount. A reversal swaps the sides; it never negates them.`);
86
+ }
87
+ // 3. EXACTLY ONE SIDE per line. A line carrying both is ambiguous about
88
+ // what it asserts, and its net effect depends on a subtraction the
89
+ // reader has to perform — which is precisely what two separate fields
90
+ // exist to avoid.
91
+ if (d > 0 && c > 0) {
92
+ throw new UnbalancedEntryError(`Line ${i} (${line.accountCode}) carries both a debit and a credit. Split it into two lines.`);
93
+ }
94
+ // 4. A ZERO line asserts nothing and is almost always a rule that
95
+ // computed an amount it then failed to use.
96
+ if (d === 0 && c === 0) {
97
+ throw new UnbalancedEntryError(`Line ${i} (${line.accountCode}) is zero on both sides.`);
98
+ }
99
+ totalDebit += d;
100
+ totalCredit += c;
101
+ }
102
+ // 5. The accounting identity itself.
103
+ if (Math.abs(totalDebit - totalCredit) > BALANCE_TOLERANCE) {
104
+ throw new UnbalancedEntryError(`Entry does not balance: debits ${totalDebit.toFixed(2)} vs credits ${totalCredit.toFixed(2)}.`);
105
+ }
106
+ return {
107
+ ...input,
108
+ period: periodOf(input.entryDate),
109
+ /*
110
+ * Rounded to two decimals AFTER the comparison, not before. Rounding
111
+ * first would mask a genuine sub-unit imbalance by making both sides
112
+ * equal; rounding after records a figure a human can reconcile against
113
+ * a bank statement.
114
+ */
115
+ totalDebit: Math.round(totalDebit * 100) / 100,
116
+ totalCredit: Math.round(totalCredit * 100) / 100,
117
+ };
118
+ }
119
+ /**
120
+ * The reversing entry for a posting — every leg's sides swapped.
121
+ *
122
+ * 🛑 **Swapped, never negated.** `debit: -100` would balance arithmetically
123
+ * and is wrong in every other way: it breaks the non-negative rule above, it
124
+ * makes a trial balance sum to the right number through cancelling errors
125
+ * rather than through correct postings, and a ledger export to Tally would
126
+ * reject it. A credit of 100 against a debit of 100 is what a reversal IS.
127
+ *
128
+ * 🛑 **The reversal's `entryDate` is the CALLER's, not the original's.** A
129
+ * correction discovered in November for an October entry posts in November
130
+ * when October is locked — that is the whole point of the lock, and
131
+ * back-dating the reversal to the original would silently reopen a filed
132
+ * period. The link between them is `reversesEntryId`, not a shared date.
133
+ */
134
+ export function buildReversal(original, at, reason) {
135
+ return {
136
+ entryDate: at,
137
+ sourceEvent: "reversal",
138
+ sourceId: original.id,
139
+ narration: `Reversal of ${original.id}: ${reason}`,
140
+ lines: original.lines.map((l) => ({
141
+ accountCode: l.accountCode,
142
+ debit: l.credit,
143
+ credit: l.debit,
144
+ ...(l.memo ? { memo: l.memo } : {}),
145
+ ...(l.counterpartyId ? { counterpartyId: l.counterpartyId } : {}),
146
+ })),
147
+ };
148
+ }
149
+ /**
150
+ * Where a late correction must post.
151
+ *
152
+ * 🛑 Returns the entry's OWN period when it is open, and the caller's
153
+ * fallback when it is not — it never silently moves a posting. A rule that
154
+ * quietly re-dated anything landing in a closed period would make the lock
155
+ * invisible, and the first anyone knew of it would be a GST return that no
156
+ * longer reconciles to the books it was filed from.
157
+ */
158
+ export function resolvePostingPeriod(wanted, isLocked, openFallback) {
159
+ if (!isLocked(wanted))
160
+ return { period: wanted, deferred: false };
161
+ if (isLocked(openFallback)) {
162
+ throw new UnbalancedEntryError(`Both ${wanted} and the fallback period ${openFallback} are locked; there is nowhere to post.`);
163
+ }
164
+ return { period: openFallback, deferred: true };
165
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Trial balance and P&L — derived views, pure, so the audit can exercise them.
3
+ *
4
+ * ## 🛑 These are REPORTS, not truth, and they carry the disclaimer
5
+ *
6
+ * Phase 12's `accounting-output` label applies to every figure produced
7
+ * here. The ledger is already a projection of orders and payouts; a report
8
+ * is a projection of the ledger. Nobody files a return from this without a
9
+ * CA reading it, and the label is what stops the number being treated as
10
+ * though they had.
11
+ *
12
+ * ## 🛑 Signs are applied ONCE, at the boundary
13
+ *
14
+ * A trial balance lists raw debit and credit totals per account, which is
15
+ * what makes it a check: the two columns must agree, and that is only
16
+ * meaningful if nothing has been netted on the way. The P&L is where a
17
+ * normal-balance sign is applied, and it is applied in exactly one place
18
+ * (`signedBalance`) so an income account can never be summed as though it
19
+ * were an expense.
20
+ */
21
+ import type { AccountType, JournalEntryDocument, JournalLine } from "../../../../features/ledger/schemas/firestore";
22
+ /** Which side increases this type. Duplicated from the schema's own helper? No — see below. */
23
+ type Side = "debit" | "credit";
24
+ export interface AccountTotals {
25
+ accountCode: string;
26
+ debit: number;
27
+ credit: number;
28
+ }
29
+ export interface TrialBalance {
30
+ period: string;
31
+ rows: AccountTotals[];
32
+ totalDebit: number;
33
+ totalCredit: number;
34
+ /** The identity. False means an entry was written outside `post()`. */
35
+ balanced: boolean;
36
+ /**
37
+ * Propagated from the read that produced it.
38
+ *
39
+ * 🛑 A truncated trial balance BALANCES — both columns lose the same
40
+ * entries — so `balanced: true` with `truncated: true` says nothing at
41
+ * all. A caller that renders the first and ignores the second is showing
42
+ * a reconciliation that did not happen.
43
+ */
44
+ truncated: boolean;
45
+ }
46
+ /** Sum every line by account. Raw columns, nothing netted. */
47
+ export declare function trialBalance(period: string, entries: readonly JournalEntryDocument[], truncated?: boolean): TrialBalance;
48
+ /**
49
+ * The net movement on an account, signed by its normal balance.
50
+ *
51
+ * 🛑 The ONE place a sign is applied. An income account with ₹1,000 credited
52
+ * and ₹100 debited has earned ₹900; the same raw figures on an expense
53
+ * account are a ₹900 loss in the other direction. Applying this per call
54
+ * site is how one of the two gets inverted and a P&L reads profitable.
55
+ */
56
+ export declare function signedBalance(type: AccountType, totals: AccountTotals): number;
57
+ export interface ProfitAndLoss {
58
+ period: string;
59
+ income: number;
60
+ expenses: number;
61
+ net: number;
62
+ truncated: boolean;
63
+ }
64
+ /**
65
+ * Income less expenses for a period.
66
+ *
67
+ * 🛑 Needs the account TYPES, which it takes as a map rather than inferring
68
+ * from the code's leading digit. The range convention is for humans (see the
69
+ * chart's own header); inferring from it makes the first out-of-range
70
+ * account silently land in the wrong half of the P&L, and the total would
71
+ * still look plausible.
72
+ */
73
+ export declare function profitAndLoss(tb: TrialBalance, typeOf: (accountCode: string) => AccountType | undefined): ProfitAndLoss;
74
+ /** Re-exported for the audit, which checks the two mappings agree. */
75
+ export declare const NORMAL_SIDE_TABLE: Readonly<Record<AccountType, Side>>;
76
+ export type { JournalLine };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Trial balance and P&L — derived views, pure, so the audit can exercise them.
3
+ *
4
+ * ## 🛑 These are REPORTS, not truth, and they carry the disclaimer
5
+ *
6
+ * Phase 12's `accounting-output` label applies to every figure produced
7
+ * here. The ledger is already a projection of orders and payouts; a report
8
+ * is a projection of the ledger. Nobody files a return from this without a
9
+ * CA reading it, and the label is what stops the number being treated as
10
+ * though they had.
11
+ *
12
+ * ## 🛑 Signs are applied ONCE, at the boundary
13
+ *
14
+ * A trial balance lists raw debit and credit totals per account, which is
15
+ * what makes it a check: the two columns must agree, and that is only
16
+ * meaningful if nothing has been netted on the way. The P&L is where a
17
+ * normal-balance sign is applied, and it is applied in exactly one place
18
+ * (`signedBalance`) so an income account can never be summed as though it
19
+ * were an expense.
20
+ */
21
+ /*
22
+ * 🛑 `normalBalanceFor` lives on the SCHEMA and is not imported here, for the
23
+ * same mechanical reason `periodOf` moved out of it: `verify-posting-rules`
24
+ * loads these modules through Node's type stripping, which erases
25
+ * `import type` but cannot resolve a runtime import against extensionless
26
+ * appkit source.
27
+ *
28
+ * So this is NOT a second definition — the table below is the same mapping,
29
+ * and the schema's function is what application code calls. Two copies of a
30
+ * rule is Root Cause #42, and the honest mitigation is that `R6` in the
31
+ * audit asserts they agree for all five types rather than trusting a
32
+ * comment to keep them in step.
33
+ */
34
+ const NORMAL_SIDE = {
35
+ asset: "debit",
36
+ expense: "debit",
37
+ liability: "credit",
38
+ equity: "credit",
39
+ income: "credit",
40
+ };
41
+ /** Sum every line by account. Raw columns, nothing netted. */
42
+ export function trialBalance(period, entries, truncated = false) {
43
+ const byAccount = new Map();
44
+ for (const entry of entries) {
45
+ for (const line of entry.lines) {
46
+ const row = byAccount.get(line.accountCode) ??
47
+ { accountCode: line.accountCode, debit: 0, credit: 0 };
48
+ row.debit += line.debit ?? 0;
49
+ row.credit += line.credit ?? 0;
50
+ byAccount.set(line.accountCode, row);
51
+ }
52
+ }
53
+ const rows = [...byAccount.values()]
54
+ .map((r) => ({
55
+ accountCode: r.accountCode,
56
+ debit: round2(r.debit),
57
+ credit: round2(r.credit),
58
+ }))
59
+ .sort((a, b) => a.accountCode.localeCompare(b.accountCode));
60
+ const totalDebit = round2(rows.reduce((s, r) => s + r.debit, 0));
61
+ const totalCredit = round2(rows.reduce((s, r) => s + r.credit, 0));
62
+ return {
63
+ period,
64
+ rows,
65
+ totalDebit,
66
+ totalCredit,
67
+ /*
68
+ * 🛑 Compared with the SAME tolerance the posting rules use. A trial
69
+ * balance stricter than the gate that wrote the entries would report an
70
+ * imbalance nobody can act on — every entry in it passed `assertBalanced`.
71
+ */
72
+ balanced: Math.abs(totalDebit - totalCredit) <= 0.005,
73
+ truncated,
74
+ };
75
+ }
76
+ /**
77
+ * The net movement on an account, signed by its normal balance.
78
+ *
79
+ * 🛑 The ONE place a sign is applied. An income account with ₹1,000 credited
80
+ * and ₹100 debited has earned ₹900; the same raw figures on an expense
81
+ * account are a ₹900 loss in the other direction. Applying this per call
82
+ * site is how one of the two gets inverted and a P&L reads profitable.
83
+ */
84
+ export function signedBalance(type, totals) {
85
+ return NORMAL_SIDE[type] === "debit"
86
+ ? round2(totals.debit - totals.credit)
87
+ : round2(totals.credit - totals.debit);
88
+ }
89
+ /**
90
+ * Income less expenses for a period.
91
+ *
92
+ * 🛑 Needs the account TYPES, which it takes as a map rather than inferring
93
+ * from the code's leading digit. The range convention is for humans (see the
94
+ * chart's own header); inferring from it makes the first out-of-range
95
+ * account silently land in the wrong half of the P&L, and the total would
96
+ * still look plausible.
97
+ */
98
+ export function profitAndLoss(tb, typeOf) {
99
+ let income = 0;
100
+ let expenses = 0;
101
+ for (const row of tb.rows) {
102
+ const type = typeOf(row.accountCode);
103
+ /*
104
+ * 🛑 An UNKNOWN account is skipped and that is why `truncated` is not
105
+ * the only honesty flag this needs — but it is the behaviour with the
106
+ * least bad failure: including it would need a guessed type, and a
107
+ * guessed type puts real money on the wrong side. An account code with
108
+ * no account is a `chartOfAccounts` wipe (the orphan case its
109
+ * `seedPurpose: "permanent"` exists to prevent), and the trial balance
110
+ * above still shows the row, so the discrepancy is visible there.
111
+ */
112
+ if (!type)
113
+ continue;
114
+ if (type === "income")
115
+ income += signedBalance(type, row);
116
+ else if (type === "expense")
117
+ expenses += signedBalance(type, row);
118
+ }
119
+ income = round2(income);
120
+ expenses = round2(expenses);
121
+ return { period: tb.period, income, expenses, net: round2(income - expenses), truncated: tb.truncated };
122
+ }
123
+ function round2(n) {
124
+ return Math.round(n * 100) / 100;
125
+ }
126
+ /** Re-exported for the audit, which checks the two mappings agree. */
127
+ export const NORMAL_SIDE_TABLE = NORMAL_SIDE;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * What each business event posts.
3
+ *
4
+ * ## 🛑 THIS FILE IS THE WHOLE SAFETY ARGUMENT
5
+ *
6
+ * "Every journal entry is machine-generated from a business event" is the
7
+ * one constraint that makes a ledger maintained by a web application
8
+ * trustworthy. If a human has to remember that a gateway fee is
9
+ * `Dr Bank Charges / Cr Bank`, it will eventually be posted backwards — and
10
+ * a backwards entry still balances, so nothing downstream objects. These
11
+ * functions are where that memory lives instead.
12
+ *
13
+ * Pure, and returning a `PostingInput` rather than writing: the caller hands
14
+ * the result to `journalRepository.post`, which is the only thing that
15
+ * touches Firestore and the only thing that checks the period lock. That
16
+ * separation is also what lets `verify-posting-rules` exercise the rules
17
+ * against real order shapes without a database.
18
+ *
19
+ * ## 🛑 Codes are referenced through ACCOUNT, never as bare strings
20
+ *
21
+ * A literal `"2200"` scattered across six rules is six chances to type
22
+ * `"2000"`, and a posting to an account that does not exist is silently
23
+ * dropped from the P&L by `profitAndLoss`'s unknown-account skip. One map,
24
+ * named, so a wrong code is a compile error.
25
+ */
26
+ import type { PostingInput } from "./posting";
27
+ import type { ExpenseCategory } from "../../../../features/ledger/schemas/expenses";
28
+ /** The accounts these rules post to. Codes match `chartOfAccountsSeedData`. */
29
+ export declare const ACCOUNT: {
30
+ readonly BANK: "1100";
31
+ readonly COURIER_FLOAT: "1150";
32
+ readonly GATEWAY_RECEIVABLE: "1200";
33
+ readonly INPUT_GST: "1300";
34
+ readonly SELLER_PAYABLE: "2100";
35
+ readonly ACCOUNTS_PAYABLE: "2300";
36
+ readonly OUTPUT_GST: "2200";
37
+ readonly BUYER_DEPOSITS: "2400";
38
+ readonly REFUNDS_PAYABLE: "2500";
39
+ readonly OPENING_EQUITY: "3900";
40
+ readonly COMMISSION_INCOME: "4100";
41
+ readonly SHIPPING_RECOVERED: "4200";
42
+ readonly ADDON_INCOME: "4300";
43
+ readonly GATEWAY_CHARGES: "5100";
44
+ readonly FREIGHT_PAID: "5200";
45
+ };
46
+ export interface OrderPostingInput {
47
+ orderId: string;
48
+ storeId: string;
49
+ placedAt: Date;
50
+ /** What the buyer paid in total, GST included. */
51
+ totalPrice: number;
52
+ /** Product GST inside `totalPrice`. */
53
+ gstAmount?: number;
54
+ /** Our commission, and the GST we charge on it. */
55
+ platformFee?: number;
56
+ platformFeeGst?: number;
57
+ /** What the buyer paid for delivery, inside `totalPrice`. */
58
+ shippingFee?: number;
59
+ /** Gift wrap, protection, WhatsApp — inside `totalPrice`. */
60
+ addonFees?: number;
61
+ }
62
+ /**
63
+ * A paid order.
64
+ *
65
+ * 🛑 **The seller payable is the RESIDUAL, not a computed share.** Everything
66
+ * the platform keeps is recognised explicitly — commission, its GST, the
67
+ * shipping the buyer paid, add-ons, and output GST — and what remains is what
68
+ * the seller is owed. Computing the seller's share independently and letting
69
+ * the two meet in the middle is how a rounding difference becomes an
70
+ * unbalanced entry that `assertBalanced` then refuses at checkout, turning a
71
+ * half-paisa into a failed order.
72
+ *
73
+ * 🛑 **Output GST is a LIABILITY, which is the entire point of the exercise.**
74
+ * `revenueRollup.totalRevenue` sums `order.totalPrice`, so today's "revenue"
75
+ * includes tax owed to the government and freight owed to a courier. Crediting
76
+ * 2200 here is what separates turnover from takings for the first time.
77
+ *
78
+ * 🛑 The debit is to GATEWAY RECEIVABLE, not Bank. Money captured is not
79
+ * money arrived — the settlement lands days later, net of MDR, and
80
+ * `settlementPosting` is what clears this account. Debiting Bank here would
81
+ * overstate cash by the whole float for the length of the settlement cycle.
82
+ */
83
+ export declare function orderPlacedPosting(o: OrderPostingInput): PostingInput;
84
+ export interface SettlementPostingInput {
85
+ settlementId: string;
86
+ settledAt: Date;
87
+ /** Gross captured in this settlement, matching what was debited to 1200. */
88
+ gross: number;
89
+ /** The gateway's real MDR for it. */
90
+ fee: number;
91
+ /** GST on that fee — an INPUT credit, not a cost. */
92
+ feeGst: number;
93
+ }
94
+ /**
95
+ * A gateway settlement arriving in the bank.
96
+ *
97
+ * 🛑 `fee` here is the gateway's REAL charge from the settlement report, and
98
+ * it is NOT `PayoutDocument.gatewayFee` — that field is our own withholding
99
+ * against a seller's gross, a different number with a different meaning.
100
+ * Posting one as the other overstates our costs and understates what we
101
+ * withheld, in opposite directions, so the error compounds.
102
+ *
103
+ * 🛑 The GST on the fee is an ASSET (input credit), not an expense. Nothing
104
+ * in this codebase could receive input tax before Part 4, so this was simply
105
+ * unrecoverable — which is money, every month, that was never claimable.
106
+ */
107
+ export declare function settlementPosting(s: SettlementPostingInput): PostingInput;
108
+ export interface PayoutPostingInput {
109
+ payoutId: string;
110
+ storeId: string;
111
+ paidAt: Date;
112
+ amount: number;
113
+ }
114
+ /**
115
+ * Paying a seller.
116
+ *
117
+ * Clears the payable raised at order time against real cash. Two legs and no
118
+ * income or expense — paying a debt is not a cost, and treating it as one
119
+ * double-counts every sale.
120
+ */
121
+ export declare function payoutPosting(p: PayoutPostingInput): PostingInput;
122
+ export interface ExpensePostingInput {
123
+ expenseId: string;
124
+ vendorId?: string;
125
+ vendorName: string;
126
+ category: ExpenseCategory;
127
+ billDate: Date;
128
+ amount: number;
129
+ inputGst?: number;
130
+ /** Paid outright, or on credit? Decides the funding leg. */
131
+ paid: boolean;
132
+ }
133
+ /**
134
+ * A bill.
135
+ *
136
+ * 🛑 The funding leg depends on whether it was PAID, and conflating the two
137
+ * is the commonest small-business bookkeeping error: crediting Bank for a
138
+ * bill not yet paid shows cash leaving that is still there, and crediting
139
+ * Accounts payable for one already settled leaves a debt that does not
140
+ * exist. Both balance; both are wrong; neither announces itself.
141
+ *
142
+ * 🛑 `inputGst` is DEBITED to an asset, never added to the cost. Expensing
143
+ * it is how input credit silently stops being claimable — the money is
144
+ * spent either way, but only one of the two gets it back.
145
+ */
146
+ export declare function expensePosting(e: ExpensePostingInput): PostingInput;
147
+ export interface RefundPostingInput {
148
+ orderId: string;
149
+ refundId: string;
150
+ refundedAt: Date;
151
+ amount: number;
152
+ /** The product GST inside the refunded amount, which is reclaimable. */
153
+ gstAmount?: number;
154
+ }
155
+ /**
156
+ * A refund.
157
+ *
158
+ * 🛑 **A refund is NOT a negative sale, and that distinction is why the top
159
+ * line is currently wrong.** `revenueRollup` removes a fully-refunded order
160
+ * from its sum entirely and does not reduce it at all for a partial — so
161
+ * revenue silently rewrites itself retroactively, and no two runs of the same
162
+ * report over the same period agree.
163
+ *
164
+ * Here it is its own event with its own date: the sale stays in the month it
165
+ * happened, the refund lands in the month it happened, and both are visible.
166
+ *
167
+ * 🛑 It DEBITS output GST, reclaiming tax already declared. Omitting that leg
168
+ * means paying GST on money that was given back.
169
+ */
170
+ export declare function refundPosting(r: RefundPostingInput): PostingInput;