@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
@@ -79,6 +79,129 @@ export declare function normaliseDimensions(d: ParcelSource["dimensionsCm"]): Pa
79
79
  * whichever tier won on weight would discard information that exists.
80
80
  */
81
81
  export declare function resolveParcel(shipment: ParcelSource | null | undefined, product: ParcelSource | null | undefined, category: ParcelSource | null | undefined): Parcel | null;
82
+ /**
83
+ * Divisors, by service class.
84
+ *
85
+ * A carrier sells space, not mass, so it bills the greater of actual weight and
86
+ * `l × w × h ÷ divisor`. The divisor is the whole ballgame on this catalogue:
87
+ * collectible packaging is mostly air, so for a stadium box the volumetric
88
+ * figure is ~5× the actual and no per-kilo negotiation touches it.
89
+ *
90
+ * 🛑 `surfaceTight` is not a typo. Some Indian surface B2C plans quote ÷4000,
91
+ * which is a silent 25% increase over ÷5000 on every bulky parcel and is
92
+ * never the number on the front of the rate card. Read the contract, then set
93
+ * this per carrier — do not assume 5000 because it is the common default.
94
+ *
95
+ * Air uses a LARGER divisor, which means a very light bulky box can genuinely
96
+ * cost less by air than by surface. That is counter-intuitive and real.
97
+ */
98
+ export declare const VOLUMETRIC_DIVISORS: {
99
+ readonly surfaceTight: 4000;
100
+ readonly standard: 5000;
101
+ readonly air: 6000;
102
+ };
103
+ export declare const DEFAULT_VOLUMETRIC_DIVISOR: 5000;
104
+ /**
105
+ * Where a parcel sits against the two thresholds, and therefore which carrier
106
+ * class is cheaper. See `carrierClassFor` for why these numbers.
107
+ */
108
+ export declare const VOLUMETRIC_RATIO_THRESHOLDS: {
109
+ /** Below this the two weights are close enough that the rate card decides. */
110
+ readonly private: 1.5;
111
+ /** At or above this the box is mostly air and a postal service wins. */
112
+ readonly postal: 2;
113
+ };
114
+ /**
115
+ * The first billing slab, in grams — the region where R means nothing.
116
+ *
117
+ * 🛑 This guard exists because the ratio rule is WRONG below it, and the error
118
+ * was caught by checking the rule against real tariffs instead of trusting it.
119
+ *
120
+ * A single boxed bey is 250 g actual / 448 g volumetric — R 1.79, which the
121
+ * thresholds above call `"either"`. The real bill at >2000 km is ₹106 by Speed
122
+ * Post against ₹50 by private courier: private wins by 2×, not "either".
123
+ *
124
+ * The reason is that both figures land inside each carrier's MINIMUM charge, so
125
+ * the comparison is floor against floor and the ratio describes nothing. India
126
+ * Post's 201–500 g floor is ₹90; a private first slab is around ₹42. Whenever
127
+ * both weights sit in that first slab, the lower floor wins and that is private.
128
+ */
129
+ export declare const FIRST_SLAB_G = 500;
130
+ export interface ChargeableWeight {
131
+ actualG: number;
132
+ /**
133
+ * `null` when the dimension triple is unknown — NEVER 0.
134
+ *
135
+ * A carrier with no dimensions bills actual weight, so `chargeableG` is still
136
+ * correct in that case. But a `0` here would read as "this box has no volume",
137
+ * and `ratio` would compute to 0, which routes a stadium to the private
138
+ * carrier that bills it at 7.3 kg. Unknown has to stay distinguishable from
139
+ * dense — same discipline as `resolveParcel` refusing to return weight 0.
140
+ */
141
+ volumetricG: number | null;
142
+ /** `max(actual, volumetric)` — what you are billed for. */
143
+ chargeableG: number;
144
+ divisor: number;
145
+ billedOn: "actual" | "volumetric";
146
+ /**
147
+ * **The R value**: volumetric ÷ actual, 2dp. `null` without dimensions.
148
+ *
149
+ * 1 means the box is exactly at the divisor's break-even density. Above 1 you
150
+ * are paying for air; below 1 the volume is free and actual weight bills.
151
+ */
152
+ ratio: number | null;
153
+ /** Packed density in kg/m³. `null` without dimensions. */
154
+ densityKgM3: number | null;
155
+ /**
156
+ * The density at which volumetric starts to bite, derived from the divisor
157
+ * rather than hardcoded: `1e6 / divisor`. 200 kg/m³ at ÷5000, 250 at ÷4000,
158
+ * ~167 at ÷6000. A parcel below this is billed on volume.
159
+ */
160
+ breakEvenKgM3: number;
161
+ }
162
+ /** Volumetric weight in grams. `l × w × h ÷ divisor` is kilos, hence ×1000. */
163
+ export declare function volumetricWeightG(d: ParcelDimensionsCm, divisor?: number): number;
164
+ /**
165
+ * What this parcel is billed at, and by how much volume beats mass.
166
+ *
167
+ * Pure arithmetic — it reports, it does not decide. `carrierClassFor` turns the
168
+ * result into a routing opinion, deliberately as a separate step so a surface
169
+ * can show the numbers without adopting the recommendation.
170
+ */
171
+ export declare function chargeableWeight(parcel: Pick<Parcel, "weightG" | "dimensionsCm">, divisor?: number): ChargeableWeight;
172
+ /**
173
+ * Which carrier class is cheaper for this parcel, on freight alone.
174
+ *
175
+ * India Post Speed Post bills ACTUAL weight and applies no volumetric
176
+ * calculation; every private carrier bills the greater of the two. So the
177
+ * freight comparison reduces to the R value:
178
+ *
179
+ * R < 1.5 the two weights are close; the rate card decides, and a private
180
+ * card is usually cheaper at low weight → `"private"`
181
+ * 1.5 ≤ R < 2 genuinely close; decide on pincode serviceability and
182
+ * whether the item needs insurance → `"either"`
183
+ * R ≥ 2 the box is mostly air and postal is materially cheaper → `"postal"`
184
+ *
185
+ * 🛑 `"unknown"` when dimensions are missing, and callers must not collapse it
186
+ * into a default. Defaulting to `"private"` is precisely the wrong way round:
187
+ * the parcels whose dimensions nobody recorded are disproportionately the big
188
+ * awkward ones, which are the parcels this function exists to divert.
189
+ *
190
+ * 🛑 This is a FREIGHT opinion and nothing more. Above roughly ₹8,000 of order
191
+ * value the decision stops being about freight — the delta is a few hundred
192
+ * rupees against an uninsured loss of the whole item — so an order-value test
193
+ * and serviceability both outrank this. It answers "which is cheaper", never
194
+ * "which should we book".
195
+ *
196
+ * ## Where it is weakest, stated rather than hidden
197
+ *
198
+ * At HIGH weight the per-additional-slab rates start to matter more than the
199
+ * ratio, and those are negotiated. A 8 kg / 14 kg parcel is R 1.75 → `"either"`,
200
+ * while one representative card puts it at ₹991 postal against ₹1,197 private —
201
+ * a real 21% gap the ratio does not see. `"either"` is the honest answer there:
202
+ * it means your own card decides, not that the two are equal.
203
+ */
204
+ export declare function carrierClassFor(c: Pick<ChargeableWeight, "ratio" | "actualG" | "volumetricG">): "private" | "either" | "postal" | "unknown";
82
205
  /**
83
206
  * The weight of a whole order group — what a single shipment actually carries.
84
207
  *
@@ -88,6 +88,145 @@ export function resolveParcel(shipment, product, category) {
88
88
  }
89
89
  return { weightG, source, ...(dimensionsCm ? { dimensionsCm } : {}) };
90
90
  }
91
+ /* ── Volumetric weight — what the carrier actually bills ──────────────────── */
92
+ /**
93
+ * Divisors, by service class.
94
+ *
95
+ * A carrier sells space, not mass, so it bills the greater of actual weight and
96
+ * `l × w × h ÷ divisor`. The divisor is the whole ballgame on this catalogue:
97
+ * collectible packaging is mostly air, so for a stadium box the volumetric
98
+ * figure is ~5× the actual and no per-kilo negotiation touches it.
99
+ *
100
+ * 🛑 `surfaceTight` is not a typo. Some Indian surface B2C plans quote ÷4000,
101
+ * which is a silent 25% increase over ÷5000 on every bulky parcel and is
102
+ * never the number on the front of the rate card. Read the contract, then set
103
+ * this per carrier — do not assume 5000 because it is the common default.
104
+ *
105
+ * Air uses a LARGER divisor, which means a very light bulky box can genuinely
106
+ * cost less by air than by surface. That is counter-intuitive and real.
107
+ */
108
+ export const VOLUMETRIC_DIVISORS = {
109
+ surfaceTight: 4000,
110
+ standard: 5000,
111
+ air: 6000,
112
+ };
113
+ export const DEFAULT_VOLUMETRIC_DIVISOR = VOLUMETRIC_DIVISORS.standard;
114
+ /**
115
+ * Where a parcel sits against the two thresholds, and therefore which carrier
116
+ * class is cheaper. See `carrierClassFor` for why these numbers.
117
+ */
118
+ export const VOLUMETRIC_RATIO_THRESHOLDS = {
119
+ /** Below this the two weights are close enough that the rate card decides. */
120
+ private: 1.5,
121
+ /** At or above this the box is mostly air and a postal service wins. */
122
+ postal: 2,
123
+ };
124
+ /**
125
+ * The first billing slab, in grams — the region where R means nothing.
126
+ *
127
+ * 🛑 This guard exists because the ratio rule is WRONG below it, and the error
128
+ * was caught by checking the rule against real tariffs instead of trusting it.
129
+ *
130
+ * A single boxed bey is 250 g actual / 448 g volumetric — R 1.79, which the
131
+ * thresholds above call `"either"`. The real bill at >2000 km is ₹106 by Speed
132
+ * Post against ₹50 by private courier: private wins by 2×, not "either".
133
+ *
134
+ * The reason is that both figures land inside each carrier's MINIMUM charge, so
135
+ * the comparison is floor against floor and the ratio describes nothing. India
136
+ * Post's 201–500 g floor is ₹90; a private first slab is around ₹42. Whenever
137
+ * both weights sit in that first slab, the lower floor wins and that is private.
138
+ */
139
+ export const FIRST_SLAB_G = 500;
140
+ /** Volumetric weight in grams. `l × w × h ÷ divisor` is kilos, hence ×1000. */
141
+ export function volumetricWeightG(d, divisor = DEFAULT_VOLUMETRIC_DIVISOR) {
142
+ if (!(divisor > 0)) {
143
+ throw new RangeError(`volumetricWeightG: divisor must be > 0, got ${divisor}`);
144
+ }
145
+ return Math.round(((d.l * d.w * d.h) / divisor) * 1000);
146
+ }
147
+ /**
148
+ * What this parcel is billed at, and by how much volume beats mass.
149
+ *
150
+ * Pure arithmetic — it reports, it does not decide. `carrierClassFor` turns the
151
+ * result into a routing opinion, deliberately as a separate step so a surface
152
+ * can show the numbers without adopting the recommendation.
153
+ */
154
+ export function chargeableWeight(parcel, divisor = DEFAULT_VOLUMETRIC_DIVISOR) {
155
+ const actualG = parcel.weightG;
156
+ const breakEvenKgM3 = Math.round((1000000 / divisor) * 10) / 10;
157
+ const d = parcel.dimensionsCm;
158
+ if (!d) {
159
+ return {
160
+ actualG,
161
+ volumetricG: null,
162
+ chargeableG: actualG,
163
+ divisor,
164
+ billedOn: "actual",
165
+ ratio: null,
166
+ densityKgM3: null,
167
+ breakEvenKgM3,
168
+ };
169
+ }
170
+ const volumetricG = volumetricWeightG(d, divisor);
171
+ const cm3 = d.l * d.w * d.h;
172
+ return {
173
+ actualG,
174
+ volumetricG,
175
+ chargeableG: Math.max(actualG, volumetricG),
176
+ divisor,
177
+ billedOn: volumetricG > actualG ? "volumetric" : "actual",
178
+ ratio: actualG > 0 ? Math.round((volumetricG / actualG) * 100) / 100 : null,
179
+ densityKgM3: cm3 > 0 ? Math.round(((actualG * 1000) / cm3) * 10) / 10 : null,
180
+ breakEvenKgM3,
181
+ };
182
+ }
183
+ /**
184
+ * Which carrier class is cheaper for this parcel, on freight alone.
185
+ *
186
+ * India Post Speed Post bills ACTUAL weight and applies no volumetric
187
+ * calculation; every private carrier bills the greater of the two. So the
188
+ * freight comparison reduces to the R value:
189
+ *
190
+ * R < 1.5 the two weights are close; the rate card decides, and a private
191
+ * card is usually cheaper at low weight → `"private"`
192
+ * 1.5 ≤ R < 2 genuinely close; decide on pincode serviceability and
193
+ * whether the item needs insurance → `"either"`
194
+ * R ≥ 2 the box is mostly air and postal is materially cheaper → `"postal"`
195
+ *
196
+ * 🛑 `"unknown"` when dimensions are missing, and callers must not collapse it
197
+ * into a default. Defaulting to `"private"` is precisely the wrong way round:
198
+ * the parcels whose dimensions nobody recorded are disproportionately the big
199
+ * awkward ones, which are the parcels this function exists to divert.
200
+ *
201
+ * 🛑 This is a FREIGHT opinion and nothing more. Above roughly ₹8,000 of order
202
+ * value the decision stops being about freight — the delta is a few hundred
203
+ * rupees against an uninsured loss of the whole item — so an order-value test
204
+ * and serviceability both outrank this. It answers "which is cheaper", never
205
+ * "which should we book".
206
+ *
207
+ * ## Where it is weakest, stated rather than hidden
208
+ *
209
+ * At HIGH weight the per-additional-slab rates start to matter more than the
210
+ * ratio, and those are negotiated. A 8 kg / 14 kg parcel is R 1.75 → `"either"`,
211
+ * while one representative card puts it at ₹991 postal against ₹1,197 private —
212
+ * a real 21% gap the ratio does not see. `"either"` is the honest answer there:
213
+ * it means your own card decides, not that the two are equal.
214
+ */
215
+ export function carrierClassFor(c) {
216
+ if (c.ratio == null)
217
+ return "unknown";
218
+ // Both weights inside the first slab → floor vs floor; see FIRST_SLAB_G.
219
+ if (c.actualG <= FIRST_SLAB_G &&
220
+ c.volumetricG != null &&
221
+ c.volumetricG <= FIRST_SLAB_G) {
222
+ return "private";
223
+ }
224
+ if (c.ratio >= VOLUMETRIC_RATIO_THRESHOLDS.postal)
225
+ return "postal";
226
+ if (c.ratio >= VOLUMETRIC_RATIO_THRESHOLDS.private)
227
+ return "either";
228
+ return "private";
229
+ }
91
230
  /**
92
231
  * The weight of a whole order group — what a single shipment actually carries.
93
232
  *
@@ -38,17 +38,25 @@ export interface BuyerFacingFees {
38
38
  codHandlingFeeCap?: number;
39
39
  codHandlingFeePercent?: number;
40
40
  codDepositPercent?: number;
41
+ /** Commission rate applied to the cart subtotal, before the cap. */
42
+ platformFeePercent?: number;
43
+ /** Rupee ceiling on the commission — the reason a ₹39,500 order pays ₹10. */
44
+ platformFeeMax?: number;
45
+ /** GST rate levied on the commission actually charged (never on the uncapped figure). */
46
+ gstPercent?: number;
47
+ /** Rupee floor on `platformFee + gstOnFee` combined. */
48
+ minimumTransactionFee?: number;
41
49
  }
42
50
  /**
43
- * Withheld on purpose (15 of 25): platformFeePercent, gstPercent,
44
- * minimumTransactionFee, platformFeeMax, gatewayFeePercent,
45
- * sellerShippingFixed, platformShippingPercent, platformShippingFixedMin,
46
- * autoPayoutWindowDays, payoutHoldDays, minPayoutAmount, auctionListingFee,
47
- * preOrderListingFee, featuredSlotFee, promotedSlotFee.
51
+ * Withheld on purpose (11 of 25): gatewayFeePercent, sellerShippingFixed,
52
+ * platformShippingPercent, platformShippingFixedMin, autoPayoutWindowDays,
53
+ * payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee,
54
+ * featuredSlotFee, promotedSlotFee — plus `surchargeSellerSharePercent`, which
55
+ * lives on the EMI block and is excluded by `BuyerEmiSettings` below.
48
56
  *
49
- * The buyer still SEES the platform fee and GST — as computed amounts in the
50
- * server pricing preview (`CartPriceBreakdownData`), never as the rates that
51
- * produced them.
57
+ * Every one of those prices something between us and a seller or a gateway.
58
+ * None of them changes what the buyer is asked to pay, so none of them has a
59
+ * reader in a browser.
52
60
  */
53
61
  export declare function toBuyerFacingFees(commissions: SiteSettingsDocument["commissions"] | null | undefined): BuyerFacingFees | null;
54
62
  /**
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Withheld on purpose (15 of 25): platformFeePercent, gstPercent,
3
- * minimumTransactionFee, platformFeeMax, gatewayFeePercent,
4
- * sellerShippingFixed, platformShippingPercent, platformShippingFixedMin,
5
- * autoPayoutWindowDays, payoutHoldDays, minPayoutAmount, auctionListingFee,
6
- * preOrderListingFee, featuredSlotFee, promotedSlotFee.
2
+ * Withheld on purpose (11 of 25): gatewayFeePercent, sellerShippingFixed,
3
+ * platformShippingPercent, platformShippingFixedMin, autoPayoutWindowDays,
4
+ * payoutHoldDays, minPayoutAmount, auctionListingFee, preOrderListingFee,
5
+ * featuredSlotFee, promotedSlotFee — plus `surchargeSellerSharePercent`, which
6
+ * lives on the EMI block and is excluded by `BuyerEmiSettings` below.
7
7
  *
8
- * The buyer still SEES the platform fee and GST — as computed amounts in the
9
- * server pricing preview (`CartPriceBreakdownData`), never as the rates that
10
- * produced them.
8
+ * Every one of those prices something between us and a seller or a gateway.
9
+ * None of them changes what the buyer is asked to pay, so none of them has a
10
+ * reader in a browser.
11
11
  */
12
12
  export function toBuyerFacingFees(commissions) {
13
13
  if (!commissions)
@@ -24,6 +24,10 @@ export function toBuyerFacingFees(commissions) {
24
24
  codHandlingFeeCap: commissions.codHandlingFeeCap,
25
25
  codHandlingFeePercent: commissions.codHandlingFeePercent,
26
26
  codDepositPercent: commissions.codDepositPercent,
27
+ platformFeePercent: commissions.platformFeePercent,
28
+ platformFeeMax: commissions.platformFeeMax,
29
+ gstPercent: commissions.gstPercent,
30
+ minimumTransactionFee: commissions.minimumTransactionFee,
27
31
  };
28
32
  }
29
33
  export function toBuyerEmiSettings(emi) {
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Plain-language derivations for every fee line a buyer is shown.
3
+ *
4
+ * 🛑 The problem this solves is not presentation. The Order Summary listed
5
+ * `EMI surcharge ₹2,133.00`, `Platform fee ₹10.00` and `GST on platform fee
6
+ * ₹1.80` beside a `₹39,500.00` subtotal, and **none of the three could be
7
+ * checked by the person paying them.** ₹2,133 on ₹39,500 is 5.4% — a buyer has
8
+ * no way to tell whether that is a rate, a tenure, a rounding error or a bug,
9
+ * and the only honest response to an unexplained charge is to distrust it.
10
+ *
11
+ * ── Why the STRINGS live here and not in the component ────────────────────
12
+ *
13
+ * Each sentence is a claim about how a number was computed, and the number is
14
+ * computed in `calculator.ts` / `emi/schedule.ts`. Putting the sentence beside
15
+ * a `<Line>` would make it a second, prose implementation of the same rule —
16
+ * free to drift the moment a cap moves (Root Cause #75: a rule reachable by
17
+ * copy-paste gets copied, and the copy never learns about the branch added
18
+ * later). These functions read the SAME rate fields the calculators read.
19
+ *
20
+ * ── Why they take the CHARGED amount as an argument ───────────────────────
21
+ *
22
+ * They explain the figure actually on screen; they do not recompute it. If a
23
+ * derivation and the rendered amount ever disagree, the explanation must say
24
+ * so rather than quietly print a second, prettier number — so each function
25
+ * ends at the charged value and describes the route to it.
26
+ *
27
+ * 🛑 NOTHING HERE MAY NAME A SELLER-SIDE RATE. `surchargeSellerSharePercent`
28
+ * (the platform/seller split of the EMI surcharge), `gatewayFeePercent`,
29
+ * `payoutHoldDays` and `minPayoutAmount` are `PRIVATE_*_FIELDS`
30
+ * (Root Cause #70). The EMI functions below are typed against
31
+ * `BuyerEmiSettings`, which excludes the split structurally — so the omission
32
+ * is enforced by `tsc`, not by this paragraph.
33
+ */
34
+ import type { BuyerEmiSettings, BuyerFacingFees } from "../features/site-settings/fees";
35
+ import type { BuyerEmiQuote } from "../features/emi/schedule";
36
+ /** One rendered explanation: a short claim plus the steps behind it. */
37
+ export interface FeeDerivation {
38
+ /** One sentence answering "why am I paying this". */
39
+ summary: string;
40
+ /** The arithmetic, one step per line, in the order it is applied. */
41
+ steps: string[];
42
+ }
43
+ /**
44
+ * The platform commission.
45
+ *
46
+ * The cap is the interesting half and the reason this needed explaining at
47
+ * all: on a ₹39,500 order the uncapped commission is large and the charge is
48
+ * ₹10, so the number on screen bears no visible relation to the subtotal.
49
+ */
50
+ export declare function explainPlatformFee(subtotal: number, charged: number, fees: BuyerFacingFees | null, currency?: string): FeeDerivation | null;
51
+ /**
52
+ * GST on the commission.
53
+ *
54
+ * 🛑 Levied on the commission ACTUALLY CHARGED, never on the uncapped figure —
55
+ * which is the one thing a buyer checking ₹1.80 against a ₹39,500 subtotal
56
+ * would get wrong.
57
+ */
58
+ export declare function explainGstOnPlatformFee(platformFee: number, charged: number, fees: BuyerFacingFees | null, currency?: string): FeeDerivation | null;
59
+ /**
60
+ * The EMI surcharge — the cost of credit, stated as a cost of credit.
61
+ *
62
+ * 🛑 Takes a `BuyerEmiQuote`, which is `computeBuyerEmiQuote`'s return type and
63
+ * carries no surcharge split. `computeEmiSchedule`'s richer result is
64
+ * assignable to it, so the type does not stop a server caller passing the
65
+ * wrong one — but nothing here reads a field that only exists on the wider
66
+ * shape, so no split can be printed either.
67
+ */
68
+ export declare function explainEmiSurcharge(sellerSubtotal: number, tenureMonths: number, quote: BuyerEmiQuote, emi: BuyerEmiSettings | null, currency?: string): FeeDerivation | null;
69
+ /** COD handling — a cap that bites on most carts, same shape as the commission. */
70
+ export declare function explainCodHandlingFee(subtotal: number, charged: number, fees: BuyerFacingFees | null, currency?: string): FeeDerivation | null;
71
+ /**
72
+ * Every derivation a price breakdown can render, keyed by the line it belongs
73
+ * to. A key with no entry simply renders no disclosure — a line we cannot
74
+ * explain says nothing rather than guessing.
75
+ */
76
+ export interface FeeDerivations {
77
+ platformFee?: FeeDerivation | null;
78
+ gstOnFee?: FeeDerivation | null;
79
+ emiSurcharge?: FeeDerivation | null;
80
+ codHandlingFee?: FeeDerivation | null;
81
+ }
@@ -0,0 +1,101 @@
1
+ import { formatCurrency } from "../../../utils/number.formatter.js";
2
+ /** `2` → `"2%"`, `2.5` → `"2.5%"` — no trailing `.0`. */
3
+ function pct(value) {
4
+ return `${Number(value.toFixed(2))}%`;
5
+ }
6
+ /**
7
+ * The platform commission.
8
+ *
9
+ * The cap is the interesting half and the reason this needed explaining at
10
+ * all: on a ₹39,500 order the uncapped commission is large and the charge is
11
+ * ₹10, so the number on screen bears no visible relation to the subtotal.
12
+ */
13
+ export function explainPlatformFee(subtotal, charged, fees, currency = "INR") {
14
+ if (!fees || fees.platformFeePercent === undefined)
15
+ return null;
16
+ const money = (v) => formatCurrency(v, currency);
17
+ const uncapped = (subtotal * fees.platformFeePercent) / 100;
18
+ const steps = [
19
+ `${pct(fees.platformFeePercent)} of ${money(subtotal)} = ${money(uncapped)}`,
20
+ ];
21
+ if (fees.platformFeeMax !== undefined && uncapped > fees.platformFeeMax) {
22
+ steps.push(`Capped at ${money(fees.platformFeeMax)} — you pay the cap, not the percentage`);
23
+ }
24
+ /*
25
+ * The floor is defined on `platformFee + gstOnFee` COMBINED and is applied
26
+ * once, inside `allocateCheckoutFees`, as extra platform fee with no GST of
27
+ * its own. So it is mentioned here and never on the GST line — see the D41
28
+ * note in `calculator.ts`.
29
+ */
30
+ if (fees.minimumTransactionFee !== undefined && charged > Math.min(uncapped, fees.platformFeeMax ?? uncapped)) {
31
+ steps.push(`Raised to meet the ${money(fees.minimumTransactionFee)} minimum transaction fee, which covers the commission and its GST together`);
32
+ }
33
+ steps.push(`You pay ${money(charged)}`);
34
+ return {
35
+ summary: fees.platformFeeMax !== undefined && uncapped > fees.platformFeeMax
36
+ ? `Our commission is ${pct(fees.platformFeePercent)}, capped at ${money(fees.platformFeeMax)} per order.`
37
+ : `Our commission is ${pct(fees.platformFeePercent)} of the order.`,
38
+ steps,
39
+ };
40
+ }
41
+ /**
42
+ * GST on the commission.
43
+ *
44
+ * 🛑 Levied on the commission ACTUALLY CHARGED, never on the uncapped figure —
45
+ * which is the one thing a buyer checking ₹1.80 against a ₹39,500 subtotal
46
+ * would get wrong.
47
+ */
48
+ export function explainGstOnPlatformFee(platformFee, charged, fees, currency = "INR") {
49
+ if (!fees || fees.gstPercent === undefined)
50
+ return null;
51
+ const money = (v) => formatCurrency(v, currency);
52
+ return {
53
+ summary: `GST at ${pct(fees.gstPercent)}, charged on our commission — not on the price of the items.`,
54
+ steps: [
55
+ `${pct(fees.gstPercent)} of the ${money(platformFee)} commission = ${money(charged)}`,
56
+ "Item prices already include their own GST, shown below the total",
57
+ ],
58
+ };
59
+ }
60
+ /**
61
+ * The EMI surcharge — the cost of credit, stated as a cost of credit.
62
+ *
63
+ * 🛑 Takes a `BuyerEmiQuote`, which is `computeBuyerEmiQuote`'s return type and
64
+ * carries no surcharge split. `computeEmiSchedule`'s richer result is
65
+ * assignable to it, so the type does not stop a server caller passing the
66
+ * wrong one — but nothing here reads a field that only exists on the wider
67
+ * shape, so no split can be printed either.
68
+ */
69
+ export function explainEmiSurcharge(sellerSubtotal, tenureMonths, quote, emi, currency = "INR") {
70
+ if (!emi)
71
+ return null;
72
+ const money = (v) => formatCurrency(v, currency);
73
+ const principal = sellerSubtotal - quote.tokenAmount;
74
+ const perMonth = quote.installments[0]?.amount ?? 0;
75
+ return {
76
+ summary: `Spreading ${money(sellerSubtotal)} over ${tenureMonths} months costs ${money(quote.surchargeAmount)} — that is the total cost of credit.`,
77
+ steps: [
78
+ `Token paid today: ${pct(emi.tokenPercent)} of ${money(sellerSubtotal)} = ${money(quote.tokenAmount)}`,
79
+ `Financed amount: ${money(sellerSubtotal)} − ${money(quote.tokenAmount)} = ${money(principal)}`,
80
+ `Surcharge: ${pct(emi.surchargePercentPerMonth)} of ${money(principal)} per month × ${tenureMonths} months = ${money(quote.surchargeAmount)}`,
81
+ `Repaid over ${tenureMonths} instalments of about ${money(perMonth)}, due on day ${emi.billingDay} of each month`,
82
+ `Total you will pay: ${money(quote.tokenAmount)} + ${money(quote.remainingBalance)} = ${money(quote.tokenAmount + quote.remainingBalance)}`,
83
+ ],
84
+ };
85
+ }
86
+ /** COD handling — a cap that bites on most carts, same shape as the commission. */
87
+ export function explainCodHandlingFee(subtotal, charged, fees, currency = "INR") {
88
+ if (!fees || fees.codHandlingFeePercent === undefined)
89
+ return null;
90
+ const money = (v) => formatCurrency(v, currency);
91
+ const uncapped = (subtotal * fees.codHandlingFeePercent) / 100;
92
+ const steps = [`${pct(fees.codHandlingFeePercent)} of ${money(subtotal)} = ${money(uncapped)}`];
93
+ if (fees.codHandlingFeeCap !== undefined && uncapped > fees.codHandlingFeeCap) {
94
+ steps.push(`Capped at ${money(fees.codHandlingFeeCap)}`);
95
+ }
96
+ steps.push(`You pay ${money(charged)}`);
97
+ return {
98
+ summary: "What it costs us to collect cash at your door and bank it.",
99
+ steps,
100
+ };
101
+ }
@@ -1,4 +1,4 @@
1
1
  export type { HistoryActorRole, HistoryActor, FieldChange, StatusChangeEntry, AppendHistoryResult, SerialisedStatusChangeEntry, } from "./types";
2
- export { STATUS_HISTORY_MAX } from "./types";
2
+ export { STATUS_HISTORY_MAX, HISTORY_ACTOR_ROLE } from "./types";
3
3
  export type { BuildHistoryEntryInput } from "./history";
4
4
  export { diffTrackedFields, buildHistoryEntry, appendHistoryEntry, withHistory, } from "./history";
@@ -1,2 +1,2 @@
1
- export { STATUS_HISTORY_MAX } from "./types.js";
1
+ export { STATUS_HISTORY_MAX, HISTORY_ACTOR_ROLE } from "./types.js";
2
2
  export { diffTrackedFields, buildHistoryEntry, appendHistoryEntry, withHistory, } from "./history.js";
@@ -9,7 +9,13 @@ import type { FirestoreValue } from "../../../schemas/types";
9
9
  * entities had any history at all — `ads`, built inline in a route handler
10
10
  * with no type and no reuse.
11
11
  */
12
- export type HistoryActorRole = "buyer" | "seller" | "admin" | "system";
12
+ export declare const HISTORY_ACTOR_ROLE: {
13
+ readonly BUYER: "buyer";
14
+ readonly SELLER: "seller";
15
+ readonly ADMIN: "admin";
16
+ readonly SYSTEM: "system";
17
+ };
18
+ export type HistoryActorRole = (typeof HISTORY_ACTOR_ROLE)[keyof typeof HISTORY_ACTOR_ROLE];
13
19
  export interface HistoryActor {
14
20
  /** Firebase Auth uid. Absent for `system` (cron jobs, Firestore triggers). */
15
21
  uid?: string;
@@ -1,3 +1,28 @@
1
+ /**
2
+ * Generic entity status history — the shape shared by every entity that
3
+ * records how it got to its current state.
4
+ *
5
+ * Built for orders first (W2) and adopted by stores, payouts, offers, bids,
6
+ * support tickets, scammer profiles, catalogue approvals, shipments and
7
+ * returns in W15. Before this existed, exactly ONE of ~40 status-bearing
8
+ * entities had any history at all — `ads`, built inline in a route handler
9
+ * with no type and no reuse.
10
+ */
11
+ /*
12
+ * 🛑 This is an ACTOR KIND, not a `UserDocument.role`. No user has the role
13
+ * "buyer" — a buyer's stored role is "user" — so the two namespaces must never
14
+ * be compared, and `audit-phantom-buyer-role` exists to catch exactly that
15
+ * mistake in a runtime auth gate.
16
+ *
17
+ * The constant below is what a gate should compare against: it names the
18
+ * namespace at the call site, where a bare `"buyer"` literal cannot.
19
+ */
20
+ export const HISTORY_ACTOR_ROLE = {
21
+ BUYER: "buyer",
22
+ SELLER: "seller",
23
+ ADMIN: "admin",
24
+ SYSTEM: "system",
25
+ };
1
26
  /**
2
27
  * Cap for any history array. Matches `HISTORY_MAX`'s reasoning: an unbounded
3
28
  * array on a hot document inflates every read of that document (Rule #6), and
package/dist/client.d.ts CHANGED
@@ -402,6 +402,9 @@ export { OrderCompletionPanel } from "./features/orders/components/OrderCompleti
402
402
  export type { OrderCompletionPanelProps, RateableLine } from "./features/orders/components/OrderCompletionPanel";
403
403
  export { EmiInstallmentsPanel } from "./features/orders/components/EmiInstallmentsPanel";
404
404
  export type { EmiInstallmentsPanelProps } from "./features/orders/components/EmiInstallmentsPanel";
405
+ export { BalancePaymentPanel } from "./features/orders/components/BalancePaymentPanel";
406
+ export type { BalancePaymentPanelProps } from "./features/orders/components/BalancePaymentPanel";
407
+ export { balanceProofSchema, balanceReviewSchema, type BalanceProofInput, type BalanceReviewInput, } from "./features/orders/schemas/balance";
405
408
  export { CountdownDisplay } from "./ui/components/CountdownDisplay";
406
409
  export type { CountdownDisplayProps } from "./ui/components/CountdownDisplay";
407
410
  export { OrderAddonBadges } from "./features/orders/components/OrderAddonBadges";
@@ -513,6 +516,7 @@ export { CommandPalette, useCommandPaletteHotkey } from "./features/shell/index"
513
516
  export type { CommandPaletteGroup } from "./features/shell/index";
514
517
  export { AdminBlogEditorView, AdminBundleEditorView, AdminCarouselEditorView, AdminCategoryEditorView, AdminCouponEditorView, AdminFaqEditorView, AdminFeatureEditorView, AdminOrderEditorView, AdminPayoutMarkPaidModal, AdminAuditLogView, AdminMoneyCasesView, AdminGatewayEventsView, ViewAuditLogEntryModal, AdminProductEditorView, AdminScammerEditorView, AdminEmployeeEditorView, AdminNavEditorView, AdminUserEditorView, AdminStoreEditorView, AdminSublistingCategoryEditorView, AdminSupportTicketDetailView, } from "./features/admin/index";
515
518
  export type { AdminStoreEditorViewProps, AdminPayoutMarkPaidModalProps, AdminAuditLogViewProps, AdminMoneyCasesViewProps, AuditLogEntryDetail, ViewAuditLogEntryModalProps } from "./features/admin/index";
519
+ export type { AdminOrderEditorViewProps } from "./features/admin/components/AdminOrderEditorView";
516
520
  export type { NavItemData } from "./features/admin/index";
517
521
  export { PageLoader } from "./ui/index";
518
522
  export { ADMIN_CHECKOUT_BYPASS_FLAG_KEY } from "./features/admin/schemas/firestore";
@@ -534,6 +538,10 @@ export { BUNDLE_COPY } from "./_internal/shared/features/categories/bundle-copy"
534
538
  export { useSiteSettings } from "./core/index";
535
539
  export { checkEmiEligibility, computeEmiSchedule, computeBuyerEmiQuote, } from "./_internal/shared/features/emi/schedule";
536
540
  export type { EmiSettings, BuyerEmiQuote } from "./_internal/shared/features/emi/schedule";
541
+ export { emiTenderBlockReason, isTenderCompatibleWithEmi } from "./_internal/shared/features/emi/choice";
542
+ export type { EmiTender } from "./_internal/shared/features/emi/choice";
543
+ export { explainCodHandlingFee, explainEmiSurcharge, explainGstOnPlatformFee, explainPlatformFee, } from "./_internal/shared/fees/explain";
544
+ export type { FeeDerivation, FeeDerivations } from "./_internal/shared/fees/explain";
537
545
  export type { BuyerFacingFees, BuyerEmiSettings, } from "./_internal/shared/features/site-settings/fees";
538
546
  export type { PublicSiteSettings } from "./_internal/shared/features/site-settings/types";
539
547
  export { computeCodHandlingFee } from "./_internal/shared/fees/calculator";
@@ -558,6 +566,8 @@ export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
558
566
  export { orderCancelSchema, paymentProofSchema, } from "./features/orders/schemas/buyer-forms";
559
567
  export { scamReportFormSchema } from "./features/scams/schemas/report-form";
560
568
  export { sublistingCategoryFormSchema } from "./features/store-extensions/schemas/sublisting-category-form";
569
+ export { adCreativeFormSchema } from "./features/ads/schemas/creative-form";
570
+ export type { AdCreativeFormValues } from "./features/ads/schemas/creative-form";
561
571
  export { adminStoreUpdateSchema, announcementBarSchema, } from "./features/admin/schemas/admin-editor-forms";
562
572
  export { adminOrderUpdateSchema, employeeInviteSchema, hardBanReasonSchema, featureFlagsUpdateSchema, adminAdFormSchema, } from "./features/admin/schemas/admin-ops-forms";
563
573
  export { changeEmailSchema } from "./features/account/schemas/index";
@@ -596,7 +606,7 @@ export type { ReturnReason } from "./_internal/shared/features/orders/return-rea
596
606
  export { DISPUTE_KIND, DISPUTE_KIND_LABEL, DISPUTE_GATE, DISPUTE_TRANSIT_WINDOW_DAYS, DISPUTE_NOT_REACHED_GRACE_DAYS, isDisputeKind, checkDisputeGate, resolveDisputeEtd, disputeNotReachedFilableFrom, } from "./_internal/shared/features/orders/dispute-reasons";
597
607
  export type { DisputeKind, DisputeGateOrder, DisputeGateResult, } from "./_internal/shared/features/orders/dispute-reasons";
598
608
  export { INTEGRATIONS, INTEGRATION_ORDER, readIntegrationStates, integrationPatch, allIntegrationsPatch, } from "./_internal/shared/features/admin/integrations";
599
- export type { IntegrationId, IntegrationDescriptor, IntegrationState, } from "./_internal/shared/features/admin/integrations";
609
+ export type { IntegrationId, IntegrationDescriptor, IntegrationPatch, IntegrationState, } from "./_internal/shared/features/admin/integrations";
600
610
  export { RefundRequestView } from "./features/orders/components/RefundRequestView";
601
611
  export type { RefundRequestViewProps, RefundRequestSubmission, } from "./features/orders/components/RefundRequestView";
602
612
  export { ORDER_RETURN_WINDOW_DAYS, ORDER_RETURN_WINDOW_MS, ORDER_CANCELLABLE_STATUSES, } from "./_internal/shared/features/orders/config";