From 661051cb6fe1215bee74c81654c7e820ac1ea582 Mon Sep 17 00:00:00 2001 From: bahram rajabi Date: Tue, 2 Jul 2024 22:16:35 +0330 Subject: [PATCH] add product sales query --- apps/e-commerce/src/e-commerce.module.ts | 2 + .../report/admin-sale/admin-sale.service.ts | 1 - .../product-sale/dto/get-vendor-sale.dto.ts | 8 + .../src/report/product-sale/dto/index.ts | 1 + .../product-sale/dto/vendor-sale-dto.ts | 34 +++ .../product-sale/product-sale.controller.ts | 49 ++++ .../product-sale/product-sale.module.ts | 21 ++ .../product-sale/product-sale.service.ts | 129 +++++++++++ .../sale-query-builder.service.ts | 5 + apps/main/src/routes/app.module.ts | 17 ++ apps/main/src/sql/core-v1.sql | 126 ++++++++++ .../query-options.builder.ts | 5 + package-lock.json | 217 ++++++++++++++++-- package.json | 2 + 14 files changed, 599 insertions(+), 18 deletions(-) create mode 100644 apps/e-commerce/src/report/product-sale/dto/get-vendor-sale.dto.ts create mode 100644 apps/e-commerce/src/report/product-sale/dto/index.ts create mode 100644 apps/e-commerce/src/report/product-sale/dto/vendor-sale-dto.ts create mode 100644 apps/e-commerce/src/report/product-sale/product-sale.controller.ts create mode 100644 apps/e-commerce/src/report/product-sale/product-sale.module.ts create mode 100644 apps/e-commerce/src/report/product-sale/product-sale.service.ts diff --git a/apps/e-commerce/src/e-commerce.module.ts b/apps/e-commerce/src/e-commerce.module.ts index 58f4a12e..f0e9569f 100644 --- a/apps/e-commerce/src/e-commerce.module.ts +++ b/apps/e-commerce/src/e-commerce.module.ts @@ -72,6 +72,7 @@ import { ProductCommentStatusModule } from './admin/product-comment-status/produ import { OrderStatusModule } from './admin/order-status/order-status.module'; import { OrderShipmentWayModule } from './admin/order-shipmentway/order-shipmentway.module'; import { AdminAddressModule } from './admin/address/address.module'; +import { ProductSaleModule } from './report/product-sale/product-sale.module'; @Module({ imports: [ @@ -146,6 +147,7 @@ import { AdminAddressModule } from './admin/address/address.module'; OrderStatusModule, OrderShipmentWayModule, AdminAddressModule, + ProductSaleModule, ], providers: [ { diff --git a/apps/e-commerce/src/report/admin-sale/admin-sale.service.ts b/apps/e-commerce/src/report/admin-sale/admin-sale.service.ts index e4238b78..b3af6f36 100644 --- a/apps/e-commerce/src/report/admin-sale/admin-sale.service.ts +++ b/apps/e-commerce/src/report/admin-sale/admin-sale.service.ts @@ -100,7 +100,6 @@ export class AdminSaleService { .includeProduct() .includeInventory() .includeVendor() - .offset(filter.offset) .limit(filter.limit); diff --git a/apps/e-commerce/src/report/product-sale/dto/get-vendor-sale.dto.ts b/apps/e-commerce/src/report/product-sale/dto/get-vendor-sale.dto.ts new file mode 100644 index 00000000..bbd12d76 --- /dev/null +++ b/apps/e-commerce/src/report/product-sale/dto/get-vendor-sale.dto.ts @@ -0,0 +1,8 @@ +import { IntersectionType } from '@nestjs/swagger'; +import { ListFilter } from '@rahino/query-filter'; +import { VendorSaleDto } from './vendor-sale-dto'; + +export class GetVendorSaleDto extends IntersectionType( + VendorSaleDto, + ListFilter, +) {} diff --git a/apps/e-commerce/src/report/product-sale/dto/index.ts b/apps/e-commerce/src/report/product-sale/dto/index.ts new file mode 100644 index 00000000..09d41351 --- /dev/null +++ b/apps/e-commerce/src/report/product-sale/dto/index.ts @@ -0,0 +1 @@ +export * from './get-vendor-sale.dto'; diff --git a/apps/e-commerce/src/report/product-sale/dto/vendor-sale-dto.ts b/apps/e-commerce/src/report/product-sale/dto/vendor-sale-dto.ts new file mode 100644 index 00000000..e6dbeb82 --- /dev/null +++ b/apps/e-commerce/src/report/product-sale/dto/vendor-sale-dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { I18nTranslations } from 'apps/main/src/generated/i18n.generated'; +import { Type } from 'class-transformer'; +import { IsInt, IsNumber, IsOptional, IsString } from 'class-validator'; +import { i18nValidationMessage } from 'nestjs-i18n'; + +export class VendorSaleDto { + @ApiProperty({ + required: true, + type: IsString, + description: 'beginDate', + }) + @IsString() + beginDate: string; + + @ApiProperty({ + required: true, + type: IsString, + description: 'endDate', + }) + @IsString() + endDate: string; + + @ApiProperty({ + required: true, + type: IsNumber, + description: 'vendorId', + }) + @IsInt({ + message: i18nValidationMessage('validation.NUMBER'), + }) + @Type(() => Number) + vendorId: number; +} diff --git a/apps/e-commerce/src/report/product-sale/product-sale.controller.ts b/apps/e-commerce/src/report/product-sale/product-sale.controller.ts new file mode 100644 index 00000000..f7995288 --- /dev/null +++ b/apps/e-commerce/src/report/product-sale/product-sale.controller.ts @@ -0,0 +1,49 @@ +import { + Controller, + Get, + HttpCode, + HttpStatus, + Query, + UseGuards, + UseInterceptors, +} from '@nestjs/common'; +import { CheckPermission } from '@rahino/permission-checker/decorator'; +import { PermissionGuard } from '@rahino/permission-checker/guard'; +import { JsonResponseTransformInterceptor } from '@rahino/response/interceptor'; +import { + ApiBearerAuth, + ApiOperation, + ApiQuery, + ApiTags, +} from '@nestjs/swagger'; +import { JwtGuard } from '@rahino/auth/guard'; +import { ProductSaleService } from './product-sale.service'; +import { GetUser } from '@rahino/auth/decorator'; +import { User } from '@rahino/database/models/core/user.entity'; +import { GetVendorSaleDto } from './dto'; + +@ApiTags('Report-ProductSales') +@UseGuards(JwtGuard, PermissionGuard) +@UseInterceptors(JsonResponseTransformInterceptor) +@ApiBearerAuth() +@Controller({ + path: '/api/ecommerce/report/productSales', + version: ['1'], +}) +export class ProductSaleController { + constructor(private service: ProductSaleService) {} + + @ApiOperation({ description: 'show all product sales' }) + @CheckPermission({ permissionSymbol: 'ecommerce.report.productsales.getall' }) + @Get('/') + @ApiQuery({ + name: 'filter', + type: GetVendorSaleDto, + style: 'deepObject', + explode: true, + }) + @HttpCode(HttpStatus.OK) + async findAll(@GetUser() user: User, @Query() filter: GetVendorSaleDto) { + return await this.service.findAll(user, filter); + } +} diff --git a/apps/e-commerce/src/report/product-sale/product-sale.module.ts b/apps/e-commerce/src/report/product-sale/product-sale.module.ts new file mode 100644 index 00000000..22f0f9cd --- /dev/null +++ b/apps/e-commerce/src/report/product-sale/product-sale.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common'; +import { ProductSaleService } from './product-sale.service'; +import { ProductSaleController } from './product-sale.controller'; +import { SequelizeModule } from '@nestjs/sequelize'; +import { User } from '@rahino/database/models/core/user.entity'; +import { Permission } from '@rahino/database/models/core/permission.entity'; +import { ECOrderDetail } from '@rahino/database/models/ecommerce-eav/ec-order-detail.entity'; +import { PersianDate } from '@rahino/database/models/core/view/persiandate.entity'; +import { SaleQueryBuilderModule } from '../sale-query-builder/sale-query-builder.module'; +import { UserVendorModule } from '@rahino/ecommerce/user/vendor/user-vendor.module'; + +@Module({ + imports: [ + SaleQueryBuilderModule, + SequelizeModule.forFeature([User, Permission, ECOrderDetail, PersianDate]), + UserVendorModule, + ], + controllers: [ProductSaleController], + providers: [ProductSaleService], +}) +export class ProductSaleModule {} diff --git a/apps/e-commerce/src/report/product-sale/product-sale.service.ts b/apps/e-commerce/src/report/product-sale/product-sale.service.ts new file mode 100644 index 00000000..15272888 --- /dev/null +++ b/apps/e-commerce/src/report/product-sale/product-sale.service.ts @@ -0,0 +1,129 @@ +import { + BadRequestException, + ForbiddenException, + Injectable, +} from '@nestjs/common'; +import { GetVendorSaleDto } from './dto'; +import { InjectModel } from '@nestjs/sequelize'; +import { PersianDate } from '@rahino/database/models/core/view/persiandate.entity'; +import { QueryOptionsBuilder } from '@rahino/query-filter/sequelize-query-builder'; +import { I18nContext, I18nService } from 'nestjs-i18n'; +import { I18nTranslations } from 'apps/main/src/generated/i18n.generated'; +import { User } from '@rahino/database/models/core/user.entity'; +import { UserVendorService } from '@rahino/ecommerce/user/vendor/user-vendor.service'; +import { Knex } from 'knex'; +import { InjectKnex } from 'nestjs-knex'; +import { OrderStatusEnum } from '@rahino/ecommerce/util/enum'; + +@Injectable() +export class ProductSaleService { + constructor( + @InjectModel(PersianDate) + private readonly persianDateRepository: typeof PersianDate, + private readonly i18n: I18nService, + private readonly userVendorService: UserVendorService, + @InjectKnex() private readonly knex: Knex, + ) {} + + async findAll(user: User, filter: GetVendorSaleDto) { + const isAccessToVendor = await this.userVendorService.isAccessToVendor( + user, + filter.vendorId, + ); + if (!isAccessToVendor) { + throw new ForbiddenException( + this.i18n.t('ecommerce.dont_access_to_this_vendor', { + lang: I18nContext.current().lang, + }), + ); + } + const isValidBeginDate = await this.isValidDate(filter.beginDate); + if (!isValidBeginDate) { + throw new BadRequestException( + this.i18n.t('ecommerce.date_is_invalid', { + lang: I18nContext.current().lang, + }), + ); + } + const isValidEndDate = await this.isValidDate(filter.endDate); + if (!isValidEndDate) { + throw new BadRequestException( + this.i18n.t('ecommerce.date_is_invalid', { + lang: I18nContext.current().lang, + }), + ); + } + + const result = await this.knex('ECOrderDetails') + .leftJoin('ECOrders', 'ECOrderDetails.orderId', 'ECOrders.id') + .leftJoin('ECProducts', 'ECOrderDetails.productId', 'ECProducts.id') + .leftJoin('ECVendors', 'ECOrderDetails.vendorId', 'ECVendors.id') + .select( + 'ECOrderDetails.vendorId', + 'ECOrderDetails.productId', + this.knex.raw('ECProducts.title as productTitle'), + this.knex.raw('ECProducts.sku as productSku'), + this.knex.raw('ECProducts.slug as productSlug'), + this.knex.raw('ECVendors.name as vendorName'), + this.knex.raw('ECVendors.slug as vendorSlug'), + this.knex.raw('SUM(ECOrderDetails.qty) as qty'), + ) + .where('ECOrders.orderStatusId', '!=', OrderStatusEnum.WaitingForPayment) + .where('ECOrderDetails.gregorianAtPersian', '>=', filter.beginDate) + .where('ECOrderDetails.gregorianAtPersian', '<=', filter.endDate) + .modify(function (queryBuilder) { + if (filter.vendorId) { + queryBuilder.where('ECOrderDetails.vendorId', filter.vendorId); + } + }) + .groupBy( + 'ECOrderDetails.vendorId', + 'ECOrderDetails.productId', + 'ECProducts.title', + 'ECProducts.sku', + 'ECProducts.slug', + 'ECVendors.name', + 'ECVendors.slug', + ) + .orderBy('qty', 'desc') + .limit(filter.limit) + .offset(filter.offset); + + const countQuery = await this.knex('ECOrderDetails') + .leftJoin('ECOrders', 'ECOrderDetails.orderId', 'ECOrders.id') + .leftJoin('ECProducts', 'ECOrderDetails.productId', 'ECProducts.id') + .leftJoin('ECVendors', 'ECOrderDetails.vendorId', 'ECVendors.id') + .select(this.knex.raw('COUNT(*) OVER () AS totalRecords')) + .where('ECOrders.orderStatusId', '!=', OrderStatusEnum.WaitingForPayment) + .where('ECOrderDetails.gregorianAtPersian', '>=', filter.beginDate) + .where('ECOrderDetails.gregorianAtPersian', '<=', filter.endDate) + .modify(function (queryBuilder) { + if (filter.vendorId) { + queryBuilder.where('ECOrderDetails.vendorId', filter.vendorId); + } + }) + .groupBy( + 'ECOrderDetails.vendorId', + 'ECOrderDetails.productId', + 'ECProducts.title', + 'ECProducts.sku', + 'ECProducts.slug', + 'ECVendors.name', + 'ECVendors.slug', + ) + .first(); + + return { + result: result, + total: countQuery['totalRecords'], + }; + } + + async isValidDate(date: string) { + const findDate = await this.persianDateRepository.findOne( + new QueryOptionsBuilder().filter({ GregorianDate: date }).build(), + ); + if (!findDate) return false; + return true; + } +} diff --git a/apps/e-commerce/src/report/sale-query-builder/sale-query-builder.service.ts b/apps/e-commerce/src/report/sale-query-builder/sale-query-builder.service.ts index ccc7ed3c..04073bc0 100644 --- a/apps/e-commerce/src/report/sale-query-builder/sale-query-builder.service.ts +++ b/apps/e-commerce/src/report/sale-query-builder/sale-query-builder.service.ts @@ -192,6 +192,11 @@ export class SaleQueryBuilderService { return this; } + nest(flag: boolean) { + this.builder = this.builder.nest(flag); + return this; + } + group(group: GroupOption) { this.builder = this.builder.group(group); return this; diff --git a/apps/main/src/routes/app.module.ts b/apps/main/src/routes/app.module.ts index acd22abb..f1077720 100644 --- a/apps/main/src/routes/app.module.ts +++ b/apps/main/src/routes/app.module.ts @@ -36,6 +36,7 @@ import * as path from 'path'; import { AppLanguageResolver } from '../i18nResolver/AppLanguageResolver'; import { ECommerceSmsModule } from '@rahino/ecommerce/util/sms/ecommerce-sms.module'; import { ECommmerceSmsService } from '@rahino/ecommerce/util/sms/ecommerce-sms.service'; +import { KnexModule } from 'nestjs-knex'; @Module({ imports: [ @@ -68,6 +69,22 @@ import { ECommmerceSmsService } from '@rahino/ecommerce/util/sms/ecommerce-sms.s ], }), DatabaseModule, + KnexModule.forRootAsync({ + useFactory: (config: ConfigService) => ({ + config: { + client: 'mssql', + useNullAsDefault: true, + connection: { + host: config.get('DB_HOST'), + port: Number(config.get('DB_PORT')), + user: config.get('DB_USER'), + password: config.get('DB_PASS'), + database: config.get('DB_NAME_DEVELOPMENT'), + }, + }, + }), + inject: [ConfigService], + }), DBLoggerModule, AutomapperModule.forRoot({ strategyInitializer: classes(), diff --git a/apps/main/src/sql/core-v1.sql b/apps/main/src/sql/core-v1.sql index 2c468097..cd2c8d92 100644 --- a/apps/main/src/sql/core-v1.sql +++ b/apps/main/src/sql/core-v1.sql @@ -14304,6 +14304,132 @@ END GO + +-- ecommerce/admin/repoort/productSales +IF NOT EXISTS ((SELECT 1 FROM Migrations WHERE version = 'CORE-Permissions-Data-v59' + )) + AND EXISTS ( + SELECT 1 FROM Settings WHERE 1=1 + AND ([key] = 'SITE_NAME' AND [value] IN ('ecommerce')) + ) +BEGIN + + DECLARE @roleId int = (SELECT TOP 1 id FROM Roles WHERE static_id = 1) + DECLARE @userId bigint = (SELECT TOP 1 id FROM Users WHERE static_id = 1) + + DECLARE @GroupTemp TABLE ( + gorupId int + ); + + DECLARE @groupId int = null; + + DECLARE @entityName nvarchar(256) = N'ReportProductSales' + DECLARE @groupName nvarchar(256) = N'ecommerce.report.productsales' + DECLARE @findParentMenu bit = 1; + DECLARE @parentMenuName nvarchar(256) = N'گزارشات' + DECLARE @menuName nvarchar(256) = N'گزارش تعداد فروش کالا' + DECLARE @menuUrl nvarchar(512) = N'/admin/ecommerce/report/productSales' + + DECLARE @permissionSymbolShowMenu nvarchar(512) = @groupName + '.showmenu'; + DECLARE @permissionSymbolGetAll nvarchar(512) = @groupName + '.getall'; + + + + + -- permission groups + INSERT INTO PermissionGroups(permissionGroupName, [visibility], createdAt, updatedAt) + OUTPUT inserted.id INTO @GroupTemp(gorupId) + SELECT @groupName, 1, GETDATE(), GETDATE(); + + SELECT @groupId = gorupId FROM @GroupTemp + + + -- permissions + + + DECLARE @PermissionTemp TABLE ( + permissionId int + ); + + INSERT INTO Permissions(permissionName ,permissionSymbol,permissionGroupId, createdAt, updatedAt) + OUTPUT inserted.id INTO @PermissionTemp(permissionId) + SELECT 'GETALL_' + @entityName, @permissionSymbolGetAll, @groupId, GETDATE(), GETDATE() + + + -- CRUD THIS Enity FOR super-admin + INSERT INTO RolePermissions(roleId, permissionId, createdAt, updatedAt) + SELECT @roleId, permissionId, GETDATE(), GETDATE() + FROM @PermissionTemp + + DELETE FROM @PermissionTemp + + INSERT INTO Permissions(permissionName ,permissionSymbol, permissionGroupId,createdAt, updatedAt) + OUTPUT inserted.id INTO @PermissionTemp(permissionId) + SELECT 'SHOWMENU_' + @entityName, @permissionSymbolShowMenu, @groupId,GETDATE(), GETDATE() + + INSERT INTO RolePermissions(roleId, permissionId, createdAt, updatedAt) + SELECT @roleId, permissionId, GETDATE(), GETDATE() + FROM @PermissionTemp + + DECLARE @permissionId int = null + SELECT @permissionId = permissionId FROM @PermissionTemp + + + + + DECLARE @parentMenuId int = null + + + + IF @findParentMenu = 0 + BEGIN + -- INSERT ParentMenu + DECLARE @ParentMenuTemp TABLE ( + menuId int + ); + + INSERT INTO Menus(title, url, className, visibility, createdAt, updatedAt) + OUTPUT inserted.id INTO @ParentMenuTemp(menuId) + SELECT @parentMenuName, null, null, null, GETDATE(), GETDATE() + + SELECT @parentMenuId = menuId FROM @ParentMenuTemp + + END + ELSE + BEGIN + SELECT @parentMenuId = id + FROM Menus + WHERE title = @parentMenuName + END + + IF @parentMenuId IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM PermissionMenus WHERE permissionId = @permissionId AND menuId = @parentMenuId) + BEGIN + INSERT INTO PermissionMenus(permissionId, menuId, createdAt, updatedAt) + SELECT @permissionId, @parentMenuId, getdate(), getdate() + + END + + DECLARE @MenuTemp TABLE ( + menuId int + ); + DECLARE @menuId int = null + + INSERT INTO Menus(title, url, parentMenuId, className, visibility, createdAt, updatedAt) + OUTPUT inserted.id INTO @MenuTemp(menuId) + SELECT @menuName, @menuUrl, @parentMenuId,null, null, GETDATE(), GETDATE() + + SELECT @menuId = menuId FROM @MenuTemp + + INSERT INTO PermissionMenus(permissionId, menuId, createdAt, updatedAt) + SELECT @permissionId, @menuId, getdate(), getdate() + + INSERT INTO Migrations(version, createdAt, updatedAt) + SELECT 'CORE-Permissions-Data-v59', GETDATE(), GETDATE() +END + +GO + -- period types IF NOT EXISTS (SELECT 1 FROM Migrations WHERE version = 'PCMPeriodTypes-Data-v1' ) diff --git a/libs/query-filter/src/sequelize-query-builder/query-options.builder.ts b/libs/query-filter/src/sequelize-query-builder/query-options.builder.ts index 721a4929..c7437a22 100644 --- a/libs/query-filter/src/sequelize-query-builder/query-options.builder.ts +++ b/libs/query-filter/src/sequelize-query-builder/query-options.builder.ts @@ -85,6 +85,11 @@ export class QueryOptionsBuilder { this.options.raw = flag; return this; } + + nest(flag: boolean) { + this.options.nest = flag; + return this; + } } function isOrderCol(x: any) { diff --git a/package-lock.json b/package-lock.json index bbb14aac..e5960a1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,12 +43,14 @@ "http-proxy-middleware": "^2.0.6", "ioredis": "^5.3.2", "iterate": "^0.1.1", + "knex": "^3.1.0", "loadash": "^1.0.0", "moment": "^2.30.1", "moment-jalaali": "^0.10.0", "moment-timezone": "^0.5.45", "mssql": "^10.0.1", "nestjs-i18n": "^10.4.5", + "nestjs-knex": "^2.0.0", "nestjs-minio-client": "^2.2.0", "passport-jwt": "^4.0.1", "persian-date": "^1.1.0", @@ -4528,6 +4530,11 @@ "color-support": "bin.js" } }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -5369,7 +5376,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -5548,6 +5554,14 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -6345,7 +6359,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, "engines": { "node": ">=8.0.0" } @@ -6376,6 +6389,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -6983,7 +7001,6 @@ "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -8131,6 +8148,91 @@ "node": ">=6" } }, + "node_modules/knex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/knex/node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/knex/node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/knex/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -8750,6 +8852,16 @@ "rxjs": "*" } }, + "node_modules/nestjs-knex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nestjs-knex/-/nestjs-knex-2.0.0.tgz", + "integrity": "sha512-W495gmpSlcGeuDFcURb7knjTLTxi4A6gjlSewRCWNnfDoBOzORzTexIl+V+LxZUiI0IBMqcP7HUcAa7QYuzZEQ==", + "peerDependencies": { + "@nestjs/common": ">=6.7.0", + "@nestjs/core": ">=6.7.0", + "knex": ">=0.95.4" + } + }, "node_modules/nestjs-minio-client": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nestjs-minio-client/-/nestjs-minio-client-2.2.0.tgz", @@ -9188,8 +9300,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.10.1", @@ -9946,7 +10057,6 @@ "version": "1.22.6", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -11005,7 +11115,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -11362,6 +11471,14 @@ "node": ">= 6" } }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "engines": { + "node": ">=8" + } + }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -15666,6 +15783,11 @@ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -16284,8 +16406,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -16405,6 +16526,11 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -17019,8 +17145,7 @@ "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" }, "get-stream": { "version": "6.0.1", @@ -17036,6 +17161,11 @@ "get-intrinsic": "^1.1.1" } }, + "getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" + }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -17452,7 +17582,6 @@ "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -18305,6 +18434,52 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "knex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", + "requires": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "dependencies": { + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" + }, + "rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "requires": { + "resolve": "^1.20.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -18789,6 +18964,12 @@ "string-format": "^2.0.0" } }, + "nestjs-knex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nestjs-knex/-/nestjs-knex-2.0.0.tgz", + "integrity": "sha512-W495gmpSlcGeuDFcURb7knjTLTxi4A6gjlSewRCWNnfDoBOzORzTexIl+V+LxZUiI0IBMqcP7HUcAa7QYuzZEQ==", + "requires": {} + }, "nestjs-minio-client": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nestjs-minio-client/-/nestjs-minio-client-2.2.0.tgz", @@ -19094,8 +19275,7 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-scurry": { "version": "1.10.1", @@ -19661,7 +19841,6 @@ "version": "1.22.6", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", - "dev": true, "requires": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -20424,8 +20603,7 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "swagger-ui-dist": { "version": "5.9.0", @@ -20688,6 +20866,11 @@ } } }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, "titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", diff --git a/package.json b/package.json index e158a2fb..45719c39 100644 --- a/package.json +++ b/package.json @@ -54,12 +54,14 @@ "http-proxy-middleware": "^2.0.6", "ioredis": "^5.3.2", "iterate": "^0.1.1", + "knex": "^3.1.0", "loadash": "^1.0.0", "moment": "^2.30.1", "moment-jalaali": "^0.10.0", "moment-timezone": "^0.5.45", "mssql": "^10.0.1", "nestjs-i18n": "^10.4.5", + "nestjs-knex": "^2.0.0", "nestjs-minio-client": "^2.2.0", "passport-jwt": "^4.0.1", "persian-date": "^1.1.0",