@mohasinac/appkit 4.22.0 → 4.24.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/_internal/server/features/bundles/actions.js +7 -0
- package/dist/_internal/server/features/checkout/actions.js +24 -16
- package/dist/_internal/server/features/checkout/locked-lines.js +12 -2
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +5 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +10 -4
- package/dist/_internal/server/jobs/core/offerExpiry.js +10 -2
- package/dist/_internal/server/jobs/core/payoutBatch.js +15 -3
- package/dist/_internal/shared/actions/action-registry.js +41 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +23 -5
- package/dist/_internal/shared/checkout/order-math.js +29 -6
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +12 -2
- package/dist/_internal/shared/checkout/rules/_limits.js +12 -2
- package/dist/_internal/shared/features/cart/config.d.ts +2 -1
- package/dist/_internal/shared/features/cart/config.js +2 -1
- package/dist/_internal/shared/features/cart/schema.d.ts +6 -6
- package/dist/_internal/shared/features/notifications/action-url.d.ts +47 -0
- package/dist/_internal/shared/features/notifications/action-url.js +111 -0
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/types.d.ts +16 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/field-names.d.ts +12 -0
- package/dist/constants/field-names.js +12 -0
- package/dist/constants/notification-types.d.ts +38 -10
- package/dist/constants/notification-types.js +39 -28
- package/dist/features/account/types/index.d.ts +1 -10
- package/dist/features/admin/actions/admin-actions.js +13 -3
- package/dist/features/admin/actions/notification-actions.d.ts +12 -0
- package/dist/features/admin/actions/notification-actions.js +149 -4
- package/dist/features/admin/components/AdminBidsView.js +7 -1
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +5 -1
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
- package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +5 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +3 -2
- package/dist/features/admin/schemas/firestore.d.ts +14 -3
- package/dist/features/admin/schemas/firestore.js +79 -0
- package/dist/features/auctions/repository/bid.repository.d.ts +21 -8
- package/dist/features/auctions/repository/bid.repository.js +57 -33
- package/dist/features/auctions/schemas/firestore.d.ts +27 -0
- package/dist/features/auctions/schemas/firestore.js +21 -0
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/cart/actions/cart-actions.d.ts +16 -0
- package/dist/features/cart/actions/cart-actions.js +26 -12
- package/dist/features/cart/components/CartDrawer.js +2 -1
- package/dist/features/cart/schemas/firestore.d.ts +132 -8
- package/dist/features/cart/schemas/firestore.js +22 -0
- package/dist/features/catalogue/actions/approve-catalogue-listing.js +7 -6
- package/dist/features/catalogue/actions/list-from-catalogue.js +2 -2
- package/dist/features/catalogue/actions/submit-for-approval.js +1 -4
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +5 -1
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +23 -0
- package/dist/features/catalogue/repository/catalogue.repository.js +28 -1
- package/dist/features/catalogue/schemas/firestore.d.ts +19 -0
- package/dist/features/catalogue/schemas/firestore.js +12 -0
- package/dist/features/categories/schemas/index.d.ts +2 -2
- package/dist/features/email/index.d.ts +1 -1
- package/dist/features/email/index.js +5 -1
- package/dist/features/email/notification-templates.d.ts +35 -0
- package/dist/features/email/notification-templates.js +96 -0
- package/dist/features/messages/schemas/index.d.ts +6 -6
- package/dist/features/orders/schemas/index.d.ts +14 -14
- package/dist/features/payments/repository/payout.repository.d.ts +18 -5
- package/dist/features/payments/repository/payout.repository.js +45 -19
- package/dist/features/payments/schemas/firestore.d.ts +33 -1
- package/dist/features/payments/schemas/firestore.js +39 -0
- package/dist/features/payments/schemas/index.d.ts +12 -12
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/promotions/schemas/index.d.ts +12 -12
- package/dist/features/scams/repository/scammer.repository.d.ts +18 -1
- package/dist/features/scams/repository/scammer.repository.js +24 -2
- package/dist/features/scams/schemas/firestore.d.ts +24 -0
- package/dist/features/scams/schemas/firestore.js +30 -0
- package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -1
- package/dist/features/shipments/repository/shipments.repository.d.ts +18 -0
- package/dist/features/shipments/repository/shipments.repository.js +23 -1
- package/dist/features/shipments/schemas/firestore.d.ts +14 -0
- package/dist/features/shipments/schemas/firestore.js +19 -0
- package/dist/features/status-history/components/RecordStatusTimeline.d.ts +12 -0
- package/dist/features/status-history/components/RecordStatusTimeline.js +90 -0
- package/dist/features/status-history/index.d.ts +2 -0
- package/dist/features/status-history/index.js +1 -0
- package/dist/features/stores/repository/store.repository.d.ts +30 -2
- package/dist/features/stores/repository/store.repository.js +43 -4
- package/dist/features/stores/schemas/firestore.d.ts +29 -0
- package/dist/features/stores/schemas/firestore.js +31 -0
- package/dist/features/support/repository/support.repository.d.ts +47 -3
- package/dist/features/support/repository/support.repository.js +72 -12
- package/dist/features/support/schemas/firestore.d.ts +38 -1
- package/dist/features/support/schemas/firestore.js +34 -0
- package/dist/features/support/schemas/index.d.ts +2 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +84 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +15 -2
- package/dist/schemas/registry.d.ts +34 -34
- package/dist/seed/bids-seed-data.js +8 -0
- package/dist/seed/factories/notification.factory.d.ts +2 -1
- package/dist/server-entry.d.ts +1 -0
- package/dist/server-entry.js +5 -0
- package/dist/server.d.ts +5 -1
- package/dist/server.js +10 -1
- package/dist/styles.css +58 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/QuantityStepper.d.ts +38 -0
- package/dist/ui/components/QuantityStepper.js +20 -0
- package/dist/ui/components/QuantityStepper.style.css +56 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { NotFoundError, ValidationError } from "../../../../errors";
|
|
16
16
|
import { categoriesRepository } from "../../../../repositories";
|
|
17
17
|
import { cartRepository } from "../../../../features/cart/repository/cart.repository";
|
|
18
|
+
import { assertCanAddNewItems } from "../../../../features/cart/actions/cart-actions";
|
|
18
19
|
import { getDefaultCurrency } from "../../../../core/baseline-resolver";
|
|
19
20
|
export async function addBundleToCartAction(userId, bundleSlug) {
|
|
20
21
|
if (!userId)
|
|
@@ -32,6 +33,12 @@ export async function addBundleToCartAction(userId, bundleSlug) {
|
|
|
32
33
|
if (!bundle.bundlePrice || bundle.bundlePrice < 1) {
|
|
33
34
|
throw new ValidationError("Bundle price is not configured");
|
|
34
35
|
}
|
|
36
|
+
// Bundles bypass `addItemToCart`'s capability gate deliberately (a bundle's
|
|
37
|
+
// `listingType: "standard"` is a lie told to satisfy it — `bundleRule` is
|
|
38
|
+
// `cartEligible: false`). The LANE gate is not optional though, and this path
|
|
39
|
+
// silently skipped it: a buyer with an unpaid auction win could keep shopping
|
|
40
|
+
// by adding bundles.
|
|
41
|
+
await assertCanAddNewItems(userId);
|
|
35
42
|
await cartRepository.addItem(userId, {
|
|
36
43
|
productId: bundle.id,
|
|
37
44
|
productTitle: bundle.name,
|
|
@@ -14,7 +14,7 @@ import { ORDER_FIELDS } from "../../../../constants/field-names";
|
|
|
14
14
|
import { serverLogger } from "../../../../monitoring";
|
|
15
15
|
import { unitOfWork, siteSettingsRepository, userRepository, storeRepository, couponsRepository, claimedCouponsRepository, addressesRepository, productRepository, } from "../../../../repositories";
|
|
16
16
|
import { calculateGst } from "../../../shared/fees/calculator";
|
|
17
|
-
import { computePreOrderDepositAmount, unitPriceFor } from "../../../shared/checkout/order-math";
|
|
17
|
+
import { computePreOrderDepositAmount, lineTotalFor, unitPriceFor } from "../../../shared/checkout/order-math";
|
|
18
18
|
import { failedCheckoutRepository } from "../../../../features/checkout/repository/failed-checkout.repository";
|
|
19
19
|
import { sendOrderConfirmationEmail } from "../../../../features/contact/server";
|
|
20
20
|
import { splitCartIntoOrderGroups } from "../../../../features/orders/index";
|
|
@@ -374,7 +374,7 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
374
374
|
// Add-ons are this store's choice, not the cart's — see OrderGroupContext.
|
|
375
375
|
const { whatsappNotifyAddon = false, giftWrapAddon = false, giftWrapMessage, shipmentProtectionAddon = false, } = storeAddons?.[firstItem.storeId] ?? {};
|
|
376
376
|
const { platformFee = 0, gstOnFee: platformFeeGst = 0 } = platformFeeByStore.get(firstItem.storeId) ?? {};
|
|
377
|
-
const groupTotal = group.reduce((sum, { item, product }) => sum +
|
|
377
|
+
const groupTotal = group.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0);
|
|
378
378
|
// S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
|
|
379
379
|
const orderItems = group.map(({ item, product }) => {
|
|
380
380
|
const lt = (product.listingType ?? "standard");
|
|
@@ -415,7 +415,7 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
415
415
|
let taxableAmount = 0;
|
|
416
416
|
let gstAmount = 0;
|
|
417
417
|
for (const { item, product } of group) {
|
|
418
|
-
const lineTotal =
|
|
418
|
+
const lineTotal = lineTotalFor(item, product);
|
|
419
419
|
const rate = product.gstRate ?? 0;
|
|
420
420
|
if (rate > 0) {
|
|
421
421
|
taxableAmount += lineTotal;
|
|
@@ -774,8 +774,16 @@ export async function createCheckoutOrderAction(input) {
|
|
|
774
774
|
}
|
|
775
775
|
}
|
|
776
776
|
if (availableItems.length > 0) {
|
|
777
|
-
|
|
778
|
-
|
|
777
|
+
// Clear by LINE identity, not by productId. `itemId` is what uniquely
|
|
778
|
+
// identifies a line; `productId` is not unique across lines and is not
|
|
779
|
+
// even a product id on a bundle/group line (it points at the bundle
|
|
780
|
+
// category, or at the group parent — which is itself purchasable). So
|
|
781
|
+
// matching on it deleted every OTHER line that happened to share the
|
|
782
|
+
// value: a loose "Set" parent sitting beside a group line built from
|
|
783
|
+
// that same group, or two lines of one product, both vanished when only
|
|
784
|
+
// one was ordered.
|
|
785
|
+
const orderedItemIds = new Set(availableItems.map((a) => a.item.itemId));
|
|
786
|
+
const remainingCartItems = cart.items.filter((ci) => !orderedItemIds.has(ci.itemId));
|
|
779
787
|
const cartRef = db.collection(CART_COLLECTION).doc(uid);
|
|
780
788
|
tx.set(cartRef, {
|
|
781
789
|
items: remainingCartItems,
|
|
@@ -818,13 +826,13 @@ export async function createCheckoutOrderAction(input) {
|
|
|
818
826
|
const orderIds = [];
|
|
819
827
|
let total = 0;
|
|
820
828
|
const emailsToSend = [];
|
|
821
|
-
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s + g.reduce((gs, { item, product }) => gs +
|
|
829
|
+
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s + g.reduce((gs, { item, product }) => gs + lineTotalFor(item, product), 0), 0);
|
|
822
830
|
const couponUsageAccumulator = new Map();
|
|
823
831
|
// One commission on one transaction, capped, then apportioned across the
|
|
824
832
|
// orders this cart splits into — see allocateCheckoutFees.
|
|
825
833
|
const platformFeeByStore = allocateCheckoutFees(orderGroups.map(({ items: g }) => [
|
|
826
834
|
g[0].item.storeId,
|
|
827
|
-
g.reduce((sum, { item, product }) => sum +
|
|
835
|
+
g.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0),
|
|
828
836
|
]), computeCheckoutFees(cartSubtotal, commissions));
|
|
829
837
|
const groupCtx = {
|
|
830
838
|
paymentMethod,
|
|
@@ -922,7 +930,7 @@ export async function previewCheckoutPricing(input) {
|
|
|
922
930
|
}
|
|
923
931
|
const checks = cartItems.map((item) => ({ item, product: productById.get(item.productId) ?? null }));
|
|
924
932
|
const orderGroups = splitCartIntoOrderGroups(checks);
|
|
925
|
-
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s + g.reduce((gs, { item, product }) => gs +
|
|
933
|
+
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s + g.reduce((gs, { item, product }) => gs + lineTotalFor(item, product), 0), 0);
|
|
926
934
|
// Same re-validation the real order placement runs, so the preview never
|
|
927
935
|
// shows a discount the order won't actually honour.
|
|
928
936
|
const { coupons: appliedCoupons } = await revalidateAppliedCoupons(cart.appliedCoupons ?? [], checks, uid);
|
|
@@ -932,7 +940,7 @@ export async function previewCheckoutPricing(input) {
|
|
|
932
940
|
const stores = [];
|
|
933
941
|
for (const { items: group } of orderGroups) {
|
|
934
942
|
const firstItem = group[0].item;
|
|
935
|
-
const groupTotal = group.reduce((sum, { item, product }) => sum +
|
|
943
|
+
const groupTotal = group.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0);
|
|
936
944
|
const { shippingFee: groupShippingFee, storeState } = await resolveShippingCost(firstItem.storeId);
|
|
937
945
|
// Add-ons are per store, read off the cart document — the same entry
|
|
938
946
|
// createOrderForGroup will read when the order is actually placed.
|
|
@@ -948,13 +956,13 @@ export async function previewCheckoutPricing(input) {
|
|
|
948
956
|
if (siteSettings?.gst?.enabled && resolvedAddress?.state) {
|
|
949
957
|
const intraState = !!storeState && storeState === resolvedAddress.state;
|
|
950
958
|
for (const { item, product } of group) {
|
|
951
|
-
const lineTotal =
|
|
959
|
+
const lineTotal = lineTotalFor(item, product);
|
|
952
960
|
const rate = product?.gstRate ?? 0;
|
|
953
961
|
if (rate > 0)
|
|
954
962
|
groupGstAmount += calculateGst(rate, intraState, lineTotal).gstAmount;
|
|
955
963
|
}
|
|
956
964
|
}
|
|
957
|
-
const groupLines = group.map(({ item, product }) => ({ itemId: item.itemId, lineTotal:
|
|
965
|
+
const groupLines = group.map(({ item, product }) => ({ itemId: item.itemId, lineTotal: lineTotalFor(item, product) }));
|
|
958
966
|
const { couponDiscount: groupCouponDiscount } = computeGroupCouponDiscount(appliedCoupons, groupLines, groupTotal, cartSubtotal, firstItem.storeId);
|
|
959
967
|
stores.push({
|
|
960
968
|
storeId: firstItem.storeId,
|
|
@@ -1131,12 +1139,12 @@ async function refundDroppedItemsForRazorpayCheckout(input) {
|
|
|
1131
1139
|
async function createRazorpayGroupOrder(group, orderType, ctx) {
|
|
1132
1140
|
const { appliedCoupons, cartSubtotal, storeAddons, platformFeeByStore, siteSettings, uid, userName, userEmail, razorpay_order_id, razorpay_payment_id, razorpay_signature, shippingAddress, notes, outOfStockPolicy, unavailablePaid, orderIds, emailsToSend, couponUsageAccumulator, } = ctx;
|
|
1133
1141
|
const firstItem = group[0].item;
|
|
1134
|
-
const groupTotal = group.reduce((sum, { item, product }) => sum +
|
|
1142
|
+
const groupTotal = group.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0);
|
|
1135
1143
|
// Reuses the same store/seller lookup as the COD/UPI path above instead
|
|
1136
1144
|
// of re-implementing it inline — was two sequential findById calls per
|
|
1137
1145
|
// seller group here, duplicated from resolveShippingCost.
|
|
1138
1146
|
const { shippingFee, storeOwnerId } = await resolveShippingCost(firstItem.storeId);
|
|
1139
|
-
const groupLines = group.map(({ item, product }) => ({ itemId: item.itemId, lineTotal:
|
|
1147
|
+
const groupLines = group.map(({ item, product }) => ({ itemId: item.itemId, lineTotal: lineTotalFor(item, product) }));
|
|
1140
1148
|
const { couponDiscount, appliedDiscounts } = computeGroupCouponDiscount(appliedCoupons, groupLines, groupTotal, cartSubtotal, firstItem.storeId);
|
|
1141
1149
|
// Mirrors createOrderForGroup — without this the online-payment path wrote
|
|
1142
1150
|
// the discount onto the order but never incremented usage.currentUsage or
|
|
@@ -1469,7 +1477,7 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
1469
1477
|
const expectedAddonFees = expectedShippingGroups.reduce((sum, g) => {
|
|
1470
1478
|
const storeId = g.items[0].item.storeId;
|
|
1471
1479
|
const addons = cart.storeAddons?.[storeId] ?? {};
|
|
1472
|
-
const groupSubtotal = g.items.reduce((gs, { item, product }) => gs +
|
|
1480
|
+
const groupSubtotal = g.items.reduce((gs, { item, product }) => gs + lineTotalFor(item, product), 0);
|
|
1473
1481
|
return (sum +
|
|
1474
1482
|
computeWhatsAppNotifyFee(addons.whatsappNotifyAddon ?? false, commissionRates) +
|
|
1475
1483
|
computeGiftWrapFee(addons.giftWrapAddon ?? false, commissionRates) +
|
|
@@ -1501,12 +1509,12 @@ export async function verifyAndPlaceRazorpayOrderAction(input) {
|
|
|
1501
1509
|
let total = 0;
|
|
1502
1510
|
const emailsToSend = [];
|
|
1503
1511
|
const cartSubtotal = orderGroups.reduce((s, { items: g }) => s +
|
|
1504
|
-
g.reduce((gs, { item, product }) => gs +
|
|
1512
|
+
g.reduce((gs, { item, product }) => gs + lineTotalFor(item, product), 0), 0);
|
|
1505
1513
|
const couponUsageAccumulator = new Map();
|
|
1506
1514
|
// Same single capped commission + pro-rata split as the COD/UPI path.
|
|
1507
1515
|
const platformFeeByStore = allocateCheckoutFees(orderGroups.map(({ items: g }) => [
|
|
1508
1516
|
g[0].item.storeId,
|
|
1509
|
-
g.reduce((sum, { item, product }) => sum +
|
|
1517
|
+
g.reduce((sum, { item, product }) => sum + lineTotalFor(item, product), 0),
|
|
1510
1518
|
]), computeCheckoutFees(cartSubtotal, siteSettings?.commissions ?? CHECKOUT_DEFAULT_COMMISSIONS));
|
|
1511
1519
|
for (const { items: group, orderType } of orderGroups) {
|
|
1512
1520
|
total += await createRazorpayGroupOrder(group, orderType, {
|
|
@@ -237,7 +237,10 @@ export async function finalizeLockedLines(items, orderId) {
|
|
|
237
237
|
}
|
|
238
238
|
if (item.bidId) {
|
|
239
239
|
try {
|
|
240
|
-
await bidRepository.attachOrder(item.bidId, orderId
|
|
240
|
+
await bidRepository.attachOrder(item.bidId, orderId, {
|
|
241
|
+
actor: { role: "system" },
|
|
242
|
+
trigger: "finalizeLockedLines",
|
|
243
|
+
});
|
|
241
244
|
}
|
|
242
245
|
catch (err) {
|
|
243
246
|
void normalizeError(err);
|
|
@@ -270,7 +273,14 @@ async function closeOutBuyoutLosers(productId, winningBidId) {
|
|
|
270
273
|
const losers = active.filter((e) => e.id !== winningBidId);
|
|
271
274
|
if (losers.length === 0)
|
|
272
275
|
return;
|
|
273
|
-
|
|
276
|
+
// Pass the {ref, data} pairs whole, not just the refs — `getActiveByProduct`
|
|
277
|
+
// already returned the documents, so the timeline gets a real before-value
|
|
278
|
+
// at zero read cost.
|
|
279
|
+
await bidRepository.markManyLost(losers, {
|
|
280
|
+
actor: { role: "system" },
|
|
281
|
+
trigger: "finalizeLockedLines:buyoutClosedAuction",
|
|
282
|
+
reason: "A Buy Now purchase ended this auction.",
|
|
283
|
+
});
|
|
274
284
|
serverLogger.info("finalizeLockedLines: buyout closed out losing bids", {
|
|
275
285
|
productId,
|
|
276
286
|
winningBidId,
|
|
@@ -27,7 +27,7 @@ import type { StoreDetail, StoreListItem } from "../../../../features/stores/typ
|
|
|
27
27
|
*/
|
|
28
28
|
export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "createdAt"];
|
|
29
29
|
/** Everything else, with the reason it stays server-side. */
|
|
30
|
-
export declare const PRIVATE_STORE_FIELDS: readonly ["updatedAt", "whatsappConfig", "adminNotes", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
|
|
30
|
+
export declare const PRIVATE_STORE_FIELDS: readonly ["updatedAt", "whatsappConfig", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
|
|
31
31
|
/**
|
|
32
32
|
* Accepted input: a `StoreDocument` (the common case) or a loosely-typed
|
|
33
33
|
* Firestore row. The legacy top-level counters below predate `stats` and are
|
|
@@ -29,6 +29,11 @@ export const PRIVATE_STORE_FIELDS = [
|
|
|
29
29
|
"updatedAt", // bookkeeping; no public reader
|
|
30
30
|
"whatsappConfig", // Meta access token + WABA/catalog ids — secret
|
|
31
31
|
"adminNotes", // schema comment: never shown to the owner or the public
|
|
32
|
+
// Admin decisions with actor UIDs and suspension reasons. The store OWNER
|
|
33
|
+
// sees this on their own admin surfaces; an anonymous visitor to /stores has
|
|
34
|
+
// no business reading who suspended whom and why.
|
|
35
|
+
"statusHistory",
|
|
36
|
+
"statusHistoryTruncated",
|
|
32
37
|
"isVerified", // admin moderation state; surfaced via a separate badge feed if ever needed
|
|
33
38
|
"suspensionReason", // admin moderation note
|
|
34
39
|
"capabilities", // internal feature gating; server-enforced, never client-read
|
|
@@ -32,7 +32,11 @@ async function releasePendingBuyouts(ctx, product, holds) {
|
|
|
32
32
|
for (const hold of holds) {
|
|
33
33
|
try {
|
|
34
34
|
await cartRepository.removeItemsByBidId(hold.data.userId, hold.id);
|
|
35
|
-
await bidRepository.markCancelled(hold.id
|
|
35
|
+
await bidRepository.markCancelled(hold.id, {
|
|
36
|
+
actor: { role: "system" },
|
|
37
|
+
trigger: "auctionSettlement:lapsedBuyoutHold",
|
|
38
|
+
reason: "The auction ended before this Buy Now claim was paid for.",
|
|
39
|
+
});
|
|
36
40
|
await sendNotification({
|
|
37
41
|
userId: hold.data.userId,
|
|
38
42
|
type: "auction_ended",
|
|
@@ -84,7 +88,9 @@ async function settleAuction(ctx, product) {
|
|
|
84
88
|
const reserve = product.reservePrice;
|
|
85
89
|
if (typeof reserve === "number" && reserve > 0 && winnerEntry.data.bidAmount < reserve) {
|
|
86
90
|
const batch = ctx.db.batch();
|
|
87
|
-
|
|
91
|
+
// `data` comes from the same query as `ref`, so the timeline entry is
|
|
92
|
+
// free even on an auction that closed with hundreds of bids (Rule #6).
|
|
93
|
+
activeBids.forEach(({ ref, data }) => bidRepository.markLost(batch, ref, { actor: { role: "system" }, trigger: "auctionSettlement" }, data));
|
|
88
94
|
productRepository.updateStatusInBatch(batch, product.id, "archived");
|
|
89
95
|
await batch.commit();
|
|
90
96
|
const reserveStore = product.storeId
|
|
@@ -117,8 +123,8 @@ async function settleAuction(ctx, product) {
|
|
|
117
123
|
return;
|
|
118
124
|
}
|
|
119
125
|
const batch = ctx.db.batch();
|
|
120
|
-
bidRepository.markWon(batch, winnerEntry.ref);
|
|
121
|
-
loserEntries.forEach(({ ref }) => bidRepository.markLost(batch, ref));
|
|
126
|
+
bidRepository.markWon(batch, winnerEntry.ref, { actor: { role: "system" }, trigger: "auctionSettlement" }, winnerEntry.data);
|
|
127
|
+
loserEntries.forEach(({ ref, data }) => bidRepository.markLost(batch, ref, { actor: { role: "system" }, trigger: "auctionSettlement" }, data));
|
|
122
128
|
// Two-axis model: mark as sold without changing publish status.
|
|
123
129
|
batch.update(ctx.db.collection("products").doc(product.id), {
|
|
124
130
|
isSold: true,
|
|
@@ -155,7 +155,11 @@ async function lapseUnpaidAuctionWins(ctx) {
|
|
|
155
155
|
for (const { userId, item } of buyoutHolds) {
|
|
156
156
|
try {
|
|
157
157
|
await cartRepository.removeItemsByBidId(userId, item.bidId);
|
|
158
|
-
await bidRepository.markCancelled(item.bidId
|
|
158
|
+
await bidRepository.markCancelled(item.bidId, {
|
|
159
|
+
actor: { role: "system" },
|
|
160
|
+
trigger: "runOfferExpiry:buyoutLapsed",
|
|
161
|
+
reason: "The Buy Now checkout window closed without payment.",
|
|
162
|
+
});
|
|
159
163
|
await sendNotification({
|
|
160
164
|
userId,
|
|
161
165
|
type: "auction_ended",
|
|
@@ -211,7 +215,11 @@ async function lapseUnpaidAuctionWins(ctx) {
|
|
|
211
215
|
for (const { userId, item } of auctionLines) {
|
|
212
216
|
try {
|
|
213
217
|
await cartRepository.removeItemsByBidId(userId, item.bidId);
|
|
214
|
-
await bidRepository.markForfeited(item.bidId
|
|
218
|
+
await bidRepository.markForfeited(item.bidId, {
|
|
219
|
+
actor: { role: "system" },
|
|
220
|
+
trigger: "runOfferExpiry:winUnpaid",
|
|
221
|
+
reason: "The 48-hour checkout window closed without payment.",
|
|
222
|
+
});
|
|
215
223
|
await sendNotification({
|
|
216
224
|
userId,
|
|
217
225
|
type: "auction_ended",
|
|
@@ -9,7 +9,19 @@ import { payoutRepository } from "../../../../repositories";
|
|
|
9
9
|
const MAX_FAILURES = 3;
|
|
10
10
|
async function dispatch(ctx, entry) {
|
|
11
11
|
const { ref, data: payout } = entry;
|
|
12
|
-
|
|
12
|
+
/*
|
|
13
|
+
* The batch's running copy of the document, threaded through every write.
|
|
14
|
+
*
|
|
15
|
+
* Two reasons it is not just `payout`. (a) The snapshot came from the
|
|
16
|
+
* pending query, so history costs no extra read. (b) The sequence is
|
|
17
|
+
* pending → processing → failed; diffing the final write against the
|
|
18
|
+
* ORIGINAL snapshot would record "pending → failed" and lose the dispatch
|
|
19
|
+
* attempt that is the whole reason anyone opens the timeline.
|
|
20
|
+
*/
|
|
21
|
+
let current = payout;
|
|
22
|
+
const actor = { role: "system" };
|
|
23
|
+
await payoutRepository.markProcessing(ref, { actor }, current);
|
|
24
|
+
current = { ...current, status: "processing" };
|
|
13
25
|
try {
|
|
14
26
|
const keyId = ctx.env("RAZORPAY_KEY_ID") ?? "";
|
|
15
27
|
const keySecret = ctx.env("RAZORPAY_KEY_SECRET") ?? "";
|
|
@@ -67,7 +79,7 @@ async function dispatch(ctx, entry) {
|
|
|
67
79
|
throw new Error(`Razorpay ${response.status}: ${errBody}`);
|
|
68
80
|
}
|
|
69
81
|
const result = (await response.json());
|
|
70
|
-
await payoutRepository.recordSuccess(ref, result.id, result.status);
|
|
82
|
+
await payoutRepository.recordSuccess(ref, result.id, result.status, { actor }, current);
|
|
71
83
|
ctx.logger.info(`Payout ${payout.id} dispatched`, { razorpayId: result.id, razorpayStatus: result.status });
|
|
72
84
|
}
|
|
73
85
|
catch (error) {
|
|
@@ -75,7 +87,7 @@ async function dispatch(ctx, entry) {
|
|
|
75
87
|
const failureCount = (payout.failureCount ?? 0) + 1;
|
|
76
88
|
const isFinal = failureCount >= MAX_FAILURES;
|
|
77
89
|
const reason = error instanceof Error ? error.message : String(error);
|
|
78
|
-
await payoutRepository.recordFailure(ref, failureCount, reason, isFinal);
|
|
90
|
+
await payoutRepository.recordFailure(ref, failureCount, reason, isFinal, { actor }, current);
|
|
79
91
|
if (isFinal) {
|
|
80
92
|
ctx.logger.error(`Payout ${payout.id} permanently failed after ${MAX_FAILURES} attempts`, error, {
|
|
81
93
|
sellerId: payout.storeId,
|
|
@@ -2412,6 +2412,47 @@ export const ACTIONS = {
|
|
|
2412
2412
|
description: "Navigate back from the cart to the product catalogue.",
|
|
2413
2413
|
kind: "ghost",
|
|
2414
2414
|
},
|
|
2415
|
+
"add-group-to-cart": {
|
|
2416
|
+
iconKey: "cart",
|
|
2417
|
+
id: "cart.add-group-to-cart",
|
|
2418
|
+
label: "Add selected to cart",
|
|
2419
|
+
ariaLabel: "Add the selected group items to the cart as one line",
|
|
2420
|
+
description: "Add a buyer-assembled selection of group members to the cart as a SINGLE line, rather than one line per product.",
|
|
2421
|
+
kind: "primary",
|
|
2422
|
+
},
|
|
2423
|
+
"add-bundle-to-cart": {
|
|
2424
|
+
iconKey: "cart",
|
|
2425
|
+
id: "cart.add-bundle-to-cart",
|
|
2426
|
+
label: "Add to cart",
|
|
2427
|
+
ariaLabel: "Add this bundle to the cart",
|
|
2428
|
+
description: "Add a whole bundle to the cart. Bundles are all-or-nothing — the quantity is the number of COPIES of the bundle.",
|
|
2429
|
+
kind: "primary",
|
|
2430
|
+
},
|
|
2431
|
+
"remove-group-member": {
|
|
2432
|
+
iconKey: "delete",
|
|
2433
|
+
id: "cart.remove-group-member",
|
|
2434
|
+
label: "Remove",
|
|
2435
|
+
ariaLabel: "Remove this item from the group line",
|
|
2436
|
+
description: "Drop one member out of a grouped cart line. Ghost rather than danger on purpose: it reuses the cart's existing undo affordance, and Rule #7.4's mandatory-confirmation requirement applies to destructive actions.",
|
|
2437
|
+
kind: "ghost",
|
|
2438
|
+
},
|
|
2439
|
+
// No iconKey — these two exist only to keep the stepper's accessible names
|
|
2440
|
+
// in the registry rather than hard-coded in the primitive. The glyphs are
|
|
2441
|
+
// the literal "+" / "−" characters.
|
|
2442
|
+
"increase-quantity": {
|
|
2443
|
+
id: "cart.increase-quantity",
|
|
2444
|
+
label: "+",
|
|
2445
|
+
ariaLabel: "Increase quantity",
|
|
2446
|
+
description: "Default accessible name for QuantityStepper's increment control.",
|
|
2447
|
+
kind: "ghost",
|
|
2448
|
+
},
|
|
2449
|
+
"decrease-quantity": {
|
|
2450
|
+
id: "cart.decrease-quantity",
|
|
2451
|
+
label: "−",
|
|
2452
|
+
ariaLabel: "Decrease quantity",
|
|
2453
|
+
description: "Default accessible name for QuantityStepper's decrement control.",
|
|
2454
|
+
kind: "ghost",
|
|
2455
|
+
},
|
|
2415
2456
|
},
|
|
2416
2457
|
CHECKOUT: {
|
|
2417
2458
|
"place-order": {
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
import type { CartItemDocument } from "../../../features/cart/schemas/firestore";
|
|
8
8
|
import type { ProductDocument } from "../../../features/products/schemas/firestore";
|
|
9
9
|
/**
|
|
10
|
-
* The single source of truth for "what do we charge for this line".
|
|
10
|
+
* The single source of truth for "what do we charge for ONE COPY of this line".
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* Four cases, in priority order:
|
|
13
13
|
*
|
|
14
14
|
* 1. **A locked price** — an accepted offer or a won auction. `lockedPrice` is
|
|
15
15
|
* the negotiated/won amount and it OVERRIDES the current listing price, which
|
|
@@ -17,15 +17,33 @@ import type { ProductDocument } from "../../../features/products/schemas/firesto
|
|
|
17
17
|
* copies of this function looked at `lockedPrice`, so an accepted offer was
|
|
18
18
|
* billed at the seller's listed price while the cart UI displayed the agreed
|
|
19
19
|
* one (`item.lockedPrice ?? item.price`) — the buyer saw ₹X and paid ₹Y.
|
|
20
|
-
* 2. **A
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* 2. **A multi-member line** — the sum of its members' own snapshotted prices,
|
|
21
|
+
* weighted by each member's per-copy quantity. A `"group"` line pins
|
|
22
|
+
* `item.quantity` to 1, so this is both the unit price and the line total;
|
|
23
|
+
* see the invariant on `CartLineMember`.
|
|
24
|
+
* 3. **A legacy bundle line** (SB-UNI-5 2026-05-13) — written before
|
|
25
|
+
* `groupMembers` existed. `item.price` is the bundle price locked at
|
|
26
|
+
* add-time.
|
|
27
|
+
* 4. **Everything else** — the live Firestore price, so a stale cart-cached
|
|
23
28
|
* price is never charged on a COD/UPI order.
|
|
24
29
|
*
|
|
25
30
|
* `product` may be null for a locked line whose listing has since been archived;
|
|
26
31
|
* the locked price is still owed, so that case must not dereference `product`.
|
|
32
|
+
*
|
|
33
|
+
* 🛑 Do not hand-roll this rule at a call site. It has been re-derived by hand
|
|
34
|
+
* six times, and the two copies that lived in the consumer repo
|
|
35
|
+
* (`src/app/api/payment/create-order/route.ts`) each reproduced the bundle
|
|
36
|
+
* branch while silently omitting case 1 — so Razorpay captured the LIST price
|
|
37
|
+
* for an accepted offer while the cart displayed the negotiated one. Call
|
|
38
|
+
* `lineTotalFor` and let this function stay the only definition.
|
|
27
39
|
*/
|
|
28
40
|
export declare function unitPriceFor(item: CartItemDocument, product: ProductDocument | null): number;
|
|
41
|
+
/**
|
|
42
|
+
* What this line contributes to a subtotal: one copy's price × the number of
|
|
43
|
+
* copies. THE call site should use this rather than multiplying `unitPriceFor`
|
|
44
|
+
* itself — that multiplication is where the six hand-rolled copies diverged.
|
|
45
|
+
*/
|
|
46
|
+
export declare function lineTotalFor(item: CartItemDocument, product: ProductDocument | null): number;
|
|
29
47
|
/**
|
|
30
48
|
* P-6 — pre-order groups charge each product's own `preOrderDepositPercent`
|
|
31
49
|
* (falling back to the generic COD deposit % when a product doesn't have one
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { roundRupees } from "../../../utils/number.formatter";
|
|
2
2
|
/**
|
|
3
|
-
* The single source of truth for "what do we charge for this line".
|
|
3
|
+
* The single source of truth for "what do we charge for ONE COPY of this line".
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Four cases, in priority order:
|
|
6
6
|
*
|
|
7
7
|
* 1. **A locked price** — an accepted offer or a won auction. `lockedPrice` is
|
|
8
8
|
* the negotiated/won amount and it OVERRIDES the current listing price, which
|
|
@@ -10,18 +10,33 @@ import { roundRupees } from "../../../utils/number.formatter";
|
|
|
10
10
|
* copies of this function looked at `lockedPrice`, so an accepted offer was
|
|
11
11
|
* billed at the seller's listed price while the cart UI displayed the agreed
|
|
12
12
|
* one (`item.lockedPrice ?? item.price`) — the buyer saw ₹X and paid ₹Y.
|
|
13
|
-
* 2. **A
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* 2. **A multi-member line** — the sum of its members' own snapshotted prices,
|
|
14
|
+
* weighted by each member's per-copy quantity. A `"group"` line pins
|
|
15
|
+
* `item.quantity` to 1, so this is both the unit price and the line total;
|
|
16
|
+
* see the invariant on `CartLineMember`.
|
|
17
|
+
* 3. **A legacy bundle line** (SB-UNI-5 2026-05-13) — written before
|
|
18
|
+
* `groupMembers` existed. `item.price` is the bundle price locked at
|
|
19
|
+
* add-time.
|
|
20
|
+
* 4. **Everything else** — the live Firestore price, so a stale cart-cached
|
|
16
21
|
* price is never charged on a COD/UPI order.
|
|
17
22
|
*
|
|
18
23
|
* `product` may be null for a locked line whose listing has since been archived;
|
|
19
24
|
* the locked price is still owed, so that case must not dereference `product`.
|
|
25
|
+
*
|
|
26
|
+
* 🛑 Do not hand-roll this rule at a call site. It has been re-derived by hand
|
|
27
|
+
* six times, and the two copies that lived in the consumer repo
|
|
28
|
+
* (`src/app/api/payment/create-order/route.ts`) each reproduced the bundle
|
|
29
|
+
* branch while silently omitting case 1 — so Razorpay captured the LIST price
|
|
30
|
+
* for an accepted offer while the cart displayed the negotiated one. Call
|
|
31
|
+
* `lineTotalFor` and let this function stay the only definition.
|
|
20
32
|
*/
|
|
21
33
|
export function unitPriceFor(item, product) {
|
|
22
34
|
if (typeof item.lockedPrice === "number" && item.lockedPrice > 0) {
|
|
23
35
|
return item.lockedPrice;
|
|
24
36
|
}
|
|
37
|
+
if (item.groupMembers?.length) {
|
|
38
|
+
return roundRupees(item.groupMembers.reduce((sum, m) => sum + m.unitPrice * m.quantity, 0));
|
|
39
|
+
}
|
|
25
40
|
if (item.bundleCategorySlug && item.bundleProductIds?.length) {
|
|
26
41
|
return item.price;
|
|
27
42
|
}
|
|
@@ -31,6 +46,14 @@ export function unitPriceFor(item, product) {
|
|
|
31
46
|
// crash whenever a cart line outlives its listing.
|
|
32
47
|
return product?.price ?? item.price;
|
|
33
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* What this line contributes to a subtotal: one copy's price × the number of
|
|
51
|
+
* copies. THE call site should use this rather than multiplying `unitPriceFor`
|
|
52
|
+
* itself — that multiplication is where the six hand-rolled copies diverged.
|
|
53
|
+
*/
|
|
54
|
+
export function lineTotalFor(item, product) {
|
|
55
|
+
return unitPriceFor(item, product) * item.quantity;
|
|
56
|
+
}
|
|
34
57
|
/**
|
|
35
58
|
* P-6 — pre-order groups charge each product's own `preOrderDepositPercent`
|
|
36
59
|
* (falling back to the generic COD deposit % when a product doesn't have one
|
|
@@ -41,7 +64,7 @@ export function unitPriceFor(item, product) {
|
|
|
41
64
|
*/
|
|
42
65
|
export function computePreOrderDepositAmount(group, defaultDepositPercent) {
|
|
43
66
|
const raw = group.reduce((sum, { item, product }) => {
|
|
44
|
-
const lineTotal =
|
|
67
|
+
const lineTotal = lineTotalFor(item, product);
|
|
45
68
|
const pct = product.preOrderDepositPercent ?? defaultDepositPercent;
|
|
46
69
|
return sum + lineTotal * (pct / 100); // audit-money-units-ok: percentage divisor, not paise
|
|
47
70
|
}, 0);
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
/** Checkout window / capacity constants — single source of truth. */
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Maximum distinct line items in one cart (hard cap, enforced at every
|
|
4
|
+
* add-to-cart entry point).
|
|
5
|
+
*
|
|
6
|
+
* Re-exported, not re-declared. This value existed as three independent `= 50`
|
|
7
|
+
* literals — here, in `constants/limits.ts`, and in
|
|
8
|
+
* `_internal/shared/features/cart/config.ts` — with nothing tying them
|
|
9
|
+
* together, so raising the cap in one place would have left two silently
|
|
10
|
+
* disagreeing. `constants/limits.ts` is a dependency-free leaf module, so this
|
|
11
|
+
* costs no import chain.
|
|
12
|
+
*/
|
|
13
|
+
export { CART_MAX_ITEMS } from "../../../../constants/limits";
|
|
4
14
|
/**
|
|
5
15
|
* Maximum number of order documents that can be created in one checkout
|
|
6
16
|
* transaction. If the cart produces more groups than this the checkout is
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
/** Checkout window / capacity constants — single source of truth. */
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Maximum distinct line items in one cart (hard cap, enforced at every
|
|
4
|
+
* add-to-cart entry point).
|
|
5
|
+
*
|
|
6
|
+
* Re-exported, not re-declared. This value existed as three independent `= 50`
|
|
7
|
+
* literals — here, in `constants/limits.ts`, and in
|
|
8
|
+
* `_internal/shared/features/cart/config.ts` — with nothing tying them
|
|
9
|
+
* together, so raising the cap in one place would have left two silently
|
|
10
|
+
* disagreeing. `constants/limits.ts` is a dependency-free leaf module, so this
|
|
11
|
+
* costs no import chain.
|
|
12
|
+
*/
|
|
13
|
+
export { CART_MAX_ITEMS } from "../../../../constants/limits";
|
|
4
14
|
/**
|
|
5
15
|
* Maximum number of order documents that can be created in one checkout
|
|
6
16
|
* transaction. If the cart produces more groups than this the checkout is
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/** Re-exported from the single declaration — see `constants/limits.ts`. */
|
|
2
|
+
export { CART_MAX_ITEMS } from "../../../../constants/limits";
|
|
2
3
|
export declare const CART_SESSION_TTL_DAYS = 30;
|
|
3
4
|
export declare const CART_GUEST_STORAGE_KEY = "appkit:cart";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/** Re-exported from the single declaration — see `constants/limits.ts`. */
|
|
2
|
+
export { CART_MAX_ITEMS } from "../../../../constants/limits";
|
|
2
3
|
export const CART_SESSION_TTL_DAYS = 30;
|
|
3
4
|
export const CART_GUEST_STORAGE_KEY = "appkit:cart";
|
|
@@ -22,9 +22,9 @@ export declare const addToCartSchema: z.ZodObject<{
|
|
|
22
22
|
productId: string;
|
|
23
23
|
productTitle: string;
|
|
24
24
|
productImage: string;
|
|
25
|
+
isOffer?: boolean | undefined;
|
|
25
26
|
offerId?: string | undefined;
|
|
26
27
|
lockedPrice?: number | undefined;
|
|
27
|
-
isOffer?: boolean | undefined;
|
|
28
28
|
}, {
|
|
29
29
|
storeId: string;
|
|
30
30
|
price: number;
|
|
@@ -35,9 +35,9 @@ export declare const addToCartSchema: z.ZodObject<{
|
|
|
35
35
|
storeName?: string | undefined;
|
|
36
36
|
quantity?: number | undefined;
|
|
37
37
|
productImage?: string | undefined;
|
|
38
|
+
isOffer?: boolean | undefined;
|
|
38
39
|
offerId?: string | undefined;
|
|
39
40
|
lockedPrice?: number | undefined;
|
|
40
|
-
isOffer?: boolean | undefined;
|
|
41
41
|
}>;
|
|
42
42
|
export declare const updateCartItemSchema: z.ZodObject<{
|
|
43
43
|
productId: z.ZodString;
|
|
@@ -80,9 +80,9 @@ export declare const mergeGuestCartSchema: z.ZodObject<{
|
|
|
80
80
|
productId: string;
|
|
81
81
|
productTitle: string;
|
|
82
82
|
productImage: string;
|
|
83
|
+
isOffer?: boolean | undefined;
|
|
83
84
|
offerId?: string | undefined;
|
|
84
85
|
lockedPrice?: number | undefined;
|
|
85
|
-
isOffer?: boolean | undefined;
|
|
86
86
|
}, {
|
|
87
87
|
storeId: string;
|
|
88
88
|
price: number;
|
|
@@ -93,9 +93,9 @@ export declare const mergeGuestCartSchema: z.ZodObject<{
|
|
|
93
93
|
storeName?: string | undefined;
|
|
94
94
|
quantity?: number | undefined;
|
|
95
95
|
productImage?: string | undefined;
|
|
96
|
+
isOffer?: boolean | undefined;
|
|
96
97
|
offerId?: string | undefined;
|
|
97
98
|
lockedPrice?: number | undefined;
|
|
98
|
-
isOffer?: boolean | undefined;
|
|
99
99
|
}>, "many">;
|
|
100
100
|
}, "strip", z.ZodTypeAny, {
|
|
101
101
|
guestItems: {
|
|
@@ -108,9 +108,9 @@ export declare const mergeGuestCartSchema: z.ZodObject<{
|
|
|
108
108
|
productId: string;
|
|
109
109
|
productTitle: string;
|
|
110
110
|
productImage: string;
|
|
111
|
+
isOffer?: boolean | undefined;
|
|
111
112
|
offerId?: string | undefined;
|
|
112
113
|
lockedPrice?: number | undefined;
|
|
113
|
-
isOffer?: boolean | undefined;
|
|
114
114
|
}[];
|
|
115
115
|
}, {
|
|
116
116
|
guestItems: {
|
|
@@ -123,9 +123,9 @@ export declare const mergeGuestCartSchema: z.ZodObject<{
|
|
|
123
123
|
storeName?: string | undefined;
|
|
124
124
|
quantity?: number | undefined;
|
|
125
125
|
productImage?: string | undefined;
|
|
126
|
+
isOffer?: boolean | undefined;
|
|
126
127
|
offerId?: string | undefined;
|
|
127
128
|
lockedPrice?: number | undefined;
|
|
128
|
-
isOffer?: boolean | undefined;
|
|
129
129
|
}[];
|
|
130
130
|
}>;
|
|
131
131
|
/**
|