add: migration
This commit is contained in:
@@ -239,6 +239,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -441,6 +450,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -691,6 +709,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -919,6 +946,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -1148,6 +1184,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -1375,6 +1420,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -1556,6 +1610,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -1805,6 +1868,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -2033,6 +2105,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -2357,6 +2438,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -2566,6 +2656,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -2818,6 +2917,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -3077,6 +3185,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -3276,6 +3393,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -3710,6 +3836,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -4037,6 +4172,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -4279,6 +4423,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -4478,6 +4631,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -4842,6 +5004,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -5144,6 +5315,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -5385,6 +5565,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -5594,6 +5783,279 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"active",
|
||||
"closed",
|
||||
"archived"
|
||||
]
|
||||
},
|
||||
"message_type": {
|
||||
"name": "message_type",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"user",
|
||||
"bot",
|
||||
"system"
|
||||
]
|
||||
},
|
||||
"message_status": {
|
||||
"name": "message_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"sent",
|
||||
"delivered",
|
||||
"read",
|
||||
"failed"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "uuid"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "current_timestamp",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "current_timestamp",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"reject_reason": {
|
||||
"name": "reject_reason",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"request_status": {
|
||||
"name": "request_status",
|
||||
"type": "company_request_status",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"nativeEnumName": "company_request_status",
|
||||
"default": "'PENDING'",
|
||||
"enumItems": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
],
|
||||
"mappedType": "enum"
|
||||
},
|
||||
"company_id": {
|
||||
"name": "company_id",
|
||||
"type": "uuid",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "uuid"
|
||||
},
|
||||
"business_id": {
|
||||
"name": "business_id",
|
||||
"type": "uuid",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "uuid"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "uuid",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "uuid"
|
||||
}
|
||||
},
|
||||
"name": "company_request",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "company_request_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"company_request_company_id_foreign": {
|
||||
"constraintName": "company_request_company_id_foreign",
|
||||
"columnNames": [
|
||||
"company_id"
|
||||
],
|
||||
"localTableName": "public.company_request",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.company",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"company_request_business_id_foreign": {
|
||||
"constraintName": "company_request_business_id_foreign",
|
||||
"columnNames": [
|
||||
"business_id"
|
||||
],
|
||||
"localTableName": "public.company_request",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.business",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"company_request_user_id_foreign": {
|
||||
"constraintName": "company_request_user_id_foreign",
|
||||
"columnNames": [
|
||||
"user_id"
|
||||
],
|
||||
"localTableName": "public.company_request",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.user",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
},
|
||||
"nativeEnums": {
|
||||
"role_enum": {
|
||||
"name": "role_enum",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"ADMIN",
|
||||
"USER"
|
||||
]
|
||||
},
|
||||
"notif_type": {
|
||||
"name": "notif_type",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"OTP",
|
||||
"USER_LOGIN",
|
||||
"ANNOUNCEMENT",
|
||||
"WALLET_CHARGE",
|
||||
"WALLET_DEDUCTION",
|
||||
"BILL_INVOICE_REMINDER",
|
||||
"BILL_INVOICE",
|
||||
"APPROVE_INVOICE",
|
||||
"CREATE_INVOICE",
|
||||
"INVOICE_OVERDUE",
|
||||
"CREATE_SERVICE",
|
||||
"UNBLOCK_SERVICE",
|
||||
"BLOCK_SERVICE",
|
||||
"ANSWER_TICKET",
|
||||
"CREATE_TICKET",
|
||||
"ASSIGN_TICKET",
|
||||
"RECURRING_INVOICE",
|
||||
"PAYMENT_REMINDER",
|
||||
"PAYMENT_CANCELLATION",
|
||||
"NEW_CUSTOMER",
|
||||
"NEW_TICKET",
|
||||
"NEW_CRITICISM"
|
||||
]
|
||||
},
|
||||
"company_status": {
|
||||
"name": "company_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"APPROVED",
|
||||
"PENDING",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"ticket_status": {
|
||||
"name": "ticket_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"ANSWERED",
|
||||
"CLOSED"
|
||||
]
|
||||
},
|
||||
"ticket_priority": {
|
||||
"name": "ticket_priority",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"LOW",
|
||||
"MEDIUM",
|
||||
"HIGH"
|
||||
]
|
||||
},
|
||||
"invoice_status": {
|
||||
"name": "invoice_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"DRAFT",
|
||||
"PENDING",
|
||||
"WAIT_PAYMENT",
|
||||
"PAID",
|
||||
"ARCHIVED",
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -5803,6 +6265,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -6036,6 +6507,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -6316,6 +6796,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -6537,6 +7026,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
@@ -6644,6 +7142,15 @@
|
||||
"OVERDUE"
|
||||
]
|
||||
},
|
||||
"company_request_status": {
|
||||
"name": "company_request_status",
|
||||
"schema": "public",
|
||||
"items": [
|
||||
"PENDING",
|
||||
"APPROVED",
|
||||
"REJECTED"
|
||||
]
|
||||
},
|
||||
"chat_session_status": {
|
||||
"name": "chat_session_status",
|
||||
"schema": "public",
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20260217121029_meterageREquired extends Migration {
|
||||
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`select 1`);
|
||||
}
|
||||
|
||||
override async down(): Promise<void> {
|
||||
this.addSql(`select 1`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Migration } from "@mikro-orm/migrations";
|
||||
|
||||
export class Migration20260217122512_meterageREquired extends Migration {
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`alter table "company" alter column "metrage" type int using ("metrage"::int);`);
|
||||
this.addSql(`update "company" set "metrage" = 0 where "metrage" is null;`);
|
||||
this.addSql(`alter table "company" alter column "metrage" set not null;`);
|
||||
|
||||
this.addSql(`alter table "invoice" alter column "total_price" type numeric using ("total_price"::numeric);`);
|
||||
this.addSql(`alter table "invoice" alter column "original_price" type numeric using ("original_price"::numeric);`);
|
||||
this.addSql(`alter table "invoice" alter column "tax" type numeric using ("tax"::numeric);`);
|
||||
this.addSql(`alter table "invoice" alter column "late_fee" type numeric using ("late_fee"::numeric);`);
|
||||
|
||||
this.addSql(`alter table "payment" alter column "amount" type numeric using ("amount"::numeric);`);
|
||||
}
|
||||
|
||||
override async down(): Promise<void> {
|
||||
this.addSql(`alter table "company" alter column "metrage" type int4 using ("metrage"::int4);`);
|
||||
this.addSql(`alter table "company" alter column "metrage" drop not null;`);
|
||||
|
||||
this.addSql(`alter table "invoice" alter column "total_price" type numeric using ("total_price"::numeric);`);
|
||||
this.addSql(`alter table "invoice" alter column "original_price" type numeric using ("original_price"::numeric);`);
|
||||
this.addSql(`alter table "invoice" alter column "tax" type numeric using ("tax"::numeric);`);
|
||||
this.addSql(`alter table "invoice" alter column "late_fee" type numeric using ("late_fee"::numeric);`);
|
||||
|
||||
this.addSql(`alter table "payment" alter column "amount" type numeric using ("amount"::numeric);`);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Options } from "@mikro-orm/core";
|
||||
import { Migrator } from "@mikro-orm/migrations";
|
||||
import { PostgreSqlDriver } from "@mikro-orm/postgresql";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
@@ -6,6 +7,7 @@ dotenv.config();
|
||||
|
||||
const config: Options = {
|
||||
driver: PostgreSqlDriver,
|
||||
extensions: [Migrator],
|
||||
dbName: process.env.DB_NAME,
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT),
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Test companies seed (production-safe)
|
||||
|
||||
Add about **1000 test companies** for a specific business, then remove them in one go.
|
||||
|
||||
- **Business ID:** `e5e760b9-46a5-4194-bb3d-00a46c24d72a`
|
||||
- All test rows use the prefix **`TEST_SEED_`** (company name, emails, etc.) so they are easy to identify and delete without touching real data.
|
||||
|
||||
## 1. Seed test companies
|
||||
|
||||
From project root:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
pnpm run seed:test-companies
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- Business `e5e760b9-46a5-4194-bb3d-00a46c24d72a` must exist.
|
||||
- At least one **Industry** must exist for that business (create one in the app if needed).
|
||||
- **Role** `USER` must exist (run `pnpm run seed:run` once if you haven’t).
|
||||
|
||||
## 2. After testing: remove test data
|
||||
|
||||
Same DB, same business:
|
||||
|
||||
```bash
|
||||
pnpm run seed:cleanup-test-companies
|
||||
```
|
||||
|
||||
This **soft-deletes** all companies whose name starts with `TEST_SEED_` and their linked users for that business. No schema changes, no hard deletes.
|
||||
|
||||
## Changing business ID or count
|
||||
|
||||
Edit the constants at the top of:
|
||||
|
||||
- `database/seeders/test-companies.seeder.ts` — `TEST_BUSINESS_ID`, `TEST_COMPANIES_COUNT`
|
||||
- `database/seeders/cleanup-test-companies.seeder.ts` — `TEST_BUSINESS_ID` (must match).
|
||||
@@ -0,0 +1,44 @@
|
||||
import { EntityManager } from "@mikro-orm/postgresql";
|
||||
import { Seeder } from "@mikro-orm/seeder";
|
||||
import { Logger } from "@nestjs/common";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { TEST_SEED_PREFIX } from "./test-companies.seeder";
|
||||
import { Company } from "../../src/modules/companies/entities/company.entity";
|
||||
|
||||
/** Must match the business ID used in TestCompaniesSeeder. */
|
||||
const TEST_BUSINESS_ID = "e5e760b9-46a5-4194-bb3d-00a46c24d72a";
|
||||
|
||||
export class CleanupTestCompaniesSeeder extends Seeder {
|
||||
private readonly logger = new Logger(CleanupTestCompaniesSeeder.name);
|
||||
|
||||
async run(em: EntityManager): Promise<void> {
|
||||
this.logger.log(`Cleaning up test companies (name like '${TEST_SEED_PREFIX}%') for business ${TEST_BUSINESS_ID}...`);
|
||||
|
||||
const companies = await em.find(
|
||||
Company,
|
||||
{
|
||||
name: { $like: `${TEST_SEED_PREFIX}%` },
|
||||
business: { id: TEST_BUSINESS_ID },
|
||||
deletedAt: null,
|
||||
},
|
||||
{ populate: ["user"] },
|
||||
);
|
||||
|
||||
if (companies.length === 0) {
|
||||
this.logger.log("No test companies found. Nothing to delete.");
|
||||
return;
|
||||
}
|
||||
|
||||
const now = dayjs().toDate();
|
||||
for (const company of companies) {
|
||||
company.deletedAt = now;
|
||||
if (company.user?.deletedAt == null) {
|
||||
company.user.deletedAt = now;
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
this.logger.log(`Soft-deleted ${companies.length} test companies and their users.`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import { EntityManager } from "@mikro-orm/postgresql";
|
||||
import { Seeder } from "@mikro-orm/seeder";
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { genSalt, hash } from "bcrypt";
|
||||
|
||||
import { Business } from "../../src/modules/businesses/entities/business.entity";
|
||||
import { CompanyProduct } from "../../src/modules/companies/entities/company-product.entity";
|
||||
import { CompanyService } from "../../src/modules/companies/entities/company-service.entity";
|
||||
import { Company } from "../../src/modules/companies/entities/company.entity";
|
||||
import { CompanyStatus } from "../../src/modules/companies/enums/company-status.enum";
|
||||
import { Industry } from "../../src/modules/industries/entities/industry.entity";
|
||||
import { Role } from "../../src/modules/users/entities/role.entity";
|
||||
import { User } from "../../src/modules/users/entities/user.entity";
|
||||
import { RoleEnum } from "../../src/modules/users/enums/role.enum";
|
||||
|
||||
/** Business ID to seed test companies for. Change or use env if needed. */
|
||||
const TEST_BUSINESS_ID = "e5e760b9-46a5-4194-bb3d-00a46c24d72a";
|
||||
|
||||
/** All test companies/users use this prefix so they can be safely deleted later. */
|
||||
export const TEST_SEED_PREFIX = "TEST_SEED_";
|
||||
|
||||
const TEST_COMPANIES_COUNT = 1000;
|
||||
const BATCH_SIZE = 50;
|
||||
const TEST_PASSWORD = "TestPassword1!";
|
||||
|
||||
export class TestCompaniesSeeder extends Seeder {
|
||||
private readonly logger = new Logger(TestCompaniesSeeder.name);
|
||||
|
||||
async run(em: EntityManager): Promise<void> {
|
||||
const business = await em.findOne(Business, { id: TEST_BUSINESS_ID, deletedAt: null });
|
||||
if (!business) {
|
||||
this.logger.error(`Business not found: ${TEST_BUSINESS_ID}. Aborting.`);
|
||||
throw new Error(`Business not found: ${TEST_BUSINESS_ID}`);
|
||||
}
|
||||
|
||||
const industry = await em.findOne(Industry, { business: { id: TEST_BUSINESS_ID }, deletedAt: null });
|
||||
if (!industry) {
|
||||
this.logger.error(`No industry found for business ${TEST_BUSINESS_ID}. Create an industry first.`);
|
||||
throw new Error("No industry found for business");
|
||||
}
|
||||
|
||||
const role = await em.findOne(Role, { name: RoleEnum.USER });
|
||||
if (!role) {
|
||||
this.logger.error("USER role not found. Run default seed first (RoleSeeder).");
|
||||
throw new Error("USER role not found");
|
||||
}
|
||||
|
||||
const hashedPassword = await hash(TEST_PASSWORD, await genSalt(10));
|
||||
const baseDate = "2020-01-01";
|
||||
const baseUrl = "https://example.com";
|
||||
|
||||
this.logger.log(`Seeding ${TEST_COMPANIES_COUNT} test companies for business ${TEST_BUSINESS_ID}...`);
|
||||
|
||||
for (let i = 1; i <= TEST_COMPANIES_COUNT; i++) {
|
||||
const pad4 = String(i).padStart(4, "0");
|
||||
const pad5 = String(i).padStart(5, "0");
|
||||
const pad7 = String(i).padStart(7, "0");
|
||||
const phone = `0912${pad7}`;
|
||||
const email = `test.seed.${i}@test.local`;
|
||||
const nationalCode = `0000${pad5}`;
|
||||
const userName = `testseed${i}`;
|
||||
|
||||
const user = em.create(User, {
|
||||
firstName: "Test",
|
||||
lastName: `User${i}`,
|
||||
email,
|
||||
phone,
|
||||
userName,
|
||||
nationalCode,
|
||||
password: hashedPassword,
|
||||
role,
|
||||
business,
|
||||
});
|
||||
await em.persist(user);
|
||||
|
||||
const company = em.create(Company, {
|
||||
name: `${TEST_SEED_PREFIX}Company_${pad4}`,
|
||||
chiefExecutiveOfficer: `Test CEO ${i}`,
|
||||
email,
|
||||
phone,
|
||||
identificationNumber: `TESTSEEDID${pad5}`,
|
||||
dateOfEstablishment: new Date(baseDate),
|
||||
address: `${TEST_SEED_PREFIX} Address ${i}`,
|
||||
mapAddressLink: `${baseUrl}/map`,
|
||||
description: `${TEST_SEED_PREFIX} Description ${i}`,
|
||||
websiteUrl: `${baseUrl}`,
|
||||
profileImageUrl: `${baseUrl}/profile.png`,
|
||||
coverImageUrl: `${baseUrl}/cover.png`,
|
||||
metrage: 100,
|
||||
isActive: true,
|
||||
status: CompanyStatus.APPROVED,
|
||||
industry,
|
||||
business,
|
||||
user,
|
||||
});
|
||||
const product = em.create(CompanyProduct, {
|
||||
title: `${TEST_SEED_PREFIX}Product_${i}`,
|
||||
imageUrl: `${baseUrl}/product.png`,
|
||||
company,
|
||||
});
|
||||
const service = em.create(CompanyService, {
|
||||
title: `${TEST_SEED_PREFIX}Service_${i}`,
|
||||
imageUrl: `${baseUrl}/service.png`,
|
||||
company,
|
||||
});
|
||||
company.products.add(product);
|
||||
company.services.add(service);
|
||||
await em.persist(company);
|
||||
|
||||
if (i % BATCH_SIZE === 0) {
|
||||
await em.flush();
|
||||
this.logger.log(` ... ${i}/${TEST_COMPANIES_COUNT}`);
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
this.logger.log(`Done. Created ${TEST_COMPANIES_COUNT} test companies (name like '${TEST_SEED_PREFIX}%').`);
|
||||
this.logger.warn(`To remove them later, run: pnpm run seed:cleanup-test-companies`);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
"orm:create": "mikro-orm migration:create --config=database/mikro-orm.config.ts",
|
||||
"orm:up": "mikro-orm migration:up --config=database/mikro-orm.config.ts",
|
||||
"orm:down": "mikro-orm migration:down --config=database/mikro-orm.config.ts",
|
||||
"orm:generate": "mikro-orm migration:create --blank --config=database/mikro-orm.config.ts",
|
||||
"orm:generate": "mikro-orm migration:create --config=database/mikro-orm.config.ts",
|
||||
"orm:update": "mikro-orm schema:update --run --config=database/mikro-orm.config.ts",
|
||||
"orm:drop": "mikro-orm schema:drop --run --drop-migrations-table --drop-db --config=database/mikro-orm.config.ts",
|
||||
"orm:seed": "mikro-orm seeder:create --config=database/mikro-orm.config.ts",
|
||||
|
||||
@@ -32,11 +32,7 @@ export class BillsController {
|
||||
@UseInterceptors(FileInterceptor("file"))
|
||||
@ApiBody({ type: CreateWaterBillFromExcelDto })
|
||||
@Post("water/excel")
|
||||
createWaterBillByExcel(
|
||||
@UploadedFile() file: IFile,
|
||||
@Body() dto: CreateWaterBillFromExcelDto,
|
||||
@BusinessDec() business: Business,
|
||||
) {
|
||||
createWaterBillByExcel(@UploadedFile() file: IFile, @Body() dto: CreateWaterBillFromExcelDto, @BusinessDec() business: Business) {
|
||||
return this.billsService.createWaterBillByExcel(file, dto, business);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MikroOrmModule } from "@mikro-orm/nestjs";
|
||||
import { BullModule } from "@nestjs/bullmq";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { BillsController } from "./bills.controller";
|
||||
@@ -6,17 +7,20 @@ import { BillsService } from "./bills.service";
|
||||
import { Bill } from "./entities/bill.entity";
|
||||
import { BusinessesModule } from "../businesses/businesses.module";
|
||||
import { CompaniesModule } from "../companies/companies.module";
|
||||
import { NotificationModule } from "../notifications/notifications.module";
|
||||
import { InvoicesModule } from "../invoices/invoices.module";
|
||||
import { BullModule } from "@nestjs/bullmq";
|
||||
import { INVOICE } from "../invoices/constants";
|
||||
import { InvoicesModule } from "../invoices/invoices.module";
|
||||
import { NotificationModule } from "../notifications/notifications.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
BullModule.registerQueue({ name: INVOICE.QUEUE_NAME }),
|
||||
MikroOrmModule.forFeature([Bill]), CompaniesModule, BusinessesModule
|
||||
, NotificationModule, InvoicesModule],
|
||||
MikroOrmModule.forFeature([Bill]),
|
||||
CompaniesModule,
|
||||
BusinessesModule,
|
||||
NotificationModule,
|
||||
InvoicesModule,
|
||||
],
|
||||
controllers: [BillsController],
|
||||
providers: [BillsService],
|
||||
})
|
||||
export class BillsModule { }
|
||||
export class BillsModule {}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { EntityManager } from "@mikro-orm/postgresql";
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { Queue } from "bullmq";
|
||||
import dayjs from "dayjs";
|
||||
import Decimal from "decimal.js";
|
||||
import { Workbook } from "exceljs";
|
||||
|
||||
import { IFile } from "../utils/interfaces/IFile";
|
||||
|
||||
import { BillListQueryDto } from "./dto/bill-list-query.dto";
|
||||
import { CreateChargeBillDto } from "./dto/create-charge-bill.dto";
|
||||
import { CreateWaterBillDto, CreateWaterBillFromExcelDto } from "./dto/create-water-bill.dto";
|
||||
@@ -16,13 +16,11 @@ import { CompanyMessage } from "../../common/enums/message.enum";
|
||||
import { Business } from "../businesses/entities/business.entity";
|
||||
import { Company } from "../companies/entities/company.entity";
|
||||
import { CompaniesService } from "../companies/services/companies.service";
|
||||
import { INVOICE } from "../invoices/constants";
|
||||
import { InvoiceItem } from "../invoices/entities/invoice-item.entity";
|
||||
import { Invoice } from "../invoices/entities/invoice.entity";
|
||||
import { NotificationQueue } from "../notifications/queue/notification.queue";
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { INVOICE } from "../invoices/constants";
|
||||
import { Queue } from "bullmq";
|
||||
|
||||
import { IFile } from "../utils/interfaces/IFile";
|
||||
|
||||
@Injectable()
|
||||
export class BillsService {
|
||||
@@ -33,8 +31,7 @@ export class BillsService {
|
||||
@InjectQueue(INVOICE.QUEUE_NAME)
|
||||
private readonly invoiceQueue: Queue,
|
||||
private readonly notificationQueue: NotificationQueue,
|
||||
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async createWaterBill(dto: CreateWaterBillDto, business: Business) {
|
||||
const { values, waterRate, dueDays } = dto;
|
||||
@@ -95,8 +92,8 @@ export class BillsService {
|
||||
}
|
||||
}
|
||||
|
||||
private async addInvoiceReminderJobs(billId: string) {
|
||||
const invoices = await this.em.find(Invoice, { bill: { id: billId } }, { populate: ["items", "company","company.user"] });
|
||||
private async addInvoiceReminderJobs(billId: string) {
|
||||
const invoices = await this.em.find(Invoice, { bill: { id: billId } }, { populate: ["items", "company", "company.user"] });
|
||||
|
||||
for (const invoice of invoices) {
|
||||
await this.notificationQueue.addInvoiceCreationNotification(invoice.company.user.id, {
|
||||
@@ -196,7 +193,7 @@ export class BillsService {
|
||||
await em.flush();
|
||||
await em.commit();
|
||||
|
||||
await this.addInvoiceReminderJobs(bill.id)
|
||||
await this.addInvoiceReminderJobs(bill.id);
|
||||
|
||||
return bill;
|
||||
} catch (error) {
|
||||
@@ -223,10 +220,7 @@ export class BillsService {
|
||||
}
|
||||
|
||||
async remove(id: string, businessId: string) {
|
||||
const bill = await this.billRepository.findOne(
|
||||
{ id, business: { id: businessId } },
|
||||
{ populate: ["invoices"] },
|
||||
);
|
||||
const bill = await this.billRepository.findOne({ id, business: { id: businessId } }, { populate: ["invoices"] });
|
||||
if (!bill) {
|
||||
throw new BadRequestException("Bill not found");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,5 @@ export class Bill extends BaseEntity {
|
||||
@Property({ persist: false })
|
||||
invoiceCount?: number;
|
||||
|
||||
|
||||
[EntityRepositoryType]?: BillsRepository;
|
||||
}
|
||||
|
||||
@@ -5,65 +5,62 @@ import { BillListQueryDto } from "../dto/bill-list-query.dto";
|
||||
import { Bill } from "../entities/bill.entity";
|
||||
|
||||
export class BillsRepository extends EntityRepository<Bill> {
|
||||
|
||||
async getBillsList(queryDto: BillListQueryDto, businessId: string) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
|
||||
const knex = this.em.getKnex();
|
||||
const query = knex
|
||||
.select('b.*')
|
||||
.select(knex.raw('COUNT(i.id) as invoice_count'))
|
||||
.from('bill as b')
|
||||
.leftJoin('invoice as i', 'i.bill_id', 'b.id')
|
||||
.where('b.business_id', businessId)
|
||||
.groupBy('b.id')
|
||||
.orderBy('b.created_at', 'desc')
|
||||
.select("b.*")
|
||||
.select(knex.raw("COUNT(i.id) as invoice_count"))
|
||||
.from("bill as b")
|
||||
.leftJoin("invoice as i", "i.bill_id", "b.id")
|
||||
.where("b.business_id", businessId)
|
||||
.groupBy("b.id")
|
||||
.orderBy("b.created_at", "desc")
|
||||
.limit(limit)
|
||||
.offset(skip);
|
||||
|
||||
|
||||
// Add filters
|
||||
if (queryDto.type) {
|
||||
query.andWhere('b.type', queryDto.type);
|
||||
query.andWhere("b.type", queryDto.type);
|
||||
}
|
||||
|
||||
|
||||
if (queryDto.since) {
|
||||
query.andWhere('b.created_at', '>=', new Date(queryDto.since));
|
||||
query.andWhere("b.created_at", ">=", new Date(queryDto.since));
|
||||
}
|
||||
|
||||
|
||||
if (queryDto.to) {
|
||||
query.andWhere('b.created_at', '<=', new Date(queryDto.to));
|
||||
query.andWhere("b.created_at", "<=", new Date(queryDto.to));
|
||||
}
|
||||
|
||||
|
||||
// Execute the query
|
||||
const results = await query;
|
||||
|
||||
|
||||
// Get total count for pagination
|
||||
const countQuery = knex('bill')
|
||||
.count('* as total')
|
||||
.where('business_id', businessId);
|
||||
|
||||
const countQuery = knex("bill").count("* as total").where("business_id", businessId);
|
||||
|
||||
if (queryDto.type) {
|
||||
countQuery.andWhere('type', queryDto.type);
|
||||
countQuery.andWhere("type", queryDto.type);
|
||||
}
|
||||
|
||||
|
||||
if (queryDto.since) {
|
||||
countQuery.andWhere('created_at', '>=', new Date(queryDto.since));
|
||||
countQuery.andWhere("created_at", ">=", new Date(queryDto.since));
|
||||
}
|
||||
|
||||
|
||||
if (queryDto.to) {
|
||||
countQuery.andWhere('created_at', '<=', new Date(queryDto.to));
|
||||
countQuery.andWhere("created_at", "<=", new Date(queryDto.to));
|
||||
}
|
||||
|
||||
|
||||
const totalResult = await countQuery;
|
||||
const total = Number(totalResult[0].total);
|
||||
|
||||
|
||||
// Map results back to Bill entities (invoice_count from knex may be string/bigint)
|
||||
const bills = results.map((row) => {
|
||||
const bill = this.em.map(Bill, row);
|
||||
bill.invoiceCount = Number(row.invoice_count ?? 0);
|
||||
return bill;
|
||||
});
|
||||
|
||||
|
||||
return [bills, total];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ export class CreateCompanyRequestDto extends PickType(CreateCompanyDto, [
|
||||
"industryId",
|
||||
"products",
|
||||
"services",
|
||||
"metrage"
|
||||
"metrage",
|
||||
]) {
|
||||
@IsNotEmpty({ message: CompanyMessage.CHIEF_EXECUTIVE_REQUIRED })
|
||||
@IsString({ message: CompanyMessage.CHIEF_EXECUTIVE_MUST_BE_STRING })
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Module } from "@nestjs/common";
|
||||
|
||||
import { CompaniesController } from "./companies.controller";
|
||||
import { CompanyProduct } from "./entities/company-product.entity";
|
||||
import { CompanyRequest } from "./entities/company-request.enitiy";
|
||||
import { CompanyRequest } from "./entities/company-request.entity";
|
||||
import { CompanyService } from "./entities/company-service.entity";
|
||||
import { Company } from "./entities/company.entity";
|
||||
import { CompaniesService } from "./services/companies.service";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EntityRepository } from "@mikro-orm/postgresql";
|
||||
|
||||
import { CompanyRequest } from "../entities/company-request.enitiy";
|
||||
import { CompanyRequest } from "../entities/company-request.entity";
|
||||
|
||||
export class CompanyRequestRepository extends EntityRepository<CompanyRequest> {}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { CompanyListPublicQueryDto, CompanyListQueryDto } from "../DTO/company-l
|
||||
import { ChangeCompanyRequestStatusDto, CreateCompanyDto, CreateCompanyRequestDto } from "../DTO/create-company.dto";
|
||||
import { UpdateCompanyDto, UpdateUserCompanyDto } from "../DTO/update-company.dto";
|
||||
import { CompanyProduct } from "../entities/company-product.entity";
|
||||
import { CompanyRequest } from "../entities/company-request.enitiy";
|
||||
import { CompanyRequest } from "../entities/company-request.entity";
|
||||
import { CompanyService } from "../entities/company-service.entity";
|
||||
import { Company } from "../entities/company.entity";
|
||||
import { CompanyRequestStatus } from "../enums/company-request-status.enum";
|
||||
|
||||
Reference in New Issue
Block a user