This commit is contained in:
shafi54 2026-03-22 21:29:04 +05:30
parent a23d3bf5b8
commit 95d2c861c0
33 changed files with 4169 additions and 732 deletions

View file

@ -1,7 +1,7 @@
import { drizzle } from "drizzle-orm/node-postgres"
import { migrate } from "drizzle-orm/node-postgres/migrator"
import path from "path"
import * as schema from "@/src/db/schema"
import * as schema from "@/src/db/schema-postgres"
const db = drizzle({ connection: process.env.DATABASE_URL!, casing: "snake_case", schema: schema })
// const db = drizzle('postgresql://postgres:postgres@localhost:2345/pooler');

View file

@ -0,0 +1,10 @@
import { drizzle } from 'drizzle-orm/bun-sqlite'
import { Database } from 'bun:sqlite'
import * as schema from '@/src/db/schema-sqlite'
const sqlitePath = process.env.SQLITE_DB_PATH || 'sqlite.db'
const sqlite = new Database(sqlitePath)
const db = drizzle(sqlite, { schema })
export { db }

View file

@ -0,0 +1,706 @@
import { pgTable, pgSchema, integer, varchar, date, boolean, timestamp, numeric, jsonb, pgEnum, unique, real, text, check, decimal } from "drizzle-orm/pg-core";
import { relations, sql } from "drizzle-orm";
const mf = pgSchema('mf');
export const users = mf.table('users', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }),
email: varchar({ length: 255 }),
mobile: varchar({ length: 255 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_email: unique('unique_email').on(t.email),
}));
export const userDetails = mf.table('user_details', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id).unique(),
bio: varchar('bio', { length: 500 }),
dateOfBirth: date('date_of_birth'),
gender: varchar('gender', { length: 20 }),
occupation: varchar('occupation', { length: 100 }),
profileImage: varchar('profile_image', { length: 500 }),
isSuspended: boolean('is_suspended').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export const userCreds = mf.table('user_creds', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
userPassword: varchar('user_password', { length: 255 }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const addresses = mf.table('addresses', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
name: varchar('name', { length: 255 }).notNull(),
phone: varchar('phone', { length: 15 }).notNull(),
addressLine1: varchar('address_line1', { length: 255 }).notNull(),
addressLine2: varchar('address_line2', { length: 255 }),
city: varchar('city', { length: 100 }).notNull(),
state: varchar('state', { length: 100 }).notNull(),
pincode: varchar('pincode', { length: 10 }).notNull(),
isDefault: boolean('is_default').notNull().default(false),
latitude: real('latitude'),
longitude: real('longitude'),
googleMapsUrl: varchar('google_maps_url', { length: 500 }),
adminLatitude: real('admin_latitude'),
adminLongitude: real('admin_longitude'),
zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const addressZones = mf.table('address_zones', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
zoneName: varchar('zone_name', { length: 255 }).notNull(),
addedAt: timestamp('added_at').notNull().defaultNow(),
});
export const addressAreas = mf.table('address_areas', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
placeName: varchar('place_name', { length: 255 }).notNull(),
zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const staffUsers = mf.table('staff_users', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
password: varchar({ length: 255 }).notNull(),
staffRoleId: integer('staff_role_id').references(() => staffRoles.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const storeInfo = mf.table('store_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
description: varchar({ length: 500 }),
imageUrl: varchar('image_url', { length: 500 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
owner: integer('owner').notNull().references(() => staffUsers.id),
});
export const units = mf.table('units', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
shortNotation: varchar('short_notation', { length: 50 }).notNull(),
fullName: varchar('full_name', { length: 100 }).notNull(),
}, (t) => ({
unq_short_notation: unique('unique_short_notation').on(t.shortNotation),
}));
export const productAvailabilityActionEnum = pgEnum('product_availability_action', ['in', 'out']);
export const productInfo = mf.table('product_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
shortDescription: varchar('short_description', { length: 500 }),
longDescription: varchar('long_description', { length: 1000 }),
unitId: integer('unit_id').notNull().references(() => units.id),
price: numeric({ precision: 10, scale: 2 }).notNull(),
marketPrice: numeric('market_price', { precision: 10, scale: 2 }),
images: jsonb('images'),
isOutOfStock: boolean('is_out_of_stock').notNull().default(false),
isSuspended: boolean('is_suspended').notNull().default(false),
isFlashAvailable: boolean('is_flash_available').notNull().default(false),
flashPrice: numeric('flash_price', { precision: 10, scale: 2 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
incrementStep: real('increment_step').notNull().default(1),
productQuantity: real('product_quantity').notNull().default(1),
storeId: integer('store_id').references(() => storeInfo.id),
scheduledAvailability: boolean('scheduled_availability').notNull().default(true),
});
export const productAvailabilitySchedules = mf.table('product_availability_schedules', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
time: varchar('time', { length: 10 }).notNull(),
scheduleName: varchar('schedule_name', { length: 255 }).notNull().unique(),
action: productAvailabilityActionEnum('action').notNull(),
productIds: integer('product_ids').array().notNull().default([]),
groupIds: integer('group_ids').array().notNull().default([]),
createdAt: timestamp('created_at').notNull().defaultNow(),
lastUpdated: timestamp('last_updated').notNull().defaultNow(),
});
export const productGroupInfo = mf.table('product_group_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
groupName: varchar('group_name', { length: 255 }).notNull(),
description: varchar({ length: 500 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const productGroupMembership = mf.table('product_group_membership', {
productId: integer('product_id').notNull().references(() => productInfo.id),
groupId: integer('group_id').notNull().references(() => productGroupInfo.id),
addedAt: timestamp('added_at').notNull().defaultNow(),
}, (t) => ({
pk: unique('product_group_membership_pk').on(t.productId, t.groupId),
}));
export const homeBanners = mf.table('home_banners', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar('name', { length: 255 }).notNull(),
imageUrl: varchar('image_url', { length: 500 }).notNull(),
description: varchar('description', { length: 500 }),
productIds: integer('product_ids').array(),
redirectUrl: varchar('redirect_url', { length: 500 }),
serialNum: integer('serial_num'),
isActive: boolean('is_active').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
lastUpdated: timestamp('last_updated').notNull().defaultNow(),
});
export const productReviews = mf.table('product_reviews', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
reviewBody: text('review_body').notNull(),
imageUrls: jsonb('image_urls').$defaultFn(() => []),
reviewTime: timestamp('review_time').notNull().defaultNow(),
ratings: real('ratings').notNull(),
adminResponse: text('admin_response'),
adminResponseImages: jsonb('admin_response_images').$defaultFn(() => []),
}, (t) => ({
ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`),
}));
export const uploadStatusEnum = pgEnum('upload_status', ['pending', 'claimed']);
export const staffRoleEnum = pgEnum('staff_role', ['super_admin', 'admin', 'marketer', 'delivery_staff']);
export const staffPermissionEnum = pgEnum('staff_permission', ['crud_product', 'make_coupon', 'crud_staff_users']);
export const uploadUrlStatus = mf.table('upload_url_status', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
createdAt: timestamp('created_at').notNull().defaultNow(),
key: varchar('key', { length: 500 }).notNull(),
status: uploadStatusEnum('status').notNull().default('pending'),
});
export const productTagInfo = mf.table('product_tag_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
tagName: varchar('tag_name', { length: 100 }).notNull().unique(),
tagDescription: varchar('tag_description', { length: 500 }),
imageUrl: varchar('image_url', { length: 500 }),
isDashboardTag: boolean('is_dashboard_tag').notNull().default(false),
relatedStores: jsonb('related_stores').$defaultFn(() => []),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const productTags = mf.table('product_tags', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
productId: integer('product_id').notNull().references(() => productInfo.id),
tagId: integer('tag_id').notNull().references(() => productTagInfo.id),
assignedAt: timestamp('assigned_at').notNull().defaultNow(),
}, (t) => ({
unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId),
}));
export const deliverySlotInfo = mf.table('delivery_slot_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
deliveryTime: timestamp('delivery_time').notNull(),
freezeTime: timestamp('freeze_time').notNull(),
isActive: boolean('is_active').notNull().default(true),
isFlash: boolean('is_flash').notNull().default(false),
isCapacityFull: boolean('is_capacity_full').notNull().default(false),
deliverySequence: jsonb('delivery_sequence').$defaultFn(() => {}),
groupIds: jsonb('group_ids').$defaultFn(() => []),
});
export const vendorSnippets = mf.table('vendor_snippets', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
snippetCode: varchar('snippet_code', { length: 255 }).notNull().unique(),
slotId: integer('slot_id').references(() => deliverySlotInfo.id),
isPermanent: boolean('is_permanent').notNull().default(false),
productIds: integer('product_ids').array().notNull(),
validTill: timestamp('valid_till'),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({
slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }),
}));
export const productSlots = mf.table('product_slots', {
productId: integer('product_id').notNull().references(() => productInfo.id),
slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id),
}, (t) => ({
pk: unique('product_slot_pk').on(t.productId, t.slotId),
}));
export const specialDeals = mf.table('special_deals', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: numeric({ precision: 10, scale: 2 }).notNull(),
price: numeric({ precision: 10, scale: 2 }).notNull(),
validTill: timestamp('valid_till').notNull(),
});
export const orders = mf.table('orders', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
addressId: integer('address_id').notNull().references(() => addresses.id),
slotId: integer('slot_id').references(() => deliverySlotInfo.id),
isCod: boolean('is_cod').notNull().default(false),
isOnlinePayment: boolean('is_online_payment').notNull().default(false),
paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id),
totalAmount: numeric('total_amount', { precision: 10, scale: 2 }).notNull(),
deliveryCharge: numeric('delivery_charge', { precision: 10, scale: 2 }).notNull().default('0'),
readableId: integer('readable_id').notNull(),
adminNotes: text('admin_notes'),
userNotes: text('user_notes'),
orderGroupId: varchar('order_group_id', { length: 255 }),
orderGroupProportion: decimal('order_group_proportion', { precision: 10, scale: 4 }),
isFlashDelivery: boolean('is_flash_delivery').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const orderItems = mf.table('order_items', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
orderId: integer('order_id').notNull().references(() => orders.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: varchar('quantity', { length: 50 }).notNull(),
price: numeric({ precision: 10, scale: 2 }).notNull(),
discountedPrice: numeric('discounted_price', { precision: 10, scale: 2 }),
is_packaged: boolean('is_packaged').notNull().default(false),
is_package_verified: boolean('is_package_verified').notNull().default(false),
});
export const paymentStatusEnum = pgEnum('payment_status', ['pending', 'success', 'cod', 'failed']);
export const orderStatus = mf.table('order_status', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
orderTime: timestamp('order_time').notNull().defaultNow(),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').notNull().references(() => orders.id),
isPackaged: boolean('is_packaged').notNull().default(false),
isDelivered: boolean('is_delivered').notNull().default(false),
isCancelled: boolean('is_cancelled').notNull().default(false),
cancelReason: varchar('cancel_reason', { length: 255 }),
isCancelledByAdmin: boolean('is_cancelled_by_admin'),
paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'),
cancellationUserNotes: text('cancellation_user_notes'),
cancellationAdminNotes: text('cancellation_admin_notes'),
cancellationReviewed: boolean('cancellation_reviewed').notNull().default(false),
cancellationReviewedAt: timestamp('cancellation_reviewed_at'),
refundCouponId: integer('refund_coupon_id').references(() => coupons.id),
});
export const paymentInfoTable = mf.table('payment_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
status: varchar({ length: 50 }).notNull(),
gateway: varchar({ length: 50 }).notNull(),
orderId: varchar('order_id', { length: 500 }),
token: varchar({ length: 500 }),
merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(),
payload: jsonb('payload'),
});
export const payments = mf.table('payments', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
status: varchar({ length: 50 }).notNull(),
gateway: varchar({ length: 50 }).notNull(),
orderId: integer('order_id').notNull().references(() => orders.id),
token: varchar({ length: 500 }),
merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(),
payload: jsonb('payload'),
});
export const refunds = mf.table('refunds', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
orderId: integer('order_id').notNull().references(() => orders.id),
refundAmount: numeric('refund_amount', { precision: 10, scale: 2 }),
refundStatus: varchar('refund_status', { length: 50 }).default('none'),
merchantRefundId: varchar('merchant_refund_id', { length: 255 }),
refundProcessedAt: timestamp('refund_processed_at'),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const keyValStore = mf.table('key_val_store', {
key: varchar('key', { length: 255 }).primaryKey(),
value: jsonb('value'),
});
export const notifications = mf.table('notifications', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
title: varchar({ length: 255 }).notNull(),
body: varchar({ length: 512 }).notNull(),
type: varchar({ length: 50 }),
isRead: boolean('is_read').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const productCategories = mf.table('product_categories', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
description: varchar({ length: 500 }),
});
export const cartItems = mf.table('cart_items', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: numeric({ precision: 10, scale: 2 }).notNull(),
addedAt: timestamp('added_at').notNull().defaultNow(),
}, (t) => ({
unq_user_product: unique('unique_user_product').on(t.userId, t.productId),
}));
export const complaints = mf.table('complaints', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').references(() => orders.id),
complaintBody: varchar('complaint_body', { length: 1000 }).notNull(),
images: jsonb('images'),
response: varchar('response', { length: 1000 }),
isResolved: boolean('is_resolved').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const coupons = mf.table('coupons', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
couponCode: varchar('coupon_code', { length: 50 }).notNull().unique('unique_coupon_code'),
isUserBased: boolean('is_user_based').notNull().default(false),
discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }),
flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }),
minOrder: numeric('min_order', { precision: 10, scale: 2 }),
productIds: jsonb('product_ids'),
createdBy: integer('created_by').references(() => staffUsers.id),
maxValue: numeric('max_value', { precision: 10, scale: 2 }),
isApplyForAll: boolean('is_apply_for_all').notNull().default(false),
validTill: timestamp('valid_till'),
maxLimitForUser: integer('max_limit_for_user'),
isInvalidated: boolean('is_invalidated').notNull().default(false),
exclusiveApply: boolean('exclusive_apply').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const couponUsage = mf.table('coupon_usage', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
orderId: integer('order_id').references(() => orders.id),
orderItemId: integer('order_item_id').references(() => orderItems.id),
usedAt: timestamp('used_at').notNull().defaultNow(),
});
export const couponApplicableUsers = mf.table('coupon_applicable_users', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
userId: integer('user_id').notNull().references(() => users.id),
}, (t) => ({
unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId),
}));
export const couponApplicableProducts = mf.table('coupon_applicable_products', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
}, (t) => ({
unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId),
}));
export const userIncidents = mf.table('user_incidents', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').references(() => orders.id),
dateAdded: timestamp('date_added').notNull().defaultNow(),
adminComment: text('admin_comment'),
addedBy: integer('added_by').references(() => staffUsers.id),
negativityScore: integer('negativity_score'),
});
export const reservedCoupons = mf.table('reserved_coupons', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
secretCode: varchar('secret_code', { length: 50 }).notNull().unique(),
couponCode: varchar('coupon_code', { length: 50 }).notNull(),
discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }),
flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }),
minOrder: numeric('min_order', { precision: 10, scale: 2 }),
productIds: jsonb('product_ids'),
maxValue: numeric('max_value', { precision: 10, scale: 2 }),
validTill: timestamp('valid_till'),
maxLimitForUser: integer('max_limit_for_user'),
exclusiveApply: boolean('exclusive_apply').notNull().default(false),
isRedeemed: boolean('is_redeemed').notNull().default(false),
redeemedBy: integer('redeemed_by').references(() => users.id),
redeemedAt: timestamp('redeemed_at'),
createdBy: integer('created_by').notNull().references(() => staffUsers.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_secret_code: unique('unique_secret_code').on(t.secretCode),
}));
export const notifCreds = mf.table('notif_creds', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
token: varchar({ length: 500 }).notNull().unique(),
addedAt: timestamp('added_at').notNull().defaultNow(),
userId: integer('user_id').notNull().references(() => users.id),
lastVerified: timestamp('last_verified'),
});
export const unloggedUserTokens = mf.table('unlogged_user_tokens', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
token: varchar({ length: 500 }).notNull().unique(),
addedAt: timestamp('added_at').notNull().defaultNow(),
lastVerified: timestamp('last_verified'),
});
export const userNotifications = mf.table('user_notifications', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
title: varchar('title', { length: 255 }).notNull(),
imageUrl: varchar('image_url', { length: 500 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
body: text('body').notNull(),
applicableUsers: jsonb('applicable_users'),
});
export const staffRoles = mf.table('staff_roles', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
roleName: staffRoleEnum('role_name').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_role_name: unique('unique_role_name').on(t.roleName),
}));
export const staffPermissions = mf.table('staff_permissions', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
permissionName: staffPermissionEnum('permission_name').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_permission_name: unique('unique_permission_name').on(t.permissionName),
}));
export const staffRolePermissions = mf.table('staff_role_permissions', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id),
staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_role_permission: unique('unique_role_permission').on(t.staffRoleId, t.staffPermissionId),
}));
// Relations
export const usersRelations = relations(users, ({ many, one }) => ({
addresses: many(addresses),
orders: many(orders),
notifications: many(notifications),
cartItems: many(cartItems),
userCreds: one(userCreds),
coupons: many(coupons),
couponUsages: many(couponUsage),
applicableCoupons: many(couponApplicableUsers),
userDetails: one(userDetails),
notifCreds: many(notifCreds),
userIncidents: many(userIncidents),
}));
export const userCredsRelations = relations(userCreds, ({ one }) => ({
user: one(users, { fields: [userCreds.userId], references: [users.id] }),
}));
export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({
role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }),
coupons: many(coupons),
stores: many(storeInfo),
}));
export const addressesRelations = relations(addresses, ({ one, many }) => ({
user: one(users, { fields: [addresses.userId], references: [users.id] }),
orders: many(orders),
zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }),
}));
export const unitsRelations = relations(units, ({ many }) => ({
products: many(productInfo),
}));
export const productInfoRelations = relations(productInfo, ({ one, many }) => ({
unit: one(units, { fields: [productInfo.unitId], references: [units.id] }),
store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }),
productSlots: many(productSlots),
specialDeals: many(specialDeals),
orderItems: many(orderItems),
cartItems: many(cartItems),
tags: many(productTags),
applicableCoupons: many(couponApplicableProducts),
reviews: many(productReviews),
groups: many(productGroupMembership),
}));
export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({
products: many(productTags),
}));
export const productTagsRelations = relations(productTags, ({ one }) => ({
product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }),
tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }),
}));
export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({
productSlots: many(productSlots),
orders: many(orders),
vendorSnippets: many(vendorSnippets),
}));
export const productSlotsRelations = relations(productSlots, ({ one }) => ({
product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }),
slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }),
}));
export const specialDealsRelations = relations(specialDeals, ({ one }) => ({
product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }),
}));
export const ordersRelations = relations(orders, ({ one, many }) => ({
user: one(users, { fields: [orders.userId], references: [users.id] }),
address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }),
slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }),
orderItems: many(orderItems),
payment: one(payments),
paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }),
orderStatus: many(orderStatus),
refunds: many(refunds),
couponUsages: many(couponUsage),
userIncidents: many(userIncidents),
}));
export const orderItemsRelations = relations(orderItems, ({ one }) => ({
order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }),
product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }),
}));
export const orderStatusRelations = relations(orderStatus, ({ one }) => ({
order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }),
user: one(users, { fields: [orderStatus.userId], references: [users.id] }),
refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }),
}));
export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({
order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }),
}));
export const paymentsRelations = relations(payments, ({ one }) => ({
order: one(orders, { fields: [payments.orderId], references: [orders.id] }),
}));
export const refundsRelations = relations(refunds, ({ one }) => ({
order: one(orders, { fields: [refunds.orderId], references: [orders.id] }),
}));
export const notificationsRelations = relations(notifications, ({ one }) => ({
user: one(users, { fields: [notifications.userId], references: [users.id] }),
}));
export const productCategoriesRelations = relations(productCategories, ({}) => ({}));
export const cartItemsRelations = relations(cartItems, ({ one }) => ({
user: one(users, { fields: [cartItems.userId], references: [users.id] }),
product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }),
}));
export const complaintsRelations = relations(complaints, ({ one }) => ({
user: one(users, { fields: [complaints.userId], references: [users.id] }),
order: one(orders, { fields: [complaints.orderId], references: [orders.id] }),
}));
export const couponsRelations = relations(coupons, ({ one, many }) => ({
creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }),
usages: many(couponUsage),
applicableUsers: many(couponApplicableUsers),
applicableProducts: many(couponApplicableProducts),
}));
export const couponUsageRelations = relations(couponUsage, ({ one }) => ({
user: one(users, { fields: [couponUsage.userId], references: [users.id] }),
coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }),
order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }),
orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }),
}));
export const userDetailsRelations = relations(userDetails, ({ one }) => ({
user: one(users, { fields: [userDetails.userId], references: [users.id] }),
}));
export const notifCredsRelations = relations(notifCreds, ({ one }) => ({
user: one(users, { fields: [notifCreds.userId], references: [users.id] }),
}));
export const userNotificationsRelations = relations(userNotifications, ({}) => ({
// No relations needed for now
}));
export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({
owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }),
products: many(productInfo),
}));
export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({
coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }),
user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }),
}));
export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({
coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }),
product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }),
}));
export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({
redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }),
creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }),
}));
export const productReviewsRelations = relations(productReviews, ({ one }) => ({
user: one(users, { fields: [productReviews.userId], references: [users.id] }),
product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }),
}));
export const addressZonesRelations = relations(addressZones, ({ many }) => ({
addresses: many(addresses),
areas: many(addressAreas),
}));
export const addressAreasRelations = relations(addressAreas, ({ one }) => ({
zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }),
}));
export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({
memberships: many(productGroupMembership),
}));
export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({
product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }),
group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }),
}));
export const homeBannersRelations = relations(homeBanners, ({}) => ({
// Relations for productIds array would be more complex, skipping for now
}));
export const staffRolesRelations = relations(staffRoles, ({ many }) => ({
staffUsers: many(staffUsers),
rolePermissions: many(staffRolePermissions),
}));
export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({
rolePermissions: many(staffRolePermissions),
}));
export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({
role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }),
permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }),
}));
export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({
user: one(users, { fields: [userIncidents.userId], references: [users.id] }),
order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }),
addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }),
}));
export const productAvailabilitySchedulesRelations = relations(productAvailabilitySchedules, ({}) => ({
}));

View file

@ -0,0 +1,735 @@
import {
sqliteTable,
integer,
text,
real,
unique,
check,
} from 'drizzle-orm/sqlite-core'
import { relations, sql } from 'drizzle-orm'
const epochSeconds = sql`(strftime('%s','now'))`
const sqliteEnum = <T extends readonly [string, ...string[]]>(
_name: string,
values: T
) => (columnName: string) => text(columnName, { enum: values })
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name'),
email: text('email'),
mobile: text('mobile'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_email: unique('unique_email').on(t.email),
}))
export const userDetails = sqliteTable('user_details', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id).unique(),
bio: text('bio'),
dateOfBirth: integer('date_of_birth', { mode: 'timestamp' }),
gender: text('gender'),
occupation: text('occupation'),
profileImage: text('profile_image'),
isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const userCreds = sqliteTable('user_creds', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
userPassword: text('user_password').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const addressZones = sqliteTable('address_zones', {
id: integer('id').primaryKey({ autoIncrement: true }),
zoneName: text('zone_name').notNull(),
addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const addresses = sqliteTable('addresses', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
name: text('name').notNull(),
phone: text('phone').notNull(),
addressLine1: text('address_line1').notNull(),
addressLine2: text('address_line2'),
city: text('city').notNull(),
state: text('state').notNull(),
pincode: text('pincode').notNull(),
isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false),
latitude: real('latitude'),
longitude: real('longitude'),
googleMapsUrl: text('google_maps_url'),
adminLatitude: real('admin_latitude'),
adminLongitude: real('admin_longitude'),
zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const addressAreas = sqliteTable('address_areas', {
id: integer('id').primaryKey({ autoIncrement: true }),
placeName: text('place_name').notNull(),
zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const staffRoleEnum = sqliteEnum('staff_role', [
'super_admin',
'admin',
'marketer',
'delivery_staff',
])
export const staffPermissionEnum = sqliteEnum('staff_permission', [
'crud_product',
'make_coupon',
'crud_staff_users',
])
export const staffRoles = sqliteTable('staff_roles', {
id: integer('id').primaryKey({ autoIncrement: true }),
roleName: staffRoleEnum('role_name').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_role_name: unique('unique_role_name').on(t.roleName),
}))
export const staffPermissions = sqliteTable('staff_permissions', {
id: integer('id').primaryKey({ autoIncrement: true }),
permissionName: staffPermissionEnum('permission_name').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_permission_name: unique('unique_permission_name').on(t.permissionName),
}))
export const staffRolePermissions = sqliteTable('staff_role_permissions', {
id: integer('id').primaryKey({ autoIncrement: true }),
staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id),
staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_role_permission: unique('unique_role_permission').on(
t.staffRoleId,
t.staffPermissionId
),
}))
export const staffUsers = sqliteTable('staff_users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
password: text('password').notNull(),
staffRoleId: integer('staff_role_id').references(() => staffRoles.id),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const storeInfo = sqliteTable('store_info', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
description: text('description'),
imageUrl: text('image_url'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
owner: integer('owner').notNull().references(() => staffUsers.id),
})
export const units = sqliteTable('units', {
id: integer('id').primaryKey({ autoIncrement: true }),
shortNotation: text('short_notation').notNull(),
fullName: text('full_name').notNull(),
}, (t) => ({
unq_short_notation: unique('unique_short_notation').on(t.shortNotation),
}))
export const productAvailabilityActionEnum = sqliteEnum(
'product_availability_action',
['in', 'out']
)
export const productInfo = sqliteTable('product_info', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
shortDescription: text('short_description'),
longDescription: text('long_description'),
unitId: integer('unit_id').notNull().references(() => units.id),
price: text('price').notNull(),
marketPrice: text('market_price'),
images: text('images'),
isOutOfStock: integer('is_out_of_stock', { mode: 'boolean' }).notNull().default(false),
isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false),
isFlashAvailable: integer('is_flash_available', { mode: 'boolean' }).notNull().default(false),
flashPrice: text('flash_price'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
incrementStep: real('increment_step').notNull().default(1),
productQuantity: real('product_quantity').notNull().default(1),
storeId: integer('store_id').references(() => storeInfo.id),
scheduledAvailability: integer('scheduled_availability', { mode: 'boolean' }).notNull().default(true),
})
export const productAvailabilitySchedules = sqliteTable('product_availability_schedules', {
id: integer('id').primaryKey({ autoIncrement: true }),
time: text('time').notNull(),
scheduleName: text('schedule_name').notNull().unique(),
action: productAvailabilityActionEnum('action').notNull(),
productIds: text('product_ids').notNull().default('[]'),
groupIds: text('group_ids').notNull().default('[]'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
lastUpdated: integer('last_updated', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const productGroupInfo = sqliteTable('product_group_info', {
id: integer('id').primaryKey({ autoIncrement: true }),
groupName: text('group_name').notNull(),
description: text('description'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const productGroupMembership = sqliteTable('product_group_membership', {
productId: integer('product_id').notNull().references(() => productInfo.id),
groupId: integer('group_id').notNull().references(() => productGroupInfo.id),
addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
pk: unique('product_group_membership_pk').on(t.productId, t.groupId),
}))
export const homeBanners = sqliteTable('home_banners', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
imageUrl: text('image_url').notNull(),
description: text('description'),
productIds: text('product_ids'),
redirectUrl: text('redirect_url'),
serialNum: integer('serial_num'),
isActive: integer('is_active', { mode: 'boolean' }).notNull().default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
lastUpdated: integer('last_updated', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const productReviews = sqliteTable('product_reviews', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
reviewBody: text('review_body').notNull(),
imageUrls: text('image_urls').default('[]'),
reviewTime: integer('review_time', { mode: 'timestamp' }).notNull().default(epochSeconds),
ratings: real('ratings').notNull(),
adminResponse: text('admin_response'),
adminResponseImages: text('admin_response_images').default('[]'),
}, (t) => ({
ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`),
}))
export const uploadStatusEnum = sqliteEnum('upload_status', ['pending', 'claimed'])
export const uploadUrlStatus = sqliteTable('upload_url_status', {
id: integer('id').primaryKey({ autoIncrement: true }),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
key: text('key').notNull(),
status: uploadStatusEnum('status').notNull().default('pending'),
})
export const productTagInfo = sqliteTable('product_tag_info', {
id: integer('id').primaryKey({ autoIncrement: true }),
tagName: text('tag_name').notNull().unique(),
tagDescription: text('tag_description'),
imageUrl: text('image_url'),
isDashboardTag: integer('is_dashboard_tag', { mode: 'boolean' }).notNull().default(false),
relatedStores: text('related_stores').default('[]'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const productTags = sqliteTable('product_tags', {
id: integer('id').primaryKey({ autoIncrement: true }),
productId: integer('product_id').notNull().references(() => productInfo.id),
tagId: integer('tag_id').notNull().references(() => productTagInfo.id),
assignedAt: integer('assigned_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId),
}))
export const deliverySlotInfo = sqliteTable('delivery_slot_info', {
id: integer('id').primaryKey({ autoIncrement: true }),
deliveryTime: integer('delivery_time', { mode: 'timestamp' }).notNull(),
freezeTime: integer('freeze_time', { mode: 'timestamp' }).notNull(),
isActive: integer('is_active', { mode: 'boolean' }).notNull().default(true),
isFlash: integer('is_flash', { mode: 'boolean' }).notNull().default(false),
isCapacityFull: integer('is_capacity_full', { mode: 'boolean' }).notNull().default(false),
deliverySequence: text('delivery_sequence').default('{}'),
groupIds: text('group_ids').default('[]'),
})
export const vendorSnippets = sqliteTable('vendor_snippets', {
id: integer('id').primaryKey({ autoIncrement: true }),
snippetCode: text('snippet_code').notNull().unique(),
slotId: integer('slot_id').references(() => deliverySlotInfo.id),
isPermanent: integer('is_permanent', { mode: 'boolean' }).notNull().default(false),
productIds: text('product_ids').notNull(),
validTill: integer('valid_till', { mode: 'timestamp' }),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const productSlots = sqliteTable('product_slots', {
productId: integer('product_id').notNull().references(() => productInfo.id),
slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id),
}, (t) => ({
pk: unique('product_slot_pk').on(t.productId, t.slotId),
}))
export const specialDeals = sqliteTable('special_deals', {
id: integer('id').primaryKey({ autoIncrement: true }),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: text('quantity').notNull(),
price: text('price').notNull(),
validTill: integer('valid_till', { mode: 'timestamp' }).notNull(),
})
export const paymentInfoTable = sqliteTable('payment_info', {
id: integer('id').primaryKey({ autoIncrement: true }),
status: text('status').notNull(),
gateway: text('gateway').notNull(),
orderId: text('order_id'),
token: text('token'),
merchantOrderId: text('merchant_order_id').notNull().unique(),
payload: text('payload'),
})
export const orders = sqliteTable('orders', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
addressId: integer('address_id').notNull().references(() => addresses.id),
slotId: integer('slot_id').references(() => deliverySlotInfo.id),
isCod: integer('is_cod', { mode: 'boolean' }).notNull().default(false),
isOnlinePayment: integer('is_online_payment', { mode: 'boolean' }).notNull().default(false),
paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id),
totalAmount: text('total_amount').notNull(),
deliveryCharge: text('delivery_charge').notNull().default('0'),
readableId: integer('readable_id').notNull(),
adminNotes: text('admin_notes'),
userNotes: text('user_notes'),
orderGroupId: text('order_group_id'),
orderGroupProportion: text('order_group_proportion'),
isFlashDelivery: integer('is_flash_delivery', { mode: 'boolean' }).notNull().default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const orderItems = sqliteTable('order_items', {
id: integer('id').primaryKey({ autoIncrement: true }),
orderId: integer('order_id').notNull().references(() => orders.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: text('quantity').notNull(),
price: text('price').notNull(),
discountedPrice: text('discounted_price'),
is_packaged: integer('is_packaged', { mode: 'boolean' }).notNull().default(false),
is_package_verified: integer('is_package_verified', { mode: 'boolean' }).notNull().default(false),
})
export const paymentStatusEnum = sqliteEnum('payment_status', [
'pending',
'success',
'cod',
'failed',
])
export const orderStatus = sqliteTable('order_status', {
id: integer('id').primaryKey({ autoIncrement: true }),
orderTime: integer('order_time', { mode: 'timestamp' }).notNull().default(epochSeconds),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').notNull().references(() => orders.id),
isPackaged: integer('is_packaged', { mode: 'boolean' }).notNull().default(false),
isDelivered: integer('is_delivered', { mode: 'boolean' }).notNull().default(false),
isCancelled: integer('is_cancelled', { mode: 'boolean' }).notNull().default(false),
cancelReason: text('cancel_reason'),
isCancelledByAdmin: integer('is_cancelled_by_admin', { mode: 'boolean' }),
paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'),
cancellationUserNotes: text('cancellation_user_notes'),
cancellationAdminNotes: text('cancellation_admin_notes'),
cancellationReviewed: integer('cancellation_reviewed', { mode: 'boolean' }).notNull().default(false),
cancellationReviewedAt: integer('cancellation_reviewed_at', { mode: 'timestamp' }),
refundCouponId: integer('refund_coupon_id').references(() => coupons.id),
})
export const payments = sqliteTable('payments', {
id: integer('id').primaryKey({ autoIncrement: true }),
status: text('status').notNull(),
gateway: text('gateway').notNull(),
orderId: integer('order_id').notNull().references(() => orders.id),
token: text('token'),
merchantOrderId: text('merchant_order_id').notNull().unique(),
payload: text('payload'),
})
export const refunds = sqliteTable('refunds', {
id: integer('id').primaryKey({ autoIncrement: true }),
orderId: integer('order_id').notNull().references(() => orders.id),
refundAmount: text('refund_amount'),
refundStatus: text('refund_status').default('none'),
merchantRefundId: text('merchant_refund_id'),
refundProcessedAt: integer('refund_processed_at', { mode: 'timestamp' }),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const keyValStore = sqliteTable('key_val_store', {
key: text('key').primaryKey(),
value: text('value'),
})
export const notifications = sqliteTable('notifications', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
title: text('title').notNull(),
body: text('body').notNull(),
type: text('type'),
isRead: integer('is_read', { mode: 'boolean' }).notNull().default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const productCategories = sqliteTable('product_categories', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
description: text('description'),
})
export const cartItems = sqliteTable('cart_items', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: text('quantity').notNull(),
addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_user_product: unique('unique_user_product').on(t.userId, t.productId),
}))
export const complaints = sqliteTable('complaints', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').references(() => orders.id),
complaintBody: text('complaint_body').notNull(),
images: text('images'),
response: text('response'),
isResolved: integer('is_resolved', { mode: 'boolean' }).notNull().default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const coupons = sqliteTable('coupons', {
id: integer('id').primaryKey({ autoIncrement: true }),
couponCode: text('coupon_code').notNull().unique('unique_coupon_code'),
isUserBased: integer('is_user_based', { mode: 'boolean' }).notNull().default(false),
discountPercent: text('discount_percent'),
flatDiscount: text('flat_discount'),
minOrder: text('min_order'),
productIds: text('product_ids'),
createdBy: integer('created_by').references(() => staffUsers.id),
maxValue: text('max_value'),
isApplyForAll: integer('is_apply_for_all', { mode: 'boolean' }).notNull().default(false),
validTill: integer('valid_till', { mode: 'timestamp' }),
maxLimitForUser: integer('max_limit_for_user'),
isInvalidated: integer('is_invalidated', { mode: 'boolean' }).notNull().default(false),
exclusiveApply: integer('exclusive_apply', { mode: 'boolean' }).notNull().default(false),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const couponUsage = sqliteTable('coupon_usage', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
orderId: integer('order_id').references(() => orders.id),
orderItemId: integer('order_item_id').references(() => orderItems.id),
usedAt: integer('used_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
})
export const couponApplicableUsers = sqliteTable('coupon_applicable_users', {
id: integer('id').primaryKey({ autoIncrement: true }),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
userId: integer('user_id').notNull().references(() => users.id),
}, (t) => ({
unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId),
}))
export const couponApplicableProducts = sqliteTable('coupon_applicable_products', {
id: integer('id').primaryKey({ autoIncrement: true }),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
}, (t) => ({
unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId),
}))
export const userIncidents = sqliteTable('user_incidents', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').references(() => orders.id),
dateAdded: integer('date_added', { mode: 'timestamp' }).notNull().default(epochSeconds),
adminComment: text('admin_comment'),
addedBy: integer('added_by').references(() => staffUsers.id),
negativityScore: integer('negativity_score'),
})
export const reservedCoupons = sqliteTable('reserved_coupons', {
id: integer('id').primaryKey({ autoIncrement: true }),
secretCode: text('secret_code').notNull().unique(),
couponCode: text('coupon_code').notNull(),
discountPercent: text('discount_percent'),
flatDiscount: text('flat_discount'),
minOrder: text('min_order'),
productIds: text('product_ids'),
maxValue: text('max_value'),
validTill: integer('valid_till', { mode: 'timestamp' }),
maxLimitForUser: integer('max_limit_for_user'),
exclusiveApply: integer('exclusive_apply', { mode: 'boolean' }).notNull().default(false),
isRedeemed: integer('is_redeemed', { mode: 'boolean' }).notNull().default(false),
redeemedBy: integer('redeemed_by').references(() => users.id),
redeemedAt: integer('redeemed_at', { mode: 'timestamp' }),
createdBy: integer('created_by').notNull().references(() => staffUsers.id),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
}, (t) => ({
unq_secret_code: unique('unique_secret_code').on(t.secretCode),
}))
export const notifCreds = sqliteTable('notif_creds', {
id: integer('id').primaryKey({ autoIncrement: true }),
token: text('token').notNull().unique(),
addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
userId: integer('user_id').notNull().references(() => users.id),
lastVerified: integer('last_verified', { mode: 'timestamp' }),
})
export const unloggedUserTokens = sqliteTable('unlogged_user_tokens', {
id: integer('id').primaryKey({ autoIncrement: true }),
token: text('token').notNull().unique(),
addedAt: integer('added_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
lastVerified: integer('last_verified', { mode: 'timestamp' }),
})
export const userNotifications = sqliteTable('user_notifications', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
imageUrl: text('image_url'),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(epochSeconds),
body: text('body').notNull(),
applicableUsers: text('applicable_users'),
})
export const uploadUrlStatusRelations = relations(uploadUrlStatus, ({}) => ({}))
export const userCredsRelations = relations(userCreds, ({ one }) => ({
user: one(users, { fields: [userCreds.userId], references: [users.id] }),
}))
export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({
role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }),
coupons: many(coupons),
stores: many(storeInfo),
}))
export const addressesRelations = relations(addresses, ({ one, many }) => ({
user: one(users, { fields: [addresses.userId], references: [users.id] }),
orders: many(orders),
zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }),
}))
export const unitsRelations = relations(units, ({ many }) => ({
products: many(productInfo),
}))
export const productInfoRelations = relations(productInfo, ({ one, many }) => ({
unit: one(units, { fields: [productInfo.unitId], references: [units.id] }),
store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }),
productSlots: many(productSlots),
specialDeals: many(specialDeals),
orderItems: many(orderItems),
cartItems: many(cartItems),
tags: many(productTags),
applicableCoupons: many(couponApplicableProducts),
reviews: many(productReviews),
groups: many(productGroupMembership),
}))
export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({
products: many(productTags),
}))
export const productTagsRelations = relations(productTags, ({ one }) => ({
product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }),
tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }),
}))
export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({
productSlots: many(productSlots),
orders: many(orders),
vendorSnippets: many(vendorSnippets),
}))
export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({
slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }),
}))
export const productSlotsRelations = relations(productSlots, ({ one }) => ({
product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }),
slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }),
}))
export const specialDealsRelations = relations(specialDeals, ({ one }) => ({
product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }),
}))
export const ordersRelations = relations(orders, ({ one, many }) => ({
user: one(users, { fields: [orders.userId], references: [users.id] }),
address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }),
slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }),
orderItems: many(orderItems),
payment: one(payments),
paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }),
orderStatus: many(orderStatus),
refunds: many(refunds),
couponUsages: many(couponUsage),
userIncidents: many(userIncidents),
}))
export const orderItemsRelations = relations(orderItems, ({ one }) => ({
order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }),
product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }),
}))
export const orderStatusRelations = relations(orderStatus, ({ one }) => ({
order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }),
user: one(users, { fields: [orderStatus.userId], references: [users.id] }),
refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }),
}))
export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({
order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }),
}))
export const paymentsRelations = relations(payments, ({ one }) => ({
order: one(orders, { fields: [payments.orderId], references: [orders.id] }),
}))
export const refundsRelations = relations(refunds, ({ one }) => ({
order: one(orders, { fields: [refunds.orderId], references: [orders.id] }),
}))
export const notificationsRelations = relations(notifications, ({ one }) => ({
user: one(users, { fields: [notifications.userId], references: [users.id] }),
}))
export const productCategoriesRelations = relations(productCategories, ({}) => ({}))
export const cartItemsRelations = relations(cartItems, ({ one }) => ({
user: one(users, { fields: [cartItems.userId], references: [users.id] }),
product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }),
}))
export const complaintsRelations = relations(complaints, ({ one }) => ({
user: one(users, { fields: [complaints.userId], references: [users.id] }),
order: one(orders, { fields: [complaints.orderId], references: [orders.id] }),
}))
export const couponsRelations = relations(coupons, ({ one, many }) => ({
creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }),
usages: many(couponUsage),
applicableUsers: many(couponApplicableUsers),
applicableProducts: many(couponApplicableProducts),
}))
export const couponUsageRelations = relations(couponUsage, ({ one }) => ({
user: one(users, { fields: [couponUsage.userId], references: [users.id] }),
coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }),
order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }),
orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }),
}))
export const userDetailsRelations = relations(userDetails, ({ one }) => ({
user: one(users, { fields: [userDetails.userId], references: [users.id] }),
}))
export const notifCredsRelations = relations(notifCreds, ({ one }) => ({
user: one(users, { fields: [notifCreds.userId], references: [users.id] }),
}))
export const userNotificationsRelations = relations(userNotifications, ({}) => ({}))
export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({
owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }),
products: many(productInfo),
}))
export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({
coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }),
user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }),
}))
export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({
coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }),
product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }),
}))
export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({
redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }),
creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }),
}))
export const productReviewsRelations = relations(productReviews, ({ one }) => ({
user: one(users, { fields: [productReviews.userId], references: [users.id] }),
product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }),
}))
export const addressZonesRelations = relations(addressZones, ({ many }) => ({
addresses: many(addresses),
areas: many(addressAreas),
}))
export const addressAreasRelations = relations(addressAreas, ({ one }) => ({
zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }),
}))
export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({
memberships: many(productGroupMembership),
}))
export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({
product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }),
group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }),
}))
export const homeBannersRelations = relations(homeBanners, ({}) => ({}))
export const staffRolesRelations = relations(staffRoles, ({ many }) => ({
staffUsers: many(staffUsers),
rolePermissions: many(staffRolePermissions),
}))
export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({
rolePermissions: many(staffRolePermissions),
}))
export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({
role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }),
permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }),
}))
export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({
user: one(users, { fields: [userIncidents.userId], references: [users.id] }),
order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }),
addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }),
}))
export const productAvailabilitySchedulesRelations = relations(
productAvailabilitySchedules,
({}) => ({})
)
export const usersRelations = relations(users, ({ many, one }) => ({
addresses: many(addresses),
orders: many(orders),
notifications: many(notifications),
cartItems: many(cartItems),
userCreds: one(userCreds),
coupons: many(coupons),
couponUsages: many(couponUsage),
applicableCoupons: many(couponApplicableUsers),
userDetails: one(userDetails),
notifCreds: many(notifCreds),
userIncidents: many(userIncidents),
}))

707
apps/backend/src/db/schema.ts Executable file → Normal file
View file

@ -1,706 +1 @@
import { pgTable, pgSchema, integer, varchar, date, boolean, timestamp, numeric, jsonb, pgEnum, unique, real, text, check, decimal } from "drizzle-orm/pg-core";
import { relations, sql } from "drizzle-orm";
const mf = pgSchema('mf');
export const users = mf.table('users', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }),
email: varchar({ length: 255 }),
mobile: varchar({ length: 255 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_email: unique('unique_email').on(t.email),
}));
export const userDetails = mf.table('user_details', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id).unique(),
bio: varchar('bio', { length: 500 }),
dateOfBirth: date('date_of_birth'),
gender: varchar('gender', { length: 20 }),
occupation: varchar('occupation', { length: 100 }),
profileImage: varchar('profile_image', { length: 500 }),
isSuspended: boolean('is_suspended').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export const userCreds = mf.table('user_creds', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
userPassword: varchar('user_password', { length: 255 }).notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const addresses = mf.table('addresses', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
name: varchar('name', { length: 255 }).notNull(),
phone: varchar('phone', { length: 15 }).notNull(),
addressLine1: varchar('address_line1', { length: 255 }).notNull(),
addressLine2: varchar('address_line2', { length: 255 }),
city: varchar('city', { length: 100 }).notNull(),
state: varchar('state', { length: 100 }).notNull(),
pincode: varchar('pincode', { length: 10 }).notNull(),
isDefault: boolean('is_default').notNull().default(false),
latitude: real('latitude'),
longitude: real('longitude'),
googleMapsUrl: varchar('google_maps_url', { length: 500 }),
adminLatitude: real('admin_latitude'),
adminLongitude: real('admin_longitude'),
zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const addressZones = mf.table('address_zones', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
zoneName: varchar('zone_name', { length: 255 }).notNull(),
addedAt: timestamp('added_at').notNull().defaultNow(),
});
export const addressAreas = mf.table('address_areas', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
placeName: varchar('place_name', { length: 255 }).notNull(),
zoneId: integer('zone_id').references(() => addressZones.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const staffUsers = mf.table('staff_users', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
password: varchar({ length: 255 }).notNull(),
staffRoleId: integer('staff_role_id').references(() => staffRoles.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const storeInfo = mf.table('store_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
description: varchar({ length: 500 }),
imageUrl: varchar('image_url', { length: 500 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
owner: integer('owner').notNull().references(() => staffUsers.id),
});
export const units = mf.table('units', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
shortNotation: varchar('short_notation', { length: 50 }).notNull(),
fullName: varchar('full_name', { length: 100 }).notNull(),
}, (t) => ({
unq_short_notation: unique('unique_short_notation').on(t.shortNotation),
}));
export const productAvailabilityActionEnum = pgEnum('product_availability_action', ['in', 'out']);
export const productInfo = mf.table('product_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
shortDescription: varchar('short_description', { length: 500 }),
longDescription: varchar('long_description', { length: 1000 }),
unitId: integer('unit_id').notNull().references(() => units.id),
price: numeric({ precision: 10, scale: 2 }).notNull(),
marketPrice: numeric('market_price', { precision: 10, scale: 2 }),
images: jsonb('images'),
isOutOfStock: boolean('is_out_of_stock').notNull().default(false),
isSuspended: boolean('is_suspended').notNull().default(false),
isFlashAvailable: boolean('is_flash_available').notNull().default(false),
flashPrice: numeric('flash_price', { precision: 10, scale: 2 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
incrementStep: real('increment_step').notNull().default(1),
productQuantity: real('product_quantity').notNull().default(1),
storeId: integer('store_id').references(() => storeInfo.id),
scheduledAvailability: boolean('scheduled_availability').notNull().default(true),
});
export const productAvailabilitySchedules = mf.table('product_availability_schedules', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
time: varchar('time', { length: 10 }).notNull(),
scheduleName: varchar('schedule_name', { length: 255 }).notNull().unique(),
action: productAvailabilityActionEnum('action').notNull(),
productIds: integer('product_ids').array().notNull().default([]),
groupIds: integer('group_ids').array().notNull().default([]),
createdAt: timestamp('created_at').notNull().defaultNow(),
lastUpdated: timestamp('last_updated').notNull().defaultNow(),
});
export const productGroupInfo = mf.table('product_group_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
groupName: varchar('group_name', { length: 255 }).notNull(),
description: varchar({ length: 500 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const productGroupMembership = mf.table('product_group_membership', {
productId: integer('product_id').notNull().references(() => productInfo.id),
groupId: integer('group_id').notNull().references(() => productGroupInfo.id),
addedAt: timestamp('added_at').notNull().defaultNow(),
}, (t) => ({
pk: unique('product_group_membership_pk').on(t.productId, t.groupId),
}));
export const homeBanners = mf.table('home_banners', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar('name', { length: 255 }).notNull(),
imageUrl: varchar('image_url', { length: 500 }).notNull(),
description: varchar('description', { length: 500 }),
productIds: integer('product_ids').array(),
redirectUrl: varchar('redirect_url', { length: 500 }),
serialNum: integer('serial_num'),
isActive: boolean('is_active').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
lastUpdated: timestamp('last_updated').notNull().defaultNow(),
});
export const productReviews = mf.table('product_reviews', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
reviewBody: text('review_body').notNull(),
imageUrls: jsonb('image_urls').$defaultFn(() => []),
reviewTime: timestamp('review_time').notNull().defaultNow(),
ratings: real('ratings').notNull(),
adminResponse: text('admin_response'),
adminResponseImages: jsonb('admin_response_images').$defaultFn(() => []),
}, (t) => ({
ratingCheck: check('rating_check', sql`${t.ratings} >= 1 AND ${t.ratings} <= 5`),
}));
export const uploadStatusEnum = pgEnum('upload_status', ['pending', 'claimed']);
export const staffRoleEnum = pgEnum('staff_role', ['super_admin', 'admin', 'marketer', 'delivery_staff']);
export const staffPermissionEnum = pgEnum('staff_permission', ['crud_product', 'make_coupon', 'crud_staff_users']);
export const uploadUrlStatus = mf.table('upload_url_status', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
createdAt: timestamp('created_at').notNull().defaultNow(),
key: varchar('key', { length: 500 }).notNull(),
status: uploadStatusEnum('status').notNull().default('pending'),
});
export const productTagInfo = mf.table('product_tag_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
tagName: varchar('tag_name', { length: 100 }).notNull().unique(),
tagDescription: varchar('tag_description', { length: 500 }),
imageUrl: varchar('image_url', { length: 500 }),
isDashboardTag: boolean('is_dashboard_tag').notNull().default(false),
relatedStores: jsonb('related_stores').$defaultFn(() => []),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const productTags = mf.table('product_tags', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
productId: integer('product_id').notNull().references(() => productInfo.id),
tagId: integer('tag_id').notNull().references(() => productTagInfo.id),
assignedAt: timestamp('assigned_at').notNull().defaultNow(),
}, (t) => ({
unq_product_tag: unique('unique_product_tag').on(t.productId, t.tagId),
}));
export const deliverySlotInfo = mf.table('delivery_slot_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
deliveryTime: timestamp('delivery_time').notNull(),
freezeTime: timestamp('freeze_time').notNull(),
isActive: boolean('is_active').notNull().default(true),
isFlash: boolean('is_flash').notNull().default(false),
isCapacityFull: boolean('is_capacity_full').notNull().default(false),
deliverySequence: jsonb('delivery_sequence').$defaultFn(() => {}),
groupIds: jsonb('group_ids').$defaultFn(() => []),
});
export const vendorSnippets = mf.table('vendor_snippets', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
snippetCode: varchar('snippet_code', { length: 255 }).notNull().unique(),
slotId: integer('slot_id').references(() => deliverySlotInfo.id),
isPermanent: boolean('is_permanent').notNull().default(false),
productIds: integer('product_ids').array().notNull(),
validTill: timestamp('valid_till'),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const vendorSnippetsRelations = relations(vendorSnippets, ({ one }) => ({
slot: one(deliverySlotInfo, { fields: [vendorSnippets.slotId], references: [deliverySlotInfo.id] }),
}));
export const productSlots = mf.table('product_slots', {
productId: integer('product_id').notNull().references(() => productInfo.id),
slotId: integer('slot_id').notNull().references(() => deliverySlotInfo.id),
}, (t) => ({
pk: unique('product_slot_pk').on(t.productId, t.slotId),
}));
export const specialDeals = mf.table('special_deals', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: numeric({ precision: 10, scale: 2 }).notNull(),
price: numeric({ precision: 10, scale: 2 }).notNull(),
validTill: timestamp('valid_till').notNull(),
});
export const orders = mf.table('orders', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
addressId: integer('address_id').notNull().references(() => addresses.id),
slotId: integer('slot_id').references(() => deliverySlotInfo.id),
isCod: boolean('is_cod').notNull().default(false),
isOnlinePayment: boolean('is_online_payment').notNull().default(false),
paymentInfoId: integer('payment_info_id').references(() => paymentInfoTable.id),
totalAmount: numeric('total_amount', { precision: 10, scale: 2 }).notNull(),
deliveryCharge: numeric('delivery_charge', { precision: 10, scale: 2 }).notNull().default('0'),
readableId: integer('readable_id').notNull(),
adminNotes: text('admin_notes'),
userNotes: text('user_notes'),
orderGroupId: varchar('order_group_id', { length: 255 }),
orderGroupProportion: decimal('order_group_proportion', { precision: 10, scale: 4 }),
isFlashDelivery: boolean('is_flash_delivery').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const orderItems = mf.table('order_items', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
orderId: integer('order_id').notNull().references(() => orders.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: varchar('quantity', { length: 50 }).notNull(),
price: numeric({ precision: 10, scale: 2 }).notNull(),
discountedPrice: numeric('discounted_price', { precision: 10, scale: 2 }),
is_packaged: boolean('is_packaged').notNull().default(false),
is_package_verified: boolean('is_package_verified').notNull().default(false),
});
export const paymentStatusEnum = pgEnum('payment_status', ['pending', 'success', 'cod', 'failed']);
export const orderStatus = mf.table('order_status', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
orderTime: timestamp('order_time').notNull().defaultNow(),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').notNull().references(() => orders.id),
isPackaged: boolean('is_packaged').notNull().default(false),
isDelivered: boolean('is_delivered').notNull().default(false),
isCancelled: boolean('is_cancelled').notNull().default(false),
cancelReason: varchar('cancel_reason', { length: 255 }),
isCancelledByAdmin: boolean('is_cancelled_by_admin'),
paymentStatus: paymentStatusEnum('payment_state').notNull().default('pending'),
cancellationUserNotes: text('cancellation_user_notes'),
cancellationAdminNotes: text('cancellation_admin_notes'),
cancellationReviewed: boolean('cancellation_reviewed').notNull().default(false),
cancellationReviewedAt: timestamp('cancellation_reviewed_at'),
refundCouponId: integer('refund_coupon_id').references(() => coupons.id),
});
export const paymentInfoTable = mf.table('payment_info', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
status: varchar({ length: 50 }).notNull(),
gateway: varchar({ length: 50 }).notNull(),
orderId: varchar('order_id', { length: 500 }),
token: varchar({ length: 500 }),
merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(),
payload: jsonb('payload'),
});
export const payments = mf.table('payments', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
status: varchar({ length: 50 }).notNull(),
gateway: varchar({ length: 50 }).notNull(),
orderId: integer('order_id').notNull().references(() => orders.id),
token: varchar({ length: 500 }),
merchantOrderId: varchar('merchant_order_id', { length: 255 }).notNull().unique(),
payload: jsonb('payload'),
});
export const refunds = mf.table('refunds', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
orderId: integer('order_id').notNull().references(() => orders.id),
refundAmount: numeric('refund_amount', { precision: 10, scale: 2 }),
refundStatus: varchar('refund_status', { length: 50 }).default('none'),
merchantRefundId: varchar('merchant_refund_id', { length: 255 }),
refundProcessedAt: timestamp('refund_processed_at'),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const keyValStore = mf.table('key_val_store', {
key: varchar('key', { length: 255 }).primaryKey(),
value: jsonb('value'),
});
export const notifications = mf.table('notifications', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
title: varchar({ length: 255 }).notNull(),
body: varchar({ length: 512 }).notNull(),
type: varchar({ length: 50 }),
isRead: boolean('is_read').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const productCategories = mf.table('product_categories', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
name: varchar({ length: 255 }).notNull(),
description: varchar({ length: 500 }),
});
export const cartItems = mf.table('cart_items', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
quantity: numeric({ precision: 10, scale: 2 }).notNull(),
addedAt: timestamp('added_at').notNull().defaultNow(),
}, (t) => ({
unq_user_product: unique('unique_user_product').on(t.userId, t.productId),
}));
export const complaints = mf.table('complaints', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').references(() => orders.id),
complaintBody: varchar('complaint_body', { length: 1000 }).notNull(),
images: jsonb('images'),
response: varchar('response', { length: 1000 }),
isResolved: boolean('is_resolved').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const coupons = mf.table('coupons', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
couponCode: varchar('coupon_code', { length: 50 }).notNull().unique('unique_coupon_code'),
isUserBased: boolean('is_user_based').notNull().default(false),
discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }),
flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }),
minOrder: numeric('min_order', { precision: 10, scale: 2 }),
productIds: jsonb('product_ids'),
createdBy: integer('created_by').references(() => staffUsers.id),
maxValue: numeric('max_value', { precision: 10, scale: 2 }),
isApplyForAll: boolean('is_apply_for_all').notNull().default(false),
validTill: timestamp('valid_till'),
maxLimitForUser: integer('max_limit_for_user'),
isInvalidated: boolean('is_invalidated').notNull().default(false),
exclusiveApply: boolean('exclusive_apply').notNull().default(false),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const couponUsage = mf.table('coupon_usage', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
orderId: integer('order_id').references(() => orders.id),
orderItemId: integer('order_item_id').references(() => orderItems.id),
usedAt: timestamp('used_at').notNull().defaultNow(),
});
export const couponApplicableUsers = mf.table('coupon_applicable_users', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
userId: integer('user_id').notNull().references(() => users.id),
}, (t) => ({
unq_coupon_user: unique('unique_coupon_user').on(t.couponId, t.userId),
}));
export const couponApplicableProducts = mf.table('coupon_applicable_products', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
couponId: integer('coupon_id').notNull().references(() => coupons.id),
productId: integer('product_id').notNull().references(() => productInfo.id),
}, (t) => ({
unq_coupon_product: unique('unique_coupon_product').on(t.couponId, t.productId),
}));
export const userIncidents = mf.table('user_incidents', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
userId: integer('user_id').notNull().references(() => users.id),
orderId: integer('order_id').references(() => orders.id),
dateAdded: timestamp('date_added').notNull().defaultNow(),
adminComment: text('admin_comment'),
addedBy: integer('added_by').references(() => staffUsers.id),
negativityScore: integer('negativity_score'),
});
export const reservedCoupons = mf.table('reserved_coupons', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
secretCode: varchar('secret_code', { length: 50 }).notNull().unique(),
couponCode: varchar('coupon_code', { length: 50 }).notNull(),
discountPercent: numeric('discount_percent', { precision: 5, scale: 2 }),
flatDiscount: numeric('flat_discount', { precision: 10, scale: 2 }),
minOrder: numeric('min_order', { precision: 10, scale: 2 }),
productIds: jsonb('product_ids'),
maxValue: numeric('max_value', { precision: 10, scale: 2 }),
validTill: timestamp('valid_till'),
maxLimitForUser: integer('max_limit_for_user'),
exclusiveApply: boolean('exclusive_apply').notNull().default(false),
isRedeemed: boolean('is_redeemed').notNull().default(false),
redeemedBy: integer('redeemed_by').references(() => users.id),
redeemedAt: timestamp('redeemed_at'),
createdBy: integer('created_by').notNull().references(() => staffUsers.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_secret_code: unique('unique_secret_code').on(t.secretCode),
}));
export const notifCreds = mf.table('notif_creds', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
token: varchar({ length: 500 }).notNull().unique(),
addedAt: timestamp('added_at').notNull().defaultNow(),
userId: integer('user_id').notNull().references(() => users.id),
lastVerified: timestamp('last_verified'),
});
export const unloggedUserTokens = mf.table('unlogged_user_tokens', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
token: varchar({ length: 500 }).notNull().unique(),
addedAt: timestamp('added_at').notNull().defaultNow(),
lastVerified: timestamp('last_verified'),
});
export const userNotifications = mf.table('user_notifications', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
title: varchar('title', { length: 255 }).notNull(),
imageUrl: varchar('image_url', { length: 500 }),
createdAt: timestamp('created_at').notNull().defaultNow(),
body: text('body').notNull(),
applicableUsers: jsonb('applicable_users'),
});
export const staffRoles = mf.table('staff_roles', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
roleName: staffRoleEnum('role_name').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_role_name: unique('unique_role_name').on(t.roleName),
}));
export const staffPermissions = mf.table('staff_permissions', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
permissionName: staffPermissionEnum('permission_name').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_permission_name: unique('unique_permission_name').on(t.permissionName),
}));
export const staffRolePermissions = mf.table('staff_role_permissions', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
staffRoleId: integer('staff_role_id').notNull().references(() => staffRoles.id),
staffPermissionId: integer('staff_permission_id').notNull().references(() => staffPermissions.id),
createdAt: timestamp('created_at').notNull().defaultNow(),
}, (t) => ({
unq_role_permission: unique('unique_role_permission').on(t.staffRoleId, t.staffPermissionId),
}));
// Relations
export const usersRelations = relations(users, ({ many, one }) => ({
addresses: many(addresses),
orders: many(orders),
notifications: many(notifications),
cartItems: many(cartItems),
userCreds: one(userCreds),
coupons: many(coupons),
couponUsages: many(couponUsage),
applicableCoupons: many(couponApplicableUsers),
userDetails: one(userDetails),
notifCreds: many(notifCreds),
userIncidents: many(userIncidents),
}));
export const userCredsRelations = relations(userCreds, ({ one }) => ({
user: one(users, { fields: [userCreds.userId], references: [users.id] }),
}));
export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({
role: one(staffRoles, { fields: [staffUsers.staffRoleId], references: [staffRoles.id] }),
coupons: many(coupons),
stores: many(storeInfo),
}));
export const addressesRelations = relations(addresses, ({ one, many }) => ({
user: one(users, { fields: [addresses.userId], references: [users.id] }),
orders: many(orders),
zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }),
}));
export const unitsRelations = relations(units, ({ many }) => ({
products: many(productInfo),
}));
export const productInfoRelations = relations(productInfo, ({ one, many }) => ({
unit: one(units, { fields: [productInfo.unitId], references: [units.id] }),
store: one(storeInfo, { fields: [productInfo.storeId], references: [storeInfo.id] }),
productSlots: many(productSlots),
specialDeals: many(specialDeals),
orderItems: many(orderItems),
cartItems: many(cartItems),
tags: many(productTags),
applicableCoupons: many(couponApplicableProducts),
reviews: many(productReviews),
groups: many(productGroupMembership),
}));
export const productTagInfoRelations = relations(productTagInfo, ({ many }) => ({
products: many(productTags),
}));
export const productTagsRelations = relations(productTags, ({ one }) => ({
product: one(productInfo, { fields: [productTags.productId], references: [productInfo.id] }),
tag: one(productTagInfo, { fields: [productTags.tagId], references: [productTagInfo.id] }),
}));
export const deliverySlotInfoRelations = relations(deliverySlotInfo, ({ many }) => ({
productSlots: many(productSlots),
orders: many(orders),
vendorSnippets: many(vendorSnippets),
}));
export const productSlotsRelations = relations(productSlots, ({ one }) => ({
product: one(productInfo, { fields: [productSlots.productId], references: [productInfo.id] }),
slot: one(deliverySlotInfo, { fields: [productSlots.slotId], references: [deliverySlotInfo.id] }),
}));
export const specialDealsRelations = relations(specialDeals, ({ one }) => ({
product: one(productInfo, { fields: [specialDeals.productId], references: [productInfo.id] }),
}));
export const ordersRelations = relations(orders, ({ one, many }) => ({
user: one(users, { fields: [orders.userId], references: [users.id] }),
address: one(addresses, { fields: [orders.addressId], references: [addresses.id] }),
slot: one(deliverySlotInfo, { fields: [orders.slotId], references: [deliverySlotInfo.id] }),
orderItems: many(orderItems),
payment: one(payments),
paymentInfo: one(paymentInfoTable, { fields: [orders.paymentInfoId], references: [paymentInfoTable.id] }),
orderStatus: many(orderStatus),
refunds: many(refunds),
couponUsages: many(couponUsage),
userIncidents: many(userIncidents),
}));
export const orderItemsRelations = relations(orderItems, ({ one }) => ({
order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }),
product: one(productInfo, { fields: [orderItems.productId], references: [productInfo.id] }),
}));
export const orderStatusRelations = relations(orderStatus, ({ one }) => ({
order: one(orders, { fields: [orderStatus.orderId], references: [orders.id] }),
user: one(users, { fields: [orderStatus.userId], references: [users.id] }),
refundCoupon: one(coupons, { fields: [orderStatus.refundCouponId], references: [coupons.id] }),
}));
export const paymentInfoRelations = relations(paymentInfoTable, ({ one }) => ({
order: one(orders, { fields: [paymentInfoTable.id], references: [orders.paymentInfoId] }),
}));
export const paymentsRelations = relations(payments, ({ one }) => ({
order: one(orders, { fields: [payments.orderId], references: [orders.id] }),
}));
export const refundsRelations = relations(refunds, ({ one }) => ({
order: one(orders, { fields: [refunds.orderId], references: [orders.id] }),
}));
export const notificationsRelations = relations(notifications, ({ one }) => ({
user: one(users, { fields: [notifications.userId], references: [users.id] }),
}));
export const productCategoriesRelations = relations(productCategories, ({}) => ({}));
export const cartItemsRelations = relations(cartItems, ({ one }) => ({
user: one(users, { fields: [cartItems.userId], references: [users.id] }),
product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }),
}));
export const complaintsRelations = relations(complaints, ({ one }) => ({
user: one(users, { fields: [complaints.userId], references: [users.id] }),
order: one(orders, { fields: [complaints.orderId], references: [orders.id] }),
}));
export const couponsRelations = relations(coupons, ({ one, many }) => ({
creator: one(staffUsers, { fields: [coupons.createdBy], references: [staffUsers.id] }),
usages: many(couponUsage),
applicableUsers: many(couponApplicableUsers),
applicableProducts: many(couponApplicableProducts),
}));
export const couponUsageRelations = relations(couponUsage, ({ one }) => ({
user: one(users, { fields: [couponUsage.userId], references: [users.id] }),
coupon: one(coupons, { fields: [couponUsage.couponId], references: [coupons.id] }),
order: one(orders, { fields: [couponUsage.orderId], references: [orders.id] }),
orderItem: one(orderItems, { fields: [couponUsage.orderItemId], references: [orderItems.id] }),
}));
export const userDetailsRelations = relations(userDetails, ({ one }) => ({
user: one(users, { fields: [userDetails.userId], references: [users.id] }),
}));
export const notifCredsRelations = relations(notifCreds, ({ one }) => ({
user: one(users, { fields: [notifCreds.userId], references: [users.id] }),
}));
export const userNotificationsRelations = relations(userNotifications, ({}) => ({
// No relations needed for now
}));
export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({
owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }),
products: many(productInfo),
}));
export const couponApplicableUsersRelations = relations(couponApplicableUsers, ({ one }) => ({
coupon: one(coupons, { fields: [couponApplicableUsers.couponId], references: [coupons.id] }),
user: one(users, { fields: [couponApplicableUsers.userId], references: [users.id] }),
}));
export const couponApplicableProductsRelations = relations(couponApplicableProducts, ({ one }) => ({
coupon: one(coupons, { fields: [couponApplicableProducts.couponId], references: [coupons.id] }),
product: one(productInfo, { fields: [couponApplicableProducts.productId], references: [productInfo.id] }),
}));
export const reservedCouponsRelations = relations(reservedCoupons, ({ one }) => ({
redeemedUser: one(users, { fields: [reservedCoupons.redeemedBy], references: [users.id] }),
creator: one(staffUsers, { fields: [reservedCoupons.createdBy], references: [staffUsers.id] }),
}));
export const productReviewsRelations = relations(productReviews, ({ one }) => ({
user: one(users, { fields: [productReviews.userId], references: [users.id] }),
product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }),
}));
export const addressZonesRelations = relations(addressZones, ({ many }) => ({
addresses: many(addresses),
areas: many(addressAreas),
}));
export const addressAreasRelations = relations(addressAreas, ({ one }) => ({
zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }),
}));
export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({
memberships: many(productGroupMembership),
}));
export const productGroupMembershipRelations = relations(productGroupMembership, ({ one }) => ({
product: one(productInfo, { fields: [productGroupMembership.productId], references: [productInfo.id] }),
group: one(productGroupInfo, { fields: [productGroupMembership.groupId], references: [productGroupInfo.id] }),
}));
export const homeBannersRelations = relations(homeBanners, ({}) => ({
// Relations for productIds array would be more complex, skipping for now
}));
export const staffRolesRelations = relations(staffRoles, ({ many }) => ({
staffUsers: many(staffUsers),
rolePermissions: many(staffRolePermissions),
}));
export const staffPermissionsRelations = relations(staffPermissions, ({ many }) => ({
rolePermissions: many(staffRolePermissions),
}));
export const staffRolePermissionsRelations = relations(staffRolePermissions, ({ one }) => ({
role: one(staffRoles, { fields: [staffRolePermissions.staffRoleId], references: [staffRoles.id] }),
permission: one(staffPermissions, { fields: [staffRolePermissions.staffPermissionId], references: [staffPermissions.id] }),
}));
export const userIncidentsRelations = relations(userIncidents, ({ one }) => ({
user: one(users, { fields: [userIncidents.userId], references: [users.id] }),
order: one(orders, { fields: [userIncidents.orderId], references: [orders.id] }),
addedBy: one(staffUsers, { fields: [userIncidents.addedBy], references: [staffUsers.id] }),
}));
export const productAvailabilitySchedulesRelations = relations(productAvailabilitySchedules, ({}) => ({
}));
export * from './schema-sqlite'

View file

@ -0,0 +1,34 @@
export const parseJsonValue = <T>(value: unknown, fallback: T): T => {
if (value === null || value === undefined) return fallback
if (typeof value === 'string') {
try {
return JSON.parse(value) as T
} catch {
return fallback
}
}
return value as T
}
export const parseNumberArray = (value: unknown): number[] => {
const parsed = parseJsonValue<unknown[]>(value, [])
return parsed
.map((item) => Number(item))
.filter((item) => !Number.isNaN(item))
}
export const toJsonString = (value: unknown, fallback: string): string => {
if (value === null || value === undefined) return fallback
if (typeof value === 'string') return value
return JSON.stringify(value)
}
export const toEpochSeconds = (value: Date | number): number => {
if (typeof value === 'number') return value
return Math.floor(value.getTime() / 1000)
}
export const fromEpochSeconds = (value: number | null | undefined): Date | null => {
if (value === null || value === undefined) return null
return new Date(value * 1000)
}

View file

@ -1,41 +1,55 @@
export type { IBannerDbService, Banner, NewBanner } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface'
export { bannerDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/banner-queries'
// export { bannerDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/banner-queries'
export { bannerDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/banner-queries'
export type { IComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface'
export { complaintDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/complaint-queries'
// export { complaintDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/complaint-queries'
export { complaintDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/complaint-queries'
export type { IConstantDbService, Constant, NewConstant } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface'
export { constantDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/constant-queries'
// export { constantDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/constant-queries'
export { constantDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/constant-queries'
export type { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface'
export { couponDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/coupon-queries'
// export { couponDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/coupon-queries'
export { couponDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/coupon-queries'
export type { IOrderDbService, Order, OrderItem, OrderStatus, OrderWithRelations, OrderWithStatus, OrderWithCouponUsages } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface'
export { orderDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/order-queries'
// export { orderDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/order-queries'
export { orderDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/order-queries'
export type { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface'
export { productDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/product-queries'
// export { productDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/product-queries'
export { productDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/product-queries'
export type { IRefundDbService, Refund, NewRefund } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface'
export { refundDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/refund-queries'
// export { refundDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/refund-queries'
export { refundDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/refund-queries'
export type { IScheduleDbService, Schedule, NewSchedule } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface'
export { scheduleDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/schedule-queries'
// export { scheduleDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/schedule-queries'
export { scheduleDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/schedule-queries'
export type { ISlotDbService, Slot, NewSlot, ProductSlot, NewProductSlot, SlotWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface'
export { slotDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/slot-queries'
// export { slotDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/slot-queries'
export { slotDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/slot-queries'
export type { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole, StaffUserWithRole } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface'
export { staffUserDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/staff-user-queries'
// export { staffUserDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/staff-user-queries'
export { staffUserDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/staff-user-queries'
export type { IStoreDbService, Store, NewStore } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface'
export { storeDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/store-queries'
// export { storeDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/store-queries'
export { storeDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/store-queries'
export type { ITagDbService, Tag, NewTag } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface'
export { tagDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/tag-queries'
// export { tagDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/tag-queries'
export { tagDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/tag-queries'
export type { IUserDbService, User, NewUser, UserDetail } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface'
export { userDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/user-queries'
// export { userDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/user-queries'
export { userDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/user-queries'
export type { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface'
export { vendorSnippetDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/vendor-snippets-queries'
// export { vendorSnippetDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/postgres/vendor-snippets-queries'
export { vendorSnippetDbService } from '@/src/trpc/apis/admin-apis/dataAccessors/sqlite/vendor-snippets-queries'

View file

@ -0,0 +1,48 @@
import { db } from '@/src/db/db_index_sqlite'
import { homeBanners } from '@/src/db/schema'
import { eq, desc } from 'drizzle-orm'
import { IBannerDbService, Banner, NewBanner } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/banner-db-service.interface'
import { toJsonString } from '@/src/db/sqlite-casts'
export class BannerDbService implements IBannerDbService {
async getAllBanners(): Promise<Banner[]> {
return db.query.homeBanners.findMany({
orderBy: desc(homeBanners.createdAt),
})
}
async getBannerById(id: number): Promise<Banner | undefined> {
return db.query.homeBanners.findFirst({
where: eq(homeBanners.id, id),
})
}
async createBanner(data: NewBanner): Promise<Banner> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
}
const [banner] = await db.insert(homeBanners).values(normalized).returning()
return banner
}
async updateBannerById(id: number, data: Partial<NewBanner>): Promise<Banner> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
lastUpdated: new Date(),
}
const [banner] = await db
.update(homeBanners)
.set(normalized)
.where(eq(homeBanners.id, id))
.returning()
return banner
}
async deleteBannerById(id: number): Promise<void> {
await db.delete(homeBanners).where(eq(homeBanners.id, id))
}
}
export const bannerDbService: IBannerDbService = new BannerDbService()

View file

@ -0,0 +1,43 @@
import { db } from '@/src/db/db_index_sqlite'
import { complaints, users } from '@/src/db/schema'
import { eq, desc, lt } from 'drizzle-orm'
import { IComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/complaint-db-service.interface'
export class ComplaintDbService implements IComplaintDbService {
async getComplaints(
cursor?: number,
limit: number = 20
): Promise<Array<Complaint & { userName?: string | null; userMobile?: string | null }>> {
let whereCondition = cursor ? lt(complaints.id, cursor) : undefined
const complaintsData = await db
.select({
id: complaints.id,
complaintBody: complaints.complaintBody,
userId: complaints.userId,
orderId: complaints.orderId,
isResolved: complaints.isResolved,
createdAt: complaints.createdAt,
response: complaints.response,
images: complaints.images,
userName: users.name,
userMobile: users.mobile,
})
.from(complaints)
.leftJoin(users, eq(complaints.userId, users.id))
.where(whereCondition)
.orderBy(desc(complaints.id))
.limit(limit + 1)
return complaintsData
}
async resolveComplaint(id: number, response?: string): Promise<void> {
await db
.update(complaints)
.set({ isResolved: true, response })
.where(eq(complaints.id, id))
}
}
export const complaintDbService: IComplaintDbService = new ComplaintDbService()

View file

@ -0,0 +1,26 @@
import { db } from '@/src/db/db_index_sqlite'
import { keyValStore } from '@/src/db/schema'
import { IConstantDbService, Constant, NewConstant } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/constant-db-service.interface'
import { toJsonString } from '@/src/db/sqlite-casts'
export class ConstantDbService implements IConstantDbService {
async getAllConstants(): Promise<Constant[]> {
return db.select().from(keyValStore)
}
async upsertConstants(constants: { key: string; value: any }[]): Promise<number> {
await db.transaction(async (tx) => {
for (const { key, value } of constants) {
await tx.insert(keyValStore)
.values({ key, value: toJsonString(value, 'null') })
.onConflictDoUpdate({
target: keyValStore.key,
set: { value: toJsonString(value, 'null') },
})
}
})
return constants.length
}
}
export const constantDbService: IConstantDbService = new ConstantDbService()

View file

@ -0,0 +1,204 @@
import { db } from '@/src/db/db_index_sqlite'
import { coupons, couponApplicableUsers, couponApplicableProducts, reservedCoupons, users, orders, orderStatus } from '@/src/db/schema'
import { eq, and, like, or, inArray, lt, asc } from 'drizzle-orm'
import { ICouponDbService, Coupon, NewCoupon, ReservedCoupon, NewReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/coupon-db-service.interface'
import { parseNumberArray, toJsonString } from '@/src/db/sqlite-casts'
export class CouponDbService implements ICouponDbService {
async createCoupon(data: NewCoupon): Promise<Coupon> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
}
const [coupon] = await db.insert(coupons).values(normalized).returning()
return coupon
}
async getCouponById(id: number): Promise<CouponWithRelations | undefined> {
const result = await db.query.coupons.findFirst({
where: eq(coupons.id, id),
with: {
creator: true,
applicableUsers: { with: { user: true } },
applicableProducts: { with: { product: true } },
},
})
if (!result) return undefined
return {
...result,
productIds: parseNumberArray(result.productIds),
} as CouponWithRelations
}
async getCouponByCode(code: string): Promise<Coupon | undefined> {
return db.query.coupons.findFirst({
where: eq(coupons.couponCode, code),
})
}
async getAllCoupons(options: { cursor?: number; limit: number; search?: string }): Promise<CouponWithRelations[]> {
const { cursor, limit, search } = options
let whereCondition = undefined
const conditions = []
if (cursor) {
conditions.push(lt(coupons.id, cursor))
}
if (search && search.trim()) {
conditions.push(like(coupons.couponCode, `%${search}%`))
}
if (conditions.length > 0) {
whereCondition = and(...conditions)
}
const result = await db.query.coupons.findMany({
where: whereCondition,
with: {
creator: true,
applicableUsers: { with: { user: true } },
applicableProducts: { with: { product: true } },
},
orderBy: (couponsRef, { desc }) => [desc(couponsRef.createdAt)],
limit: limit + 1,
})
return result.map((coupon) => ({
...coupon,
productIds: parseNumberArray(coupon.productIds),
})) as CouponWithRelations[]
}
async updateCoupon(id: number, data: Partial<NewCoupon>): Promise<Coupon> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
}
const [coupon] = await db.update(coupons).set(normalized).where(eq(coupons.id, id)).returning()
return coupon
}
async invalidateCoupon(id: number): Promise<Coupon> {
const [coupon] = await db.update(coupons).set({ isInvalidated: true }).where(eq(coupons.id, id)).returning()
return coupon
}
async addApplicableUsers(couponId: number, userIds: number[]): Promise<void> {
await db.insert(couponApplicableUsers).values(
userIds.map(userId => ({ couponId, userId }))
)
}
async addApplicableProducts(couponId: number, productIds: number[]): Promise<void> {
await db.insert(couponApplicableProducts).values(
productIds.map(productId => ({ couponId, productId }))
)
}
async removeAllApplicableUsers(couponId: number): Promise<void> {
await db.delete(couponApplicableUsers).where(eq(couponApplicableUsers.couponId, couponId))
}
async removeAllApplicableProducts(couponId: number): Promise<void> {
await db.delete(couponApplicableProducts).where(eq(couponApplicableProducts.couponId, couponId))
}
async countApplicableUsers(couponId: number): Promise<number> {
return db.$count(couponApplicableUsers, eq(couponApplicableUsers.couponId, couponId))
}
async createReservedCoupon(data: NewReservedCoupon): Promise<ReservedCoupon> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
}
const [coupon] = await db.insert(reservedCoupons).values(normalized).returning()
return coupon
}
async getReservedCoupons(options: { cursor?: number; limit: number; search?: string }): Promise<ReservedCoupon[]> {
const { cursor, limit, search } = options
let whereCondition = undefined
const conditions = []
if (cursor) {
conditions.push(lt(reservedCoupons.id, cursor))
}
if (search && search.trim()) {
conditions.push(or(
like(reservedCoupons.secretCode, `%${search}%`),
like(reservedCoupons.couponCode, `%${search}%`)
))
}
if (conditions.length > 0) {
whereCondition = and(...conditions)
}
return db.query.reservedCoupons.findMany({
where: whereCondition,
with: { redeemedUser: true, creator: true },
orderBy: (reservedCouponsRef, { desc }) => [desc(reservedCouponsRef.createdAt)],
limit: limit + 1,
})
}
async getUsersByIds(ids: number[]): Promise<Array<{ id: number; name: string | null; mobile: string | null }>> {
return db.query.users.findMany({
where: inArray(users.id, ids),
columns: { id: true, name: true, mobile: true },
})
}
async getUsersBySearch(search: string, limit: number, offset: number): Promise<Array<{ id: number; name: string | null; mobile: string | null }>> {
const whereCondition = or(
like(users.name, `%${search}%`),
like(users.mobile, `%${search}%`)
)
return db.query.users.findMany({
where: whereCondition,
columns: { id: true, name: true, mobile: true },
limit,
offset,
orderBy: (usersRef, { asc }) => [asc(usersRef.name)],
})
}
async createUser(data: Partial<typeof users.$inferInsert>): Promise<typeof users.$inferSelect> {
const [user] = await db.insert(users).values(data).returning()
return user
}
async getUserByMobile(mobile: string): Promise<typeof users.$inferSelect | undefined> {
return db.query.users.findFirst({
where: eq(users.mobile, mobile),
})
}
async getOrderByIdWithUserAndStatus(id: number): Promise<typeof orders.$inferSelect & { user?: typeof users.$inferSelect; orderStatus?: any[] } | undefined> {
return db.query.orders.findFirst({
where: eq(orders.id, id),
with: {
user: true,
orderStatus: true,
},
})
}
async updateOrderStatusRefundCoupon(orderId: number, couponId: number): Promise<void> {
await db.update(orderStatus)
.set({ refundCouponId: couponId })
.where(eq(orderStatus.orderId, orderId))
}
async withTransaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
return db.transaction(fn)
}
}
export const couponDbService: ICouponDbService = new CouponDbService()

View file

@ -0,0 +1,332 @@
import { db } from '@/src/db/db_index_sqlite'
import {
orders,
orderItems,
orderStatus,
users,
addresses,
refunds,
coupons,
couponUsage,
complaints,
payments,
deliverySlotInfo,
productInfo,
units,
paymentInfoTable,
} from '@/src/db/schema'
import { eq, and, gte, lt, desc, inArray, SQL } from 'drizzle-orm'
import {
IOrderDbService,
Order,
OrderItem,
OrderStatus,
Address,
Refund,
OrderWithRelations,
OrderWithStatus,
OrderWithCouponUsages,
} from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/order-db-service.interface'
export class OrderDbService implements IOrderDbService {
async updateOrderNotes(orderId: number, adminNotes: string | null): Promise<Order> {
const [updated] = await db
.update(orders)
.set({ adminNotes })
.where(eq(orders.id, orderId))
.returning()
return updated
}
async removeDeliveryCharge(orderId: number, totalAmount: string): Promise<void> {
await db
.update(orders)
.set({ deliveryCharge: '0', totalAmount })
.where(eq(orders.id, orderId))
}
async getOrderById(orderId: number): Promise<Order | undefined> {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
})
}
async getOrderWithRelations(orderId: number): Promise<OrderWithRelations | undefined> {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
user: true,
address: true,
slot: true,
orderItems: {
with: {
product: {
with: { unit: true },
},
},
},
payment: true,
paymentInfo: true,
},
}) as Promise<OrderWithRelations | undefined>
}
async getOrderWithDetails(orderId: number): Promise<OrderWithRelations | undefined> {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
user: true,
address: true,
slot: true,
orderItems: {
with: {
product: {
with: { unit: true },
},
},
},
payment: true,
paymentInfo: true,
orderStatus: true,
refunds: true,
},
}) as Promise<OrderWithRelations | undefined>
}
async getOrderWithStatus(orderId: number): Promise<OrderWithStatus | undefined> {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
orderStatus: true,
},
}) as Promise<OrderWithStatus | undefined>
}
async getOrderStatusByOrderId(orderId: number): Promise<OrderStatus | undefined> {
return db.query.orderStatus.findFirst({
where: eq(orderStatus.orderId, orderId),
})
}
async updateOrderStatusPackaged(orderId: number, isPackaged: boolean, isDelivered: boolean): Promise<void> {
await db
.update(orderStatus)
.set({ isPackaged, isDelivered })
.where(eq(orderStatus.orderId, orderId))
}
async updateOrderStatusDelivered(orderId: number, isDelivered: boolean): Promise<void> {
await db
.update(orderStatus)
.set({ isDelivered })
.where(eq(orderStatus.orderId, orderId))
}
async cancelOrderStatus(statusId: number, reason: string): Promise<void> {
await db
.update(orderStatus)
.set({
isCancelled: true,
isCancelledByAdmin: true,
cancelReason: reason,
cancellationAdminNotes: reason,
cancellationReviewed: true,
cancellationReviewedAt: new Date(),
})
.where(eq(orderStatus.id, statusId))
}
async getRefundByOrderId(orderId: number): Promise<Refund | undefined> {
return db.query.refunds.findFirst({
where: eq(refunds.orderId, orderId),
})
}
async createRefund(orderId: number, refundStatus: string): Promise<void> {
await db.insert(refunds).values({ orderId, refundStatus })
}
async getCouponUsageByOrderId(orderId: number): Promise<Array<typeof couponUsage.$inferSelect & { coupon: typeof coupons.$inferSelect }>> {
return db.query.couponUsage.findMany({
where: eq(couponUsage.orderId, orderId),
with: { coupon: true },
})
}
async getOrderItemById(orderItemId: number): Promise<OrderItem | undefined> {
return db.query.orderItems.findFirst({
where: eq(orderItems.id, orderItemId),
})
}
async updateOrderItem(orderItemId: number, data: Partial<OrderItem>): Promise<void> {
await db
.update(orderItems)
.set(data)
.where(eq(orderItems.id, orderItemId))
}
async updateOrderItemsPackaged(orderId: number, isPackaged: boolean): Promise<void> {
await db
.update(orderItems)
.set({ is_packaged: isPackaged })
.where(eq(orderItems.orderId, orderId))
}
async updateAddressCoords(addressId: number, latitude: number, longitude: number): Promise<Address> {
const [updated] = await db
.update(addresses)
.set({ adminLatitude: latitude, adminLongitude: longitude })
.where(eq(addresses.id, addressId))
.returning()
return updated
}
async getOrdersBySlotId(slotId: number): Promise<OrderWithRelations[]> {
return db.query.orders.findMany({
where: eq(orders.slotId, slotId),
with: {
user: true,
address: true,
slot: true,
orderItems: {
with: {
product: { with: { unit: true } },
},
},
orderStatus: true,
},
}) as Promise<OrderWithRelations[]>
}
async getOrdersBySlotIds(slotIds: number[]): Promise<OrderWithCouponUsages[]> {
return db.query.orders.findMany({
where: inArray(orders.slotId, slotIds),
with: {
orderItems: { with: { product: true } },
couponUsages: { with: { coupon: true } },
},
}) as Promise<OrderWithCouponUsages[]>
}
async getOrdersByDateRange(start: Date, end: Date, slotId?: number): Promise<OrderWithRelations[]> {
const conditions: Array<SQL> = [
gte(orders.createdAt, start),
lt(orders.createdAt, end),
]
if (slotId !== undefined) {
conditions.push(eq(orders.slotId, slotId))
}
return db.query.orders.findMany({
where: and(...conditions),
with: {
user: true,
address: true,
slot: true,
orderItems: {
with: {
product: { with: { unit: true } },
},
},
orderStatus: true,
},
}) as Promise<OrderWithRelations[]>
}
async getAllOrdersWithFilters(options: {
cursor?: number
limit: number
slotId?: number | null
packagedFilter: 'all' | 'packaged' | 'not_packaged'
deliveredFilter: 'all' | 'delivered' | 'not_delivered'
cancellationFilter: 'all' | 'cancelled' | 'not_cancelled'
flashDeliveryFilter: 'all' | 'flash' | 'regular'
}): Promise<OrderWithRelations[]> {
const { cursor, limit, slotId, packagedFilter, deliveredFilter, cancellationFilter, flashDeliveryFilter } = options
const conditions: Array<SQL> = []
if (cursor) {
conditions.push(lt(orders.id, cursor))
}
if (slotId !== undefined && slotId !== null) {
conditions.push(eq(orders.slotId, slotId))
}
if (packagedFilter === 'packaged') {
conditions.push(eq(orderStatus.isPackaged, true))
} else if (packagedFilter === 'not_packaged') {
conditions.push(eq(orderStatus.isPackaged, false))
}
if (deliveredFilter === 'delivered') {
conditions.push(eq(orderStatus.isDelivered, true))
} else if (deliveredFilter === 'not_delivered') {
conditions.push(eq(orderStatus.isDelivered, false))
}
if (cancellationFilter === 'cancelled') {
conditions.push(eq(orderStatus.isCancelled, true))
} else if (cancellationFilter === 'not_cancelled') {
conditions.push(eq(orderStatus.isCancelled, false))
}
if (flashDeliveryFilter === 'flash') {
conditions.push(eq(orders.isFlashDelivery, true))
} else if (flashDeliveryFilter === 'regular') {
conditions.push(eq(orders.isFlashDelivery, false))
}
const whereCondition = conditions.length > 0 ? and(...conditions) : undefined
return db.query.orders.findMany({
where: whereCondition,
with: {
user: true,
address: true,
slot: true,
orderItems: {
with: {
product: { with: { unit: true } },
},
},
orderStatus: true,
},
orderBy: (ordersRef, { desc }) => [desc(ordersRef.createdAt)],
limit: limit + 1,
}) as Promise<OrderWithRelations[]>
}
async updateOrdersAndItemsInTransaction(data: Array<{ orderId: number; totalAmount: string; items: Array<{ id: number; price: string; discountedPrice: string }> }>): Promise<void> {
await db.transaction(async (tx) => {
for (const order of data) {
await tx.update(orders)
.set({ totalAmount: order.totalAmount })
.where(eq(orders.id, order.orderId))
for (const item of order.items) {
await tx.update(orderItems)
.set({ price: item.price, discountedPrice: item.discountedPrice })
.where(eq(orderItems.id, item.id))
}
}
})
}
async deleteOrderById(orderId: number): Promise<void> {
await db.transaction(async (tx) => {
await tx.delete(couponUsage).where(eq(couponUsage.orderId, orderId))
await tx.delete(orderStatus).where(eq(orderStatus.orderId, orderId))
await tx.delete(orderItems).where(eq(orderItems.orderId, orderId))
await tx.delete(refunds).where(eq(refunds.orderId, orderId))
await tx.delete(payments).where(eq(payments.orderId, orderId))
await tx.delete(complaints).where(eq(complaints.orderId, orderId))
await tx.delete(orders).where(eq(orders.id, orderId))
})
}
}
export const orderDbService: IOrderDbService = new OrderDbService()

View file

@ -0,0 +1,244 @@
import { db } from '@/src/db/db_index_sqlite'
import { productInfo, units, specialDeals, productSlots, productTags, productReviews, productGroupInfo, productGroupMembership, users } from '@/src/db/schema'
import { eq, and, inArray, desc, sql } from 'drizzle-orm'
import { IProductDbService, Product, NewProduct, ProductGroup, NewProductGroup } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/product-db-service.interface'
import { toJsonString } from '@/src/db/sqlite-casts'
export class ProductDbService implements IProductDbService {
async getAllProducts(): Promise<Product[]> {
return db.query.productInfo.findMany({
orderBy: productInfo.name,
with: {
unit: true,
store: true,
},
})
}
async getProductById(id: number): Promise<Product | undefined> {
return db.query.productInfo.findFirst({
where: eq(productInfo.id, id),
with: {
unit: true,
},
})
}
async createProduct(data: NewProduct): Promise<Product> {
const normalized = {
...data,
images: data.images ? toJsonString(data.images, '[]') : data.images,
}
const [product] = await db.insert(productInfo).values(normalized).returning()
return product
}
async updateProduct(id: number, data: Partial<NewProduct>): Promise<Product> {
const normalized = {
...data,
images: data.images ? toJsonString(data.images, '[]') : data.images,
}
const [product] = await db
.update(productInfo)
.set(normalized)
.where(eq(productInfo.id, id))
.returning()
return product
}
async deleteProduct(id: number): Promise<Product> {
const [product] = await db
.delete(productInfo)
.where(eq(productInfo.id, id))
.returning()
return product
}
async getDealsByProductId(productId: number): Promise<typeof specialDeals.$inferSelect[]> {
return db.query.specialDeals.findMany({
where: eq(specialDeals.productId, productId),
orderBy: specialDeals.quantity,
})
}
async createDeals(deals: Partial<typeof specialDeals.$inferInsert>[]): Promise<void> {
if (deals.length > 0) {
await db.insert(specialDeals).values(deals as any)
}
}
async deleteDealsByProductId(productId: number): Promise<void> {
await db.delete(specialDeals).where(eq(specialDeals.productId, productId))
}
async getTagsByProductId(productId: number): Promise<Array<{ tag: { id: number; tagName: string; tagDescription: string | null; imageUrl: string | null; isDashboardTag: boolean; relatedStores: any } }>> {
return db.query.productTags.findMany({
where: eq(productTags.productId, productId),
with: {
tag: true,
},
}) as any
}
async createTagAssociations(associations: { productId: number; tagId: number }[]): Promise<void> {
if (associations.length > 0) {
await db.insert(productTags).values(associations)
}
}
async deleteTagAssociationsByProductId(productId: number): Promise<void> {
await db.delete(productTags).where(eq(productTags.productId, productId))
}
async getProductSlotsBySlotId(slotId: number): Promise<typeof productSlots.$inferSelect[]> {
return db.query.productSlots.findMany({
where: eq(productSlots.slotId, slotId),
})
}
async getProductSlotsBySlotIds(slotIds: number[]): Promise<typeof productSlots.$inferSelect[]> {
return db.query.productSlots.findMany({
where: inArray(productSlots.slotId, slotIds),
columns: { slotId: true, productId: true },
})
}
async createProductSlot(slotId: number, productId: number): Promise<void> {
await db.insert(productSlots).values({ slotId, productId })
}
async deleteProductSlotsBySlotId(slotId: number): Promise<void> {
await db.delete(productSlots).where(eq(productSlots.slotId, slotId))
}
async deleteProductSlot(slotId: number, productId: number): Promise<void> {
await db
.delete(productSlots)
.where(and(eq(productSlots.slotId, slotId), eq(productSlots.productId, productId)))
}
async getReviewsByProductId(productId: number, limit: number, offset: number): Promise<(typeof productReviews.$inferSelect & { userName: string | null })[]> {
const reviews = await db
.select({
id: productReviews.id,
reviewBody: productReviews.reviewBody,
ratings: productReviews.ratings,
imageUrls: productReviews.imageUrls,
reviewTime: productReviews.reviewTime,
adminResponse: productReviews.adminResponse,
adminResponseImages: productReviews.adminResponseImages,
userName: users.name,
})
.from(productReviews)
.innerJoin(users, eq(productReviews.userId, users.id))
.where(eq(productReviews.productId, productId))
.orderBy(desc(productReviews.reviewTime))
.limit(limit)
.offset(offset)
return reviews as any
}
async getReviewCountByProductId(productId: number): Promise<number> {
const result = await db
.select({ count: sql`count(*)` })
.from(productReviews)
.where(eq(productReviews.productId, productId))
return Number(result[0].count)
}
async updateReview(reviewId: number, data: Partial<typeof productReviews.$inferInsert>): Promise<typeof productReviews.$inferSelect> {
const normalized = {
...data,
imageUrls: data.imageUrls ? toJsonString(data.imageUrls, '[]') : data.imageUrls,
adminResponseImages: data.adminResponseImages ? toJsonString(data.adminResponseImages, '[]') : data.adminResponseImages,
}
const [review] = await db
.update(productReviews)
.set(normalized)
.where(eq(productReviews.id, reviewId))
.returning()
return review
}
async getAllGroups(): Promise<ProductGroup[]> {
return db.query.productGroupInfo.findMany({
with: {
memberships: {
with: {
product: true,
},
},
},
orderBy: desc(productGroupInfo.createdAt),
})
}
async getGroupById(id: number): Promise<ProductGroup | undefined> {
return db.query.productGroupInfo.findFirst({
where: eq(productGroupInfo.id, id),
})
}
async createGroup(data: NewProductGroup): Promise<ProductGroup> {
const [group] = await db.insert(productGroupInfo).values(data).returning()
return group
}
async updateGroup(id: number, data: Partial<NewProductGroup>): Promise<ProductGroup> {
const [group] = await db
.update(productGroupInfo)
.set(data)
.where(eq(productGroupInfo.id, id))
.returning()
return group
}
async deleteGroup(id: number): Promise<ProductGroup> {
const [group] = await db
.delete(productGroupInfo)
.where(eq(productGroupInfo.id, id))
.returning()
return group
}
async deleteGroupMembershipsByGroupId(groupId: number): Promise<void> {
await db.delete(productGroupMembership).where(eq(productGroupMembership.groupId, groupId))
}
async createGroupMemberships(memberships: { productId: number; groupId: number }[]): Promise<void> {
if (memberships.length > 0) {
await db.insert(productGroupMembership).values(memberships)
}
}
async getUnitById(id: number): Promise<typeof units.$inferSelect | undefined> {
return db.query.units.findFirst({
where: eq(units.id, id),
})
}
async validateProductIdsExist(productIds: number[]): Promise<boolean> {
const products = await db.query.productInfo.findMany({
where: inArray(productInfo.id, productIds),
columns: { id: true },
})
return products.length === productIds.length
}
async batchUpdateProducts(updates: { productId: number; data: Partial<NewProduct> }[]): Promise<void> {
const promises = updates.map(update => {
const normalized = {
...update.data,
images: update.data.images ? toJsonString(update.data.images, '[]') : update.data.images,
}
return db
.update(productInfo)
.set(normalized)
.where(eq(productInfo.id, update.productId))
})
await Promise.all(promises)
}
}
export const productDbService: IProductDbService = new ProductDbService()

View file

@ -0,0 +1,49 @@
import { db } from '@/src/db/db_index_sqlite'
import { refunds, orders, orderStatus, payments } from '@/src/db/schema'
import { eq, and } from 'drizzle-orm'
import { IRefundDbService, Refund, NewRefund } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/refund-db-service.interface'
export class RefundDbService implements IRefundDbService {
async createRefund(data: NewRefund): Promise<Refund> {
const [refund] = await db.insert(refunds).values(data).returning()
return refund
}
async updateRefund(id: number, data: Partial<NewRefund>): Promise<Refund> {
const [refund] = await db
.update(refunds)
.set(data)
.where(eq(refunds.id, id))
.returning()
return refund
}
async getRefundByOrderId(orderId: number): Promise<Refund | undefined> {
return db.query.refunds.findFirst({
where: eq(refunds.orderId, orderId),
})
}
async getOrderById(id: number): Promise<typeof orders.$inferSelect | undefined> {
return db.query.orders.findFirst({
where: eq(orders.id, id),
})
}
async getOrderStatusByOrderId(orderId: number): Promise<typeof orderStatus.$inferSelect | undefined> {
return db.query.orderStatus.findFirst({
where: eq(orderStatus.orderId, orderId),
})
}
async getSuccessfulPaymentByOrderId(orderId: number): Promise<typeof payments.$inferSelect | undefined> {
return db.query.payments.findFirst({
where: and(
eq(payments.orderId, orderId),
eq(payments.status, 'success')
),
})
}
}
export const refundDbService: IRefundDbService = new RefundDbService()

View file

@ -0,0 +1,60 @@
import { db } from '@/src/db/db_index_sqlite'
import { productAvailabilitySchedules } from '@/src/db/schema'
import { eq, desc } from 'drizzle-orm'
import { IScheduleDbService, Schedule, NewSchedule } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/schedule-db-service.interface'
import { toJsonString } from '@/src/db/sqlite-casts'
export class ScheduleDbService implements IScheduleDbService {
async createSchedule(data: NewSchedule): Promise<Schedule> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
groupIds: data.groupIds ? toJsonString(data.groupIds, '[]') : data.groupIds,
}
const [schedule] = await db.insert(productAvailabilitySchedules).values(normalized).returning()
return schedule
}
async getAllSchedules(): Promise<Schedule[]> {
return db.query.productAvailabilitySchedules.findMany({
orderBy: desc(productAvailabilitySchedules.createdAt),
})
}
async getScheduleById(id: number): Promise<Schedule | undefined> {
return db.query.productAvailabilitySchedules.findFirst({
where: eq(productAvailabilitySchedules.id, id),
})
}
async getScheduleByName(name: string): Promise<Schedule | undefined> {
return db.query.productAvailabilitySchedules.findFirst({
where: eq(productAvailabilitySchedules.scheduleName, name),
})
}
async updateSchedule(id: number, data: Partial<NewSchedule>): Promise<Schedule> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
groupIds: data.groupIds ? toJsonString(data.groupIds, '[]') : data.groupIds,
lastUpdated: new Date(),
}
const [schedule] = await db
.update(productAvailabilitySchedules)
.set(normalized)
.where(eq(productAvailabilitySchedules.id, id))
.returning()
return schedule
}
async deleteSchedule(id: number): Promise<Schedule> {
const [schedule] = await db
.delete(productAvailabilitySchedules)
.where(eq(productAvailabilitySchedules.id, id))
.returning()
return schedule
}
}
export const scheduleDbService: IScheduleDbService = new ScheduleDbService()

View file

@ -0,0 +1,161 @@
import { db } from '@/src/db/db_index_sqlite'
import { deliverySlotInfo, productSlots, vendorSnippets, productInfo, productGroupInfo } from '@/src/db/schema'
import { eq, inArray, and, desc } from 'drizzle-orm'
import { ISlotDbService, Slot, NewSlot, ProductSlot, SlotWithRelations } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/slot-db-service.interface'
import { parseNumberArray, toJsonString } from '@/src/db/sqlite-casts'
export class SlotDbService implements ISlotDbService {
async getAllSlots(): Promise<SlotWithRelations[]> {
return db.query.deliverySlotInfo.findMany({
orderBy: desc(deliverySlotInfo.deliveryTime),
with: {
productSlots: {
with: {
product: {
columns: { id: true, name: true, images: true },
},
},
},
},
}) as Promise<SlotWithRelations[]>
}
async getActiveSlots(): Promise<Slot[]> {
return db.query.deliverySlotInfo.findMany({
where: eq(deliverySlotInfo.isActive, true),
orderBy: desc(deliverySlotInfo.deliveryTime),
})
}
async getSlotById(id: number): Promise<SlotWithRelations | undefined> {
return db.query.deliverySlotInfo.findFirst({
where: eq(deliverySlotInfo.id, id),
with: {
productSlots: {
with: {
product: {
columns: { id: true, name: true, images: true },
},
},
},
vendorSnippets: true,
},
}) as Promise<SlotWithRelations | undefined>
}
async createSlot(data: NewSlot): Promise<Slot> {
const normalized = {
...data,
deliverySequence: data.deliverySequence ? toJsonString(data.deliverySequence, '{}') : data.deliverySequence,
groupIds: data.groupIds ? toJsonString(data.groupIds, '[]') : data.groupIds,
}
const [slot] = await db.insert(deliverySlotInfo).values(normalized).returning()
return slot
}
async updateSlot(id: number, data: Partial<NewSlot>): Promise<Slot> {
const normalized = {
...data,
deliverySequence: data.deliverySequence ? toJsonString(data.deliverySequence, '{}') : data.deliverySequence,
groupIds: data.groupIds ? toJsonString(data.groupIds, '[]') : data.groupIds,
}
const [slot] = await db
.update(deliverySlotInfo)
.set(normalized)
.where(eq(deliverySlotInfo.id, id))
.returning()
return slot
}
async deactivateSlot(id: number): Promise<Slot> {
const [slot] = await db
.update(deliverySlotInfo)
.set({ isActive: false })
.where(eq(deliverySlotInfo.id, id))
.returning()
return slot
}
async getProductSlotsBySlotId(slotId: number): Promise<ProductSlot[]> {
return db.query.productSlots.findMany({
where: eq(productSlots.slotId, slotId),
})
}
async getProductSlotsBySlotIds(slotIds: number[]): Promise<ProductSlot[]> {
return db.query.productSlots.findMany({
where: inArray(productSlots.slotId, slotIds),
columns: { slotId: true, productId: true },
})
}
async createProductSlot(slotId: number, productId: number): Promise<void> {
await db.insert(productSlots).values({ slotId, productId })
}
async deleteProductSlot(slotId: number, productId: number): Promise<void> {
await db
.delete(productSlots)
.where(and(eq(productSlots.slotId, slotId), eq(productSlots.productId, productId)))
}
async deleteProductSlotsBySlotId(slotId: number): Promise<void> {
await db.delete(productSlots).where(eq(productSlots.slotId, slotId))
}
async getVendorSnippetsBySlotId(slotId: number): Promise<Array<{ id: number; snippetCode: string; slotId: number | null; productIds: number[]; validTill: Date | null; createdAt: Date; isPermanent: boolean | null }>> {
const snippets = await db.query.vendorSnippets.findMany({
where: eq(vendorSnippets.slotId, slotId),
})
return snippets.map((snippet) => ({
...snippet,
productIds: parseNumberArray(snippet.productIds),
})) as Array<{ id: number; snippetCode: string; slotId: number | null; productIds: number[]; validTill: Date | null; createdAt: Date; isPermanent: boolean | null }>
}
async createVendorSnippet(data: { snippetCode: string; slotId: number; productIds: number[]; validTill?: Date }): Promise<{ id: number; snippetCode: string; slotId: number | null; productIds: number[]; validTill: Date | null; createdAt: Date; isPermanent: boolean | null }> {
const [snippet] = await db.insert(vendorSnippets).values({
snippetCode: data.snippetCode,
slotId: data.slotId,
productIds: toJsonString(data.productIds, '[]'),
validTill: data.validTill || null,
}).returning()
return {
...snippet,
productIds: parseNumberArray(snippet.productIds),
}
}
async checkSnippetCodeExists(code: string): Promise<boolean> {
const existing = await db.query.vendorSnippets.findFirst({
where: eq(vendorSnippets.snippetCode, code),
})
return !!existing
}
async validateProductsExist(productIds: number[]): Promise<boolean> {
const products = await db.query.productInfo.findMany({
where: inArray(productInfo.id, productIds),
})
return products.length === productIds.length
}
async getProductsByIds(productIds: number[]): Promise<typeof productInfo.$inferSelect[]> {
return db.query.productInfo.findMany({
where: inArray(productInfo.id, productIds),
})
}
async getGroupsByIds(groupIds: number[]): Promise<Array<{ id: number; groupName: string; description: string | null; createdAt: Date }>> {
return db.query.productGroupInfo.findMany({
where: inArray(productGroupInfo.id, groupIds),
})
}
async withTransaction<T>(fn: (tx: any) => Promise<T>): Promise<T> {
return db.transaction(fn)
}
}
export const slotDbService: ISlotDbService = new SlotDbService()

View file

@ -0,0 +1,104 @@
import { db } from '@/src/db/db_index_sqlite'
import { staffUsers, staffRoles, users, userDetails, orders } from '@/src/db/schema'
import { eq, or, like, and, lt, desc } from 'drizzle-orm'
import { IStaffUserDbService, StaffUser, NewStaffUser, StaffRole, StaffUserWithRole } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/staff-user-db-service.interface'
export class StaffUserDbService implements IStaffUserDbService {
async getStaffUserByName(name: string): Promise<StaffUser | undefined> {
return db.query.staffUsers.findFirst({
where: eq(staffUsers.name, name),
})
}
async getAllStaff(): Promise<StaffUserWithRole[]> {
return db.query.staffUsers.findMany({
columns: { id: true, name: true },
with: {
role: {
with: {
rolePermissions: {
with: { permission: true },
},
},
},
},
})
}
async createStaffUser(data: NewStaffUser): Promise<StaffUser> {
const [user] = await db.insert(staffUsers).values(data).returning()
return user
}
async getRoleById(id: number): Promise<StaffRole | undefined> {
return db.query.staffRoles.findFirst({
where: eq(staffRoles.id, id),
})
}
async getAllRoles(): Promise<Array<{ id: number; roleName: string }>> {
return db.query.staffRoles.findMany({
columns: { id: true, roleName: true },
})
}
async getUsers(options: { cursor?: number; limit: number; search?: string }): Promise<typeof users.$inferSelect[]> {
const { cursor, limit, search } = options
let whereCondition = undefined
if (search) {
whereCondition = or(
like(users.name, `%${search}%`),
like(users.email, `%${search}%`),
like(users.mobile, `%${search}%`)
)
}
if (cursor) {
const cursorCondition = lt(users.id, cursor)
whereCondition = whereCondition ? and(whereCondition, cursorCondition) : cursorCondition
}
return db.query.users.findMany({
where: whereCondition,
with: { userDetails: true },
orderBy: desc(users.id),
limit: limit + 1,
})
}
async getUserById(id: number): Promise<typeof users.$inferSelect | undefined> {
return db.query.users.findFirst({
where: eq(users.id, id),
with: {
userDetails: true,
orders: {
orderBy: desc(orders.createdAt),
limit: 1,
},
},
})
}
async upsertUserDetails(data: Partial<typeof userDetails.$inferInsert> & { userId: number }): Promise<void> {
await db
.insert(userDetails)
.values(data)
.onConflictDoUpdate({
target: userDetails.userId,
set: data,
})
}
async getLastOrderByUserId(userId: number): Promise<typeof orders.$inferSelect | undefined> {
const userOrders = await db.query.orders.findMany({
where: eq(orders.userId, userId),
orderBy: desc(orders.createdAt),
limit: 1,
})
return userOrders[0]
}
}
export const staffUserDbService: IStaffUserDbService = new StaffUserDbService()

View file

@ -0,0 +1,53 @@
import { db } from '@/src/db/db_index_sqlite'
import { storeInfo, productInfo } from '@/src/db/schema'
import { eq, inArray } from 'drizzle-orm'
import { IStoreDbService, Store, NewStore } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/store-db-service.interface'
export class StoreDbService implements IStoreDbService {
async getAllStores(): Promise<Store[]> {
return db.query.storeInfo.findMany({
with: { owner: true },
})
}
async getStoreById(id: number): Promise<Store | undefined> {
return db.query.storeInfo.findFirst({
where: eq(storeInfo.id, id),
with: { owner: true },
})
}
async createStore(data: NewStore): Promise<Store> {
const [store] = await db.insert(storeInfo).values(data).returning()
return store
}
async updateStore(id: number, data: Partial<NewStore>): Promise<Store> {
const [store] = await db
.update(storeInfo)
.set(data)
.where(eq(storeInfo.id, id))
.returning()
return store
}
async deleteStore(id: number): Promise<void> {
await db.delete(storeInfo).where(eq(storeInfo.id, id))
}
async assignProductsToStore(storeId: number, productIds: number[]): Promise<void> {
await db
.update(productInfo)
.set({ storeId })
.where(inArray(productInfo.id, productIds))
}
async removeProductsFromStore(storeId: number): Promise<void> {
await db
.update(productInfo)
.set({ storeId: null })
.where(eq(productInfo.storeId, storeId))
}
}
export const storeDbService: IStoreDbService = new StoreDbService()

View file

@ -0,0 +1,51 @@
import { db } from '@/src/db/db_index_sqlite'
import { productTagInfo } from '@/src/db/schema'
import { eq } from 'drizzle-orm'
import { ITagDbService, Tag, NewTag } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/tag-db-service.interface'
import { toJsonString } from '@/src/db/sqlite-casts'
export class TagDbService implements ITagDbService {
async getAllTags(): Promise<Tag[]> {
return db.select().from(productTagInfo).orderBy(productTagInfo.tagName)
}
async getTagById(id: number): Promise<Tag | undefined> {
return db.query.productTagInfo.findFirst({
where: eq(productTagInfo.id, id),
})
}
async getTagByName(name: string): Promise<Tag | undefined> {
return db.query.productTagInfo.findFirst({
where: eq(productTagInfo.tagName, name.trim()),
})
}
async createTag(data: NewTag): Promise<Tag> {
const normalized = {
...data,
relatedStores: data.relatedStores ? toJsonString(data.relatedStores, '[]') : data.relatedStores,
}
const [tag] = await db.insert(productTagInfo).values(normalized).returning()
return tag
}
async updateTag(id: number, data: Partial<NewTag>): Promise<Tag> {
const normalized = {
...data,
relatedStores: data.relatedStores ? toJsonString(data.relatedStores, '[]') : data.relatedStores,
}
const [tag] = await db
.update(productTagInfo)
.set(normalized)
.where(eq(productTagInfo.id, id))
.returning()
return tag
}
async deleteTag(id: number): Promise<void> {
await db.delete(productTagInfo).where(eq(productTagInfo.id, id))
}
}
export const tagDbService: ITagDbService = new TagDbService()

View file

@ -0,0 +1,172 @@
import { db } from '@/src/db/db_index_sqlite'
import { users, userDetails, orders, orderItems, orderStatus, complaints, notifCreds, unloggedUserTokens, userIncidents } from '@/src/db/schema'
import { eq, desc, asc, count, max, inArray, and, like, gt } from 'drizzle-orm'
import { IUserDbService, User, NewUser, UserDetail } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/user-db-service.interface'
export class UserDbService implements IUserDbService {
async getUserById(id: number): Promise<User | undefined> {
return db.query.users.findFirst({
where: eq(users.id, id),
})
}
async getUserByMobile(mobile: string): Promise<User | undefined> {
return db.query.users.findFirst({
where: eq(users.mobile, mobile),
})
}
async getUsers(options: { limit: number; cursor?: number; search?: string }): Promise<User[]> {
const { limit, cursor, search } = options
const conditions = []
if (search && search.trim()) {
conditions.push(like(users.mobile, `%${search.trim()}%`))
}
if (cursor) {
conditions.push(gt(users.id, cursor))
}
const whereCondition = conditions.length > 0 ? and(...conditions) : undefined
return db
.select()
.from(users)
.where(whereCondition)
.orderBy(asc(users.id))
.limit(limit + 1)
}
async createUser(data: NewUser): Promise<User> {
const [user] = await db.insert(users).values(data).returning()
return user
}
async getUserDetailsByUserId(userId: number): Promise<UserDetail | undefined> {
return db.query.userDetails.findFirst({
where: eq(userDetails.userId, userId),
})
}
async upsertUserDetails(data: Partial<UserDetail> & { userId: number }): Promise<void> {
await db
.insert(userDetails)
.values(data)
.onConflictDoUpdate({
target: userDetails.userId,
set: data,
})
}
async getOrdersByUserId(userId: number): Promise<typeof orders.$inferSelect[]> {
return db
.select()
.from(orders)
.where(eq(orders.userId, userId))
.orderBy(desc(orders.createdAt))
}
async getLastOrderByUserId(userId: number): Promise<typeof orders.$inferSelect | undefined> {
const userOrders = await db
.select()
.from(orders)
.where(eq(orders.userId, userId))
.orderBy(desc(orders.createdAt))
.limit(1)
return userOrders[0]
}
async getOrderCountByUserIds(userIds: number[]): Promise<{ userId: number; totalOrders: number }[]> {
if (userIds.length === 0) return []
return db
.select({
userId: orders.userId,
totalOrders: count(orders.id),
})
.from(orders)
.where(inArray(orders.userId, userIds))
.groupBy(orders.userId)
}
async getLastOrderDateByUserIds(userIds: number[]): Promise<{ userId: number; lastOrderDate: Date | null }[]> {
if (userIds.length === 0) return []
return db
.select({
userId: orders.userId,
lastOrderDate: max(orders.createdAt),
})
.from(orders)
.where(inArray(orders.userId, userIds))
.groupBy(orders.userId)
}
async getOrderStatusByOrderIds(orderIds: number[]): Promise<{ orderId: number; isDelivered: boolean; isCancelled: boolean }[]> {
if (orderIds.length === 0) return []
return db
.select({
orderId: orderStatus.orderId,
isDelivered: orderStatus.isDelivered,
isCancelled: orderStatus.isCancelled,
})
.from(orderStatus)
.where(inArray(orderStatus.orderId, orderIds))
}
async getOrderItemCountByOrderIds(orderIds: number[]): Promise<{ orderId: number; itemCount: number }[]> {
if (orderIds.length === 0) return []
return db
.select({
orderId: orderItems.orderId,
itemCount: count(orderItems.id),
})
.from(orderItems)
.where(inArray(orderItems.orderId, orderIds))
.groupBy(orderItems.orderId)
}
async getUnresolvedComplaintCount(): Promise<number> {
return db.$count(complaints, eq(complaints.isResolved, false))
}
async getAllNotifTokens(): Promise<string[]> {
const tokens = await db.select({ token: notifCreds.token }).from(notifCreds)
return tokens.map(t => t.token)
}
async getNotifTokensByUserIds(userIds: number[]): Promise<string[]> {
const tokens = await db
.select({ token: notifCreds.token })
.from(notifCreds)
.where(inArray(notifCreds.userId, userIds))
return tokens.map(t => t.token)
}
async getUnloggedTokens(): Promise<string[]> {
const tokens = await db.select({ token: unloggedUserTokens.token }).from(unloggedUserTokens)
return tokens.map(t => t.token)
}
async getUserIncidentsByUserId(userId: number): Promise<Array<typeof userIncidents.$inferSelect & { order?: { orderStatus: Array<{ isCancelled: boolean }> } | null; addedBy?: { name: string | null } | null }>> {
return db.query.userIncidents.findMany({
where: eq(userIncidents.userId, userId),
with: {
order: {
with: {
orderStatus: true,
},
},
addedBy: true,
},
orderBy: desc(userIncidents.dateAdded),
})
}
async createUserIncident(data: { userId: number; orderId?: number | null; adminComment?: string | null; addedBy: number; negativityScore?: number | null }): Promise<typeof userIncidents.$inferSelect> {
const [incident] = await db.insert(userIncidents).values(data).returning()
return incident
}
}
export const userDbService: IUserDbService = new UserDbService()

View file

@ -0,0 +1,141 @@
import { db } from '@/src/db/db_index_sqlite'
import { vendorSnippets, deliverySlotInfo, orders, orderItems, productInfo } from '@/src/db/schema'
import { eq, and, inArray, gt, asc, desc } from 'drizzle-orm'
import { IVendorSnippetDbService, VendorSnippet, NewVendorSnippet } from '@/src/trpc/apis/admin-apis/dataAccessors/interfaces/vendor-snippet-db-service.interface'
import { toJsonString } from '@/src/db/sqlite-casts'
export class VendorSnippetDbService implements IVendorSnippetDbService {
async createSnippet(data: NewVendorSnippet): Promise<VendorSnippet> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
}
const [snippet] = await db.insert(vendorSnippets).values(normalized).returning()
return snippet
}
async getAllSnippets(): Promise<VendorSnippet[]> {
return db.query.vendorSnippets.findMany({
with: { slot: true },
orderBy: desc(vendorSnippets.createdAt),
})
}
async getSnippetById(id: number): Promise<VendorSnippet | undefined> {
return db.query.vendorSnippets.findFirst({
where: eq(vendorSnippets.id, id),
with: { slot: true },
})
}
async getSnippetByCode(code: string): Promise<VendorSnippet | undefined> {
return db.query.vendorSnippets.findFirst({
where: eq(vendorSnippets.snippetCode, code),
})
}
async updateSnippet(id: number, data: Partial<NewVendorSnippet>): Promise<VendorSnippet> {
const normalized = {
...data,
productIds: data.productIds ? toJsonString(data.productIds, '[]') : data.productIds,
}
const [snippet] = await db
.update(vendorSnippets)
.set(normalized)
.where(eq(vendorSnippets.id, id))
.returning()
return snippet
}
async deleteSnippet(id: number): Promise<VendorSnippet> {
const [snippet] = await db
.delete(vendorSnippets)
.where(eq(vendorSnippets.id, id))
.returning()
return snippet
}
async checkSnippetCodeExists(code: string): Promise<boolean> {
const existing = await db.query.vendorSnippets.findFirst({
where: eq(vendorSnippets.snippetCode, code),
})
return !!existing
}
async getSlotById(id: number): Promise<typeof deliverySlotInfo.$inferSelect | undefined> {
return db.query.deliverySlotInfo.findFirst({
where: eq(deliverySlotInfo.id, id),
})
}
async getUpcomingSlots(since: Date): Promise<typeof deliverySlotInfo.$inferSelect[]> {
return db.query.deliverySlotInfo.findMany({
where: and(
eq(deliverySlotInfo.isActive, true),
gt(deliverySlotInfo.deliveryTime, since)
),
orderBy: asc(deliverySlotInfo.deliveryTime),
})
}
async getProductsByIds(ids: number[]): Promise<Array<{ id: number; name: string }>> {
return db.query.productInfo.findMany({
where: inArray(productInfo.id, ids),
columns: { id: true, name: true },
})
}
async validateProductsExist(ids: number[]): Promise<boolean> {
const products = await db.query.productInfo.findMany({
where: inArray(productInfo.id, ids),
})
return products.length === ids.length
}
async getOrdersBySlotId(slotId: number): Promise<typeof orders.$inferSelect[]> {
return db.query.orders.findMany({
where: eq(orders.slotId, slotId),
with: {
orderItems: {
with: {
product: {
with: { unit: true },
},
},
},
orderStatus: true,
user: true,
slot: true,
},
orderBy: desc(orders.createdAt),
})
}
async getOrderItemsByOrderIds(orderIds: number[]): Promise<typeof orderItems.$inferSelect[]> {
return db.query.orderItems.findMany({
where: inArray(orderItems.orderId, orderIds),
})
}
async getOrderItemById(id: number): Promise<typeof orderItems.$inferSelect | undefined> {
return db.query.orderItems.findFirst({
where: eq(orderItems.id, id),
})
}
async updateOrderItemPackaging(id: number, is_packaged: boolean): Promise<void> {
await db
.update(orderItems)
.set({ is_packaged })
.where(eq(orderItems.id, id))
}
async hasSnippetForSlot(slotId: number): Promise<boolean> {
const snippet = await db.query.vendorSnippets.findFirst({
where: eq(vendorSnippets.slotId, slotId),
})
return !!snippet
}
}
export const vendorSnippetDbService: IVendorSnippetDbService = new VendorSnippetDbService()

View file

@ -1,32 +1,43 @@
export type { IUserBannerDbService, UserBanner } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface'
export { userBannerDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-banner-queries'
// export { userBannerDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-banner-queries'
export { userBannerDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-banner-queries'
export type { IUserStoreDbService, Store as UserStore, StoreBasic } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface'
export { userStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-store-queries'
// export { userStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-store-queries'
export { userStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-store-queries'
export type { IUserAddressDbService, Address, NewAddress } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface'
export { userAddressDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-address-queries'
// export { userAddressDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-address-queries'
export { userAddressDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-address-queries'
export type { IUserCartDbService, CartItem } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface'
export { userCartDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-cart-queries'
// export { userCartDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-cart-queries'
export { userCartDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-cart-queries'
export type { IUserComplaintDbService, Complaint, NewComplaint } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface'
export { userComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-complaint-queries'
// export { userComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-complaint-queries'
export { userComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-complaint-queries'
export type { IUserProductDbService, Product, Store as ProductStore, Review, ProductWithUnit } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface'
export { userProductDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-product-queries'
// export { userProductDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-product-queries'
export { userProductDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-product-queries'
export type { IUserAuthDbService, User, UserCred, UserDetail } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface'
export { userAuthDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-auth-queries'
// export { userAuthDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-auth-queries'
export { userAuthDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-auth-queries'
export type { IUserProfileDbService, User as ProfileUser, UserDetail as ProfileUserDetail, UserCred as ProfileUserCred, NotifCred, UnloggedToken } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface'
export { userProfileDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-profile-queries'
// export { userProfileDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-profile-queries'
export { userProfileDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-profile-queries'
export type { IUserSlotDbService, Slot } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface'
export { userSlotDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-slot-queries'
// export { userSlotDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-slot-queries'
export { userSlotDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-slot-queries'
export type { IUserCouponDbService, Coupon, CouponWithRelations, ReservedCoupon } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface'
export { userCouponDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-coupon-queries'
// export { userCouponDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-coupon-queries'
export { userCouponDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-coupon-queries'
export type { IUserOrderDbService, Order, OrderInsert, OrderItemInsert, OrderStatusInsert, Coupon as OrderCoupon } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface'
export { userOrderDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-order-queries'
// export { userOrderDbService } from '@/src/trpc/apis/user-apis/dataAccessors/postgres/user-order-queries'
export { userOrderDbService } from '@/src/trpc/apis/user-apis/dataAccessors/sqlite/user-order-queries'

View file

@ -0,0 +1,71 @@
import { db } from '@/src/db/db_index_sqlite'
import { addresses, orders, orderStatus, deliverySlotInfo } from '@/src/db/schema'
import { eq, and, gte } from 'drizzle-orm'
import { IUserAddressDbService, Address, NewAddress } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-address-db-service.interface'
export class UserAddressDbService implements IUserAddressDbService {
async getDefaultAddress(userId: number): Promise<Address | undefined> {
const [defaultAddress] = await db
.select()
.from(addresses)
.where(and(eq(addresses.userId, userId), eq(addresses.isDefault, true)))
.limit(1)
return defaultAddress
}
async getUserAddresses(userId: number): Promise<Address[]> {
return db.select().from(addresses).where(eq(addresses.userId, userId))
}
async unsetDefaultForUser(userId: number): Promise<void> {
await db.update(addresses).set({ isDefault: false }).where(eq(addresses.userId, userId))
}
async createAddress(data: NewAddress): Promise<Address> {
const [newAddress] = await db.insert(addresses).values(data).returning()
return newAddress
}
async getAddressByIdForUser(addressId: number, userId: number): Promise<Address | undefined> {
const [address] = await db
.select()
.from(addresses)
.where(and(eq(addresses.id, addressId), eq(addresses.userId, userId)))
.limit(1)
return address
}
async updateAddressForUser(addressId: number, userId: number, data: Partial<NewAddress>): Promise<Address> {
const [updated] = await db
.update(addresses)
.set(data)
.where(and(eq(addresses.id, addressId), eq(addresses.userId, userId)))
.returning()
return updated
}
async deleteAddressForUser(addressId: number, userId: number): Promise<void> {
await db.delete(addresses).where(and(eq(addresses.id, addressId), eq(addresses.userId, userId)))
}
async hasOngoingOrdersForAddress(addressId: number): Promise<boolean> {
const ongoingOrders = await db
.select({
orderId: orders.id,
})
.from(orders)
.innerJoin(orderStatus, eq(orders.id, orderStatus.orderId))
.innerJoin(deliverySlotInfo, eq(orders.slotId, deliverySlotInfo.id))
.where(
and(
eq(orders.addressId, addressId),
eq(orderStatus.isCancelled, false),
gte(deliverySlotInfo.deliveryTime, new Date())
)
)
.limit(1)
return ongoingOrders.length > 0
}
}
export const userAddressDbService: IUserAddressDbService = new UserAddressDbService()

View file

@ -0,0 +1,122 @@
import { db } from '@/src/db/db_index_sqlite'
import { users, userCreds, userDetails, addresses, cartItems, complaints, couponApplicableUsers, couponUsage, notifCreds, notifications, orderItems, orderStatus, orders, payments, refunds, productReviews, reservedCoupons } from '@/src/db/schema'
import { eq } from 'drizzle-orm'
import { IUserAuthDbService, User, UserCred, UserDetail } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-auth-db-service.interface'
export class UserAuthDbService implements IUserAuthDbService {
async getUserByEmail(email: string): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.email, email)).limit(1)
return user
}
async getUserByMobile(mobile: string): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.mobile, mobile)).limit(1)
return user
}
async getUserById(userId: number): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.id, userId)).limit(1)
return user
}
async getUserCredsByUserId(userId: number): Promise<UserCred | undefined> {
const [creds] = await db.select().from(userCreds).where(eq(userCreds.userId, userId)).limit(1)
return creds
}
async getUserDetailsByUserId(userId: number): Promise<UserDetail | undefined> {
const [detail] = await db.select().from(userDetails).where(eq(userDetails.userId, userId)).limit(1)
return detail
}
async createUserWithCredsAndDetails(data: { name: string | null; email: string | null; mobile: string; passwordHash: string; imageKey?: string | null }): Promise<User> {
const { name, email, mobile, passwordHash, imageKey } = data
return db.transaction(async (tx) => {
const [user] = await tx
.insert(users)
.values({ name, email, mobile })
.returning()
await tx
.insert(userCreds)
.values({ userId: user.id, userPassword: passwordHash })
if (imageKey) {
await tx.insert(userDetails).values({ userId: user.id, profileImage: imageKey })
}
return user
})
}
async createUser(data: { name: string | null; email: string | null; mobile: string }): Promise<User> {
const [user] = await db.insert(users).values(data).returning()
return user
}
async upsertUserCreds(userId: number, passwordHash: string): Promise<void> {
await db
.insert(userCreds)
.values({ userId, userPassword: passwordHash })
.onConflictDoUpdate({
target: userCreds.userId,
set: { userPassword: passwordHash },
})
}
async updateUserName(userId: number, name: string): Promise<void> {
await db.update(users).set({ name }).where(eq(users.id, userId))
}
async updateUserEmail(userId: number, email: string): Promise<void> {
await db.update(users).set({ email }).where(eq(users.id, userId))
}
async upsertUserDetails(userId: number, data: Partial<UserDetail>): Promise<void> {
await db
.insert(userDetails)
.values({ userId, ...data })
.onConflictDoUpdate({
target: userDetails.userId,
set: data,
})
}
async deleteAccountByUserId(userId: number): Promise<void> {
await db.transaction(async (tx) => {
await tx.delete(notifCreds).where(eq(notifCreds.userId, userId))
await tx.delete(couponApplicableUsers).where(eq(couponApplicableUsers.userId, userId))
await tx.delete(couponUsage).where(eq(couponUsage.userId, userId))
await tx.delete(complaints).where(eq(complaints.userId, userId))
await tx.delete(cartItems).where(eq(cartItems.userId, userId))
await tx.delete(notifications).where(eq(notifications.userId, userId))
await tx.delete(productReviews).where(eq(productReviews.userId, userId))
await tx.update(reservedCoupons)
.set({ redeemedBy: null })
.where(eq(reservedCoupons.redeemedBy, userId))
const userOrders = await tx
.select({ id: orders.id })
.from(orders)
.where(eq(orders.userId, userId))
for (const order of userOrders) {
await tx.delete(orderItems).where(eq(orderItems.orderId, order.id))
await tx.delete(orderStatus).where(eq(orderStatus.orderId, order.id))
await tx.delete(payments).where(eq(payments.orderId, order.id))
await tx.delete(refunds).where(eq(refunds.orderId, order.id))
await tx.delete(couponUsage).where(eq(couponUsage.orderId, order.id))
await tx.delete(complaints).where(eq(complaints.orderId, order.id))
}
await tx.delete(orders).where(eq(orders.userId, userId))
await tx.delete(addresses).where(eq(addresses.userId, userId))
await tx.delete(userDetails).where(eq(userDetails.userId, userId))
await tx.delete(userCreds).where(eq(userCreds.userId, userId))
await tx.delete(users).where(eq(users.id, userId))
})
}
}
export const userAuthDbService: IUserAuthDbService = new UserAuthDbService()

View file

@ -0,0 +1,15 @@
import { db } from '@/src/db/db_index_sqlite'
import { homeBanners } from '@/src/db/schema'
import { isNotNull, asc } from 'drizzle-orm'
import { IUserBannerDbService, UserBanner } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-banner-db-service.interface'
export class UserBannerDbService implements IUserBannerDbService {
async getActiveBanners(): Promise<UserBanner[]> {
return db.query.homeBanners.findMany({
where: isNotNull(homeBanners.serialNum),
orderBy: asc(homeBanners.serialNum),
})
}
}
export const userBannerDbService: IUserBannerDbService = new UserBannerDbService()

View file

@ -0,0 +1,75 @@
import { db } from '@/src/db/db_index_sqlite'
import { cartItems, productInfo, units } from '@/src/db/schema'
import { eq, and, sql } from 'drizzle-orm'
import { IUserCartDbService, CartItem } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-cart-db-service.interface'
export class UserCartDbService implements IUserCartDbService {
async getCartItemsWithProducts(userId: number) {
return db
.select({
cartId: cartItems.id,
productId: productInfo.id,
productName: productInfo.name,
productPrice: productInfo.price,
productImages: productInfo.images,
productQuantity: productInfo.productQuantity,
isOutOfStock: productInfo.isOutOfStock,
unitShortNotation: units.shortNotation,
quantity: cartItems.quantity,
addedAt: cartItems.addedAt,
})
.from(cartItems)
.innerJoin(productInfo, eq(cartItems.productId, productInfo.id))
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(eq(cartItems.userId, userId))
}
async getProductById(productId: number) {
return db.query.productInfo.findFirst({
where: eq(productInfo.id, productId),
})
}
async getCartItemByUserAndProduct(userId: number, productId: number): Promise<CartItem | undefined> {
return db.query.cartItems.findFirst({
where: and(eq(cartItems.userId, userId), eq(cartItems.productId, productId)),
})
}
async incrementCartItemQuantity(cartItemId: number, quantity: number): Promise<void> {
await db.update(cartItems)
.set({
quantity: sql`${cartItems.quantity} + ${quantity}`,
})
.where(eq(cartItems.id, cartItemId))
}
async createCartItem(userId: number, productId: number, quantity: number): Promise<void> {
await db.insert(cartItems).values({
userId,
productId,
quantity: quantity.toString(),
})
}
async updateCartItemQuantity(itemId: number, userId: number, quantity: number): Promise<CartItem | undefined> {
const [updatedItem] = await db.update(cartItems)
.set({ quantity: quantity.toString() })
.where(and(eq(cartItems.id, itemId), eq(cartItems.userId, userId)))
.returning()
return updatedItem
}
async deleteCartItem(itemId: number, userId: number): Promise<CartItem | undefined> {
const [deletedItem] = await db.delete(cartItems)
.where(and(eq(cartItems.id, itemId), eq(cartItems.userId, userId)))
.returning()
return deletedItem
}
async clearCart(userId: number): Promise<void> {
await db.delete(cartItems).where(eq(cartItems.userId, userId))
}
}
export const userCartDbService: IUserCartDbService = new UserCartDbService()

View file

@ -0,0 +1,28 @@
import { db } from '@/src/db/db_index_sqlite'
import { complaints } from '@/src/db/schema'
import { eq, asc } from 'drizzle-orm'
import { IUserComplaintDbService } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-complaint-db-service.interface'
export class UserComplaintDbService implements IUserComplaintDbService {
async getComplaintsByUserId(userId: number) {
return db
.select({
id: complaints.id,
complaintBody: complaints.complaintBody,
response: complaints.response,
isResolved: complaints.isResolved,
createdAt: complaints.createdAt,
orderId: complaints.orderId,
images: complaints.images,
})
.from(complaints)
.where(eq(complaints.userId, userId))
.orderBy(asc(complaints.createdAt))
}
async createComplaint(data: { userId: number; orderId?: number | null; complaintBody: string; images: string[] }) {
await db.insert(complaints).values(data)
}
}
export const userComplaintDbService: IUserComplaintDbService = new UserComplaintDbService()

View file

@ -0,0 +1,88 @@
import { db } from '@/src/db/db_index_sqlite'
import { coupons, couponUsage, couponApplicableUsers, couponApplicableProducts, reservedCoupons } from '@/src/db/schema'
import { eq, and, or, gt, isNull } from 'drizzle-orm'
import { IUserCouponDbService, Coupon, ReservedCoupon, CouponWithRelations } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-coupon-db-service.interface'
export class UserCouponDbService implements IUserCouponDbService {
async getActiveCouponsForUser(userId: number): Promise<CouponWithRelations[]> {
return db.query.coupons.findMany({
where: and(
eq(coupons.isInvalidated, false),
or(
isNull(coupons.validTill),
gt(coupons.validTill, new Date())
)
),
with: {
usages: {
where: eq(couponUsage.userId, userId),
},
applicableUsers: {
with: { user: true },
},
applicableProducts: {
with: { product: true },
},
},
}) as Promise<CouponWithRelations[]>
}
async getAllCouponsForUser(userId: number): Promise<CouponWithRelations[]> {
return db.query.coupons.findMany({
with: {
usages: {
where: eq(couponUsage.userId, userId),
},
applicableUsers: {
with: { user: true },
},
applicableProducts: {
with: { product: true },
},
},
}) as Promise<CouponWithRelations[]>
}
async getReservedCouponBySecretCode(secretCode: string): Promise<ReservedCoupon | undefined> {
return db.query.reservedCoupons.findFirst({
where: and(
eq(reservedCoupons.secretCode, secretCode.toUpperCase()),
eq(reservedCoupons.isRedeemed, false)
),
})
}
async redeemReservedCoupon(userId: number, reservedCoupon: ReservedCoupon): Promise<Coupon> {
return db.transaction(async (tx) => {
const [coupon] = await tx.insert(coupons).values({
couponCode: reservedCoupon.couponCode,
isUserBased: true,
discountPercent: reservedCoupon.discountPercent,
flatDiscount: reservedCoupon.flatDiscount,
minOrder: reservedCoupon.minOrder,
productIds: reservedCoupon.productIds,
maxValue: reservedCoupon.maxValue,
isApplyForAll: false,
validTill: reservedCoupon.validTill,
maxLimitForUser: reservedCoupon.maxLimitForUser,
exclusiveApply: reservedCoupon.exclusiveApply,
createdBy: reservedCoupon.createdBy,
}).returning()
await tx.insert(couponApplicableUsers).values({
couponId: coupon.id,
userId,
})
await tx.update(reservedCoupons).set({
isRedeemed: true,
redeemedBy: userId,
redeemedAt: new Date(),
}).where(eq(reservedCoupons.id, reservedCoupon.id))
return coupon
})
}
}
export const userCouponDbService: IUserCouponDbService = new UserCouponDbService()

View file

@ -0,0 +1,265 @@
import { db } from '@/src/db/db_index_sqlite'
import {
orders,
orderItems,
orderStatus,
addresses,
productInfo,
paymentInfoTable,
coupons,
couponUsage,
cartItems,
refunds,
units,
userDetails,
} from '@/src/db/schema'
import { and, desc, eq, gte, inArray } from 'drizzle-orm'
import {
IUserOrderDbService,
Order,
} from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-order-db-service.interface'
export class UserOrderDbService implements IUserOrderDbService {
async getUserDetailByUserId(userId: number) {
return db.query.userDetails.findFirst({
where: eq(userDetails.userId, userId),
})
}
async getAddressByUserId(userId: number, addressId: number) {
return db.query.addresses.findFirst({
where: and(eq(addresses.userId, userId), eq(addresses.id, addressId)),
})
}
async getProductById(productId: number) {
return db.query.productInfo.findFirst({
where: eq(productInfo.id, productId),
})
}
async getCouponWithUsage(couponId: number, userId: number) {
return db.query.coupons.findFirst({
where: eq(coupons.id, couponId),
with: {
usages: { where: eq(couponUsage.userId, userId) },
},
})
}
async createOrdersWithItems(params: {
ordersData: Array<{
order: Omit<typeof orders.$inferInsert, 'id'>
orderItems: Omit<typeof orderItems.$inferInsert, 'id'>[]
orderStatus: Omit<typeof orderStatus.$inferInsert, 'id'>
}>
paymentMethod: 'online' | 'cod'
}): Promise<Order[]> {
const { ordersData, paymentMethod } = params
return db.transaction(async (tx) => {
let sharedPaymentInfoId: number | null = null
if (paymentMethod === 'online') {
const [paymentInfo] = await tx
.insert(paymentInfoTable)
.values({
status: 'pending',
gateway: 'razorpay',
merchantOrderId: `multi_order_${Date.now()}`,
})
.returning()
sharedPaymentInfoId = paymentInfo.id
}
const ordersToInsert: Omit<typeof orders.$inferInsert, 'id'>[] = ordersData.map(
(od) => ({
...od.order,
paymentInfoId: sharedPaymentInfoId,
})
)
const insertedOrders = await tx.insert(orders).values(ordersToInsert).returning()
const allOrderItems: Omit<typeof orderItems.$inferInsert, 'id'>[] = []
const allOrderStatuses: Omit<typeof orderStatus.$inferInsert, 'id'>[] = []
insertedOrders.forEach((order, index) => {
const od = ordersData[index]
od.orderItems.forEach((item) => {
allOrderItems.push({ ...item, orderId: order.id as number })
})
allOrderStatuses.push({
...od.orderStatus,
orderId: order.id as number,
})
})
await tx.insert(orderItems).values(allOrderItems)
await tx.insert(orderStatus).values(allOrderStatuses)
return insertedOrders
})
}
async deleteCartItemsByUserAndProductIds(userId: number, productIds: number[]) {
await db.delete(cartItems).where(
and(eq(cartItems.userId, userId), inArray(cartItems.productId, productIds))
)
}
async createCouponUsage(params: { userId: number; couponId: number; orderId: number }) {
const { userId, couponId, orderId } = params
await db.insert(couponUsage).values({
userId,
couponId,
orderId,
orderItemId: null,
usedAt: new Date(),
})
}
async getOrdersCount(userId: number) {
return db.$count(orders, eq(orders.userId, userId))
}
async getOrdersWithRelations(userId: number, limit: number, offset: number) {
return db.query.orders.findMany({
where: eq(orders.userId, userId),
with: {
orderItems: {
with: {
product: true,
},
},
slot: true,
paymentInfo: true,
orderStatus: true,
refunds: true,
},
orderBy: (ordersRef, { desc }) => [desc(ordersRef.createdAt)],
limit,
offset,
})
}
async getOrderWithDetailsById(orderId: number, userId: number) {
return db.query.orders.findFirst({
where: and(eq(orders.id, orderId), eq(orders.userId, userId)),
with: {
orderItems: {
with: {
product: true,
},
},
slot: true,
paymentInfo: true,
orderStatus: {
with: {
refundCoupon: true,
},
},
refunds: true,
},
})
}
async getCouponUsagesByOrderId(orderId: number) {
return db.query.couponUsage.findMany({
where: eq(couponUsage.orderId, orderId),
with: {
coupon: true,
},
})
}
async getOrderWithStatus(orderId: number) {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
orderStatus: true,
},
})
}
async cancelOrderTransaction(params: {
statusId: number
reason: string
orderId: number
refundStatus: string
}) {
const { statusId, reason, orderId, refundStatus } = params
await db.transaction(async (tx) => {
await tx
.update(orderStatus)
.set({
isCancelled: true,
cancelReason: reason,
cancellationUserNotes: reason,
cancellationReviewed: false,
})
.where(eq(orderStatus.id, statusId))
await tx.insert(refunds).values({
orderId,
refundStatus,
})
})
}
async updateOrderNotes(orderId: number, userNotes: string | null) {
await db
.update(orders)
.set({
userNotes,
})
.where(eq(orders.id, orderId))
}
async getRecentDeliveredOrderIds(userId: number, since: Date, limit: number) {
return db
.select({ id: orders.id })
.from(orders)
.innerJoin(orderStatus, eq(orders.id, orderStatus.orderId))
.where(
and(
eq(orders.userId, userId),
eq(orderStatus.isDelivered, true),
gte(orders.createdAt, since)
)
)
.orderBy(desc(orders.createdAt))
.limit(limit)
}
async getProductIdsByOrderIds(orderIds: number[]) {
return db
.select({ productId: orderItems.productId })
.from(orderItems)
.where(inArray(orderItems.orderId, orderIds))
}
async getProductsWithUnitsByIds(productIds: number[], limit: number) {
return db
.select({
id: productInfo.id,
name: productInfo.name,
shortDescription: productInfo.shortDescription,
price: productInfo.price,
images: productInfo.images,
isOutOfStock: productInfo.isOutOfStock,
unitShortNotation: units.shortNotation,
incrementStep: productInfo.incrementStep,
})
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(
and(
inArray(productInfo.id, productIds),
eq(productInfo.isSuspended, false)
)
)
.orderBy(desc(productInfo.createdAt))
.limit(limit)
}
}
export const userOrderDbService: IUserOrderDbService = new UserOrderDbService()

View file

@ -0,0 +1,111 @@
import { db } from '@/src/db/db_index_sqlite'
import { productInfo, units, storeInfo, productSlots, deliverySlotInfo, specialDeals, productReviews, users } from '@/src/db/schema'
import { eq, and, gt, sql, desc } from 'drizzle-orm'
import { IUserProductDbService, Review } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-product-db-service.interface'
export class UserProductDbService implements IUserProductDbService {
async getProductById(productId: number) {
const result = await db
.select({
id: productInfo.id,
name: productInfo.name,
shortDescription: productInfo.shortDescription,
longDescription: productInfo.longDescription,
price: productInfo.price,
marketPrice: productInfo.marketPrice,
images: productInfo.images,
isOutOfStock: productInfo.isOutOfStock,
storeId: productInfo.storeId,
unitShortNotation: units.shortNotation,
incrementStep: productInfo.incrementStep,
productQuantity: productInfo.productQuantity,
isFlashAvailable: productInfo.isFlashAvailable,
flashPrice: productInfo.flashPrice,
})
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(eq(productInfo.id, productId))
.limit(1)
return result[0]
}
async getStoreBasicById(storeId: number) {
return db.query.storeInfo.findFirst({
where: eq(storeInfo.id, storeId),
columns: { id: true, name: true, description: true },
})
}
async getDeliverySlotsForProduct(productId: number) {
const now = new Date()
return db
.select({
id: deliverySlotInfo.id,
deliveryTime: deliverySlotInfo.deliveryTime,
freezeTime: deliverySlotInfo.freezeTime,
})
.from(productSlots)
.innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id))
.where(
and(
eq(productSlots.productId, productId),
eq(deliverySlotInfo.isActive, true),
eq(deliverySlotInfo.isCapacityFull, false),
gt(deliverySlotInfo.deliveryTime, now),
gt(deliverySlotInfo.freezeTime, now)
)
)
.orderBy(deliverySlotInfo.deliveryTime)
}
async getSpecialDealsForProduct(productId: number) {
const now = new Date()
return db
.select({
quantity: specialDeals.quantity,
price: specialDeals.price,
validTill: specialDeals.validTill,
})
.from(specialDeals)
.where(
and(
eq(specialDeals.productId, productId),
gt(specialDeals.validTill, now)
)
)
.orderBy(specialDeals.quantity)
}
async getProductReviews(productId: number, limit: number, offset: number) {
return db
.select({
id: productReviews.id,
reviewBody: productReviews.reviewBody,
ratings: productReviews.ratings,
imageUrls: productReviews.imageUrls,
reviewTime: productReviews.reviewTime,
userName: users.name,
})
.from(productReviews)
.innerJoin(users, eq(productReviews.userId, users.id))
.where(eq(productReviews.productId, productId))
.orderBy(desc(productReviews.reviewTime))
.limit(limit)
.offset(offset)
}
async getReviewCount(productId: number): Promise<number> {
const result = await db
.select({ count: sql`count(*)` })
.from(productReviews)
.where(eq(productReviews.productId, productId))
return Number(result[0].count)
}
async createReview(data: { userId: number; productId: number; reviewBody: string; ratings: number; imageUrls: string[] }): Promise<Review> {
const [newReview] = await db.insert(productReviews).values(data).returning()
return newReview
}
}
export const userProductDbService: IUserProductDbService = new UserProductDbService()

View file

@ -0,0 +1,74 @@
import { db } from '@/src/db/db_index_sqlite'
import { users, userDetails, userCreds, notifCreds, unloggedUserTokens } from '@/src/db/schema'
import { eq, and } from 'drizzle-orm'
import { IUserProfileDbService, User, UserDetail, UserCred, NotifCred, UnloggedToken } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-profile-db-service.interface'
export class UserProfileDbService implements IUserProfileDbService {
async getUserById(userId: number): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.id, userId)).limit(1)
return user
}
async getUserDetailByUserId(userId: number): Promise<UserDetail | undefined> {
const [detail] = await db.select().from(userDetails).where(eq(userDetails.userId, userId)).limit(1)
return detail
}
async getUserWithCreds(userId: number): Promise<{ user: User; creds: UserCred | null } | undefined> {
const result = await db
.select()
.from(users)
.leftJoin(userCreds, eq(users.id, userCreds.userId))
.where(eq(users.id, userId))
.limit(1)
if (result.length === 0) return undefined
const row = result[0] as any
return {
user: row.users,
creds: row.user_creds || null,
}
}
async getNotifCredByUserAndToken(userId: number, token: string): Promise<NotifCred | undefined> {
return db.query.notifCreds.findFirst({
where: and(eq(notifCreds.userId, userId), eq(notifCreds.token, token)),
})
}
async updateNotifCredLastVerified(id: number): Promise<void> {
await db.update(notifCreds).set({ lastVerified: new Date() }).where(eq(notifCreds.id, id))
}
async insertNotifCred(userId: number, token: string): Promise<void> {
await db.insert(notifCreds).values({
userId,
token,
lastVerified: new Date(),
})
}
async deleteUnloggedToken(token: string): Promise<void> {
await db.delete(unloggedUserTokens).where(eq(unloggedUserTokens.token, token))
}
async getUnloggedToken(token: string): Promise<UnloggedToken | undefined> {
return db.query.unloggedUserTokens.findFirst({
where: eq(unloggedUserTokens.token, token),
})
}
async updateUnloggedTokenLastVerified(id: number): Promise<void> {
await db.update(unloggedUserTokens).set({ lastVerified: new Date() }).where(eq(unloggedUserTokens.id, id))
}
async insertUnloggedToken(token: string): Promise<void> {
await db.insert(unloggedUserTokens).values({
token,
lastVerified: new Date(),
})
}
}
export const userProfileDbService: IUserProfileDbService = new UserProfileDbService()

View file

@ -0,0 +1,26 @@
import { db } from '@/src/db/db_index_sqlite'
import { deliverySlotInfo, productInfo } from '@/src/db/schema'
import { eq } from 'drizzle-orm'
import { IUserSlotDbService, Slot } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-slot-db-service.interface'
export class UserSlotDbService implements IUserSlotDbService {
async getActiveSlots(): Promise<Slot[]> {
return db.query.deliverySlotInfo.findMany({
where: eq(deliverySlotInfo.isActive, true),
})
}
async getProductAvailability(): Promise<Array<{ id: number; name: string; isOutOfStock: boolean; isFlashAvailable: boolean }>> {
return db
.select({
id: productInfo.id,
name: productInfo.name,
isOutOfStock: productInfo.isOutOfStock,
isFlashAvailable: productInfo.isFlashAvailable,
})
.from(productInfo)
.where(eq(productInfo.isSuspended, false))
}
}
export const userSlotDbService: IUserSlotDbService = new UserSlotDbService()

View file

@ -0,0 +1,69 @@
import { db } from '@/src/db/db_index_sqlite'
import { storeInfo, productInfo, units } from '@/src/db/schema'
import { eq, and, sql } from 'drizzle-orm'
import { IUserStoreDbService } from '@/src/trpc/apis/user-apis/dataAccessors/interfaces/user-store-db-service.interface'
export class UserStoreDbService implements IUserStoreDbService {
async getStoresWithProductCount(): Promise<Array<{ id: number; name: string; description: string | null; imageUrl: string | null; productCount: number }>> {
return db
.select({
id: storeInfo.id,
name: storeInfo.name,
description: storeInfo.description,
imageUrl: storeInfo.imageUrl,
productCount: sql<number>`count(${productInfo.id})`.as('productCount'),
})
.from(storeInfo)
.leftJoin(
productInfo,
and(eq(productInfo.storeId, storeInfo.id), eq(productInfo.isSuspended, false))
)
.groupBy(storeInfo.id)
}
async getStoreById(storeId: number) {
return db.query.storeInfo.findFirst({
where: eq(storeInfo.id, storeId),
columns: {
id: true,
name: true,
description: true,
imageUrl: true,
},
})
}
async getSampleProductsByStoreId(storeId: number, limit: number) {
return db
.select({
id: productInfo.id,
name: productInfo.name,
images: productInfo.images,
})
.from(productInfo)
.where(and(eq(productInfo.storeId, storeId), eq(productInfo.isSuspended, false)))
.limit(limit)
}
async getStoreProductsWithUnits(storeId: number) {
return db
.select({
id: productInfo.id,
name: productInfo.name,
shortDescription: productInfo.shortDescription,
price: productInfo.price,
marketPrice: productInfo.marketPrice,
images: productInfo.images,
isOutOfStock: productInfo.isOutOfStock,
incrementStep: productInfo.incrementStep,
unitShortNotation: units.shortNotation,
unitNotation: units.shortNotation,
productQuantity: productInfo.productQuantity,
})
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(and(eq(productInfo.storeId, storeId), eq(productInfo.isSuspended, false)))
}
}
export const userStoreDbService: IUserStoreDbService = new UserStoreDbService()