enh
This commit is contained in:
parent
a23d3bf5b8
commit
95d2c861c0
33 changed files with 4169 additions and 732 deletions
|
|
@ -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');
|
||||
|
|
|
|||
10
apps/backend/src/db/db_index_sqlite.ts
Normal file
10
apps/backend/src/db/db_index_sqlite.ts
Normal 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 }
|
||||
706
apps/backend/src/db/schema-postgres.ts
Normal file
706
apps/backend/src/db/schema-postgres.ts
Normal 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, ({}) => ({
|
||||
}));
|
||||
735
apps/backend/src/db/schema-sqlite.ts
Normal file
735
apps/backend/src/db/schema-sqlite.ts
Normal 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
707
apps/backend/src/db/schema.ts
Executable file → Normal 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'
|
||||
|
|
|
|||
34
apps/backend/src/db/sqlite-casts.ts
Normal file
34
apps/backend/src/db/sqlite-casts.ts
Normal 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)
|
||||
}
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
Loading…
Add table
Reference in a new issue