diff --git a/apps/e-commerce/src/admin/product/product.controller.ts b/apps/e-commerce/src/admin/product/product.controller.ts index d8ed90cb..925d77b6 100644 --- a/apps/e-commerce/src/admin/product/product.controller.ts +++ b/apps/e-commerce/src/admin/product/product.controller.ts @@ -38,7 +38,7 @@ import { User } from '@rahino/database/models/core/user.entity'; export class ProductController { constructor(private service: ProductService) {} @ApiOperation({ description: 'show all products' }) - // @CheckPermission({ permissionSymbol: 'ecommerce.admin.products.getall' }) + @CheckPermission({ permissionSymbol: 'ecommerce.admin.products.getall' }) @Get('/') @ApiQuery({ name: 'filter', @@ -55,11 +55,11 @@ export class ProductController { @CheckPermission({ permissionSymbol: 'ecommerce.admin.products.getone' }) @Get('/:id') @HttpCode(HttpStatus.OK) - async findById(@Param('id') entityId: bigint) { - return await this.service.findById(entityId); + async findById(@GetUser() user: User, @Param('id') entityId: bigint) { + return await this.service.findById(user, entityId); } @ApiOperation({ description: 'create product by admin' }) - //@CheckPermission({ permissionSymbol: 'ecommerce.admin.products.create' }) + @CheckPermission({ permissionSymbol: 'ecommerce.admin.products.create' }) @Post('/') @HttpCode(HttpStatus.CREATED) async create(@GetUser() user: User, @Body() dto: ProductDto) { diff --git a/apps/e-commerce/src/admin/product/product.service.ts b/apps/e-commerce/src/admin/product/product.service.ts index e4dfd6a5..81cdd6fe 100644 --- a/apps/e-commerce/src/admin/product/product.service.ts +++ b/apps/e-commerce/src/admin/product/product.service.ts @@ -49,6 +49,7 @@ import { ECCity } from '@rahino/database/models/ecommerce-eav/ec-city.entity'; import { ECNeighborhood } from '@rahino/database/models/ecommerce-eav/ec-neighborhood.entity'; import { ECVariationPrice } from '@rahino/database/models/ecommerce-eav/ec-variation-prices'; import { inventoryStatusService } from '@rahino/ecommerce/inventory/inventory-status.service'; +import { Attachment } from '@rahino/database/models/core/attachment.entity'; @Injectable() export class ProductService { @@ -302,6 +303,12 @@ export class ProductService { ], required: false, }, + { + attributes: ['id', 'fileName'], + model: Attachment, + as: 'attachments', + required: false, + }, ]) .subQuery(false) .limit(filter.limit) @@ -320,14 +327,20 @@ export class ProductService { }; } - async findById(id: bigint) { + async findById(user: User, id: bigint) { + const vendorResult = await this.userVendorService.findAll( + user, + this.listFilter, + ); + + const vendorIds = vendorResult.result.map((vendor) => vendor.id); const product = await this.repository.findOne( new QueryOptionsBuilder() .attributes([ 'id', 'title', 'sku', - 'description', + // 'description', 'slug', 'entityTypeId', 'colorBased', @@ -356,7 +369,190 @@ export class ProductService { model: EAVEntityType, as: 'entityType', }, + { + attributes: [ + 'attributeId', + [ + Sequelize.fn( + 'isnull', + Sequelize.col('productAttributeValues.val'), + Sequelize.col('productAttributeValues.attributeValue.value'), + ), + 'val', + ], + [Sequelize.col('attributeValueId'), 'attributeValueId'], + ], + model: EAVEntityAttributeValue, + as: 'productAttributeValues', + include: [ + { + attributes: ['id', 'name', 'attributeTypeId'], + model: EAVAttribute, + as: 'attribute', + }, + { + attributes: ['id', 'attributeId', 'value'], + model: EAVAttributeValue, + as: 'attributeValue', + }, + ], + required: false, + }, + { + attributes: [ + 'id', + 'productId', + 'vendorId', + 'colorId', + 'guaranteeId', + 'guaranteeMonthId', + 'buyPrice', + 'qty', + 'onlyProvinceId', + 'vendorAddressId', + 'weight', + 'inventoryStatusId', + 'description', + ], + model: ECInventory, + as: 'inventories', + where: { + vendorId: { + [Op.in]: vendorIds, + }, + }, + include: [ + { + attributes: ['id', 'name'], + model: ECInventoryStatus, + as: 'inventoryStatus', + }, + { + attributes: ['id', 'name'], + model: ECVendor, + as: 'vendor', + }, + { + attributes: ['id', 'name', 'hexCode'], + model: ECColor, + as: 'color', + }, + { + attributes: ['id', 'name'], + model: ECGuarantee, + as: 'guarantee', + }, + { + attributes: ['id', 'name'], + model: ECGuaranteeMonth, + as: 'guaranteeMonth', + }, + { + attributes: ['id', 'name'], + model: ECProvince, + as: 'onlyProvince', + }, + { + attributes: ['id', 'vendorId', 'addressId'], + model: ECVendorAddress, + as: 'vendorAddress', + include: [ + { + attributes: [ + 'id', + 'name', + 'latitude', + 'longitude', + 'provinceId', + 'cityId', + 'neighborhoodId', + 'street', + 'alley', + 'plaque', + 'floorNumber', + ], + model: ECAddress, + as: 'address', + include: [ + { + attributes: ['id', 'name'], + model: ECProvince, + as: 'province', + }, + { + attributes: ['id', 'name'], + model: ECCity, + as: 'city', + }, + { + attributes: ['id', 'name'], + model: ECNeighborhood, + as: 'neighborhood', + }, + ], + }, + { + attributes: ['id', 'name'], + model: ECVendor, + as: 'vendor', + }, + ], + }, + { + attributes: ['price'], + model: ECInventoryPrice, + as: 'firstPrice', + include: [ + { + attributes: ['id', 'name'], + model: ECVariationPrice, + as: 'variationPrice', + }, + ], + where: Sequelize.where( + Sequelize.fn( + 'isnull', + Sequelize.col('inventories.firstPrice.isDeleted'), + 0, + ), + { + [Op.eq]: 0, + }, + ), + }, + { + attributes: ['price'], + model: ECInventoryPrice, + as: 'secondaryPrice', + include: [ + { + attributes: ['id', 'name'], + model: ECVariationPrice, + as: 'variationPrice', + }, + ], + where: Sequelize.where( + Sequelize.fn( + 'isnull', + Sequelize.col('inventories.secondaryPrice.isDeleted'), + 0, + ), + { + [Op.eq]: 0, + }, + ), + }, + ], + required: false, + }, + { + attributes: ['id', 'fileName'], + model: Attachment, + as: 'attachments', + required: false, + }, ]) + .subQuery(false) .filter( Sequelize.where( Sequelize.fn('isnull', Sequelize.col('ECProduct.isDeleted'), 0), @@ -368,6 +564,12 @@ export class ProductService { .filter({ id: id, }) + .order([ + { model: ECInventory, as: 'inventories' }, + { model: ECVendor, as: 'vendor' }, + 'priorityOrder', + 'asc', + ]) .build(), ); if (!product) { @@ -507,7 +709,7 @@ export class ProductService { } return { - result: await this.findById(product.id), + result: (await this.findById(user, product.id)).result, }; } diff --git a/apps/main/src/sql/core-v1.sql b/apps/main/src/sql/core-v1.sql index 31502744..89bb87fd 100644 --- a/apps/main/src/sql/core-v1.sql +++ b/apps/main/src/sql/core-v1.sql @@ -8444,6 +8444,150 @@ END GO +-- ecommerce/admin/products +IF NOT EXISTS ((SELECT 1 FROM Migrations WHERE version = 'CORE-Permissions-Data-v27' + )) + 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'Products' + DECLARE @groupName nvarchar(256) = N'ecommerce.admin.products' + DECLARE @findParentMenu bit = 0; + DECLARE @parentMenuName nvarchar(256) = N'محصول' + DECLARE @menuName nvarchar(256) = N'محصولات' + DECLARE @menuUrl nvarchar(512) = N'/admin/ecommerce/products' + + DECLARE @permissionSymbolShowMenu nvarchar(512) = @groupName + '.showmenu'; + DECLARE @permissionSymbolGetAll nvarchar(512) = @groupName + '.getall'; + DECLARE @permissionSymbolGetOne nvarchar(512) = @groupName + '.getone'; + DECLARE @permissionSymbolCreate nvarchar(512) = @groupName + '.create'; + DECLARE @permissionSymbolUpdate nvarchar(512) = @groupName + '.update'; + DECLARE @permissionSymbolDelete nvarchar(512) = @groupName + '.delete'; + + + + -- 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() + + INSERT INTO Permissions(permissionName ,permissionSymbol,permissionGroupId, createdAt, updatedAt) + OUTPUT inserted.id INTO @PermissionTemp(permissionId) + SELECT 'GETONE_' + @entityName, @permissionSymbolGetOne, @groupId, GETDATE(), GETDATE() + + INSERT INTO Permissions(permissionName ,permissionSymbol,permissionGroupId, createdAt, updatedAt) + OUTPUT inserted.id INTO @PermissionTemp(permissionId) + SELECT 'CREATE_' + @entityName, @permissionSymbolCreate, @groupId, GETDATE(), GETDATE() + + INSERT INTO Permissions(permissionName ,permissionSymbol,permissionGroupId, createdAt, updatedAt) + OUTPUT inserted.id INTO @PermissionTemp(permissionId) + SELECT 'UPDATE_' + @entityName, @permissionSymbolUpdate, @groupId, GETDATE(), GETDATE() + + INSERT INTO Permissions(permissionName ,permissionSymbol,permissionGroupId, createdAt, updatedAt) + OUTPUT inserted.id INTO @PermissionTemp(permissionId) + SELECT 'DELETE_' + @entityName, @permissionSymbolDelete, @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-v27', GETDATE(), GETDATE() +END + +GO + -- period types IF NOT EXISTS (SELECT 1 FROM Migrations WHERE version = 'PCMPeriodTypes-Data-v1' ) diff --git a/libs/database/src/models/eav/eav-entity-photo.entity.ts b/libs/database/src/models/eav/eav-entity-photo.entity.ts index bb6f1ae2..3fb28257 100644 --- a/libs/database/src/models/eav/eav-entity-photo.entity.ts +++ b/libs/database/src/models/eav/eav-entity-photo.entity.ts @@ -30,6 +30,9 @@ export class EAVEntityPhoto extends Model { @ForeignKey(() => Attachment) attachmentId: bigint; - @BelongsTo(() => Attachment, { as: 'attachment', foreignKey: 'attachmentId' }) + @BelongsTo(() => Attachment, { + as: 'attachment', + foreignKey: 'attachmentId', + }) attachment?: Attachment; } diff --git a/libs/database/src/models/ecommerce-eav/ec-product.entity.ts b/libs/database/src/models/ecommerce-eav/ec-product.entity.ts index f7701517..c871ae27 100644 --- a/libs/database/src/models/ecommerce-eav/ec-product.entity.ts +++ b/libs/database/src/models/ecommerce-eav/ec-product.entity.ts @@ -6,6 +6,7 @@ import { BelongsTo, ForeignKey, HasMany, + BelongsToMany, } from 'sequelize-typescript'; import { EAVEntityType } from '../eav/eav-entity-type.entity'; import { ECPublishStatus } from './ec-publish-status.entity'; @@ -16,6 +17,8 @@ import { AutoMap } from 'automapper-classes'; import { EAVEntityAttributeValue } from '../eav/eav-entity-attribute-value.entity'; import { ECInventory } from './ec-inventory.entity'; import { Op, Sequelize } from 'sequelize'; +import { Attachment } from '../core/attachment.entity'; +import { EAVEntityPhoto } from '../eav/eav-entity-photo.entity'; @Table({ tableName: 'ECProducts' }) export class ECProduct extends Model { @@ -153,4 +156,19 @@ export class ECProduct extends Model { }, }) inventories?: ECInventory[]; + + @HasMany(() => EAVEntityPhoto, { + as: 'productPhotos', + foreignKey: 'entityId', + sourceKey: 'id', + }) + productPhotos?: EAVEntityPhoto[]; + + @BelongsToMany( + () => Attachment, + () => EAVEntityPhoto, + 'entityId', + 'attachmentId', + ) + attachments?: Attachment[]; }