@mohasinac/appkit 4.62.0 → 4.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
- package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
- package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
- package/dist/_internal/server/features/ads/booking.d.ts +118 -0
- package/dist/_internal/server/features/ads/booking.js +326 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +129 -9
- package/dist/_internal/server/features/checkout/actions.js +850 -79
- package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
- package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
- package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
- package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
- package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
- package/dist/_internal/server/features/dashboard/attention.js +183 -0
- package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
- package/dist/_internal/server/features/dashboard/data.js +82 -0
- package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
- package/dist/_internal/server/features/inventory/reservations.js +273 -0
- package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
- package/dist/_internal/server/features/invoicing/allocate.js +162 -0
- package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
- package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
- package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
- package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
- package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
- package/dist/_internal/server/features/invoicing/issue.js +202 -0
- package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -0
- package/dist/_internal/server/features/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +54 -0
- package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
- package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
- package/dist/_internal/server/features/orders/balance.d.ts +89 -0
- package/dist/_internal/server/features/orders/balance.js +256 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
- package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
- package/dist/_internal/server/features/points/award.js +46 -4
- package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -0
- package/dist/_internal/server/features/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
- package/dist/_internal/server/features/shipping/book-split.js +130 -0
- package/dist/_internal/server/features/shipping/book.d.ts +61 -0
- package/dist/_internal/server/features/shipping/book.js +176 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +264 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +29 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +65 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +95 -2
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
- package/dist/_internal/server/jobs/core/index.d.ts +2 -1
- package/dist/_internal/server/jobs/core/index.js +2 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
- package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
- package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +296 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
- package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
- package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
- package/dist/_internal/shared/checkout/order-math.js +159 -0
- package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
- package/dist/_internal/shared/checkout/refundable-base.js +13 -1
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
- package/dist/_internal/shared/checkout/split.d.ts +75 -0
- package/dist/_internal/shared/checkout/split.js +120 -0
- package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
- package/dist/_internal/shared/content/disclaimers.js +217 -0
- package/dist/_internal/shared/content/published-values.d.ts +93 -0
- package/dist/_internal/shared/content/published-values.js +206 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
- package/dist/_internal/shared/features/admin/integrations.js +263 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
- package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
- package/dist/_internal/shared/features/emi/choice.js +69 -0
- package/dist/_internal/shared/features/emi/group.d.ts +84 -0
- package/dist/_internal/shared/features/emi/group.js +126 -0
- package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
- package/dist/_internal/shared/features/ledger/posting.js +165 -0
- package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
- package/dist/_internal/shared/features/ledger/reports.js +127 -0
- package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
- package/dist/_internal/shared/features/ledger/rules.js +221 -0
- package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
- package/dist/_internal/shared/features/offers/coordination.js +80 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
- package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
- package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
- package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
- package/dist/_internal/shared/features/points/accepts.js +49 -0
- package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
- package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
- package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
- package/dist/_internal/shared/features/shipping/parcel.js +139 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
- package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/fees/calculator.d.ts +33 -1
- package/dist/_internal/shared/fees/calculator.js +32 -2
- package/dist/_internal/shared/fees/explain.d.ts +81 -0
- package/dist/_internal/shared/fees/explain.js +101 -0
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/index.js +1 -1
- package/dist/_internal/shared/history/types.d.ts +7 -1
- package/dist/_internal/shared/history/types.js +25 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.js +43 -0
- package/dist/constants/api-endpoints.d.ts +114 -0
- package/dist/constants/api-endpoints.js +55 -0
- package/dist/constants/field-names.d.ts +42 -0
- package/dist/constants/field-names.js +52 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/shipping.d.ts +45 -0
- package/dist/contracts/shipping.js +15 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOrdersView.js +47 -35
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +86 -5
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
- package/dist/features/admin/constants/filter-tabs.js +6 -0
- package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminListingData.js +16 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +62 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/ads/schemas/booking-write.d.ts +120 -0
- package/dist/features/ads/schemas/booking-write.js +75 -0
- package/dist/features/ads/schemas/creative-form.d.ts +18 -0
- package/dist/features/ads/schemas/creative-form.js +51 -0
- package/dist/features/ads/schemas/firestore.d.ts +140 -0
- package/dist/features/ads/schemas/firestore.js +109 -0
- package/dist/features/auctions/repository/bid.repository.js +3 -1
- package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
- package/dist/features/audit-log/schemas/firestore.js +89 -0
- package/dist/features/auth/actions/profile-actions.d.ts +69 -1
- package/dist/features/auth/actions/profile-actions.js +62 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/auth/schemas/firestore.d.ts +16 -2
- package/dist/features/auth/schemas/firestore.js +2 -1
- package/dist/features/blog/actions/blog-actions.d.ts +6 -0
- package/dist/features/blog/actions/blog-actions.js +17 -2
- package/dist/features/blog/api/route.js +4 -1
- package/dist/features/blog/columns/index.js +5 -0
- package/dist/features/blog/components/BlogIndexPageView.js +2 -1
- package/dist/features/blog/repository/blog.repository.d.ts +4 -0
- package/dist/features/blog/repository/blog.repository.js +5 -0
- package/dist/features/blog/schemas/blog-form.d.ts +3 -0
- package/dist/features/blog/schemas/blog-form.js +1 -0
- package/dist/features/blog/schemas/firestore.d.ts +38 -1
- package/dist/features/blog/schemas/firestore.js +23 -0
- package/dist/features/blog/schemas/index.d.ts +6 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
- package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
- package/dist/features/cart/components/PointsTenderPicker.js +15 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
- package/dist/features/cart/components/index.d.ts +2 -0
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +33 -0
- package/dist/features/cart/repository/cart.repository.js +47 -0
- package/dist/features/cart/schemas/firestore.d.ts +116 -0
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
- package/dist/features/dashboard/components/DashboardHubView.js +56 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- package/dist/features/faq/components/FAQPageContent.js +2 -2
- package/dist/features/gateway-events/schemas/index.d.ts +2 -2
- package/dist/features/homepage/schemas/index.d.ts +8 -8
- package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
- package/dist/features/inventory/schemas/stock-reservation.js +103 -0
- package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
- package/dist/features/invoicing/schemas/firestore.js +45 -0
- package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
- package/dist/features/invoicing/utils/financial-year.js +53 -0
- package/dist/features/jobs/actions/enqueue-job.js +18 -1
- package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
- package/dist/features/jobs/repository/jobs.repository.js +84 -1
- package/dist/features/jobs/schemas/firestore.d.ts +80 -0
- package/dist/features/jobs/schemas/firestore.js +37 -0
- package/dist/features/layout/AppLayoutShell.d.ts +9 -1
- package/dist/features/layout/AppLayoutShell.js +12 -4
- package/dist/features/layout/BottomActions.js +2 -1
- package/dist/features/layout/BottomNavbar.d.ts +10 -2
- package/dist/features/layout/SideActionDock.js +52 -2
- package/dist/features/layout/TitleBarLayout.js +13 -4
- package/dist/features/layout/TitleBarLayout.style.css +59 -0
- package/dist/features/layout/useCtaReveal.d.ts +16 -1
- package/dist/features/layout/useCtaReveal.js +17 -26
- package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
- package/dist/features/ledger/repository/journal.repository.js +312 -0
- package/dist/features/ledger/schemas/expenses.d.ts +87 -0
- package/dist/features/ledger/schemas/expenses.js +27 -0
- package/dist/features/ledger/schemas/firestore.d.ts +183 -0
- package/dist/features/ledger/schemas/firestore.js +69 -0
- package/dist/features/lottery/schemas/zod.d.ts +6 -6
- package/dist/features/media/MediaImage.js +22 -1
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/obligations/constants/policies.js +24 -1
- package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
- package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
- package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
- package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
- package/dist/features/orders/components/RefundRequestView.js +19 -1
- package/dist/features/orders/components/index.d.ts +4 -0
- package/dist/features/orders/components/index.js +2 -0
- package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
- package/dist/features/orders/constants/order-lane-tabs.js +88 -0
- package/dist/features/orders/repository/orders.repository.d.ts +85 -1
- package/dist/features/orders/repository/orders.repository.js +205 -6
- package/dist/features/orders/schemas/balance.d.ts +40 -0
- package/dist/features/orders/schemas/balance.js +47 -0
- package/dist/features/orders/schemas/firestore.d.ts +398 -1
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +56 -8
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +47 -0
- package/dist/features/orders/utils/courier-block.d.ts +25 -0
- package/dist/features/orders/utils/courier-block.js +23 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
- package/dist/features/payment-cases/schemas/firestore.js +1 -0
- package/dist/features/payments/repository/payout.repository.js +3 -2
- package/dist/features/payments/schemas/firestore.d.ts +92 -1
- package/dist/features/payments/schemas/firestore.js +23 -0
- package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
- package/dist/features/pricing/components/AdminPricingView.js +157 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/schemas/firestore.d.ts +26 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
- package/dist/features/promotions/actions/coupon-stacking.js +21 -2
- package/dist/features/promotions/components/CouponCard.js +13 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
- package/dist/features/promotions/constants/coupon-help.js +9 -1
- package/dist/features/promotions/schemas/firestore.d.ts +24 -0
- package/dist/features/promotions/schemas/firestore.js +4 -0
- package/dist/features/promotions/types/index.d.ts +19 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +2 -1
- package/dist/features/scams/schemas/firestore.js +2 -1
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/offer-actions.js +56 -0
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +37 -3
- package/dist/features/seller/components/DispatchForm.d.ts +11 -0
- package/dist/features/seller/components/DispatchForm.js +190 -0
- package/dist/features/seller/components/SellerOrdersView.js +221 -27
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/seller/repository/offer.repository.js +4 -3
- package/dist/features/seller/schemas/firestore.d.ts +23 -2
- package/dist/features/seller/schemas/firestore.js +3 -2
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +61 -4
- package/dist/features/seller/schemas/order-forms.js +41 -8
- package/dist/features/shipments/schemas/firestore.d.ts +25 -0
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
- package/dist/features/stores/repository/store.repository.d.ts +18 -0
- package/dist/features/stores/repository/store.repository.js +27 -1
- package/dist/features/stores/schemas/firestore.d.ts +112 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/repository/support.repository.js +72 -6
- package/dist/features/support/schemas/firestore.d.ts +17 -0
- package/dist/features/support/schemas/index.d.ts +8 -8
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +33 -1
- package/dist/next/routing/route-map.d.ts +112 -0
- package/dist/next/routing/route-map.js +63 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +69 -34
- package/dist/schemas/sieve.d.ts +2 -2
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +36 -1
- package/dist/server.js +183 -1
- package/dist/styles.css +412 -80
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -0
- package/dist/tokens/themes/default-light.js +1 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +1 -0
- package/dist/tokens/tokens.css +15 -0
- package/dist/ui/components/Button.d.ts +10 -1
- package/dist/ui/components/Button.js +25 -3
- package/dist/ui/components/Chart.d.ts +63 -0
- package/dist/ui/components/Chart.js +169 -0
- package/dist/ui/components/Chart.style.css +214 -0
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +83 -0
- package/dist/ui/components/OverflowRow.js +54 -2
- package/dist/ui/components/OverflowRow.style.css +38 -0
- package/dist/ui/components/RichTextEditor.js +99 -8
- package/dist/ui/components/chart-spec.d.ts +68 -0
- package/dist/ui/components/chart-spec.js +140 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/surface-tokens.d.ts +1 -1
- package/dist/ui/components/surface-tokens.js +9 -0
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/components/toolbar-collapse.d.ts +13 -0
- package/dist/ui/components/toolbar-collapse.js +10 -1
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/rich-text/RichText.d.ts +10 -1
- package/dist/ui/rich-text/RichText.js +33 -2
- package/dist/ui/rich-text/RichText.style.css +83 -79
- package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
- package/dist/ui/rich-text/RichTextRenderer.js +34 -2
- package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
- package/dist/ui/rich-text/chart-blocks.js +87 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1834 -1822
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A HOLD on stock that is not yet an order.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 Why this has to exist before S5b (Part 6)
|
|
5
|
+
*
|
|
6
|
+
* The plan calls this "the largest hidden cost" of the shared-purchase work,
|
|
7
|
+
* and the reason is that today there is no way to hold stock at all. The only
|
|
8
|
+
* mechanism is: `onOrderCreate` decrements `availableQuantity`, and
|
|
9
|
+
* `pendingOrderTimeout` puts it back if the buyer does not pay inside the
|
|
10
|
+
* 15-minute window. Both are keyed to an ORDER.
|
|
11
|
+
*
|
|
12
|
+
* A shared purchase runs for DAYS while co-buyers pay. With no primitive,
|
|
13
|
+
* either the coordinator places an order up front — which starts a 15-minute
|
|
14
|
+
* payment clock on a multi-day agreement — or nothing is held, and the item is
|
|
15
|
+
* sold out from under the arrangement by an ordinary buyer who happened to
|
|
16
|
+
* check out first. Neither is a product.
|
|
17
|
+
*
|
|
18
|
+
* ## The invariant, and where it already lives
|
|
19
|
+
*
|
|
20
|
+
* `products.repository.ts` already states it:
|
|
21
|
+
*
|
|
22
|
+
* reserved = stockQuantity - availableQuantity
|
|
23
|
+
*
|
|
24
|
+
* …as the comment on `resolveStockResync` puts it, *"the difference between
|
|
25
|
+
* the two IS the outstanding reservation"*. This collection does not invent a
|
|
26
|
+
* new idea; it makes that difference ADDRESSABLE — every held unit gets a row
|
|
27
|
+
* saying who holds it and until when, instead of being an anonymous gap
|
|
28
|
+
* between two numbers that only an order can close.
|
|
29
|
+
*
|
|
30
|
+
* 🛑 So a reservation MUST move `availableQuantity` in the same transaction
|
|
31
|
+
* that writes its row. A row without the decrement holds nothing; a decrement
|
|
32
|
+
* without a row is the anonymous gap again, and nothing can expire it.
|
|
33
|
+
*
|
|
34
|
+
* ## Why a TTL rather than a cancel button
|
|
35
|
+
*
|
|
36
|
+
* A hold with no expiry is a leak: the holder closes the tab, and those units
|
|
37
|
+
* are off the shelf until a human notices. `expiresAt` means the worst case is
|
|
38
|
+
* bounded by the sweep interval, and the seller never has to ask anyone to
|
|
39
|
+
* release anything.
|
|
40
|
+
*/
|
|
41
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
42
|
+
export declare const STOCK_RESERVATION_COLLECTION = "stockReservations";
|
|
43
|
+
/**
|
|
44
|
+
* Who is holding, and therefore what releases it.
|
|
45
|
+
*
|
|
46
|
+
* 🛑 A plain `holderId: string` was the first draft and it is wrong: the sweep
|
|
47
|
+
* and the release path both need to know WHICH system owns the hold before
|
|
48
|
+
* they can decide whether it is still legitimate. A discriminator makes
|
|
49
|
+
* "release every hold belonging to arrangement X" expressible; an opaque id
|
|
50
|
+
* makes it a string-prefix convention nobody enforces.
|
|
51
|
+
*/
|
|
52
|
+
export declare const RESERVATION_HOLDER_KINDS: readonly ["shared-purchase", "manual"];
|
|
53
|
+
export type ReservationHolderKind = (typeof RESERVATION_HOLDER_KINDS)[number];
|
|
54
|
+
/**
|
|
55
|
+
* 🛑 `cart` and `order` are deliberately NOT holder kinds.
|
|
56
|
+
*
|
|
57
|
+
* A cart holds nothing today — adding an item does not decrement anything —
|
|
58
|
+
* and making it hold would silently turn every abandoned cart into held
|
|
59
|
+
* stock. An order already has its own hold through `onOrderCreate` +
|
|
60
|
+
* `pendingOrderTimeout`; a second mechanism over the same units would
|
|
61
|
+
* double-decrement, which is exactly the class of bug `resolveStockResync`'s
|
|
62
|
+
* delta exists to prevent. Add a kind only when the thing that owns it also
|
|
63
|
+
* owns its release.
|
|
64
|
+
*/
|
|
65
|
+
export declare const RESERVATION_STATUSES: readonly ["held", "consumed", "released", "expired"];
|
|
66
|
+
export type ReservationStatus = (typeof RESERVATION_STATUSES)[number];
|
|
67
|
+
export interface StockReservationDocument extends BaseDocument {
|
|
68
|
+
productId: string;
|
|
69
|
+
/** Denormalised so the sweep can restore stock without reading the product first. */
|
|
70
|
+
quantity: number;
|
|
71
|
+
holderKind: ReservationHolderKind;
|
|
72
|
+
/** The arrangement / manual-hold id. Scoped by `holderKind`, never globally unique. */
|
|
73
|
+
holderId: string;
|
|
74
|
+
/** Who asked for it — an Auth uid, for the audit trail and for admin release. */
|
|
75
|
+
createdBy: string;
|
|
76
|
+
status: ReservationStatus;
|
|
77
|
+
/**
|
|
78
|
+
* 🛑 The whole point. A hold with no expiry is a leak: the holder closes the
|
|
79
|
+
* tab and the units are gone until a human notices.
|
|
80
|
+
*/
|
|
81
|
+
expiresAt: Date;
|
|
82
|
+
/**
|
|
83
|
+
* Set when the hold ENDS, whichever way it ended. One field rather than
|
|
84
|
+
* three (`consumedAt`/`releasedAt`/`expiredAt`) because `status` already
|
|
85
|
+
* says which, and three nullable dates is three chances for the one that
|
|
86
|
+
* matters to be the one nobody set.
|
|
87
|
+
*/
|
|
88
|
+
endedAt?: Date;
|
|
89
|
+
/** Free text on a manual release — why an admin took the hold back. */
|
|
90
|
+
endedReason?: string;
|
|
91
|
+
}
|
|
92
|
+
/** Default hold, when a caller names none. */
|
|
93
|
+
export declare const RESERVATION_DEFAULT_TTL_HOURS = 72;
|
|
94
|
+
/**
|
|
95
|
+
* 🛑 A ceiling, and it is load-bearing rather than defensive.
|
|
96
|
+
*
|
|
97
|
+
* A caller may pass any TTL it likes, which makes "hold this for a year" one
|
|
98
|
+
* typo away — and a year-long hold is indistinguishable from permanently
|
|
99
|
+
* deleted stock, because nothing in the UI shows a seller why their own
|
|
100
|
+
* product is unbuyable. 30 days is longer than any agreement window Part 6
|
|
101
|
+
* contemplates and short enough that a mistake self-heals.
|
|
102
|
+
*/
|
|
103
|
+
export declare const RESERVATION_MAX_TTL_HOURS: number;
|
|
104
|
+
/** A reservation is only holding stock while it is `held` and unexpired. */
|
|
105
|
+
export declare function isReservationActive(reservation: Pick<StockReservationDocument, "status" | "expiresAt">, now?: Date): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Firestore hands back a `Timestamp`, a `Date` or an ISO string depending on
|
|
108
|
+
* the path a document took to get here, and the adapter that normalises those
|
|
109
|
+
* is not reachable from this file. Returning `null` on anything unparseable is
|
|
110
|
+
* the honest answer — and callers treat an unparseable expiry as NOT active,
|
|
111
|
+
* so the failure mode is a hold released early rather than one held forever.
|
|
112
|
+
*/
|
|
113
|
+
declare function toDate(value: unknown): Date | null;
|
|
114
|
+
export { toDate as reservationExpiryDate };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A HOLD on stock that is not yet an order.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 Why this has to exist before S5b (Part 6)
|
|
5
|
+
*
|
|
6
|
+
* The plan calls this "the largest hidden cost" of the shared-purchase work,
|
|
7
|
+
* and the reason is that today there is no way to hold stock at all. The only
|
|
8
|
+
* mechanism is: `onOrderCreate` decrements `availableQuantity`, and
|
|
9
|
+
* `pendingOrderTimeout` puts it back if the buyer does not pay inside the
|
|
10
|
+
* 15-minute window. Both are keyed to an ORDER.
|
|
11
|
+
*
|
|
12
|
+
* A shared purchase runs for DAYS while co-buyers pay. With no primitive,
|
|
13
|
+
* either the coordinator places an order up front — which starts a 15-minute
|
|
14
|
+
* payment clock on a multi-day agreement — or nothing is held, and the item is
|
|
15
|
+
* sold out from under the arrangement by an ordinary buyer who happened to
|
|
16
|
+
* check out first. Neither is a product.
|
|
17
|
+
*
|
|
18
|
+
* ## The invariant, and where it already lives
|
|
19
|
+
*
|
|
20
|
+
* `products.repository.ts` already states it:
|
|
21
|
+
*
|
|
22
|
+
* reserved = stockQuantity - availableQuantity
|
|
23
|
+
*
|
|
24
|
+
* …as the comment on `resolveStockResync` puts it, *"the difference between
|
|
25
|
+
* the two IS the outstanding reservation"*. This collection does not invent a
|
|
26
|
+
* new idea; it makes that difference ADDRESSABLE — every held unit gets a row
|
|
27
|
+
* saying who holds it and until when, instead of being an anonymous gap
|
|
28
|
+
* between two numbers that only an order can close.
|
|
29
|
+
*
|
|
30
|
+
* 🛑 So a reservation MUST move `availableQuantity` in the same transaction
|
|
31
|
+
* that writes its row. A row without the decrement holds nothing; a decrement
|
|
32
|
+
* without a row is the anonymous gap again, and nothing can expire it.
|
|
33
|
+
*
|
|
34
|
+
* ## Why a TTL rather than a cancel button
|
|
35
|
+
*
|
|
36
|
+
* A hold with no expiry is a leak: the holder closes the tab, and those units
|
|
37
|
+
* are off the shelf until a human notices. `expiresAt` means the worst case is
|
|
38
|
+
* bounded by the sweep interval, and the seller never has to ask anyone to
|
|
39
|
+
* release anything.
|
|
40
|
+
*/
|
|
41
|
+
export const STOCK_RESERVATION_COLLECTION = "stockReservations";
|
|
42
|
+
/**
|
|
43
|
+
* Who is holding, and therefore what releases it.
|
|
44
|
+
*
|
|
45
|
+
* 🛑 A plain `holderId: string` was the first draft and it is wrong: the sweep
|
|
46
|
+
* and the release path both need to know WHICH system owns the hold before
|
|
47
|
+
* they can decide whether it is still legitimate. A discriminator makes
|
|
48
|
+
* "release every hold belonging to arrangement X" expressible; an opaque id
|
|
49
|
+
* makes it a string-prefix convention nobody enforces.
|
|
50
|
+
*/
|
|
51
|
+
export const RESERVATION_HOLDER_KINDS = ["shared-purchase", "manual"];
|
|
52
|
+
/**
|
|
53
|
+
* 🛑 `cart` and `order` are deliberately NOT holder kinds.
|
|
54
|
+
*
|
|
55
|
+
* A cart holds nothing today — adding an item does not decrement anything —
|
|
56
|
+
* and making it hold would silently turn every abandoned cart into held
|
|
57
|
+
* stock. An order already has its own hold through `onOrderCreate` +
|
|
58
|
+
* `pendingOrderTimeout`; a second mechanism over the same units would
|
|
59
|
+
* double-decrement, which is exactly the class of bug `resolveStockResync`'s
|
|
60
|
+
* delta exists to prevent. Add a kind only when the thing that owns it also
|
|
61
|
+
* owns its release.
|
|
62
|
+
*/
|
|
63
|
+
export const RESERVATION_STATUSES = ["held", "consumed", "released", "expired"];
|
|
64
|
+
/** Default hold, when a caller names none. */
|
|
65
|
+
export const RESERVATION_DEFAULT_TTL_HOURS = 72;
|
|
66
|
+
/**
|
|
67
|
+
* 🛑 A ceiling, and it is load-bearing rather than defensive.
|
|
68
|
+
*
|
|
69
|
+
* A caller may pass any TTL it likes, which makes "hold this for a year" one
|
|
70
|
+
* typo away — and a year-long hold is indistinguishable from permanently
|
|
71
|
+
* deleted stock, because nothing in the UI shows a seller why their own
|
|
72
|
+
* product is unbuyable. 30 days is longer than any agreement window Part 6
|
|
73
|
+
* contemplates and short enough that a mistake self-heals.
|
|
74
|
+
*/
|
|
75
|
+
export const RESERVATION_MAX_TTL_HOURS = 24 * 30;
|
|
76
|
+
/** A reservation is only holding stock while it is `held` and unexpired. */
|
|
77
|
+
export function isReservationActive(reservation, now = new Date()) {
|
|
78
|
+
if (reservation.status !== "held")
|
|
79
|
+
return false;
|
|
80
|
+
const expires = toDate(reservation.expiresAt);
|
|
81
|
+
return expires !== null && expires.getTime() > now.getTime();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Firestore hands back a `Timestamp`, a `Date` or an ISO string depending on
|
|
85
|
+
* the path a document took to get here, and the adapter that normalises those
|
|
86
|
+
* is not reachable from this file. Returning `null` on anything unparseable is
|
|
87
|
+
* the honest answer — and callers treat an unparseable expiry as NOT active,
|
|
88
|
+
* so the failure mode is a hold released early rather than one held forever.
|
|
89
|
+
*/
|
|
90
|
+
function toDate(value) {
|
|
91
|
+
if (value instanceof Date)
|
|
92
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
93
|
+
if (value && typeof value.toDate === "function") {
|
|
94
|
+
const d = value.toDate();
|
|
95
|
+
return d instanceof Date && !Number.isNaN(d.getTime()) ? d : null;
|
|
96
|
+
}
|
|
97
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
98
|
+
const d = new Date(value);
|
|
99
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
export { toDate as reservationExpiryDate };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoicing — the two gapless series (Phase 7, D12)
|
|
3
|
+
*
|
|
4
|
+
* ## Why two series and not one field
|
|
5
|
+
*
|
|
6
|
+
* **A GST invoice is issued once and must not change.** So a partial
|
|
7
|
+
* cancellation or refund gets a CREDIT NOTE with its own number referencing
|
|
8
|
+
* the invoice it adjusts — never a re-issued invoice. That is the whole reason
|
|
9
|
+
* `creditNoteNumber` is a second series rather than a revision suffix.
|
|
10
|
+
*
|
|
11
|
+
* ## 🛑 Gapless means the counter CANNOT be sharded
|
|
12
|
+
*
|
|
13
|
+
* Firestore's ~1 write/sec/document ceiling applies to a counter doc, and the
|
|
14
|
+
* usual answer is to shard it. That answer is unavailable here: a sharded
|
|
15
|
+
* counter cannot be gapless, which is the entire requirement. At this volume
|
|
16
|
+
* the ceiling is irrelevant; the note exists so nobody "optimises" it later
|
|
17
|
+
* and silently breaks the one property an auditor checks.
|
|
18
|
+
*
|
|
19
|
+
* ## 🛑 The FINANCIAL YEAR is the riskiest line in the phase
|
|
20
|
+
*
|
|
21
|
+
* The series is `LIR/26-27/0001` (decision 21), resetting on 1 April. So the
|
|
22
|
+
* allocating transaction must key on the FY — if it keys on anything else, two
|
|
23
|
+
* years share one counter and NEITHER series is gapless. And a number cannot
|
|
24
|
+
* be withdrawn without leaving a gap, so this is not a mistake that can be
|
|
25
|
+
* corrected after the fact.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* ## 🛑 OUTSTANDING — both collections need a PRESERVE entry in the tester tier
|
|
29
|
+
*
|
|
30
|
+
* `invoiceCounters` and `invoiceAllocations` are in **no tier**, and
|
|
31
|
+
* `assertDeletable` THROWS on an unlisted collection name *mid-run* — which
|
|
32
|
+
* reads as a harness fault rather than a classification gap. That is the exact
|
|
33
|
+
* latent trip `gatewayOrderClaims` sat on, and these two are worse: a wiped
|
|
34
|
+
* counter would restart a series at 1 and re-issue numbers that already exist
|
|
35
|
+
* on real invoices, which is unrecoverable.
|
|
36
|
+
*
|
|
37
|
+
* The entry belongs in `tester/scripts/lib/collections.mjs`'s PRESERVE list,
|
|
38
|
+
* beside `payoutMethods` and `adminAuditLog`. It is not in this change because
|
|
39
|
+
* **the `tester` submodule is not initialised in this worktree** (`git
|
|
40
|
+
* submodule status` shows it uninitialised), so the file does not exist here to
|
|
41
|
+
* edit. Recorded rather than silently skipped.
|
|
42
|
+
*
|
|
43
|
+
* 🛑 And they must get **no `SEED_COLLECTION_PURPOSE` entry**:
|
|
44
|
+
* `audit-seed-purpose` R1 requires that registry to match `COLLECTION_MAP` in
|
|
45
|
+
* BOTH directions, so a key for a collection the seeder does not write is a
|
|
46
|
+
* DEAD entry and fails the audit. Neither of these is ever seeded — they are
|
|
47
|
+
* written only by allocation. The same mistake was made with `refundClaims`
|
|
48
|
+
* and reverted.
|
|
49
|
+
*/
|
|
50
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
51
|
+
export declare const INVOICE_COUNTER_COLLECTION = "invoiceCounters";
|
|
52
|
+
/** The two independent series. */
|
|
53
|
+
export type InvoiceSeriesKind = "invoice" | "credit_note";
|
|
54
|
+
/**
|
|
55
|
+
* One counter per (series, financial year).
|
|
56
|
+
*
|
|
57
|
+
* The document id IS `${kind}_${fy}` — e.g. `invoice_26-27` — so the
|
|
58
|
+
* transaction that allocates a number needs no query, and two series in the
|
|
59
|
+
* same year cannot contend with each other.
|
|
60
|
+
*/
|
|
61
|
+
export interface InvoiceCounterDocument extends BaseDocument {
|
|
62
|
+
kind: InvoiceSeriesKind;
|
|
63
|
+
/** `"26-27"` — the FY label that appears in the number itself. */
|
|
64
|
+
fy: string;
|
|
65
|
+
/**
|
|
66
|
+
* The last number ISSUED. The next allocation is `lastNumber + 1`.
|
|
67
|
+
*
|
|
68
|
+
* 🛑 Last-issued rather than next-to-issue, deliberately: a counter created
|
|
69
|
+
* at 0 means "nothing issued yet", which is true and self-evident, whereas a
|
|
70
|
+
* next-to-issue counter created at 0 would mint `LIR/26-27/0000`.
|
|
71
|
+
*/
|
|
72
|
+
lastNumber: number;
|
|
73
|
+
/** When this counter last allocated — for the operator, never read by code. */
|
|
74
|
+
lastIssuedAt?: Date;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A record of every number allocated, keyed on what it was allocated FOR.
|
|
78
|
+
*
|
|
79
|
+
* ## 🛑 This is the idempotency claim, and it is why a number cannot be issued
|
|
80
|
+
* twice for one order
|
|
81
|
+
*
|
|
82
|
+
* `onOrderStatusChange` can fire more than once, and **two numbers for one
|
|
83
|
+
* order is the single unrecoverable failure in this phase** — the spare number
|
|
84
|
+
* is a permanent gap that cannot be explained to an auditor and cannot be
|
|
85
|
+
* reclaimed.
|
|
86
|
+
*
|
|
87
|
+
* So allocation is a `.create()` on `invoiceAllocations/{subjectId}` INSIDE
|
|
88
|
+
* the same transaction that bumps the counter. `.create()` fails if the
|
|
89
|
+
* document exists, which makes "already allocated" a transaction-level fact
|
|
90
|
+
* rather than a read-then-write race. It is the same primitive the refund
|
|
91
|
+
* claim, the gateway-event inbox and the gateway-order claim all use.
|
|
92
|
+
*/
|
|
93
|
+
export declare const INVOICE_ALLOCATION_COLLECTION = "invoiceAllocations";
|
|
94
|
+
export interface InvoiceAllocationDocument extends BaseDocument {
|
|
95
|
+
kind: InvoiceSeriesKind;
|
|
96
|
+
/**
|
|
97
|
+
* What this number was issued for.
|
|
98
|
+
*
|
|
99
|
+
* 🛑 For an invoice this is the ORDER id, so one order can only ever hold
|
|
100
|
+
* one invoice number. For a credit note it is `${orderId}_${refundId}`,
|
|
101
|
+
* because an order can legitimately have several credit notes — one per
|
|
102
|
+
* refund event — and keying a credit note on the order alone would refuse
|
|
103
|
+
* the second one.
|
|
104
|
+
*/
|
|
105
|
+
subjectId: string;
|
|
106
|
+
orderId: string;
|
|
107
|
+
/** The formatted number, e.g. `LIR/26-27/0001`. */
|
|
108
|
+
number: string;
|
|
109
|
+
fy: string;
|
|
110
|
+
sequence: number;
|
|
111
|
+
issuedAt: Date;
|
|
112
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoicing — the two gapless series (Phase 7, D12)
|
|
3
|
+
*
|
|
4
|
+
* ## Why two series and not one field
|
|
5
|
+
*
|
|
6
|
+
* **A GST invoice is issued once and must not change.** So a partial
|
|
7
|
+
* cancellation or refund gets a CREDIT NOTE with its own number referencing
|
|
8
|
+
* the invoice it adjusts — never a re-issued invoice. That is the whole reason
|
|
9
|
+
* `creditNoteNumber` is a second series rather than a revision suffix.
|
|
10
|
+
*
|
|
11
|
+
* ## 🛑 Gapless means the counter CANNOT be sharded
|
|
12
|
+
*
|
|
13
|
+
* Firestore's ~1 write/sec/document ceiling applies to a counter doc, and the
|
|
14
|
+
* usual answer is to shard it. That answer is unavailable here: a sharded
|
|
15
|
+
* counter cannot be gapless, which is the entire requirement. At this volume
|
|
16
|
+
* the ceiling is irrelevant; the note exists so nobody "optimises" it later
|
|
17
|
+
* and silently breaks the one property an auditor checks.
|
|
18
|
+
*
|
|
19
|
+
* ## 🛑 The FINANCIAL YEAR is the riskiest line in the phase
|
|
20
|
+
*
|
|
21
|
+
* The series is `LIR/26-27/0001` (decision 21), resetting on 1 April. So the
|
|
22
|
+
* allocating transaction must key on the FY — if it keys on anything else, two
|
|
23
|
+
* years share one counter and NEITHER series is gapless. And a number cannot
|
|
24
|
+
* be withdrawn without leaving a gap, so this is not a mistake that can be
|
|
25
|
+
* corrected after the fact.
|
|
26
|
+
*/
|
|
27
|
+
export const INVOICE_COUNTER_COLLECTION = "invoiceCounters";
|
|
28
|
+
/**
|
|
29
|
+
* A record of every number allocated, keyed on what it was allocated FOR.
|
|
30
|
+
*
|
|
31
|
+
* ## 🛑 This is the idempotency claim, and it is why a number cannot be issued
|
|
32
|
+
* twice for one order
|
|
33
|
+
*
|
|
34
|
+
* `onOrderStatusChange` can fire more than once, and **two numbers for one
|
|
35
|
+
* order is the single unrecoverable failure in this phase** — the spare number
|
|
36
|
+
* is a permanent gap that cannot be explained to an auditor and cannot be
|
|
37
|
+
* reclaimed.
|
|
38
|
+
*
|
|
39
|
+
* So allocation is a `.create()` on `invoiceAllocations/{subjectId}` INSIDE
|
|
40
|
+
* the same transaction that bumps the counter. `.create()` fails if the
|
|
41
|
+
* document exists, which makes "already allocated" a transaction-level fact
|
|
42
|
+
* rather than a read-then-write race. It is the same primitive the refund
|
|
43
|
+
* claim, the gateway-event inbox and the gateway-order claim all use.
|
|
44
|
+
*/
|
|
45
|
+
export const INVOICE_ALLOCATION_COLLECTION = "invoiceAllocations";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Indian financial year, and the series format built on it.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 1 April, and getting the boundary wrong is unrecoverable
|
|
5
|
+
*
|
|
6
|
+
* The FY segment is what makes the reset safe: a counter keyed on the year
|
|
7
|
+
* resets by construction, because 1 April produces a new key. But it also
|
|
8
|
+
* means a boundary error is permanent — a number issued under the wrong FY
|
|
9
|
+
* leaves a gap in one year's series and an extra in the other, and a number
|
|
10
|
+
* cannot be withdrawn.
|
|
11
|
+
*
|
|
12
|
+
* So the month test is `>= 3` on a ZERO-BASED month (April is 3, not 4). That
|
|
13
|
+
* single off-by-one is the whole risk, and it is why this is a named function
|
|
14
|
+
* with its reasoning attached rather than an inline expression at the call
|
|
15
|
+
* site.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* `"26-27"` for any date from 2026-04-01 to 2027-03-31.
|
|
19
|
+
*
|
|
20
|
+
* Two-digit years, matching decision 21's `LIR/26-27/0001`. The full number is
|
|
21
|
+
* 14 characters, inside the statutory 16, and uses only `-` and `/` as
|
|
22
|
+
* separators.
|
|
23
|
+
*/
|
|
24
|
+
export declare function financialYearLabel(at: Date): string;
|
|
25
|
+
/**
|
|
26
|
+
* Prefixes, per decision 21.
|
|
27
|
+
*
|
|
28
|
+
* 🛑 Distinct prefixes rather than one with a suffix, so the two series are
|
|
29
|
+
* distinguishable at a glance in a bank statement, a spreadsheet and a GSTR-1
|
|
30
|
+
* row — and so a text search for one can never match the other.
|
|
31
|
+
*/
|
|
32
|
+
export declare const INVOICE_PREFIX = "LIR";
|
|
33
|
+
export declare const CREDIT_NOTE_PREFIX = "LIRCN";
|
|
34
|
+
/**
|
|
35
|
+
* `LIR/26-27/0001`.
|
|
36
|
+
*
|
|
37
|
+
* Zero-padded to four digits, which covers 9,999 documents per series per
|
|
38
|
+
* year. 🛑 Overflow does NOT wrap or throw: `padStart` is a floor, not a
|
|
39
|
+
* ceiling, so number 10,000 renders as `10000` and the series stays gapless
|
|
40
|
+
* and sortable-by-sequence. Width is cosmetic; gaplessness is not, and the
|
|
41
|
+
* failure mode of a throw here would be a paid order that cannot be invoiced.
|
|
42
|
+
*/
|
|
43
|
+
export declare function formatSeriesNumber(prefix: string, fy: string, sequence: number): string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Indian financial year, and the series format built on it.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 1 April, and getting the boundary wrong is unrecoverable
|
|
5
|
+
*
|
|
6
|
+
* The FY segment is what makes the reset safe: a counter keyed on the year
|
|
7
|
+
* resets by construction, because 1 April produces a new key. But it also
|
|
8
|
+
* means a boundary error is permanent — a number issued under the wrong FY
|
|
9
|
+
* leaves a gap in one year's series and an extra in the other, and a number
|
|
10
|
+
* cannot be withdrawn.
|
|
11
|
+
*
|
|
12
|
+
* So the month test is `>= 3` on a ZERO-BASED month (April is 3, not 4). That
|
|
13
|
+
* single off-by-one is the whole risk, and it is why this is a named function
|
|
14
|
+
* with its reasoning attached rather than an inline expression at the call
|
|
15
|
+
* site.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* `"26-27"` for any date from 2026-04-01 to 2027-03-31.
|
|
19
|
+
*
|
|
20
|
+
* Two-digit years, matching decision 21's `LIR/26-27/0001`. The full number is
|
|
21
|
+
* 14 characters, inside the statutory 16, and uses only `-` and `/` as
|
|
22
|
+
* separators.
|
|
23
|
+
*/
|
|
24
|
+
export function financialYearLabel(at) {
|
|
25
|
+
const year = at.getFullYear();
|
|
26
|
+
// 🛑 ZERO-BASED. `getMonth()` returns 3 for April, so `>= 3` is "April or
|
|
27
|
+
// later" and `< 3` is "January to March", which belongs to the FY that
|
|
28
|
+
// STARTED in the previous calendar year.
|
|
29
|
+
const startYear = at.getMonth() >= 3 ? year : year - 1;
|
|
30
|
+
const endYear = startYear + 1;
|
|
31
|
+
return `${String(startYear % 100).padStart(2, "0")}-${String(endYear % 100).padStart(2, "0")}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Prefixes, per decision 21.
|
|
35
|
+
*
|
|
36
|
+
* 🛑 Distinct prefixes rather than one with a suffix, so the two series are
|
|
37
|
+
* distinguishable at a glance in a bank statement, a spreadsheet and a GSTR-1
|
|
38
|
+
* row — and so a text search for one can never match the other.
|
|
39
|
+
*/
|
|
40
|
+
export const INVOICE_PREFIX = "LIR";
|
|
41
|
+
export const CREDIT_NOTE_PREFIX = "LIRCN";
|
|
42
|
+
/**
|
|
43
|
+
* `LIR/26-27/0001`.
|
|
44
|
+
*
|
|
45
|
+
* Zero-padded to four digits, which covers 9,999 documents per series per
|
|
46
|
+
* year. 🛑 Overflow does NOT wrap or throw: `padStart` is a floor, not a
|
|
47
|
+
* ceiling, so number 10,000 renders as `10000` and the series stays gapless
|
|
48
|
+
* and sortable-by-sequence. Width is cosmetic; gaplessness is not, and the
|
|
49
|
+
* failure mode of a throw here would be a paid order that cannot be invoiced.
|
|
50
|
+
*/
|
|
51
|
+
export function formatSeriesNumber(prefix, fy, sequence) {
|
|
52
|
+
return `${prefix}/${fy}/${String(sequence).padStart(4, "0")}`;
|
|
53
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
16
16
|
import { serverLogger } from "../../../monitoring/index.js";
|
|
17
17
|
import { jobsRepository } from "../../../repositories/index.js";
|
|
18
|
-
import { JobStatusValues } from "../schemas/firestore.js";
|
|
18
|
+
import { JOB_MAX_ATTEMPTS, JobStatusValues } from "../schemas/firestore.js";
|
|
19
19
|
import { getAdminAuth, getAdminRealtimeDb } from "../../../providers/db-firebase/index.js";
|
|
20
20
|
import { RTDB_PATHS } from "../../../providers/db-firebase/rtdb-paths.js";
|
|
21
21
|
export async function enqueueJob(input) {
|
|
@@ -24,6 +24,23 @@ export async function enqueueJob(input) {
|
|
|
24
24
|
status: JobStatusValues.PENDING,
|
|
25
25
|
payload: input.payload,
|
|
26
26
|
requestedBy: input.requestedBy,
|
|
27
|
+
/*
|
|
28
|
+
* Phase 8 — attempt 1 of N, stated rather than implied.
|
|
29
|
+
*
|
|
30
|
+
* `attempt` defaults to 1 everywhere it is read, so this write is not
|
|
31
|
+
* strictly required — but an explicit 1 is what makes an ENQUEUED job
|
|
32
|
+
* distinguishable from a pre-Phase-8 row at a glance, and it is what a
|
|
33
|
+
* reader of the collection needs when three documents share a
|
|
34
|
+
* `parentJobId`.
|
|
35
|
+
*
|
|
36
|
+
* 🛑 `nextAttemptAt` is deliberately ABSENT. It is the retry sweep's query
|
|
37
|
+
* field, and the create trigger skips any job carrying a future one — so
|
|
38
|
+
* setting it here (even to `now`) would put every first attempt at the
|
|
39
|
+
* mercy of a 5-minute sweep instead of running immediately, turning a
|
|
40
|
+
* synchronous-feeling admin action into one that appears to hang.
|
|
41
|
+
*/
|
|
42
|
+
attempt: 1,
|
|
43
|
+
maxAttempts: JOB_MAX_ATTEMPTS,
|
|
27
44
|
});
|
|
28
45
|
try {
|
|
29
46
|
await getAdminRealtimeDb()
|
|
@@ -13,8 +13,69 @@ export declare class JobsRepository extends BaseRepository<JobDocument> {
|
|
|
13
13
|
markDone(id: string, result: JobResult): Promise<JobDocument>;
|
|
14
14
|
markFailed(id: string, error: string): Promise<JobDocument>;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Attempts exhausted, or a permanent failure. The dead-letter state
|
|
17
|
+
* (Phase 8).
|
|
18
|
+
*
|
|
19
|
+
* 🛑 Distinct from `markFailed`, which now means "this attempt failed and a
|
|
20
|
+
* retry is scheduled". Before Phase 8 there was only `failed`, and it was
|
|
21
|
+
* terminal with no signal — so a job that gave up looked exactly like a job
|
|
22
|
+
* that had just failed once, and nothing anywhere distinguished them.
|
|
23
|
+
*/
|
|
24
|
+
markDead(id: string, error: string): Promise<JobDocument>;
|
|
25
|
+
/**
|
|
26
|
+
* Schedule the next attempt as a NEW document (Phase 8).
|
|
27
|
+
*
|
|
28
|
+
* ## 🛑 Why a new document and not a reset of this one
|
|
29
|
+
*
|
|
30
|
+
* `onJobCreated` fires on CREATE. Flipping a job back to `pending` would
|
|
31
|
+
* never wake the trigger, so it would sit `pending` forever looking
|
|
32
|
+
* scheduled — a stall that reads as a queue. A fresh document is what makes
|
|
33
|
+
* the retry visible to the only mechanism that can run it.
|
|
34
|
+
*
|
|
35
|
+
* It also keeps the history: three attempts are three documents, each with
|
|
36
|
+
* its own error, so a repeating failure is legible instead of being
|
|
37
|
+
* overwritten by the last one.
|
|
38
|
+
*
|
|
39
|
+
* `parentJobId` points at the ORIGINAL — `parentJobId ?? id`, so attempt 3
|
|
40
|
+
* is linked to attempt 1 rather than to attempt 2. A flat fan means "every
|
|
41
|
+
* attempt of this job" is one equality query, where a linked list needs N
|
|
42
|
+
* reads to walk.
|
|
43
|
+
*/
|
|
44
|
+
createRetry(id: string, job: JobDocument, next: {
|
|
45
|
+
attempt: number;
|
|
46
|
+
maxAttempts: number;
|
|
47
|
+
nextAttemptAt: Date;
|
|
48
|
+
}): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Retries whose `nextAttemptAt` has passed — the sweep's only query.
|
|
51
|
+
*
|
|
52
|
+
* 🛑 `status == pending` AND `nextAttemptAt <= now`, so it can never pick up
|
|
53
|
+
* a FIRST attempt: an original job carries no `nextAttemptAt`, and a
|
|
54
|
+
* Firestore inequality excludes every document that lacks the field. That
|
|
55
|
+
* exclusion is usually the trap (see `.where("isTestData","!=",true)` in
|
|
56
|
+
* CLAUDE.md, and D26's `orderBy` on an optional field); here it is exactly
|
|
57
|
+
* the filter wanted, and it is written down so nobody "fixes" it by adding
|
|
58
|
+
* a default.
|
|
59
|
+
*
|
|
60
|
+
* Bounded at 20 per tick. The sweep rides `paymentWindowTimeout`, which runs
|
|
61
|
+
* every 5 minutes, so that is 240 retries an hour — far above any real
|
|
62
|
+
* backlog, and low enough that a pathological queue cannot exhaust the
|
|
63
|
+
* function's own budget in one pass.
|
|
64
|
+
*/
|
|
65
|
+
getDueRetries(limit?: number): Promise<Array<{
|
|
66
|
+
id: string;
|
|
67
|
+
data: JobDocument;
|
|
68
|
+
}>>;
|
|
69
|
+
/**
|
|
70
|
+
* Refs for finished jobs older than `ttlDays` — used by the
|
|
17
71
|
* `cleanupRtdbEvents` sweep to prune the `jobs` collection.
|
|
72
|
+
*
|
|
73
|
+
* 🛑 `DEAD` is deliberately NOT in this list. A dead job has an open
|
|
74
|
+
* `dead_job` case pointing at it, and the job document holds the payload
|
|
75
|
+
* that would let someone re-run the work by hand — pruning it would leave a
|
|
76
|
+
* case referencing nothing, which is worse than a stale row. They are
|
|
77
|
+
* retained until a human resolves the case and can be pruned by a separate,
|
|
78
|
+
* case-aware sweep if they ever accumulate.
|
|
18
79
|
*/
|
|
19
80
|
getStaleFinishedRefs(ttlDays?: number): Promise<DocumentReference[]>;
|
|
20
81
|
}
|
|
@@ -31,8 +31,91 @@ export class JobsRepository extends BaseRepository {
|
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Attempts exhausted, or a permanent failure. The dead-letter state
|
|
35
|
+
* (Phase 8).
|
|
36
|
+
*
|
|
37
|
+
* 🛑 Distinct from `markFailed`, which now means "this attempt failed and a
|
|
38
|
+
* retry is scheduled". Before Phase 8 there was only `failed`, and it was
|
|
39
|
+
* terminal with no signal — so a job that gave up looked exactly like a job
|
|
40
|
+
* that had just failed once, and nothing anywhere distinguished them.
|
|
41
|
+
*/
|
|
42
|
+
markDead(id, error) {
|
|
43
|
+
return this.update(id, {
|
|
44
|
+
status: JobStatusValues.DEAD,
|
|
45
|
+
error,
|
|
46
|
+
finishedAt: new Date(),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Schedule the next attempt as a NEW document (Phase 8).
|
|
51
|
+
*
|
|
52
|
+
* ## 🛑 Why a new document and not a reset of this one
|
|
53
|
+
*
|
|
54
|
+
* `onJobCreated` fires on CREATE. Flipping a job back to `pending` would
|
|
55
|
+
* never wake the trigger, so it would sit `pending` forever looking
|
|
56
|
+
* scheduled — a stall that reads as a queue. A fresh document is what makes
|
|
57
|
+
* the retry visible to the only mechanism that can run it.
|
|
58
|
+
*
|
|
59
|
+
* It also keeps the history: three attempts are three documents, each with
|
|
60
|
+
* its own error, so a repeating failure is legible instead of being
|
|
61
|
+
* overwritten by the last one.
|
|
62
|
+
*
|
|
63
|
+
* `parentJobId` points at the ORIGINAL — `parentJobId ?? id`, so attempt 3
|
|
64
|
+
* is linked to attempt 1 rather than to attempt 2. A flat fan means "every
|
|
65
|
+
* attempt of this job" is one equality query, where a linked list needs N
|
|
66
|
+
* reads to walk.
|
|
67
|
+
*/
|
|
68
|
+
async createRetry(id, job, next) {
|
|
69
|
+
const created = await this.create({
|
|
70
|
+
jobType: job.jobType,
|
|
71
|
+
status: JobStatusValues.PENDING,
|
|
72
|
+
payload: job.payload,
|
|
73
|
+
requestedBy: job.requestedBy,
|
|
74
|
+
attempt: next.attempt,
|
|
75
|
+
maxAttempts: next.maxAttempts,
|
|
76
|
+
nextAttemptAt: next.nextAttemptAt,
|
|
77
|
+
parentJobId: job.parentJobId ?? id,
|
|
78
|
+
lastError: job.error,
|
|
79
|
+
});
|
|
80
|
+
return created.id;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Retries whose `nextAttemptAt` has passed — the sweep's only query.
|
|
84
|
+
*
|
|
85
|
+
* 🛑 `status == pending` AND `nextAttemptAt <= now`, so it can never pick up
|
|
86
|
+
* a FIRST attempt: an original job carries no `nextAttemptAt`, and a
|
|
87
|
+
* Firestore inequality excludes every document that lacks the field. That
|
|
88
|
+
* exclusion is usually the trap (see `.where("isTestData","!=",true)` in
|
|
89
|
+
* CLAUDE.md, and D26's `orderBy` on an optional field); here it is exactly
|
|
90
|
+
* the filter wanted, and it is written down so nobody "fixes" it by adding
|
|
91
|
+
* a default.
|
|
92
|
+
*
|
|
93
|
+
* Bounded at 20 per tick. The sweep rides `paymentWindowTimeout`, which runs
|
|
94
|
+
* every 5 minutes, so that is 240 retries an hour — far above any real
|
|
95
|
+
* backlog, and low enough that a pathological queue cannot exhaust the
|
|
96
|
+
* function's own budget in one pass.
|
|
97
|
+
*/
|
|
98
|
+
async getDueRetries(limit = 20) {
|
|
99
|
+
const snap = await this.getCollection()
|
|
100
|
+
.where(JOB_FIELDS.STATUS, "==", JobStatusValues.PENDING)
|
|
101
|
+
.where(JOB_FIELDS.NEXT_ATTEMPT_AT, "<=", new Date())
|
|
102
|
+
.limit(limit)
|
|
103
|
+
.get();
|
|
104
|
+
return snap.docs.map((d) => ({
|
|
105
|
+
id: d.id,
|
|
106
|
+
data: { id: d.id, ...d.data() },
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Refs for finished jobs older than `ttlDays` — used by the
|
|
35
111
|
* `cleanupRtdbEvents` sweep to prune the `jobs` collection.
|
|
112
|
+
*
|
|
113
|
+
* 🛑 `DEAD` is deliberately NOT in this list. A dead job has an open
|
|
114
|
+
* `dead_job` case pointing at it, and the job document holds the payload
|
|
115
|
+
* that would let someone re-run the work by hand — pruning it would leave a
|
|
116
|
+
* case referencing nothing, which is worse than a stale row. They are
|
|
117
|
+
* retained until a human resolves the case and can be pruned by a separate,
|
|
118
|
+
* case-aware sweep if they ever accumulate.
|
|
36
119
|
*/
|
|
37
120
|
async getStaleFinishedRefs(ttlDays = 30) {
|
|
38
121
|
const cutoff = new Date();
|