@mohasinac/appkit 4.62.1 → 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 +51 -11
- package/dist/_internal/server/features/checkout/actions.js +335 -80
- 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/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -0
- package/dist/_internal/server/features/orders/adapters.js +21 -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/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -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.js +26 -1
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +14 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +14 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +57 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +2 -0
- package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +4 -1
- 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/weeklyPayoutEligibility.js +15 -0
- 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/reservationExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +268 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +31 -2
- package/dist/_internal/shared/checkout/order-math.js +22 -36
- 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 +0 -12
- package/dist/_internal/shared/checkout/split.js +22 -2
- package/dist/_internal/shared/content/disclaimers.js +7 -7
- package/dist/_internal/shared/content/published-values.d.ts +6 -1
- package/dist/_internal/shared/content/published-values.js +8 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +32 -2
- package/dist/_internal/shared/features/admin/integrations.js +19 -9
- 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/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 +1 -8
- package/dist/_internal/shared/features/points/accepts.js +12 -6
- 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/site-settings/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- 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 +11 -1
- package/dist/client.js +13 -0
- package/dist/constants/api-endpoints.d.ts +72 -0
- package/dist/constants/api-endpoints.js +41 -0
- package/dist/constants/field-names.d.ts +41 -0
- package/dist/constants/field-names.js +47 -0
- package/dist/features/account/components/UserOrdersView.js +28 -2
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +16 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +72 -3
- package/dist/features/admin/components/AdminOrdersView.js +20 -1
- package/dist/features/admin/schemas/firestore.d.ts +32 -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 +110 -0
- package/dist/features/audit-log/schemas/firestore.js +74 -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/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 +12 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +24 -5
- package/dist/features/cart/repository/cart.repository.d.ts +14 -0
- package/dist/features/cart/repository/cart.repository.js +20 -0
- package/dist/features/cart/schemas/firestore.d.ts +24 -0
- 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 +24 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +28 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +3 -5
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- 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/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/orders/components/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +19 -11
- package/dist/features/orders/repository/orders.repository.d.ts +28 -1
- package/dist/features/orders/repository/orders.repository.js +60 -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 +202 -0
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/orders/types/index.d.ts +21 -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/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/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- 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/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 +4 -0
- 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 +94 -2
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -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/shipping-boxes/repository/shipping-boxes.repository.d.ts +39 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +55 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +2 -4
- 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 +41 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- 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 +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.js +19 -0
- package/dist/next/routing/route-map.d.ts +92 -0
- package/dist/next/routing/route-map.js +53 -0
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +22 -16
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server.d.ts +27 -1
- package/dist/server.js +144 -0
- package/dist/styles.css +349 -1
- package/dist/tailwind-utilities.css +1 -1
- 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.js +10 -1
- 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/toolbar-collapse.d.ts +13 -0
- package/dist/ui/components/toolbar-collapse.js +10 -1
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -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 +35 -0
- 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 +3 -0
|
@@ -64,6 +64,73 @@ export function actionLabel(def) {
|
|
|
64
64
|
// just establishes the shape + a few "obvious" actions that other Phase 5
|
|
65
65
|
// flows can reference today.
|
|
66
66
|
export const ACTIONS = {
|
|
67
|
+
/*
|
|
68
|
+
* D56 — self-serve ad bookings.
|
|
69
|
+
*
|
|
70
|
+
* 🛑 `book-ad-slot` carries a confirmation because it is a PURCHASE of a
|
|
71
|
+
* named month, and cancelling it does not free that month for re-sale —
|
|
72
|
+
* the booking document IS the availability lock and keeping it is
|
|
73
|
+
* deliberate. A control that spends money and cannot be undone by the
|
|
74
|
+
* person who clicked it is exactly what Rule #7's confirmation is for.
|
|
75
|
+
*/
|
|
76
|
+
ADS: {
|
|
77
|
+
"book-ad-slot": {
|
|
78
|
+
iconKey: "create",
|
|
79
|
+
id: "ads.book-ad-slot",
|
|
80
|
+
label: "Book this slot",
|
|
81
|
+
ariaLabel: "Book this ad slot for the selected month",
|
|
82
|
+
description: "Reserves one placement for one month at the listed flat fee. The creative is reviewed by a person before it appears anywhere.",
|
|
83
|
+
kind: "primary",
|
|
84
|
+
permissions: ["user"],
|
|
85
|
+
confirmation: {
|
|
86
|
+
title: "Book this slot?",
|
|
87
|
+
body: "This reserves the placement for the whole month at the listed price. Cancelling later does not release the month for someone else to buy.",
|
|
88
|
+
confirmLabel: "Yes, book it",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
"cancel-ad-booking": {
|
|
92
|
+
iconKey: "cancel",
|
|
93
|
+
id: "ads.cancel-ad-booking",
|
|
94
|
+
label: "Cancel booking",
|
|
95
|
+
ariaLabel: "Cancel this ad booking",
|
|
96
|
+
description: "Stops the creative running. 🛑 Does NOT free the month — the slot stays yours for the period you booked; releasing it is a separate admin act with a refund behind it.",
|
|
97
|
+
kind: "danger",
|
|
98
|
+
permissions: ["user"],
|
|
99
|
+
confirmation: {
|
|
100
|
+
title: "Cancel this booking?",
|
|
101
|
+
body: "Your ad stops running. The month is NOT released for another advertiser, and this does not issue a refund on its own.",
|
|
102
|
+
confirmLabel: "Yes, cancel",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
"approve-ad-creative": {
|
|
106
|
+
iconKey: "verify",
|
|
107
|
+
id: "ads.approve-ad-creative",
|
|
108
|
+
label: "Approve creative",
|
|
109
|
+
ariaLabel: "Approve this ad creative so it can render",
|
|
110
|
+
description: "Clears the creative to render for its booked month. Approval is of THIS creative — an edit returns it to review.",
|
|
111
|
+
kind: "primary",
|
|
112
|
+
permissions: ["admin", "moderator"],
|
|
113
|
+
confirmation: {
|
|
114
|
+
title: "Approve this creative?",
|
|
115
|
+
body: "It will render on the public site for its booked month once the booking is paid.",
|
|
116
|
+
confirmLabel: "Yes, approve",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
"reject-ad-creative": {
|
|
120
|
+
iconKey: "reject",
|
|
121
|
+
id: "ads.reject-ad-creative",
|
|
122
|
+
label: "Reject creative",
|
|
123
|
+
ariaLabel: "Reject this ad creative and tell the advertiser why",
|
|
124
|
+
description: "Sends it back with your note. A note is REQUIRED — without one the advertiser resubmits the same creative and you review it twice.",
|
|
125
|
+
kind: "danger",
|
|
126
|
+
permissions: ["admin", "moderator"],
|
|
127
|
+
confirmation: {
|
|
128
|
+
title: "Reject this creative?",
|
|
129
|
+
body: "The advertiser sees your note and can submit a new creative against the same booking. The slot stays theirs.",
|
|
130
|
+
confirmLabel: "Yes, reject",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
67
134
|
PRODUCT: {
|
|
68
135
|
/*
|
|
69
136
|
* The buyer-facing verbs below shipped long ago; the four CRUD entries
|
|
@@ -929,6 +996,36 @@ export const ACTIONS = {
|
|
|
929
996
|
kind: "primary",
|
|
930
997
|
permissions: ["seller", "admin"],
|
|
931
998
|
},
|
|
999
|
+
"dispatch-custom": {
|
|
1000
|
+
iconKey: "ship",
|
|
1001
|
+
id: "store.dispatch-custom",
|
|
1002
|
+
label: "Record dispatch",
|
|
1003
|
+
ariaLabel: "Record a parcel you posted yourself",
|
|
1004
|
+
description: "Record the carrier and tracking id for a parcel the seller posted themselves.",
|
|
1005
|
+
kind: "primary",
|
|
1006
|
+
permissions: ["seller", "admin"],
|
|
1007
|
+
},
|
|
1008
|
+
"dispatch-platform": {
|
|
1009
|
+
iconKey: "ship",
|
|
1010
|
+
id: "store.dispatch-platform",
|
|
1011
|
+
label: "Book through the platform",
|
|
1012
|
+
ariaLabel: "Book this parcel through the platform's courier account",
|
|
1013
|
+
description: "Book a pickup on the platform's own carrier account. The tracking id comes back from the courier.",
|
|
1014
|
+
kind: "primary",
|
|
1015
|
+
permissions: ["seller", "admin"],
|
|
1016
|
+
/*
|
|
1017
|
+
* 🛑 Confirmed because it SPENDS MONEY and cannot be undone from this
|
|
1018
|
+
* screen. A booking debits the platform's carrier wallet and schedules
|
|
1019
|
+
* a real pickup; `bookShipment` then refuses a second attempt on the
|
|
1020
|
+
* same order, so a mis-click is not a thing the seller can take back by
|
|
1021
|
+
* pressing the button again.
|
|
1022
|
+
*/
|
|
1023
|
+
confirmation: {
|
|
1024
|
+
title: "Book this parcel through the platform?",
|
|
1025
|
+
body: "A courier pickup will be scheduled at the address you picked and the cost charged to the platform's carrier account. This cannot be undone from here.",
|
|
1026
|
+
confirmLabel: "Book pickup",
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
932
1029
|
"mark-installment-paid": {
|
|
933
1030
|
iconKey: "confirm",
|
|
934
1031
|
id: "store.mark-installment-paid",
|
|
@@ -1727,6 +1824,137 @@ export const ACTIONS = {
|
|
|
1727
1824
|
},
|
|
1728
1825
|
},
|
|
1729
1826
|
ADMIN: {
|
|
1827
|
+
/*
|
|
1828
|
+
* ── Integration switches (12b) ──────────────────────────────────
|
|
1829
|
+
*
|
|
1830
|
+
* 🛑 The confirmation BODY is deliberately empty on `disable-integration`,
|
|
1831
|
+
* and that is not an omission.
|
|
1832
|
+
*
|
|
1833
|
+
* `INTEGRATIONS[id].offConsequence` is the real body, and it differs per
|
|
1834
|
+
* integration by design — "payments off" abandons in-flight payments,
|
|
1835
|
+
* "support off" hides a contact route, "shipping off" selects a working
|
|
1836
|
+
* manual path and stops nothing. A generic "are you sure?" here would
|
|
1837
|
+
* flatten exactly the distinction that file exists to preserve, and a COPY
|
|
1838
|
+
* of each consequence string would be a second place for it to drift from
|
|
1839
|
+
* (Root Cause #42). `AdminIntegrationsView` composes the dialog body from
|
|
1840
|
+
* the descriptor at render time.
|
|
1841
|
+
*
|
|
1842
|
+
* The registry still owns the label, the aria-label, the kind, and the fact
|
|
1843
|
+
* that a confirmation is REQUIRED — which is what Rule #7 is about.
|
|
1844
|
+
*/
|
|
1845
|
+
"enable-integration": {
|
|
1846
|
+
iconKey: "approve",
|
|
1847
|
+
id: "admin.enable-integration",
|
|
1848
|
+
label: "Enable",
|
|
1849
|
+
ariaLabel: "Enable this integration",
|
|
1850
|
+
description: "Turn an integration on. Writes the authoritative settings field for it.",
|
|
1851
|
+
kind: "primary",
|
|
1852
|
+
permissions: ["admin"],
|
|
1853
|
+
},
|
|
1854
|
+
"update-pricing": {
|
|
1855
|
+
iconKey: "save",
|
|
1856
|
+
id: "admin.update-pricing",
|
|
1857
|
+
label: "Save new rate",
|
|
1858
|
+
ariaLabel: "Save the new rate",
|
|
1859
|
+
description: "Change one platform rate from now on. Orders, invoices and payouts already recorded keep the rate they were charged at — a rate change never restates history.",
|
|
1860
|
+
kind: "primary",
|
|
1861
|
+
permissions: ["admin"],
|
|
1862
|
+
/*
|
|
1863
|
+
* 🛑 A confirmation even though `kind` is "primary" rather than "danger".
|
|
1864
|
+
* The registry requires one for destructive actions; this one is here
|
|
1865
|
+
* because the act is IRREVERSIBLE IN A WAY THAT DOES NOT LOOK IT — the
|
|
1866
|
+
* rate moves forward only, so "undo" means a second change with a second
|
|
1867
|
+
* audit row, and nothing restores what was charged in between.
|
|
1868
|
+
*/
|
|
1869
|
+
confirmation: {
|
|
1870
|
+
title: "Change this rate?",
|
|
1871
|
+
body: "Every checkout from now on uses the new value. Past orders, invoices and payouts are untouched and keep the rate they were charged at.",
|
|
1872
|
+
confirmLabel: "Change the rate",
|
|
1873
|
+
},
|
|
1874
|
+
},
|
|
1875
|
+
"add-dispute-reason": {
|
|
1876
|
+
iconKey: "create",
|
|
1877
|
+
id: "admin.add-dispute-reason",
|
|
1878
|
+
label: "Add reason",
|
|
1879
|
+
ariaLabel: "Add a dispute reason",
|
|
1880
|
+
description: "Add a reason buyers can pick when reporting a problem. The reason's KIND decides which gate it is subject to; the wording is yours.",
|
|
1881
|
+
kind: "primary",
|
|
1882
|
+
permissions: ["admin"],
|
|
1883
|
+
},
|
|
1884
|
+
"retire-dispute-reason": {
|
|
1885
|
+
iconKey: "reject",
|
|
1886
|
+
id: "admin.retire-dispute-reason",
|
|
1887
|
+
label: "Retire",
|
|
1888
|
+
ariaLabel: "Retire this dispute reason",
|
|
1889
|
+
description: "Take a reason out of the buyer's picker. Never a delete — past disputes name it, and the order snapshots its label so history survives either way.",
|
|
1890
|
+
kind: "danger",
|
|
1891
|
+
permissions: ["admin"],
|
|
1892
|
+
confirmation: {
|
|
1893
|
+
title: "Retire this reason?",
|
|
1894
|
+
/*
|
|
1895
|
+
* Names what retiring does NOT do, because that is the part an agent
|
|
1896
|
+
* is most likely to be unsure about and the reason this is not a
|
|
1897
|
+
* delete. A vague "are you sure" would invite exactly the hesitation
|
|
1898
|
+
* the schema's soft-retire design exists to remove.
|
|
1899
|
+
*/
|
|
1900
|
+
body: "Buyers will stop seeing it on the dispute form. Disputes already raised under it keep their wording, and you can put it back at any time.",
|
|
1901
|
+
confirmLabel: "Retire it",
|
|
1902
|
+
confirmKind: "danger",
|
|
1903
|
+
},
|
|
1904
|
+
},
|
|
1905
|
+
"reactivate-dispute-reason": {
|
|
1906
|
+
iconKey: "approve",
|
|
1907
|
+
id: "admin.reactivate-dispute-reason",
|
|
1908
|
+
label: "Reactivate",
|
|
1909
|
+
ariaLabel: "Put this dispute reason back in the picker",
|
|
1910
|
+
description: "Offer a retired reason to buyers again. The original author is kept.",
|
|
1911
|
+
kind: "primary",
|
|
1912
|
+
permissions: ["admin"],
|
|
1913
|
+
},
|
|
1914
|
+
"disable-integration": {
|
|
1915
|
+
iconKey: "reject",
|
|
1916
|
+
id: "admin.disable-integration",
|
|
1917
|
+
label: "Disable",
|
|
1918
|
+
ariaLabel: "Disable this integration",
|
|
1919
|
+
description: "Turn an integration off. The consequence differs per integration and is stated on the row.",
|
|
1920
|
+
kind: "danger",
|
|
1921
|
+
permissions: ["admin"],
|
|
1922
|
+
confirmation: {
|
|
1923
|
+
title: "Switch this integration off?",
|
|
1924
|
+
body: "",
|
|
1925
|
+
confirmLabel: "Switch it off",
|
|
1926
|
+
confirmKind: "danger",
|
|
1927
|
+
},
|
|
1928
|
+
},
|
|
1929
|
+
"enable-all-integrations": {
|
|
1930
|
+
iconKey: "approve",
|
|
1931
|
+
id: "admin.enable-all-integrations",
|
|
1932
|
+
label: "Enable all",
|
|
1933
|
+
ariaLabel: "Enable every integration",
|
|
1934
|
+
description: "Turn every integration on in a single write.",
|
|
1935
|
+
kind: "secondary",
|
|
1936
|
+
permissions: ["admin"],
|
|
1937
|
+
confirmation: {
|
|
1938
|
+
title: "Enable every integration?",
|
|
1939
|
+
body: "Online payments, customer email, courier booking and the support surface will all be switched on. One write, one audit-log entry.",
|
|
1940
|
+
confirmLabel: "Enable everything",
|
|
1941
|
+
},
|
|
1942
|
+
},
|
|
1943
|
+
"disable-all-integrations": {
|
|
1944
|
+
iconKey: "reject",
|
|
1945
|
+
id: "admin.disable-all-integrations",
|
|
1946
|
+
label: "Disable all",
|
|
1947
|
+
ariaLabel: "Disable every integration",
|
|
1948
|
+
description: "Turn every integration off in a single write.",
|
|
1949
|
+
kind: "danger",
|
|
1950
|
+
permissions: ["admin"],
|
|
1951
|
+
confirmation: {
|
|
1952
|
+
title: "Disable every integration?",
|
|
1953
|
+
body: "This stops online payments, customer email and courier booking, and hides the buyer support surface. 🛑 Payments already in progress are abandoned — a buyer part-way through the gateway may be charged with no order placed. Staff email and open tickets stay workable. One write, one audit-log entry.",
|
|
1954
|
+
confirmLabel: "Disable everything",
|
|
1955
|
+
confirmKind: "danger",
|
|
1956
|
+
},
|
|
1957
|
+
},
|
|
1730
1958
|
// ── Product moderation ─────────────────────────────────────────────────
|
|
1731
1959
|
"approve-product": {
|
|
1732
1960
|
iconKey: "approve",
|
|
@@ -1883,6 +2111,46 @@ export const ACTIONS = {
|
|
|
1883
2111
|
confirmKind: "danger",
|
|
1884
2112
|
},
|
|
1885
2113
|
},
|
|
2114
|
+
/*
|
|
2115
|
+
* D55 — the pre-order BALANCE's two review verdicts.
|
|
2116
|
+
*
|
|
2117
|
+
* 🛑 Deliberately separate from `verify-payment` / `reject-payment-fraud`
|
|
2118
|
+
* above, which review the DEPOSIT. A pre-order's two payments are months
|
|
2119
|
+
* apart, and the consequences do not match: rejecting a deposit proof
|
|
2120
|
+
* cancels the order, restores stock and bans the account for 7 days,
|
|
2121
|
+
* because the order had no legitimate basis. Rejecting a BALANCE proof on
|
|
2122
|
+
* goods already made and allocated must do none of that — it asks for a
|
|
2123
|
+
* better screenshot. Reusing the deposit's action would attach a 7-day
|
|
2124
|
+
* ban to a blurry photo.
|
|
2125
|
+
*/
|
|
2126
|
+
"verify-balance": {
|
|
2127
|
+
iconKey: "verify",
|
|
2128
|
+
id: "admin.verify-balance",
|
|
2129
|
+
label: "Accept Balance",
|
|
2130
|
+
ariaLabel: "Accept the pre-order balance payment proof",
|
|
2131
|
+
description: "Confirms the pre-order balance was received. The order becomes shippable. Does NOT post a ledger entry — the sale was recognised in full at order creation.",
|
|
2132
|
+
kind: "primary",
|
|
2133
|
+
permissions: ["admin", "moderator"],
|
|
2134
|
+
confirmation: {
|
|
2135
|
+
title: "Accept the balance payment?",
|
|
2136
|
+
body: "This records the remaining balance as received and clears the order to ship. Check the reference against your bank first.",
|
|
2137
|
+
confirmLabel: "Yes, accept",
|
|
2138
|
+
},
|
|
2139
|
+
},
|
|
2140
|
+
"reject-balance": {
|
|
2141
|
+
iconKey: "reject",
|
|
2142
|
+
id: "admin.reject-balance",
|
|
2143
|
+
label: "Reject Balance Proof",
|
|
2144
|
+
ariaLabel: "Reject the pre-order balance proof and ask the buyer to resend",
|
|
2145
|
+
description: "Sends the proof back with your note. The buyer can submit again — no cancellation, no stock change, no ban: the goods are already made and allocated to them.",
|
|
2146
|
+
kind: "secondary",
|
|
2147
|
+
permissions: ["admin", "moderator"],
|
|
2148
|
+
confirmation: {
|
|
2149
|
+
title: "Reject this balance proof?",
|
|
2150
|
+
body: "The buyer is told why and can send another. The order stays open and the item stays theirs.",
|
|
2151
|
+
confirmLabel: "Reject and ask again",
|
|
2152
|
+
},
|
|
2153
|
+
},
|
|
1886
2154
|
"verify-vendor": {
|
|
1887
2155
|
iconKey: "verify",
|
|
1888
2156
|
id: "admin.verify-vendor",
|
|
@@ -521,10 +521,39 @@ export declare function resolvePointsTender(input: {
|
|
|
521
521
|
* shown and charged figures diverge on exactly the carts where the balance is
|
|
522
522
|
* tight.
|
|
523
523
|
*/
|
|
524
|
-
|
|
524
|
+
/** One order group's payable total, and whether its store takes points. */
|
|
525
|
+
export interface PointsBase {
|
|
525
526
|
total: number;
|
|
526
527
|
accepted: boolean;
|
|
527
|
-
}
|
|
528
|
+
}
|
|
529
|
+
/** Everything one group contributes to its own payable total. */
|
|
530
|
+
export interface PointsBaseParts {
|
|
531
|
+
/** Goods subtotal for the group, before any discount. */
|
|
532
|
+
goods: number;
|
|
533
|
+
couponDiscount: number;
|
|
534
|
+
shippingFee: number;
|
|
535
|
+
/** Gift wrap + shipment protection + any other per-store add-on. */
|
|
536
|
+
addonFee: number;
|
|
537
|
+
platformFee: number;
|
|
538
|
+
gstOnFee: number;
|
|
539
|
+
acceptsPoints: boolean;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* The payable total a points allocation is measured against.
|
|
543
|
+
*
|
|
544
|
+
* 🛑 ONE implementation, because it was two. `POST /api/payment/create-order`
|
|
545
|
+
* and `verifyAndPlaceRazorpayOrderAction`'s amount guard each built this sum
|
|
546
|
+
* by hand, in the same shape, from the same six parts — and they are the
|
|
547
|
+
* *shown* figure and the *charged* figure for the same cart. Two hand-written
|
|
548
|
+
* copies of one money rule is Root Cause #75 exactly, and the failure mode is
|
|
549
|
+
* the worst kind: they only disagree on carts where the arithmetic differs, so
|
|
550
|
+
* nothing errors and the buyer is quoted one number and charged another.
|
|
551
|
+
*
|
|
552
|
+
* The coupon is floored at zero before the fees are added, never after — a
|
|
553
|
+
* discount larger than the goods must not eat the shipping and platform fee.
|
|
554
|
+
*/
|
|
555
|
+
export declare function pointsBaseForGroup(parts: PointsBaseParts): PointsBase;
|
|
556
|
+
export declare function allocatePointsAcrossGroups(groups: readonly PointsBase[], balance: number, optedIn: boolean): {
|
|
528
557
|
perGroup: number[];
|
|
529
558
|
pointsApplied: number;
|
|
530
559
|
};
|
|
@@ -715,43 +715,29 @@ export function resolvePointsTender(input) {
|
|
|
715
715
|
return { pointsApplied, amountDue: roundRupees(total - pointsApplied) };
|
|
716
716
|
}
|
|
717
717
|
/**
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
*
|
|
729
|
-
*
|
|
730
|
-
* | `POST /api/payment/create-order` | the figure CAPTURED |
|
|
731
|
-
* | the post-capture guard | the figure compared |
|
|
732
|
-
* | the two order paths | the per-group figure recorded |
|
|
733
|
-
*
|
|
734
|
-
* D37 is the record of what happens when one of those four is left out: the
|
|
735
|
-
* coupon discount reached three of them and not the capture route, so the
|
|
736
|
-
* buyer was shown one number, charged another, and the order recorded the
|
|
737
|
-
* first — invisible in our own data because the guard was one-sided.
|
|
738
|
-
*
|
|
739
|
-
* 🛑 The order paths deliberately do NOT call this. They apply
|
|
740
|
-
* `resolvePointsTender` to their own group as they create it, drawing down a
|
|
741
|
-
* shared remainder — which is the same function applied in the same order, so
|
|
742
|
-
* the sum is identical by construction. Making them call this instead would
|
|
743
|
-
* mean computing every group's final total before creating any of them, which
|
|
744
|
-
* is a restructure of two 200-line functions for an answer they already have.
|
|
745
|
-
*
|
|
746
|
-
* ## The cap is per group, and the ORDER of the groups therefore matters
|
|
747
|
-
*
|
|
748
|
-
* A balance smaller than the whole checkout's 50% allowance is consumed by the
|
|
749
|
-
* earliest groups. That is arbitrary but it must be *consistently* arbitrary:
|
|
750
|
-
* every caller iterates `orderGroups` in the order `splitCartIntoOrderGroups`
|
|
751
|
-
* produced, so all four agree. Sorting differently anywhere would make the
|
|
752
|
-
* shown and charged figures diverge on exactly the carts where the balance is
|
|
753
|
-
* tight.
|
|
718
|
+
* The payable total a points allocation is measured against.
|
|
719
|
+
*
|
|
720
|
+
* 🛑 ONE implementation, because it was two. `POST /api/payment/create-order`
|
|
721
|
+
* and `verifyAndPlaceRazorpayOrderAction`'s amount guard each built this sum
|
|
722
|
+
* by hand, in the same shape, from the same six parts — and they are the
|
|
723
|
+
* *shown* figure and the *charged* figure for the same cart. Two hand-written
|
|
724
|
+
* copies of one money rule is Root Cause #75 exactly, and the failure mode is
|
|
725
|
+
* the worst kind: they only disagree on carts where the arithmetic differs, so
|
|
726
|
+
* nothing errors and the buyer is quoted one number and charged another.
|
|
727
|
+
*
|
|
728
|
+
* The coupon is floored at zero before the fees are added, never after — a
|
|
729
|
+
* discount larger than the goods must not eat the shipping and platform fee.
|
|
754
730
|
*/
|
|
731
|
+
export function pointsBaseForGroup(parts) {
|
|
732
|
+
return {
|
|
733
|
+
total: roundRupees(Math.max(0, parts.goods - parts.couponDiscount) +
|
|
734
|
+
parts.shippingFee +
|
|
735
|
+
parts.addonFee +
|
|
736
|
+
parts.platformFee +
|
|
737
|
+
parts.gstOnFee),
|
|
738
|
+
accepted: parts.acceptsPoints,
|
|
739
|
+
};
|
|
740
|
+
}
|
|
755
741
|
export function allocatePointsAcrossGroups(groups, balance, optedIn) {
|
|
756
742
|
const perGroup = groups.map(() => 0);
|
|
757
743
|
if (!optedIn)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { ListingCheckoutRule, CategoryCheckoutRule, CartItemProductPair, OrderItemInput, RefundPolicy, } from "./types";
|
|
2
2
|
export { CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, } from "./_registry";
|
|
3
|
-
export { assertPaymentMethodAllowed, blockedMethodsForCart, type BlockedMethod, } from "./payment-gate";
|
|
3
|
+
export { assertPaymentMethodAllowed, assertEmiOptInAllowed, blockedMethodsForCart, type BlockedMethod, } from "./payment-gate";
|
|
4
4
|
export { CART_MAX_ITEMS, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_limits";
|
|
5
5
|
export { standardRule } from "./standard.rule";
|
|
6
6
|
export { auctionRule } from "./auction.rule";
|
|
@@ -6,7 +6,7 @@ export { CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, getListingRule, getCategoryRul
|
|
|
6
6
|
* apart is what stops someone folding it back into preflight, where it would
|
|
7
7
|
* silently stop covering bundles and grouped lines.
|
|
8
8
|
*/
|
|
9
|
-
export { assertPaymentMethodAllowed, blockedMethodsForCart, } from "./payment-gate.js";
|
|
9
|
+
export { assertPaymentMethodAllowed, assertEmiOptInAllowed, blockedMethodsForCart, } from "./payment-gate.js";
|
|
10
10
|
export { CART_MAX_ITEMS, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_limits.js";
|
|
11
11
|
// Individual rules — exported for tests and direct access.
|
|
12
12
|
export { standardRule } from "./standard.rule.js";
|
|
@@ -24,3 +24,21 @@ export declare function blockedMethodsForCart(items: readonly CartItemDocument[]
|
|
|
24
24
|
* decision.
|
|
25
25
|
*/
|
|
26
26
|
export declare function assertPaymentMethodAllowed(items: readonly CartItemDocument[], productById: ReadonlyMap<string, ProductDocument>, paymentMethod: CheckoutPaymentMethod | undefined): void;
|
|
27
|
+
/**
|
|
28
|
+
* Throw when anything in the cart forbids an instalment plan.
|
|
29
|
+
*
|
|
30
|
+
* 🛑 Reads the SAME `blockedPaymentMethods` declaration, under the key
|
|
31
|
+
* `"emi"`, rather than introducing a parallel `blocksEmi` field. The listing
|
|
32
|
+
* rules already say "this listing type cannot be paid in instalments" — they
|
|
33
|
+
* said it by naming a payment method, because that is what EMI was. Once EMI
|
|
34
|
+
* became an order-level opt-in, that declaration needed a second reader, not
|
|
35
|
+
* a second declaration: a rule stated twice is a rule that drifts (Root Cause
|
|
36
|
+
* #61), and `prizeDrawRule`'s entry would otherwise have gone inert the day
|
|
37
|
+
* the tender disappeared.
|
|
38
|
+
*
|
|
39
|
+
* `blockedPaymentMessage` is reused verbatim for the same reason the gate
|
|
40
|
+
* above prefers it to a generated sentence: it names the REASON (winner
|
|
41
|
+
* assignment waits on confirmed payment), which is what lets a buyer choose
|
|
42
|
+
* differently instead of abandoning.
|
|
43
|
+
*/
|
|
44
|
+
export declare function assertEmiOptInAllowed(items: readonly CartItemDocument[], productById: ReadonlyMap<string, ProductDocument>, emiOptIn: boolean): void;
|
|
@@ -131,3 +131,28 @@ export function assertPaymentMethodAllowed(items, productById, paymentMethod) {
|
|
|
131
131
|
? `${offender.message} Available instead: ${allowed.join(", ")}.`
|
|
132
132
|
: `${offender.message} Nothing in this cart's combination can be paid for — remove the item and buy it on its own.`), { code: "PAYMENT_METHOD_BLOCKED" });
|
|
133
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Throw when anything in the cart forbids an instalment plan.
|
|
136
|
+
*
|
|
137
|
+
* 🛑 Reads the SAME `blockedPaymentMethods` declaration, under the key
|
|
138
|
+
* `"emi"`, rather than introducing a parallel `blocksEmi` field. The listing
|
|
139
|
+
* rules already say "this listing type cannot be paid in instalments" — they
|
|
140
|
+
* said it by naming a payment method, because that is what EMI was. Once EMI
|
|
141
|
+
* became an order-level opt-in, that declaration needed a second reader, not
|
|
142
|
+
* a second declaration: a rule stated twice is a rule that drifts (Root Cause
|
|
143
|
+
* #61), and `prizeDrawRule`'s entry would otherwise have gone inert the day
|
|
144
|
+
* the tender disappeared.
|
|
145
|
+
*
|
|
146
|
+
* `blockedPaymentMessage` is reused verbatim for the same reason the gate
|
|
147
|
+
* above prefers it to a generated sentence: it names the REASON (winner
|
|
148
|
+
* assignment waits on confirmed payment), which is what lets a buyer choose
|
|
149
|
+
* differently instead of abandoning.
|
|
150
|
+
*/
|
|
151
|
+
export function assertEmiOptInAllowed(items, productById, emiOptIn) {
|
|
152
|
+
if (!emiOptIn)
|
|
153
|
+
return;
|
|
154
|
+
const offender = blockedMethodsForCart(items, productById).get("emi");
|
|
155
|
+
if (!offender)
|
|
156
|
+
return;
|
|
157
|
+
throw Object.assign(new ValidationError(`${offender.message} Pay for this order in full instead, or remove the item and buy it on its own.`), { code: "EMI_NOT_ALLOWED" });
|
|
158
|
+
}
|
|
@@ -53,18 +53,6 @@ export interface SplitResolution {
|
|
|
53
53
|
/** The invariant holds. A caller must refuse to commit when false. */
|
|
54
54
|
valid: boolean;
|
|
55
55
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Resolve contributions against a landed cost.
|
|
58
|
-
*
|
|
59
|
-
* Pure. No I/O, no clock — the landed cost is computed by the caller from the
|
|
60
|
-
* order group it already has.
|
|
61
|
-
*
|
|
62
|
-
* 🛑 ROUNDS ONCE, at the end of each member's amount, and then puts the
|
|
63
|
-
* ROUNDING REMAINDER on the last member. Rounding each percentage
|
|
64
|
-
* independently and summing is how a 3-way 33.33% split charges a rupee less
|
|
65
|
-
* than the agreed total and the surplus silently goes negative — the same
|
|
66
|
-
* remainder discipline `allocateAcrossMembers` uses for bundle tax.
|
|
67
|
-
*/
|
|
68
56
|
export declare function resolveSplit(contributions: readonly SplitContribution[], landedCost: number): SplitResolution;
|
|
69
57
|
/**
|
|
70
58
|
* The price to lock on the line, given the goods subtotal and a surplus.
|
|
@@ -41,10 +41,30 @@ import { roundRupees } from "../../../utils/number.formatter.js";
|
|
|
41
41
|
* than the agreed total and the surplus silently goes negative — the same
|
|
42
42
|
* remainder discipline `allocateAcrossMembers` uses for bundle tax.
|
|
43
43
|
*/
|
|
44
|
+
/**
|
|
45
|
+
* `percent` of `amount`, where `percent` is 0–100.
|
|
46
|
+
*
|
|
47
|
+
* 🛑 A NAMED HELPER rather than the division written inline, for legibility
|
|
48
|
+
* to a reader AND to `audit-money-units`. That audit flags a scale division
|
|
49
|
+
* beside a money identifier because it is the exact shape of the 2026-05
|
|
50
|
+
* minor-unit bug — a conversion left behind by a migration — and a
|
|
51
|
+
* percentage conversion is visually identical to it. Two such lines are two
|
|
52
|
+
* judgement calls; one helper is a single place to establish that this
|
|
53
|
+
* divisor is about PERCENTAGES and never about currency scale.
|
|
54
|
+
*
|
|
55
|
+
* 🛑 That audit reads COMMENTS as well as code, so this one cannot quote the
|
|
56
|
+
* expression it is about: a worked example of the shape is indistinguishable
|
|
57
|
+
* from the shape. Reading comments is correct of it — a stale "stored in
|
|
58
|
+
* minor units" remark in prose is one of the things it hunts, and Root Cause
|
|
59
|
+
* #87 is the opposite mistake, an audit that stripped comments and so
|
|
60
|
+
* counted a commented-out registration as live.
|
|
61
|
+
*/
|
|
62
|
+
// audit-money-units-ok: a percent-to-fraction conversion, not a minor-unit conversion
|
|
63
|
+
const applyPercent = (percent, amount) => (percent / 100) * amount;
|
|
44
64
|
export function resolveSplit(contributions, landedCost) {
|
|
45
65
|
const resolved = [];
|
|
46
66
|
for (const c of contributions) {
|
|
47
|
-
const raw = c.splitMode === "percent" ? (
|
|
67
|
+
const raw = c.splitMode === "percent" ? applyPercent(c.percent ?? 0, landedCost) : (c.amount ?? 0);
|
|
48
68
|
resolved.push({ productId: c.productId, amount: roundRupees(raw) });
|
|
49
69
|
}
|
|
50
70
|
/*
|
|
@@ -59,7 +79,7 @@ export function resolveSplit(contributions, landedCost) {
|
|
|
59
79
|
const allPercent = contributions.every((c) => c.splitMode === "percent");
|
|
60
80
|
if (allPercent && resolved.length > 0) {
|
|
61
81
|
const pctTotal = contributions.reduce((s, c) => s + (c.percent ?? 0), 0);
|
|
62
|
-
const target = roundRupees((pctTotal
|
|
82
|
+
const target = roundRupees(applyPercent(pctTotal, landedCost));
|
|
63
83
|
const sum = resolved.reduce((s, r) => s + r.amount, 0);
|
|
64
84
|
const drift = roundRupees(target - sum);
|
|
65
85
|
if (drift !== 0) {
|
|
@@ -78,7 +78,7 @@ export const DISCLAIMERS = {
|
|
|
78
78
|
body: "Entry requires a purchase and is void where prohibited by local law. Residents of states that " +
|
|
79
79
|
"restrict prize draws or games of chance are not eligible, and an entry from such a state will be " +
|
|
80
80
|
"refunded rather than drawn. Prizes are as described on the listing, are not transferable and cannot " +
|
|
81
|
-
"be exchanged for cash. The draw is run by
|
|
81
|
+
"be exchanged for cash. The draw is run by {{siteName}} and is not affiliated with any government lottery.",
|
|
82
82
|
tone: "warning",
|
|
83
83
|
surfaces: ["PrizeDrawDetailPageView", "/prize-draws", "/lottery", "PrizeRevealModal"],
|
|
84
84
|
},
|
|
@@ -90,7 +90,7 @@ export const DISCLAIMERS = {
|
|
|
90
90
|
*/
|
|
91
91
|
"not-investment-advice": {
|
|
92
92
|
body: "Prices shown are past transactions, not a valuation, a projection or a guarantee. Collectibles can " +
|
|
93
|
-
"fall in value as easily as rise, may be difficult to sell at any price, and
|
|
93
|
+
"fall in value as easily as rise, may be difficult to sell at any price, and {{siteName}} does not provide " +
|
|
94
94
|
"investment advice. Nothing here should be relied on as a reason to buy.",
|
|
95
95
|
tone: "legal",
|
|
96
96
|
surfaces: ["PriceIndexView", "RealisedPriceStrip", "/franchise", "ProductDetailPageView"],
|
|
@@ -108,7 +108,7 @@ export const DISCLAIMERS = {
|
|
|
108
108
|
surfaces: ["PriceIndexView", "PriceBandChart"],
|
|
109
109
|
},
|
|
110
110
|
"authenticity": {
|
|
111
|
-
body: "
|
|
111
|
+
body: "{{siteName}} is not an authentication or grading service. We do not inspect, test or re-grade items, " +
|
|
112
112
|
"and a grade shown on a listing is the one the seller states a third-party grader assigned. " +
|
|
113
113
|
"Guidance on spotting a counterfeit is general information, not a verification of any particular item.",
|
|
114
114
|
tone: "legal",
|
|
@@ -125,7 +125,7 @@ export const DISCLAIMERS = {
|
|
|
125
125
|
*/
|
|
126
126
|
"protection-cover": {
|
|
127
127
|
heading: "What protection covers",
|
|
128
|
-
body: "Buyer protection is cover provided by
|
|
128
|
+
body: "Buyer protection is cover provided by {{siteName}}. It is **not insurance** and there is no insurer. " +
|
|
129
129
|
"Shipment protection covers loss or damage in transit and must be claimed within " +
|
|
130
130
|
"{{shipmentClaimHours}} hours of delivery. Sale protection covers an item up to " +
|
|
131
131
|
"{{saleProtectionMaxValue}} and must be claimed within {{saleClaimHours}} hours of delivery. " +
|
|
@@ -142,7 +142,7 @@ export const DISCLAIMERS = {
|
|
|
142
142
|
"accounting-output": {
|
|
143
143
|
body: "These are management figures produced from this platform's own records. They are not audited " +
|
|
144
144
|
"accounts and have not been reviewed by an accountant. The GSTR-1 export is a draft for your " +
|
|
145
|
-
"accountant to check and use — nothing here is filed with any tax authority, and
|
|
145
|
+
"accountant to check and use — nothing here is filed with any tax authority, and {{siteName}} has no " +
|
|
146
146
|
"connection to the GST portal.",
|
|
147
147
|
tone: "warning",
|
|
148
148
|
surfaces: ["AdminInvoicingView", "AdminAnalyticsView", "ProfitLossView", "TrialBalanceView"],
|
|
@@ -172,7 +172,7 @@ export const DISCLAIMERS = {
|
|
|
172
172
|
},
|
|
173
173
|
"emi": {
|
|
174
174
|
body: "EMI adds a surcharge to the order total, shown before you pay. The instalment plan is between you " +
|
|
175
|
-
"and
|
|
175
|
+
"and {{siteName}} rather than a bank, and a missed instalment may suspend access to the order and is " +
|
|
176
176
|
"recoverable as a debt.",
|
|
177
177
|
tone: "legal",
|
|
178
178
|
surfaces: ["CheckoutRouteClient", "/fees", "EmiScheduleTable"],
|
|
@@ -185,7 +185,7 @@ export const DISCLAIMERS = {
|
|
|
185
185
|
},
|
|
186
186
|
"user-content": {
|
|
187
187
|
body: "Reviews, catalogue entries and comments are the opinions of the people who wrote them, not " +
|
|
188
|
-
"
|
|
188
|
+
"{{siteName}}'s. We remove content that breaks our rules when it is reported, but we do not verify every " +
|
|
189
189
|
"claim made in it.",
|
|
190
190
|
tone: "legal",
|
|
191
191
|
surfaces: ["ReviewsList", "ReviewDetailShell", "PublicProfileView", "BlogPostView"],
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* sentence with a word missing, which nobody reports, while `{{wrongKey}}` on
|
|
45
45
|
* a live page gets reported in minutes.
|
|
46
46
|
*/
|
|
47
|
-
export type PublishedValueKey = "returnWindowDays" | "sellerAcceptDays" | "sellerDispatchBusinessDays" | "returnRespondHours" | "returnPostDays" | "offerCheckoutHours" | "paymentWindowMinutes" | "deliveryConfirmDays" | "orderCompleteDays" | "shipmentClaimHours" | "saleClaimHours" | "disputeTransitWindowDays" | "disputeNotReachedGraceDays" | "platformFeeMax" | "platformFeePercent" | "shipmentProtectionPercent" | "shipmentProtectionMin" | "saleProtectionPercent" | "saleProtectionMin" | "saleProtectionMaxValue" | "codHandlingPercent" | "codDepositPercent" | "giftWrapFee" | "pointsEarnPercent" | "pointsMaxRedemptionPercent" | "pointsExpiryMonths" | "refundBusinessDaysMin" | "refundBusinessDaysMax";
|
|
47
|
+
export type PublishedValueKey = "returnWindowDays" | "sellerAcceptDays" | "sellerDispatchBusinessDays" | "returnRespondHours" | "returnPostDays" | "offerCheckoutHours" | "paymentWindowMinutes" | "deliveryConfirmDays" | "orderCompleteDays" | "shipmentClaimHours" | "saleClaimHours" | "disputeTransitWindowDays" | "disputeNotReachedGraceDays" | "platformFeeMax" | "platformFeePercent" | "shipmentProtectionPercent" | "shipmentProtectionMin" | "saleProtectionPercent" | "saleProtectionMin" | "saleProtectionMaxValue" | "codHandlingPercent" | "codDepositPercent" | "giftWrapFee" | "pointsEarnPercent" | "pointsMaxRedemptionPercent" | "pointsExpiryMonths" | "refundBusinessDaysMin" | "refundBusinessDaysMax" | "siteName";
|
|
48
48
|
export type PublishedValues = Record<PublishedValueKey, string>;
|
|
49
49
|
/**
|
|
50
50
|
* The settings this registry reads, where a value genuinely is configurable.
|
|
@@ -56,6 +56,11 @@ export type PublishedValues = Record<PublishedValueKey, string>;
|
|
|
56
56
|
* TypeScript's structural typing strips nothing at runtime).
|
|
57
57
|
*/
|
|
58
58
|
export interface PublishedValueSettings {
|
|
59
|
+
/**
|
|
60
|
+
* The operator's display name, for the disclaimers that name who is making
|
|
61
|
+
* the statement. Falls back to `SITE_CONFIG.brand.name`.
|
|
62
|
+
*/
|
|
63
|
+
siteName?: string;
|
|
59
64
|
platformFeeMax?: number;
|
|
60
65
|
platformFeePercent?: number;
|
|
61
66
|
shipmentProtectionFeePercent?: number;
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* somebody has to remember — and it is why there is no literal for the offer
|
|
41
41
|
* deadline or the claim windows even though both appear in published copy.
|
|
42
42
|
*/
|
|
43
|
+
import { SITE_CONFIG } from "../../../core/site-config.js";
|
|
43
44
|
import { OBLIGATION_POLICIES } from "../../../features/obligations/constants/policies.js";
|
|
44
45
|
import { POINTS_EARN_PERCENT, POINTS_EXPIRY_MONTHS, POINTS_MAX_REDEMPTION_PERCENT, } from "../../../features/points/schemas/firestore.js";
|
|
45
46
|
import { PAYMENT_WINDOW_MINUTES } from "../../../features/orders/constants/payment-window.js";
|
|
@@ -171,6 +172,13 @@ export function resolvePublishedValues(settings) {
|
|
|
171
172
|
*/
|
|
172
173
|
refundBusinessDaysMin: "5",
|
|
173
174
|
refundBusinessDaysMax: "7",
|
|
175
|
+
/*
|
|
176
|
+
* `SITE_CONFIG.brand.name` is appkit's own neutral default ("App") until a
|
|
177
|
+
* consumer overrides it, which is exactly the right failure mode here: a
|
|
178
|
+
* fork that forgets to configure its brand publishes a visibly generic
|
|
179
|
+
* disclaimer rather than silently attributing its legal statements to us.
|
|
180
|
+
*/
|
|
181
|
+
siteName: settings?.siteName ?? SITE_CONFIG.brand.name,
|
|
174
182
|
};
|
|
175
183
|
}
|
|
176
184
|
/**
|