@mohasinac/appkit 4.61.0 → 4.62.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
- package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
- package/dist/_internal/server/features/checkout/actions.js +542 -26
- 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/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +33 -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/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +239 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +15 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +51 -0
- package/dist/_internal/server/functions/scheduled.js +38 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -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/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 +62 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +87 -97
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -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 +10 -2
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.js +177 -9
- 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 +145 -0
- package/dist/_internal/shared/checkout/order-math.js +173 -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/split.d.ts +87 -0
- package/dist/_internal/shared/checkout/split.js +100 -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 +88 -0
- package/dist/_internal/shared/content/published-values.js +198 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
- package/dist/_internal/shared/features/admin/integrations.js +253 -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/points/accepts.d.ts +36 -0
- package/dist/_internal/shared/features/points/accepts.js +43 -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/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/media/limits.d.ts +10 -0
- package/dist/_internal/shared/media/limits.js +10 -0
- package/dist/_internal/shared/types/action-result.js +15 -1
- package/dist/client/api/surface-error.js +16 -2
- package/dist/client/api/useApiMutation.js +17 -1
- package/dist/client.d.ts +12 -1
- package/dist/client.js +31 -1
- package/dist/constants/api-endpoints.d.ts +45 -0
- package/dist/constants/api-endpoints.js +15 -0
- package/dist/constants/field-names.d.ts +18 -0
- package/dist/constants/field-names.js +28 -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/core/server-action.js +19 -3
- package/dist/errors/error-mapping.js +26 -1
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOffersPanel.js +5 -3
- package/dist/features/account/components/UserOrdersView.js +19 -33
- package/dist/features/admin/components/AdminActionIndexView.js +100 -5
- package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselView.js +96 -4
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminMediaView.js +36 -3
- package/dist/features/admin/components/AdminNavigationView.js +4 -1
- package/dist/features/admin/components/AdminOffersView.js +2 -1
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +67 -5
- package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
- package/dist/features/admin/components/AdminProductEditorView.js +2 -2
- package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
- package/dist/features/admin/components/AdminSectionsView.js +486 -224
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
- 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/hooks/useOptimisticRowFlag.d.ts +33 -0
- package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
- package/dist/features/admin/repository/notification.repository.d.ts +15 -0
- package/dist/features/admin/repository/notification.repository.js +32 -0
- package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
- package/dist/features/admin/schemas/action-index-form.js +115 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +30 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
- package/dist/features/audit-log/schemas/firestore.js +15 -0
- package/dist/features/auth/components/RegisterForm.js +2 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
- package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
- 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 +19 -0
- package/dist/features/cart/repository/cart.repository.js +27 -0
- package/dist/features/cart/schemas/firestore.d.ts +92 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/AdminEventEditorView.js +8 -6
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- 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/hooks/useFormBottomActions.js +20 -1
- 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/EmiInstallmentsPanel.js +25 -7
- 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 +111 -0
- package/dist/features/orders/components/RefundRequestView.js +22 -4
- 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 +57 -0
- package/dist/features/orders/repository/orders.repository.js +145 -0
- package/dist/features/orders/schemas/firestore.d.ts +196 -1
- package/dist/features/orders/schemas/index.d.ts +60 -12
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +26 -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/products/components/GroupMemberPicker.js +4 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +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/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/seller-actions.js +33 -3
- package/dist/features/seller/components/FulfillmentView.js +3 -2
- package/dist/features/seller/components/SellerAddressesView.js +32 -4
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
- package/dist/features/seller/components/SellerBundlesView.js +53 -14
- package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
- package/dist/features/seller/components/SellerListingTypeView.js +56 -10
- package/dist/features/seller/components/SellerOrdersView.js +129 -27
- package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
- package/dist/features/seller/components/SellerProductsView.js +5 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
- package/dist/features/seller/components/SellerShippingView.js +2 -1
- package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
- package/dist/features/seller/components/SellerSupportView.js +69 -2
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/shell/SectionForm.js +20 -16
- package/dist/features/shell/build-sections.js +14 -1
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
- package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
- package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
- 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/schemas/firestore.d.ts +71 -0
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/schemas/index.d.ts +6 -6
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +17 -2
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +19 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/schemas/registry.d.ts +51 -22
- package/dist/schemas/sieve.d.ts +5 -5
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +11 -2
- package/dist/server.js +40 -2
- package/dist/styles.css +64 -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/Checkbox.js +10 -2
- package/dist/ui/components/DateInput.js +8 -1
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +74 -0
- package/dist/ui/components/Input.js +22 -2
- package/dist/ui/components/PaginatedSelect.d.ts +8 -19
- package/dist/ui/components/PaginatedSelect.js +43 -9
- package/dist/ui/components/Radio.js +8 -1
- package/dist/ui/components/Select.js +10 -2
- package/dist/ui/components/TagInput.d.ts +8 -1
- package/dist/ui/components/TagInput.js +4 -3
- package/dist/ui/components/Textarea.js +10 -2
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/forms/FieldCheckbox.js +1 -1
- package/dist/ui/forms/FormErrorList.d.ts +0 -16
- package/dist/ui/forms/FormErrorList.js +34 -1
- package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
- package/dist/ui/forms/apply-zod-issues.js +23 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/rich-text/RichText.style.css +48 -79
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1831 -1822
|
@@ -15,6 +15,8 @@ import { adminNotificationsRepository, orderRepository, storeRepository } from "
|
|
|
15
15
|
import { requireRoleUser } from "../../../../providers/auth-firebase/helpers.js";
|
|
16
16
|
import { createOrderSchema, updateOrderStatusSchema, cancelOrderSchema, returnRequestSchema, } from "../../../shared/features/orders/schema.js";
|
|
17
17
|
import { isFinalSaleExempt } from "../../../shared/features/orders/return-reasons.js";
|
|
18
|
+
import { DISPUTE_KIND_LABEL, checkDisputeGate, isDisputeKind, } from "../../../shared/features/orders/dispute-reasons.js";
|
|
19
|
+
import { disputeReasonsRepository } from "../../../../features/dispute-reasons/repository/dispute-reasons.repository.js";
|
|
18
20
|
import { REFUND_COPY } from "../../../shared/features/orders/refund-copy.js";
|
|
19
21
|
import { assertOrderCancellable, assertReturnWindowOpen } from "./service.js";
|
|
20
22
|
import { cancelOrderWithRefund } from "./cancellation-refund.js";
|
|
@@ -390,10 +392,12 @@ export async function adminRejectPaymentAsFraudAction(orderId, note) {
|
|
|
390
392
|
* the seller (store owner), or admin/moderator. Does not itself reverse
|
|
391
393
|
* payment/order status — surfaces to the admin queue for investigation.
|
|
392
394
|
*/
|
|
393
|
-
export async function raiseOrderDisputeAction(orderId,
|
|
395
|
+
export async function raiseOrderDisputeAction(orderId, input) {
|
|
394
396
|
return wrapAction(async () => {
|
|
395
397
|
const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
|
|
396
|
-
if (!
|
|
398
|
+
if (!isDisputeKind(input.kind))
|
|
399
|
+
throw new ValidationError("Pick what kind of problem this is.");
|
|
400
|
+
if (!input.reasonId?.trim())
|
|
397
401
|
throw new ValidationError(REASON_REQUIRED_MSG);
|
|
398
402
|
const order = await orderRepository.findById(orderId);
|
|
399
403
|
if (!order)
|
|
@@ -407,11 +411,50 @@ export async function raiseOrderDisputeAction(orderId, reason) {
|
|
|
407
411
|
if (!isBuyer && !isSeller && !isAdminUser(user) && !isModeratorUser(user)) {
|
|
408
412
|
throw new OrderOwnershipError(orderId);
|
|
409
413
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
414
|
+
/*
|
|
415
|
+
* 🛑 THE `autoApproved` RESTRICTION IS GONE, and that is the one
|
|
416
|
+
* behaviour change an existing path will notice.
|
|
417
|
+
*
|
|
418
|
+
* It used to refuse every order whose payment was not auto-approved,
|
|
419
|
+
* telling the caller to use the return/refund flow — which made this a
|
|
420
|
+
* narrow "the 2-hour auto-approval might have been wrong" escalation
|
|
421
|
+
* rather than a dispute mechanism. The owner's rules are about delivery
|
|
422
|
+
* and item problems on ANY order, so the scope had to widen.
|
|
423
|
+
*
|
|
424
|
+
* What replaces it is a REAL gate per kind, below: the old check refused
|
|
425
|
+
* 99% of orders for a reason unrelated to the complaint, while accepting
|
|
426
|
+
* "it never arrived" on an order delivered last week.
|
|
427
|
+
*/
|
|
413
428
|
if (order.disputeStatus === "open")
|
|
414
429
|
return; // idempotent
|
|
430
|
+
/*
|
|
431
|
+
* The reason must exist AND belong to the kind being claimed.
|
|
432
|
+
*
|
|
433
|
+
* 🛑 Both halves matter. Without the kind check a caller could pass an
|
|
434
|
+
* `item` reason with `kind: "not_reached"` and pass the wrong gate — the
|
|
435
|
+
* kind is what the gate keys on, so an unvalidated pairing is the gate
|
|
436
|
+
* being bypassed by the request body.
|
|
437
|
+
*/
|
|
438
|
+
const reasonDoc = await disputeReasonsRepository.findById(input.reasonId.trim());
|
|
439
|
+
if (!reasonDoc || !reasonDoc.isActive) {
|
|
440
|
+
throw new ValidationError("That reason is no longer available. Pick another.");
|
|
441
|
+
}
|
|
442
|
+
if (reasonDoc.kind !== input.kind) {
|
|
443
|
+
throw new ValidationError("That reason doesn't match the kind of problem selected.");
|
|
444
|
+
}
|
|
445
|
+
/*
|
|
446
|
+
* ── THE GATES ─────────────────────────────────────────────────────────
|
|
447
|
+
*
|
|
448
|
+
* Owner's rules: an item dispute only after the order was received; a
|
|
449
|
+
* "not reached" dispute only once the ETD is more than 3 days past.
|
|
450
|
+
*
|
|
451
|
+
* Enforced HERE, where the request arrives, for the same reason the
|
|
452
|
+
* 7-day cutoff below is: a refusal has to live where the refusing
|
|
453
|
+
* happens. The message is buyer-facing and names the next action.
|
|
454
|
+
*/
|
|
455
|
+
const gate = checkDisputeGate(input.kind, order);
|
|
456
|
+
if (!gate.allowed)
|
|
457
|
+
throw new ValidationError(gate.message);
|
|
415
458
|
/*
|
|
416
459
|
* ── THE DISPUTE CUTOFF ────────────────────────────────────────────────
|
|
417
460
|
*
|
|
@@ -436,15 +479,39 @@ export async function raiseOrderDisputeAction(orderId, reason) {
|
|
|
436
479
|
* `disputeWindowClosed` boolean would need a backfill and would drift from
|
|
437
480
|
* the policy the day the duration changed (Root Cause #42).
|
|
438
481
|
*/
|
|
482
|
+
/*
|
|
483
|
+
* 🛑 This correctly does NOT apply to a `not_reached` dispute, and that is
|
|
484
|
+
* not an oversight to "fix".
|
|
485
|
+
*
|
|
486
|
+
* `order-complete` is anchored on `deliveryDate`, so `deadlinesFor` yields
|
|
487
|
+
* no such deadline when nothing was delivered — and you cannot be past a
|
|
488
|
+
* delivery window that never happened. Making the cutoff refuse
|
|
489
|
+
* undelivered parcels would block exactly the buyers whose parcel is lost,
|
|
490
|
+
* which is the opposite of what a dispute window is for.
|
|
491
|
+
*/
|
|
439
492
|
const completion = deadlinesFor(order).find((d) => d.kind === "order-complete");
|
|
440
493
|
if (completion && completion.dueAt.getTime() <= Date.now()) {
|
|
441
494
|
throw new ValidationError(`The dispute window for this order closed on ${completion.dueAt.toLocaleDateString("en-IN")} — 7 days after delivery. Contact support if you believe this is wrong.`);
|
|
442
495
|
}
|
|
496
|
+
/*
|
|
497
|
+
* No `as any`. Every field below is declared on `OrderDocument`, and the
|
|
498
|
+
* cast that used to be here is precisely how a misspelled field reaches
|
|
499
|
+
* Firestore unnoticed — it silences the check on all six at once.
|
|
500
|
+
*
|
|
501
|
+
* 🛑 `disputeReason` stores the reason's LABEL, not its id. It is a
|
|
502
|
+
* SNAPSHOT: staff can retire or reword a `disputeReasons` row, and neither
|
|
503
|
+
* may rewrite what a buyer said months ago. `disputeReasonId` beside it is
|
|
504
|
+
* the join key for "what are buyers reporting"; the label is the record.
|
|
505
|
+
* Same discipline as the fee terms and the box presets.
|
|
506
|
+
*/
|
|
443
507
|
await orderRepository.update(orderId, {
|
|
444
508
|
disputeRaised: true,
|
|
445
509
|
disputeRaisedBy: user.uid,
|
|
446
510
|
disputeRaisedAt: new Date(),
|
|
447
|
-
|
|
511
|
+
disputeKind: input.kind,
|
|
512
|
+
disputeReasonId: reasonDoc.id,
|
|
513
|
+
disputeReason: reasonDoc.label,
|
|
514
|
+
...(input.detail?.trim() ? { disputeDetail: input.detail.trim() } : {}),
|
|
448
515
|
disputeStatus: "open",
|
|
449
516
|
});
|
|
450
517
|
/*
|
|
@@ -463,8 +530,14 @@ export async function raiseOrderDisputeAction(orderId, reason) {
|
|
|
463
530
|
await adminNotificationsRepository
|
|
464
531
|
.create({
|
|
465
532
|
category: "fraud",
|
|
466
|
-
|
|
467
|
-
|
|
533
|
+
/*
|
|
534
|
+
* 🛑 No longer "on auto-approved order". The title said that because
|
|
535
|
+
* the action refused everything else; now any order can be disputed,
|
|
536
|
+
* and a title naming a payment path the dispute has nothing to do with
|
|
537
|
+
* would send an operator looking in the wrong place.
|
|
538
|
+
*/
|
|
539
|
+
title: `Dispute raised: ${DISPUTE_KIND_LABEL[input.kind]}`,
|
|
540
|
+
body: `A dispute was raised on order "${order.productTitle}". Reason: ${reasonDoc.label}.${input.detail?.trim() ? ` Buyer said: "${input.detail.trim()}"` : ""}`,
|
|
468
541
|
severity: "error",
|
|
469
542
|
isRead: false,
|
|
470
543
|
entityType: "order",
|
|
@@ -203,6 +203,39 @@ export function orderDocumentToOrder(doc) {
|
|
|
203
203
|
saleProtectionAddon: doc.saleProtectionAddon,
|
|
204
204
|
saleProtectionFee: doc.saleProtectionFee,
|
|
205
205
|
platformFee: doc.platformFee,
|
|
206
|
+
/*
|
|
207
|
+
* D46/D47 — the points tender, mapped for the same reason as everything
|
|
208
|
+
* above it and stated once more because this is the field most likely to
|
|
209
|
+
* be dropped: it is optional, it is absent on every historical order, and a
|
|
210
|
+
* surface that never receives it renders as though no points were used.
|
|
211
|
+
*
|
|
212
|
+
* That is Root Cause #57 exactly, and on this field it is not cosmetic —
|
|
213
|
+
* the order page would show a buyer "Total ₹1,000" and a card statement
|
|
214
|
+
* reading ₹750 with nothing on the page explaining the difference, which
|
|
215
|
+
* reads as an overcharge rather than as their own discount.
|
|
216
|
+
*
|
|
217
|
+
* Readers must use `amountDue ?? total`, never a `?? 0`: absent means "no
|
|
218
|
+
* points, the whole total was collected", and zero means free.
|
|
219
|
+
*/
|
|
220
|
+
pointsApplied: doc.pointsApplied,
|
|
221
|
+
amountDue: doc.amountDue,
|
|
222
|
+
/*
|
|
223
|
+
* Phase 7 — the invoice number, mapped for the same Root Cause #57 reason
|
|
224
|
+
* as everything above. Unmapped, the buyer's invoice page would render a
|
|
225
|
+
* statutory document with no number on it while the database held one, and
|
|
226
|
+
* the only symptom would be a blank line where an auditor looks first.
|
|
227
|
+
*
|
|
228
|
+
* 🛑 `invoiceStoragePath` is deliberately NOT mapped. It is a raw Storage
|
|
229
|
+
* path, and the client has no use for one — the bytes are served by an
|
|
230
|
+
* authenticated route. Publishing the path into an RSC payload would hand
|
|
231
|
+
* every buyer a string pointing at a private object, which is the shape of
|
|
232
|
+
* Root Cause #70's leak even though the `private/` prefix is refused by
|
|
233
|
+
* the proxy.
|
|
234
|
+
*/
|
|
235
|
+
invoiceNumber: doc.invoiceNumber,
|
|
236
|
+
invoiceIssuedAt: doc.invoiceIssuedAt
|
|
237
|
+
? new Date(doc.invoiceIssuedAt).toISOString()
|
|
238
|
+
: undefined,
|
|
206
239
|
/*
|
|
207
240
|
* 🛑 `gstAmount` -> `tax`, and codHandlingFee alongside it.
|
|
208
241
|
*
|
|
@@ -59,15 +59,21 @@
|
|
|
59
59
|
* — one reversal per order, checked by query before the write, because an
|
|
60
60
|
* append-only ledger cannot net off a double reversal.
|
|
61
61
|
*
|
|
62
|
-
* 🛑 It
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
62
|
+
* 🛑 **It stopped being a no-op on 2026-10-22.** This paragraph read "It is a
|
|
63
|
+
* NO-OP today and still correct to call … when redemption becomes a real
|
|
64
|
+
* tender, cancellation already gives the points back" — and the tender is now
|
|
65
|
+
* built (`resolvePointsTender` + `settlePointsTender` in `checkout/actions.ts`),
|
|
66
|
+
* so orders carry `pointsApplied` and this query finds real redemptions. The
|
|
67
|
+
* bet paid off: no cancellation path had to be remembered, because the reversal
|
|
68
|
+
* was already wired and keyed.
|
|
69
|
+
*
|
|
70
|
+
* 🛑 **The MONEY half had to change in the same commit, and it is the sharper
|
|
71
|
+
* of the two.** `refundableBase` now subtracts `pointsApplied`, because a
|
|
72
|
+
* refund can only return what was collected: on a ₹1,000 order settled with
|
|
73
|
+
* 250 points the card paid ₹750, so refunding the full base would return
|
|
74
|
+
* ₹1,000 in cash AND 250 points — ₹250 of real money conjured from a discount.
|
|
75
|
+
* Two halves of one refund, in two units, and only their sum is what the buyer
|
|
76
|
+
* gave up.
|
|
71
77
|
*
|
|
72
78
|
* **Cancel, then stock, then money.** The cancellation is the urgent half and
|
|
73
79
|
* must not be held hostage to a gateway round trip; the restock is a local
|
|
@@ -59,15 +59,21 @@
|
|
|
59
59
|
* — one reversal per order, checked by query before the write, because an
|
|
60
60
|
* append-only ledger cannot net off a double reversal.
|
|
61
61
|
*
|
|
62
|
-
* 🛑 It
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
62
|
+
* 🛑 **It stopped being a no-op on 2026-10-22.** This paragraph read "It is a
|
|
63
|
+
* NO-OP today and still correct to call … when redemption becomes a real
|
|
64
|
+
* tender, cancellation already gives the points back" — and the tender is now
|
|
65
|
+
* built (`resolvePointsTender` + `settlePointsTender` in `checkout/actions.ts`),
|
|
66
|
+
* so orders carry `pointsApplied` and this query finds real redemptions. The
|
|
67
|
+
* bet paid off: no cancellation path had to be remembered, because the reversal
|
|
68
|
+
* was already wired and keyed.
|
|
69
|
+
*
|
|
70
|
+
* 🛑 **The MONEY half had to change in the same commit, and it is the sharper
|
|
71
|
+
* of the two.** `refundableBase` now subtracts `pointsApplied`, because a
|
|
72
|
+
* refund can only return what was collected: on a ₹1,000 order settled with
|
|
73
|
+
* 250 points the card paid ₹750, so refunding the full base would return
|
|
74
|
+
* ₹1,000 in cash AND 250 points — ₹250 of real money conjured from a discount.
|
|
75
|
+
* Two halves of one refund, in two units, and only their sum is what the buyer
|
|
76
|
+
* gave up.
|
|
71
77
|
*
|
|
72
78
|
* **Cancel, then stock, then money.** The cancellation is the urgent half and
|
|
73
79
|
* must not be held hostage to a gateway round trip; the restock is a local
|
|
@@ -90,16 +90,51 @@ export async function awardPurchasePoints(order) {
|
|
|
90
90
|
});
|
|
91
91
|
return { awarded: 0, skipped: "no-buyer" };
|
|
92
92
|
}
|
|
93
|
+
/*
|
|
94
|
+
* ── The earn base, and why the points tender comes off it (D46/D47) ───────
|
|
95
|
+
*
|
|
96
|
+
* 🛑 **Points must not breed points.** Earning on an amount that was itself
|
|
97
|
+
* paid with points is a loop: spend 100 points, earn 5 on them, spend those,
|
|
98
|
+
* earn again. It converges rather than exploding, so nothing would ever
|
|
99
|
+
* error — it just leaks margin, and it is indefensible on a balance sheet
|
|
100
|
+
* because the issuance has no sale behind it.
|
|
101
|
+
*
|
|
102
|
+
* The base is therefore the GOODS VALUE ACTUALLY PAID FOR IN MONEY. `goods`
|
|
103
|
+
* already excludes shipping and every fee (`orderTaxableValue` reads
|
|
104
|
+
* `items[]` only — we should not issue points against postage handed to a
|
|
105
|
+
* courier); this subtracts the part of those goods the buyer settled with
|
|
106
|
+
* their balance.
|
|
107
|
+
*
|
|
108
|
+
* 🛑 Clamped at zero rather than assumed smaller. `pointsApplied` is capped
|
|
109
|
+
* at 50% of the ORDER TOTAL, which includes shipping and fees — so on an
|
|
110
|
+
* order that is mostly shipping it can legitimately exceed the goods value,
|
|
111
|
+
* and the subtraction can go negative. `roundPoints` of a negative is a
|
|
112
|
+
* DEBIT, so without the clamp a buyer completing such an order would have
|
|
113
|
+
* points taken away by the reward programme.
|
|
114
|
+
*/
|
|
93
115
|
const goods = orderTaxableValue(order);
|
|
116
|
+
const moneyPaidForGoods = Math.max(0, goods - Math.max(0, order.pointsApplied ?? 0));
|
|
94
117
|
/*
|
|
95
|
-
* `
|
|
96
|
-
*
|
|
118
|
+
* `x * (PERCENT / 100)`, not `(x * PERCENT) / 100` — the same value, written
|
|
119
|
+
* the way every fee in `fees/calculator.ts` is
|
|
97
120
|
* (`subtotal * (commissions.platformFeePercent / 100)`). The division
|
|
98
121
|
* belongs to the PERCENT, and grouping it that way is what distinguishes a
|
|
99
122
|
* percentage from the subunit-conversion `/100` that `audit-money-units`
|
|
100
123
|
* exists to catch — which read the old grouping as exactly that.
|
|
124
|
+
*
|
|
125
|
+
* 🛑 Merge resolution, 2026-10-22: both sides of this conflict were real and
|
|
126
|
+
* independent, so the answer is BOTH, not either.
|
|
127
|
+
*
|
|
128
|
+
* - the BASE is `moneyPaidForGoods`, not `goods` — points must not be
|
|
129
|
+
* issued against the part of the order the buyer settled with points,
|
|
130
|
+
* which is what the whole docstring above this line exists to explain.
|
|
131
|
+
* Taking only the other side would have silently restored
|
|
132
|
+
* points-on-points issuance AND left that docstring describing a
|
|
133
|
+
* variable that no longer existed.
|
|
134
|
+
* - the GROUPING is the other side's, because the audit reasoning is
|
|
135
|
+
* correct and applies to this expression whatever its base.
|
|
101
136
|
*/
|
|
102
|
-
const amount = roundPoints(
|
|
137
|
+
const amount = roundPoints(moneyPaidForGoods * (POINTS_EARN_PERCENT / 100));
|
|
103
138
|
if (amount <= 0) {
|
|
104
139
|
/*
|
|
105
140
|
* A fully-cancelled or zero-value order. Marked as awarded anyway below so
|
|
@@ -125,7 +160,14 @@ export async function awardPurchasePoints(order) {
|
|
|
125
160
|
* later — and written once rather than re-derived, so it stays true of this
|
|
126
161
|
* moment even if the order is edited afterwards.
|
|
127
162
|
*/
|
|
128
|
-
description: `${POINTS_EARN_PERCENT}% back on ${order.id}: ₹${
|
|
163
|
+
description: `${POINTS_EARN_PERCENT}% back on ${order.id}: ₹${moneyPaidForGoods.toFixed(2)} of items` +
|
|
164
|
+
// Named explicitly, because an unexplained gap between the order's item
|
|
165
|
+
// value and the earn base is exactly the kind of figure a buyer writes
|
|
166
|
+
// in to ask about — and the answer ("you paid part of it with points")
|
|
167
|
+
// is a fact about their own order that they can check.
|
|
168
|
+
((order.pointsApplied ?? 0) > 0
|
|
169
|
+
? ` (₹${goods.toFixed(2)} of items less ${order.pointsApplied} points).`
|
|
170
|
+
: "."),
|
|
129
171
|
expiresAt: pointsExpiryFrom(now),
|
|
130
172
|
});
|
|
131
173
|
/*
|
|
@@ -45,6 +45,8 @@ import { serverLogger } from "../../../../monitoring/index.js";
|
|
|
45
45
|
import { isFinalSaleExempt, RETURN_REASON_LABEL, } from "../../../shared/features/orders/return-reasons.js";
|
|
46
46
|
import { REFUND_COPY } from "../../../shared/features/orders/refund-copy.js";
|
|
47
47
|
import { applyRefundDeductionAction } from "../payouts/actions.js";
|
|
48
|
+
/* Phase 7c — the credit note. Swallows its own failures by design: see its 🛑. */
|
|
49
|
+
import { issueCreditNoteForRefund } from "../invoicing/credit-note.js";
|
|
48
50
|
import { getAdminDb } from "../../../../providers/db-firebase/index.js";
|
|
49
51
|
import { PRODUCT_COLLECTION, PRODUCT_CODES_SUBCOLLECTION, } from "../../../../features/products/schemas/firestore.js";
|
|
50
52
|
/**
|
|
@@ -362,6 +364,30 @@ export async function processRefundAction(input) {
|
|
|
362
364
|
// the reconciler will look at it once and find it already done.
|
|
363
365
|
serverLogger.warn("refund: claim not marked settled", { refundId });
|
|
364
366
|
});
|
|
367
|
+
/*
|
|
368
|
+
* ── PHASE 7c: the credit note that documents this reversal ────────────
|
|
369
|
+
*
|
|
370
|
+
* 🛑 HERE, at settlement, and not at reservation. A credit note numbered
|
|
371
|
+
* when the entry was reserved would leave a numbered reversal in the
|
|
372
|
+
* series on every provider failure — for money that never moved, with a
|
|
373
|
+
* number that cannot be withdrawn.
|
|
374
|
+
*
|
|
375
|
+
* 🛑 AWAITED but never allowed to throw (the issuer swallows its own
|
|
376
|
+
* failures and says so). The money has already left the gateway by this
|
|
377
|
+
* line; raising here would report a successful refund as failed, and an
|
|
378
|
+
* operator would retry a refund that already happened. A missing credit
|
|
379
|
+
* note is a reporting gap a backfill can close; a double refund is not.
|
|
380
|
+
*
|
|
381
|
+
* No renderer is passed, so this NUMBERS the reversal without producing
|
|
382
|
+
* a PDF. That is what the GSTR-1 export needs — it reconciles numbers
|
|
383
|
+
* and amounts. The buyer's PDF follows the invoice's own lazy pattern
|
|
384
|
+
* when a surface asks for it.
|
|
385
|
+
*/
|
|
386
|
+
const settledOrder = await orderRepository.findById(input.orderId);
|
|
387
|
+
const settledEvent = settledOrder?.refunds?.find((r) => r.refundId === refundId);
|
|
388
|
+
if (settledOrder && settledEvent) {
|
|
389
|
+
await issueCreditNoteForRefund(settledOrder, settledEvent);
|
|
390
|
+
}
|
|
365
391
|
// SB-UNI-N — revoke any available (unclaimed) digital codes linked to this order.
|
|
366
392
|
revokeAvailableDigitalCodes(input.orderId, digitalCodeItems);
|
|
367
393
|
/*
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raising and clearing a courier block, and telling the ADMIN about it.
|
|
3
|
+
*
|
|
4
|
+
* ## The problem this exists for
|
|
5
|
+
*
|
|
6
|
+
* Shiprocket is PREPAID and has no recharge API, so topping up the courier
|
|
7
|
+
* wallet is irreducibly a human action. If the platform books couriers, an
|
|
8
|
+
* empty wallet silently stops dispatch on orders buyers have already paid for.
|
|
9
|
+
*
|
|
10
|
+
* ## The two rules
|
|
11
|
+
*
|
|
12
|
+
* 1. 🛑 **The order is never auto-cancelled over it.** `seller-dispatch`'s
|
|
13
|
+
* `precondition` carries `!hasOpenCourierBlock(o)`, so its 2-business-day
|
|
14
|
+
* `onDue: "act"` clock PAUSES while blocked instead of cancelling the order
|
|
15
|
+
* and refunding the buyer with the seller blamed for our funding state.
|
|
16
|
+
*
|
|
17
|
+
* 2. 🛑 **The admin alert is deduped on the WALLET, never on the order.** Fifty
|
|
18
|
+
* blocked orders are ONE problem with one fix. The daily email ceiling
|
|
19
|
+
* applies to staff mail too (`send-guard.ts:92` — the kill switch spares
|
|
20
|
+
* staff, the ceiling does not), so fifty per-order emails would spend the
|
|
21
|
+
* whole 80/day budget saying the same thing fifty times and leave the
|
|
22
|
+
* operator blind to everything else.
|
|
23
|
+
*/
|
|
24
|
+
import type { OrderDocument } from "../../../../features/orders/schemas/firestore";
|
|
25
|
+
import type { WalletBalance } from "../../../../contracts";
|
|
26
|
+
/**
|
|
27
|
+
* ## 🛑 What is HERE and what deliberately is not
|
|
28
|
+
*
|
|
29
|
+
* Shipped: the wallet THRESHOLD check and the whole admin-alert path, driven by
|
|
30
|
+
* the daily digest. That is a real producer today and needs nothing new — and
|
|
31
|
+
* it is the better signal anyway: reacting only to failures means the first
|
|
32
|
+
* warning is always a buyer's order already stuck.
|
|
33
|
+
*
|
|
34
|
+
* NOT shipped: raising a block on a specific order. That belongs with the
|
|
35
|
+
* booking path, and nothing platform-books yet (`createShipment` has zero
|
|
36
|
+
* callers). A `raiseCourierBlock` with no producer is the speculative-infra
|
|
37
|
+
* pattern, so it lands when the caller does.
|
|
38
|
+
*
|
|
39
|
+
* `OrderDocument.courierBlock` and `seller-dispatch`'s
|
|
40
|
+
* `!hasOpenCourierBlock(o)` guard ARE already in place, and that ordering is
|
|
41
|
+
* deliberate: a safety property must exist BEFORE the thing it guards against,
|
|
42
|
+
* or the very first block auto-cancels a paid order. Inert today, correct the
|
|
43
|
+
* moment anything writes the field.
|
|
44
|
+
*/
|
|
45
|
+
/** Why we could not book. Mirrors `OrderDocument.courierBlock.reason`. */
|
|
46
|
+
export type CourierBlockReason = NonNullable<OrderDocument["courierBlock"]>["reason"];
|
|
47
|
+
export interface CourierWalletCheck {
|
|
48
|
+
/** `null` when the provider has no wallet, or we could not read it. */
|
|
49
|
+
balance: number | null;
|
|
50
|
+
low: boolean;
|
|
51
|
+
alerted: boolean;
|
|
52
|
+
reason?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Read the courier wallet and, if it is running low, alert the admin ONCE.
|
|
56
|
+
*
|
|
57
|
+
* Called from `dailyStatusDigest` — 🛑 deliberately NOT a new scheduled
|
|
58
|
+
* function. Cloud Scheduler bills per registered job and this is one read a
|
|
59
|
+
* day; the same reasoning that put the ledger reconciler inside
|
|
60
|
+
* `dailyDataCleanup`.
|
|
61
|
+
*
|
|
62
|
+
* Never throws: a wallet read is diagnostics, and a digest that fails because
|
|
63
|
+
* one provider was unreachable stops reporting the fifteen things that were
|
|
64
|
+
* fine.
|
|
65
|
+
*/
|
|
66
|
+
export declare function checkCourierWalletAndAlert(provider: {
|
|
67
|
+
name: string;
|
|
68
|
+
getWalletBalance: () => Promise<WalletBalance>;
|
|
69
|
+
} | undefined): Promise<CourierWalletCheck>;
|