update customers
This commit is contained in:
@@ -1113,6 +1113,16 @@
|
|||||||
"length": 255,
|
"length": 255,
|
||||||
"mappedType": "string"
|
"mappedType": "string"
|
||||||
},
|
},
|
||||||
|
"card_owner": {
|
||||||
|
"name": "card_owner",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"length": 255,
|
||||||
|
"mappedType": "string"
|
||||||
|
},
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"name": "enabled",
|
"name": "enabled",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -5428,6 +5438,173 @@
|
|||||||
},
|
},
|
||||||
"nativeEnums": {}
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "char(26)",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 26,
|
||||||
|
"mappedType": "character"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 6,
|
||||||
|
"default": "now()",
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 6,
|
||||||
|
"default": "now()",
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"deleted_at": {
|
||||||
|
"name": "deleted_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"length": 6,
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "char(26)",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 26,
|
||||||
|
"mappedType": "character"
|
||||||
|
},
|
||||||
|
"restaurant_id": {
|
||||||
|
"name": "restaurant_id",
|
||||||
|
"type": "char(26)",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 26,
|
||||||
|
"mappedType": "character"
|
||||||
|
},
|
||||||
|
"order_count": {
|
||||||
|
"name": "order_count",
|
||||||
|
"type": "int",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "integer"
|
||||||
|
},
|
||||||
|
"total_order_amount": {
|
||||||
|
"name": "total_order_amount",
|
||||||
|
"type": "int",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "user_restuarant",
|
||||||
|
"schema": "public",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"keyName": "user_restuarant_created_at_index",
|
||||||
|
"columnNames": [
|
||||||
|
"created_at"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
|
"primary": false,
|
||||||
|
"unique": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "user_restuarant_deleted_at_index",
|
||||||
|
"columnNames": [
|
||||||
|
"deleted_at"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
|
"primary": false,
|
||||||
|
"unique": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "user_restuarant_restaurant_id_index",
|
||||||
|
"columnNames": [
|
||||||
|
"restaurant_id"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
|
"primary": false,
|
||||||
|
"unique": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "user_restuarant_user_id_restaurant_id_unique",
|
||||||
|
"columnNames": [
|
||||||
|
"user_id",
|
||||||
|
"restaurant_id"
|
||||||
|
],
|
||||||
|
"composite": true,
|
||||||
|
"constraint": true,
|
||||||
|
"primary": false,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "user_restuarant_pkey",
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
|
"primary": true,
|
||||||
|
"unique": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": [],
|
||||||
|
"foreignKeys": {
|
||||||
|
"user_restuarant_user_id_foreign": {
|
||||||
|
"constraintName": "user_restuarant_user_id_foreign",
|
||||||
|
"columnNames": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"localTableName": "public.user_restuarant",
|
||||||
|
"referencedColumnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"referencedTableName": "public.users",
|
||||||
|
"updateRule": "cascade"
|
||||||
|
},
|
||||||
|
"user_restuarant_restaurant_id_foreign": {
|
||||||
|
"constraintName": "user_restuarant_restaurant_id_foreign",
|
||||||
|
"columnNames": [
|
||||||
|
"restaurant_id"
|
||||||
|
],
|
||||||
|
"localTableName": "public.user_restuarant",
|
||||||
|
"referencedColumnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"referencedTableName": "public.restaurants",
|
||||||
|
"updateRule": "cascade"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nativeEnums": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Migration } from '@mikro-orm/migrations';
|
||||||
|
|
||||||
|
export class Migration20260618162454_add_user_restuarant_table extends Migration {
|
||||||
|
|
||||||
|
override async up(): Promise<void> {
|
||||||
|
this.addSql(`create table "user_restuarant" ("id" char(26) not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "user_id" char(26) not null, "restaurant_id" char(26) not null, "order_count" int not null, "total_order_amount" int not null, constraint "user_restuarant_pkey" primary key ("id"));`);
|
||||||
|
this.addSql(`create index "user_restuarant_created_at_index" on "user_restuarant" ("created_at");`);
|
||||||
|
this.addSql(`create index "user_restuarant_deleted_at_index" on "user_restuarant" ("deleted_at");`);
|
||||||
|
this.addSql(`create index "user_restuarant_restaurant_id_index" on "user_restuarant" ("restaurant_id");`);
|
||||||
|
this.addSql(`alter table "user_restuarant" add constraint "user_restuarant_user_id_restaurant_id_unique" unique ("user_id", "restaurant_id");`);
|
||||||
|
|
||||||
|
this.addSql(`alter table "user_restuarant" add constraint "user_restuarant_user_id_foreign" foreign key ("user_id") references "users" ("id") on update cascade;`);
|
||||||
|
this.addSql(`alter table "user_restuarant" add constraint "user_restuarant_restaurant_id_foreign" foreign key ("restaurant_id") references "restaurants" ("id") on update cascade;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async down(): Promise<void> {
|
||||||
|
this.addSql(`drop table if exists "user_restuarant";`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -73,6 +73,9 @@ export class AuthService {
|
|||||||
throw new BadRequestException(RestMessage.NOT_FOUND);
|
throw new BadRequestException(RestMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add user to restuarant
|
||||||
|
await this.userService.addUserToRestaurant(user, rest);
|
||||||
|
|
||||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(user.id, rest.id, false, slug);
|
const tokens = await this.tokensService.generateAccessAndRefreshToken(user.id, rest.id, false, slug);
|
||||||
|
|
||||||
const userResponse = UserLoginTransformer.transform(user, rest);
|
const userResponse = UserLoginTransformer.transform(user, rest);
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ export class UsersController {
|
|||||||
query: FindUsersDto,
|
query: FindUsersDto,
|
||||||
@RestId() restId: string,
|
@RestId() restId: string,
|
||||||
) {
|
) {
|
||||||
return this.userService.findAll(restId, query);
|
return this.userService.findPaginated(restId, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(AdminAuthGuard)
|
@UseGuards(AdminAuthGuard)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Entity, ManyToOne, Index, Property, Unique } from '@mikro-orm/core';
|
||||||
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
|
import { User } from './user.entity';
|
||||||
|
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
|
||||||
|
|
||||||
|
@Entity({ tableName: 'user_restuarant' })
|
||||||
|
@Index({ properties: ['restaurant'] })
|
||||||
|
@Unique({ properties: ['user', 'restaurant'] })
|
||||||
|
export class UserRestaurant extends BaseEntity {
|
||||||
|
@ManyToOne(() => User)
|
||||||
|
user!: User;
|
||||||
|
|
||||||
|
@ManyToOne(() => Restaurant)
|
||||||
|
restaurant!: Restaurant;
|
||||||
|
|
||||||
|
@Property({ type: 'integer' })
|
||||||
|
orderCount!: number;
|
||||||
|
|
||||||
|
@Property({ type: 'integer' })
|
||||||
|
totalOrderAmount!: number;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ import { WalletTransactionRepository } from '../repositories/wallet-transaction.
|
|||||||
import { WalletService } from './wallet.service';
|
import { WalletService } from './wallet.service';
|
||||||
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
import { WalletTransactionReason, WalletTransactionType } from '../interface/wallet';
|
||||||
import { WalletTransaction } from '../entities/wallet-transaction.entity';
|
import { WalletTransaction } from '../entities/wallet-transaction.entity';
|
||||||
|
import { UserRestaurantRepository } from '../repositories/user-restuarant.repository';
|
||||||
|
import { UserRestaurant } from '../entities/user-restuarant.entity';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
@@ -27,6 +29,7 @@ export class UserService {
|
|||||||
private readonly walletTransactionRepository: WalletTransactionRepository,
|
private readonly walletTransactionRepository: WalletTransactionRepository,
|
||||||
private readonly walletService: WalletService,
|
private readonly walletService: WalletService,
|
||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
|
private readonly userRestaurantRepository: UserRestaurantRepository,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async findOrCreateByPhone(phone: string): Promise<User> {
|
async findOrCreateByPhone(phone: string): Promise<User> {
|
||||||
@@ -129,18 +132,16 @@ export class UserService {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(restId: string, dto: FindUsersDto): Promise<PaginatedResult<User>> {
|
async findPaginated(restId: string, dto: FindUsersDto): Promise<PaginatedResult<UserRestaurant>> {
|
||||||
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc' } = dto;
|
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc' } = dto;
|
||||||
|
|
||||||
// 1. Calculate pagination
|
// 1. Calculate pagination
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
|
|
||||||
// 2. Build the 'where' filter query
|
// 2. Build the 'where' filter query
|
||||||
const where: FilterQuery<User> = {
|
const where: FilterQuery<UserRestaurant> = {
|
||||||
orders: {
|
restaurant: {
|
||||||
restaurant: {
|
id: restId,
|
||||||
id: restId,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,19 +152,22 @@ export class UserService {
|
|||||||
const normalizedSearchPattern = `%${normalizedSearch}%`;
|
const normalizedSearchPattern = `%${normalizedSearch}%`;
|
||||||
// $ilike is case-insensitive (PostgreSQL). Use $like for MySQL (often CI by default)
|
// $ilike is case-insensitive (PostgreSQL). Use $like for MySQL (often CI by default)
|
||||||
// Search with both original and normalized phone patterns to handle various input formats
|
// Search with both original and normalized phone patterns to handle various input formats
|
||||||
where.$or = [
|
where.user = {
|
||||||
{ firstName: { $ilike: searchPattern } },
|
$or: [
|
||||||
{ lastName: { $ilike: searchPattern } },
|
{ firstName: { $ilike: searchPattern } },
|
||||||
{ phone: { $ilike: searchPattern } },
|
{ lastName: { $ilike: searchPattern } },
|
||||||
...(normalizedSearch !== search ? [{ phone: { $ilike: normalizedSearchPattern } }] : []),
|
{ phone: { $ilike: searchPattern } },
|
||||||
];
|
...(normalizedSearch !== search ? [{ phone: { $ilike: normalizedSearchPattern } }] : []),
|
||||||
|
],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Execute the query using findAndCount
|
// 5. Execute the query using findAndCount
|
||||||
const [users, total] = await this.userRepository.findAndCount(where, {
|
const [userRestaurants, total] = await this.userRestaurantRepository.findAndCount(where, {
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||||
|
populate: ['user'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 6. Calculate total pages
|
// 6. Calculate total pages
|
||||||
@@ -171,7 +175,7 @@ export class UserService {
|
|||||||
|
|
||||||
// 7. Return the paginated result
|
// 7. Return the paginated result
|
||||||
return {
|
return {
|
||||||
data: users,
|
data: userRestaurants,
|
||||||
meta: {
|
meta: {
|
||||||
total,
|
total,
|
||||||
page,
|
page,
|
||||||
@@ -225,6 +229,33 @@ export class UserService {
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add user to retuarant customers list
|
||||||
|
async addUserToRestaurant(user: User, restaurant: Restaurant) {
|
||||||
|
// const restaurant = await this.restaurantRepository.findOne({ id: restId });
|
||||||
|
// if (!restaurant) {
|
||||||
|
// throw new NotFoundException(`Restaurant with ID ${restId} not found.`);
|
||||||
|
// }
|
||||||
|
// const user = await this.userRepository.findOne({ id: userId });
|
||||||
|
// if (!user) {
|
||||||
|
// throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
const existingUSer = await this.userRestaurantRepository.findOne({ user, restaurant });
|
||||||
|
|
||||||
|
if (existingUSer) {
|
||||||
|
return existingUSer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userRestaurant = this.userRestaurantRepository.create({
|
||||||
|
user,
|
||||||
|
restaurant,
|
||||||
|
orderCount: 0,
|
||||||
|
totalOrderAmount: 0,
|
||||||
|
});
|
||||||
|
await this.em.persistAndFlush(userRestaurant);
|
||||||
|
return userRestaurant;
|
||||||
|
}
|
||||||
|
|
||||||
async getUserAddress(userId: string, addressId: string): Promise<UserAddress> {
|
async getUserAddress(userId: string, addressId: string): Promise<UserAddress> {
|
||||||
const address = await this.em.findOne(UserAddress, {
|
const address = await this.em.findOne(UserAddress, {
|
||||||
id: addressId,
|
id: addressId,
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
|
import { UserRestaurant } from '../entities/user-restuarant.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserRestaurantRepository extends EntityRepository<UserRestaurant> {
|
||||||
|
constructor(readonly em: EntityManager) {
|
||||||
|
super(em, UserRestaurant);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,15 +12,19 @@ import { WalletService } from './providers/wallet.service';
|
|||||||
import { WalletTransaction } from './entities/wallet-transaction.entity';
|
import { WalletTransaction } from './entities/wallet-transaction.entity';
|
||||||
import { PointTransaction } from './entities/point-transaction.entity';
|
import { PointTransaction } from './entities/point-transaction.entity';
|
||||||
import { PointTransactionRepository } from './repositories/point-transaction.repository';
|
import { PointTransactionRepository } from './repositories/point-transaction.repository';
|
||||||
|
import { UserRestaurant } from './entities/user-restuarant.entity';
|
||||||
|
import { UserRestaurantRepository } from './repositories/user-restuarant.repository';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [UserService, WalletService, UserRepository, WalletTransactionRepository, PointTransactionRepository],
|
providers: [UserService, WalletService,
|
||||||
|
UserRepository, WalletTransactionRepository,
|
||||||
|
PointTransactionRepository, UserRestaurantRepository],
|
||||||
controllers: [UsersController],
|
controllers: [UsersController],
|
||||||
imports: [
|
imports: [
|
||||||
MikroOrmModule.forFeature([User, UserAddress, WalletTransaction, PointTransaction]),
|
MikroOrmModule.forFeature([User, UserAddress, WalletTransaction, PointTransaction, UserRestaurant]),
|
||||||
JwtModule,
|
JwtModule,
|
||||||
RestaurantsModule,
|
RestaurantsModule,
|
||||||
],
|
],
|
||||||
exports: [UserService, WalletService, UserRepository, WalletTransactionRepository, PointTransactionRepository],
|
exports: [UserService, WalletService, UserRepository, WalletTransactionRepository, PointTransactionRepository],
|
||||||
})
|
})
|
||||||
export class UserModule {}
|
export class UserModule { }
|
||||||
|
|||||||
Reference in New Issue
Block a user