@mohasinac/appkit 4.62.0 → 4.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
- package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
- package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
- package/dist/_internal/server/features/ads/booking.d.ts +118 -0
- package/dist/_internal/server/features/ads/booking.js +326 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +129 -9
- package/dist/_internal/server/features/checkout/actions.js +850 -79
- package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
- package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
- package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
- package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
- package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
- package/dist/_internal/server/features/dashboard/attention.js +183 -0
- package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
- package/dist/_internal/server/features/dashboard/data.js +82 -0
- package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
- package/dist/_internal/server/features/inventory/reservations.js +273 -0
- package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
- package/dist/_internal/server/features/invoicing/allocate.js +162 -0
- package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
- package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
- package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
- package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
- package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
- package/dist/_internal/server/features/invoicing/issue.js +202 -0
- package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -0
- package/dist/_internal/server/features/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +54 -0
- package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
- package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
- package/dist/_internal/server/features/orders/balance.d.ts +89 -0
- package/dist/_internal/server/features/orders/balance.js +256 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
- package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
- package/dist/_internal/server/features/points/award.js +46 -4
- package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -0
- package/dist/_internal/server/features/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
- package/dist/_internal/server/features/shipping/book-split.js +130 -0
- package/dist/_internal/server/features/shipping/book.d.ts +61 -0
- package/dist/_internal/server/features/shipping/book.js +176 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +264 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +29 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +65 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +95 -2
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
- package/dist/_internal/server/jobs/core/index.d.ts +2 -1
- package/dist/_internal/server/jobs/core/index.js +2 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
- package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
- package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +296 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
- package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
- package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
- package/dist/_internal/shared/checkout/order-math.js +159 -0
- package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
- package/dist/_internal/shared/checkout/refundable-base.js +13 -1
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
- package/dist/_internal/shared/checkout/split.d.ts +75 -0
- package/dist/_internal/shared/checkout/split.js +120 -0
- package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
- package/dist/_internal/shared/content/disclaimers.js +217 -0
- package/dist/_internal/shared/content/published-values.d.ts +93 -0
- package/dist/_internal/shared/content/published-values.js +206 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
- package/dist/_internal/shared/features/admin/integrations.js +263 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
- package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
- package/dist/_internal/shared/features/emi/choice.js +69 -0
- package/dist/_internal/shared/features/emi/group.d.ts +84 -0
- package/dist/_internal/shared/features/emi/group.js +126 -0
- package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
- package/dist/_internal/shared/features/ledger/posting.js +165 -0
- package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
- package/dist/_internal/shared/features/ledger/reports.js +127 -0
- package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
- package/dist/_internal/shared/features/ledger/rules.js +221 -0
- package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
- package/dist/_internal/shared/features/offers/coordination.js +80 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
- package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
- package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
- package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
- package/dist/_internal/shared/features/points/accepts.js +49 -0
- package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
- package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
- package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
- package/dist/_internal/shared/features/shipping/parcel.js +139 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
- package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/fees/calculator.d.ts +33 -1
- package/dist/_internal/shared/fees/calculator.js +32 -2
- package/dist/_internal/shared/fees/explain.d.ts +81 -0
- package/dist/_internal/shared/fees/explain.js +101 -0
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/index.js +1 -1
- package/dist/_internal/shared/history/types.d.ts +7 -1
- package/dist/_internal/shared/history/types.js +25 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.js +43 -0
- package/dist/constants/api-endpoints.d.ts +114 -0
- package/dist/constants/api-endpoints.js +55 -0
- package/dist/constants/field-names.d.ts +42 -0
- package/dist/constants/field-names.js +52 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/shipping.d.ts +45 -0
- package/dist/contracts/shipping.js +15 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOrdersView.js +47 -35
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +86 -5
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
- package/dist/features/admin/constants/filter-tabs.js +6 -0
- package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminListingData.js +16 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +62 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/ads/schemas/booking-write.d.ts +120 -0
- package/dist/features/ads/schemas/booking-write.js +75 -0
- package/dist/features/ads/schemas/creative-form.d.ts +18 -0
- package/dist/features/ads/schemas/creative-form.js +51 -0
- package/dist/features/ads/schemas/firestore.d.ts +140 -0
- package/dist/features/ads/schemas/firestore.js +109 -0
- package/dist/features/auctions/repository/bid.repository.js +3 -1
- package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
- package/dist/features/audit-log/schemas/firestore.js +89 -0
- package/dist/features/auth/actions/profile-actions.d.ts +69 -1
- package/dist/features/auth/actions/profile-actions.js +62 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/auth/schemas/firestore.d.ts +16 -2
- package/dist/features/auth/schemas/firestore.js +2 -1
- package/dist/features/blog/actions/blog-actions.d.ts +6 -0
- package/dist/features/blog/actions/blog-actions.js +17 -2
- package/dist/features/blog/api/route.js +4 -1
- package/dist/features/blog/columns/index.js +5 -0
- package/dist/features/blog/components/BlogIndexPageView.js +2 -1
- package/dist/features/blog/repository/blog.repository.d.ts +4 -0
- package/dist/features/blog/repository/blog.repository.js +5 -0
- package/dist/features/blog/schemas/blog-form.d.ts +3 -0
- package/dist/features/blog/schemas/blog-form.js +1 -0
- package/dist/features/blog/schemas/firestore.d.ts +38 -1
- package/dist/features/blog/schemas/firestore.js +23 -0
- package/dist/features/blog/schemas/index.d.ts +6 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
- package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
- package/dist/features/cart/components/PointsTenderPicker.js +15 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
- package/dist/features/cart/components/index.d.ts +2 -0
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +33 -0
- package/dist/features/cart/repository/cart.repository.js +47 -0
- package/dist/features/cart/schemas/firestore.d.ts +116 -0
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
- package/dist/features/dashboard/components/DashboardHubView.js +56 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- package/dist/features/faq/components/FAQPageContent.js +2 -2
- package/dist/features/gateway-events/schemas/index.d.ts +2 -2
- package/dist/features/homepage/schemas/index.d.ts +8 -8
- package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
- package/dist/features/inventory/schemas/stock-reservation.js +103 -0
- package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
- package/dist/features/invoicing/schemas/firestore.js +45 -0
- package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
- package/dist/features/invoicing/utils/financial-year.js +53 -0
- package/dist/features/jobs/actions/enqueue-job.js +18 -1
- package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
- package/dist/features/jobs/repository/jobs.repository.js +84 -1
- package/dist/features/jobs/schemas/firestore.d.ts +80 -0
- package/dist/features/jobs/schemas/firestore.js +37 -0
- package/dist/features/layout/AppLayoutShell.d.ts +9 -1
- package/dist/features/layout/AppLayoutShell.js +12 -4
- package/dist/features/layout/BottomActions.js +2 -1
- package/dist/features/layout/BottomNavbar.d.ts +10 -2
- package/dist/features/layout/SideActionDock.js +52 -2
- package/dist/features/layout/TitleBarLayout.js +13 -4
- package/dist/features/layout/TitleBarLayout.style.css +59 -0
- package/dist/features/layout/useCtaReveal.d.ts +16 -1
- package/dist/features/layout/useCtaReveal.js +17 -26
- package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
- package/dist/features/ledger/repository/journal.repository.js +312 -0
- package/dist/features/ledger/schemas/expenses.d.ts +87 -0
- package/dist/features/ledger/schemas/expenses.js +27 -0
- package/dist/features/ledger/schemas/firestore.d.ts +183 -0
- package/dist/features/ledger/schemas/firestore.js +69 -0
- package/dist/features/lottery/schemas/zod.d.ts +6 -6
- package/dist/features/media/MediaImage.js +22 -1
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/obligations/constants/policies.js +24 -1
- package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
- package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
- package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
- package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
- package/dist/features/orders/components/RefundRequestView.js +19 -1
- package/dist/features/orders/components/index.d.ts +4 -0
- package/dist/features/orders/components/index.js +2 -0
- package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
- package/dist/features/orders/constants/order-lane-tabs.js +88 -0
- package/dist/features/orders/repository/orders.repository.d.ts +85 -1
- package/dist/features/orders/repository/orders.repository.js +205 -6
- package/dist/features/orders/schemas/balance.d.ts +40 -0
- package/dist/features/orders/schemas/balance.js +47 -0
- package/dist/features/orders/schemas/firestore.d.ts +398 -1
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +56 -8
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +47 -0
- package/dist/features/orders/utils/courier-block.d.ts +25 -0
- package/dist/features/orders/utils/courier-block.js +23 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
- package/dist/features/payment-cases/schemas/firestore.js +1 -0
- package/dist/features/payments/repository/payout.repository.js +3 -2
- package/dist/features/payments/schemas/firestore.d.ts +92 -1
- package/dist/features/payments/schemas/firestore.js +23 -0
- package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
- package/dist/features/pricing/components/AdminPricingView.js +157 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/schemas/firestore.d.ts +26 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
- package/dist/features/promotions/actions/coupon-stacking.js +21 -2
- package/dist/features/promotions/components/CouponCard.js +13 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
- package/dist/features/promotions/constants/coupon-help.js +9 -1
- package/dist/features/promotions/schemas/firestore.d.ts +24 -0
- package/dist/features/promotions/schemas/firestore.js +4 -0
- package/dist/features/promotions/types/index.d.ts +19 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +2 -1
- package/dist/features/scams/schemas/firestore.js +2 -1
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/offer-actions.js +56 -0
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +37 -3
- package/dist/features/seller/components/DispatchForm.d.ts +11 -0
- package/dist/features/seller/components/DispatchForm.js +190 -0
- package/dist/features/seller/components/SellerOrdersView.js +221 -27
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/seller/repository/offer.repository.js +4 -3
- package/dist/features/seller/schemas/firestore.d.ts +23 -2
- package/dist/features/seller/schemas/firestore.js +3 -2
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +61 -4
- package/dist/features/seller/schemas/order-forms.js +41 -8
- package/dist/features/shipments/schemas/firestore.d.ts +25 -0
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
- package/dist/features/stores/repository/store.repository.d.ts +18 -0
- package/dist/features/stores/repository/store.repository.js +27 -1
- package/dist/features/stores/schemas/firestore.d.ts +112 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/repository/support.repository.js +72 -6
- package/dist/features/support/schemas/firestore.d.ts +17 -0
- package/dist/features/support/schemas/index.d.ts +8 -8
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +33 -1
- package/dist/next/routing/route-map.d.ts +112 -0
- package/dist/next/routing/route-map.js +63 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +69 -34
- package/dist/schemas/sieve.d.ts +2 -2
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +36 -1
- package/dist/server.js +183 -1
- package/dist/styles.css +412 -80
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -0
- package/dist/tokens/themes/default-light.js +1 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +1 -0
- package/dist/tokens/tokens.css +15 -0
- package/dist/ui/components/Button.d.ts +10 -1
- package/dist/ui/components/Button.js +25 -3
- package/dist/ui/components/Chart.d.ts +63 -0
- package/dist/ui/components/Chart.js +169 -0
- package/dist/ui/components/Chart.style.css +214 -0
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +83 -0
- package/dist/ui/components/OverflowRow.js +54 -2
- package/dist/ui/components/OverflowRow.style.css +38 -0
- package/dist/ui/components/RichTextEditor.js +99 -8
- package/dist/ui/components/chart-spec.d.ts +68 -0
- package/dist/ui/components/chart-spec.js +140 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/surface-tokens.d.ts +1 -1
- package/dist/ui/components/surface-tokens.js +9 -0
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/components/toolbar-collapse.d.ts +13 -0
- package/dist/ui/components/toolbar-collapse.js +10 -1
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/rich-text/RichText.d.ts +10 -1
- package/dist/ui/rich-text/RichText.js +33 -2
- package/dist/ui/rich-text/RichText.style.css +83 -79
- package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
- package/dist/ui/rich-text/RichTextRenderer.js +34 -2
- package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
- package/dist/ui/rich-text/chart-blocks.js +87 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1834 -1822
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correcting a delivery address after the order was placed.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 This APPENDS. It never touches the snapshot.
|
|
5
|
+
*
|
|
6
|
+
* `delivery*` on the order is what the buyer chose when they paid — it is
|
|
7
|
+
* what the invoice states and what a dispute argues over. A correction is a
|
|
8
|
+
* new fact about where the parcel should go, not a revision of what was
|
|
9
|
+
* agreed, and collapsing the two is how an invoice silently restates itself.
|
|
10
|
+
*
|
|
11
|
+
* `deliveryPartyFor` reads the latest amendment; `placeOfSupplyFrom` reads
|
|
12
|
+
* the snapshot. Tax is fixed at the time of supply.
|
|
13
|
+
*
|
|
14
|
+
* ## 🛑 There is a window, and it closes at dispatch
|
|
15
|
+
*
|
|
16
|
+
* Once a tracking id exists the parcel is with the courier and a new address
|
|
17
|
+
* changes nothing we control — accepting one would tell the buyer their
|
|
18
|
+
* correction took effect when it did not. That is a worse outcome than
|
|
19
|
+
* refusing, which at least routes them to support.
|
|
20
|
+
*/
|
|
21
|
+
import { type DeliveryAmendment, type OrderDocument } from "../../../../features/orders/schemas/firestore";
|
|
22
|
+
import { type HistoryActor } from "../../../shared/history";
|
|
23
|
+
/**
|
|
24
|
+
* How many corrections one order may carry.
|
|
25
|
+
*
|
|
26
|
+
* 🛑 Bounded for the same reason `statusHistory` is: this array rides on the
|
|
27
|
+
* order document, which has a 1 MiB ceiling and is read by every list query.
|
|
28
|
+
* Unlike `statusHistory` it does NOT evict — reaching the cap is refused,
|
|
29
|
+
* because dropping the oldest amendment would silently discard an address a
|
|
30
|
+
* parcel may already have gone to.
|
|
31
|
+
*
|
|
32
|
+
* Ten is far past any honest use. A buyer correcting an address eleven times
|
|
33
|
+
* is a support conversation, not a form submission.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DELIVERY_AMENDMENT_MAX = 10;
|
|
36
|
+
export interface AmendDeliveryInput {
|
|
37
|
+
orderId: string;
|
|
38
|
+
/** An `addresses` row belonging to the actor. Never a loose address. */
|
|
39
|
+
addressId: string;
|
|
40
|
+
reason?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 🛑 The new address is a REFERENCE the caller must own, resolved here — not
|
|
44
|
+
* a free-form address object.
|
|
45
|
+
*
|
|
46
|
+
* Accepting fields directly would let a correction route a parcel anywhere
|
|
47
|
+
* with no record that the destination was ever the buyer's, which is the
|
|
48
|
+
* same hole `assertOwnedAddresses` closes for coordinated purchases. The
|
|
49
|
+
* resolved values are then COPIED into the amendment, because the row can be
|
|
50
|
+
* edited or deleted afterwards and a label must not depend on that.
|
|
51
|
+
*/
|
|
52
|
+
export declare function amendDeliveryAddress(input: AmendDeliveryInput, actor: HistoryActor & {
|
|
53
|
+
uid: string;
|
|
54
|
+
}): Promise<DeliveryAmendment>;
|
|
55
|
+
/** Plaintext amendments, for a surface that renders them. */
|
|
56
|
+
export declare function revealAmendments(order: OrderDocument): DeliveryAmendment[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correcting a delivery address after the order was placed.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 This APPENDS. It never touches the snapshot.
|
|
5
|
+
*
|
|
6
|
+
* `delivery*` on the order is what the buyer chose when they paid — it is
|
|
7
|
+
* what the invoice states and what a dispute argues over. A correction is a
|
|
8
|
+
* new fact about where the parcel should go, not a revision of what was
|
|
9
|
+
* agreed, and collapsing the two is how an invoice silently restates itself.
|
|
10
|
+
*
|
|
11
|
+
* `deliveryPartyFor` reads the latest amendment; `placeOfSupplyFrom` reads
|
|
12
|
+
* the snapshot. Tax is fixed at the time of supply.
|
|
13
|
+
*
|
|
14
|
+
* ## 🛑 There is a window, and it closes at dispatch
|
|
15
|
+
*
|
|
16
|
+
* Once a tracking id exists the parcel is with the courier and a new address
|
|
17
|
+
* changes nothing we control — accepting one would tell the buyer their
|
|
18
|
+
* correction took effect when it did not. That is a worse outcome than
|
|
19
|
+
* refusing, which at least routes them to support.
|
|
20
|
+
*/
|
|
21
|
+
import { addressesRepository } from "../../../../features/addresses/repository/addresses.repository.js";
|
|
22
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
23
|
+
import { AMENDMENT_PII_FIELDS, } from "../../../../features/orders/schemas/firestore.js";
|
|
24
|
+
import { revealPii } from "../../../../security/pii-accessors.js";
|
|
25
|
+
import { ValidationError } from "../../../../errors/validation-error.js";
|
|
26
|
+
import { NotFoundError } from "../../../../errors/not-found-error.js";
|
|
27
|
+
import { HISTORY_ACTOR_ROLE } from "../../../shared/history/index.js";
|
|
28
|
+
/**
|
|
29
|
+
* How many corrections one order may carry.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 Bounded for the same reason `statusHistory` is: this array rides on the
|
|
32
|
+
* order document, which has a 1 MiB ceiling and is read by every list query.
|
|
33
|
+
* Unlike `statusHistory` it does NOT evict — reaching the cap is refused,
|
|
34
|
+
* because dropping the oldest amendment would silently discard an address a
|
|
35
|
+
* parcel may already have gone to.
|
|
36
|
+
*
|
|
37
|
+
* Ten is far past any honest use. A buyer correcting an address eleven times
|
|
38
|
+
* is a support conversation, not a form submission.
|
|
39
|
+
*/
|
|
40
|
+
export const DELIVERY_AMENDMENT_MAX = 10;
|
|
41
|
+
/**
|
|
42
|
+
* 🛑 The new address is a REFERENCE the caller must own, resolved here — not
|
|
43
|
+
* a free-form address object.
|
|
44
|
+
*
|
|
45
|
+
* Accepting fields directly would let a correction route a parcel anywhere
|
|
46
|
+
* with no record that the destination was ever the buyer's, which is the
|
|
47
|
+
* same hole `assertOwnedAddresses` closes for coordinated purchases. The
|
|
48
|
+
* resolved values are then COPIED into the amendment, because the row can be
|
|
49
|
+
* edited or deleted afterwards and a label must not depend on that.
|
|
50
|
+
*/
|
|
51
|
+
export async function amendDeliveryAddress(input, actor) {
|
|
52
|
+
const order = (await orderRepository.findById(input.orderId));
|
|
53
|
+
if (!order)
|
|
54
|
+
throw new NotFoundError("Order not found.");
|
|
55
|
+
/*
|
|
56
|
+
* Ownership. A seller or admin may correct on a buyer's behalf — that is
|
|
57
|
+
* the support flow — but a BUYER may only touch their own order.
|
|
58
|
+
*/
|
|
59
|
+
if (actor.role === HISTORY_ACTOR_ROLE.BUYER && order.userId !== actor.uid) {
|
|
60
|
+
throw new NotFoundError("Order not found.");
|
|
61
|
+
}
|
|
62
|
+
if (order.trackingNumber) {
|
|
63
|
+
throw new ValidationError("This order has already been dispatched, so its address can no longer be changed.", { addressId: "Already dispatched — contact support." });
|
|
64
|
+
}
|
|
65
|
+
const existing = order.deliveryAmendments ?? [];
|
|
66
|
+
if (existing.length >= DELIVERY_AMENDMENT_MAX) {
|
|
67
|
+
throw new ValidationError("This order has been corrected too many times. Please contact support.", { addressId: `Limit of ${DELIVERY_AMENDMENT_MAX} corrections reached.` });
|
|
68
|
+
}
|
|
69
|
+
const addressDoc = await addressesRepository.findById(input.addressId);
|
|
70
|
+
/*
|
|
71
|
+
* Owned by the ORDER'S BUYER, not by the actor. A support agent correcting
|
|
72
|
+
* on someone's behalf must still pick from that buyer's own addresses —
|
|
73
|
+
* checking the actor would let staff route a parcel to their own address.
|
|
74
|
+
*/
|
|
75
|
+
if (!addressDoc || addressDoc.ownerType !== "user" || addressDoc.ownerId !== order.userId) {
|
|
76
|
+
throw new ValidationError("That delivery address is not available.", {
|
|
77
|
+
addressId: "That address cannot be used.",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const address = addressesRepository.revealAddress(addressDoc);
|
|
81
|
+
const amendment = {
|
|
82
|
+
at: new Date(),
|
|
83
|
+
actorUid: actor.uid,
|
|
84
|
+
actorRole: actor.role,
|
|
85
|
+
...(input.reason?.trim() ? { reason: input.reason.trim() } : {}),
|
|
86
|
+
fullName: String(address.fullName ?? ""),
|
|
87
|
+
phone: String(address.phone ?? ""),
|
|
88
|
+
addressLine1: String(address.addressLine1 ?? ""),
|
|
89
|
+
...(address.addressLine2 ? { addressLine2: String(address.addressLine2) } : {}),
|
|
90
|
+
...(address.landmark ? { landmark: String(address.landmark) } : {}),
|
|
91
|
+
city: address.city ?? "",
|
|
92
|
+
state: address.state ?? "",
|
|
93
|
+
pincode: address.postalCode ?? "",
|
|
94
|
+
country: address.country ?? "",
|
|
95
|
+
};
|
|
96
|
+
/*
|
|
97
|
+
* 🛑 NOT encrypted here. `ORDER_PII_FIELDS` declares
|
|
98
|
+
* `deliveryAmendments[].fullName` and friends, and the repository's write
|
|
99
|
+
* hook runs that list through `encryptInArray` — which the encryptor has
|
|
100
|
+
* always supported.
|
|
101
|
+
*
|
|
102
|
+
* The first draft of this file encrypted by hand, on the belief that
|
|
103
|
+
* `encryptPiiFields` "is a flat top-level loop that never descends". That
|
|
104
|
+
* is CLAUDE.md's wording and it is false: the function handles `a.b` and
|
|
105
|
+
* `a[].b` in both directions, and `CONVERSATION_PII_FIELDS` has been using
|
|
106
|
+
* the dotted form in production. Encrypting here as well would double-
|
|
107
|
+
* encrypt — the guard is a `enc:v1:` prefix test, so the second pass is a
|
|
108
|
+
* no-op today and a silent corruption the day the prefix changes.
|
|
109
|
+
*/
|
|
110
|
+
/*
|
|
111
|
+
* 🛑 Whole array, not `arrayUnion`. `arrayUnion` cannot enforce the cap —
|
|
112
|
+
* the same reason `withHistory`'s batch paths fold in memory — and it
|
|
113
|
+
* de-duplicates by value, so two identical corrections would collapse into
|
|
114
|
+
* one and the record would under-state what happened.
|
|
115
|
+
*/
|
|
116
|
+
await orderRepository.update(input.orderId, {
|
|
117
|
+
deliveryAmendments: [...existing, amendment],
|
|
118
|
+
});
|
|
119
|
+
return amendment;
|
|
120
|
+
}
|
|
121
|
+
/** Plaintext amendments, for a surface that renders them. */
|
|
122
|
+
export function revealAmendments(order) {
|
|
123
|
+
return (order.deliveryAmendments ?? []).map((a) => revealPii(a, AMENDMENT_PII_FIELDS));
|
|
124
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D55 — collecting a pre-order balance.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 `balanceDue` has been computed and rendered since Part 2, and
|
|
5
|
+
* nothing has ever collected it
|
|
6
|
+
*
|
|
7
|
+
* `OrderSourcePreOrder.balanceDue` is written at order creation and
|
|
8
|
+
* `OrderProvenanceCard` prints it — so `/user/pre-orders` has been
|
|
9
|
+
* advertising a figure the product has no way to accept. That is worse than
|
|
10
|
+
* omitting it: a buyer who sees "Balance due ₹4,000" and finds no control
|
|
11
|
+
* reasonably concludes the site is broken, and a seller waiting on it has no
|
|
12
|
+
* mechanism to chase.
|
|
13
|
+
*
|
|
14
|
+
* ## Manual only, and that is a decision rather than a stopping point
|
|
15
|
+
*
|
|
16
|
+
* The manual path needs no mandate, no scheduled debit and no account
|
|
17
|
+
* change, so it is what makes the balance payable AT ALL today. The auto
|
|
18
|
+
* path is blocked on RBI requirements and on Razorpay recurring being
|
|
19
|
+
* enabled against live keys — see the schema's own note, which declares the
|
|
20
|
+
* union so adding `auto` is a compile-checked change.
|
|
21
|
+
*
|
|
22
|
+
* ## 🛑 It reuses the manual-payment REVIEW flow and none of its STORAGE
|
|
23
|
+
*
|
|
24
|
+
* The vocabulary is the same (submit a proof, an admin approves or rejects
|
|
25
|
+
* with a note) and the fields are separate, because a pre-order has two
|
|
26
|
+
* payments months apart. Writing the balance's proof over the deposit's
|
|
27
|
+
* would destroy the evidence for the payment that created the order — and
|
|
28
|
+
* `paymentWindowTimeout`, which sweeps `pending`/`pending` orders with no
|
|
29
|
+
* `paymentProofUrl`, would then cancel and restock a paid one.
|
|
30
|
+
*/
|
|
31
|
+
import type { OrderDocument } from "../../../../features/orders/schemas/firestore";
|
|
32
|
+
/**
|
|
33
|
+
* How long a buyer gets, once the balance is opened for collection.
|
|
34
|
+
*
|
|
35
|
+
* 🛑 Days, not the 15 MINUTES the deposit window uses. That window exists to
|
|
36
|
+
* stop an unpaid order holding stock; this one is a bill falling due on goods
|
|
37
|
+
* that have arrived, and the buyer may legitimately need to move money. A
|
|
38
|
+
* 15-minute balance window would cancel real orders for no benefit.
|
|
39
|
+
*/
|
|
40
|
+
export declare const BALANCE_WINDOW_DAYS = 7;
|
|
41
|
+
/** The balance a pre-order still owes, or `null` when it is not one. */
|
|
42
|
+
export declare function balanceDueOn(order: OrderDocument): number | null;
|
|
43
|
+
/**
|
|
44
|
+
* Open the balance for collection — the seller's "the goods have landed"
|
|
45
|
+
* signal.
|
|
46
|
+
*
|
|
47
|
+
* 🛑 **Opening is an EVENT, not a derived state.** The balance could be
|
|
48
|
+
* computed as payable the moment the order exists, and that would be wrong:
|
|
49
|
+
* a pre-order's whole premise is that the goods do not exist yet, so asking
|
|
50
|
+
* for the balance before they do is asking a buyer to fund inventory. The
|
|
51
|
+
* seller opens it when stock arrives, and `dueBy` runs from that moment.
|
|
52
|
+
*
|
|
53
|
+
* 🛑 Idempotent. A seller clicking twice must not restart the buyer's
|
|
54
|
+
* window — which, since `dueBy` is what a reminder and any future
|
|
55
|
+
* auto-cancel would read, would silently extend the deadline every time
|
|
56
|
+
* anyone touched the button.
|
|
57
|
+
*
|
|
58
|
+
* 🛑 **Ownership is checked HERE, and it has to be**: the route that calls
|
|
59
|
+
* this is role-gated to sellers, and every seller passes that gate. Without
|
|
60
|
+
* this block any seller could open — and therefore start the payment clock
|
|
61
|
+
* on, and notify the buyer about — another seller's pre-order.
|
|
62
|
+
*
|
|
63
|
+
* It was missing, and the reason it was invisible is worth recording: the
|
|
64
|
+
* route also carried `permission: "orders:write"` alongside a `roles` array
|
|
65
|
+
* holding `seller`, which `getServerPermissions()` resolves for `employee`
|
|
66
|
+
* only — so every seller got a 403 before reaching this function at all. A
|
|
67
|
+
* broken gate masked an absent one, exactly as in Root Cause #98.
|
|
68
|
+
*/
|
|
69
|
+
export declare function openBalanceCollection(orderId: string, actor: {
|
|
70
|
+
uid: string;
|
|
71
|
+
role: string;
|
|
72
|
+
}, now?: Date): Promise<OrderDocument>;
|
|
73
|
+
/**
|
|
74
|
+
* The buyer submits a proof.
|
|
75
|
+
*
|
|
76
|
+
* 🛑 Allowed from `awaiting` AND from `rejected`. A rejected proof is the
|
|
77
|
+
* commonest reason to need a second attempt — a blurred screenshot, the
|
|
78
|
+
* wrong reference — and locking the buyer out after one rejection turns a
|
|
79
|
+
* recoverable mistake into a support ticket.
|
|
80
|
+
*/
|
|
81
|
+
export declare function submitBalanceProof(orderId: string, buyerUid: string, proofUrl: string, transactionId: string, now?: Date): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* An admin approves or rejects the balance proof.
|
|
84
|
+
*
|
|
85
|
+
* 🛑 A rejection REQUIRES a note, for the same reason a reversal does: the
|
|
86
|
+
* buyer has to know what to send instead, and "rejected" alone guarantees
|
|
87
|
+
* the next attempt is the same proof again.
|
|
88
|
+
*/
|
|
89
|
+
export declare function reviewBalanceProof(orderId: string, reviewerUid: string, approve: boolean, note?: string, now?: Date): Promise<void>;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D55 — collecting a pre-order balance.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 `balanceDue` has been computed and rendered since Part 2, and
|
|
5
|
+
* nothing has ever collected it
|
|
6
|
+
*
|
|
7
|
+
* `OrderSourcePreOrder.balanceDue` is written at order creation and
|
|
8
|
+
* `OrderProvenanceCard` prints it — so `/user/pre-orders` has been
|
|
9
|
+
* advertising a figure the product has no way to accept. That is worse than
|
|
10
|
+
* omitting it: a buyer who sees "Balance due ₹4,000" and finds no control
|
|
11
|
+
* reasonably concludes the site is broken, and a seller waiting on it has no
|
|
12
|
+
* mechanism to chase.
|
|
13
|
+
*
|
|
14
|
+
* ## Manual only, and that is a decision rather than a stopping point
|
|
15
|
+
*
|
|
16
|
+
* The manual path needs no mandate, no scheduled debit and no account
|
|
17
|
+
* change, so it is what makes the balance payable AT ALL today. The auto
|
|
18
|
+
* path is blocked on RBI requirements and on Razorpay recurring being
|
|
19
|
+
* enabled against live keys — see the schema's own note, which declares the
|
|
20
|
+
* union so adding `auto` is a compile-checked change.
|
|
21
|
+
*
|
|
22
|
+
* ## 🛑 It reuses the manual-payment REVIEW flow and none of its STORAGE
|
|
23
|
+
*
|
|
24
|
+
* The vocabulary is the same (submit a proof, an admin approves or rejects
|
|
25
|
+
* with a note) and the fields are separate, because a pre-order has two
|
|
26
|
+
* payments months apart. Writing the balance's proof over the deposit's
|
|
27
|
+
* would destroy the evidence for the payment that created the order — and
|
|
28
|
+
* `paymentWindowTimeout`, which sweeps `pending`/`pending` orders with no
|
|
29
|
+
* `paymentProofUrl`, would then cancel and restock a paid one.
|
|
30
|
+
*/
|
|
31
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
32
|
+
import { ValidationError } from "../../../../errors/validation-error.js";
|
|
33
|
+
import { NotFoundError } from "../../../../errors/not-found-error.js";
|
|
34
|
+
import { AuthorizationError } from "../../../../errors/authorization-error.js";
|
|
35
|
+
import { storeRepository } from "../../../../features/stores/repository/store.repository.js";
|
|
36
|
+
import { sendNotification } from "../../../../features/admin/actions/notification-actions.js";
|
|
37
|
+
import { serverLogger } from "../../../../monitoring/server-logger.js";
|
|
38
|
+
/*
|
|
39
|
+
* 🛑 ONE message, for the same reason the ads collector names its own: an
|
|
40
|
+
* order that is not the caller's answers 404 rather than 403, so "no such
|
|
41
|
+
* order" and "not yours" must read identically. Two matching literals are a
|
|
42
|
+
* coincidence; a constant is the rule.
|
|
43
|
+
*/
|
|
44
|
+
const ORDER_NOT_FOUND = "Order not found.";
|
|
45
|
+
/**
|
|
46
|
+
* How long a buyer gets, once the balance is opened for collection.
|
|
47
|
+
*
|
|
48
|
+
* 🛑 Days, not the 15 MINUTES the deposit window uses. That window exists to
|
|
49
|
+
* stop an unpaid order holding stock; this one is a bill falling due on goods
|
|
50
|
+
* that have arrived, and the buyer may legitimately need to move money. A
|
|
51
|
+
* 15-minute balance window would cancel real orders for no benefit.
|
|
52
|
+
*/
|
|
53
|
+
export const BALANCE_WINDOW_DAYS = 7;
|
|
54
|
+
/** The balance a pre-order still owes, or `null` when it is not one. */
|
|
55
|
+
export function balanceDueOn(order) {
|
|
56
|
+
const ctx = order.sourceContext;
|
|
57
|
+
if (!ctx || ctx.path !== "pre-order")
|
|
58
|
+
return null;
|
|
59
|
+
return ctx.balanceDue > 0 ? ctx.balanceDue : null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Open the balance for collection — the seller's "the goods have landed"
|
|
63
|
+
* signal.
|
|
64
|
+
*
|
|
65
|
+
* 🛑 **Opening is an EVENT, not a derived state.** The balance could be
|
|
66
|
+
* computed as payable the moment the order exists, and that would be wrong:
|
|
67
|
+
* a pre-order's whole premise is that the goods do not exist yet, so asking
|
|
68
|
+
* for the balance before they do is asking a buyer to fund inventory. The
|
|
69
|
+
* seller opens it when stock arrives, and `dueBy` runs from that moment.
|
|
70
|
+
*
|
|
71
|
+
* 🛑 Idempotent. A seller clicking twice must not restart the buyer's
|
|
72
|
+
* window — which, since `dueBy` is what a reminder and any future
|
|
73
|
+
* auto-cancel would read, would silently extend the deadline every time
|
|
74
|
+
* anyone touched the button.
|
|
75
|
+
*
|
|
76
|
+
* 🛑 **Ownership is checked HERE, and it has to be**: the route that calls
|
|
77
|
+
* this is role-gated to sellers, and every seller passes that gate. Without
|
|
78
|
+
* this block any seller could open — and therefore start the payment clock
|
|
79
|
+
* on, and notify the buyer about — another seller's pre-order.
|
|
80
|
+
*
|
|
81
|
+
* It was missing, and the reason it was invisible is worth recording: the
|
|
82
|
+
* route also carried `permission: "orders:write"` alongside a `roles` array
|
|
83
|
+
* holding `seller`, which `getServerPermissions()` resolves for `employee`
|
|
84
|
+
* only — so every seller got a 403 before reaching this function at all. A
|
|
85
|
+
* broken gate masked an absent one, exactly as in Root Cause #98.
|
|
86
|
+
*/
|
|
87
|
+
export async function openBalanceCollection(orderId, actor, now = new Date()) {
|
|
88
|
+
const order = (await orderRepository.findById(orderId));
|
|
89
|
+
if (!order)
|
|
90
|
+
throw new NotFoundError(ORDER_NOT_FOUND);
|
|
91
|
+
/*
|
|
92
|
+
* 🛑 `order.storeId` is the store SLUG, never the seller's Firebase UID —
|
|
93
|
+
* two namespaces by design (§ "Store Identity Architecture"). Comparing it
|
|
94
|
+
* to `actor.uid` returns false for every legitimate seller, which is the
|
|
95
|
+
* other half of Root Cause #98. Resolve the store first.
|
|
96
|
+
*/
|
|
97
|
+
if (actor.role !== "admin") {
|
|
98
|
+
const store = await storeRepository.findByOwnerId(actor.uid);
|
|
99
|
+
if (!store || order.storeId !== store.id) {
|
|
100
|
+
throw new AuthorizationError("You do not own this order");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const due = balanceDueOn(order);
|
|
104
|
+
if (due == null) {
|
|
105
|
+
throw new ValidationError("This order has no pre-order balance.", {
|
|
106
|
+
orderId: "Only a pre-order carries a balance.",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (order.balance)
|
|
110
|
+
return order; // already open — see the idempotency note.
|
|
111
|
+
const dueBy = new Date(now.getTime() + BALANCE_WINDOW_DAYS * 24 * 60 * 60 * 1000);
|
|
112
|
+
await orderRepository.update(orderId, {
|
|
113
|
+
balance: {
|
|
114
|
+
/*
|
|
115
|
+
* 🛑 Snapshotted HERE, from `sourceContext`, and never read live
|
|
116
|
+
* again. The gap between deposit and balance is months — the worst in
|
|
117
|
+
* the codebase for D61 — so a price or tax change in between must not
|
|
118
|
+
* move what the buyer was told they owe.
|
|
119
|
+
*/
|
|
120
|
+
amount: due,
|
|
121
|
+
method: "manual",
|
|
122
|
+
status: "awaiting",
|
|
123
|
+
dueBy,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
await sendNotification({
|
|
127
|
+
userId: order.userId,
|
|
128
|
+
type: "order_confirmed",
|
|
129
|
+
priority: "high",
|
|
130
|
+
title: "Your pre-order balance is due",
|
|
131
|
+
message: `The balance of ₹${due.toFixed(2)} on order ${orderId} is now payable. Your item is ready to ship once it clears.`,
|
|
132
|
+
relatedId: orderId,
|
|
133
|
+
relatedType: "order",
|
|
134
|
+
});
|
|
135
|
+
return { ...order, balance: { amount: due, method: "manual", status: "awaiting", dueBy } };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The buyer submits a proof.
|
|
139
|
+
*
|
|
140
|
+
* 🛑 Allowed from `awaiting` AND from `rejected`. A rejected proof is the
|
|
141
|
+
* commonest reason to need a second attempt — a blurred screenshot, the
|
|
142
|
+
* wrong reference — and locking the buyer out after one rejection turns a
|
|
143
|
+
* recoverable mistake into a support ticket.
|
|
144
|
+
*/
|
|
145
|
+
export async function submitBalanceProof(orderId, buyerUid, proofUrl, transactionId, now = new Date()) {
|
|
146
|
+
const order = (await orderRepository.findById(orderId));
|
|
147
|
+
if (!order)
|
|
148
|
+
throw new NotFoundError(ORDER_NOT_FOUND);
|
|
149
|
+
/*
|
|
150
|
+
* 🛑 Ownership checked against `userId`, which is the buyer field on
|
|
151
|
+
* `OrderDocument` — NOT `buyerId`, which does not exist. A query or guard
|
|
152
|
+
* written against `buyerId` returns nothing and raises nothing, which is
|
|
153
|
+
* indistinguishable from "not your order" (CLAUDE.md records this costing
|
|
154
|
+
* real time once already).
|
|
155
|
+
*/
|
|
156
|
+
if (order.userId !== buyerUid) {
|
|
157
|
+
throw new NotFoundError(ORDER_NOT_FOUND);
|
|
158
|
+
}
|
|
159
|
+
if (!order.balance) {
|
|
160
|
+
throw new ValidationError("No balance has been opened on this order yet.", {
|
|
161
|
+
orderId: "The seller opens the balance when the goods arrive.",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (order.balance.status === "paid") {
|
|
165
|
+
throw new ValidationError("This balance is already paid.", {
|
|
166
|
+
orderId: "Nothing further is owed.",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (order.balance.status === "submitted") {
|
|
170
|
+
throw new ValidationError("A proof is already awaiting review.", {
|
|
171
|
+
orderId: "Wait for the current one to be reviewed before sending another.",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (!transactionId.trim()) {
|
|
175
|
+
throw new ValidationError("Enter the transaction reference.", {
|
|
176
|
+
transactionId: "The UTR or reference from your bank or UPI app.",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
await orderRepository.update(orderId, {
|
|
180
|
+
balance: {
|
|
181
|
+
...order.balance,
|
|
182
|
+
status: "submitted",
|
|
183
|
+
proofUrl,
|
|
184
|
+
transactionId: transactionId.trim(),
|
|
185
|
+
submittedAt: now,
|
|
186
|
+
/*
|
|
187
|
+
* 🛑 The previous rejection's note is CLEARED on resubmission. Leaving
|
|
188
|
+
* it would show the buyer a stale "your proof was unreadable" beside a
|
|
189
|
+
* proof they have just replaced.
|
|
190
|
+
*/
|
|
191
|
+
reviewNote: undefined,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* An admin approves or rejects the balance proof.
|
|
197
|
+
*
|
|
198
|
+
* 🛑 A rejection REQUIRES a note, for the same reason a reversal does: the
|
|
199
|
+
* buyer has to know what to send instead, and "rejected" alone guarantees
|
|
200
|
+
* the next attempt is the same proof again.
|
|
201
|
+
*/
|
|
202
|
+
export async function reviewBalanceProof(orderId, reviewerUid, approve, note, now = new Date()) {
|
|
203
|
+
const order = (await orderRepository.findById(orderId));
|
|
204
|
+
if (!order)
|
|
205
|
+
throw new NotFoundError(ORDER_NOT_FOUND);
|
|
206
|
+
if (order.balance?.status !== "submitted") {
|
|
207
|
+
throw new ValidationError("There is no submitted balance proof to review.", {
|
|
208
|
+
orderId: `The balance is "${order.balance?.status ?? "not opened"}".`,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (!approve && !note?.trim()) {
|
|
212
|
+
throw new ValidationError("A rejection needs a reason.", {
|
|
213
|
+
note: "Say what was wrong, or the buyer will send the same proof again.",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
await orderRepository.update(orderId, {
|
|
217
|
+
balance: {
|
|
218
|
+
...order.balance,
|
|
219
|
+
status: approve ? "paid" : "rejected",
|
|
220
|
+
reviewedBy: reviewerUid,
|
|
221
|
+
reviewedAt: now,
|
|
222
|
+
...(note?.trim() ? { reviewNote: note.trim() } : {}),
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
/*
|
|
226
|
+
* 🛑 The LEDGER is not posted here, and that is deliberate.
|
|
227
|
+
*
|
|
228
|
+
* A balance payment is a second capture against an order whose sale was
|
|
229
|
+
* already posted in full at creation — `orderPlacedPosting` recognises the
|
|
230
|
+
* whole `totalPrice`, not the deposit. Posting again would double the
|
|
231
|
+
* sale. What actually changes is which asset holds the money, and that is
|
|
232
|
+
* the settlement's job: the gateway receivable clears when the settlement
|
|
233
|
+
* lands, exactly as it does for a single-payment order.
|
|
234
|
+
*
|
|
235
|
+
* Recording this is left to whoever wires the deposit/balance split into
|
|
236
|
+
* `orderPlacedPosting`, which is a change to that rule rather than an
|
|
237
|
+
* extra entry here. An extra entry would balance and be wrong, which is
|
|
238
|
+
* the failure mode Part 4's whole design is about.
|
|
239
|
+
*/
|
|
240
|
+
serverLogger.info("balance proof reviewed", {
|
|
241
|
+
orderId,
|
|
242
|
+
approved: approve,
|
|
243
|
+
reviewerUid,
|
|
244
|
+
});
|
|
245
|
+
await sendNotification({
|
|
246
|
+
userId: order.userId,
|
|
247
|
+
type: approve ? "order_confirmed" : "payment_review",
|
|
248
|
+
priority: "high",
|
|
249
|
+
title: approve ? "Balance received" : "We could not accept your payment proof",
|
|
250
|
+
message: approve
|
|
251
|
+
? `Your balance on order ${orderId} has been confirmed. It is ready to ship.`
|
|
252
|
+
: `Your balance proof on order ${orderId} was not accepted: ${note?.trim() ?? ""}`,
|
|
253
|
+
relatedId: orderId,
|
|
254
|
+
relatedType: "order",
|
|
255
|
+
});
|
|
256
|
+
}
|
|
@@ -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
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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
|
-
* `
|
|
96
|
-
*
|
|
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(
|
|
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}: ₹${
|
|
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
|
/*
|