Spaces:
Sleeping
Sleeping
Merge branch 'main' of https://github.com/PBL6-team-CATS/PBL6-informative-system into feature/frontend_update
Browse files- Dockerfile +1 -1
- README.md +1 -0
- backend/src/app.module.ts +43 -39
- backend/src/common/enums/OrderStatus.enum.ts +7 -0
- backend/src/common/enums/OrderType.enum.ts +5 -0
- backend/src/common/enums/PaymentMethod.enum.ts +5 -0
- backend/src/common/enums/role.enum.ts +7 -7
- backend/src/entities/branch-menu.entity.ts +5 -2
- backend/src/entities/branch.entity.ts +37 -35
- backend/src/entities/feed.entity.ts +2 -2
- backend/src/entities/menu-item.entity.ts +2 -1
- backend/src/entities/order-item.entity.ts +37 -0
- backend/src/entities/order.entity.ts +70 -0
- backend/src/entities/payment.entity.ts +28 -0
- backend/src/entities/user.entity.ts +55 -54
- backend/src/migrations/1729963419864-enum-role.ts +0 -22
- backend/src/migrations/1730474673934-RefactorAll.ts +56 -0
- backend/src/modules/branch-menus/branch-menus.controller.ts +55 -0
- backend/src/modules/branch-menus/branch-menus.module.ts +13 -0
- backend/src/modules/branch-menus/branch-menus.service.ts +72 -0
- backend/src/modules/branch-menus/dto/create-branch-menu.dto.ts +10 -0
- backend/src/modules/branch-menus/dto/update-branch-menu.dto.ts +4 -0
- backend/src/modules/branch/branch.controller.ts +4 -4
- backend/src/modules/branch/branch.module.ts +1 -0
- backend/src/modules/branch/branch.service.ts +11 -2
- backend/src/modules/branch/dto/create-branch.dto.ts +3 -0
- backend/src/modules/menu-item/menu-item.module.ts +1 -0
- backend/src/modules/order/dto/create-order.dto.ts +26 -0
- backend/src/modules/order/dto/order-items.dto.ts +9 -0
- backend/src/modules/order/order.controller.ts +66 -0
- backend/src/modules/order/order.module.ts +12 -0
- backend/src/modules/order/order.service.ts +137 -0
Dockerfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# Use an official Node.js runtime as a parent image
|
2 |
-
FROM node:22
|
3 |
|
4 |
# Set the working directory
|
5 |
WORKDIR /app
|
|
|
1 |
# Use an official Node.js runtime as a parent image
|
2 |
+
FROM node:22.4.0
|
3 |
|
4 |
# Set the working directory
|
5 |
WORKDIR /app
|
README.md
CHANGED
@@ -14,3 +14,4 @@ app_port: 3000
|
|
14 |
|
15 |
- [Trello](https://trello.com/w/pbl6hthngthongtin1)
|
16 |
- [Google drive](https://drive.google.com/drive/folders/19qQ5Wn4uat0hSeDX3N5c0X19abYkQLfc?usp=drive_link)
|
|
|
|
14 |
|
15 |
- [Trello](https://trello.com/w/pbl6hthngthongtin1)
|
16 |
- [Google drive](https://drive.google.com/drive/folders/19qQ5Wn4uat0hSeDX3N5c0X19abYkQLfc?usp=drive_link)
|
17 |
+
- [API docs](https://hackmd.io/NCILuSy3Rxif-KRVQZjNIg#Menu-item)
|
backend/src/app.module.ts
CHANGED
@@ -1,39 +1,43 @@
|
|
1 |
-
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
2 |
-
import { AppController } from './app.controller.js';
|
3 |
-
import { AppService } from './app.service.js';
|
4 |
-
import { ConfigModule } from '@nestjs/config';
|
5 |
-
import { TypeOrmModule } from '@nestjs/typeorm';
|
6 |
-
import { configuration } from './config/config.js';
|
7 |
-
import { DatabaseConfigService } from './config/database.js';
|
8 |
-
import { AppLoggerMiddleware } from './common/middlewares/app-logger.middleware.js';
|
9 |
-
import { DeviceInfoMiddleware } from './common/middlewares/device-info.middleware.js';
|
10 |
-
import { UserModule } from './modules/user/user.module.js';
|
11 |
-
import { BranchModule } from './modules/branch/branch.module.js';
|
12 |
-
import { AuthenticationModule } from './modules/authentication/authentication.module.js';
|
13 |
-
import { MenuItemModule } from './modules/menu-item/menu-item.module.js';
|
14 |
-
import { FeedsModule } from './modules/feeds/feeds.module.js';
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
2 |
+
import { AppController } from './app.controller.js';
|
3 |
+
import { AppService } from './app.service.js';
|
4 |
+
import { ConfigModule } from '@nestjs/config';
|
5 |
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
6 |
+
import { configuration } from './config/config.js';
|
7 |
+
import { DatabaseConfigService } from './config/database.js';
|
8 |
+
import { AppLoggerMiddleware } from './common/middlewares/app-logger.middleware.js';
|
9 |
+
import { DeviceInfoMiddleware } from './common/middlewares/device-info.middleware.js';
|
10 |
+
import { UserModule } from './modules/user/user.module.js';
|
11 |
+
import { BranchModule } from './modules/branch/branch.module.js';
|
12 |
+
import { AuthenticationModule } from './modules/authentication/authentication.module.js';
|
13 |
+
import { MenuItemModule } from './modules/menu-item/menu-item.module.js';
|
14 |
+
import { FeedsModule } from './modules/feeds/feeds.module.js';
|
15 |
+
import { OrderModule } from './modules/order/order.module.js';
|
16 |
+
import { BranchMenusModule } from './modules/branch-menus/branch-menus.module.js';
|
17 |
+
@Module({
|
18 |
+
imports: [
|
19 |
+
ConfigModule.forRoot({
|
20 |
+
isGlobal: true,
|
21 |
+
load: [configuration],
|
22 |
+
}),
|
23 |
+
TypeOrmModule.forRootAsync({
|
24 |
+
imports: [ConfigModule],
|
25 |
+
useClass: DatabaseConfigService,
|
26 |
+
}),
|
27 |
+
UserModule,
|
28 |
+
BranchModule,
|
29 |
+
AuthenticationModule,
|
30 |
+
MenuItemModule,
|
31 |
+
FeedsModule,
|
32 |
+
OrderModule,
|
33 |
+
BranchMenusModule,
|
34 |
+
],
|
35 |
+
controllers: [AppController],
|
36 |
+
providers: [AppService],
|
37 |
+
})
|
38 |
+
export class AppModule implements NestModule {
|
39 |
+
configure(consumer: MiddlewareConsumer): void {
|
40 |
+
consumer.apply(AppLoggerMiddleware).forRoutes('*');
|
41 |
+
consumer.apply(DeviceInfoMiddleware).forRoutes('*');
|
42 |
+
}
|
43 |
+
}
|
backend/src/common/enums/OrderStatus.enum.ts
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export enum OrderStatus {
|
2 |
+
PENDING = 'pending', // KH đặt hàng đã thanh toán online, nhân viên chưa xác nhận <online>
|
3 |
+
CONFIRMED = 'confirmed', // nhân viên xác nhận và chuyển sang trạng thái <online>
|
4 |
+
PREPARING = 'preparing', // nhân viên xác nhận và sang trạng thái preparing này ngay lập tức <online/offline>
|
5 |
+
DELIVERING = 'delivering', // dang giao hàng <online>
|
6 |
+
DONE = 'done', // <online, offline>
|
7 |
+
}
|
backend/src/common/enums/OrderType.enum.ts
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export enum OrderType {
|
2 |
+
TAKE_AWAY = 'take_away',
|
3 |
+
OFFLINE = 'offline',
|
4 |
+
ONLINE = 'online',
|
5 |
+
}
|
backend/src/common/enums/PaymentMethod.enum.ts
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export enum PaymentMethod {
|
2 |
+
CASH = 'cash',
|
3 |
+
CARD = 'card',
|
4 |
+
ONLINE_PAYMENT = 'online_payment',
|
5 |
+
}
|
backend/src/common/enums/role.enum.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
-
export enum Role {
|
2 |
-
CUSTOMER = 'CUSTOMER',
|
3 |
-
ADMIN = 'ADMIN',
|
4 |
-
BRANCH_MANAGER = 'BRANCH_MANAGER',
|
5 |
-
AREA_MANAGER = 'AREA_MANAGER',
|
6 |
-
STAFF = 'STAFF',
|
7 |
-
SHIPPER = 'SHIPPER'
|
8 |
}
|
|
|
1 |
+
export enum Role {
|
2 |
+
CUSTOMER = 'CUSTOMER',
|
3 |
+
ADMIN = 'ADMIN',
|
4 |
+
BRANCH_MANAGER = 'BRANCH_MANAGER',
|
5 |
+
AREA_MANAGER = 'AREA_MANAGER',
|
6 |
+
STAFF = 'STAFF',
|
7 |
+
SHIPPER = 'SHIPPER'
|
8 |
}
|
backend/src/entities/branch-menu.entity.ts
CHANGED
@@ -2,6 +2,7 @@ import {
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
Entity,
|
|
|
5 |
ManyToOne,
|
6 |
OneToMany,
|
7 |
PrimaryGeneratedColumn,
|
@@ -21,15 +22,17 @@ export class BranchMenuEntity extends BaseEntity {
|
|
21 |
@Column()
|
22 |
menu_id: string;
|
23 |
|
24 |
-
@Column()
|
25 |
description: string;
|
26 |
|
27 |
-
@Column()
|
28 |
is_open: boolean;
|
29 |
|
30 |
@ManyToOne(() => BranchEntity, (a) => a.menu_items)
|
|
|
31 |
branch: Relation<BranchEntity>;
|
32 |
|
33 |
@ManyToOne(() => MenuItemEntity, (a) => a.branch_menus)
|
|
|
34 |
menu_item: Relation<MenuItemEntity>;
|
35 |
}
|
|
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
Entity,
|
5 |
+
JoinColumn,
|
6 |
ManyToOne,
|
7 |
OneToMany,
|
8 |
PrimaryGeneratedColumn,
|
|
|
22 |
@Column()
|
23 |
menu_id: string;
|
24 |
|
25 |
+
@Column({ nullable: true })
|
26 |
description: string;
|
27 |
|
28 |
+
@Column({ default: true })
|
29 |
is_open: boolean;
|
30 |
|
31 |
@ManyToOne(() => BranchEntity, (a) => a.menu_items)
|
32 |
+
@JoinColumn({ name: 'branch_id' })
|
33 |
branch: Relation<BranchEntity>;
|
34 |
|
35 |
@ManyToOne(() => MenuItemEntity, (a) => a.branch_menus)
|
36 |
+
@JoinColumn({ name: 'menu_id' })
|
37 |
menu_item: Relation<MenuItemEntity>;
|
38 |
}
|
backend/src/entities/branch.entity.ts
CHANGED
@@ -1,35 +1,37 @@
|
|
1 |
-
import {
|
2 |
-
BaseEntity,
|
3 |
-
Column,
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
CreateDateColumn,
|
5 |
+
Entity,
|
6 |
+
ManyToOne,
|
7 |
+
OneToMany,
|
8 |
+
PrimaryColumn,
|
9 |
+
PrimaryGeneratedColumn,
|
10 |
+
Relation,
|
11 |
+
} from 'typeorm';
|
12 |
+
import { UserEntity } from './user.entity.js';
|
13 |
+
import { BranchMenuEntity } from './branch-menu.entity.js';
|
14 |
+
|
15 |
+
@Entity('branches')
|
16 |
+
export class BranchEntity extends BaseEntity {
|
17 |
+
@PrimaryColumn()
|
18 |
+
id: string;
|
19 |
+
|
20 |
+
@Column()
|
21 |
+
name: string;
|
22 |
+
|
23 |
+
@Column()
|
24 |
+
location: string;
|
25 |
+
|
26 |
+
@Column()
|
27 |
+
phone_number: string;
|
28 |
+
|
29 |
+
@ManyToOne(() => UserEntity, (user) => user.branches)
|
30 |
+
owner: Relation<UserEntity>;
|
31 |
+
|
32 |
+
@OneToMany(() => BranchMenuEntity, (a) => a.branch)
|
33 |
+
menu_items: Relation<BranchMenuEntity>[];
|
34 |
+
|
35 |
+
@CreateDateColumn()
|
36 |
+
create_at: Date;
|
37 |
+
}
|
backend/src/entities/feed.entity.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import { Entity, Column, BaseEntity, PrimaryGeneratedColumn } from 'typeorm';
|
2 |
|
3 |
@Entity('feeds')
|
4 |
export class FeedEntity extends BaseEntity {
|
@@ -17,6 +17,6 @@ export class FeedEntity extends BaseEntity {
|
|
17 |
@Column({ nullable: true })
|
18 |
description: string;
|
19 |
|
20 |
-
@
|
21 |
create_at: Date;
|
22 |
}
|
|
|
1 |
+
import { Entity, Column, BaseEntity, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
|
2 |
|
3 |
@Entity('feeds')
|
4 |
export class FeedEntity extends BaseEntity {
|
|
|
17 |
@Column({ nullable: true })
|
18 |
description: string;
|
19 |
|
20 |
+
@CreateDateColumn()
|
21 |
create_at: Date;
|
22 |
}
|
backend/src/entities/menu-item.entity.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
import {
|
2 |
BaseEntity,
|
3 |
Column,
|
|
|
4 |
Entity,
|
5 |
OneToMany,
|
6 |
PrimaryColumn,
|
@@ -32,6 +33,6 @@ export class MenuItemEntity extends BaseEntity {
|
|
32 |
@OneToMany(() => BranchMenuEntity, (a) => a.menu_item)
|
33 |
branch_menus: Relation<BranchMenuEntity>[];
|
34 |
|
35 |
-
@
|
36 |
create_at: Date;
|
37 |
}
|
|
|
1 |
import {
|
2 |
BaseEntity,
|
3 |
Column,
|
4 |
+
CreateDateColumn,
|
5 |
Entity,
|
6 |
OneToMany,
|
7 |
PrimaryColumn,
|
|
|
33 |
@OneToMany(() => BranchMenuEntity, (a) => a.menu_item)
|
34 |
branch_menus: Relation<BranchMenuEntity>[];
|
35 |
|
36 |
+
@CreateDateColumn()
|
37 |
create_at: Date;
|
38 |
}
|
backend/src/entities/order-item.entity.ts
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
Entity,
|
5 |
+
JoinColumn,
|
6 |
+
ManyToOne,
|
7 |
+
PrimaryGeneratedColumn,
|
8 |
+
Relation,
|
9 |
+
} from 'typeorm';
|
10 |
+
import { BranchMenuEntity } from './branch-menu.entity.js';
|
11 |
+
import { OrderEntity } from './order.entity.js';
|
12 |
+
|
13 |
+
@Entity('order_items')
|
14 |
+
export class OrderItemEntity extends BaseEntity {
|
15 |
+
@PrimaryGeneratedColumn()
|
16 |
+
id: number;
|
17 |
+
|
18 |
+
@Column()
|
19 |
+
order_id: number;
|
20 |
+
|
21 |
+
@ManyToOne(() => OrderEntity, (a) => a.order_items)
|
22 |
+
@JoinColumn({ name: 'order_id' })
|
23 |
+
order: Relation<OrderEntity>;
|
24 |
+
|
25 |
+
@Column()
|
26 |
+
branch_menu_id: string;
|
27 |
+
|
28 |
+
@ManyToOne(() => BranchMenuEntity)
|
29 |
+
@JoinColumn({ name: 'branch_menu_id' })
|
30 |
+
branch_menu: Relation<BranchMenuEntity>;
|
31 |
+
|
32 |
+
@Column()
|
33 |
+
quantity: number;
|
34 |
+
|
35 |
+
@Column()
|
36 |
+
price: number;
|
37 |
+
}
|
backend/src/entities/order.entity.ts
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
CreateDateColumn,
|
5 |
+
Entity,
|
6 |
+
JoinColumn,
|
7 |
+
ManyToOne,
|
8 |
+
OneToMany,
|
9 |
+
OneToOne,
|
10 |
+
PrimaryGeneratedColumn,
|
11 |
+
Relation,
|
12 |
+
} from 'typeorm';
|
13 |
+
import { BranchEntity } from './branch.entity.js';
|
14 |
+
import { UserEntity } from './user.entity.js';
|
15 |
+
import { OrderType } from '../common/enums/OrderType.enum.js';
|
16 |
+
import { OrderStatus } from '../common/enums/OrderStatus.enum.js';
|
17 |
+
import { OrderItemEntity } from './order-item.entity.js';
|
18 |
+
import { PaymentEntity } from './payment.entity.js';
|
19 |
+
|
20 |
+
@Entity('orders')
|
21 |
+
export class OrderEntity extends BaseEntity {
|
22 |
+
@PrimaryGeneratedColumn()
|
23 |
+
id: number;
|
24 |
+
|
25 |
+
@Column({ nullable: true })
|
26 |
+
customer_id: string;
|
27 |
+
|
28 |
+
@ManyToOne(() => UserEntity, { nullable: true })
|
29 |
+
@JoinColumn({ name: 'customer_id' })
|
30 |
+
customer: Relation<UserEntity>;
|
31 |
+
|
32 |
+
@Column()
|
33 |
+
branch_id: string;
|
34 |
+
|
35 |
+
@ManyToOne(() => BranchEntity)
|
36 |
+
@JoinColumn({ name: 'branch_id' })
|
37 |
+
branch: Relation<BranchEntity>;
|
38 |
+
|
39 |
+
@Column({ nullable: true })
|
40 |
+
staff_id: string;
|
41 |
+
|
42 |
+
@ManyToOne(() => UserEntity, { nullable: true })
|
43 |
+
@JoinColumn({ name: 'staff_id' })
|
44 |
+
staff: Relation<UserEntity>;
|
45 |
+
|
46 |
+
@Column({ nullable: true })
|
47 |
+
table_number: number;
|
48 |
+
|
49 |
+
@Column()
|
50 |
+
total_value: number;
|
51 |
+
|
52 |
+
@CreateDateColumn()
|
53 |
+
create_at: Date;
|
54 |
+
|
55 |
+
@Column({ type: 'enum', enum: OrderType, default: OrderType.ONLINE })
|
56 |
+
order_type: OrderType;
|
57 |
+
|
58 |
+
@Column({ type: 'enum', enum: OrderStatus, default: OrderStatus.PENDING })
|
59 |
+
order_status: OrderStatus;
|
60 |
+
|
61 |
+
@OneToMany(() => OrderItemEntity, (a) => a.order)
|
62 |
+
order_items: Relation<OrderItemEntity>[];
|
63 |
+
|
64 |
+
@Column({ nullable: true })
|
65 |
+
payment_id: number;
|
66 |
+
|
67 |
+
@OneToOne(() => PaymentEntity, (a) => a.order)
|
68 |
+
@JoinColumn({ name: 'payment_id' })
|
69 |
+
payment: Relation<PaymentEntity>;
|
70 |
+
}
|
backend/src/entities/payment.entity.ts
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
BaseEntity,
|
3 |
+
Column,
|
4 |
+
Entity,
|
5 |
+
JoinColumn,
|
6 |
+
ManyToOne,
|
7 |
+
OneToOne,
|
8 |
+
PrimaryGeneratedColumn,
|
9 |
+
Relation,
|
10 |
+
} from 'typeorm';
|
11 |
+
import { BranchMenuEntity } from './branch-menu.entity.js';
|
12 |
+
import { OrderEntity } from './order.entity.js';
|
13 |
+
import { PaymentMethod } from '../common/enums/PaymentMethod.enum.js';
|
14 |
+
|
15 |
+
@Entity('payments')
|
16 |
+
export class PaymentEntity extends BaseEntity {
|
17 |
+
@PrimaryGeneratedColumn()
|
18 |
+
id: number;
|
19 |
+
|
20 |
+
@OneToOne(() => OrderEntity, (a) => a.payment)
|
21 |
+
order: Relation<OrderEntity>;
|
22 |
+
|
23 |
+
@Column({ type: 'enum', enum: PaymentMethod, default: PaymentMethod.CASH })
|
24 |
+
payment_method: PaymentMethod; // E.g., 'Cash', 'Credit Card', 'Online Payment'
|
25 |
+
|
26 |
+
@Column()
|
27 |
+
value: number;
|
28 |
+
}
|
backend/src/entities/user.entity.ts
CHANGED
@@ -1,54 +1,55 @@
|
|
1 |
-
import {
|
2 |
-
Entity,
|
3 |
-
Column,
|
4 |
-
BaseEntity,
|
5 |
-
PrimaryGeneratedColumn,
|
6 |
-
OneToMany,
|
7 |
-
ManyToOne,
|
8 |
-
Relation,
|
9 |
-
JoinColumn,
|
10 |
-
|
11 |
-
|
12 |
-
import {
|
13 |
-
import {
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
@
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
@
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
@
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
@
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
@
|
53 |
-
|
54 |
-
|
|
|
|
1 |
+
import {
|
2 |
+
Entity,
|
3 |
+
Column,
|
4 |
+
BaseEntity,
|
5 |
+
PrimaryGeneratedColumn,
|
6 |
+
OneToMany,
|
7 |
+
ManyToOne,
|
8 |
+
Relation,
|
9 |
+
JoinColumn,
|
10 |
+
CreateDateColumn,
|
11 |
+
} from 'typeorm';
|
12 |
+
import { BranchEntity } from './branch.entity.js';
|
13 |
+
import { IsOptional } from 'class-validator';
|
14 |
+
import { Role } from '../common/enums/role.enum.js';
|
15 |
+
|
16 |
+
@Entity('users')
|
17 |
+
export class UserEntity extends BaseEntity {
|
18 |
+
@PrimaryGeneratedColumn('uuid')
|
19 |
+
id: string;
|
20 |
+
|
21 |
+
@IsOptional()
|
22 |
+
@Column({ nullable: true })
|
23 |
+
avatar: string;
|
24 |
+
|
25 |
+
@Column()
|
26 |
+
full_name: string;
|
27 |
+
|
28 |
+
@Column({ unique: true })
|
29 |
+
phone_number: string;
|
30 |
+
|
31 |
+
@IsOptional()
|
32 |
+
@Column({ nullable: true })
|
33 |
+
address: string;
|
34 |
+
|
35 |
+
@Column({ nullable: true, unique: true })
|
36 |
+
email: string;
|
37 |
+
|
38 |
+
@Column({ type: 'enum', enum: Role, default: 'CUSTOMER' })
|
39 |
+
role: Role;
|
40 |
+
|
41 |
+
@Column()
|
42 |
+
hash_password: string;
|
43 |
+
|
44 |
+
@IsOptional()
|
45 |
+
@Column({ default: true })
|
46 |
+
is_valid: boolean;
|
47 |
+
|
48 |
+
@IsOptional()
|
49 |
+
@CreateDateColumn()
|
50 |
+
create_at: Date;
|
51 |
+
|
52 |
+
@IsOptional()
|
53 |
+
@OneToMany(() => BranchEntity, (branch) => branch.owner)
|
54 |
+
branches: Relation<BranchEntity>[];
|
55 |
+
}
|
backend/src/migrations/1729963419864-enum-role.ts
DELETED
@@ -1,22 +0,0 @@
|
|
1 |
-
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
-
|
3 |
-
export class EnumRole1729963419864 implements MigrationInterface {
|
4 |
-
name = 'EnumRole1729963419864'
|
5 |
-
|
6 |
-
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
-
await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "FK_a2cecd1a3531c0b041e29ba46e1"`);
|
8 |
-
await queryRunner.query(`ALTER TABLE "users" RENAME COLUMN "role_id" TO "role"`);
|
9 |
-
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "role"`);
|
10 |
-
await queryRunner.query(`CREATE TYPE "public"."users_role_enum" AS ENUM('CUSTOMER', 'ADMIN', 'BRANCH_MANAGER', 'AREA_MANAGER', 'STAFF', 'SHIPPER')`);
|
11 |
-
await queryRunner.query(`ALTER TABLE "users" ADD "role" "public"."users_role_enum" NOT NULL DEFAULT 'CUSTOMER'`);
|
12 |
-
}
|
13 |
-
|
14 |
-
public async down(queryRunner: QueryRunner): Promise<void> {
|
15 |
-
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "role"`);
|
16 |
-
await queryRunner.query(`DROP TYPE "public"."users_role_enum"`);
|
17 |
-
await queryRunner.query(`ALTER TABLE "users" ADD "role" uuid NOT NULL DEFAULT 'f3750930-48ab-4c30-8681-d50e68e2bda7'`);
|
18 |
-
await queryRunner.query(`ALTER TABLE "users" RENAME COLUMN "role" TO "role_id"`);
|
19 |
-
await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "FK_a2cecd1a3531c0b041e29ba46e1" FOREIGN KEY ("role_id") REFERENCES "role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
20 |
-
}
|
21 |
-
|
22 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/src/migrations/1730474673934-RefactorAll.ts
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
2 |
+
|
3 |
+
export class RefactorAll1730474673934 implements MigrationInterface {
|
4 |
+
name = 'RefactorAll1730474673934'
|
5 |
+
|
6 |
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
7 |
+
await queryRunner.query(`CREATE TABLE "feeds" ("id" SERIAL NOT NULL, "author_id" character varying, "image_url" character varying, "title" character varying NOT NULL, "description" character varying, "create_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_3dafbf766ecbb1eb2017732153f" PRIMARY KEY ("id"))`);
|
8 |
+
await queryRunner.query(`CREATE TYPE "public"."users_role_enum" AS ENUM('CUSTOMER', 'ADMIN', 'BRANCH_MANAGER', 'AREA_MANAGER', 'STAFF', 'SHIPPER')`);
|
9 |
+
await queryRunner.query(`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "avatar" character varying, "full_name" character varying NOT NULL, "phone_number" character varying NOT NULL, "address" character varying, "email" character varying, "role" "public"."users_role_enum" NOT NULL DEFAULT 'CUSTOMER', "hash_password" character varying NOT NULL, "is_valid" boolean NOT NULL DEFAULT true, "create_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_17d1817f241f10a3dbafb169fd2" UNIQUE ("phone_number"), CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email"), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`);
|
10 |
+
await queryRunner.query(`CREATE TABLE "branches" ("id" character varying NOT NULL, "name" character varying NOT NULL, "location" character varying NOT NULL, "phone_number" character varying NOT NULL, "create_at" TIMESTAMP NOT NULL DEFAULT now(), "ownerId" uuid, CONSTRAINT "PK_7f37d3b42defea97f1df0d19535" PRIMARY KEY ("id"))`);
|
11 |
+
await queryRunner.query(`CREATE TYPE "public"."menu_items_item_type_enum" AS ENUM('monchinh', 'trangmieng', 'giaikhat', 'khac')`);
|
12 |
+
await queryRunner.query(`CREATE TABLE "menu_items" ("id" character varying NOT NULL, "item_name" character varying NOT NULL, "image_url" character varying, "item_type" "public"."menu_items_item_type_enum" NOT NULL DEFAULT 'khac', "description" character varying NOT NULL, "price" integer NOT NULL, "create_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_57e6188f929e5dc6919168620c8" PRIMARY KEY ("id"))`);
|
13 |
+
await queryRunner.query(`CREATE TABLE "branch_menu" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "branch_id" character varying NOT NULL, "menu_id" character varying NOT NULL, "description" character varying, "is_open" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_977becffe98bbc626a56031b9e7" PRIMARY KEY ("id"))`);
|
14 |
+
await queryRunner.query(`CREATE TYPE "public"."payments_payment_method_enum" AS ENUM('cash', 'card', 'online_payment')`);
|
15 |
+
await queryRunner.query(`CREATE TABLE "payments" ("id" SERIAL NOT NULL, "payment_method" "public"."payments_payment_method_enum" NOT NULL DEFAULT 'cash', "value" integer NOT NULL, CONSTRAINT "PK_197ab7af18c93fbb0c9b28b4a59" PRIMARY KEY ("id"))`);
|
16 |
+
await queryRunner.query(`CREATE TYPE "public"."orders_order_type_enum" AS ENUM('take_away', 'offline', 'online')`);
|
17 |
+
await queryRunner.query(`CREATE TYPE "public"."orders_order_status_enum" AS ENUM('pending', 'confirmed', 'preparing', 'delivering', 'done')`);
|
18 |
+
await queryRunner.query(`CREATE TABLE "orders" ("id" SERIAL NOT NULL, "customer_id" uuid, "branch_id" character varying NOT NULL, "staff_id" uuid, "table_number" integer, "total_value" integer NOT NULL, "create_at" TIMESTAMP NOT NULL DEFAULT now(), "order_type" "public"."orders_order_type_enum" NOT NULL DEFAULT 'online', "order_status" "public"."orders_order_status_enum" NOT NULL DEFAULT 'pending', "payment_id" integer, CONSTRAINT "REL_5b3e94bd2aedc184f9ad8c1043" UNIQUE ("payment_id"), CONSTRAINT "PK_710e2d4957aa5878dfe94e4ac2f" PRIMARY KEY ("id"))`);
|
19 |
+
await queryRunner.query(`CREATE TABLE "order_items" ("id" SERIAL NOT NULL, "order_id" integer NOT NULL, "branch_menu_id" uuid NOT NULL, "quantity" integer NOT NULL, "price" integer NOT NULL, CONSTRAINT "PK_005269d8574e6fac0493715c308" PRIMARY KEY ("id"))`);
|
20 |
+
await queryRunner.query(`ALTER TABLE "branches" ADD CONSTRAINT "FK_8c6ae9f9c654c4fac71bccbb7ed" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
21 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" ADD CONSTRAINT "FK_96fd74bed807987cf2ee5d8f168" FOREIGN KEY ("branch_id") REFERENCES "branches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
22 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" ADD CONSTRAINT "FK_703aa953158d2e80f3fbb0eb9ea" FOREIGN KEY ("menu_id") REFERENCES "menu_items"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
23 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD CONSTRAINT "FK_772d0ce0473ac2ccfa26060dbe9" FOREIGN KEY ("customer_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
24 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD CONSTRAINT "FK_17b723da2c12837f4bc21e33398" FOREIGN KEY ("branch_id") REFERENCES "branches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
25 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD CONSTRAINT "FK_40337bbb0e0cc7113dc3037fc60" FOREIGN KEY ("staff_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
26 |
+
await queryRunner.query(`ALTER TABLE "orders" ADD CONSTRAINT "FK_5b3e94bd2aedc184f9ad8c10439" FOREIGN KEY ("payment_id") REFERENCES "payments"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
27 |
+
await queryRunner.query(`ALTER TABLE "order_items" ADD CONSTRAINT "FK_145532db85752b29c57d2b7b1f1" FOREIGN KEY ("order_id") REFERENCES "orders"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
28 |
+
await queryRunner.query(`ALTER TABLE "order_items" ADD CONSTRAINT "FK_927879f38b3098216737427d2f0" FOREIGN KEY ("branch_menu_id") REFERENCES "branch_menu"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
29 |
+
}
|
30 |
+
|
31 |
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
32 |
+
await queryRunner.query(`ALTER TABLE "order_items" DROP CONSTRAINT "FK_927879f38b3098216737427d2f0"`);
|
33 |
+
await queryRunner.query(`ALTER TABLE "order_items" DROP CONSTRAINT "FK_145532db85752b29c57d2b7b1f1"`);
|
34 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP CONSTRAINT "FK_5b3e94bd2aedc184f9ad8c10439"`);
|
35 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP CONSTRAINT "FK_40337bbb0e0cc7113dc3037fc60"`);
|
36 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP CONSTRAINT "FK_17b723da2c12837f4bc21e33398"`);
|
37 |
+
await queryRunner.query(`ALTER TABLE "orders" DROP CONSTRAINT "FK_772d0ce0473ac2ccfa26060dbe9"`);
|
38 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" DROP CONSTRAINT "FK_703aa953158d2e80f3fbb0eb9ea"`);
|
39 |
+
await queryRunner.query(`ALTER TABLE "branch_menu" DROP CONSTRAINT "FK_96fd74bed807987cf2ee5d8f168"`);
|
40 |
+
await queryRunner.query(`ALTER TABLE "branches" DROP CONSTRAINT "FK_8c6ae9f9c654c4fac71bccbb7ed"`);
|
41 |
+
await queryRunner.query(`DROP TABLE "order_items"`);
|
42 |
+
await queryRunner.query(`DROP TABLE "orders"`);
|
43 |
+
await queryRunner.query(`DROP TYPE "public"."orders_order_status_enum"`);
|
44 |
+
await queryRunner.query(`DROP TYPE "public"."orders_order_type_enum"`);
|
45 |
+
await queryRunner.query(`DROP TABLE "payments"`);
|
46 |
+
await queryRunner.query(`DROP TYPE "public"."payments_payment_method_enum"`);
|
47 |
+
await queryRunner.query(`DROP TABLE "branch_menu"`);
|
48 |
+
await queryRunner.query(`DROP TABLE "menu_items"`);
|
49 |
+
await queryRunner.query(`DROP TYPE "public"."menu_items_item_type_enum"`);
|
50 |
+
await queryRunner.query(`DROP TABLE "branches"`);
|
51 |
+
await queryRunner.query(`DROP TABLE "users"`);
|
52 |
+
await queryRunner.query(`DROP TYPE "public"."users_role_enum"`);
|
53 |
+
await queryRunner.query(`DROP TABLE "feeds"`);
|
54 |
+
}
|
55 |
+
|
56 |
+
}
|
backend/src/modules/branch-menus/branch-menus.controller.ts
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Controller,
|
3 |
+
Get,
|
4 |
+
Post,
|
5 |
+
Body,
|
6 |
+
Patch,
|
7 |
+
Param,
|
8 |
+
Delete,
|
9 |
+
} from '@nestjs/common';
|
10 |
+
import { BranchMenusService } from './branch-menus.service.js';
|
11 |
+
import { CreateBranchMenuDto } from './dto/create-branch-menu.dto.js';
|
12 |
+
import { UpdateBranchMenuDto } from './dto/update-branch-menu.dto.js';
|
13 |
+
import { Public } from '../authentication/authentication.decorator.js';
|
14 |
+
import { Paginate, PaginateQuery } from 'nestjs-paginate';
|
15 |
+
|
16 |
+
@Public()
|
17 |
+
@Controller('branchs/:branchId/menus')
|
18 |
+
export class BranchMenusController {
|
19 |
+
constructor(private readonly branchMenusService: BranchMenusService) {}
|
20 |
+
|
21 |
+
@Post() // thêm menu vào branch
|
22 |
+
create(
|
23 |
+
@Param('branchId') branchId: string,
|
24 |
+
@Body() createBranchMenuDto: CreateBranchMenuDto,
|
25 |
+
) {
|
26 |
+
return this.branchMenusService.create(branchId, createBranchMenuDto);
|
27 |
+
}
|
28 |
+
|
29 |
+
@Get() // lấy danh sách menu trong branch
|
30 |
+
findAll(
|
31 |
+
@Param('branchId') branchId: string,
|
32 |
+
@Paginate() query: PaginateQuery,
|
33 |
+
) {
|
34 |
+
// console.log('branchId', branchId);
|
35 |
+
return this.branchMenusService.findAll(branchId, query);
|
36 |
+
}
|
37 |
+
|
38 |
+
@Get(':id') // lấy một menu trong branch
|
39 |
+
findOne(@Param('branchId') branchId: string, @Param('id') id: string) {
|
40 |
+
return this.branchMenusService.findOne(branchId, id);
|
41 |
+
}
|
42 |
+
|
43 |
+
@Patch(':id')
|
44 |
+
update(
|
45 |
+
@Param('id') id: string,
|
46 |
+
@Body() updateBranchMenuDto: UpdateBranchMenuDto,
|
47 |
+
) {
|
48 |
+
return this.branchMenusService.update(+id, updateBranchMenuDto);
|
49 |
+
}
|
50 |
+
|
51 |
+
@Delete(':id')
|
52 |
+
remove(@Param('id') id: string) {
|
53 |
+
return this.branchMenusService.remove(+id);
|
54 |
+
}
|
55 |
+
}
|
backend/src/modules/branch-menus/branch-menus.module.ts
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { BranchMenusService } from './branch-menus.service.js';
|
3 |
+
import { BranchMenusController } from './branch-menus.controller.js';
|
4 |
+
import { BranchModule } from '../branch/branch.module.js';
|
5 |
+
import { MenuItemModule } from '../menu-item/menu-item.module.js';
|
6 |
+
|
7 |
+
@Module({
|
8 |
+
imports: [BranchModule, MenuItemModule],
|
9 |
+
controllers: [BranchMenusController],
|
10 |
+
providers: [BranchMenusService],
|
11 |
+
exports: [BranchMenusService],
|
12 |
+
})
|
13 |
+
export class BranchMenusModule {}
|
backend/src/modules/branch-menus/branch-menus.service.ts
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Injectable } from '@nestjs/common';
|
2 |
+
import { CreateBranchMenuDto } from './dto/create-branch-menu.dto.js';
|
3 |
+
import { UpdateBranchMenuDto } from './dto/update-branch-menu.dto.js';
|
4 |
+
import { BranchService } from '../branch/branch.service.js';
|
5 |
+
import { MenuItemService } from '../menu-item/menu-item.service.js';
|
6 |
+
import { BranchMenuEntity } from '../../entities/branch-menu.entity.js';
|
7 |
+
import { paginate, PaginateConfig, PaginateQuery } from 'nestjs-paginate';
|
8 |
+
import { isUUID } from 'class-validator';
|
9 |
+
|
10 |
+
@Injectable()
|
11 |
+
export class BranchMenusService {
|
12 |
+
constructor(
|
13 |
+
private readonly branchService: BranchService,
|
14 |
+
private readonly menuItemService: MenuItemService,
|
15 |
+
) {}
|
16 |
+
async create(branchId: string, createBranchMenuDto: CreateBranchMenuDto) {
|
17 |
+
const branch = await this.branchService.getBranchOrError(branchId);
|
18 |
+
const menuItem = await this.menuItemService.getMenuItemOrError(
|
19 |
+
createBranchMenuDto.menu_id,
|
20 |
+
);
|
21 |
+
if (createBranchMenuDto.description) {
|
22 |
+
return await BranchMenuEntity.create({
|
23 |
+
...createBranchMenuDto,
|
24 |
+
branch_id: branchId,
|
25 |
+
}).save();
|
26 |
+
} else {
|
27 |
+
return await BranchMenuEntity.create({
|
28 |
+
...createBranchMenuDto,
|
29 |
+
branch_id: branchId,
|
30 |
+
description: menuItem.description,
|
31 |
+
}).save();
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
async findAll(branchId: string, query: PaginateQuery) {
|
36 |
+
const paginateConfig: PaginateConfig<BranchMenuEntity> = {
|
37 |
+
sortableColumns: ['id', 'branch_id', 'menu_id', 'description'],
|
38 |
+
nullSort: 'last',
|
39 |
+
defaultSortBy: [['id', 'DESC']],
|
40 |
+
searchableColumns: ['description'],
|
41 |
+
filterableColumns: {
|
42 |
+
// price: [],
|
43 |
+
// item_type: [FilterOperator.EQ],
|
44 |
+
},
|
45 |
+
};
|
46 |
+
return paginate(
|
47 |
+
query,
|
48 |
+
BranchMenuEntity.createQueryBuilder('bm')
|
49 |
+
.leftJoinAndSelect('bm.menu_item', 'menu_item')
|
50 |
+
.where('bm.branch_id = :branchId', { branchId: branchId }),
|
51 |
+
paginateConfig,
|
52 |
+
);
|
53 |
+
}
|
54 |
+
|
55 |
+
async findOne(branchId: string, id: string) {
|
56 |
+
if (isUUID(id)) return await BranchMenuEntity.findOneBy({ id });
|
57 |
+
else {
|
58 |
+
return await BranchMenuEntity.findOne({
|
59 |
+
where: { branch_id: branchId, menu_id: id },
|
60 |
+
relations: ['menu_item'],
|
61 |
+
});
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
update(id: number, updateBranchMenuDto: UpdateBranchMenuDto) {
|
66 |
+
return `This action updates a #${id} branchMenu`;
|
67 |
+
}
|
68 |
+
|
69 |
+
remove(id: number) {
|
70 |
+
return `This action removes a #${id} branchMenu`;
|
71 |
+
}
|
72 |
+
}
|
backend/src/modules/branch-menus/dto/create-branch-menu.dto.ts
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsOptional, IsString } from 'class-validator';
|
2 |
+
|
3 |
+
export class CreateBranchMenuDto {
|
4 |
+
@IsString()
|
5 |
+
menu_id: string;
|
6 |
+
|
7 |
+
@IsString()
|
8 |
+
@IsOptional()
|
9 |
+
description?: string;
|
10 |
+
}
|
backend/src/modules/branch-menus/dto/update-branch-menu.dto.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { PartialType } from '@nestjs/mapped-types';
|
2 |
+
import { CreateBranchMenuDto } from './create-branch-menu.dto.js';
|
3 |
+
|
4 |
+
export class UpdateBranchMenuDto extends PartialType(CreateBranchMenuDto) {}
|
backend/src/modules/branch/branch.controller.ts
CHANGED
@@ -10,8 +10,10 @@ import {
|
|
10 |
import { BranchService } from './branch.service.js';
|
11 |
import { CreateBranchDto } from './dto/create-branch.dto.js';
|
12 |
import { UpdateBranchDto } from './dto/update-branch.dto.js';
|
|
|
13 |
|
14 |
-
@
|
|
|
15 |
export class BranchController {
|
16 |
constructor(private readonly branchService: BranchService) {}
|
17 |
|
@@ -47,7 +49,5 @@ export class BranchController {
|
|
47 |
async addMenuItemToBranch(@Param('id') id: string) {}
|
48 |
|
49 |
@Get(':id/menu-items')
|
50 |
-
async getMenuItemWithBranchId(@Param('id') id: string) {
|
51 |
-
|
52 |
-
}
|
53 |
}
|
|
|
10 |
import { BranchService } from './branch.service.js';
|
11 |
import { CreateBranchDto } from './dto/create-branch.dto.js';
|
12 |
import { UpdateBranchDto } from './dto/update-branch.dto.js';
|
13 |
+
import { Public } from '../authentication/authentication.decorator.js';
|
14 |
|
15 |
+
@Public()
|
16 |
+
@Controller('branchs')
|
17 |
export class BranchController {
|
18 |
constructor(private readonly branchService: BranchService) {}
|
19 |
|
|
|
49 |
async addMenuItemToBranch(@Param('id') id: string) {}
|
50 |
|
51 |
@Get(':id/menu-items')
|
52 |
+
async getMenuItemWithBranchId(@Param('id') id: string) {}
|
|
|
|
|
53 |
}
|
backend/src/modules/branch/branch.module.ts
CHANGED
@@ -5,5 +5,6 @@ import { BranchController } from './branch.controller.js';
|
|
5 |
@Module({
|
6 |
controllers: [BranchController],
|
7 |
providers: [BranchService],
|
|
|
8 |
})
|
9 |
export class BranchModule {}
|
|
|
5 |
@Module({
|
6 |
controllers: [BranchController],
|
7 |
providers: [BranchService],
|
8 |
+
exports: [BranchService],
|
9 |
})
|
10 |
export class BranchModule {}
|
backend/src/modules/branch/branch.service.ts
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
-
import {
|
|
|
|
|
|
|
|
|
2 |
import { CreateBranchDto } from './dto/create-branch.dto.js';
|
3 |
import { BranchEntity } from '../../entities/branch.entity.js';
|
4 |
import { Public } from '../authentication/authentication.decorator.js';
|
@@ -9,6 +13,10 @@ import { plainToClass } from 'class-transformer';
|
|
9 |
@Injectable()
|
10 |
export class BranchService {
|
11 |
async create(createBranchDto: CreateBranchDto) {
|
|
|
|
|
|
|
|
|
12 |
return await BranchEntity.create({ ...createBranchDto }).save();
|
13 |
}
|
14 |
|
@@ -21,9 +29,10 @@ export class BranchService {
|
|
21 |
}
|
22 |
|
23 |
async getBranchOrError(id: string) {
|
|
|
24 |
const branch = await BranchEntity.findOneBy({ id });
|
25 |
if (!branch) {
|
26 |
-
throw new NotFoundException('
|
27 |
}
|
28 |
return branch;
|
29 |
}
|
|
|
1 |
+
import {
|
2 |
+
BadRequestException,
|
3 |
+
Injectable,
|
4 |
+
NotFoundException,
|
5 |
+
} from '@nestjs/common';
|
6 |
import { CreateBranchDto } from './dto/create-branch.dto.js';
|
7 |
import { BranchEntity } from '../../entities/branch.entity.js';
|
8 |
import { Public } from '../authentication/authentication.decorator.js';
|
|
|
13 |
@Injectable()
|
14 |
export class BranchService {
|
15 |
async create(createBranchDto: CreateBranchDto) {
|
16 |
+
const branch = await BranchEntity.findOneBy({ id: createBranchDto.id });
|
17 |
+
if (branch) {
|
18 |
+
throw new BadRequestException('Branch already exists');
|
19 |
+
}
|
20 |
return await BranchEntity.create({ ...createBranchDto }).save();
|
21 |
}
|
22 |
|
|
|
29 |
}
|
30 |
|
31 |
async getBranchOrError(id: string) {
|
32 |
+
console.log(id);
|
33 |
const branch = await BranchEntity.findOneBy({ id });
|
34 |
if (!branch) {
|
35 |
+
throw new NotFoundException('Branch not found');
|
36 |
}
|
37 |
return branch;
|
38 |
}
|
backend/src/modules/branch/dto/create-branch.dto.ts
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
import { IsString } from 'class-validator';
|
2 |
|
3 |
export class CreateBranchDto {
|
|
|
|
|
|
|
4 |
@IsString()
|
5 |
name: string;
|
6 |
|
|
|
1 |
import { IsString } from 'class-validator';
|
2 |
|
3 |
export class CreateBranchDto {
|
4 |
+
@IsString()
|
5 |
+
id: string;
|
6 |
+
|
7 |
@IsString()
|
8 |
name: string;
|
9 |
|
backend/src/modules/menu-item/menu-item.module.ts
CHANGED
@@ -5,5 +5,6 @@ import { MenuItemController } from './menu-item.controller.js';
|
|
5 |
@Module({
|
6 |
controllers: [MenuItemController],
|
7 |
providers: [MenuItemService],
|
|
|
8 |
})
|
9 |
export class MenuItemModule {}
|
|
|
5 |
@Module({
|
6 |
controllers: [MenuItemController],
|
7 |
providers: [MenuItemService],
|
8 |
+
exports: [MenuItemService],
|
9 |
})
|
10 |
export class MenuItemModule {}
|
backend/src/modules/order/dto/create-order.dto.ts
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
IsArray,
|
3 |
+
IsEnum,
|
4 |
+
IsNumber,
|
5 |
+
IsObject,
|
6 |
+
IsOptional,
|
7 |
+
IsString,
|
8 |
+
ValidateNested,
|
9 |
+
} from 'class-validator';
|
10 |
+
import { OrderType } from '../../../common/enums/OrderType.enum.js';
|
11 |
+
import { OrderItemsDto } from './order-items.dto.js';
|
12 |
+
import { Type } from 'class-transformer';
|
13 |
+
|
14 |
+
export class CreateOrderDto {
|
15 |
+
@IsOptional()
|
16 |
+
@IsNumber()
|
17 |
+
table_number?: number;
|
18 |
+
|
19 |
+
@IsEnum(OrderType)
|
20 |
+
order_type: OrderType;
|
21 |
+
|
22 |
+
@IsArray()
|
23 |
+
@ValidateNested()
|
24 |
+
@Type(() => OrderItemsDto)
|
25 |
+
order_items: OrderItemsDto[];
|
26 |
+
}
|
backend/src/modules/order/dto/order-items.dto.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsNumber, IsString } from 'class-validator';
|
2 |
+
|
3 |
+
export class OrderItemsDto {
|
4 |
+
@IsString()
|
5 |
+
menu_id: string;
|
6 |
+
|
7 |
+
@IsNumber()
|
8 |
+
quantity: number;
|
9 |
+
}
|
backend/src/modules/order/order.controller.ts
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Controller,
|
3 |
+
Get,
|
4 |
+
Post,
|
5 |
+
Body,
|
6 |
+
Patch,
|
7 |
+
Param,
|
8 |
+
Delete,
|
9 |
+
Req,
|
10 |
+
} from '@nestjs/common';
|
11 |
+
import { OrderService } from './order.service.js';
|
12 |
+
import { CreateOrderDto } from './dto/create-order.dto.js';
|
13 |
+
import { Role } from '../../common/enums/role.enum.js';
|
14 |
+
|
15 |
+
@Controller('branchs/:branchId/orders')
|
16 |
+
export class OrderController {
|
17 |
+
constructor(private readonly orderService: OrderService) {}
|
18 |
+
|
19 |
+
@Post()
|
20 |
+
async create(
|
21 |
+
@Param('branchId') branchId: string,
|
22 |
+
@Req() req: Request,
|
23 |
+
@Body() createOrderDto: CreateOrderDto,
|
24 |
+
) {
|
25 |
+
const userId = req['user'].sub;
|
26 |
+
const role = req['user'].roles;
|
27 |
+
console.log(req['user']);
|
28 |
+
if (role == Role.CUSTOMER)
|
29 |
+
return this.orderService.createFromCustomer(
|
30 |
+
branchId,
|
31 |
+
userId,
|
32 |
+
createOrderDto,
|
33 |
+
);
|
34 |
+
else
|
35 |
+
return this.orderService.createFromStaff(
|
36 |
+
branchId,
|
37 |
+
userId,
|
38 |
+
createOrderDto,
|
39 |
+
);
|
40 |
+
}
|
41 |
+
|
42 |
+
@Get()
|
43 |
+
async findAll(@Req() req: Request) {
|
44 |
+
const userId = req['user'].sub;
|
45 |
+
console.log(req['user']);
|
46 |
+
return this.orderService.findAll();
|
47 |
+
}
|
48 |
+
|
49 |
+
@Get(':id')
|
50 |
+
async findOne(@Param('id') id: string) {
|
51 |
+
return this.orderService.findOne(+id);
|
52 |
+
}
|
53 |
+
|
54 |
+
// @Patch(':id')
|
55 |
+
// async update(
|
56 |
+
// @Param('id') id: string,
|
57 |
+
// @Body() updateOrderDto: UpdateOrderDto,
|
58 |
+
// ) {
|
59 |
+
// return this.orderService.update(+id, updateOrderDto);
|
60 |
+
// }
|
61 |
+
|
62 |
+
@Delete(':id')
|
63 |
+
remove(@Param('id') id: string) {
|
64 |
+
return this.orderService.remove(+id);
|
65 |
+
}
|
66 |
+
}
|
backend/src/modules/order/order.module.ts
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Module } from '@nestjs/common';
|
2 |
+
import { OrderService } from './order.service.js';
|
3 |
+
import { OrderController } from './order.controller.js';
|
4 |
+
import { BranchModule } from '../branch/branch.module.js';
|
5 |
+
import { BranchMenusModule } from '../branch-menus/branch-menus.module.js';
|
6 |
+
|
7 |
+
@Module({
|
8 |
+
imports: [BranchModule, BranchMenusModule],
|
9 |
+
controllers: [OrderController],
|
10 |
+
providers: [OrderService],
|
11 |
+
})
|
12 |
+
export class OrderModule {}
|
backend/src/modules/order/order.service.ts
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { BadRequestException, Injectable } from '@nestjs/common';
|
2 |
+
import { CreateOrderDto } from './dto/create-order.dto.js';
|
3 |
+
import { UserEntity } from '../../entities/user.entity.js';
|
4 |
+
import { OrderEntity } from '../../entities/order.entity.js';
|
5 |
+
import { BranchService } from '../branch/branch.service.js';
|
6 |
+
import { OrderType } from '../../common/enums/OrderType.enum.js';
|
7 |
+
import { isUUID } from 'class-validator';
|
8 |
+
import { OrderItemEntity } from '../../entities/order-item.entity.js';
|
9 |
+
import { BranchMenuEntity } from '../../entities/branch-menu.entity.js';
|
10 |
+
import { OrderStatus } from '../../common/enums/OrderStatus.enum.js';
|
11 |
+
|
12 |
+
@Injectable()
|
13 |
+
export class OrderService {
|
14 |
+
constructor(private readonly branchService: BranchService) {}
|
15 |
+
async createFromCustomer(
|
16 |
+
branchId: string,
|
17 |
+
userId: string,
|
18 |
+
createOrderDto: CreateOrderDto,
|
19 |
+
) {
|
20 |
+
console.log('??');
|
21 |
+
if (createOrderDto.order_type != OrderType.ONLINE) {
|
22 |
+
throw new BadRequestException('customer cannot create offline order');
|
23 |
+
}
|
24 |
+
|
25 |
+
const user = await UserEntity.findOneBy({ id: userId });
|
26 |
+
if (!user) {
|
27 |
+
throw new BadRequestException('User not found');
|
28 |
+
}
|
29 |
+
const branch = await this.branchService.getBranchOrError(branchId);
|
30 |
+
if (!branch) {
|
31 |
+
throw new BadRequestException('Branch not found');
|
32 |
+
}
|
33 |
+
const order = OrderEntity.create();
|
34 |
+
order.branch = branch;
|
35 |
+
order.customer = user;
|
36 |
+
order.order_type = createOrderDto.order_type;
|
37 |
+
order.order_status = OrderStatus.PENDING;
|
38 |
+
order.total_value = 0;
|
39 |
+
await order.save();
|
40 |
+
|
41 |
+
let orderItems: OrderItemEntity[] = [];
|
42 |
+
let totalValue = 0;
|
43 |
+
for (const item of createOrderDto.order_items) {
|
44 |
+
let branchMenu: BranchMenuEntity;
|
45 |
+
if (!isUUID(item.menu_id)) {
|
46 |
+
branchMenu = await BranchMenuEntity.findOne({
|
47 |
+
where: { branch_id: branchId, menu_id: item.menu_id },
|
48 |
+
relations: ['menu_item'],
|
49 |
+
});
|
50 |
+
} else {
|
51 |
+
branchMenu = await BranchMenuEntity.findOne({
|
52 |
+
where: { branch_id: branchId, id: item.menu_id },
|
53 |
+
relations: ['menu_item'],
|
54 |
+
});
|
55 |
+
}
|
56 |
+
if (!branchMenu) {
|
57 |
+
throw new BadRequestException('Item not found in branch menu');
|
58 |
+
}
|
59 |
+
const orderItem = OrderItemEntity.create();
|
60 |
+
orderItem.branch_menu = branchMenu;
|
61 |
+
orderItem.price = branchMenu.menu_item.price;
|
62 |
+
orderItem.quantity = item.quantity;
|
63 |
+
orderItem.order_id = order.id;
|
64 |
+
totalValue += orderItem.price * orderItem.quantity;
|
65 |
+
orderItems.push(orderItem);
|
66 |
+
}
|
67 |
+
await order.save();
|
68 |
+
order.total_value = totalValue;
|
69 |
+
await OrderItemEntity.save(orderItems);
|
70 |
+
return { ...order, order_items: orderItems };
|
71 |
+
}
|
72 |
+
|
73 |
+
async createFromStaff(
|
74 |
+
branchId: string,
|
75 |
+
userId: string,
|
76 |
+
createOrderDto: CreateOrderDto,
|
77 |
+
) {
|
78 |
+
if (createOrderDto.order_type == OrderType.ONLINE) {
|
79 |
+
throw new BadRequestException('staff cannot create online order');
|
80 |
+
}
|
81 |
+
// staff
|
82 |
+
const staff = await UserEntity.findOneBy({ id: userId });
|
83 |
+
const branch = await this.branchService.getBranchOrError(branchId);
|
84 |
+
const order = OrderEntity.create();
|
85 |
+
|
86 |
+
order.branch = branch;
|
87 |
+
order.staff = staff;
|
88 |
+
order.order_type = createOrderDto.order_type;
|
89 |
+
order.table_number = createOrderDto.table_number;
|
90 |
+
order.order_status = OrderStatus.PREPARING;
|
91 |
+
order.total_value = 0;
|
92 |
+
await order.save();
|
93 |
+
|
94 |
+
let orderItems: OrderItemEntity[] = [];
|
95 |
+
let totalValue = 0;
|
96 |
+
for (const item of createOrderDto.order_items) {
|
97 |
+
let branchMenu: BranchMenuEntity;
|
98 |
+
if (!isUUID(item.menu_id)) {
|
99 |
+
branchMenu = await BranchMenuEntity.findOne({
|
100 |
+
where: { branch_id: branchId, menu_id: item.menu_id },
|
101 |
+
relations: ['menu_item'],
|
102 |
+
});
|
103 |
+
} else {
|
104 |
+
branchMenu = await BranchMenuEntity.findOne({
|
105 |
+
where: { branch_id: branchId, id: item.menu_id },
|
106 |
+
relations: ['menu_item'],
|
107 |
+
});
|
108 |
+
}
|
109 |
+
if (!branchMenu) {
|
110 |
+
throw new BadRequestException('Item not found in branch menu');
|
111 |
+
}
|
112 |
+
const orderItem = OrderItemEntity.create();
|
113 |
+
orderItem.branch_menu = branchMenu;
|
114 |
+
orderItem.price = branchMenu.menu_item.price;
|
115 |
+
orderItem.quantity = item.quantity;
|
116 |
+
orderItem.order_id = order.id;
|
117 |
+
totalValue += orderItem.price * orderItem.quantity;
|
118 |
+
orderItems.push(orderItem);
|
119 |
+
}
|
120 |
+
await order.save();
|
121 |
+
order.total_value = totalValue;
|
122 |
+
await OrderItemEntity.save(orderItems);
|
123 |
+
return { ...order, order_items: orderItems };
|
124 |
+
}
|
125 |
+
|
126 |
+
findAll() {
|
127 |
+
return `This action returns all order`;
|
128 |
+
}
|
129 |
+
|
130 |
+
findOne(id: number) {
|
131 |
+
return `This action returns a #${id} order`;
|
132 |
+
}
|
133 |
+
|
134 |
+
remove(id: number) {
|
135 |
+
return `This action removes a #${id} order`;
|
136 |
+
}
|
137 |
+
}
|