@mohasinac/appkit 4.59.0 → 4.60.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
- package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
- package/dist/_internal/server/features/brands/actions.js +78 -4
- package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
- package/dist/_internal/server/features/checkout/actions.js +668 -183
- package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
- package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
- package/dist/_internal/server/features/gateway-events/index.js +10 -0
- package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
- package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
- package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
- package/dist/_internal/server/features/gateway-events/redact.js +151 -0
- package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
- package/dist/_internal/server/features/grievance/actions.js +173 -0
- package/dist/_internal/server/features/grievance/index.d.ts +8 -0
- package/dist/_internal/server/features/grievance/index.js +8 -0
- package/dist/_internal/server/features/grouped/data.js +28 -3
- package/dist/_internal/server/features/lottery/actions.js +99 -4
- package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
- package/dist/_internal/server/features/order-completion/actions.js +336 -0
- package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
- package/dist/_internal/server/features/order-completion/index.js +7 -0
- package/dist/_internal/server/features/orders/actions.d.ts +13 -0
- package/dist/_internal/server/features/orders/actions.js +56 -2
- package/dist/_internal/server/features/orders/adapters.d.ts +20 -2
- package/dist/_internal/server/features/orders/adapters.js +44 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
- package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
- package/dist/_internal/server/features/orders/index.d.ts +2 -1
- package/dist/_internal/server/features/orders/index.js +1 -1
- package/dist/_internal/server/features/orders/service.d.ts +60 -0
- package/dist/_internal/server/features/orders/service.js +74 -0
- package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
- package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
- package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
- package/dist/_internal/server/features/payment-cases/index.js +9 -0
- package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
- package/dist/_internal/server/features/payouts/actions.js +84 -10
- package/dist/_internal/server/features/points/adjust.d.ts +61 -0
- package/dist/_internal/server/features/points/adjust.js +121 -0
- package/dist/_internal/server/features/points/award.d.ts +65 -0
- package/dist/_internal/server/features/points/award.js +145 -0
- package/dist/_internal/server/features/price-index/market.d.ts +33 -0
- package/dist/_internal/server/features/price-index/market.js +54 -1
- package/dist/_internal/server/features/products/data.js +40 -22
- package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
- package/dist/_internal/server/features/protection-claims/actions.js +228 -0
- package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
- package/dist/_internal/server/features/protection-claims/index.js +8 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
- package/dist/_internal/server/features/refunds/actions.js +259 -17
- package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
- package/dist/_internal/server/features/refunds/capacity.js +37 -0
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/search/index-build/sources.js +20 -1
- package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
- package/dist/_internal/server/features/shipping/reconcile.js +34 -3
- package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
- package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
- package/dist/_internal/server/features/stores/tabs.js +24 -4
- package/dist/_internal/server/functions/https.d.ts +31 -1
- package/dist/_internal/server/functions/https.js +52 -1
- package/dist/_internal/server/functions/scheduled.d.ts +72 -1
- package/dist/_internal/server/functions/scheduled.js +124 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
- package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
- package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
- package/dist/_internal/server/jobs/core/index.d.ts +2 -0
- package/dist/_internal/server/jobs/core/index.js +2 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
- package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
- package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
- package/dist/_internal/server/jobs/handlers/index.js +5 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
- package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +71 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
- package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
- package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
- package/dist/_internal/shared/checkout/lanes.js +46 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
- package/dist/_internal/shared/checkout/order-math.js +251 -3
- package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
- package/dist/_internal/shared/checkout/refundable-base.js +122 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
- package/dist/_internal/shared/features/auctions/config.js +30 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
- package/dist/_internal/shared/features/brands/schema.js +18 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
- package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
- package/dist/_internal/shared/features/print/label-profiles.js +350 -0
- package/dist/_internal/shared/features/products/to-product-item.js +33 -5
- package/dist/_internal/shared/fees/calculator.d.ts +31 -2
- package/dist/_internal/shared/fees/calculator.js +103 -5
- package/dist/_internal/shared/fees/india-states.d.ts +195 -0
- package/dist/_internal/shared/fees/india-states.js +190 -0
- package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
- package/dist/_internal/shared/fees/payout-hold.js +74 -0
- package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
- package/dist/_internal/shared/obligations/deadlines.js +155 -0
- package/dist/_internal/shared/shipping/quote.d.ts +96 -0
- package/dist/_internal/shared/shipping/quote.js +67 -0
- package/dist/_internal/shared/time/business-days.d.ts +91 -0
- package/dist/_internal/shared/time/business-days.js +185 -0
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +10 -4
- package/dist/client.js +29 -3
- package/dist/configs/tailwind.d.ts +0 -12
- package/dist/configs/tailwind.js +28 -15
- package/dist/constants/api-endpoints.d.ts +51 -9
- package/dist/constants/api-endpoints.js +23 -7
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/payment.d.ts +131 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/about/components/DeveloperView.js +5 -2
- package/dist/features/about/components/FAQPageView.js +2 -1
- package/dist/features/about/components/FeesView.js +2 -1
- package/dist/features/about/components/HelpPageView.js +2 -1
- package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
- package/dist/features/about/components/HowEmiWorksView.js +2 -1
- package/dist/features/about/components/HowOffersWorkView.js +2 -1
- package/dist/features/about/components/HowOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
- package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowReviewsWorkView.js +2 -1
- package/dist/features/about/components/PolicyPageView.js +2 -1
- package/dist/features/about/components/ProseHero.d.ts +73 -0
- package/dist/features/about/components/ProseHero.js +18 -0
- package/dist/features/about/components/SecurityPrivacyView.js +2 -1
- package/dist/features/about/components/ShippingPolicyView.js +2 -1
- package/dist/features/about/components/TrackOrderView.js +2 -1
- package/dist/features/about/components/UnauthorizedView.js +1 -1
- package/dist/features/about/index.d.ts +2 -0
- package/dist/features/about/index.js +5 -0
- package/dist/features/account/actions/address-actions.d.ts +26 -2
- package/dist/features/account/actions/address-actions.js +25 -1
- package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOrdersView.js +17 -0
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/hooks/useNotifications.js +29 -0
- package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
- package/dist/features/admin/components/AdminBeysView.js +2 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
- package/dist/features/admin/components/AdminContactEditorView.js +1 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
- package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +2 -2
- package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
- package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
- package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
- package/dist/features/admin/components/AdminPartsView.js +2 -2
- package/dist/features/admin/components/AdminProductsView.js +75 -4
- package/dist/features/admin/components/AdminSidebar.js +2 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
- package/dist/features/admin/components/AdminUsersView.js +72 -1
- package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
- package/dist/features/admin/components/AdminViewCards.js +4 -4
- package/dist/features/admin/components/DataListingView.d.ts +28 -0
- package/dist/features/admin/components/DataListingView.js +175 -14
- package/dist/features/admin/components/DataTable.d.ts +12 -1
- package/dist/features/admin/components/DataTable.js +3 -3
- package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/OwnerQuickActions.js +60 -2
- package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
- package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
- package/dist/features/admin/components/index.d.ts +4 -0
- package/dist/features/admin/components/index.js +6 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
- package/dist/features/admin/constants/filter-tabs.js +106 -0
- package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
- package/dist/features/admin/repository/site-settings.repository.js +82 -1
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
- package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
- package/dist/features/admin/schemas/firestore.js +5 -1
- package/dist/features/admin/schemas/site-settings-form.js +30 -1
- package/dist/features/auctions/actions/bid-actions.js +13 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
- package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
- package/dist/features/audit-log/schemas/firestore.js +14 -0
- package/dist/features/auth/permissions/constants.d.ts +17 -1
- package/dist/features/auth/permissions/constants.js +18 -0
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
- package/dist/features/categories/api/[id]/description-template/route.js +107 -1
- package/dist/features/categories/components/BrandDetailPageView.js +2 -2
- package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
- package/dist/features/categories/components/BundleDetailView.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryForm.js +12 -1
- package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
- package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
- package/dist/features/categories/repository/market-price-index.repository.js +105 -0
- package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
- package/dist/features/categories/repository/price-observation.repository.js +132 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/faq/components/FAQAccordion.js +2 -1
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
- package/dist/features/faq/components/RelatedFAQs.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +1 -1
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
- package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
- package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
- package/dist/features/gateway-events/schemas/firestore.js +125 -0
- package/dist/features/gateway-events/schemas/index.d.ts +81 -0
- package/dist/features/gateway-events/schemas/index.js +79 -0
- package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.js +37 -1
- package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
- package/dist/features/homepage/components/HeroCarousel.js +2 -2
- package/dist/features/homepage/components/SocialPostCard.js +1 -1
- package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
- package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
- package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
- package/dist/features/inventory/components/ReceiveStationView.js +413 -0
- package/dist/features/inventory/components/operator-message.d.ts +29 -0
- package/dist/features/inventory/components/operator-message.js +41 -0
- package/dist/features/inventory/index.d.ts +13 -0
- package/dist/features/inventory/index.js +28 -0
- package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
- package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
- package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
- package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
- package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
- package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
- package/dist/features/inventory/schemas/firestore.d.ts +197 -0
- package/dist/features/inventory/schemas/firestore.js +159 -0
- package/dist/features/layout/AppLayoutShell.d.ts +12 -1
- package/dist/features/layout/AppLayoutShell.js +25 -12
- package/dist/features/layout/AutoBreadcrumbs.js +162 -3
- package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
- package/dist/features/layout/BottomNavbar.d.ts +1 -1
- package/dist/features/layout/BottomNavbar.js +2 -2
- package/dist/features/media/MediaAvatar.js +1 -1
- package/dist/features/media/MediaImage.d.ts +24 -4
- package/dist/features/media/MediaImage.js +44 -4
- package/dist/features/media/MediaLightbox.js +1 -1
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +91 -3
- package/dist/features/obligations/constants/policies.d.ts +92 -0
- package/dist/features/obligations/constants/policies.js +378 -0
- package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
- package/dist/features/obligations/repository/obligations.repository.js +158 -0
- package/dist/features/obligations/schemas/firestore.d.ts +160 -0
- package/dist/features/obligations/schemas/firestore.js +87 -0
- package/dist/features/orders/actions/order-actions.d.ts +43 -4
- package/dist/features/orders/actions/order-actions.js +60 -3
- package/dist/features/orders/actions/refund-actions.js +70 -10
- package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
- package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/orders/components/RefundHistoryTable.js +3 -1
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +6 -0
- package/dist/features/orders/constants/transitions.d.ts +59 -0
- package/dist/features/orders/constants/transitions.js +104 -0
- package/dist/features/orders/repository/orders.repository.d.ts +109 -6
- package/dist/features/orders/repository/orders.repository.js +464 -21
- package/dist/features/orders/schemas/firestore.d.ts +270 -0
- package/dist/features/orders/schemas/firestore.js +24 -0
- package/dist/features/orders/schemas/index.d.ts +61 -0
- package/dist/features/orders/schemas/index.js +22 -0
- package/dist/features/orders/types/index.d.ts +79 -0
- package/dist/features/parts/components/PartsIndexPageView.js +8 -1
- package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
- package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
- package/dist/features/payment-cases/schemas/firestore.js +105 -0
- package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
- package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
- package/dist/features/payments/repository/payout.repository.d.ts +0 -1
- package/dist/features/payments/repository/payout.repository.js +0 -1
- package/dist/features/points/repository/points.repository.d.ts +122 -0
- package/dist/features/points/repository/points.repository.js +304 -1
- package/dist/features/points/schemas/adjust-form.d.ts +58 -0
- package/dist/features/points/schemas/adjust-form.js +63 -0
- package/dist/features/points/schemas/firestore.d.ts +108 -1
- package/dist/features/points/schemas/firestore.js +84 -0
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
- package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
- package/dist/features/price-index/market-stats.d.ts +82 -0
- package/dist/features/price-index/market-stats.js +230 -0
- package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
- package/dist/features/print/components/BoxLabelPrintView.js +66 -0
- package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
- package/dist/features/print/components/LabelProfilePicker.js +38 -0
- package/dist/features/print/components/labels.d.ts +117 -0
- package/dist/features/print/components/labels.js +258 -0
- package/dist/features/print/components/labels.style.css +46 -0
- package/dist/features/print/index.d.ts +6 -0
- package/dist/features/print/index.js +16 -0
- package/dist/features/products/columns/productTableColumns.js +5 -4
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
- package/dist/features/products/components/PrizeDrawCollage.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/PrizeRevealModal.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +34 -23
- package/dist/features/products/components/ProductGrid.js +86 -62
- package/dist/features/products/constants/card-badges.d.ts +97 -0
- package/dist/features/products/constants/card-badges.js +100 -0
- package/dist/features/products/repository/products.repository.js +34 -1
- package/dist/features/products/schemas/firestore.d.ts +46 -3
- package/dist/features/products/schemas/firestore.js +21 -0
- package/dist/features/promotions/components/PromotionsView.js +1 -1
- package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
- package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
- package/dist/features/promotions/schemas/firestore.js +30 -3
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
- package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
- package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
- package/dist/features/protection-claims/schemas/firestore.js +130 -0
- package/dist/features/reviews/actions/review-actions.js +3 -2
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/schemas/firestore.d.ts +20 -1
- package/dist/features/scams/constants/scam-types.d.ts +2 -0
- package/dist/features/scams/constants/scam-types.js +18 -0
- package/dist/features/scams/schemas/report-form.d.ts +3 -3
- package/dist/features/scams/schemas/report-form.js +26 -1
- package/dist/features/search/components/Search.js +9 -9
- package/dist/features/search/components/SearchResultsClient.js +18 -3
- package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
- package/dist/features/search/components/SearchResultsPageView.js +2 -2
- package/dist/features/seller/actions/offer-actions.js +16 -1
- package/dist/features/seller/actions/seller-actions.js +14 -3
- package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
- package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
- package/dist/features/seller/components/PrintCenterView.js +41 -30
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
- package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
- package/dist/features/seller/components/SellerProductShell.js +123 -13
- package/dist/features/seller/components/SellerReviewsView.js +4 -2
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
- package/dist/features/seller/schemas/coupon-form.js +27 -0
- package/dist/features/seller/schemas/product-form.d.ts +3 -0
- package/dist/features/seller/schemas/product-form.js +5 -0
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +14 -0
- package/dist/features/shell/SectionForm.d.ts +19 -0
- package/dist/features/shell/SectionForm.js +133 -9
- package/dist/features/shell/SectionForm.style.css +54 -0
- package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
- package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
- package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
- package/dist/features/stores/components/StoreGuideHubView.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/support/schemas/firestore.d.ts +73 -2
- package/dist/features/support/schemas/firestore.js +59 -0
- package/dist/index.d.ts +16 -24
- package/dist/index.js +38 -47
- package/dist/next/ErrorBoundary.js +1 -1
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +40 -0
- package/dist/providers/payment-manual/index.d.ts +15 -1
- package/dist/providers/payment-manual/index.js +37 -0
- package/dist/providers/payment-phonepe/index.d.ts +14 -1
- package/dist/providers/payment-phonepe/index.js +36 -0
- package/dist/providers/payment-razorpay/index.d.ts +44 -1
- package/dist/providers/payment-razorpay/index.js +99 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
- package/dist/providers/shipping-shiprocket/index.js +18 -0
- package/dist/schemas/registry.d.ts +44 -11
- package/dist/server-entry.d.ts +2 -0
- package/dist/server-entry.js +27 -0
- package/dist/server.d.ts +43 -0
- package/dist/server.js +98 -0
- package/dist/styles.css +418 -149
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +15 -12
- package/dist/tokens/themes/default-light.js +14 -11
- package/dist/tokens/tokens.css +106 -23
- package/dist/ui/DataTable.style.css +4 -4
- package/dist/ui/columns/cell-renderers.d.ts +20 -0
- package/dist/ui/columns/cell-renderers.js +26 -1
- package/dist/ui/columns/index.d.ts +1 -1
- package/dist/ui/columns/index.js +4 -1
- package/dist/ui/components/Badge.style.css +1 -1
- package/dist/ui/components/Breadcrumb.js +19 -1
- package/dist/ui/components/BulkActionBar.style.css +4 -4
- package/dist/ui/components/Button.style.css +1 -1
- package/dist/ui/components/Checkbox.style.css +1 -1
- package/dist/ui/components/FallbackShell.js +1 -1
- package/dist/ui/components/Form.d.ts +14 -1
- package/dist/ui/components/Form.js +2 -2
- package/dist/ui/components/FormField.js +18 -4
- package/dist/ui/components/FormField.style.css +43 -3
- package/dist/ui/components/GatedPrice.style.css +1 -1
- package/dist/ui/components/IconButton.style.css +1 -1
- package/dist/ui/components/Input.js +22 -1
- package/dist/ui/components/Input.style.css +38 -22
- package/dist/ui/components/ListingFilterDrawer.js +1 -1
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/Radio.style.css +1 -1
- package/dist/ui/components/Repeater.d.ts +53 -0
- package/dist/ui/components/Repeater.js +7 -0
- package/dist/ui/components/RichTextEditor.d.ts +20 -1
- package/dist/ui/components/RichTextEditor.js +23 -2
- package/dist/ui/components/Select.style.css +7 -21
- package/dist/ui/components/Semantic.js +27 -3
- package/dist/ui/components/TagInput.style.css +4 -9
- package/dist/ui/components/Textarea.style.css +4 -18
- package/dist/ui/components/Toggle.style.css +1 -1
- package/dist/ui/components/Typography.d.ts +3 -3
- package/dist/ui/components/Typography.js +15 -15
- package/dist/ui/components/ViewToggle.style.css +4 -4
- package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
- package/dist/ui/forms/AddAnotherToggle.js +56 -0
- package/dist/ui/forms/ColorPickerField.js +15 -2
- package/dist/ui/forms/FieldCheckbox.js +15 -2
- package/dist/ui/forms/FieldInput.d.ts +22 -1
- package/dist/ui/forms/FieldInput.js +19 -5
- package/dist/ui/forms/FieldLabel.d.ts +35 -0
- package/dist/ui/forms/FieldLabel.js +70 -0
- package/dist/ui/forms/FieldSelect.d.ts +4 -1
- package/dist/ui/forms/FieldSelect.js +27 -5
- package/dist/ui/forms/FieldTextarea.d.ts +4 -1
- package/dist/ui/forms/FieldTextarea.js +22 -5
- package/dist/ui/forms/FormShell.d.ts +17 -1
- package/dist/ui/forms/FormShell.js +36 -2
- package/dist/ui/forms/FormShell.style.css +41 -33
- package/dist/ui/forms/index.d.ts +5 -1
- package/dist/ui/forms/index.js +2 -0
- package/dist/ui/icons/icon-registry.d.ts +1 -0
- package/dist/ui/icons/icon-registry.js +17 -1
- package/dist/utils/id-generators.d.ts +80 -0
- package/dist/utils/id-generators.js +93 -0
- package/dist/utils/search-txt-builders.d.ts +12 -0
- package/dist/utils/search-txt-builders.js +17 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +106 -5
- package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
- package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
- package/dist/features/loyalty/api/balance/route.d.ts +0 -12
- package/dist/features/loyalty/api/balance/route.js +0 -41
- package/dist/features/loyalty/api/earn/route.d.ts +0 -12
- package/dist/features/loyalty/api/earn/route.js +0 -38
- package/dist/features/loyalty/api/history/route.d.ts +0 -12
- package/dist/features/loyalty/api/history/route.js +0 -58
- package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
- package/dist/features/loyalty/api/redeem/route.js +0 -38
- package/dist/features/loyalty/columns/index.d.ts +0 -17
- package/dist/features/loyalty/columns/index.js +0 -40
- package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
- package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
- package/dist/features/loyalty/components/index.d.ts +0 -1
- package/dist/features/loyalty/components/index.js +0 -1
- package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
- package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
- package/dist/features/loyalty/index.d.ts +0 -6
- package/dist/features/loyalty/index.js +0 -6
- package/dist/features/loyalty/manifest.d.ts +0 -2
- package/dist/features/loyalty/manifest.js +0 -16
- package/dist/features/loyalty/messages/en.json +0 -19
- package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
- package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
- package/dist/features/loyalty/schemas/index.d.ts +0 -85
- package/dist/features/loyalty/schemas/index.js +0 -40
- package/dist/features/loyalty/server.d.ts +0 -6
- package/dist/features/loyalty/server.js +0 -6
- package/dist/features/loyalty/types/index.d.ts +0 -31
- package/dist/features/loyalty/types/index.js +0 -1
- package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
- package/dist/features/loyalty/types/loyalty-config.js +0 -1
- package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
- package/dist/features/loyalty/types/loyalty-math.js +0 -27
- package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
- package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
import { buildOrderSearchTxt } from "../../../utils/search-txt-builders.js";
|
|
2
2
|
import { planSearchTxt, beginSearchWindow, emptySearchResult, } from "../../../utils/search-txt-query.js";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
4
|
-
|
|
4
|
+
/*
|
|
5
|
+
* `ValidationError` from `../../../errors`, which is the REAL one — the class
|
|
6
|
+
* `mapToHttpError` instanceof-checks to produce a 400. The other
|
|
7
|
+
* `ValidationError` (in `_internal/shared/errors`) is a structurally
|
|
8
|
+
* unrelated class that falls through to a generic 500; Root Cause #53 records
|
|
9
|
+
* that mix-up swallowing ~100 call sites' validation messages.
|
|
10
|
+
*/
|
|
11
|
+
import { DatabaseError, NotFoundError, ValidationError } from "../../../errors/index.js";
|
|
5
12
|
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase/index.js";
|
|
6
13
|
import { maskPii, revealPii, ORDER_PII_FIELDS, } from "../../../security/index.js";
|
|
7
14
|
import { serverTimestamp, arrayUnion } from "../../../contracts/field-ops.js";
|
|
8
15
|
import { createOrderId, ORDER_COLLECTION, ORDER_TRACKED_FIELDS, OrderStatusValues, } from "../schemas/index.js";
|
|
9
16
|
import { withHistory } from "../../../_internal/shared/history/index.js";
|
|
10
17
|
import { MANUAL_PAYMENT_METHODS, PAYMENT_REVIEW_QUEUE_SCAN_LIMIT, } from "../constants/payment-window.js";
|
|
18
|
+
import { isLegalOrderTransition, describeIllegalTransition, } from "../constants/transitions.js";
|
|
19
|
+
import { reconcileObligations } from "../../../_internal/shared/obligations/deadlines.js";
|
|
20
|
+
import { obligationsRepository } from "../../obligations/repository/obligations.repository.js";
|
|
11
21
|
import { ORDER_FIELDS } from "../../../constants/field-names.js";
|
|
22
|
+
/**
|
|
23
|
+
* An illegal status transition was refused.
|
|
24
|
+
*
|
|
25
|
+
* Its own class so the webhook ingest can distinguish "this event is LATE"
|
|
26
|
+
* from "this event is broken": a replayed `order.paid` arriving after the order
|
|
27
|
+
* shipped is not wrong, it is out of date, and the correct ack is 200 with a
|
|
28
|
+
* `status: "conflict"` row rather than a 500 asking the gateway to retry a
|
|
29
|
+
* regression it can never win.
|
|
30
|
+
*/
|
|
31
|
+
export class OrderStatusError extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.code = "ORDER_STATUS_TRANSITION_INVALID";
|
|
35
|
+
this.name = "OrderStatusError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
12
38
|
/**
|
|
13
39
|
* Statuses that count toward a user's per-product / per-bundle purchase
|
|
14
40
|
* allowance (SB6 maxPerUser enforcement). Cancelled and refunded orders are
|
|
@@ -21,6 +47,101 @@ const ACTIVE_ALLOWANCE_STATUSES = [
|
|
|
21
47
|
ORDER_FIELDS.STATUS_VALUES.SHIPPED,
|
|
22
48
|
ORDER_FIELDS.STATUS_VALUES.DELIVERED,
|
|
23
49
|
];
|
|
50
|
+
/**
|
|
51
|
+
* Status → the timestamp field recording when the order ENTERED that status.
|
|
52
|
+
*
|
|
53
|
+
* 🛑 `deliveryDate` had NO WRITER anywhere in the live application until
|
|
54
|
+
* 2026-10-21 — only seed data and display components referenced it — and that
|
|
55
|
+
* single omission unanchored every post-delivery deadline in the product:
|
|
56
|
+
*
|
|
57
|
+
* · `assertReturnWindowOpen` fell through to an `updatedAt` fallback, so the
|
|
58
|
+
* return window was measured from the order's LAST MUTATION. Any write at
|
|
59
|
+
* all — a note, `assignWorker`, a `statusHistory` append — silently
|
|
60
|
+
* restarted the buyer's clock.
|
|
61
|
+
* · `PROTECTION_CLAIM_WINDOW_HOURS` had nothing to measure from, so the
|
|
62
|
+
* shipment- and sale-protection claim windows (which we charge 2% and 3%
|
|
63
|
+
* for) could not open or close.
|
|
64
|
+
* · The payout hold had no anchor, which is half of why three payout paths
|
|
65
|
+
* honour one intended hold between them (D25).
|
|
66
|
+
*
|
|
67
|
+
* A FACT, never a deadline. The deadline is derived from it on read, so a
|
|
68
|
+
* policy change re-dates nothing and there is no stored mirror to drift
|
|
69
|
+
* (Root Cause #42).
|
|
70
|
+
*
|
|
71
|
+
* 🛑 `cancellationDate` is deliberately ABSENT from this map even though the
|
|
72
|
+
* field exists. `cancelOrder` already sets it in the same patch, and stamping
|
|
73
|
+
* it here too would be a second writer for one fact — the exact shape the rest
|
|
74
|
+
* of this comment is about. Add a status here only when nothing else owns its
|
|
75
|
+
* anchor.
|
|
76
|
+
*/
|
|
77
|
+
const STATUS_ANCHOR_FIELD = {
|
|
78
|
+
[ORDER_FIELDS.STATUS_VALUES.SHIPPED]: "shippingDate",
|
|
79
|
+
[ORDER_FIELDS.STATUS_VALUES.DELIVERED]: "deliveryDate",
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Stamp the anchor for a status this patch is moving INTO.
|
|
83
|
+
*
|
|
84
|
+
* Three properties, each load-bearing:
|
|
85
|
+
*
|
|
86
|
+
* 1. **Only on a real transition.** Gated on `current?.status !== patch.status`,
|
|
87
|
+
* so re-writing `delivered` over `delivered` (a tracking-number edit, a
|
|
88
|
+
* replayed webhook) does not move the anchor. Without that gate every
|
|
89
|
+
* subsequent write would push the return window further out — which is the
|
|
90
|
+
* `updatedAt` bug this exists to remove, reintroduced by the fix for it.
|
|
91
|
+
* 2. **Never overwrites an explicit value.** A caller passing its own
|
|
92
|
+
* `deliveryDate` — a backfill, an admin correcting a date, a courier
|
|
93
|
+
* webhook reporting when delivery actually happened — wins. We are
|
|
94
|
+
* supplying a default, not asserting authority.
|
|
95
|
+
* 3. **Costs no reads.** `updateWithHistory` already holds `current`, which is
|
|
96
|
+
* why this belongs there and not in a service layer (Rule #6).
|
|
97
|
+
*/
|
|
98
|
+
/**
|
|
99
|
+
* Coerce whatever Firestore actually handed us into a `Date`, or `null`.
|
|
100
|
+
*
|
|
101
|
+
* 🛑 FOUR shapes reach this, and the type annotations claim only one.
|
|
102
|
+
* `OrderDocument` declares these fields `Date`, but the queries below read
|
|
103
|
+
* `d.data()` RAW — no `mapDoc`, because they need the `DocumentReference` for
|
|
104
|
+
* a later batch write — so what arrives is:
|
|
105
|
+
*
|
|
106
|
+
* · a Firestore `Timestamp` (the normal case, exposing `.toDate()`)
|
|
107
|
+
* · a real `Date` (when a doc was just written in-process)
|
|
108
|
+
* · a **string** — `/api/store/orders/[id]` wrote `shippingDate` as
|
|
109
|
+
* `new Date().toISOString()` until 2026-10-21 while the sibling
|
|
110
|
+
* `customShipOrder` wrote a `Date`, so live documents carry both
|
|
111
|
+
* · a number (epoch ms), from any hand-written fixture
|
|
112
|
+
*
|
|
113
|
+
* A bare `o.data.deliveryDate.getTime()` therefore throws on the common case
|
|
114
|
+
* (a Timestamp has no `getTime`), and `new Date(timestamp)` yields an Invalid
|
|
115
|
+
* Date rather than failing — which would silently compare as `false` and
|
|
116
|
+
* withhold the payout forever with no error. Mirrors the four-shape handling
|
|
117
|
+
* in `orders/adapters.ts:27`, returning a `Date` rather than an ISO string.
|
|
118
|
+
*/
|
|
119
|
+
function toDateOrNull(value) {
|
|
120
|
+
if (!value)
|
|
121
|
+
return null;
|
|
122
|
+
if (value instanceof Date)
|
|
123
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
124
|
+
if (typeof value === "object" && typeof value.toDate === "function") {
|
|
125
|
+
const d = value.toDate();
|
|
126
|
+
return d instanceof Date && !Number.isNaN(d.getTime()) ? d : null;
|
|
127
|
+
}
|
|
128
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
129
|
+
const d = new Date(value);
|
|
130
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
function stampStatusAnchor(current, patch) {
|
|
135
|
+
const nextStatus = patch.status;
|
|
136
|
+
if (!nextStatus || current?.status === nextStatus)
|
|
137
|
+
return patch;
|
|
138
|
+
const field = STATUS_ANCHOR_FIELD[nextStatus];
|
|
139
|
+
if (!field)
|
|
140
|
+
return patch;
|
|
141
|
+
if (patch[field] !== undefined)
|
|
142
|
+
return patch;
|
|
143
|
+
return { ...patch, [field]: new Date() };
|
|
144
|
+
}
|
|
24
145
|
class OrderRepository extends BaseRepository {
|
|
25
146
|
constructor() {
|
|
26
147
|
super(ORDER_COLLECTION);
|
|
@@ -130,6 +251,52 @@ class OrderRepository extends BaseRepository {
|
|
|
130
251
|
*/
|
|
131
252
|
known) {
|
|
132
253
|
const current = known !== undefined ? known : await this.findById(orderId);
|
|
254
|
+
/*
|
|
255
|
+
* ── THE STATUS TRANSITION GUARD ───────────────────────────────────────────
|
|
256
|
+
*
|
|
257
|
+
* 🛑 ENFORCED HERE, and the location is the whole reason this is cheap.
|
|
258
|
+
*
|
|
259
|
+
* `updateWithHistory` is the single choke point all seven write primitives
|
|
260
|
+
* route through, AND it is the only place that already holds the current
|
|
261
|
+
* document — so the guard costs **zero extra Firestore reads**. Enforcing
|
|
262
|
+
* at the service layer instead would be four call-site edits and would
|
|
263
|
+
* still miss `settleRefund`'s own write, which goes straight through here.
|
|
264
|
+
*
|
|
265
|
+
* What it stops, from the census in `constants/transitions.ts`: two of
|
|
266
|
+
* `updateStatus`'s four callers pass an arbitrary status from a REQUEST
|
|
267
|
+
* BODY with `as any`, so a seller could set `delivered` on an unpaid order
|
|
268
|
+
* or regress `delivered` → `pending`, and nothing refused it. A replayed
|
|
269
|
+
* `order.paid` webhook could do the same.
|
|
270
|
+
*
|
|
271
|
+
* 🛑 It is skipped when `current` is null — a brand-new document has no
|
|
272
|
+
* `from` state, and refusing the first write would make the guard reject
|
|
273
|
+
* order creation. Also skipped when the patch carries no `status`, which
|
|
274
|
+
* is most writes (a tracking number, a note, a payout flag).
|
|
275
|
+
*/
|
|
276
|
+
const nextStatus = patch.status;
|
|
277
|
+
if (current?.status && nextStatus && !isLegalOrderTransition(current.status, nextStatus)) {
|
|
278
|
+
if (ctx?.allowStatusOverride) {
|
|
279
|
+
/*
|
|
280
|
+
* The admin escape hatch — RECORDED, never silent. An override that
|
|
281
|
+
* leaves no trace is indistinguishable from the bug this guard exists
|
|
282
|
+
* to catch, so it goes into `statusHistory` as an explicit note and the
|
|
283
|
+
* write proceeds.
|
|
284
|
+
*/
|
|
285
|
+
ctx = {
|
|
286
|
+
...ctx,
|
|
287
|
+
note: [
|
|
288
|
+
ctx.note,
|
|
289
|
+
`status override: ${current.status} → ${nextStatus} is not a legal transition`,
|
|
290
|
+
]
|
|
291
|
+
.filter(Boolean)
|
|
292
|
+
.join(" · "),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
throw new OrderStatusError(describeIllegalTransition(current.status, nextStatus));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
patch = stampStatusAnchor(current, patch);
|
|
133
300
|
const withEntry = withHistory(current, patch, {
|
|
134
301
|
tracked: ORDER_TRACKED_FIELDS,
|
|
135
302
|
actor: ctx?.actor ?? { role: "system" },
|
|
@@ -148,7 +315,72 @@ class OrderRepository extends BaseRepository {
|
|
|
148
315
|
// `null` means no tracked field actually changed — write the patch as-is
|
|
149
316
|
// so an untracked update never grows the array.
|
|
150
317
|
// Plain: this is a write payload and `update` encrypts.
|
|
151
|
-
|
|
318
|
+
const written = await this.update(orderId, (withEntry ?? patch));
|
|
319
|
+
/*
|
|
320
|
+
* ── OBLIGATION RECONCILIATION (Phase 5b) ──────────────────────────────────
|
|
321
|
+
*
|
|
322
|
+
* Reconciled from the order's OWN write, here, for two reasons.
|
|
323
|
+
*
|
|
324
|
+
* 🛑 NOT a Firestore trigger. An `onOrderWrite` trigger that wrote
|
|
325
|
+
* `obligations`, whose sweep then wrote `orders`, is Root Cause #92's
|
|
326
|
+
* self-write loop exactly — 1,017,548 invocations in 24 hours against a
|
|
327
|
+
* 2M/month quota, plus a correctly-guarded sibling woken a million times
|
|
328
|
+
* just to return. A correct guard does not save you from a neighbour's
|
|
329
|
+
* loop.
|
|
330
|
+
*
|
|
331
|
+
* 🛑 And it costs ZERO extra reads: the post-write document is assembled
|
|
332
|
+
* from `current` plus `patch` rather than re-read. `reconcileObligations`
|
|
333
|
+
* is a pure function over that shape.
|
|
334
|
+
*
|
|
335
|
+
* Fire-and-forget, and that is deliberate: an obligation is a derived HINT.
|
|
336
|
+
* Letting its write fail the ORDER write — the one the caller is waiting on,
|
|
337
|
+
* which may be a payment confirmation — would trade a real transaction for
|
|
338
|
+
* a bookkeeping row. The next write reconciles again, and the sweep's
|
|
339
|
+
* re-read of the subject is what makes a missed row self-correcting.
|
|
340
|
+
*/
|
|
341
|
+
void this.reconcileObligationsFor({ ...(current ?? {}), ...patch, id: orderId }, current).catch((err) => {
|
|
342
|
+
void normalizeError(err);
|
|
343
|
+
});
|
|
344
|
+
return written;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Open/close this order's obligations to match what its own facts imply.
|
|
348
|
+
*
|
|
349
|
+
* Public so the obligation sweep can re-run it after acting, and so a
|
|
350
|
+
* backfill can reconcile existing orders without a synthetic write.
|
|
351
|
+
*/
|
|
352
|
+
async reconcileObligationsFor(order,
|
|
353
|
+
/**
|
|
354
|
+
* The pre-write document, so the close set is `was-open MINUS is-open`
|
|
355
|
+
* rather than every kind that is not open. Omit ONLY for a deliberate
|
|
356
|
+
* backfill — see `reconcileObligations`' own 🛑 on the cost.
|
|
357
|
+
*/
|
|
358
|
+
previous) {
|
|
359
|
+
const { open, close } = reconcileObligations(order, previous);
|
|
360
|
+
const repo = obligationsRepository();
|
|
361
|
+
for (const deadline of open) {
|
|
362
|
+
await repo.open({
|
|
363
|
+
kind: deadline.kind,
|
|
364
|
+
subjectCollection: ORDER_COLLECTION,
|
|
365
|
+
subjectId: order.id,
|
|
366
|
+
dueAt: deadline.dueAt,
|
|
367
|
+
...(order.isTestData ? { isTestData: true } : {}),
|
|
368
|
+
note: deadline.policy.describe,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/*
|
|
372
|
+
* Closed as `met`, not `superseded`: reaching here means the order's own
|
|
373
|
+
* facts no longer satisfy the precondition, i.e. the thing the obligation
|
|
374
|
+
* was waiting for HAPPENED. `superseded` is reserved for the sweep finding
|
|
375
|
+
* a stale row at fire time, which is a different story and worth
|
|
376
|
+
* distinguishing in the record.
|
|
377
|
+
*/
|
|
378
|
+
for (const kind of close) {
|
|
379
|
+
await repo.close(order.id, kind, {
|
|
380
|
+
state: "met",
|
|
381
|
+
closeReason: "the order's own state no longer requires it",
|
|
382
|
+
});
|
|
383
|
+
}
|
|
152
384
|
}
|
|
153
385
|
async updateStatus(orderId, status, additionalData, ctx) {
|
|
154
386
|
return this.updateWithHistory(orderId, { status, ...additionalData, updatedAt: new Date() }, ctx, "orderRepository.updateStatus");
|
|
@@ -404,6 +636,162 @@ class OrderRepository extends BaseRepository {
|
|
|
404
636
|
* Append one refund event to the order and mark it non-contestable.
|
|
405
637
|
* If `becomeRefunded` is true, also transitions the order status to "refunded".
|
|
406
638
|
*/
|
|
639
|
+
/**
|
|
640
|
+
* PHASE A — reserve a refund slot atomically, enforcing the cumulative cap.
|
|
641
|
+
*
|
|
642
|
+
* 🛑 WHY A TRANSACTION, AND WHY NOT `arrayUnion` (D1 + D6).
|
|
643
|
+
*
|
|
644
|
+
* `postRefundEvent` below does `findById` then `[...existing, event]` — a
|
|
645
|
+
* read-then-write. Two concurrent refunds both read the same `existing` and
|
|
646
|
+
* the second write **overwrites the first's append, losing an event
|
|
647
|
+
* entirely**. And the cap check lived in the ACTION, outside any
|
|
648
|
+
* transaction, so it was TOCTOU: two requests could each see a clear cap and
|
|
649
|
+
* both proceed. Combined with the cap comparing against `totalPrice` rather
|
|
650
|
+
* than the remaining balance (D1), ₹500 and another ₹500 both passed on a
|
|
651
|
+
* ₹600 order — **two real gateway refunds, both successful.**
|
|
652
|
+
*
|
|
653
|
+
* `arrayUnion` is the obvious fix and is WRONG here on three counts: it
|
|
654
|
+
* cannot evaluate the cumulative cap (the whole point), it cannot build the
|
|
655
|
+
* `withHistory` entry, and it dedupes by DEEP EQUALITY — so two legitimate
|
|
656
|
+
* identical ₹100 partial refunds would silently merge into one.
|
|
657
|
+
*
|
|
658
|
+
* The entry lands with `status: "reserved"`, which `committedRefundTotal`
|
|
659
|
+
* counts. That is what makes the reservation real: a concurrent request sees
|
|
660
|
+
* the pending amount and cannot refund against a balance already spoken for.
|
|
661
|
+
* A later `failed` settlement releases it simply by being skipped.
|
|
662
|
+
*
|
|
663
|
+
* Throws rather than returning a flag — a caller that ignores a boolean here
|
|
664
|
+
* calls the gateway anyway, which is the failure this prevents.
|
|
665
|
+
*/
|
|
666
|
+
async reserveRefund(orderId, event,
|
|
667
|
+
/** Resolved by the caller from `refundCapacity`; see `refundable-base.ts`. */
|
|
668
|
+
opts) {
|
|
669
|
+
const docRef = this.getCollection().doc(orderId);
|
|
670
|
+
await this.db.runTransaction(async (tx) => {
|
|
671
|
+
const snap = (await tx.get(docRef));
|
|
672
|
+
const data = typeof snap?.data === "function" ? snap.data() : undefined;
|
|
673
|
+
if (!data)
|
|
674
|
+
throw new NotFoundError(`Order ${orderId} not found`);
|
|
675
|
+
const existing = (data.refunds ?? []);
|
|
676
|
+
/*
|
|
677
|
+
* Idempotency inside the transaction too, not only at the claim doc.
|
|
678
|
+
* The claim is the at-most-once gate for a RETRY of the same request;
|
|
679
|
+
* this catches the case where a reservation landed but the claim write
|
|
680
|
+
* did not, and makes re-entry a no-op rather than a second slot.
|
|
681
|
+
*/
|
|
682
|
+
if (existing.some((r) => r.refundId === event.refundId))
|
|
683
|
+
return;
|
|
684
|
+
const committed = existing
|
|
685
|
+
.filter((r) => r.status !== "failed")
|
|
686
|
+
.reduce((sum, r) => sum + (r.amount ?? 0), 0);
|
|
687
|
+
/*
|
|
688
|
+
* The cap, evaluated HERE rather than by the caller — inside the
|
|
689
|
+
* transaction is the only place it is not a race.
|
|
690
|
+
*/
|
|
691
|
+
if (committed + event.amount > opts.refundableBase + opts.epsilon) {
|
|
692
|
+
const remaining = Math.max(0, opts.refundableBase - committed);
|
|
693
|
+
throw new ValidationError(`Refund of ₹${event.amount.toFixed(2)} exceeds the ₹${remaining.toFixed(2)} still refundable on this order.`);
|
|
694
|
+
}
|
|
695
|
+
tx.update(docRef, {
|
|
696
|
+
refunds: [...existing, { ...event, status: "reserved" }],
|
|
697
|
+
contestable: false,
|
|
698
|
+
updatedAt: new Date(),
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* PHASE B — settle a reserved refund: `processed` with the gateway's id, or
|
|
704
|
+
* `failed`, which releases the reservation.
|
|
705
|
+
*
|
|
706
|
+
* Runs AFTER the provider call, which must sit outside any transaction: a
|
|
707
|
+
* Firestore transaction retries its body, and a retried network call is a
|
|
708
|
+
* second refund. This is the only ordering in which "money left the gateway
|
|
709
|
+
* and we have no record of it" is impossible — a crash between the provider
|
|
710
|
+
* call and this write leaves a `reserved` entry and an unsettled claim,
|
|
711
|
+
* which is exactly the shape Phase 4's reconciler looks for.
|
|
712
|
+
*
|
|
713
|
+
* Goes through `updateWithHistory`, so the TIMELINE entry is written at
|
|
714
|
+
* settlement rather than at reservation. That is deliberate: the history
|
|
715
|
+
* should record money that moved, not an intent that may yet fail.
|
|
716
|
+
*/
|
|
717
|
+
async settleRefund(orderId, refundId, outcome, becomeRefunded = false, ctx) {
|
|
718
|
+
const order = await this.findById(orderId);
|
|
719
|
+
if (!order)
|
|
720
|
+
throw new NotFoundError(`Order ${orderId} not found`);
|
|
721
|
+
const existing = order.refunds ?? [];
|
|
722
|
+
const target = existing.find((r) => r.refundId === refundId);
|
|
723
|
+
if (!target)
|
|
724
|
+
throw new NotFoundError(`Reserved refund ${refundId} not found on order ${orderId}`);
|
|
725
|
+
const settled = existing.map((r) => r.refundId === refundId
|
|
726
|
+
? {
|
|
727
|
+
...r,
|
|
728
|
+
...outcome,
|
|
729
|
+
...(outcome.status === "processed" ? (outcome.providerFields ?? {}) : {}),
|
|
730
|
+
}
|
|
731
|
+
: r);
|
|
732
|
+
return this.updateWithHistory(orderId, {
|
|
733
|
+
refunds: settled,
|
|
734
|
+
/*
|
|
735
|
+
* `becomeRefunded` only on a SUCCESSFUL settlement. Marking an order
|
|
736
|
+
* refunded on a failed gateway call would be the worst possible lie:
|
|
737
|
+
* terminal status, no money moved.
|
|
738
|
+
*/
|
|
739
|
+
...(becomeRefunded && outcome.status === "processed"
|
|
740
|
+
? {
|
|
741
|
+
status: "refunded",
|
|
742
|
+
/*
|
|
743
|
+
* 🛑 Clears the INTENT that `cancelOrder` wrote.
|
|
744
|
+
*
|
|
745
|
+
* `cancelOrder(…, refundAmount)` sets `refundStatus: "pending"`,
|
|
746
|
+
* and until 2026-10-21 nothing anywhere ever wrote a terminal
|
|
747
|
+
* value — so a successfully refunded cancellation kept reading
|
|
748
|
+
* `"pending"` forever. That is not cosmetic: D14's detector looks
|
|
749
|
+
* for cancelled orders that still owe money, and a flag stuck at
|
|
750
|
+
* `"pending"` would make every *successful* refund look like a
|
|
751
|
+
* failure.
|
|
752
|
+
*
|
|
753
|
+
* It also gives `issuePartialRefund`'s
|
|
754
|
+
* `refundStatus === COMPLETED` guard its first writer. That check
|
|
755
|
+
* has existed for a long time and could never fire, so a
|
|
756
|
+
* fully-refunded order could be submitted for another full refund
|
|
757
|
+
* and was stopped only by the cumulative cap one layer down.
|
|
758
|
+
*
|
|
759
|
+
* Only on `processed` + `isFull`, because those are the two facts
|
|
760
|
+
* that make "completed" true. A failed settlement deliberately
|
|
761
|
+
* leaves `"pending"` standing — that is the state the detector
|
|
762
|
+
* must still see.
|
|
763
|
+
*/
|
|
764
|
+
refundStatus: "completed",
|
|
765
|
+
}
|
|
766
|
+
: {}),
|
|
767
|
+
updatedAt: new Date(),
|
|
768
|
+
}, {
|
|
769
|
+
...ctx,
|
|
770
|
+
extraChanges: {
|
|
771
|
+
refund: {
|
|
772
|
+
from: null,
|
|
773
|
+
to: {
|
|
774
|
+
refundId,
|
|
775
|
+
type: target.type,
|
|
776
|
+
amount: target.amount,
|
|
777
|
+
reason: target.reason,
|
|
778
|
+
outcome: outcome.status,
|
|
779
|
+
},
|
|
780
|
+
},
|
|
781
|
+
},
|
|
782
|
+
actor: ctx?.actor ?? { uid: target.refundedBy, role: "admin" },
|
|
783
|
+
reason: ctx?.reason ?? target.reason,
|
|
784
|
+
}, "orderRepository.settleRefund", order);
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* 🛑 LEGACY — prefer `reserveRefund` + `settleRefund`.
|
|
788
|
+
*
|
|
789
|
+
* Kept because several non-gateway paths (manual refunds with no provider
|
|
790
|
+
* call, and the out-of-stock auto-refund) post a single terminal event with
|
|
791
|
+
* nothing to reserve against. Its read-then-write append is still a lost-
|
|
792
|
+
* update risk under concurrency (D6); do not route a new gateway refund
|
|
793
|
+
* through it.
|
|
794
|
+
*/
|
|
407
795
|
async postRefundEvent(orderId, event, becomeRefunded = false, ctx) {
|
|
408
796
|
const order = await this.findById(orderId);
|
|
409
797
|
if (!order)
|
|
@@ -594,21 +982,23 @@ class OrderRepository extends BaseRepository {
|
|
|
594
982
|
* once it's delivered (a seller-enabled `allowShipBeforeEmiComplete` item
|
|
595
983
|
* can be delivered long before EMI collection finishes).
|
|
596
984
|
*/
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
985
|
+
/*
|
|
986
|
+
* 🛑 `getEligibleForPayoutSweep()` was DELETED on 2026-10-21. Do not
|
|
987
|
+
* reinstate it.
|
|
988
|
+
*
|
|
989
|
+
* It filtered on `payoutStatus == "eligible" && status == "delivered"` and
|
|
990
|
+
* NOTHING ELSE — no time cutoff of any kind. `weeklyPayoutEligibility` (the
|
|
991
|
+
* Saturday job) called it, so the intended 7-day payout hold was not merely
|
|
992
|
+
* miscomputed, it was **unreachable as a floor**: the weekly sweep paid out
|
|
993
|
+
* an order delivered yesterday, every Saturday.
|
|
994
|
+
*
|
|
995
|
+
* Three payout paths existed and ONE hold was intended; it was honoured by
|
|
996
|
+
* zero of the three. The manual seller "Request payout"
|
|
997
|
+
* (`seller-actions.ts`) had no time check either. Collapsing to a single
|
|
998
|
+
* query is what makes the hold enforceable at all — a second entry point
|
|
999
|
+
* with its own predicate is how this happened, so the fix is one query, not
|
|
1000
|
+
* a cutoff added to two.
|
|
1001
|
+
*/
|
|
612
1002
|
/**
|
|
613
1003
|
* Cloud Functions: find auto-payout eligible delivered orders older than windowDays business days.
|
|
614
1004
|
* @param windowDays - number of business days since delivery
|
|
@@ -616,21 +1006,74 @@ class OrderRepository extends BaseRepository {
|
|
|
616
1006
|
*
|
|
617
1007
|
* See {@link getEligibleForPayoutSweep} for the EMI-incomplete exclusion rationale.
|
|
618
1008
|
*/
|
|
619
|
-
async getEligibleAutomatic(cutoff) {
|
|
1009
|
+
async getEligibleAutomatic(cutoff, opts) {
|
|
1010
|
+
/*
|
|
1011
|
+
* 🛑 NO DATE INEQUALITY IN THE QUERY, and this is the load-bearing
|
|
1012
|
+
* decision in the whole payout-hold fix. Both obvious shapes are wrong:
|
|
1013
|
+
*
|
|
1014
|
+
* (a) `.where(UPDATED_AT, "<=", cutoff)` — what this used to do (D29).
|
|
1015
|
+
* `updatedAt` is bumped by EVERY write, so a note, an `assignWorker`,
|
|
1016
|
+
* a `statusHistory` append or a tracking-number edit silently RESTARTS
|
|
1017
|
+
* the hold. The clock measured "time since anyone last touched this
|
|
1018
|
+
* order", not "time since delivery".
|
|
1019
|
+
*
|
|
1020
|
+
* (b) `.where(DELIVERY_DATE, "<=", cutoff)` — the obvious fix, and it
|
|
1021
|
+
* would pay out NOBODY, FOREVER, SILENTLY. A Firestore inequality
|
|
1022
|
+
* excludes every document that LACKS the field, and `deliveryDate`
|
|
1023
|
+
* had no writer at all until 2026-10-21 — so every order delivered
|
|
1024
|
+
* before today is missing it, the query returns zero rows, and
|
|
1025
|
+
* "no eligible orders" is indistinguishable from a working sweep with
|
|
1026
|
+
* nothing to do. That is Root Cause #100 exactly, which is the
|
|
1027
|
+
* incident where `orderBy("itemsSold")` returned 0 of 95 products and
|
|
1028
|
+
* the endpoint answered 200.
|
|
1029
|
+
*
|
|
1030
|
+
* So the query finds CANDIDATES on fields that certainly exist, bounded,
|
|
1031
|
+
* and the anchor test is a per-row predicate below. One query, no new
|
|
1032
|
+
* composite index, and no way to silently return nothing.
|
|
1033
|
+
*/
|
|
620
1034
|
const snap = await this.db
|
|
621
1035
|
.collection(this.collection)
|
|
622
1036
|
.where(ORDER_FIELDS.PAYOUT_STATUS, "==", "eligible")
|
|
623
1037
|
.where(ORDER_FIELDS.STATUS, "==", OrderStatusValues.DELIVERED)
|
|
624
|
-
.where(ORDER_FIELDS.UPDATED_AT, "<=", cutoff)
|
|
625
1038
|
.limit(500)
|
|
626
1039
|
.get();
|
|
627
|
-
|
|
1040
|
+
let missingAnchor = 0;
|
|
1041
|
+
const rows = snap.docs
|
|
628
1042
|
.map((d) => ({
|
|
629
1043
|
id: d.id,
|
|
630
1044
|
ref: d.ref,
|
|
631
1045
|
data: { id: d.id, ...d.data() },
|
|
632
1046
|
}))
|
|
633
|
-
.filter((o) => !o.data.emiEnabled || o.data.emiComplete)
|
|
1047
|
+
.filter((o) => !o.data.emiEnabled || o.data.emiComplete)
|
|
1048
|
+
.filter((o) => {
|
|
1049
|
+
/*
|
|
1050
|
+
* The anchor, with a conservative fallback. `deliveryDate` is the
|
|
1051
|
+
* truth; `updatedAt` is what every pre-2026-10-21 order has instead.
|
|
1052
|
+
*
|
|
1053
|
+
* Falling back is safe in the one direction that matters: `updatedAt`
|
|
1054
|
+
* is always >= `deliveryDate` (any write bumps it), so the fallback can
|
|
1055
|
+
* only ever WITHHOLD a payout longer than intended, never release one
|
|
1056
|
+
* early. Withholding is recoverable — the next sweep picks it up;
|
|
1057
|
+
* releasing early is not, because the money has gone.
|
|
1058
|
+
*/
|
|
1059
|
+
const anchor = toDateOrNull(o.data.deliveryDate) ?? toDateOrNull(o.data.updatedAt);
|
|
1060
|
+
if (!o.data.deliveryDate)
|
|
1061
|
+
missingAnchor++;
|
|
1062
|
+
if (!anchor)
|
|
1063
|
+
return false;
|
|
1064
|
+
return anchor.getTime() <= cutoff.getTime();
|
|
1065
|
+
});
|
|
1066
|
+
if (missingAnchor > 0) {
|
|
1067
|
+
/*
|
|
1068
|
+
* Visible, because the fallback is a transitional state and not the
|
|
1069
|
+
* design. As orders delivered after 2026-10-21 flow through, this count
|
|
1070
|
+
* should trend to zero; if it does not, something is still setting
|
|
1071
|
+
* `delivered` by a path that bypasses `updateWithHistory`'s anchor
|
|
1072
|
+
* stamp, and these sellers are being paid late for no stated reason.
|
|
1073
|
+
*/
|
|
1074
|
+
opts?.onWarn?.(`${missingAnchor} delivered order(s) have no deliveryDate — the payout hold fell back to updatedAt, which withholds longer than intended`, { missingAnchor, candidates: snap.size });
|
|
1075
|
+
}
|
|
1076
|
+
return rows;
|
|
634
1077
|
}
|
|
635
1078
|
/**
|
|
636
1079
|
* Cloud Functions: EMI reminder sweep — orders on an active (not yet fully
|