@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,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allocating a number from a gapless series (Phase 7, D12).
|
|
3
|
+
*
|
|
4
|
+
* ## The whole design in one paragraph
|
|
5
|
+
*
|
|
6
|
+
* One transaction does three things: read the (series, FY) counter, `.create()`
|
|
7
|
+
* an allocation claim keyed on the subject, and write `lastNumber + 1`. Any one
|
|
8
|
+
* of the three failing rolls back all of them, so there is no state where a
|
|
9
|
+
* number was consumed without being recorded, and no state where a subject
|
|
10
|
+
* holds two numbers.
|
|
11
|
+
*
|
|
12
|
+
* ## 🛑 Why the claim is INSIDE the transaction
|
|
13
|
+
*
|
|
14
|
+
* The obvious shape is "check whether this order already has a number, then
|
|
15
|
+
* allocate". That is a read-then-write race: two concurrent triggers both read
|
|
16
|
+
* "no number", both allocate, and the order ends up with two — one of which is
|
|
17
|
+
* a permanent gap in a series an auditor reads.
|
|
18
|
+
*
|
|
19
|
+
* `.create()` inside the transaction makes it impossible rather than unlikely.
|
|
20
|
+
* A duplicate attempt fails the transaction, and the caller treats that as
|
|
21
|
+
* "already allocated" and reads the existing number back. This is the fourth
|
|
22
|
+
* use of that primitive in this codebase (refund claims, the gateway-event
|
|
23
|
+
* inbox, the gateway-order claim), and the one with the least recoverable
|
|
24
|
+
* failure mode.
|
|
25
|
+
*
|
|
26
|
+
* ## 🛑 Allocate on `paymentStatus → paid`, never at order creation
|
|
27
|
+
*
|
|
28
|
+
* COD and manual orders are created `pending`. Numbering at creation burns a
|
|
29
|
+
* number on every abandoned order, and *gapless* means those gaps are
|
|
30
|
+
* unexplainable — "that one was abandoned" is not an answer an auditor accepts
|
|
31
|
+
* for a missing invoice number.
|
|
32
|
+
*/
|
|
33
|
+
import { getAdminDb } from "../../../../providers/db-firebase/index.js";
|
|
34
|
+
import { serverLogger } from "../../../../monitoring/index.js";
|
|
35
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
36
|
+
import { INVOICE_ALLOCATION_COLLECTION, INVOICE_COUNTER_COLLECTION, } from "../../../../features/invoicing/schemas/firestore.js";
|
|
37
|
+
import { CREDIT_NOTE_PREFIX, INVOICE_PREFIX, financialYearLabel, formatSeriesNumber, } from "../../../../features/invoicing/utils/financial-year.js";
|
|
38
|
+
function prefixFor(kind) {
|
|
39
|
+
return kind === "credit_note" ? CREDIT_NOTE_PREFIX : INVOICE_PREFIX;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Allocate (or return the already-allocated) number for one subject.
|
|
43
|
+
*
|
|
44
|
+
* 🛑 Idempotent and NOT best-effort. It throws rather than returning a
|
|
45
|
+
* sentinel, because every caller's correct response to "we could not number
|
|
46
|
+
* this" is to leave the order un-invoiced and retry — a caller that treated
|
|
47
|
+
* failure as "no number needed" would produce a paid order with no invoice,
|
|
48
|
+
* silently.
|
|
49
|
+
*/
|
|
50
|
+
export async function allocateSeriesNumber(input) {
|
|
51
|
+
const db = getAdminDb();
|
|
52
|
+
const at = input.at ?? new Date();
|
|
53
|
+
const fy = financialYearLabel(at);
|
|
54
|
+
const counterRef = db
|
|
55
|
+
.collection(INVOICE_COUNTER_COLLECTION)
|
|
56
|
+
.doc(`${input.kind}_${fy}`);
|
|
57
|
+
const claimRef = db.collection(INVOICE_ALLOCATION_COLLECTION).doc(input.subjectId);
|
|
58
|
+
try {
|
|
59
|
+
return await db.runTransaction(async (tx) => {
|
|
60
|
+
/*
|
|
61
|
+
* 🛑 Both reads BEFORE any write — a Firestore transaction requires it,
|
|
62
|
+
* and the claim read is what lets an already-allocated subject return
|
|
63
|
+
* its existing number instead of failing.
|
|
64
|
+
*/
|
|
65
|
+
const [claimSnap, counterSnap] = await Promise.all([
|
|
66
|
+
tx.get(claimRef),
|
|
67
|
+
tx.get(counterRef),
|
|
68
|
+
]);
|
|
69
|
+
if (claimSnap.exists) {
|
|
70
|
+
const existing = claimSnap.data();
|
|
71
|
+
return {
|
|
72
|
+
number: existing.number,
|
|
73
|
+
fy: existing.fy,
|
|
74
|
+
sequence: existing.sequence,
|
|
75
|
+
allocated: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const last = counterSnap.exists
|
|
79
|
+
? (counterSnap.data().lastNumber ?? 0)
|
|
80
|
+
: 0;
|
|
81
|
+
const sequence = last + 1;
|
|
82
|
+
const number = formatSeriesNumber(prefixFor(input.kind), fy, sequence);
|
|
83
|
+
/*
|
|
84
|
+
* `set(..., { merge: true })` on the counter rather than `update`,
|
|
85
|
+
* because the first allocation of a financial year creates it — and
|
|
86
|
+
* `update` on a missing document throws, which would make 1 April the
|
|
87
|
+
* one day of the year invoicing breaks.
|
|
88
|
+
*/
|
|
89
|
+
tx.set(counterRef, {
|
|
90
|
+
kind: input.kind,
|
|
91
|
+
fy,
|
|
92
|
+
lastNumber: sequence,
|
|
93
|
+
lastIssuedAt: at,
|
|
94
|
+
updatedAt: at,
|
|
95
|
+
...(counterSnap.exists ? {} : { createdAt: at }),
|
|
96
|
+
}, { merge: true });
|
|
97
|
+
/*
|
|
98
|
+
* 🛑 `create`, not `set`. Within a transaction both are atomic against
|
|
99
|
+
* the counter, but `create` additionally refuses if the claim appeared
|
|
100
|
+
* between our read and our write — which is exactly the window a
|
|
101
|
+
* concurrent trigger occupies.
|
|
102
|
+
*/
|
|
103
|
+
tx.create(claimRef, {
|
|
104
|
+
kind: input.kind,
|
|
105
|
+
subjectId: input.subjectId,
|
|
106
|
+
orderId: input.orderId,
|
|
107
|
+
number,
|
|
108
|
+
fy,
|
|
109
|
+
sequence,
|
|
110
|
+
issuedAt: at,
|
|
111
|
+
createdAt: at,
|
|
112
|
+
updatedAt: at,
|
|
113
|
+
});
|
|
114
|
+
return { number, fy, sequence, allocated: true };
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const normalized = normalizeError(err);
|
|
119
|
+
/*
|
|
120
|
+
* A lost race: the other side created the claim first. Read it back rather
|
|
121
|
+
* than failing — both callers want the same answer, and whoever wrote it
|
|
122
|
+
* wrote the number this subject now permanently has.
|
|
123
|
+
*/
|
|
124
|
+
/*
|
|
125
|
+
* Logged, not swallowed. `.catch(() => null)` alone made a Firestore
|
|
126
|
+
* failure indistinguishable from "the claim genuinely is not there" — and
|
|
127
|
+
* the two take opposite branches below: one must fall through to a fresh
|
|
128
|
+
* allocation, the other must NOT, because re-allocating over a claim that
|
|
129
|
+
* exists is how an invoice number gets issued twice. Root Cause #59's
|
|
130
|
+
* shape, in the one place in this codebase where a silent degrade would
|
|
131
|
+
* duplicate a filed document number.
|
|
132
|
+
*/
|
|
133
|
+
const after = await claimRef.get().catch((readErr) => {
|
|
134
|
+
const e = normalizeError(readErr);
|
|
135
|
+
serverLogger.error("[invoicing] could not re-read the allocation claim after a race", {
|
|
136
|
+
collection: INVOICE_ALLOCATION_COLLECTION,
|
|
137
|
+
subjectId: input.subjectId,
|
|
138
|
+
error: e.message,
|
|
139
|
+
});
|
|
140
|
+
return null;
|
|
141
|
+
});
|
|
142
|
+
if (after?.exists) {
|
|
143
|
+
const existing = after.data();
|
|
144
|
+
serverLogger.info("[invoicing] allocation race resolved to the existing number", {
|
|
145
|
+
subjectId: input.subjectId,
|
|
146
|
+
number: existing.number,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
number: existing.number,
|
|
150
|
+
fy: existing.fy,
|
|
151
|
+
sequence: existing.sequence,
|
|
152
|
+
allocated: false,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
serverLogger.error("[invoicing] allocation failed", {
|
|
156
|
+
subjectId: input.subjectId,
|
|
157
|
+
kind: input.kind,
|
|
158
|
+
error: normalized.message,
|
|
159
|
+
});
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit notes — the document that reverses tax on a refund (Phase 7, 7c).
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 A refund needs a credit note; it never needs a re-issued invoice
|
|
5
|
+
*
|
|
6
|
+
* A GST invoice is issued once. When money goes back, the reversal is its own
|
|
7
|
+
* numbered document referencing the original invoice — which is why there are
|
|
8
|
+
* two series rather than a revision suffix on one.
|
|
9
|
+
*
|
|
10
|
+
* ## 🛑 Numbered on SETTLEMENT, not on reservation
|
|
11
|
+
*
|
|
12
|
+
* `processRefundAction` is two-phase: it reserves an entry, calls the provider,
|
|
13
|
+
* then settles to `processed` or `failed`. A credit note numbered at
|
|
14
|
+
* reservation would leave a numbered reversal in the series for money that
|
|
15
|
+
* never moved, on every provider failure — and the number could not be
|
|
16
|
+
* withdrawn.
|
|
17
|
+
*
|
|
18
|
+
* So this is called from the SETTLE step, for `processed` only.
|
|
19
|
+
*
|
|
20
|
+
* ## One per refund event, keyed accordingly
|
|
21
|
+
*
|
|
22
|
+
* An order can be refunded several times. The allocation claim is therefore
|
|
23
|
+
* `${orderId}_${refundId}` — keying on the order alone would refuse the second
|
|
24
|
+
* credit note, which is the inverse of the invoice's rule and is why
|
|
25
|
+
* `allocateSeriesNumber` takes `subjectId` separately from `orderId`.
|
|
26
|
+
*/
|
|
27
|
+
import type { OrderDocument, OrderRefundEvent } from "../../../../features/orders/schemas/firestore";
|
|
28
|
+
/** Same private prefix family as invoices — refused by the media proxy. */
|
|
29
|
+
export declare const CREDIT_NOTE_PREFIX_PATH = "private/credit-notes";
|
|
30
|
+
/**
|
|
31
|
+
* Renders a credit note. Injected for the same reason as `InvoiceRenderer`:
|
|
32
|
+
* `pdfkit` is a Functions dependency.
|
|
33
|
+
*/
|
|
34
|
+
export type CreditNoteRenderer = (order: OrderDocument, refund: OrderRefundEvent, creditNoteNumber: string) => Promise<Buffer>;
|
|
35
|
+
export interface IssueCreditNoteResult {
|
|
36
|
+
creditNoteNumber: string;
|
|
37
|
+
creditNoteStoragePath?: string;
|
|
38
|
+
issued: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Number and (optionally) render the credit note for one settled refund.
|
|
42
|
+
*
|
|
43
|
+
* 🛑 **Best-effort by design, and that is the opposite of the invoice rule.**
|
|
44
|
+
* It is called from the refund settle path, where the money has ALREADY left
|
|
45
|
+
* the gateway. Throwing here would turn a successful refund into a failed
|
|
46
|
+
* action — the buyer has their money, our code reports an error, and an
|
|
47
|
+
* operator retries a refund that already happened. A missing credit note is a
|
|
48
|
+
* reporting gap recoverable by a backfill; a double refund is not.
|
|
49
|
+
*
|
|
50
|
+
* The invoice path throws for the mirror-image reason: nothing irreversible has
|
|
51
|
+
* happened yet there, so refusing to proceed is the safe direction.
|
|
52
|
+
*
|
|
53
|
+
* `render` is optional — passing none numbers the reversal without producing a
|
|
54
|
+
* document, which is what the GSTR-1 export actually needs (it reconciles
|
|
55
|
+
* numbers and amounts, not PDFs). The PDF is for the buyer.
|
|
56
|
+
*/
|
|
57
|
+
export declare function issueCreditNoteForRefund(order: OrderDocument, refund: OrderRefundEvent, render?: CreditNoteRenderer): Promise<IssueCreditNoteResult | null>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit notes — the document that reverses tax on a refund (Phase 7, 7c).
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 A refund needs a credit note; it never needs a re-issued invoice
|
|
5
|
+
*
|
|
6
|
+
* A GST invoice is issued once. When money goes back, the reversal is its own
|
|
7
|
+
* numbered document referencing the original invoice — which is why there are
|
|
8
|
+
* two series rather than a revision suffix on one.
|
|
9
|
+
*
|
|
10
|
+
* ## 🛑 Numbered on SETTLEMENT, not on reservation
|
|
11
|
+
*
|
|
12
|
+
* `processRefundAction` is two-phase: it reserves an entry, calls the provider,
|
|
13
|
+
* then settles to `processed` or `failed`. A credit note numbered at
|
|
14
|
+
* reservation would leave a numbered reversal in the series for money that
|
|
15
|
+
* never moved, on every provider failure — and the number could not be
|
|
16
|
+
* withdrawn.
|
|
17
|
+
*
|
|
18
|
+
* So this is called from the SETTLE step, for `processed` only.
|
|
19
|
+
*
|
|
20
|
+
* ## One per refund event, keyed accordingly
|
|
21
|
+
*
|
|
22
|
+
* An order can be refunded several times. The allocation claim is therefore
|
|
23
|
+
* `${orderId}_${refundId}` — keying on the order alone would refuse the second
|
|
24
|
+
* credit note, which is the inverse of the invoice's rule and is why
|
|
25
|
+
* `allocateSeriesNumber` takes `subjectId` separately from `orderId`.
|
|
26
|
+
*/
|
|
27
|
+
import { randomUUID } from "node:crypto";
|
|
28
|
+
import { getAdminStorage } from "../../../../providers/db-firebase/index.js";
|
|
29
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
30
|
+
import { serverLogger } from "../../../../monitoring/index.js";
|
|
31
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
32
|
+
import { allocateSeriesNumber } from "./allocate.js";
|
|
33
|
+
/** Same private prefix family as invoices — refused by the media proxy. */
|
|
34
|
+
export const CREDIT_NOTE_PREFIX_PATH = "private/credit-notes";
|
|
35
|
+
/**
|
|
36
|
+
* Number and (optionally) render the credit note for one settled refund.
|
|
37
|
+
*
|
|
38
|
+
* 🛑 **Best-effort by design, and that is the opposite of the invoice rule.**
|
|
39
|
+
* It is called from the refund settle path, where the money has ALREADY left
|
|
40
|
+
* the gateway. Throwing here would turn a successful refund into a failed
|
|
41
|
+
* action — the buyer has their money, our code reports an error, and an
|
|
42
|
+
* operator retries a refund that already happened. A missing credit note is a
|
|
43
|
+
* reporting gap recoverable by a backfill; a double refund is not.
|
|
44
|
+
*
|
|
45
|
+
* The invoice path throws for the mirror-image reason: nothing irreversible has
|
|
46
|
+
* happened yet there, so refusing to proceed is the safe direction.
|
|
47
|
+
*
|
|
48
|
+
* `render` is optional — passing none numbers the reversal without producing a
|
|
49
|
+
* document, which is what the GSTR-1 export actually needs (it reconciles
|
|
50
|
+
* numbers and amounts, not PDFs). The PDF is for the buyer.
|
|
51
|
+
*/
|
|
52
|
+
export async function issueCreditNoteForRefund(order, refund, render) {
|
|
53
|
+
if (refund.creditNoteNumber) {
|
|
54
|
+
return {
|
|
55
|
+
creditNoteNumber: refund.creditNoteNumber,
|
|
56
|
+
creditNoteStoragePath: refund.creditNoteStoragePath,
|
|
57
|
+
issued: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/*
|
|
61
|
+
* 🛑 Only a refund that actually moved money gets a number. `status`
|
|
62
|
+
* undefined means "committed" to every reader (rows written before the
|
|
63
|
+
* two-phase lifecycle existed carry none), so the test admits those and
|
|
64
|
+
* refuses only an explicit `reserved` or `failed`.
|
|
65
|
+
*/
|
|
66
|
+
if (refund.status === "reserved" || refund.status === "failed") {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const issuedAt = new Date();
|
|
71
|
+
const allocation = await allocateSeriesNumber({
|
|
72
|
+
kind: "credit_note",
|
|
73
|
+
subjectId: `${order.id}_${refund.refundId}`,
|
|
74
|
+
orderId: order.id,
|
|
75
|
+
at: issuedAt,
|
|
76
|
+
});
|
|
77
|
+
let storagePath;
|
|
78
|
+
if (render) {
|
|
79
|
+
storagePath = `${CREDIT_NOTE_PREFIX_PATH}/${order.id}/${randomUUID()}.pdf`;
|
|
80
|
+
const buffer = await render(order, refund, allocation.number);
|
|
81
|
+
await getAdminStorage()
|
|
82
|
+
.bucket()
|
|
83
|
+
.file(storagePath)
|
|
84
|
+
.save(buffer, {
|
|
85
|
+
contentType: "application/pdf",
|
|
86
|
+
metadata: { cacheControl: "private, no-store" },
|
|
87
|
+
resumable: false,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/*
|
|
91
|
+
* 🛑 Written through the repository's own refund-event mutator, NOT by
|
|
92
|
+
* read-modify-writing `refunds[]`. That array is appended by a transaction
|
|
93
|
+
* precisely because a read-then-`[...existing, event]` write LOSES an
|
|
94
|
+
* event under concurrency (D6), and stamping a credit-note number the
|
|
95
|
+
* naive way would reintroduce exactly that defect on the same array.
|
|
96
|
+
*/
|
|
97
|
+
await orderRepository.setRefundCreditNote(order.id, refund.refundId, {
|
|
98
|
+
creditNoteNumber: allocation.number,
|
|
99
|
+
creditNoteIssuedAt: issuedAt,
|
|
100
|
+
...(storagePath ? { creditNoteStoragePath: storagePath } : {}),
|
|
101
|
+
});
|
|
102
|
+
serverLogger.info("[invoicing] credit note issued", {
|
|
103
|
+
orderId: order.id,
|
|
104
|
+
refundId: refund.refundId,
|
|
105
|
+
creditNoteNumber: allocation.number,
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
creditNoteNumber: allocation.number,
|
|
109
|
+
creditNoteStoragePath: storagePath,
|
|
110
|
+
issued: allocation.allocated,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
/*
|
|
115
|
+
* Reported loudly and swallowed — see the 🛑 on this function. A refund
|
|
116
|
+
* that succeeded must not be reported as failed because its paperwork
|
|
117
|
+
* could not be numbered.
|
|
118
|
+
*/
|
|
119
|
+
serverLogger.error("[invoicing] credit note FAILED — refund stands, reporting gap", {
|
|
120
|
+
orderId: order.id,
|
|
121
|
+
refundId: refund.refundId,
|
|
122
|
+
error: normalizeError(err).message,
|
|
123
|
+
});
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSTR-1 export (Phase 7d) — the INPUT to a human filing, never a filing.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 What this is and is not
|
|
5
|
+
*
|
|
6
|
+
* It is a CSV an accountant opens, checks and uses. It is not a return, it is
|
|
7
|
+
* not filed, and nothing here talks to the GST portal — there is no webhook
|
|
8
|
+
* facility in existence and no GSP contract. Labelling it otherwise would be
|
|
9
|
+
* the most consequential lie in this codebase.
|
|
10
|
+
*
|
|
11
|
+
* ## 🛑 Credit notes are not optional
|
|
12
|
+
*
|
|
13
|
+
* A return listing invoices and no credit notes **does not reconcile**: the
|
|
14
|
+
* output tax declared would be the tax on every sale including the ones that
|
|
15
|
+
* were reversed. So both series are exported, the credit notes as negative
|
|
16
|
+
* amounts, and the file's own total is the net.
|
|
17
|
+
*
|
|
18
|
+
* That is also why the two series exist in the first place — a reversal is its
|
|
19
|
+
* own numbered document, so it has its own row here.
|
|
20
|
+
*
|
|
21
|
+
* ## The rate split comes from `gstByRate`
|
|
22
|
+
*
|
|
23
|
+
* Already on the order (`firestore.ts`), already per rate, already recorded at
|
|
24
|
+
* the moment of supply. Nothing is recomputed from today's tax codes: an order
|
|
25
|
+
* supplied under a rate that has since changed must appear at the rate it was
|
|
26
|
+
* charged, and re-deriving it would silently restate history — the same defect
|
|
27
|
+
* the PDF renderer's own 🛑 records about reading live site settings.
|
|
28
|
+
*/
|
|
29
|
+
export interface Gstr1Row {
|
|
30
|
+
/** `LIR/26-27/0001` or `LIRCN/26-27/0001`. */
|
|
31
|
+
documentNumber: string;
|
|
32
|
+
documentType: "invoice" | "credit_note";
|
|
33
|
+
documentDate: string;
|
|
34
|
+
orderId: string;
|
|
35
|
+
/** The buyer's state, which decides CGST+SGST vs IGST. */
|
|
36
|
+
placeOfSupply: string;
|
|
37
|
+
gstRate: number;
|
|
38
|
+
taxableValue: number;
|
|
39
|
+
cgst: number;
|
|
40
|
+
sgst: number;
|
|
41
|
+
igst: number;
|
|
42
|
+
total: number;
|
|
43
|
+
}
|
|
44
|
+
export interface Gstr1Export {
|
|
45
|
+
rows: Gstr1Row[];
|
|
46
|
+
/** Net of credit notes — what the period actually owes. */
|
|
47
|
+
totals: {
|
|
48
|
+
taxableValue: number;
|
|
49
|
+
cgst: number;
|
|
50
|
+
sgst: number;
|
|
51
|
+
igst: number;
|
|
52
|
+
total: number;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Orders in the window that are PAID but carry no invoice number.
|
|
56
|
+
*
|
|
57
|
+
* 🛑 Reported, never silently excluded. A paid order missing from a tax
|
|
58
|
+
* return is the one error this export must not hide, and "the row simply
|
|
59
|
+
* wasn't there" is indistinguishable from "there was nothing to report" in a
|
|
60
|
+
* CSV. Root Cause #84's lesson applied to an accounting artefact: a count
|
|
61
|
+
* that goes quiet is as likely to have stopped looking as to be satisfied.
|
|
62
|
+
*/
|
|
63
|
+
unnumberedPaidOrders: string[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Build the export for a date window.
|
|
67
|
+
*
|
|
68
|
+
* 🛑 Windowed on the INVOICE date, not the order date. A COD order placed on
|
|
69
|
+
* 30 March and paid on 2 April is invoiced in the new financial year, and that
|
|
70
|
+
* is the period its tax belongs to — numbering happens at payment for exactly
|
|
71
|
+
* this reason.
|
|
72
|
+
*/
|
|
73
|
+
export declare function buildGstr1Export(from: Date, to: Date): Promise<Gstr1Export>;
|
|
74
|
+
export declare function gstr1ToCsv(data: Gstr1Export): string;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSTR-1 export (Phase 7d) — the INPUT to a human filing, never a filing.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 What this is and is not
|
|
5
|
+
*
|
|
6
|
+
* It is a CSV an accountant opens, checks and uses. It is not a return, it is
|
|
7
|
+
* not filed, and nothing here talks to the GST portal — there is no webhook
|
|
8
|
+
* facility in existence and no GSP contract. Labelling it otherwise would be
|
|
9
|
+
* the most consequential lie in this codebase.
|
|
10
|
+
*
|
|
11
|
+
* ## 🛑 Credit notes are not optional
|
|
12
|
+
*
|
|
13
|
+
* A return listing invoices and no credit notes **does not reconcile**: the
|
|
14
|
+
* output tax declared would be the tax on every sale including the ones that
|
|
15
|
+
* were reversed. So both series are exported, the credit notes as negative
|
|
16
|
+
* amounts, and the file's own total is the net.
|
|
17
|
+
*
|
|
18
|
+
* That is also why the two series exist in the first place — a reversal is its
|
|
19
|
+
* own numbered document, so it has its own row here.
|
|
20
|
+
*
|
|
21
|
+
* ## The rate split comes from `gstByRate`
|
|
22
|
+
*
|
|
23
|
+
* Already on the order (`firestore.ts`), already per rate, already recorded at
|
|
24
|
+
* the moment of supply. Nothing is recomputed from today's tax codes: an order
|
|
25
|
+
* supplied under a rate that has since changed must appear at the rate it was
|
|
26
|
+
* charged, and re-deriving it would silently restate history — the same defect
|
|
27
|
+
* the PDF renderer's own 🛑 records about reading live site settings.
|
|
28
|
+
*/
|
|
29
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
30
|
+
import { roundRupees } from "../../../../utils/number.formatter.js";
|
|
31
|
+
import { serverLogger } from "../../../../monitoring/index.js";
|
|
32
|
+
/** One row per (document, rate) slice — which is what a GSTR-1 line is. */
|
|
33
|
+
function rowsForOrder(order) {
|
|
34
|
+
/*
|
|
35
|
+
* 🛑 `placeOfSupply` ONLY — never parsed out of `shippingAddress`, which is
|
|
36
|
+
* a flat string on this document. Deriving a state from free text to file a
|
|
37
|
+
* tax return is not a thing to do, and an order placed before the field
|
|
38
|
+
* existed is reported as blank rather than guessed at.
|
|
39
|
+
*/
|
|
40
|
+
const placeOfSupply = order.placeOfSupply ?? "";
|
|
41
|
+
const date = order.invoiceIssuedAt ?? order.orderDate ?? order.createdAt;
|
|
42
|
+
const dateStr = date instanceof Date ? date.toISOString().slice(0, 10) : "";
|
|
43
|
+
const byRate = order.gstByRate ?? [];
|
|
44
|
+
const invoiceRows = order.invoiceNumber
|
|
45
|
+
? byRate.map((slice) => ({
|
|
46
|
+
documentNumber: order.invoiceNumber,
|
|
47
|
+
documentType: "invoice",
|
|
48
|
+
documentDate: dateStr,
|
|
49
|
+
orderId: order.id,
|
|
50
|
+
placeOfSupply,
|
|
51
|
+
gstRate: slice.gstRate,
|
|
52
|
+
taxableValue: roundRupees(slice.taxableAmount),
|
|
53
|
+
cgst: roundRupees(slice.cgst),
|
|
54
|
+
sgst: roundRupees(slice.sgst),
|
|
55
|
+
igst: roundRupees(slice.igst),
|
|
56
|
+
total: roundRupees(slice.taxableAmount + slice.cgst + slice.sgst + slice.igst),
|
|
57
|
+
}))
|
|
58
|
+
: [];
|
|
59
|
+
/*
|
|
60
|
+
* ── Credit notes, as NEGATIVE rows ────────────────────────────────────────
|
|
61
|
+
*
|
|
62
|
+
* 🛑 The tax reversed is apportioned across the SAME rate slices the invoice
|
|
63
|
+
* carried, pro-rata on the refunded amount. A refund is a rupee figure with
|
|
64
|
+
* no rate attached, and a multi-rate order's reversal has to be split
|
|
65
|
+
* somehow — pro-rata on the invoice's own slices is the only split that
|
|
66
|
+
* reconciles back to the invoice it adjusts.
|
|
67
|
+
*
|
|
68
|
+
* 🛑 Only SETTLED refunds appear, and only ones that were numbered. A
|
|
69
|
+
* `reserved` entry is money that has not moved; a numbered reversal for it
|
|
70
|
+
* would overstate the period's credit notes.
|
|
71
|
+
*/
|
|
72
|
+
const invoiceTotal = byRate.reduce((s, r) => s + r.taxableAmount, 0);
|
|
73
|
+
const creditRows = [];
|
|
74
|
+
for (const refund of order.refunds ?? []) {
|
|
75
|
+
if (!refund.creditNoteNumber)
|
|
76
|
+
continue;
|
|
77
|
+
if (refund.status === "reserved" || refund.status === "failed")
|
|
78
|
+
continue;
|
|
79
|
+
const refundDate = refund.creditNoteIssuedAt ?? refund.refundedAt;
|
|
80
|
+
const refundDateStr = refundDate instanceof Date ? refundDate.toISOString().slice(0, 10) : dateStr;
|
|
81
|
+
// No rate information at all — reported as one untaxed row rather than
|
|
82
|
+
// dropped, so the CSV's total still matches the money that moved.
|
|
83
|
+
if (invoiceTotal <= 0 || byRate.length === 0) {
|
|
84
|
+
creditRows.push({
|
|
85
|
+
documentNumber: refund.creditNoteNumber,
|
|
86
|
+
documentType: "credit_note",
|
|
87
|
+
documentDate: refundDateStr,
|
|
88
|
+
orderId: order.id,
|
|
89
|
+
placeOfSupply,
|
|
90
|
+
gstRate: 0,
|
|
91
|
+
taxableValue: -roundRupees(refund.amount),
|
|
92
|
+
cgst: 0,
|
|
93
|
+
sgst: 0,
|
|
94
|
+
igst: 0,
|
|
95
|
+
total: -roundRupees(refund.amount),
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
for (const slice of byRate) {
|
|
100
|
+
/*
|
|
101
|
+
* The slice's share of the refund, and then that amount's share of the
|
|
102
|
+
* slice — two ratios, because a refund is a gross rupee figure while a
|
|
103
|
+
* slice is split into taxable value and tax.
|
|
104
|
+
*
|
|
105
|
+
* 🛑 `sliceTotal > 0` guards a 0%-rated (exempt) slice, where the tax is
|
|
106
|
+
* legitimately zero and a naive `portion / sliceTotal` would be a
|
|
107
|
+
* division by zero producing `Infinity` — and `roundRupees(Infinity)` is
|
|
108
|
+
* `NaN`, which propagates into the file's TOTAL row and makes every
|
|
109
|
+
* figure in it unusable. One bad cell would be visible; a NaN total is
|
|
110
|
+
* the whole return.
|
|
111
|
+
*/
|
|
112
|
+
const share = slice.taxableAmount / invoiceTotal;
|
|
113
|
+
const portion = refund.amount * share;
|
|
114
|
+
const sliceTotal = slice.taxableAmount + slice.cgst + slice.sgst + slice.igst;
|
|
115
|
+
const taxFraction = sliceTotal > 0 ? portion / sliceTotal : 0;
|
|
116
|
+
creditRows.push({
|
|
117
|
+
documentNumber: refund.creditNoteNumber,
|
|
118
|
+
documentType: "credit_note",
|
|
119
|
+
documentDate: refundDateStr,
|
|
120
|
+
orderId: order.id,
|
|
121
|
+
placeOfSupply,
|
|
122
|
+
gstRate: slice.gstRate,
|
|
123
|
+
taxableValue: -roundRupees(slice.taxableAmount * taxFraction),
|
|
124
|
+
cgst: -roundRupees(slice.cgst * taxFraction),
|
|
125
|
+
sgst: -roundRupees(slice.sgst * taxFraction),
|
|
126
|
+
igst: -roundRupees(slice.igst * taxFraction),
|
|
127
|
+
total: -roundRupees(portion),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return [...invoiceRows, ...creditRows];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build the export for a date window.
|
|
135
|
+
*
|
|
136
|
+
* 🛑 Windowed on the INVOICE date, not the order date. A COD order placed on
|
|
137
|
+
* 30 March and paid on 2 April is invoiced in the new financial year, and that
|
|
138
|
+
* is the period its tax belongs to — numbering happens at payment for exactly
|
|
139
|
+
* this reason.
|
|
140
|
+
*/
|
|
141
|
+
export async function buildGstr1Export(from, to) {
|
|
142
|
+
const orders = await orderRepository.listInvoicedBetween(from, to);
|
|
143
|
+
const rows = [];
|
|
144
|
+
const unnumberedPaidOrders = [];
|
|
145
|
+
for (const order of orders) {
|
|
146
|
+
if (order.paymentStatus === "paid" && !order.invoiceNumber) {
|
|
147
|
+
unnumberedPaidOrders.push(order.id);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
rows.push(...rowsForOrder(order));
|
|
151
|
+
}
|
|
152
|
+
const totals = rows.reduce((acc, r) => ({
|
|
153
|
+
taxableValue: acc.taxableValue + r.taxableValue,
|
|
154
|
+
cgst: acc.cgst + r.cgst,
|
|
155
|
+
sgst: acc.sgst + r.sgst,
|
|
156
|
+
igst: acc.igst + r.igst,
|
|
157
|
+
total: acc.total + r.total,
|
|
158
|
+
}), { taxableValue: 0, cgst: 0, sgst: 0, igst: 0, total: 0 });
|
|
159
|
+
if (unnumberedPaidOrders.length > 0) {
|
|
160
|
+
serverLogger.warn("[gstr1] paid orders with no invoice number in window", {
|
|
161
|
+
count: unnumberedPaidOrders.length,
|
|
162
|
+
from: from.toISOString(),
|
|
163
|
+
to: to.toISOString(),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
rows,
|
|
168
|
+
totals: {
|
|
169
|
+
taxableValue: roundRupees(totals.taxableValue),
|
|
170
|
+
cgst: roundRupees(totals.cgst),
|
|
171
|
+
sgst: roundRupees(totals.sgst),
|
|
172
|
+
igst: roundRupees(totals.igst),
|
|
173
|
+
total: roundRupees(totals.total),
|
|
174
|
+
},
|
|
175
|
+
unnumberedPaidOrders,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const CSV_HEADER = [
|
|
179
|
+
"Document Number",
|
|
180
|
+
"Document Type",
|
|
181
|
+
"Document Date",
|
|
182
|
+
"Order ID",
|
|
183
|
+
"Place of Supply",
|
|
184
|
+
"GST Rate",
|
|
185
|
+
"Taxable Value",
|
|
186
|
+
"CGST",
|
|
187
|
+
"SGST",
|
|
188
|
+
"IGST",
|
|
189
|
+
"Total",
|
|
190
|
+
].join(",");
|
|
191
|
+
/**
|
|
192
|
+
* 🛑 Every field is quoted and internal quotes are doubled.
|
|
193
|
+
*
|
|
194
|
+
* An invoice number contains `/`, a place of supply can contain a comma, and a
|
|
195
|
+
* CSV whose columns shift by one on a single row is worse than no CSV — it
|
|
196
|
+
* reconciles to a plausible wrong number rather than failing to open.
|
|
197
|
+
*/
|
|
198
|
+
function csvCell(value) {
|
|
199
|
+
const s = String(value);
|
|
200
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
201
|
+
}
|
|
202
|
+
export function gstr1ToCsv(data) {
|
|
203
|
+
const lines = [CSV_HEADER];
|
|
204
|
+
for (const r of data.rows) {
|
|
205
|
+
lines.push([
|
|
206
|
+
r.documentNumber,
|
|
207
|
+
r.documentType === "credit_note" ? "Credit Note" : "Invoice",
|
|
208
|
+
r.documentDate,
|
|
209
|
+
r.orderId,
|
|
210
|
+
r.placeOfSupply,
|
|
211
|
+
r.gstRate,
|
|
212
|
+
r.taxableValue.toFixed(2),
|
|
213
|
+
r.cgst.toFixed(2),
|
|
214
|
+
r.sgst.toFixed(2),
|
|
215
|
+
r.igst.toFixed(2),
|
|
216
|
+
r.total.toFixed(2),
|
|
217
|
+
]
|
|
218
|
+
.map(csvCell)
|
|
219
|
+
.join(","));
|
|
220
|
+
}
|
|
221
|
+
/*
|
|
222
|
+
* A TOTAL row, and it is net of credit notes. An accountant's first act is to
|
|
223
|
+
* check the file's own total against their own figure; making them sum 400
|
|
224
|
+
* rows to do it is how a transcription error enters a tax return.
|
|
225
|
+
*/
|
|
226
|
+
lines.push([
|
|
227
|
+
"TOTAL",
|
|
228
|
+
"",
|
|
229
|
+
"",
|
|
230
|
+
"",
|
|
231
|
+
"",
|
|
232
|
+
"",
|
|
233
|
+
data.totals.taxableValue.toFixed(2),
|
|
234
|
+
data.totals.cgst.toFixed(2),
|
|
235
|
+
data.totals.sgst.toFixed(2),
|
|
236
|
+
data.totals.igst.toFixed(2),
|
|
237
|
+
data.totals.total.toFixed(2),
|
|
238
|
+
]
|
|
239
|
+
.map(csvCell)
|
|
240
|
+
.join(","));
|
|
241
|
+
/*
|
|
242
|
+
* 🛑 The exceptions go IN the file, not only in a log. The person filing is
|
|
243
|
+
* not the person reading our logs, and a paid order absent from a tax return
|
|
244
|
+
* is the one error this export must not hide.
|
|
245
|
+
*/
|
|
246
|
+
if (data.unnumberedPaidOrders.length > 0) {
|
|
247
|
+
lines.push("");
|
|
248
|
+
lines.push(csvCell(`WARNING: ${data.unnumberedPaidOrders.length} paid order(s) in this window carry no invoice number and are NOT included above: ${data.unnumberedPaidOrders.join(" ")}`));
|
|
249
|
+
}
|
|
250
|
+
return lines.join("\n");
|
|
251
|
+
}
|