@mohasinac/appkit 4.59.0 → 4.60.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 (532) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
  3. package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
  4. package/dist/_internal/server/features/brands/actions.js +78 -4
  5. package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
  6. package/dist/_internal/server/features/checkout/actions.js +668 -183
  7. package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
  8. package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
  9. package/dist/_internal/server/features/gateway-events/index.js +10 -0
  10. package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
  11. package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
  12. package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
  13. package/dist/_internal/server/features/gateway-events/redact.js +151 -0
  14. package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
  15. package/dist/_internal/server/features/grievance/actions.js +173 -0
  16. package/dist/_internal/server/features/grievance/index.d.ts +8 -0
  17. package/dist/_internal/server/features/grievance/index.js +8 -0
  18. package/dist/_internal/server/features/grouped/data.js +28 -3
  19. package/dist/_internal/server/features/lottery/actions.js +99 -4
  20. package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
  21. package/dist/_internal/server/features/order-completion/actions.js +336 -0
  22. package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
  23. package/dist/_internal/server/features/order-completion/index.js +7 -0
  24. package/dist/_internal/server/features/orders/actions.d.ts +13 -0
  25. package/dist/_internal/server/features/orders/actions.js +56 -2
  26. package/dist/_internal/server/features/orders/adapters.d.ts +20 -2
  27. package/dist/_internal/server/features/orders/adapters.js +44 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  29. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  30. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  31. package/dist/_internal/server/features/orders/index.js +1 -1
  32. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  33. package/dist/_internal/server/features/orders/service.js +74 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  35. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  36. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  37. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  38. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  39. package/dist/_internal/server/features/payouts/actions.js +84 -10
  40. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  41. package/dist/_internal/server/features/points/adjust.js +121 -0
  42. package/dist/_internal/server/features/points/award.d.ts +65 -0
  43. package/dist/_internal/server/features/points/award.js +145 -0
  44. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  45. package/dist/_internal/server/features/price-index/market.js +54 -1
  46. package/dist/_internal/server/features/products/data.js +40 -22
  47. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  48. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  49. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  50. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  51. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  52. package/dist/_internal/server/features/refunds/actions.js +259 -17
  53. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  54. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  55. package/dist/_internal/server/features/reviews/actions.js +3 -2
  56. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  57. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  58. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  59. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  60. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  61. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  62. package/dist/_internal/server/features/stores/tabs.js +24 -4
  63. package/dist/_internal/server/functions/https.d.ts +31 -1
  64. package/dist/_internal/server/functions/https.js +52 -1
  65. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  66. package/dist/_internal/server/functions/scheduled.js +124 -1
  67. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  69. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  70. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  72. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  73. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/core/index.js +2 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  76. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  78. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  80. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  81. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  82. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  84. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  86. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  87. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  88. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  90. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  92. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  93. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  95. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  96. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  97. package/dist/_internal/shared/actions/action-registry.js +71 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  99. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  100. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  101. package/dist/_internal/shared/checkout/lanes.js +46 -0
  102. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  103. package/dist/_internal/shared/checkout/order-math.js +251 -3
  104. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  105. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  106. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  107. package/dist/_internal/shared/features/auctions/config.js +30 -1
  108. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  109. package/dist/_internal/shared/features/brands/schema.js +18 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  111. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  112. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  113. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  114. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  115. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  116. package/dist/_internal/shared/fees/calculator.js +103 -5
  117. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  118. package/dist/_internal/shared/fees/india-states.js +190 -0
  119. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  120. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  121. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  122. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  123. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  124. package/dist/_internal/shared/shipping/quote.js +67 -0
  125. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  126. package/dist/_internal/shared/time/business-days.js +185 -0
  127. package/dist/cli/index.js +0 -2
  128. package/dist/client.d.ts +10 -4
  129. package/dist/client.js +29 -3
  130. package/dist/configs/tailwind.d.ts +0 -12
  131. package/dist/configs/tailwind.js +28 -15
  132. package/dist/constants/api-endpoints.d.ts +51 -9
  133. package/dist/constants/api-endpoints.js +23 -7
  134. package/dist/constants/index.d.ts +1 -1
  135. package/dist/constants/index.js +1 -1
  136. package/dist/contracts/index.d.ts +1 -1
  137. package/dist/contracts/payment.d.ts +131 -0
  138. package/dist/features/about/components/AboutView.js +1 -1
  139. package/dist/features/about/components/DeveloperView.js +5 -2
  140. package/dist/features/about/components/FAQPageView.js +2 -1
  141. package/dist/features/about/components/FeesView.js +2 -1
  142. package/dist/features/about/components/HelpPageView.js +2 -1
  143. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  144. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  145. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  146. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  147. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  148. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  149. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  150. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  151. package/dist/features/about/components/PolicyPageView.js +2 -1
  152. package/dist/features/about/components/ProseHero.d.ts +73 -0
  153. package/dist/features/about/components/ProseHero.js +18 -0
  154. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  155. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  156. package/dist/features/about/components/TrackOrderView.js +2 -1
  157. package/dist/features/about/components/UnauthorizedView.js +1 -1
  158. package/dist/features/about/index.d.ts +2 -0
  159. package/dist/features/about/index.js +5 -0
  160. package/dist/features/account/actions/address-actions.d.ts +26 -2
  161. package/dist/features/account/actions/address-actions.js +25 -1
  162. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  163. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  164. package/dist/features/account/components/UserOrdersView.js +17 -0
  165. package/dist/features/account/components/UserSidebar.js +1 -1
  166. package/dist/features/account/hooks/useNotifications.js +29 -0
  167. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  168. package/dist/features/admin/components/AdminBeysView.js +2 -2
  169. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  170. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  171. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  175. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  176. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  177. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  178. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  179. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  180. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  181. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  182. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  183. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  184. package/dist/features/admin/components/AdminPartsView.js +2 -2
  185. package/dist/features/admin/components/AdminProductsView.js +75 -4
  186. package/dist/features/admin/components/AdminSidebar.js +2 -2
  187. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  188. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  189. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  190. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  191. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  192. package/dist/features/admin/components/AdminUsersView.js +72 -1
  193. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  194. package/dist/features/admin/components/AdminViewCards.js +4 -4
  195. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  196. package/dist/features/admin/components/DataListingView.js +175 -14
  197. package/dist/features/admin/components/DataTable.d.ts +12 -1
  198. package/dist/features/admin/components/DataTable.js +3 -3
  199. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  200. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  201. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  202. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  203. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  204. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  205. package/dist/features/admin/components/index.d.ts +4 -0
  206. package/dist/features/admin/components/index.js +6 -0
  207. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  208. package/dist/features/admin/constants/filter-tabs.js +106 -0
  209. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  210. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  211. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  212. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  213. package/dist/features/admin/schemas/firestore.js +5 -1
  214. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  215. package/dist/features/auctions/actions/bid-actions.js +13 -1
  216. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  217. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  219. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  220. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  221. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  222. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  223. package/dist/features/audit-log/schemas/firestore.js +14 -0
  224. package/dist/features/auth/permissions/constants.d.ts +17 -1
  225. package/dist/features/auth/permissions/constants.js +18 -0
  226. package/dist/features/blog/components/BlogPostView.js +1 -1
  227. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  228. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  229. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  230. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  231. package/dist/features/categories/components/BundleDetailView.js +1 -1
  232. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  233. package/dist/features/categories/components/CategoryForm.js +12 -1
  234. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  235. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  236. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  237. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  238. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  239. package/dist/features/events/components/EventSaleBanner.js +1 -1
  240. package/dist/features/events/components/EventStatusBadge.js +1 -1
  241. package/dist/features/faq/components/FAQAccordion.js +2 -1
  242. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  243. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  244. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  245. package/dist/features/filters/FilterFacetSection.js +1 -1
  246. package/dist/features/filters/SwitchFilter.js +1 -1
  247. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  248. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  249. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  250. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  251. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  252. package/dist/features/gateway-events/schemas/index.js +79 -0
  253. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  254. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  255. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  256. package/dist/features/homepage/components/BrandsSection.js +1 -1
  257. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  258. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  259. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  260. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  261. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  262. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  264. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  265. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  266. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  267. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  268. package/dist/features/inventory/components/operator-message.js +41 -0
  269. package/dist/features/inventory/index.d.ts +13 -0
  270. package/dist/features/inventory/index.js +28 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  272. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  274. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  276. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  277. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  278. package/dist/features/inventory/schemas/firestore.js +159 -0
  279. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  280. package/dist/features/layout/AppLayoutShell.js +25 -12
  281. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  282. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  283. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  284. package/dist/features/layout/BottomNavbar.js +2 -2
  285. package/dist/features/media/MediaAvatar.js +1 -1
  286. package/dist/features/media/MediaImage.d.ts +24 -4
  287. package/dist/features/media/MediaImage.js +44 -4
  288. package/dist/features/media/MediaLightbox.js +1 -1
  289. package/dist/features/media/modals/ImageCropModal.js +1 -1
  290. package/dist/features/media/modals/ImageEditor.js +1 -1
  291. package/dist/features/media/upload/MediaUploadField.js +91 -3
  292. package/dist/features/obligations/constants/policies.d.ts +92 -0
  293. package/dist/features/obligations/constants/policies.js +378 -0
  294. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  295. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  296. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  297. package/dist/features/obligations/schemas/firestore.js +87 -0
  298. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  299. package/dist/features/orders/actions/order-actions.js +60 -3
  300. package/dist/features/orders/actions/refund-actions.js +70 -10
  301. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  302. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  303. package/dist/features/orders/components/OrdersList.js +2 -2
  304. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  305. package/dist/features/orders/components/index.d.ts +2 -0
  306. package/dist/features/orders/components/index.js +6 -0
  307. package/dist/features/orders/constants/transitions.d.ts +59 -0
  308. package/dist/features/orders/constants/transitions.js +104 -0
  309. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  310. package/dist/features/orders/repository/orders.repository.js +464 -21
  311. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  312. package/dist/features/orders/schemas/firestore.js +24 -0
  313. package/dist/features/orders/schemas/index.d.ts +61 -0
  314. package/dist/features/orders/schemas/index.js +22 -0
  315. package/dist/features/orders/types/index.d.ts +79 -0
  316. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  317. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  318. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  319. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  320. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  322. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  323. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  324. package/dist/features/payments/repository/payout.repository.js +0 -1
  325. package/dist/features/points/repository/points.repository.d.ts +122 -0
  326. package/dist/features/points/repository/points.repository.js +304 -1
  327. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  328. package/dist/features/points/schemas/adjust-form.js +63 -0
  329. package/dist/features/points/schemas/firestore.d.ts +108 -1
  330. package/dist/features/points/schemas/firestore.js +84 -0
  331. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  332. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  333. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  334. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  335. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  336. package/dist/features/price-index/market-stats.d.ts +82 -0
  337. package/dist/features/price-index/market-stats.js +230 -0
  338. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  339. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  340. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  341. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  342. package/dist/features/print/components/labels.d.ts +117 -0
  343. package/dist/features/print/components/labels.js +258 -0
  344. package/dist/features/print/components/labels.style.css +46 -0
  345. package/dist/features/print/index.d.ts +6 -0
  346. package/dist/features/print/index.js +16 -0
  347. package/dist/features/products/columns/productTableColumns.js +5 -4
  348. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  349. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  350. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  351. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  352. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  353. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  354. package/dist/features/products/components/ProductForm.js +34 -23
  355. package/dist/features/products/components/ProductGrid.js +86 -62
  356. package/dist/features/products/constants/card-badges.d.ts +97 -0
  357. package/dist/features/products/constants/card-badges.js +100 -0
  358. package/dist/features/products/repository/products.repository.js +34 -1
  359. package/dist/features/products/schemas/firestore.d.ts +46 -3
  360. package/dist/features/products/schemas/firestore.js +21 -0
  361. package/dist/features/promotions/components/PromotionsView.js +1 -1
  362. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  363. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  364. package/dist/features/promotions/schemas/firestore.js +30 -3
  365. package/dist/features/promotions/schemas/index.d.ts +6 -6
  366. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  367. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  368. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  369. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  370. package/dist/features/reviews/actions/review-actions.js +3 -2
  371. package/dist/features/reviews/components/ReviewModal.js +1 -1
  372. package/dist/features/reviews/components/ReviewsList.js +1 -1
  373. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  374. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  375. package/dist/features/scams/constants/scam-types.js +18 -0
  376. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  377. package/dist/features/scams/schemas/report-form.js +26 -1
  378. package/dist/features/search/components/Search.js +9 -9
  379. package/dist/features/search/components/SearchResultsClient.js +18 -3
  380. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  381. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  382. package/dist/features/seller/actions/offer-actions.js +16 -1
  383. package/dist/features/seller/actions/seller-actions.js +14 -3
  384. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  385. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  386. package/dist/features/seller/components/PrintCenterView.js +41 -30
  387. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  388. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  389. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  390. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  391. package/dist/features/seller/components/SellerProductShell.js +123 -13
  392. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  393. package/dist/features/seller/components/SellerSidebar.js +1 -1
  394. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  395. package/dist/features/seller/schemas/coupon-form.js +27 -0
  396. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  397. package/dist/features/seller/schemas/product-form.js +5 -0
  398. package/dist/features/shell/FormShell.js +2 -2
  399. package/dist/features/shell/QuickFormDrawer.js +14 -0
  400. package/dist/features/shell/SectionForm.d.ts +19 -0
  401. package/dist/features/shell/SectionForm.js +133 -9
  402. package/dist/features/shell/SectionForm.style.css +54 -0
  403. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  404. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  405. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  406. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  407. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  408. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  409. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  410. package/dist/features/stores/components/StoreHeader.js +1 -1
  411. package/dist/features/support/schemas/firestore.d.ts +73 -2
  412. package/dist/features/support/schemas/firestore.js +59 -0
  413. package/dist/index.d.ts +16 -24
  414. package/dist/index.js +38 -47
  415. package/dist/next/ErrorBoundary.js +1 -1
  416. package/dist/next/routing/route-map.d.ts +24 -0
  417. package/dist/next/routing/route-map.js +40 -0
  418. package/dist/providers/payment-manual/index.d.ts +15 -1
  419. package/dist/providers/payment-manual/index.js +37 -0
  420. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  421. package/dist/providers/payment-phonepe/index.js +36 -0
  422. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  423. package/dist/providers/payment-razorpay/index.js +99 -0
  424. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  425. package/dist/providers/shipping-shiprocket/index.js +18 -0
  426. package/dist/schemas/registry.d.ts +44 -11
  427. package/dist/server-entry.d.ts +2 -0
  428. package/dist/server-entry.js +27 -0
  429. package/dist/server.d.ts +43 -0
  430. package/dist/server.js +98 -0
  431. package/dist/styles.css +418 -149
  432. package/dist/tailwind-utilities.css +1 -1
  433. package/dist/tokens/themes/default-dark.js +15 -12
  434. package/dist/tokens/themes/default-light.js +14 -11
  435. package/dist/tokens/tokens.css +106 -23
  436. package/dist/ui/DataTable.style.css +4 -4
  437. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  438. package/dist/ui/columns/cell-renderers.js +26 -1
  439. package/dist/ui/columns/index.d.ts +1 -1
  440. package/dist/ui/columns/index.js +4 -1
  441. package/dist/ui/components/Badge.style.css +1 -1
  442. package/dist/ui/components/Breadcrumb.js +19 -1
  443. package/dist/ui/components/BulkActionBar.style.css +4 -4
  444. package/dist/ui/components/Button.style.css +1 -1
  445. package/dist/ui/components/Checkbox.style.css +1 -1
  446. package/dist/ui/components/FallbackShell.js +1 -1
  447. package/dist/ui/components/Form.d.ts +14 -1
  448. package/dist/ui/components/Form.js +2 -2
  449. package/dist/ui/components/FormField.js +18 -4
  450. package/dist/ui/components/FormField.style.css +43 -3
  451. package/dist/ui/components/GatedPrice.style.css +1 -1
  452. package/dist/ui/components/IconButton.style.css +1 -1
  453. package/dist/ui/components/Input.js +22 -1
  454. package/dist/ui/components/Input.style.css +38 -22
  455. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  456. package/dist/ui/components/ListingToolbar.js +1 -1
  457. package/dist/ui/components/PaginatedSelect.js +0 -0
  458. package/dist/ui/components/Radio.style.css +1 -1
  459. package/dist/ui/components/Repeater.d.ts +53 -0
  460. package/dist/ui/components/Repeater.js +7 -0
  461. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  462. package/dist/ui/components/RichTextEditor.js +23 -2
  463. package/dist/ui/components/Select.style.css +7 -21
  464. package/dist/ui/components/Semantic.js +27 -3
  465. package/dist/ui/components/TagInput.style.css +4 -9
  466. package/dist/ui/components/Textarea.style.css +4 -18
  467. package/dist/ui/components/Toggle.style.css +1 -1
  468. package/dist/ui/components/Typography.d.ts +3 -3
  469. package/dist/ui/components/Typography.js +15 -15
  470. package/dist/ui/components/ViewToggle.style.css +4 -4
  471. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  472. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  473. package/dist/ui/forms/ColorPickerField.js +15 -2
  474. package/dist/ui/forms/FieldCheckbox.js +15 -2
  475. package/dist/ui/forms/FieldInput.d.ts +22 -1
  476. package/dist/ui/forms/FieldInput.js +19 -5
  477. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  478. package/dist/ui/forms/FieldLabel.js +70 -0
  479. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  480. package/dist/ui/forms/FieldSelect.js +27 -5
  481. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  482. package/dist/ui/forms/FieldTextarea.js +22 -5
  483. package/dist/ui/forms/FormShell.d.ts +17 -1
  484. package/dist/ui/forms/FormShell.js +36 -2
  485. package/dist/ui/forms/FormShell.style.css +41 -33
  486. package/dist/ui/forms/index.d.ts +5 -1
  487. package/dist/ui/forms/index.js +2 -0
  488. package/dist/ui/icons/icon-registry.d.ts +1 -0
  489. package/dist/ui/icons/icon-registry.js +17 -1
  490. package/dist/utils/id-generators.d.ts +80 -0
  491. package/dist/utils/id-generators.js +93 -0
  492. package/dist/utils/search-txt-builders.d.ts +12 -0
  493. package/dist/utils/search-txt-builders.js +17 -0
  494. package/package.json +1 -1
  495. package/scripts/seed-cli.mjs +106 -5
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  497. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  498. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  499. package/dist/features/loyalty/api/balance/route.js +0 -41
  500. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  501. package/dist/features/loyalty/api/earn/route.js +0 -38
  502. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  503. package/dist/features/loyalty/api/history/route.js +0 -58
  504. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  505. package/dist/features/loyalty/api/redeem/route.js +0 -38
  506. package/dist/features/loyalty/columns/index.d.ts +0 -17
  507. package/dist/features/loyalty/columns/index.js +0 -40
  508. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  509. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  510. package/dist/features/loyalty/components/index.d.ts +0 -1
  511. package/dist/features/loyalty/components/index.js +0 -1
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  513. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  514. package/dist/features/loyalty/index.d.ts +0 -6
  515. package/dist/features/loyalty/index.js +0 -6
  516. package/dist/features/loyalty/manifest.d.ts +0 -2
  517. package/dist/features/loyalty/manifest.js +0 -16
  518. package/dist/features/loyalty/messages/en.json +0 -19
  519. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  520. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  521. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  522. package/dist/features/loyalty/schemas/index.js +0 -40
  523. package/dist/features/loyalty/server.d.ts +0 -6
  524. package/dist/features/loyalty/server.js +0 -6
  525. package/dist/features/loyalty/types/index.d.ts +0 -31
  526. package/dist/features/loyalty/types/index.js +0 -1
  527. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  528. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  529. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  530. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  531. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  532. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -0,0 +1,466 @@
1
+ /**
2
+ * Cancel an order AND give the money back — one owner, three callers.
3
+ *
4
+ * ## D14, and why passing the amount was only half of it
5
+ *
6
+ * `cancelOrder(orderId, reason, refundAmount?)` has carried an optional
7
+ * `refundAmount` since it was written, and **all four callers omitted it**. So
8
+ * cancelling a *paid* order restored stock and left the money with us, while
9
+ * `previewCancellationRefund` showed the buyer a figure nothing honoured.
10
+ *
11
+ * 🛑 **But passing the amount alone would have made it WORSE.** `cancelOrder`'s
12
+ * body writes `{ refundAmount, refundStatus: "pending" }` and nothing else —
13
+ * there is no provider call anywhere in it. So the obvious fix creates a *new*
14
+ * dead state: a recorded refund intent that nothing ever settles, which is
15
+ * strictly worse than today's honest silence because it reads, in the database,
16
+ * as though a refund is on its way. The amount and the movement have to land
17
+ * together, which is why this module exists rather than a one-line edit at each
18
+ * call site.
19
+ *
20
+ * ## The decision table
21
+ *
22
+ * Every row cancels the order and **restores its stock** — see `releaseStock`,
23
+ * which closes a second half of D14 the plan had recorded backwards. The table
24
+ * is only about the money.
25
+ *
26
+ * | condition | what happens to the money |
27
+ * |---|---|
28
+ * | `paymentStatus !== "paid"` | **nothing** — nothing was ever captured |
29
+ * | nothing left refundable | nothing — already refunded in full |
30
+ * | goods have left the seller | intent recorded, **case opened** — a human decides |
31
+ * | paid by cash / UPI-manual / EMI | intent recorded, **case opened** — there is no automatic rail |
32
+ * | paid through the gateway | **refunded automatically**, in full, once |
33
+ *
34
+ * The last row is the only automatic money movement, and it satisfies the
35
+ * plan's guardrail for one: the ceiling is an amount we captured, the recipient
36
+ * is the payer, and no counterparty is debited.
37
+ *
38
+ * ## Three things that are load-bearing
39
+ *
40
+ * **It goes through `processRefundAction`, never `payment.refund()` directly.**
41
+ * That action owns the idempotency claim, the cumulative cap against
42
+ * `refundableBase`, and the two-phase reserve/settle write around the provider
43
+ * call. A direct provider call here would reintroduce every defect step 8
44
+ * closed — D1, D2, D6 and D7 at once.
45
+ *
46
+ * **The key is `cancel_{orderId}`** — a NATURAL key, so a double-clicked Cancel
47
+ * button, a retried server action and a sweep all collapse to one refund. A
48
+ * `randomUUID()` here would be D2 wearing a new name.
49
+ *
50
+ * ## Points ARE reversed here now (Phase P)
51
+ *
52
+ * `pointsLedger`'s own header states the rule as a legal boundary: *"points
53
+ * spent on a cancelled order are returned as points"* — never as money, because
54
+ * paying them out in rupees is what would make this stored value, which in
55
+ * India is PPI territory with RBI authorisation attached.
56
+ *
57
+ * `reverseRedemptionForOrder` is that rule's first writer; the kind had been
58
+ * declared with none since it was written (D48). It is idempotent on the order
59
+ * — one reversal per order, checked by query before the write, because an
60
+ * append-only ledger cannot net off a double reversal.
61
+ *
62
+ * 🛑 It is a NO-OP today and still correct to call. No order has ever consumed
63
+ * points: the one code path that redeemed any was deleted on 2026-10-21
64
+ * because it debited the buyer without reducing the charge (D46 — see the note
65
+ * at its former site in `checkout/actions.ts`), and it had no caller, route
66
+ * field or UI tender before that. So the query finds no redemption and returns
67
+ * `{ reversed: 0 }`.
68
+ * Wiring it now rather than later is the cheap half — the expensive half is the
69
+ * ledger write, which is already keyed. When redemption becomes a real tender,
70
+ * cancellation already gives the points back instead of needing to remember to.
71
+ *
72
+ * **Cancel, then stock, then money.** The cancellation is the urgent half and
73
+ * must not be held hostage to a gateway round trip; the restock is a local
74
+ * write another buyer is actively waiting on; the refund is the one step that
75
+ * can hang for a full network timeout. The cost of that ordering is a window
76
+ * where the order is `cancelled` with the money still held — which is exactly
77
+ * why `refundAmount` is written *at cancellation time*, before the provider is
78
+ * called: a crash in the window leaves a detectable artefact rather than
79
+ * nothing, and R5 in `paymentReconcile` is the thing that finds it. The reverse
80
+ * ordering has no such consolation, because a refund posted against a live
81
+ * order that then fails to cancel is money gone from an order the buyer still
82
+ * expects to receive.
83
+ */
84
+ import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
85
+ import { isManualPaymentMethod } from "../../../../features/orders/constants/payment-window.js";
86
+ import { MONEY_EPSILON } from "../../../shared/checkout/refundable-base.js";
87
+ /*
88
+ * 🛑 `refundCapacity` and NOT `totalPrice − platformFeeWithheld`, and the
89
+ * difference matters on a partially-refunded order: capacity subtracts what has
90
+ * already been committed, so cancelling an order that already had one line
91
+ * refunded returns the REMAINDER rather than the whole base a second time. The
92
+ * naive expression would over-refund by exactly the earlier refund, and
93
+ * `reserveRefund`'s transactional cap would then reject it — so it would
94
+ * surface as a confusing failure rather than a loss, but it would surface.
95
+ *
96
+ * Through `resolveRefundCapacity` rather than the pure function, so the
97
+ * settings read that legacy orders need has one owner shared with the sweep.
98
+ */
99
+ import { resolveRefundCapacity } from "../refunds/capacity.js";
100
+ import { paymentCasesRepository } from "../../../../features/payment-cases/repository/payment-cases.repository.js";
101
+ import { processRefundAction } from "../refunds/actions.js";
102
+ import { restoreStockForOrder } from "../checkout/stock-restore.js";
103
+ import { pointsRepository } from "../../../../features/points/repository/points.repository.js";
104
+ import { getAdminDb } from "../../../../providers/db-firebase/index.js";
105
+ import { classifyCancellation } from "./service.js";
106
+ import { normalizeError } from "../../../../errors/normalize.js";
107
+ import { serverLogger } from "../../../../monitoring/index.js";
108
+ /**
109
+ * Put the units back on the shelf.
110
+ *
111
+ * ## 🛑 A SECOND HALF OF D14 THAT THE PLAN RECORDED BACKWARDS
112
+ *
113
+ * The plan's D14 entry reads *"Stock is restored; **money is not** — including
114
+ * on a `paid` order."* Measured 2026-10-21: **neither is.**
115
+ * `restoreStockForOrder` has exactly four call sites —
116
+ * `paymentWindowTimeout`, `pendingOrderTimeout`,
117
+ * `adminRejectPaymentAsFraudAction`, and its own definition — and **not one of
118
+ * them is a user-initiated cancellation.** So a buyer, seller or admin
119
+ * cancelling an order left its units decremented, and the item stayed
120
+ * unavailable to every other buyer permanently. That is Root Cause #27's
121
+ * "holds stock forever" shape, reached by a different door.
122
+ *
123
+ * The plan's sentence is how the mistake survived: it describes the TIMEOUT
124
+ * jobs' behaviour and generalises it to all four callers, so "stock is fine,
125
+ * only money is broken" reads as settled and nobody re-checks.
126
+ *
127
+ * ## Why this is a separate call and not `extraOrderUpdate`
128
+ *
129
+ * `restoreStockForOrder` offers to write the caller's cancellation fields
130
+ * inside its own transaction, and its docstring explains why: atomicity, so
131
+ * the two "can never partially apply". Taking that offer would mean bypassing
132
+ * `orderRepository.cancelOrder` — and with it the status transition guard, the
133
+ * `statusHistory` entry and the obligation reconciliation, all of which live in
134
+ * `updateWithHistory`. Three mechanisms built on purpose, lost to gain
135
+ * atomicity on a pair of writes whose split failure is both detectable
136
+ * (`status: "cancelled"` with `stockRestored` unset) and repairable by calling
137
+ * this again.
138
+ *
139
+ * That repair is free because the function is **idempotent on its own flag** —
140
+ * `if (order.stockRestored) return` — which is independent of the order's
141
+ * status, so running it after the cancellation is fine.
142
+ *
143
+ * ## It never throws upward
144
+ *
145
+ * The cancellation has already been written and the buyer has already been told
146
+ * their order is cancelled. Failing the call now would report a failure for
147
+ * something that succeeded, and would leave the caller unsure whether to
148
+ * retry — while the money half still needs to run. Logged loudly instead.
149
+ */
150
+ async function releaseStock(order) {
151
+ if (order.stockRestored)
152
+ return;
153
+ try {
154
+ await restoreStockForOrder(getAdminDb(), order, {});
155
+ }
156
+ catch (err) {
157
+ void normalizeError(err);
158
+ serverLogger.error("cancellation: stock was NOT restored — these units are unavailable to other buyers until this is repaired", { orderId: order.id });
159
+ }
160
+ }
161
+ /**
162
+ * Give back any points this order consumed — `redemption_reversal`.
163
+ *
164
+ * 🛑 As POINTS, never as money. `pointsLedger`'s header states that as a legal
165
+ * boundary rather than a preference: cash-refunding them is what would make
166
+ * this stored value, and stored value in India is PPI territory with RBI
167
+ * authorisation attached.
168
+ *
169
+ * Idempotent on the order inside the repository, so calling it on every
170
+ * cancellation — including a re-run — is safe. Currently a no-op for every
171
+ * order, because nothing consumes points yet; wiring it now means cancellation
172
+ * will already be correct on the day redemption becomes a real tender, rather
173
+ * than needing somebody to remember.
174
+ *
175
+ * Never throws upward: the order is cancelled and the buyer has been told. A
176
+ * failed reversal is points the buyer should have back, so it is logged loudly
177
+ * and the balance is recoverable by an admin adjustment — whereas failing the
178
+ * cancellation here would leave them unable to retry something that already
179
+ * happened.
180
+ */
181
+ async function returnPoints(order) {
182
+ try {
183
+ const { reversed } = await pointsRepository.reverseRedemptionForOrder(order.id, `order ${order.id} was cancelled`);
184
+ if (reversed > 0) {
185
+ serverLogger.info("cancellation: points returned to the buyer", {
186
+ orderId: order.id,
187
+ points: reversed,
188
+ });
189
+ }
190
+ }
191
+ catch (err) {
192
+ void normalizeError(err);
193
+ serverLogger.error("cancellation: points were NOT returned — the buyer is short by whatever they spent", { orderId: order.id });
194
+ }
195
+ }
196
+ /** Record that money is owed and nobody can move it automatically. */
197
+ async function openOwedCase(order, amount, why, detailLines) {
198
+ try {
199
+ const result = await paymentCasesRepository().openOrObserve({
200
+ kind: "manual_refund_due",
201
+ /*
202
+ * Keyed on the ORDER, not on the attempt. A retried cancellation, a
203
+ * sweep and an operator clicking twice are the same problem — one case,
204
+ * one notification. Including a timestamp here is what makes a queue
205
+ * unusable within a week.
206
+ */
207
+ subjectId: order.id,
208
+ severity: "error",
209
+ title: `Refund of ₹${amount.toFixed(2)} owed on cancelled order ${order.id}`,
210
+ detail: [
211
+ ...detailLines,
212
+ "",
213
+ `Order: ${order.id}`,
214
+ `Buyer: ${order.userId}`,
215
+ `Store: ${order.storeId ?? "—"}`,
216
+ `Payment method: ${order.paymentMethod ?? "unknown"}`,
217
+ `Gateway reference: ${order.paymentId ?? "—"}`,
218
+ `Amount owed: ₹${amount.toFixed(2)}`,
219
+ "",
220
+ "The order is cancelled and its stock is restored. `refundAmount` is recorded on the order with `refundStatus: \"pending\"`, so this case and the order agree about what is owed.",
221
+ ].join("\n"),
222
+ entityType: "order",
223
+ entityId: order.id,
224
+ amountAtRisk: amount,
225
+ orderIds: [order.id],
226
+ ...(order.paymentId ? { gatewayPaymentId: order.paymentId } : {}),
227
+ });
228
+ return result.id;
229
+ }
230
+ catch (err) {
231
+ void normalizeError(err);
232
+ /*
233
+ * The case is the RECORD, not the obligation. Failing to write it must not
234
+ * fail the cancellation — the buyer's order is already cancelled and their
235
+ * stock released. Logged loudly, and the amount is still on the order, so
236
+ * D14's (c) detector finds it on the next pass regardless.
237
+ */
238
+ serverLogger.error("cancellation refund: could not open the owed case", {
239
+ orderId: order.id,
240
+ amount,
241
+ why,
242
+ });
243
+ return undefined;
244
+ }
245
+ }
246
+ /**
247
+ * Cancel an order and settle whatever money that owes.
248
+ *
249
+ * Callers pass the order they have already fetched and authorised. That is not
250
+ * only a saved read: the authorisation differs per caller (a buyer owns the
251
+ * order, a seller owns the store, an admin owns neither) and this function must
252
+ * not be the place that decides it.
253
+ */
254
+ export async function cancelOrderWithRefund(order, opts) {
255
+ const ctx = {
256
+ actor: {
257
+ role: opts.actorRole,
258
+ ...(opts.actorUid ? { uid: opts.actorUid } : {}),
259
+ },
260
+ reason: opts.reason,
261
+ ...(opts.trigger ? { trigger: opts.trigger } : {}),
262
+ };
263
+ /*
264
+ * `refundedBy` on the other hand is a REQUIRED string on
265
+ * `ProcessRefundInput`, and a prefixed sentinel is the established shape
266
+ * there — `checkout/actions.ts` already passes
267
+ * `"system:checkout-auto-refund"`. Prefixed rather than bare so it can never
268
+ * be mistaken for a uid by something that only checks for a non-empty value.
269
+ */
270
+ const refundedBy = opts.actorUid ?? `system:${opts.trigger ?? "cancellation"}`;
271
+ /*
272
+ * ── The unpaid case, and the asymmetry this comment exists to protect ─────
273
+ *
274
+ * 🛑 `paymentWindowTimeout` and `pendingOrderTimeout` call `cancelOrder` with
275
+ * NO amount, and that is CORRECT, not an oversight of the same kind D14
276
+ * describes: both sweeps exist precisely to cancel orders that were never
277
+ * paid. Someone reading D14 ("no cancel path passes a refund amount") will be
278
+ * tempted to make them uniform with the three user-initiated paths. Don't —
279
+ * a refund against a payment that never happened is not a no-op, it is a
280
+ * reserved slot against a `refundableBase` of zero, which `reserveRefund`
281
+ * refuses and which would make the sweeps start throwing.
282
+ *
283
+ * The guard lives here rather than at the three call sites so the rule has
284
+ * one statement, and so a fourth caller inherits it.
285
+ */
286
+ if (order.paymentStatus !== "paid") {
287
+ const cancelled = await orderRepository.cancelOrder(order.id, opts.reason, undefined, ctx);
288
+ await releaseStock(order);
289
+ return { order: cancelled, refund: { moved: "none", why: "not-paid", amount: 0 } };
290
+ }
291
+ const capacity = await resolveRefundCapacity(order);
292
+ if (capacity.remaining <= MONEY_EPSILON) {
293
+ /*
294
+ * Paid, but already refunded in full — an order cancelled after its money
295
+ * was returned by another path. Nothing owed, and writing an amount of zero
296
+ * would set `refundStatus: "pending"` for a refund that will never come.
297
+ */
298
+ const cancelled = await orderRepository.cancelOrder(order.id, opts.reason, undefined, ctx);
299
+ await releaseStock(order);
300
+ return {
301
+ order: cancelled,
302
+ refund: { moved: "none", why: "nothing-refundable", amount: 0 },
303
+ };
304
+ }
305
+ const amount = capacity.remaining;
306
+ /*
307
+ * The intent, written in the same patch as the cancellation. This is what
308
+ * makes a crash in the window below detectable: `status: "cancelled"` with a
309
+ * `refundAmount` and no settled refund event is a provable, findable state.
310
+ */
311
+ const cancelled = await orderRepository.cancelOrder(order.id, opts.reason, amount, ctx);
312
+ /*
313
+ * Stock before money. Restocking is a local write that another buyer is
314
+ * actively waiting on; the refund is a network call that can hang for its
315
+ * whole timeout. Doing the slow one first would hold the item hostage to the
316
+ * gateway's latency for no benefit.
317
+ */
318
+ await releaseStock(order);
319
+ await returnPoints(order);
320
+ /*
321
+ * ── Goods have left the seller ────────────────────────────────────────────
322
+ *
323
+ * Asks the EXISTING classifier rather than re-testing `shippingDate`, so
324
+ * "has this been dispatched" has one definition shared with
325
+ * `assertOrderCancellable`. Re-deriving it here is how two copies of a rule
326
+ * start disagreeing (Root Cause #75) — and this particular rule is the one
327
+ * the plan calls the single highest-value line in the obligation design.
328
+ *
329
+ * Note the two buyer paths cannot reach this branch: `assertOrderCancellable`
330
+ * refuses a dispatched order outright ("request a return instead"), and
331
+ * `cancelOrderForUser`'s own status gate admits only pending/confirmed. It is
332
+ * reachable from the ADMIN route, which cancels any status the transition map
333
+ * allows — and `shipped → cancelled` is allowed.
334
+ */
335
+ const route = classifyCancellation(order);
336
+ if (route.route !== "cancel") {
337
+ const caseId = await openOwedCase(cancelled, amount, "post-dispatch", [
338
+ "This order was cancelled AFTER the goods were dispatched, so the refund was not issued automatically.",
339
+ "",
340
+ "Who bears the outbound shipping leg is a decision about this order, not a rule: the parcel physically moved and somebody paid a courier for it. Decide the amount, then refund from the admin order page.",
341
+ ]);
342
+ return {
343
+ order: cancelled,
344
+ refund: { moved: "owed", amount, why: "post-dispatch", ...(caseId ? { caseId } : {}) },
345
+ };
346
+ }
347
+ /*
348
+ * ── No automatic rail exists ──────────────────────────────────────────────
349
+ *
350
+ * Cash, UPI-manual and EMI reach `paymentStatus: "paid"` through the manual
351
+ * payment review flow — an admin verified a screenshot. There is no gateway
352
+ * transaction to reverse, so the money goes back by bank transfer and a human
353
+ * records the UTR. Calling `processRefundAction` with `method: "manual"` here
354
+ * would append a refund event claiming the money was returned when nobody has
355
+ * sent it, which is the dead-state trap this module exists to avoid, one
356
+ * level in.
357
+ *
358
+ * The missing-reference case is folded in for the same reason: a gateway
359
+ * refund is order-keyed on `order.paymentId`, so without it there is nothing
360
+ * to call, and guessing is not an option.
361
+ */
362
+ if (isManualPaymentMethod(order.paymentMethod)) {
363
+ const caseId = await openOwedCase(cancelled, amount, "manual-payment", [
364
+ `This order was paid by ${order.paymentMethod} — a manual method with no gateway transaction to reverse.`,
365
+ "",
366
+ "Transfer the amount to the buyer and record it from the admin order page, which captures the reference so the order and the bank statement agree.",
367
+ ]);
368
+ return {
369
+ order: cancelled,
370
+ refund: { moved: "owed", amount, why: "manual-payment", ...(caseId ? { caseId } : {}) },
371
+ };
372
+ }
373
+ if (!order.paymentId) {
374
+ const caseId = await openOwedCase(cancelled, amount, "no-gateway-reference", [
375
+ "This order is marked paid but carries no gateway reference (`paymentId`), so an automatic refund has nothing to call.",
376
+ "",
377
+ "Find the payment in the gateway dashboard by amount and buyer, then refund from the admin order page.",
378
+ ]);
379
+ return {
380
+ order: cancelled,
381
+ refund: { moved: "owed", amount, why: "no-gateway-reference", ...(caseId ? { caseId } : {}) },
382
+ };
383
+ }
384
+ /*
385
+ * ── The automatic path ────────────────────────────────────────────────────
386
+ *
387
+ * `type: "full"` because the amount IS the whole refundable base — the
388
+ * platform fee is withheld by policy, so "full" has never meant `totalPrice`
389
+ * (see `refundableBase`). On a partially-refunded order the remainder is
390
+ * still the rest of the base, so full is still the honest label.
391
+ */
392
+ const result = await processRefundAction({
393
+ orderId: order.id,
394
+ type: "full",
395
+ amount,
396
+ /*
397
+ * `changed_mind` is the honest default: a cancellation before dispatch is a
398
+ * buyer deciding not to proceed with goods they never received. It is also
399
+ * the conservative choice, because it is NOT final-sale exempt — so a
400
+ * cancellation cannot be used to route around a final-sale listing's terms.
401
+ *
402
+ * 🛑 That gate cannot actually bite here, and it is worth knowing why
403
+ * rather than discovering it: `finalSale` is per-LINE and a pre-dispatch
404
+ * cancellation of a final-sale line would be refused by
405
+ * `processRefundAction`. If that is ever reported as a bug, the fix is a
406
+ * decision about whether final sale survives a pre-dispatch cancellation —
407
+ * not a quiet swap to an exempt reason code here.
408
+ */
409
+ reasonCode: "changed_mind",
410
+ reasonNote: `Order cancelled — ${opts.reason}`,
411
+ /*
412
+ * The natural key. A double-clicked Cancel button, a retried server action
413
+ * and a future sweep all produce the same key, so the second one returns
414
+ * the first's refundId as a success instead of issuing a second refund.
415
+ */
416
+ idempotencyKey: `cancel_${order.id}`,
417
+ method: "phonepe",
418
+ phonepeOrderId: order.paymentId,
419
+ confirmIrrevocable: true,
420
+ refundedBy,
421
+ });
422
+ /*
423
+ * 🛑 UNWRAP THE ENVELOPE. `processRefundAction` is a `"use server"` action, so
424
+ * it returns `ActionResult<T>` — `{ ok, data }`, never the payload. Treating
425
+ * the envelope as the result is Root Cause #98, which cost nine seller edit
426
+ * pages a blank form and a destructive save: an envelope is always truthy, so
427
+ * `if (!result)` can never fire and `result.refundId` is silently `undefined`.
428
+ */
429
+ if (!result.ok) {
430
+ const caseId = await openOwedCase(cancelled, amount, "gateway-failed", [
431
+ "The order was cancelled and its stock restored, but the automatic gateway refund FAILED.",
432
+ "",
433
+ `Gateway error: ${result.error ?? "unknown"}`,
434
+ "",
435
+ "Retry the refund from the admin order page. It is idempotent on this order, so a retry cannot double-refund — if the first attempt did reach the gateway, the retry returns that same refund.",
436
+ ]);
437
+ serverLogger.error("cancellation refund: gateway refund failed — money still held", {
438
+ orderId: order.id,
439
+ amount,
440
+ caseId,
441
+ });
442
+ return {
443
+ order: cancelled,
444
+ refund: { moved: "owed", amount, why: "gateway-failed", ...(caseId ? { caseId } : {}) },
445
+ };
446
+ }
447
+ serverLogger.info("cancellation refunded", {
448
+ orderId: order.id,
449
+ amount,
450
+ withheld: capacity.withheld,
451
+ refundId: result.data.refundId,
452
+ actorRole: opts.actorRole,
453
+ });
454
+ /*
455
+ * Re-read, because `processRefundAction` wrote to the order twice after our
456
+ * `cancelOrder` returned (reserve, then settle) — so the document we hold is
457
+ * already stale, and handing a caller a stale order whose `refunds[]` is
458
+ * empty would make every surface downstream show "no refund yet" on an order
459
+ * that was just refunded.
460
+ */
461
+ const settled = await orderRepository.findById(order.id);
462
+ return {
463
+ order: settled ?? cancelled,
464
+ refund: { moved: "gateway", amount, refundId: result.data.refundId },
465
+ };
466
+ }
@@ -1,5 +1,6 @@
1
1
  export { getOrderForDetail, getOrdersForBuyer, getRecentOrdersForBuyer } from "./data";
2
2
  export { orderDocumentToOrder } from "./adapters";
3
- export { assertOrderOwnership, assertOrderCancellable } from "./service";
3
+ export { assertOrderOwnership, assertOrderCancellable, classifyCancellation } from "./service";
4
+ export type { CancellationRoute } from "./service";
4
5
  export { createOrderAction, cancelOrderAction, requestReturnAction, updateOrderStatusAction } from "./actions";
5
6
  export { ORDERS_PAGE_SIZE, ORDER_CANCELLABLE_STATUSES, ORDER_RETURN_WINDOW_DAYS } from "../../../shared/features/orders/config";
@@ -1,5 +1,5 @@
1
1
  export { getOrderForDetail, getOrdersForBuyer, getRecentOrdersForBuyer } from "./data.js";
2
2
  export { orderDocumentToOrder } from "./adapters.js";
3
- export { assertOrderOwnership, assertOrderCancellable } from "./service.js";
3
+ export { assertOrderOwnership, assertOrderCancellable, classifyCancellation } from "./service.js";
4
4
  export { createOrderAction, cancelOrderAction, requestReturnAction, updateOrderStatusAction } from "./actions.js";
5
5
  export { ORDERS_PAGE_SIZE, ORDER_CANCELLABLE_STATUSES, ORDER_RETURN_WINDOW_DAYS } from "../../../shared/features/orders/config.js";
@@ -1,4 +1,64 @@
1
1
  import type { OrderDocument } from "../../../../features/orders/schemas";
2
2
  export declare function assertOrderOwnership(orderId: string, userId: string): Promise<OrderDocument>;
3
+ /**
4
+ * What a buyer asking to cancel is actually asking for.
5
+ *
6
+ * 🛑 **`shippingDate != null` IS THE DISCRIMINATOR, not the status set**, and
7
+ * the plan calls codifying this the single highest-value line in the obligation
8
+ * design. The reason is that "cancel before dispatch" and "cancel after
9
+ * dispatch" are different EVENTS with different money:
10
+ *
11
+ * | | before dispatch | after dispatch |
12
+ * |---|---|---|
13
+ * | what happens | cancel, restock, full refund | refuse the cancel — convert to a return/RTO |
14
+ * | who bears shipping | nobody; it is refunded | the buyer bears the outbound leg |
15
+ * | when money moves | immediately | on receipt of the goods |
16
+ *
17
+ * The status set cannot express that, because `processing` covers both "paid,
18
+ * sitting in a queue" and "packed, courier collecting this afternoon" — while
19
+ * `shippingDate` is the fact that the parcel physically left.
20
+ *
21
+ * And the old behaviour was a dead end: `ORDER_CANCELLABLE_STATUSES` is
22
+ * `["pending", "confirmed"]`, so a buyer whose order was `processing` — or
23
+ * shipped an hour ago — got a flat *"Only pending or confirmed orders can be
24
+ * cancelled"* and **no alternative offered**. That is the Root Cause #56 shape
25
+ * (a surface that refuses without routing anywhere) applied to money.
26
+ */
27
+ export type CancellationRoute = {
28
+ route: "cancel";
29
+ reason: "not yet dispatched";
30
+ } | {
31
+ route: "return";
32
+ reason: "already dispatched";
33
+ dispatchedAt: Date;
34
+ } | {
35
+ route: "refused";
36
+ reason: string;
37
+ };
38
+ export declare function classifyCancellation(order: OrderDocument): CancellationRoute;
3
39
  export declare function assertOrderCancellable(orderId: string, userId: string): Promise<OrderDocument>;
40
+ /**
41
+ * Is the buyer's return window still open?
42
+ *
43
+ * 🛑 The window is measured from DELIVERY, and until 2026-10-21 nothing in the
44
+ * application wrote `deliveryDate` at all — only seed data and display
45
+ * components referenced it — so this function always took its fallback branch
46
+ * and measured from **`updatedAt`**, i.e. from the last time anyone touched
47
+ * the order. A seller adding a note, an admin assigning a worker, or any
48
+ * `statusHistory` append silently restarted the buyer's 7 days. The anchor is
49
+ * now stamped by `OrderRepository.updateWithHistory` on the transition into
50
+ * `delivered`, so the primary branch is reachable for the first time.
51
+ *
52
+ * Two deliberate choices in the fallback:
53
+ *
54
+ * · **It still exists**, because every order delivered before that date has
55
+ * no anchor, and the alternative — refusing the return outright — would
56
+ * punish buyers for our omission.
57
+ * · **It fails OPEN** (`?? Date.now()` keeps the window open rather than
58
+ * closing it). For a *buyer's* right the safe direction is to allow; this
59
+ * is the mirror of the payout hold, where the same uncertainty must
60
+ * WITHHOLD, because there the unsafe direction is releasing money we
61
+ * cannot get back. Same missing anchor, opposite correct defaults — which
62
+ * is why the two are not sharing one helper.
63
+ */
4
64
  export declare function assertReturnWindowOpen(orderId: string, userId: string): Promise<OrderDocument>;
@@ -9,18 +9,92 @@ export async function assertOrderOwnership(orderId, userId) {
9
9
  throw new OrderOwnershipError(orderId);
10
10
  return order;
11
11
  }
12
+ export function classifyCancellation(order) {
13
+ /*
14
+ * Terminal first. A cancelled or refunded order is not a cancellation
15
+ * request, it is a duplicate — and answering "convert this to a return"
16
+ * would send the buyer into a flow for goods they no longer have.
17
+ */
18
+ if (order.status === "cancelled" || order.status === "refunded") {
19
+ return { route: "refused", reason: `this order is already ${order.status}` };
20
+ }
21
+ /*
22
+ * A return already in flight is its own conversation. Offering a cancellation
23
+ * on top would give the buyer two parallel money paths for one order, which
24
+ * is how a double refund starts.
25
+ */
26
+ if (order.status === "return_requested" || order.status === "returned") {
27
+ return { route: "refused", reason: "a return is already in progress for this order" };
28
+ }
29
+ if (order.shippingDate) {
30
+ return {
31
+ route: "return",
32
+ reason: "already dispatched",
33
+ dispatchedAt: order.shippingDate,
34
+ };
35
+ }
36
+ return { route: "cancel", reason: "not yet dispatched" };
37
+ }
12
38
  export async function assertOrderCancellable(orderId, userId) {
13
39
  const order = await assertOrderOwnership(orderId, userId);
40
+ /*
41
+ * 🛑 The classifier decides, and `ORDER_CANCELLABLE_STATUSES` is now only a
42
+ * BACKSTOP below rather than the rule.
43
+ *
44
+ * Kept in that order deliberately: the classifier widens what is cancellable
45
+ * (a `processing` order that has not shipped now qualifies, correctly), and
46
+ * the status check behind it still refuses anything the classifier admitted
47
+ * but the lifecycle does not recognise. Replacing the status check outright
48
+ * would have been a wider behaviour change than the evidence supports.
49
+ */
50
+ const route = classifyCancellation(order);
51
+ if (route.route === "return") {
52
+ throw new OrderCancelError(`${order.status} (dispatched ${route.dispatchedAt.toISOString()} — request a return instead)`);
53
+ }
54
+ if (route.route === "refused") {
55
+ throw new OrderCancelError(`${order.status} (${route.reason})`);
56
+ }
14
57
  const cancellable = ORDER_CANCELLABLE_STATUSES;
15
58
  if (!cancellable.includes(order.status)) {
16
59
  throw new OrderCancelError(order.status);
17
60
  }
18
61
  return order;
19
62
  }
63
+ /**
64
+ * Is the buyer's return window still open?
65
+ *
66
+ * 🛑 The window is measured from DELIVERY, and until 2026-10-21 nothing in the
67
+ * application wrote `deliveryDate` at all — only seed data and display
68
+ * components referenced it — so this function always took its fallback branch
69
+ * and measured from **`updatedAt`**, i.e. from the last time anyone touched
70
+ * the order. A seller adding a note, an admin assigning a worker, or any
71
+ * `statusHistory` append silently restarted the buyer's 7 days. The anchor is
72
+ * now stamped by `OrderRepository.updateWithHistory` on the transition into
73
+ * `delivered`, so the primary branch is reachable for the first time.
74
+ *
75
+ * Two deliberate choices in the fallback:
76
+ *
77
+ * · **It still exists**, because every order delivered before that date has
78
+ * no anchor, and the alternative — refusing the return outright — would
79
+ * punish buyers for our omission.
80
+ * · **It fails OPEN** (`?? Date.now()` keeps the window open rather than
81
+ * closing it). For a *buyer's* right the safe direction is to allow; this
82
+ * is the mirror of the payout hold, where the same uncertainty must
83
+ * WITHHOLD, because there the unsafe direction is releasing money we
84
+ * cannot get back. Same missing anchor, opposite correct defaults — which
85
+ * is why the two are not sharing one helper.
86
+ */
20
87
  export async function assertReturnWindowOpen(orderId, userId) {
21
88
  const order = await assertOrderOwnership(orderId, userId);
22
89
  if (order.status !== "delivered")
23
90
  throw new OrderCancelError(order.status);
91
+ /*
92
+ * `BaseRepository.mapDoc` runs `deserializeTimestamps`, so a Firestore
93
+ * Timestamp has already become a real `Date` by the time it reaches here —
94
+ * which is what makes `instanceof Date` a valid test on THIS path. It would
95
+ * not be on a raw `d.data()` read (see `toDateOrNull` in the repository,
96
+ * which handles all four shapes for exactly that reason).
97
+ */
24
98
  const deliveredAt = order.deliveryDate instanceof Date
25
99
  ? order.deliveryDate
26
100
  : new Date(order.updatedAt ?? Date.now());