space product service

This commit is contained in:
hannathkadher
2024-12-17 19:08:48 +04:00
parent b3e8af7540
commit d1050babd1
9 changed files with 212 additions and 22 deletions

View File

@ -24,7 +24,7 @@ export class SubspaceEntity extends AbstractEntity<SubspaceDto> {
nullable: false,
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'space_id' })
@JoinColumn({ name: 'space_uuid' })
space: SpaceEntity;
@OneToMany(() => DeviceEntity, (device) => device.subspace, {

View File

@ -1,7 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
ArrayNotEmpty,
IsArray,
IsBoolean,
IsNotEmpty,
@ -43,7 +42,6 @@ export class ProductAssignmentDto {
example: [{ tag: 'Light 1' }, { tag: 'Light 2' }, { tag: 'Light 3' }],
})
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => CreateSpaceProductItemDto)
items: CreateSpaceProductItemDto[];

View File

@ -78,6 +78,27 @@ export class SpaceProductItemService {
queryRunner: QueryRunner,
space: SpaceEntity,
) {
const query = `
SELECT spi.tag
FROM "space-product-item" spi
INNER JOIN "space-product" spm
ON spi.space_product_uuid = spm.uuid
WHERE spm.space_uuid = $1
UNION
SELECT spi.tag
FROM "subspace-product-item" spi
INNER JOIN "subspace-product" spm
ON spi.subspace_product_uuid = spm.uuid
INNER JOIN "subspace" sm
ON spm.subspace_uuid = sm.uuid
WHERE sm.space_uuid = $1;
`;
const result = await queryRunner.manager.query(query, [space.uuid]);
console.log(result);
const incomingTags = itemModelDtos.map((item) => item.tag);
const duplicateTags = incomingTags.filter(
@ -90,13 +111,7 @@ export class SpaceProductItemService {
);
}
const existingTags = await queryRunner.manager.find(
this.spaceProductItemRepository.target,
{
where: { spaceProduct: { space } },
select: ['tag'],
},
);
const existingTags = await queryRunner.manager.query(query, [space.uuid]);
const existingTagSet = new Set(existingTags.map((item) => item.tag));
const conflictingTags = incomingTags.filter((tag) =>

View File

@ -14,7 +14,7 @@ export class SpaceProductService {
private readonly spaceProductItemService: SpaceProductItemService,
) {}
async createProductItemFromModel(
async createFromModel(
spaceModel: SpaceModelEntity,
space: SpaceEntity,
queryRunner: QueryRunner,

View File

@ -81,7 +81,7 @@ export class SpaceService {
}
if (subspaces?.length) {
await this.subSpaceService.createSubspacesFromNames(
await this.subSpaceService.createSubspacesFromDto(
subspaces,
newSpace,
queryRunner,
@ -101,7 +101,7 @@ export class SpaceService {
queryRunner,
);
} else if (spaceModel && spaceModel.spaceProductModels.length) {
await this.spaceProductService.createProductItemFromModel(
await this.spaceProductService.createFromModel(
spaceModel,
newSpace,
queryRunner,

View File

@ -1,9 +1,17 @@
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { QueryRunner } from 'typeorm';
import { SubspaceProductEntity } from '@app/common/modules/space';
import { SubspaceProductModelEntity } from '@app/common/modules/space-model';
import {
SpaceEntity,
SubspaceProductEntity,
SubspaceProductItemEntity,
} from '@app/common/modules/space';
import {
SubspaceProductItemModelEntity,
SubspaceProductModelEntity,
} from '@app/common/modules/space-model';
import { SubspaceProductItemRepository } from '@app/common/modules/space/repositories/subspace.repository';
import { CreateSpaceProductItemDto } from 'src/space/dtos';
@Injectable()
export class SubspaceProductItemService {
@ -39,12 +47,98 @@ export class SubspaceProductItemService {
private createProductItem(
product: SubspaceProductEntity,
model: any,
model: SubspaceProductItemModelEntity,
queryRunner: QueryRunner,
): Partial<any> {
return queryRunner.manager.create(this.productItemRepository.target, {
tag: model.tag,
product,
model,
});
}
async createItemFromDtos(
product: SubspaceProductEntity,
itemDto: CreateSpaceProductItemDto[],
queryRunner: QueryRunner,
space: SpaceEntity,
) {
if (!itemDto?.length) return;
try {
await this.validateTags(itemDto, queryRunner, space);
const productItems = itemDto.map((dto) =>
queryRunner.manager.create(SubspaceProductItemEntity, {
tag: dto.tag,
subspaceProduct: product,
}),
);
await queryRunner.manager.save(
this.productItemRepository.target,
productItems,
);
} catch (error) {
throw new HttpException(
error.message || 'An error occurred while creating product items.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
private async validateTags(
subspaceItemModelDtos: CreateSpaceProductItemDto[],
queryRunner: QueryRunner,
space: SpaceEntity,
) {
const incomingTags = subspaceItemModelDtos.map((item) => item.tag);
const duplicateTags = incomingTags.filter(
(tag, index) => incomingTags.indexOf(tag) !== index,
);
if (duplicateTags.length > 0) {
throw new HttpException(
`Duplicate tags found in the request: ${[...new Set(duplicateTags)].join(', ')}`,
HttpStatus.BAD_REQUEST,
);
}
const existingTagsQuery = `
SELECT spi.tag
FROM "space-product-item" spi
INNER JOIN "space-product" spm
ON spi.space_product_uuid = spm.uuid
WHERE spm.space_uuid = $1
UNION
SELECT spi.tag
FROM "subspace-product-item" spi
INNER JOIN "subspace-product" spm
ON spi.subspace_product_uuid = spm.uuid
INNER JOIN "subspace" sm
ON spm.subspace_uuid = sm.uuid
WHERE sm.space_uuid = $1;
`;
const existingTags = await queryRunner.manager.query(existingTagsQuery, [
space.uuid,
]);
console.log(existingTags);
const existingTagsSet = new Set(
existingTags.map((row: { tag: string }) => row.tag),
);
const conflictingTags = [...incomingTags].filter((tag) =>
existingTagsSet.has(tag),
);
if (conflictingTags.length > 0) {
throw new HttpException(
`Tags already exist in the model: ${conflictingTags.join(', ')}`,
HttpStatus.CONFLICT,
);
}
}
}

View File

@ -2,6 +2,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { QueryRunner } from 'typeorm';
import {
SpaceEntity,
SubspaceEntity,
SubspaceProductEntity,
} from '@app/common/modules/space';
@ -10,11 +11,15 @@ import {
SubspaceProductModelEntity,
} from '@app/common/modules/space-model';
import { SubspaceProductItemService } from './subspace-product-item.service';
import { ProductAssignmentDto } from 'src/space/dtos';
import { ProductService } from 'src/product/services';
import { ProductEntity } from '@app/common/modules/product/entities';
@Injectable()
export class SubspaceProductService {
constructor(
private readonly subspaceProductItemService: SubspaceProductItemService,
private readonly productService: ProductService,
) {}
async createFromModel(
@ -63,4 +68,61 @@ export class SubspaceProductService {
model: productModel,
};
}
async createFromDto(
productDtos: ProductAssignmentDto[],
subspace: SubspaceEntity,
queryRunner: QueryRunner,
space: SpaceEntity,
): Promise<void> {
try {
const newSpaceProducts = await Promise.all(
productDtos.map(async (dto) => {
this.validateProductCount(dto);
const product = await this.getProduct(dto.productId);
return queryRunner.manager.create(SubspaceProductEntity, {
subspace,
product,
productCount: dto.count,
});
}),
);
const subspaceProducts = await queryRunner.manager.save(
SubspaceProductEntity,
newSpaceProducts,
);
await Promise.all(
productDtos.map((dto, index) =>
this.subspaceProductItemService.createItemFromDtos(
subspaceProducts[index],
dto.items,
queryRunner,
space,
),
),
);
} catch (error) {
throw new HttpException(
`Failed to create subspace products from DTOs. Error: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getProduct(productId: string): Promise<ProductEntity> {
const product = await this.productService.findOne(productId);
return product.data;
}
async validateProductCount(dto: ProductAssignmentDto) {
if (dto.count !== dto.items.length) {
throw new HttpException(
'Producy item and count doesnot match',
HttpStatus.BAD_REQUEST,
);
}
}
}

View File

@ -79,17 +79,36 @@ export class SubSpaceService {
);
}
async createSubspacesFromNames(
async createSubspacesFromDto(
addSubspaceDtos: AddSubspaceDto[],
space: SpaceEntity,
queryRunner: QueryRunner,
): Promise<SubspaceEntity[]> {
const subspaceData = addSubspaceDtos.map((dto) => ({
subspaceName: dto.subspaceName,
space,
}));
try {
const subspaceData = addSubspaceDtos.map((dto) => ({
subspaceName: dto.subspaceName,
space,
}));
return await this.createSubspaces(subspaceData, queryRunner);
const subspaces = await this.createSubspaces(subspaceData, queryRunner);
await Promise.all(
addSubspaceDtos.map((dto, index) =>
this.productService.createFromDto(
dto.products,
subspaces[index],
queryRunner,
space,
),
),
);
return subspaces;
} catch (error) {
throw new Error(
`Transaction failed: Unable to create subspaces and products. ${error.message}`,
);
}
}
async createSubspace(

View File

@ -50,6 +50,7 @@ import { CommunityModule } from 'src/community/community.module';
import { ValidationService } from './services';
import {
SubspaceProductItemRepository,
SubspaceProductRepository,
SubspaceRepository,
} from '@app/common/modules/space/repositories/subspace.repository';
import { SubspaceProductService } from './services';
@ -99,6 +100,7 @@ import { SubspaceProductService } from './services';
SpaceProductItemRepository,
SubspaceProductService,
SubspaceProductItemService,
SubspaceProductRepository,
],
exports: [SpaceService],
})