@mohasinac/appkit 4.59.0 → 4.60.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 +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.js +26 -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 +9 -4
- package/dist/client.js +26 -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 +42 -0
- package/dist/server.js +93 -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
|
@@ -31,8 +31,9 @@ export async function createReviewAction(input) {
|
|
|
31
31
|
// line translated it. Both now use the document's own name.
|
|
32
32
|
comment,
|
|
33
33
|
images: images ?? [],
|
|
34
|
-
verified
|
|
35
|
-
|
|
34
|
+
/* `verified` and `status` are set by `createReview` itself and are
|
|
35
|
+
omitted from `ReviewCreateInput` — passing them here duplicated the
|
|
36
|
+
repository default and was the reason the input type demanded them. */
|
|
36
37
|
});
|
|
37
38
|
});
|
|
38
39
|
}
|
|
@@ -134,7 +134,26 @@ export const PUBLIC_INDEX_SOURCES = [
|
|
|
134
134
|
* those words appeared was a facet nobody searches. Humanised, so
|
|
135
135
|
* "pre-order" and "digital code" read as words rather than slugs.
|
|
136
136
|
*/
|
|
137
|
-
|
|
137
|
+
/*
|
|
138
|
+
* Capitalised, not just de-slugged.
|
|
139
|
+
*
|
|
140
|
+
* This returned the lowercase value, so a shopper's result subtitle read
|
|
141
|
+
* "Beyblade · Victory Valkyrie Boost Variable · standard · new" — two
|
|
142
|
+
* database values sitting in a line of otherwise presentable text, which
|
|
143
|
+
* is the audit's "subtitles print raw field values".
|
|
144
|
+
*
|
|
145
|
+
* 🛑 Casing is FREE here, which is the only reason to do it in the index
|
|
146
|
+
* rather than at the renderer. `prepareIndex` matches through
|
|
147
|
+
* `normalizeSearchText`, which lowercases both the haystack and the
|
|
148
|
+
* query — so "Standard" is still found by typing "standard", and the
|
|
149
|
+
* searchability this field exists for (see the note above) is untouched.
|
|
150
|
+
* The alternative, a second display-only string per record, would grow
|
|
151
|
+
* every row of an index the client downloads.
|
|
152
|
+
*/
|
|
153
|
+
const human = (v) => {
|
|
154
|
+
const words = (str(v) ?? "").replace(/[-_]/g, " ").trim();
|
|
155
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : "";
|
|
156
|
+
};
|
|
138
157
|
return {
|
|
139
158
|
title,
|
|
140
159
|
// Leaf-first, so [0] is the most specific label.
|
|
@@ -4,8 +4,17 @@ export interface ReconcileShippingResult {
|
|
|
4
4
|
outcome: "credited" | "seller-absorbed" | "exact" | null;
|
|
5
5
|
sellerAbsorbs: number;
|
|
6
6
|
buyerPoints: number;
|
|
7
|
+
/**
|
|
8
|
+
* Rupees of postage the seller is owed back — `min(spent, charged)`.
|
|
9
|
+
*
|
|
10
|
+
* 🛑 Computed on BOTH branches, because today the platform keeps shipping in
|
|
11
|
+
* the quoted path too, so D45 is a fix for both rather than a new rule for
|
|
12
|
+
* one. Whether it is PAID is a separate gate: `isReimbursableShipment`
|
|
13
|
+
* refuses a Shiprocket-booked parcel, which we paid for ourselves.
|
|
14
|
+
*/
|
|
15
|
+
reimbursable: number;
|
|
7
16
|
/** Why nothing happened, when `outcome` is null. For logs and the UI. */
|
|
8
|
-
skipped?: "no-quote" | "already-reconciled" | "no-buyer";
|
|
17
|
+
skipped?: "no-quote" | "already-reconciled" | "no-buyer" | "awaiting-evidence";
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
11
20
|
* 🛑 IDEMPOTENT, and the guard is `order.shippingReconciliation` being unset.
|
|
@@ -29,7 +29,13 @@ import { serverLogger } from "../../../../monitoring/index.js";
|
|
|
29
29
|
export async function reconcileOrderShipping(orderId, actual) {
|
|
30
30
|
const order = await orderRepository.findById(orderId);
|
|
31
31
|
if (!order) {
|
|
32
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
outcome: null,
|
|
34
|
+
sellerAbsorbs: 0,
|
|
35
|
+
buyerPoints: 0,
|
|
36
|
+
reimbursable: 0,
|
|
37
|
+
skipped: "no-quote",
|
|
38
|
+
};
|
|
33
39
|
}
|
|
34
40
|
/*
|
|
35
41
|
* No quote means this order never went through the courier picker — a
|
|
@@ -39,13 +45,31 @@ export async function reconcileOrderShipping(orderId, actual) {
|
|
|
39
45
|
* against a number that was never a courier quote.
|
|
40
46
|
*/
|
|
41
47
|
if (!order.selectedShippingQuote) {
|
|
42
|
-
|
|
48
|
+
/*
|
|
49
|
+
* 🛑 THIS SKIP BRANCH IS THE D45 CASE, and that is why it is still a skip
|
|
50
|
+
* HERE rather than being handled inline.
|
|
51
|
+
*
|
|
52
|
+
* No quote means the seller shipped manually, so there is no quoted figure
|
|
53
|
+
* to measure a saving against — and the money question is a different one:
|
|
54
|
+
* what did the postage actually cost, proven by a receipt an admin has
|
|
55
|
+
* approved. That is an admin-triggered event, not a dispatch-triggered one,
|
|
56
|
+
* so it lives in `approveShippingReimbursement` below. Both write the SAME
|
|
57
|
+
* `shippingReconciliation` field and therefore share one idempotency key.
|
|
58
|
+
*/
|
|
59
|
+
return {
|
|
60
|
+
outcome: null,
|
|
61
|
+
sellerAbsorbs: 0,
|
|
62
|
+
buyerPoints: 0,
|
|
63
|
+
reimbursable: 0,
|
|
64
|
+
skipped: "no-quote",
|
|
65
|
+
};
|
|
43
66
|
}
|
|
44
67
|
if (order.shippingReconciliation) {
|
|
45
68
|
return {
|
|
46
69
|
outcome: null,
|
|
47
70
|
sellerAbsorbs: order.shippingReconciliation.sellerAbsorbs,
|
|
48
71
|
buyerPoints: order.shippingReconciliation.buyerPoints,
|
|
72
|
+
reimbursable: order.shippingReconciliation.reimbursable ?? 0,
|
|
49
73
|
skipped: "already-reconciled",
|
|
50
74
|
};
|
|
51
75
|
}
|
|
@@ -63,7 +87,13 @@ export async function reconcileOrderShipping(orderId, actual) {
|
|
|
63
87
|
orderId,
|
|
64
88
|
buyerPoints: result.buyerPoints,
|
|
65
89
|
});
|
|
66
|
-
return {
|
|
90
|
+
return {
|
|
91
|
+
outcome: null,
|
|
92
|
+
sellerAbsorbs: 0,
|
|
93
|
+
buyerPoints: 0,
|
|
94
|
+
reimbursable: 0,
|
|
95
|
+
skipped: "no-buyer",
|
|
96
|
+
};
|
|
67
97
|
}
|
|
68
98
|
await pointsRepository.append({
|
|
69
99
|
userId: buyerUid,
|
|
@@ -89,5 +119,6 @@ export async function reconcileOrderShipping(orderId, actual) {
|
|
|
89
119
|
outcome: result.buyerPoints > 0 ? "credited" : result.sellerAbsorbs > 0 ? "seller-absorbed" : "exact",
|
|
90
120
|
sellerAbsorbs: result.sellerAbsorbs,
|
|
91
121
|
buyerPoints: result.buyerPoints,
|
|
122
|
+
reimbursable: result.reimbursable,
|
|
92
123
|
};
|
|
93
124
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The self-shipped postage reimbursement — D45's other half.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is a separate module from `reconcile.ts`
|
|
5
|
+
*
|
|
6
|
+
* `reconcileOrderShipping` runs at DISPATCH, on an order that went through the
|
|
7
|
+
* courier picker, and compares a quote against a declared spend. This runs on
|
|
8
|
+
* an order with NO quote, is triggered by a seller submitting evidence and then
|
|
9
|
+
* by an admin approving it, and compares what the buyer paid against an
|
|
10
|
+
* approved receipt. Different trigger, different inputs, different actor.
|
|
11
|
+
*
|
|
12
|
+
* 🛑 But they write the SAME `shippingReconciliation` field, and that is
|
|
13
|
+
* deliberate: two records of "what shipping actually cost" on one order is the
|
|
14
|
+
* denormalised-mirror trap (Root Cause #42). Sharing the field also shares the
|
|
15
|
+
* idempotency key — its presence is what stops a double-approval paying twice,
|
|
16
|
+
* exactly as it stops a double-dispatch crediting points twice.
|
|
17
|
+
*
|
|
18
|
+
* ## The owner's rule, verbatim
|
|
19
|
+
*
|
|
20
|
+
* *"If seller ships on their own then they have to upload the receipt of the
|
|
21
|
+
* shipment with the tracking number so that it is paid to them directly; if
|
|
22
|
+
* they use our platform's integration (Shiprocket) to place the order, they do
|
|
23
|
+
* not get the money, the platform keeps it… The seller may set an initial value
|
|
24
|
+
* of shipping but the only approved amount will be given to them."*
|
|
25
|
+
*
|
|
26
|
+
* Plus: reimbursement **capped at what the buyer paid**, and the saving to the
|
|
27
|
+
* **buyer as points**.
|
|
28
|
+
*
|
|
29
|
+
* ## The invariant worth stating
|
|
30
|
+
*
|
|
31
|
+
* **The platform nets exactly zero on shipping in every case.**
|
|
32
|
+
*
|
|
33
|
+
* | buyer paid | approved receipt | seller gets | buyer gets | platform |
|
|
34
|
+
* |---|---|---|---|---|
|
|
35
|
+
* | ₹120 | ₹80 | ₹80 | ₹40 points | **₹0** |
|
|
36
|
+
* | ₹80 | ₹120 | ₹80 | — | **₹0** |
|
|
37
|
+
* | ₹100 | ₹100 | ₹100 | — | **₹0** |
|
|
38
|
+
*/
|
|
39
|
+
import type { ReconcileShippingResult } from "./reconcile";
|
|
40
|
+
export interface SubmitShippingEvidenceInput {
|
|
41
|
+
orderId: string;
|
|
42
|
+
/** What the seller says the postage cost. An ESTIMATE, not an entitlement. */
|
|
43
|
+
declaredAmount: number;
|
|
44
|
+
evidenceUrl?: string;
|
|
45
|
+
trackingNumber?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The seller submits postage evidence on a self-shipped order.
|
|
49
|
+
*
|
|
50
|
+
* ## 🛑 It records a CLAIM and moves nothing
|
|
51
|
+
*
|
|
52
|
+
* *"The seller may set an initial value of shipping but the only approved
|
|
53
|
+
* amount will be given to them."* So this writes `declaredAmount` and stops.
|
|
54
|
+
* The entitlement is `approvedAmount`, written only once a human has read the
|
|
55
|
+
* receipt — and they are separate fields rather than one that gets
|
|
56
|
+
* overwritten, because a seller who claims ₹120 and is approved ₹80 must leave
|
|
57
|
+
* both figures visible.
|
|
58
|
+
*
|
|
59
|
+
* ## 🛑 The platform-booked refusal is about WHO BOOKED, not about evidence
|
|
60
|
+
*
|
|
61
|
+
* `isReimbursableShipment` keys on the booking. Gating on "a receipt exists"
|
|
62
|
+
* would be the exploitable version: a seller could upload a receipt for a
|
|
63
|
+
* parcel we paid Shiprocket for and be reimbursed for our own spend.
|
|
64
|
+
*
|
|
65
|
+
* Refused HERE as well as at approval, so the seller is told at submission
|
|
66
|
+
* rather than after waiting for a review that could only ever decline. That is
|
|
67
|
+
* the dead-end shape avoided (Root Cause #56).
|
|
68
|
+
*/
|
|
69
|
+
export declare function submitShippingEvidence(input: SubmitShippingEvidenceInput): Promise<{
|
|
70
|
+
ok: true;
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
reason: string;
|
|
74
|
+
}>;
|
|
75
|
+
export interface ApproveShippingReimbursementInput {
|
|
76
|
+
orderId: string;
|
|
77
|
+
/** What an admin will actually pay. May differ from the declared amount. */
|
|
78
|
+
approvedAmount: number;
|
|
79
|
+
/** Why, when it differs from what was declared. */
|
|
80
|
+
note?: string;
|
|
81
|
+
/** 🛑 An admin uid. Never `"system"` — see below. */
|
|
82
|
+
approvedBy: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* An admin approves a postage reimbursement.
|
|
86
|
+
*
|
|
87
|
+
* ## 🛑 Never automatic, and the ABSENCE of a system path is the enforcement
|
|
88
|
+
*
|
|
89
|
+
* This moves money to a seller on the strength of a document somebody has to
|
|
90
|
+
* look at. There is no overload accepting `"system"`, and the guard below
|
|
91
|
+
* throws on it — the same shape and the same reason as
|
|
92
|
+
* `paymentCasesRepository.resolve()`. A rule enforced by a flag can be flipped;
|
|
93
|
+
* a rule enforced by the absence of a code path cannot.
|
|
94
|
+
*
|
|
95
|
+
* ## 🛑 The cap is `shippingFee`, NOT the quote — and getting that backwards is easy
|
|
96
|
+
*
|
|
97
|
+
* There is no `selectedShippingQuote` on this branch; its absence is what makes
|
|
98
|
+
* it this branch. The distinction between the two figures is real:
|
|
99
|
+
*
|
|
100
|
+
* · the QUOTE answers *"did the seller save money against what they told the
|
|
101
|
+
* buyer"* — the right basis for the dispatch-time comparison;
|
|
102
|
+
* · `shippingFee` answers *"what did the buyer actually hand over"* — the
|
|
103
|
+
* right basis for a cap, and the owner's stated one.
|
|
104
|
+
*
|
|
105
|
+
* Reading the quote here would be `undefined` and would cap everything at
|
|
106
|
+
* zero. Conversely, inventing a quote from `shippingFee` on the OTHER branch
|
|
107
|
+
* would compare a real spend against a number that was never a courier quote,
|
|
108
|
+
* which is exactly why that branch refuses to do it.
|
|
109
|
+
*
|
|
110
|
+
* ## The saving still goes to the buyer, because it is ONE rule for both branches
|
|
111
|
+
*
|
|
112
|
+
* A buyer's outcome must not depend on whether a courier picker happened to be
|
|
113
|
+
* involved in their order.
|
|
114
|
+
*/
|
|
115
|
+
export declare function approveShippingReimbursement(input: ApproveShippingReimbursementInput): Promise<ReconcileShippingResult & {
|
|
116
|
+
approved: number;
|
|
117
|
+
}>;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The self-shipped postage reimbursement — D45's other half.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is a separate module from `reconcile.ts`
|
|
5
|
+
*
|
|
6
|
+
* `reconcileOrderShipping` runs at DISPATCH, on an order that went through the
|
|
7
|
+
* courier picker, and compares a quote against a declared spend. This runs on
|
|
8
|
+
* an order with NO quote, is triggered by a seller submitting evidence and then
|
|
9
|
+
* by an admin approving it, and compares what the buyer paid against an
|
|
10
|
+
* approved receipt. Different trigger, different inputs, different actor.
|
|
11
|
+
*
|
|
12
|
+
* 🛑 But they write the SAME `shippingReconciliation` field, and that is
|
|
13
|
+
* deliberate: two records of "what shipping actually cost" on one order is the
|
|
14
|
+
* denormalised-mirror trap (Root Cause #42). Sharing the field also shares the
|
|
15
|
+
* idempotency key — its presence is what stops a double-approval paying twice,
|
|
16
|
+
* exactly as it stops a double-dispatch crediting points twice.
|
|
17
|
+
*
|
|
18
|
+
* ## The owner's rule, verbatim
|
|
19
|
+
*
|
|
20
|
+
* *"If seller ships on their own then they have to upload the receipt of the
|
|
21
|
+
* shipment with the tracking number so that it is paid to them directly; if
|
|
22
|
+
* they use our platform's integration (Shiprocket) to place the order, they do
|
|
23
|
+
* not get the money, the platform keeps it… The seller may set an initial value
|
|
24
|
+
* of shipping but the only approved amount will be given to them."*
|
|
25
|
+
*
|
|
26
|
+
* Plus: reimbursement **capped at what the buyer paid**, and the saving to the
|
|
27
|
+
* **buyer as points**.
|
|
28
|
+
*
|
|
29
|
+
* ## The invariant worth stating
|
|
30
|
+
*
|
|
31
|
+
* **The platform nets exactly zero on shipping in every case.**
|
|
32
|
+
*
|
|
33
|
+
* | buyer paid | approved receipt | seller gets | buyer gets | platform |
|
|
34
|
+
* |---|---|---|---|---|
|
|
35
|
+
* | ₹120 | ₹80 | ₹80 | ₹40 points | **₹0** |
|
|
36
|
+
* | ₹80 | ₹120 | ₹80 | — | **₹0** |
|
|
37
|
+
* | ₹100 | ₹100 | ₹100 | — | **₹0** |
|
|
38
|
+
*/
|
|
39
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
40
|
+
import { pointsRepository } from "../../../../features/points/repository/points.repository.js";
|
|
41
|
+
import { reconcileShipping, isReimbursableShipment, } from "../../../shared/shipping/quote.js";
|
|
42
|
+
import { serverLogger } from "../../../../monitoring/index.js";
|
|
43
|
+
/**
|
|
44
|
+
* The seller submits postage evidence on a self-shipped order.
|
|
45
|
+
*
|
|
46
|
+
* ## 🛑 It records a CLAIM and moves nothing
|
|
47
|
+
*
|
|
48
|
+
* *"The seller may set an initial value of shipping but the only approved
|
|
49
|
+
* amount will be given to them."* So this writes `declaredAmount` and stops.
|
|
50
|
+
* The entitlement is `approvedAmount`, written only once a human has read the
|
|
51
|
+
* receipt — and they are separate fields rather than one that gets
|
|
52
|
+
* overwritten, because a seller who claims ₹120 and is approved ₹80 must leave
|
|
53
|
+
* both figures visible.
|
|
54
|
+
*
|
|
55
|
+
* ## 🛑 The platform-booked refusal is about WHO BOOKED, not about evidence
|
|
56
|
+
*
|
|
57
|
+
* `isReimbursableShipment` keys on the booking. Gating on "a receipt exists"
|
|
58
|
+
* would be the exploitable version: a seller could upload a receipt for a
|
|
59
|
+
* parcel we paid Shiprocket for and be reimbursed for our own spend.
|
|
60
|
+
*
|
|
61
|
+
* Refused HERE as well as at approval, so the seller is told at submission
|
|
62
|
+
* rather than after waiting for a review that could only ever decline. That is
|
|
63
|
+
* the dead-end shape avoided (Root Cause #56).
|
|
64
|
+
*/
|
|
65
|
+
export async function submitShippingEvidence(input) {
|
|
66
|
+
const order = await orderRepository.findById(input.orderId);
|
|
67
|
+
if (!order)
|
|
68
|
+
return { ok: false, reason: "That order does not exist." };
|
|
69
|
+
if (!isReimbursableShipment(order)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
reason: "This parcel was booked and paid for through the platform's courier integration, so there is no postage to reimburse.",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (order.shippingReimbursement?.approvedAt) {
|
|
76
|
+
return { ok: false, reason: "A reimbursement has already been approved on this order." };
|
|
77
|
+
}
|
|
78
|
+
if (!Number.isFinite(input.declaredAmount) || input.declaredAmount < 0) {
|
|
79
|
+
return { ok: false, reason: "The declared postage needs to be a number." };
|
|
80
|
+
}
|
|
81
|
+
await orderRepository.update(input.orderId, {
|
|
82
|
+
shippingReimbursement: {
|
|
83
|
+
declaredAmount: input.declaredAmount,
|
|
84
|
+
...(input.evidenceUrl ? { evidenceUrl: input.evidenceUrl } : {}),
|
|
85
|
+
...(input.trackingNumber ? { trackingNumber: input.trackingNumber } : {}),
|
|
86
|
+
submittedAt: new Date(),
|
|
87
|
+
/*
|
|
88
|
+
* No approved values carried forward — there are none, because the
|
|
89
|
+
* `approvedAt` guard above refuses resubmission after approval. Before
|
|
90
|
+
* approval a seller may correct their claim freely, which is the right
|
|
91
|
+
* affordance: the alternative is a support ticket to fix a typo.
|
|
92
|
+
*/
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* An admin approves a postage reimbursement.
|
|
99
|
+
*
|
|
100
|
+
* ## 🛑 Never automatic, and the ABSENCE of a system path is the enforcement
|
|
101
|
+
*
|
|
102
|
+
* This moves money to a seller on the strength of a document somebody has to
|
|
103
|
+
* look at. There is no overload accepting `"system"`, and the guard below
|
|
104
|
+
* throws on it — the same shape and the same reason as
|
|
105
|
+
* `paymentCasesRepository.resolve()`. A rule enforced by a flag can be flipped;
|
|
106
|
+
* a rule enforced by the absence of a code path cannot.
|
|
107
|
+
*
|
|
108
|
+
* ## 🛑 The cap is `shippingFee`, NOT the quote — and getting that backwards is easy
|
|
109
|
+
*
|
|
110
|
+
* There is no `selectedShippingQuote` on this branch; its absence is what makes
|
|
111
|
+
* it this branch. The distinction between the two figures is real:
|
|
112
|
+
*
|
|
113
|
+
* · the QUOTE answers *"did the seller save money against what they told the
|
|
114
|
+
* buyer"* — the right basis for the dispatch-time comparison;
|
|
115
|
+
* · `shippingFee` answers *"what did the buyer actually hand over"* — the
|
|
116
|
+
* right basis for a cap, and the owner's stated one.
|
|
117
|
+
*
|
|
118
|
+
* Reading the quote here would be `undefined` and would cap everything at
|
|
119
|
+
* zero. Conversely, inventing a quote from `shippingFee` on the OTHER branch
|
|
120
|
+
* would compare a real spend against a number that was never a courier quote,
|
|
121
|
+
* which is exactly why that branch refuses to do it.
|
|
122
|
+
*
|
|
123
|
+
* ## The saving still goes to the buyer, because it is ONE rule for both branches
|
|
124
|
+
*
|
|
125
|
+
* A buyer's outcome must not depend on whether a courier picker happened to be
|
|
126
|
+
* involved in their order.
|
|
127
|
+
*/
|
|
128
|
+
export async function approveShippingReimbursement(input) {
|
|
129
|
+
const nothing = {
|
|
130
|
+
outcome: null,
|
|
131
|
+
sellerAbsorbs: 0,
|
|
132
|
+
buyerPoints: 0,
|
|
133
|
+
reimbursable: 0,
|
|
134
|
+
approved: 0,
|
|
135
|
+
};
|
|
136
|
+
if (!input.approvedBy || input.approvedBy === "system") {
|
|
137
|
+
/*
|
|
138
|
+
* Thrown, not returned. A caller that ignored an `{ ok: false }` here would
|
|
139
|
+
* have approved a payout with no human behind it, and that is not a
|
|
140
|
+
* condition to hand back as data for someone to forget to check.
|
|
141
|
+
*/
|
|
142
|
+
throw new Error("A shipping reimbursement must be approved by a named admin — there is no system approver.");
|
|
143
|
+
}
|
|
144
|
+
const order = await orderRepository.findById(input.orderId);
|
|
145
|
+
if (!order)
|
|
146
|
+
return { ...nothing, skipped: "no-quote" };
|
|
147
|
+
if (!isReimbursableShipment(order)) {
|
|
148
|
+
return { ...nothing, skipped: "no-quote" };
|
|
149
|
+
}
|
|
150
|
+
if (!order.shippingReimbursement) {
|
|
151
|
+
/*
|
|
152
|
+
* Its own skip reason rather than a silent zero: "the seller has not
|
|
153
|
+
* claimed yet" and "we decided nothing is owed" are different facts, and an
|
|
154
|
+
* operator surface has to be able to tell them apart.
|
|
155
|
+
*/
|
|
156
|
+
return { ...nothing, skipped: "awaiting-evidence" };
|
|
157
|
+
}
|
|
158
|
+
if (order.shippingReconciliation) {
|
|
159
|
+
return {
|
|
160
|
+
outcome: null,
|
|
161
|
+
sellerAbsorbs: order.shippingReconciliation.sellerAbsorbs,
|
|
162
|
+
buyerPoints: order.shippingReconciliation.buyerPoints,
|
|
163
|
+
reimbursable: order.shippingReconciliation.reimbursable ?? 0,
|
|
164
|
+
approved: order.shippingReimbursement.approvedAmount ?? 0,
|
|
165
|
+
skipped: "already-reconciled",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const chargedToBuyer = typeof order.shippingFee === "number" && Number.isFinite(order.shippingFee)
|
|
169
|
+
? Math.max(0, order.shippingFee)
|
|
170
|
+
: 0;
|
|
171
|
+
const spent = Math.max(0, input.approvedAmount);
|
|
172
|
+
/*
|
|
173
|
+
* The SAME pure function the quoted branch uses, fed the buyer-paid figure as
|
|
174
|
+
* the charged rate. One formula, both branches — which is the whole reason
|
|
175
|
+
* `reimbursable` was added to it rather than computed a second time here
|
|
176
|
+
* (Root Cause #75).
|
|
177
|
+
*
|
|
178
|
+
* The courier ids are a matched sentinel pair so `courierChanged` comes out
|
|
179
|
+
* false: on a self-shipped order the buyer never chose a courier, so "the
|
|
180
|
+
* seller changed it" is not a fact that exists.
|
|
181
|
+
*/
|
|
182
|
+
const result = reconcileShipping({ chargedRate: chargedToBuyer, courierId: "self-shipped" }, { actualRate: spent, courierId: "self-shipped", courierName: "Seller-arranged" });
|
|
183
|
+
if (result.buyerPoints > 0 && order.userId) {
|
|
184
|
+
await pointsRepository.append({
|
|
185
|
+
userId: order.userId,
|
|
186
|
+
kind: "shipping_saving",
|
|
187
|
+
amount: result.buyerPoints,
|
|
188
|
+
orderId: input.orderId,
|
|
189
|
+
/*
|
|
190
|
+
* Names both figures. "₹40 points" alone is unexplainable weeks later,
|
|
191
|
+
* and this string is written once and never re-derived, so it stays true
|
|
192
|
+
* of the moment it describes.
|
|
193
|
+
*/
|
|
194
|
+
description: `Shipping saving on ${input.orderId}: you paid ₹${chargedToBuyer} for postage, ` +
|
|
195
|
+
`the approved cost was ₹${spent}.`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
await orderRepository.update(input.orderId, {
|
|
199
|
+
shippingReimbursement: {
|
|
200
|
+
...order.shippingReimbursement,
|
|
201
|
+
approvedAmount: spent,
|
|
202
|
+
approvedBy: input.approvedBy,
|
|
203
|
+
approvedAt: new Date(),
|
|
204
|
+
...(input.note ? { approvalNote: input.note } : {}),
|
|
205
|
+
},
|
|
206
|
+
shippingReconciliation: { ...result, reconciledAt: new Date() },
|
|
207
|
+
});
|
|
208
|
+
serverLogger.info("shipping reimbursement approved", {
|
|
209
|
+
orderId: input.orderId,
|
|
210
|
+
declared: order.shippingReimbursement.declaredAmount,
|
|
211
|
+
approved: spent,
|
|
212
|
+
chargedToBuyer,
|
|
213
|
+
buyerPoints: result.buyerPoints,
|
|
214
|
+
approvedBy: input.approvedBy,
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
outcome: result.buyerPoints > 0 ? "credited" : result.sellerAbsorbs > 0 ? "seller-absorbed" : "exact",
|
|
218
|
+
sellerAbsorbs: result.sellerAbsorbs,
|
|
219
|
+
buyerPoints: result.buyerPoints,
|
|
220
|
+
reimbursable: result.reimbursable,
|
|
221
|
+
approved: spent,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
@@ -22,9 +22,24 @@ export interface ResolvedStoreTabs {
|
|
|
22
22
|
}
|
|
23
23
|
export declare function resolveStoreTabs(storeSlug: string, storeId: string | undefined, settings: SiteSettingsSnapshot): Promise<ResolvedStoreTabs>;
|
|
24
24
|
/**
|
|
25
|
-
* Where the bare `/stores/[slug]` route should land
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Where the bare `/stores/[slug]` route should land.
|
|
26
|
+
*
|
|
27
|
+
* Order: **About when the store has written one**, else the first
|
|
28
|
+
* listing-type tab holding anything, else Coupons, else Reviews, else About.
|
|
29
|
+
* `about` is always present in `tabs`, so this never falls through to nothing.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 About was promoted to first on 2026-10-21, deliberately. Previously the
|
|
32
|
+
* richest listing tab always won, so any stocked store sent every visitor
|
|
33
|
+
* straight to a filtered product grid — the storefront read as a search
|
|
34
|
+
* result, and a seller who had written an introduction was the only person who
|
|
35
|
+
* ever saw it.
|
|
36
|
+
*
|
|
37
|
+
* It is gated on the store ACTUALLY having About content rather than applied
|
|
38
|
+
* unconditionally: landing on an empty About page would be strictly worse than
|
|
39
|
+
* landing on products, so the old behaviour remains the fallback. The caller
|
|
40
|
+
* supplies the flag because this module takes a `storeId`, not the document,
|
|
41
|
+
* and giving it store-shape knowledge it does not otherwise need would make it
|
|
42
|
+
* a second place that decides what "has an About" means.
|
|
28
43
|
*/
|
|
29
|
-
export declare function getDefaultStoreTabPath(storeSlug: string, storeId: string | undefined, settings: SiteSettingsSnapshot): Promise<string>;
|
|
44
|
+
export declare function getDefaultStoreTabPath(storeSlug: string, storeId: string | undefined, settings: SiteSettingsSnapshot, hasAboutContent?: boolean): Promise<string>;
|
|
30
45
|
export {};
|
|
@@ -114,11 +114,31 @@ export async function resolveStoreTabs(storeSlug, storeId, settings) {
|
|
|
114
114
|
return { dropdownTabs, tabs, listingCounts, couponsCount, reviewsCount };
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
|
-
* Where the bare `/stores/[slug]` route should land
|
|
118
|
-
*
|
|
119
|
-
*
|
|
117
|
+
* Where the bare `/stores/[slug]` route should land.
|
|
118
|
+
*
|
|
119
|
+
* Order: **About when the store has written one**, else the first
|
|
120
|
+
* listing-type tab holding anything, else Coupons, else Reviews, else About.
|
|
121
|
+
* `about` is always present in `tabs`, so this never falls through to nothing.
|
|
122
|
+
*
|
|
123
|
+
* 🛑 About was promoted to first on 2026-10-21, deliberately. Previously the
|
|
124
|
+
* richest listing tab always won, so any stocked store sent every visitor
|
|
125
|
+
* straight to a filtered product grid — the storefront read as a search
|
|
126
|
+
* result, and a seller who had written an introduction was the only person who
|
|
127
|
+
* ever saw it.
|
|
128
|
+
*
|
|
129
|
+
* It is gated on the store ACTUALLY having About content rather than applied
|
|
130
|
+
* unconditionally: landing on an empty About page would be strictly worse than
|
|
131
|
+
* landing on products, so the old behaviour remains the fallback. The caller
|
|
132
|
+
* supplies the flag because this module takes a `storeId`, not the document,
|
|
133
|
+
* and giving it store-shape knowledge it does not otherwise need would make it
|
|
134
|
+
* a second place that decides what "has an About" means.
|
|
120
135
|
*/
|
|
121
|
-
export async function getDefaultStoreTabPath(storeSlug, storeId, settings) {
|
|
136
|
+
export async function getDefaultStoreTabPath(storeSlug, storeId, settings, hasAboutContent) {
|
|
122
137
|
const { dropdownTabs, tabs } = await resolveStoreTabs(storeSlug, storeId, settings);
|
|
138
|
+
if (hasAboutContent) {
|
|
139
|
+
const about = tabs.find((t) => t.value === "about");
|
|
140
|
+
if (about)
|
|
141
|
+
return about.href;
|
|
142
|
+
}
|
|
123
143
|
return (dropdownTabs[0] ?? tabs[0]).href;
|
|
124
144
|
}
|
|
@@ -19,6 +19,36 @@ interface GatewayInput {
|
|
|
19
19
|
readonly action?: string;
|
|
20
20
|
readonly [key: string]: unknown;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Recompute every category and store counter, on demand.
|
|
24
|
+
*
|
|
25
|
+
* `countersReconcile` already runs this nightly at 03:00 UTC, which is right
|
|
26
|
+
* for drift that accumulates on its own and wrong for drift you have just
|
|
27
|
+
* caused. A seed, a purge or a bulk import leaves the counters wrong
|
|
28
|
+
* immediately — and a catalogue that reads as empty until tomorrow morning is
|
|
29
|
+
* not an acceptable gap. Measured 2026-10-21: a demo purge left all 17 stocked
|
|
30
|
+
* categories reporting zero while their products were filed perfectly.
|
|
31
|
+
*
|
|
32
|
+
* 🛑 DELIBERATELY NOT IN `GATEWAY_HANDLERS`, and that is the whole reason it
|
|
33
|
+
* is defined separately rather than added to the multiplexer.
|
|
34
|
+
*
|
|
35
|
+
* The gateway runs at **120s**; this job's scheduled twin runs at **540s** and
|
|
36
|
+
* needs it — the category pass alone walks 4,699 rows. Behind the gateway the
|
|
37
|
+
* same handler would silently get a quarter of its budget and time out
|
|
38
|
+
* part-way, which on a reconciler means it stops after writing SOME counters
|
|
39
|
+
* and leaves the taxonomy half-corrected, with a 200 and no error.
|
|
40
|
+
*
|
|
41
|
+
* That is precisely Root Cause #85: one handler reachable by two entry points
|
|
42
|
+
* with different ceilings, where nothing at the call site says which one
|
|
43
|
+
* served it. The rule there was to align the budgets; here they cannot be
|
|
44
|
+
* aligned without raising the gateway for six unrelated handlers, so the job
|
|
45
|
+
* stays out of it instead.
|
|
46
|
+
*
|
|
47
|
+
* Matching `timeoutSeconds` and `memory` to the scheduled definition is the
|
|
48
|
+
* same discipline — cron and callable must run under identical limits, or a
|
|
49
|
+
* job that works nightly fails on demand for reasons no log explains.
|
|
50
|
+
*/
|
|
51
|
+
export declare const countersReconcileNow: import("./types").HttpsFunctionDefinition<void, import("../jobs").CountersReconcileNowResult>;
|
|
22
52
|
export declare const gateway: import("./types").HttpsFunctionDefinition<GatewayInput, unknown>;
|
|
23
|
-
export declare const HTTPS_FUNCTIONS: readonly [import("./types").HttpsFunctionDefinition<void, import("../jobs").AdminAnalyticsResult>, import("./types").HttpsFunctionDefinition<import("../jobs").StoreAnalyticsInput, import("../jobs").StoreAnalyticsResult>, import("./types").HttpsFunctionDefinition<void, import("../jobs").PromotionsCallableResult>, import("./types").HttpsFunctionDefinition<import("../jobs").ListingRequestBody, import("../jobs").ListingResponseBody>, import("./types").HttpsFunctionDefinition<import("../jobs").TriggerEventRaffleInput, import("../jobs").TriggerEventRaffleResult>, import("./types").HttpsFunctionDefinition<import("../jobs").AssignSpinPrizeInput, import("../jobs").AssignSpinPrizeResult>, import("./types").HttpsFunctionDefinition<GatewayInput, unknown>];
|
|
53
|
+
export declare const HTTPS_FUNCTIONS: readonly [import("./types").HttpsFunctionDefinition<void, import("../jobs").AdminAnalyticsResult>, import("./types").HttpsFunctionDefinition<import("../jobs").StoreAnalyticsInput, import("../jobs").StoreAnalyticsResult>, import("./types").HttpsFunctionDefinition<void, import("../jobs").PromotionsCallableResult>, import("./types").HttpsFunctionDefinition<import("../jobs").ListingRequestBody, import("../jobs").ListingResponseBody>, import("./types").HttpsFunctionDefinition<import("../jobs").TriggerEventRaffleInput, import("../jobs").TriggerEventRaffleResult>, import("./types").HttpsFunctionDefinition<import("../jobs").AssignSpinPrizeInput, import("../jobs").AssignSpinPrizeResult>, import("./types").HttpsFunctionDefinition<void, import("../jobs").CountersReconcileNowResult>, import("./types").HttpsFunctionDefinition<GatewayInput, unknown>];
|
|
24
54
|
export {};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* single URL so callers only manage one env var. It dispatches by the
|
|
10
10
|
* `action` field on the request body.
|
|
11
11
|
*/
|
|
12
|
-
import { adminAnalyticsHandler, assignSpinPrizeHandler, listingProcessorHandler, promotionsHandler, storeAnalyticsHandler, triggerEventRaffleHandler, } from "../jobs/handlers/index.js";
|
|
12
|
+
import { adminAnalyticsHandler, assignSpinPrizeHandler, countersReconcileNowHandler, listingProcessorHandler, promotionsHandler, storeAnalyticsHandler, triggerEventRaffleHandler, } from "../jobs/handlers/index.js";
|
|
13
13
|
import { defineFunction } from "./define.js";
|
|
14
14
|
const REGION = "asia-south1";
|
|
15
15
|
// Consumer-specific env var name — chosen at consumer install time, not appkit.
|
|
@@ -115,6 +115,56 @@ export const assignSpinPrize = defineFunction({
|
|
|
115
115
|
secretEnvVar: SECRET_ENV,
|
|
116
116
|
},
|
|
117
117
|
});
|
|
118
|
+
/**
|
|
119
|
+
* Recompute every category and store counter, on demand.
|
|
120
|
+
*
|
|
121
|
+
* `countersReconcile` already runs this nightly at 03:00 UTC, which is right
|
|
122
|
+
* for drift that accumulates on its own and wrong for drift you have just
|
|
123
|
+
* caused. A seed, a purge or a bulk import leaves the counters wrong
|
|
124
|
+
* immediately — and a catalogue that reads as empty until tomorrow morning is
|
|
125
|
+
* not an acceptable gap. Measured 2026-10-21: a demo purge left all 17 stocked
|
|
126
|
+
* categories reporting zero while their products were filed perfectly.
|
|
127
|
+
*
|
|
128
|
+
* 🛑 DELIBERATELY NOT IN `GATEWAY_HANDLERS`, and that is the whole reason it
|
|
129
|
+
* is defined separately rather than added to the multiplexer.
|
|
130
|
+
*
|
|
131
|
+
* The gateway runs at **120s**; this job's scheduled twin runs at **540s** and
|
|
132
|
+
* needs it — the category pass alone walks 4,699 rows. Behind the gateway the
|
|
133
|
+
* same handler would silently get a quarter of its budget and time out
|
|
134
|
+
* part-way, which on a reconciler means it stops after writing SOME counters
|
|
135
|
+
* and leaves the taxonomy half-corrected, with a 200 and no error.
|
|
136
|
+
*
|
|
137
|
+
* That is precisely Root Cause #85: one handler reachable by two entry points
|
|
138
|
+
* with different ceilings, where nothing at the call site says which one
|
|
139
|
+
* served it. The rule there was to align the budgets; here they cannot be
|
|
140
|
+
* aligned without raising the gateway for six unrelated handlers, so the job
|
|
141
|
+
* stays out of it instead.
|
|
142
|
+
*
|
|
143
|
+
* Matching `timeoutSeconds` and `memory` to the scheduled definition is the
|
|
144
|
+
* same discipline — cron and callable must run under identical limits, or a
|
|
145
|
+
* job that works nightly fails on demand for reasons no log explains.
|
|
146
|
+
*/
|
|
147
|
+
export const countersReconcileNow = defineFunction({
|
|
148
|
+
name: "countersReconcileNow",
|
|
149
|
+
description: "Recompute category + store counters on demand (after a seed, purge or bulk import).",
|
|
150
|
+
trigger: { kind: "https" },
|
|
151
|
+
handler: countersReconcileNowHandler,
|
|
152
|
+
options: {
|
|
153
|
+
region: REGION,
|
|
154
|
+
/* Identical to the `countersReconcile` schedule — see above. */
|
|
155
|
+
timeoutSeconds: 540,
|
|
156
|
+
memory: "256MiB",
|
|
157
|
+
/*
|
|
158
|
+
* 🛑 ONE instance. Two concurrent reconciles read the same products and
|
|
159
|
+
* write the same counters, so the loser's writes land on top of a tally
|
|
160
|
+
* computed from a different snapshot. The scheduled twin is also
|
|
161
|
+
* `maxInstances: 1`.
|
|
162
|
+
*/
|
|
163
|
+
maxInstances: 1,
|
|
164
|
+
cors: false,
|
|
165
|
+
secretEnvVar: SECRET_ENV,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
118
168
|
const GATEWAY_HANDLERS = {
|
|
119
169
|
listingProcessor: listingProcessorHandler,
|
|
120
170
|
adminAnalytics: adminAnalyticsHandler,
|
|
@@ -158,5 +208,6 @@ export const HTTPS_FUNCTIONS = [
|
|
|
158
208
|
listingProcessor,
|
|
159
209
|
triggerEventRaffle,
|
|
160
210
|
assignSpinPrize,
|
|
211
|
+
countersReconcileNow,
|
|
161
212
|
gateway,
|
|
162
213
|
];
|