@mohasinac/appkit 4.61.0 → 4.62.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 (281) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
  3. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
  4. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
  5. package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
  6. package/dist/_internal/server/features/checkout/actions.js +542 -26
  7. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  8. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  9. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  10. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  11. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  12. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  13. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  14. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  15. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  16. package/dist/_internal/server/features/orders/actions.js +81 -8
  17. package/dist/_internal/server/features/orders/adapters.js +33 -0
  18. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  19. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  20. package/dist/_internal/server/features/points/award.js +46 -4
  21. package/dist/_internal/server/features/refunds/actions.js +26 -0
  22. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  23. package/dist/_internal/server/features/shipping/courier-block.js +239 -0
  24. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  25. package/dist/_internal/server/features/site-settings/adapters.js +15 -0
  26. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  27. package/dist/_internal/server/features/stores/adapters.js +51 -0
  28. package/dist/_internal/server/functions/scheduled.js +38 -1
  29. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
  30. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  31. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  32. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  33. package/dist/_internal/server/jobs/core/index.js +2 -1
  34. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  35. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  36. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  37. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
  38. package/dist/_internal/server/jobs/core/paymentReconcile.js +87 -97
  39. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  40. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  41. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  42. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
  43. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  44. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  45. package/dist/_internal/shared/actions/action-registry.js +177 -9
  46. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  47. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  48. package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
  49. package/dist/_internal/shared/checkout/order-math.js +173 -0
  50. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  51. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  52. package/dist/_internal/shared/checkout/split.d.ts +87 -0
  53. package/dist/_internal/shared/checkout/split.js +100 -0
  54. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  55. package/dist/_internal/shared/content/disclaimers.js +217 -0
  56. package/dist/_internal/shared/content/published-values.d.ts +88 -0
  57. package/dist/_internal/shared/content/published-values.js +198 -0
  58. package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
  59. package/dist/_internal/shared/features/admin/integrations.js +253 -0
  60. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  61. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  62. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  63. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  64. package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
  65. package/dist/_internal/shared/features/points/accepts.js +43 -0
  66. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  67. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  68. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  69. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  70. package/dist/_internal/shared/fees/calculator.js +32 -2
  71. package/dist/_internal/shared/media/limits.d.ts +10 -0
  72. package/dist/_internal/shared/media/limits.js +10 -0
  73. package/dist/_internal/shared/types/action-result.js +15 -1
  74. package/dist/client/api/surface-error.js +16 -2
  75. package/dist/client/api/useApiMutation.js +17 -1
  76. package/dist/client.d.ts +12 -1
  77. package/dist/client.js +31 -1
  78. package/dist/constants/api-endpoints.d.ts +45 -0
  79. package/dist/constants/api-endpoints.js +15 -0
  80. package/dist/constants/field-names.d.ts +18 -0
  81. package/dist/constants/field-names.js +28 -0
  82. package/dist/contracts/index.d.ts +1 -1
  83. package/dist/contracts/shipping.d.ts +45 -0
  84. package/dist/contracts/shipping.js +15 -0
  85. package/dist/core/server-action.js +19 -3
  86. package/dist/errors/error-mapping.js +26 -1
  87. package/dist/features/about/components/AboutView.js +1 -1
  88. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  89. package/dist/features/account/components/UserOffersPanel.js +5 -3
  90. package/dist/features/account/components/UserOrdersView.js +19 -33
  91. package/dist/features/admin/components/AdminActionIndexView.js +100 -5
  92. package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
  93. package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
  94. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  95. package/dist/features/admin/components/AdminCarouselView.js +96 -4
  96. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  97. package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
  98. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  99. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  100. package/dist/features/admin/components/AdminMediaView.js +36 -3
  101. package/dist/features/admin/components/AdminNavigationView.js +4 -1
  102. package/dist/features/admin/components/AdminOffersView.js +2 -1
  103. package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
  104. package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
  105. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  106. package/dist/features/admin/components/AdminOrdersView.js +67 -5
  107. package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
  108. package/dist/features/admin/components/AdminProductEditorView.js +2 -2
  109. package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
  110. package/dist/features/admin/components/AdminSectionsView.js +486 -224
  111. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  112. package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
  113. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  114. package/dist/features/admin/constants/filter-tabs.js +6 -0
  115. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  116. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  117. package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
  118. package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
  119. package/dist/features/admin/repository/notification.repository.d.ts +15 -0
  120. package/dist/features/admin/repository/notification.repository.js +32 -0
  121. package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
  122. package/dist/features/admin/schemas/action-index-form.js +115 -0
  123. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  124. package/dist/features/admin/schemas/firestore.d.ts +30 -0
  125. package/dist/features/admin/schemas/firestore.js +5 -0
  126. package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
  127. package/dist/features/audit-log/schemas/firestore.js +15 -0
  128. package/dist/features/auth/components/RegisterForm.js +2 -1
  129. package/dist/features/auth/permissions/constants.js +13 -0
  130. package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
  131. package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
  132. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  133. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  134. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  135. package/dist/features/cart/components/index.d.ts +2 -0
  136. package/dist/features/cart/components/index.js +1 -0
  137. package/dist/features/cart/repository/cart.repository.d.ts +19 -0
  138. package/dist/features/cart/repository/cart.repository.js +27 -0
  139. package/dist/features/cart/schemas/firestore.d.ts +92 -0
  140. package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
  141. package/dist/features/contact/email.d.ts +27 -10
  142. package/dist/features/contact/email.js +20 -3
  143. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
  144. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
  145. package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
  146. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  147. package/dist/features/events/components/AdminEventEditorView.js +8 -6
  148. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  149. package/dist/features/faq/components/FAQAccordion.js +29 -4
  150. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  151. package/dist/features/invoicing/schemas/firestore.js +45 -0
  152. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  153. package/dist/features/invoicing/utils/financial-year.js +53 -0
  154. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  155. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  156. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  157. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  158. package/dist/features/jobs/schemas/firestore.js +37 -0
  159. package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
  160. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  161. package/dist/features/media/MediaImage.js +22 -1
  162. package/dist/features/media/upload/ImageUpload.js +1 -1
  163. package/dist/features/obligations/constants/policies.js +24 -1
  164. package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
  165. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  166. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  167. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  168. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  169. package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
  170. package/dist/features/orders/components/RefundRequestView.js +22 -4
  171. package/dist/features/orders/components/index.d.ts +4 -0
  172. package/dist/features/orders/components/index.js +2 -0
  173. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  174. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  175. package/dist/features/orders/repository/orders.repository.d.ts +57 -0
  176. package/dist/features/orders/repository/orders.repository.js +145 -0
  177. package/dist/features/orders/schemas/firestore.d.ts +196 -1
  178. package/dist/features/orders/schemas/index.d.ts +60 -12
  179. package/dist/features/orders/schemas/index.js +12 -0
  180. package/dist/features/orders/types/index.d.ts +26 -0
  181. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  182. package/dist/features/orders/utils/courier-block.js +23 -0
  183. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  184. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  185. package/dist/features/products/components/GroupMemberPicker.js +4 -1
  186. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  187. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  188. package/dist/features/products/components/ProductDetailActions.js +2 -1
  189. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  190. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  191. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  192. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  193. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  194. package/dist/features/promotions/components/CouponCard.js +13 -0
  195. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  196. package/dist/features/promotions/constants/coupon-help.js +9 -1
  197. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  198. package/dist/features/promotions/schemas/firestore.js +4 -0
  199. package/dist/features/promotions/types/index.d.ts +19 -1
  200. package/dist/features/scams/components/ScamProfileView.js +2 -2
  201. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  202. package/dist/features/search/action-index/tab-entries.js +1 -1
  203. package/dist/features/seller/actions/seller-actions.js +33 -3
  204. package/dist/features/seller/components/FulfillmentView.js +3 -2
  205. package/dist/features/seller/components/SellerAddressesView.js +32 -4
  206. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
  207. package/dist/features/seller/components/SellerBundlesView.js +53 -14
  208. package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
  209. package/dist/features/seller/components/SellerListingTypeView.js +56 -10
  210. package/dist/features/seller/components/SellerOrdersView.js +129 -27
  211. package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
  212. package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
  213. package/dist/features/seller/components/SellerProductsView.js +5 -1
  214. package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
  215. package/dist/features/seller/components/SellerShippingView.js +2 -1
  216. package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
  217. package/dist/features/seller/components/SellerSupportView.js +69 -2
  218. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  219. package/dist/features/shell/SectionForm.js +20 -16
  220. package/dist/features/shell/build-sections.js +14 -1
  221. package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
  222. package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
  223. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  224. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
  225. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
  226. package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
  227. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  228. package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
  229. package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
  230. package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
  231. package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
  232. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  233. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  234. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  235. package/dist/features/stores/schemas/firestore.d.ts +71 -0
  236. package/dist/features/stores/types/index.d.ts +19 -0
  237. package/dist/features/support/schemas/index.d.ts +6 -6
  238. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
  239. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  240. package/dist/index.d.ts +11 -2
  241. package/dist/index.js +17 -2
  242. package/dist/next/routing/route-map.d.ts +24 -0
  243. package/dist/next/routing/route-map.js +19 -0
  244. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  245. package/dist/providers/shipping-shiprocket/index.js +84 -1
  246. package/dist/schemas/registry.d.ts +51 -22
  247. package/dist/schemas/sieve.d.ts +5 -5
  248. package/dist/server-entry.d.ts +2 -2
  249. package/dist/server-entry.js +8 -1
  250. package/dist/server.d.ts +11 -2
  251. package/dist/server.js +40 -2
  252. package/dist/styles.css +64 -80
  253. package/dist/tailwind-utilities.css +1 -1
  254. package/dist/tokens/themes/default-dark.js +1 -0
  255. package/dist/tokens/themes/default-light.js +1 -0
  256. package/dist/tokens/themes/required.d.ts +1 -1
  257. package/dist/tokens/themes/required.js +1 -0
  258. package/dist/tokens/tokens.css +15 -0
  259. package/dist/ui/components/Checkbox.js +10 -2
  260. package/dist/ui/components/DateInput.js +8 -1
  261. package/dist/ui/components/Disclaimer.d.ts +19 -0
  262. package/dist/ui/components/Disclaimer.js +74 -0
  263. package/dist/ui/components/Input.js +22 -2
  264. package/dist/ui/components/PaginatedSelect.d.ts +8 -19
  265. package/dist/ui/components/PaginatedSelect.js +43 -9
  266. package/dist/ui/components/Radio.js +8 -1
  267. package/dist/ui/components/Select.js +10 -2
  268. package/dist/ui/components/TagInput.d.ts +8 -1
  269. package/dist/ui/components/TagInput.js +4 -3
  270. package/dist/ui/components/Textarea.js +10 -2
  271. package/dist/ui/components/tabs-overflow.js +64 -11
  272. package/dist/ui/forms/FieldCheckbox.js +1 -1
  273. package/dist/ui/forms/FormErrorList.d.ts +0 -16
  274. package/dist/ui/forms/FormErrorList.js +34 -1
  275. package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
  276. package/dist/ui/forms/apply-zod-issues.js +23 -1
  277. package/dist/ui/index.d.ts +2 -0
  278. package/dist/ui/index.js +6 -0
  279. package/dist/ui/rich-text/RichText.style.css +48 -79
  280. package/package.json +1 -1
  281. package/scripts/seed-cli.mjs +1831 -1822
@@ -15,6 +15,8 @@ import { adminNotificationsRepository, orderRepository, storeRepository } from "
15
15
  import { requireRoleUser } from "../../../../providers/auth-firebase/helpers.js";
16
16
  import { createOrderSchema, updateOrderStatusSchema, cancelOrderSchema, returnRequestSchema, } from "../../../shared/features/orders/schema.js";
17
17
  import { isFinalSaleExempt } from "../../../shared/features/orders/return-reasons.js";
18
+ import { DISPUTE_KIND_LABEL, checkDisputeGate, isDisputeKind, } from "../../../shared/features/orders/dispute-reasons.js";
19
+ import { disputeReasonsRepository } from "../../../../features/dispute-reasons/repository/dispute-reasons.repository.js";
18
20
  import { REFUND_COPY } from "../../../shared/features/orders/refund-copy.js";
19
21
  import { assertOrderCancellable, assertReturnWindowOpen } from "./service.js";
20
22
  import { cancelOrderWithRefund } from "./cancellation-refund.js";
@@ -390,10 +392,12 @@ export async function adminRejectPaymentAsFraudAction(orderId, note) {
390
392
  * the seller (store owner), or admin/moderator. Does not itself reverse
391
393
  * payment/order status — surfaces to the admin queue for investigation.
392
394
  */
393
- export async function raiseOrderDisputeAction(orderId, reason) {
395
+ export async function raiseOrderDisputeAction(orderId, input) {
394
396
  return wrapAction(async () => {
395
397
  const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
396
- if (!reason.trim())
398
+ if (!isDisputeKind(input.kind))
399
+ throw new ValidationError("Pick what kind of problem this is.");
400
+ if (!input.reasonId?.trim())
397
401
  throw new ValidationError(REASON_REQUIRED_MSG);
398
402
  const order = await orderRepository.findById(orderId);
399
403
  if (!order)
@@ -407,11 +411,50 @@ export async function raiseOrderDisputeAction(orderId, reason) {
407
411
  if (!isBuyer && !isSeller && !isAdminUser(user) && !isModeratorUser(user)) {
408
412
  throw new OrderOwnershipError(orderId);
409
413
  }
410
- if (!order.autoApproved) {
411
- throw new ValidationError("Disputes can only be raised on auto-approved orders. Use the return/refund flow for manually-reviewed orders.");
412
- }
414
+ /*
415
+ * 🛑 THE `autoApproved` RESTRICTION IS GONE, and that is the one
416
+ * behaviour change an existing path will notice.
417
+ *
418
+ * It used to refuse every order whose payment was not auto-approved,
419
+ * telling the caller to use the return/refund flow — which made this a
420
+ * narrow "the 2-hour auto-approval might have been wrong" escalation
421
+ * rather than a dispute mechanism. The owner's rules are about delivery
422
+ * and item problems on ANY order, so the scope had to widen.
423
+ *
424
+ * What replaces it is a REAL gate per kind, below: the old check refused
425
+ * 99% of orders for a reason unrelated to the complaint, while accepting
426
+ * "it never arrived" on an order delivered last week.
427
+ */
413
428
  if (order.disputeStatus === "open")
414
429
  return; // idempotent
430
+ /*
431
+ * The reason must exist AND belong to the kind being claimed.
432
+ *
433
+ * 🛑 Both halves matter. Without the kind check a caller could pass an
434
+ * `item` reason with `kind: "not_reached"` and pass the wrong gate — the
435
+ * kind is what the gate keys on, so an unvalidated pairing is the gate
436
+ * being bypassed by the request body.
437
+ */
438
+ const reasonDoc = await disputeReasonsRepository.findById(input.reasonId.trim());
439
+ if (!reasonDoc || !reasonDoc.isActive) {
440
+ throw new ValidationError("That reason is no longer available. Pick another.");
441
+ }
442
+ if (reasonDoc.kind !== input.kind) {
443
+ throw new ValidationError("That reason doesn't match the kind of problem selected.");
444
+ }
445
+ /*
446
+ * ── THE GATES ─────────────────────────────────────────────────────────
447
+ *
448
+ * Owner's rules: an item dispute only after the order was received; a
449
+ * "not reached" dispute only once the ETD is more than 3 days past.
450
+ *
451
+ * Enforced HERE, where the request arrives, for the same reason the
452
+ * 7-day cutoff below is: a refusal has to live where the refusing
453
+ * happens. The message is buyer-facing and names the next action.
454
+ */
455
+ const gate = checkDisputeGate(input.kind, order);
456
+ if (!gate.allowed)
457
+ throw new ValidationError(gate.message);
415
458
  /*
416
459
  * ── THE DISPUTE CUTOFF ────────────────────────────────────────────────
417
460
  *
@@ -436,15 +479,39 @@ export async function raiseOrderDisputeAction(orderId, reason) {
436
479
  * `disputeWindowClosed` boolean would need a backfill and would drift from
437
480
  * the policy the day the duration changed (Root Cause #42).
438
481
  */
482
+ /*
483
+ * 🛑 This correctly does NOT apply to a `not_reached` dispute, and that is
484
+ * not an oversight to "fix".
485
+ *
486
+ * `order-complete` is anchored on `deliveryDate`, so `deadlinesFor` yields
487
+ * no such deadline when nothing was delivered — and you cannot be past a
488
+ * delivery window that never happened. Making the cutoff refuse
489
+ * undelivered parcels would block exactly the buyers whose parcel is lost,
490
+ * which is the opposite of what a dispute window is for.
491
+ */
439
492
  const completion = deadlinesFor(order).find((d) => d.kind === "order-complete");
440
493
  if (completion && completion.dueAt.getTime() <= Date.now()) {
441
494
  throw new ValidationError(`The dispute window for this order closed on ${completion.dueAt.toLocaleDateString("en-IN")} — 7 days after delivery. Contact support if you believe this is wrong.`);
442
495
  }
496
+ /*
497
+ * No `as any`. Every field below is declared on `OrderDocument`, and the
498
+ * cast that used to be here is precisely how a misspelled field reaches
499
+ * Firestore unnoticed — it silences the check on all six at once.
500
+ *
501
+ * 🛑 `disputeReason` stores the reason's LABEL, not its id. It is a
502
+ * SNAPSHOT: staff can retire or reword a `disputeReasons` row, and neither
503
+ * may rewrite what a buyer said months ago. `disputeReasonId` beside it is
504
+ * the join key for "what are buyers reporting"; the label is the record.
505
+ * Same discipline as the fee terms and the box presets.
506
+ */
443
507
  await orderRepository.update(orderId, {
444
508
  disputeRaised: true,
445
509
  disputeRaisedBy: user.uid,
446
510
  disputeRaisedAt: new Date(),
447
- disputeReason: reason,
511
+ disputeKind: input.kind,
512
+ disputeReasonId: reasonDoc.id,
513
+ disputeReason: reasonDoc.label,
514
+ ...(input.detail?.trim() ? { disputeDetail: input.detail.trim() } : {}),
448
515
  disputeStatus: "open",
449
516
  });
450
517
  /*
@@ -463,8 +530,14 @@ export async function raiseOrderDisputeAction(orderId, reason) {
463
530
  await adminNotificationsRepository
464
531
  .create({
465
532
  category: "fraud",
466
- title: "Dispute raised on auto-approved order",
467
- body: `A dispute was raised on order "${order.productTitle}" (auto-approved payment). Reason: ${reason}.`,
533
+ /*
534
+ * 🛑 No longer "on auto-approved order". The title said that because
535
+ * the action refused everything else; now any order can be disputed,
536
+ * and a title naming a payment path the dispute has nothing to do with
537
+ * would send an operator looking in the wrong place.
538
+ */
539
+ title: `Dispute raised: ${DISPUTE_KIND_LABEL[input.kind]}`,
540
+ body: `A dispute was raised on order "${order.productTitle}". Reason: ${reasonDoc.label}.${input.detail?.trim() ? ` Buyer said: "${input.detail.trim()}"` : ""}`,
468
541
  severity: "error",
469
542
  isRead: false,
470
543
  entityType: "order",
@@ -203,6 +203,39 @@ export function orderDocumentToOrder(doc) {
203
203
  saleProtectionAddon: doc.saleProtectionAddon,
204
204
  saleProtectionFee: doc.saleProtectionFee,
205
205
  platformFee: doc.platformFee,
206
+ /*
207
+ * D46/D47 — the points tender, mapped for the same reason as everything
208
+ * above it and stated once more because this is the field most likely to
209
+ * be dropped: it is optional, it is absent on every historical order, and a
210
+ * surface that never receives it renders as though no points were used.
211
+ *
212
+ * That is Root Cause #57 exactly, and on this field it is not cosmetic —
213
+ * the order page would show a buyer "Total ₹1,000" and a card statement
214
+ * reading ₹750 with nothing on the page explaining the difference, which
215
+ * reads as an overcharge rather than as their own discount.
216
+ *
217
+ * Readers must use `amountDue ?? total`, never a `?? 0`: absent means "no
218
+ * points, the whole total was collected", and zero means free.
219
+ */
220
+ pointsApplied: doc.pointsApplied,
221
+ amountDue: doc.amountDue,
222
+ /*
223
+ * Phase 7 — the invoice number, mapped for the same Root Cause #57 reason
224
+ * as everything above. Unmapped, the buyer's invoice page would render a
225
+ * statutory document with no number on it while the database held one, and
226
+ * the only symptom would be a blank line where an auditor looks first.
227
+ *
228
+ * 🛑 `invoiceStoragePath` is deliberately NOT mapped. It is a raw Storage
229
+ * path, and the client has no use for one — the bytes are served by an
230
+ * authenticated route. Publishing the path into an RSC payload would hand
231
+ * every buyer a string pointing at a private object, which is the shape of
232
+ * Root Cause #70's leak even though the `private/` prefix is refused by
233
+ * the proxy.
234
+ */
235
+ invoiceNumber: doc.invoiceNumber,
236
+ invoiceIssuedAt: doc.invoiceIssuedAt
237
+ ? new Date(doc.invoiceIssuedAt).toISOString()
238
+ : undefined,
206
239
  /*
207
240
  * 🛑 `gstAmount` -> `tax`, and codHandlingFee alongside it.
208
241
  *
@@ -59,15 +59,21 @@
59
59
  * — one reversal per order, checked by query before the write, because an
60
60
  * append-only ledger cannot net off a double reversal.
61
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.
62
+ * 🛑 **It stopped being a no-op on 2026-10-22.** This paragraph read "It is a
63
+ * NO-OP today and still correct to call … when redemption becomes a real
64
+ * tender, cancellation already gives the points back" — and the tender is now
65
+ * built (`resolvePointsTender` + `settlePointsTender` in `checkout/actions.ts`),
66
+ * so orders carry `pointsApplied` and this query finds real redemptions. The
67
+ * bet paid off: no cancellation path had to be remembered, because the reversal
68
+ * was already wired and keyed.
69
+ *
70
+ * 🛑 **The MONEY half had to change in the same commit, and it is the sharper
71
+ * of the two.** `refundableBase` now subtracts `pointsApplied`, because a
72
+ * refund can only return what was collected: on a ₹1,000 order settled with
73
+ * 250 points the card paid ₹750, so refunding the full base would return
74
+ * ₹1,000 in cash AND 250 points — ₹250 of real money conjured from a discount.
75
+ * Two halves of one refund, in two units, and only their sum is what the buyer
76
+ * gave up.
71
77
  *
72
78
  * **Cancel, then stock, then money.** The cancellation is the urgent half and
73
79
  * must not be held hostage to a gateway round trip; the restock is a local
@@ -59,15 +59,21 @@
59
59
  * — one reversal per order, checked by query before the write, because an
60
60
  * append-only ledger cannot net off a double reversal.
61
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.
62
+ * 🛑 **It stopped being a no-op on 2026-10-22.** This paragraph read "It is a
63
+ * NO-OP today and still correct to call … when redemption becomes a real
64
+ * tender, cancellation already gives the points back" — and the tender is now
65
+ * built (`resolvePointsTender` + `settlePointsTender` in `checkout/actions.ts`),
66
+ * so orders carry `pointsApplied` and this query finds real redemptions. The
67
+ * bet paid off: no cancellation path had to be remembered, because the reversal
68
+ * was already wired and keyed.
69
+ *
70
+ * 🛑 **The MONEY half had to change in the same commit, and it is the sharper
71
+ * of the two.** `refundableBase` now subtracts `pointsApplied`, because a
72
+ * refund can only return what was collected: on a ₹1,000 order settled with
73
+ * 250 points the card paid ₹750, so refunding the full base would return
74
+ * ₹1,000 in cash AND 250 points — ₹250 of real money conjured from a discount.
75
+ * Two halves of one refund, in two units, and only their sum is what the buyer
76
+ * gave up.
71
77
  *
72
78
  * **Cancel, then stock, then money.** The cancellation is the urgent half and
73
79
  * must not be held hostage to a gateway round trip; the restock is a local
@@ -90,16 +90,51 @@ export async function awardPurchasePoints(order) {
90
90
  });
91
91
  return { awarded: 0, skipped: "no-buyer" };
92
92
  }
93
+ /*
94
+ * ── The earn base, and why the points tender comes off it (D46/D47) ───────
95
+ *
96
+ * 🛑 **Points must not breed points.** Earning on an amount that was itself
97
+ * paid with points is a loop: spend 100 points, earn 5 on them, spend those,
98
+ * earn again. It converges rather than exploding, so nothing would ever
99
+ * error — it just leaks margin, and it is indefensible on a balance sheet
100
+ * because the issuance has no sale behind it.
101
+ *
102
+ * The base is therefore the GOODS VALUE ACTUALLY PAID FOR IN MONEY. `goods`
103
+ * already excludes shipping and every fee (`orderTaxableValue` reads
104
+ * `items[]` only — we should not issue points against postage handed to a
105
+ * courier); this subtracts the part of those goods the buyer settled with
106
+ * their balance.
107
+ *
108
+ * 🛑 Clamped at zero rather than assumed smaller. `pointsApplied` is capped
109
+ * at 50% of the ORDER TOTAL, which includes shipping and fees — so on an
110
+ * order that is mostly shipping it can legitimately exceed the goods value,
111
+ * and the subtraction can go negative. `roundPoints` of a negative is a
112
+ * DEBIT, so without the clamp a buyer completing such an order would have
113
+ * points taken away by the reward programme.
114
+ */
93
115
  const goods = orderTaxableValue(order);
116
+ const moneyPaidForGoods = Math.max(0, goods - Math.max(0, order.pointsApplied ?? 0));
94
117
  /*
95
- * `goods * (PERCENT / 100)`, not `(goods * PERCENT) / 100` — the same value,
96
- * written the way every fee in `fees/calculator.ts` is
118
+ * `x * (PERCENT / 100)`, not `(x * PERCENT) / 100` — the same value, written
119
+ * the way every fee in `fees/calculator.ts` is
97
120
  * (`subtotal * (commissions.platformFeePercent / 100)`). The division
98
121
  * belongs to the PERCENT, and grouping it that way is what distinguishes a
99
122
  * percentage from the subunit-conversion `/100` that `audit-money-units`
100
123
  * exists to catch — which read the old grouping as exactly that.
124
+ *
125
+ * 🛑 Merge resolution, 2026-10-22: both sides of this conflict were real and
126
+ * independent, so the answer is BOTH, not either.
127
+ *
128
+ * - the BASE is `moneyPaidForGoods`, not `goods` — points must not be
129
+ * issued against the part of the order the buyer settled with points,
130
+ * which is what the whole docstring above this line exists to explain.
131
+ * Taking only the other side would have silently restored
132
+ * points-on-points issuance AND left that docstring describing a
133
+ * variable that no longer existed.
134
+ * - the GROUPING is the other side's, because the audit reasoning is
135
+ * correct and applies to this expression whatever its base.
101
136
  */
102
- const amount = roundPoints(goods * (POINTS_EARN_PERCENT / 100));
137
+ const amount = roundPoints(moneyPaidForGoods * (POINTS_EARN_PERCENT / 100));
103
138
  if (amount <= 0) {
104
139
  /*
105
140
  * A fully-cancelled or zero-value order. Marked as awarded anyway below so
@@ -125,7 +160,14 @@ export async function awardPurchasePoints(order) {
125
160
  * later — and written once rather than re-derived, so it stays true of this
126
161
  * moment even if the order is edited afterwards.
127
162
  */
128
- description: `${POINTS_EARN_PERCENT}% back on ${order.id}: ₹${goods.toFixed(2)} of items.`,
163
+ description: `${POINTS_EARN_PERCENT}% back on ${order.id}: ₹${moneyPaidForGoods.toFixed(2)} of items` +
164
+ // Named explicitly, because an unexplained gap between the order's item
165
+ // value and the earn base is exactly the kind of figure a buyer writes
166
+ // in to ask about — and the answer ("you paid part of it with points")
167
+ // is a fact about their own order that they can check.
168
+ ((order.pointsApplied ?? 0) > 0
169
+ ? ` (₹${goods.toFixed(2)} of items less ${order.pointsApplied} points).`
170
+ : "."),
129
171
  expiresAt: pointsExpiryFrom(now),
130
172
  });
131
173
  /*
@@ -45,6 +45,8 @@ import { serverLogger } from "../../../../monitoring/index.js";
45
45
  import { isFinalSaleExempt, RETURN_REASON_LABEL, } from "../../../shared/features/orders/return-reasons.js";
46
46
  import { REFUND_COPY } from "../../../shared/features/orders/refund-copy.js";
47
47
  import { applyRefundDeductionAction } from "../payouts/actions.js";
48
+ /* Phase 7c — the credit note. Swallows its own failures by design: see its 🛑. */
49
+ import { issueCreditNoteForRefund } from "../invoicing/credit-note.js";
48
50
  import { getAdminDb } from "../../../../providers/db-firebase/index.js";
49
51
  import { PRODUCT_COLLECTION, PRODUCT_CODES_SUBCOLLECTION, } from "../../../../features/products/schemas/firestore.js";
50
52
  /**
@@ -362,6 +364,30 @@ export async function processRefundAction(input) {
362
364
  // the reconciler will look at it once and find it already done.
363
365
  serverLogger.warn("refund: claim not marked settled", { refundId });
364
366
  });
367
+ /*
368
+ * ── PHASE 7c: the credit note that documents this reversal ────────────
369
+ *
370
+ * 🛑 HERE, at settlement, and not at reservation. A credit note numbered
371
+ * when the entry was reserved would leave a numbered reversal in the
372
+ * series on every provider failure — for money that never moved, with a
373
+ * number that cannot be withdrawn.
374
+ *
375
+ * 🛑 AWAITED but never allowed to throw (the issuer swallows its own
376
+ * failures and says so). The money has already left the gateway by this
377
+ * line; raising here would report a successful refund as failed, and an
378
+ * operator would retry a refund that already happened. A missing credit
379
+ * note is a reporting gap a backfill can close; a double refund is not.
380
+ *
381
+ * No renderer is passed, so this NUMBERS the reversal without producing
382
+ * a PDF. That is what the GSTR-1 export needs — it reconciles numbers
383
+ * and amounts. The buyer's PDF follows the invoice's own lazy pattern
384
+ * when a surface asks for it.
385
+ */
386
+ const settledOrder = await orderRepository.findById(input.orderId);
387
+ const settledEvent = settledOrder?.refunds?.find((r) => r.refundId === refundId);
388
+ if (settledOrder && settledEvent) {
389
+ await issueCreditNoteForRefund(settledOrder, settledEvent);
390
+ }
365
391
  // SB-UNI-N — revoke any available (unclaimed) digital codes linked to this order.
366
392
  revokeAvailableDigitalCodes(input.orderId, digitalCodeItems);
367
393
  /*
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Raising and clearing a courier block, and telling the ADMIN about it.
3
+ *
4
+ * ## The problem this exists for
5
+ *
6
+ * Shiprocket is PREPAID and has no recharge API, so topping up the courier
7
+ * wallet is irreducibly a human action. If the platform books couriers, an
8
+ * empty wallet silently stops dispatch on orders buyers have already paid for.
9
+ *
10
+ * ## The two rules
11
+ *
12
+ * 1. 🛑 **The order is never auto-cancelled over it.** `seller-dispatch`'s
13
+ * `precondition` carries `!hasOpenCourierBlock(o)`, so its 2-business-day
14
+ * `onDue: "act"` clock PAUSES while blocked instead of cancelling the order
15
+ * and refunding the buyer with the seller blamed for our funding state.
16
+ *
17
+ * 2. 🛑 **The admin alert is deduped on the WALLET, never on the order.** Fifty
18
+ * blocked orders are ONE problem with one fix. The daily email ceiling
19
+ * applies to staff mail too (`send-guard.ts:92` — the kill switch spares
20
+ * staff, the ceiling does not), so fifty per-order emails would spend the
21
+ * whole 80/day budget saying the same thing fifty times and leave the
22
+ * operator blind to everything else.
23
+ */
24
+ import type { OrderDocument } from "../../../../features/orders/schemas/firestore";
25
+ import type { WalletBalance } from "../../../../contracts";
26
+ /**
27
+ * ## 🛑 What is HERE and what deliberately is not
28
+ *
29
+ * Shipped: the wallet THRESHOLD check and the whole admin-alert path, driven by
30
+ * the daily digest. That is a real producer today and needs nothing new — and
31
+ * it is the better signal anyway: reacting only to failures means the first
32
+ * warning is always a buyer's order already stuck.
33
+ *
34
+ * NOT shipped: raising a block on a specific order. That belongs with the
35
+ * booking path, and nothing platform-books yet (`createShipment` has zero
36
+ * callers). A `raiseCourierBlock` with no producer is the speculative-infra
37
+ * pattern, so it lands when the caller does.
38
+ *
39
+ * `OrderDocument.courierBlock` and `seller-dispatch`'s
40
+ * `!hasOpenCourierBlock(o)` guard ARE already in place, and that ordering is
41
+ * deliberate: a safety property must exist BEFORE the thing it guards against,
42
+ * or the very first block auto-cancels a paid order. Inert today, correct the
43
+ * moment anything writes the field.
44
+ */
45
+ /** Why we could not book. Mirrors `OrderDocument.courierBlock.reason`. */
46
+ export type CourierBlockReason = NonNullable<OrderDocument["courierBlock"]>["reason"];
47
+ export interface CourierWalletCheck {
48
+ /** `null` when the provider has no wallet, or we could not read it. */
49
+ balance: number | null;
50
+ low: boolean;
51
+ alerted: boolean;
52
+ reason?: string;
53
+ }
54
+ /**
55
+ * Read the courier wallet and, if it is running low, alert the admin ONCE.
56
+ *
57
+ * Called from `dailyStatusDigest` — 🛑 deliberately NOT a new scheduled
58
+ * function. Cloud Scheduler bills per registered job and this is one read a
59
+ * day; the same reasoning that put the ledger reconciler inside
60
+ * `dailyDataCleanup`.
61
+ *
62
+ * Never throws: a wallet read is diagnostics, and a digest that fails because
63
+ * one provider was unreachable stops reporting the fifteen things that were
64
+ * fine.
65
+ */
66
+ export declare function checkCourierWalletAndAlert(provider: {
67
+ name: string;
68
+ getWalletBalance: () => Promise<WalletBalance>;
69
+ } | undefined): Promise<CourierWalletCheck>;