import { HttpException, HttpStatus } from '@nestjs/common'; import { EntityNotFoundError, IsNull, Repository } from 'typeorm'; import { Product } from './entities/product.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { CreateProductDto } from './dto/product/create-product.dto'; import { ProductSubCategoriesService } from './product-sub-categories.service'; import { UpdateProductDto } from './dto/product/update-product.dto'; import { ProductHistoryPrice } from './entities/product-history-price.entity'; import { productType } from '../helper/enum-list'; import { UpdatePriceProductDto } from './dto/product/update-price-product.dto'; import { UsersService } from '../users/users.service'; import { SupplierService } from '../users/supplier/supplier.service'; import { parsingFile } from '../helper/csv-parser'; import { PartnerService } from '../users/partner/partner.service'; import { mapSeries } from 'bluebird'; export class ProductService { constructor( @InjectRepository(Product) private productRepository: Repository, @InjectRepository(ProductHistoryPrice) private productHistoryPrice: Repository, private productSubCategoriesService: ProductSubCategoriesService, private usersService: UsersService, private supplierService: SupplierService, private partnerService: PartnerService, ) {} async create(createProductDto: CreateProductDto) { const subCategories = await this.productSubCategoriesService.findOne( createProductDto.subCategoriesId, ); const result = await this.productRepository.insert({ name: createProductDto.name, code: createProductDto.code, status: createProductDto.status, sub_categories: subCategories, price: createProductDto.price, }); await this.productHistoryPrice.insert({ product: result.identifiers[0], type: productType.NORMAL, price: createProductDto.price, mark_up_price: createProductDto.markUpPrice, startDate: new Date(), endDate: null, }); return this.productRepository.findOneOrFail(result.identifiers[0].id); } async processUploadCSV(uploadFile: string, supplierCode: string) { const supplierData = await this.supplierService.findByCode(supplierCode); const data = await parsingFile(uploadFile); data.shift(); await mapSeries(data, async (it) => { let dataHistoryPrice; let partnerData; const subCategories = await this.productSubCategoriesService.findOneForCSVParser(it[2]); if (!subCategories) { return; } const productData = await this.productRepository.findOne({ code: it[0], supplier: supplierData, }); if (productData) { //TODO : Handle Update Product productData.name = it[1]; productData.status = it[5] == 'active' ? 'ACTIVE' : 'NOT ACTIVE'; await this.productRepository.save(productData); //TODO : Handle History Price if (it[6] != '-' && it[6] != '') { partnerData = await this.partnerService.findOne(it[6]); dataHistoryPrice = await this.productHistoryPrice.findOne({ where: { product: productData.id, partner: partnerData.id, endDate: IsNull(), }, }); } else { dataHistoryPrice = await this.productHistoryPrice.findOne({ product: productData, partner: IsNull(), endDate: IsNull(), }); } if (dataHistoryPrice) { await this.productHistoryPrice.update(dataHistoryPrice.id, { endDate: new Date(), }); } await this.productHistoryPrice.insert({ product: productData, mark_up_price: it[4], price: it[3], type: productType.NORMAL, startDate: new Date(), partner: it[6] != '-' ? partnerData : null, }); } else { let partnerData; if (it[6] != '-' && it[6] != '') { partnerData = await this.partnerService.findOne(it[6]); } const savedProduct = await this.productRepository.insert({ name: it[1], code: it[0], status: it[5] == 'active' ? 'ACTIVE' : 'NOT ACTIVE', sub_categories: subCategories, supplier: supplierData, }); return await this.productHistoryPrice.insert({ product: savedProduct.identifiers[0], mark_up_price: it[4], price: it[3], type: productType.NORMAL, startDate: new Date(), endDate: null, partner: partnerData, }); } }); return data; } async findAll( page: number, supplier: string, subCategories: string, pageSize?: number, ) { let filterSupplier, filterSubCategories; if (supplier) { filterSupplier = supplier.split(',').map((data) => { return data.trim(); }); } if (subCategories) { filterSubCategories = subCategories.split(',').map((data) => { return data.trim(); }); } const baseQuery = this.productRepository .createQueryBuilder('product') .leftJoin('product.sub_categories', 'sub_categories') .leftJoin('sub_categories.category', 'category') .leftJoin('product.supplier', 'supplier') .where('supplier.status = :status', { status: true }) .innerJoinAndMapOne( 'product.currentPrice', 'product.priceHistory', 'current_price', 'current_price.partner_id is null and current_price.end_date is NULL', ) .select(['product.id']) .addSelect([ 'product.name', 'product.code', 'sub_categories.name', 'supplier.name', 'category.name', 'product.status', ]) .addSelect('current_price.price') .addSelect( '(current_price.price + current_price.mark_up_price) as mark_up_price', ) .orderBy('product.code'); if (subCategories && filterSubCategories.length > 0) { baseQuery.where('product.sub_categories_id IN (:...subCategoryId)', { subCategoryId: filterSubCategories, }); } if (supplier && filterSupplier.length > 0) { baseQuery.where('supplier.id IN (:...supplierId)', { supplierId: filterSupplier, }); } const data = await baseQuery .offset(page * (pageSize || 10)) .limit(pageSize || 10) .getRawMany(); const totalData = await baseQuery.getCount(); return { data, count: totalData, }; } async findAllByCategories(page, subCategories, supplier) { const baseQuery = this.productRepository .createQueryBuilder('product') .leftJoin('product.sub_categories', 'sub_categories') .where( 'sub_categories.category_id = :id and product.supplier_id = :supplier_id', { id: subCategories, supplier_id: supplier, }, ) .leftJoinAndMapOne( 'product.currentPrice', 'product.priceHistory', 'current_price', 'current_price.partner_id is null', ); const data = await baseQuery .skip(page * 10) .take(10) .getMany(); const totalData = await baseQuery.getCount(); return { data, count: totalData, }; } async findAllBySubCategories(page, subCategories, supplier, pageSize?) { if (supplier != 'null' && !supplier) { supplier = (await this.supplierService.findByActive()).id; } const baseQuery = this.productRepository .createQueryBuilder('product') .leftJoin('product.sub_categories', 'sub_categories') .leftJoinAndMapOne( 'product.currentPrice', 'product.priceHistory', 'current_price', 'current_price.partner_id is NULL and current_price.end_date is NULL', ) .where( `product.supplier_id = :supplier_id and product.status = 'ACTIVE'`, { supplier_id: supplier, }, ) .select(['product.id']) .addSelect(['product.name', 'product.code', 'sub_categories.name']) .addSelect( '(current_price.price + current_price.mark_up_price) as price', ); if (subCategories != 'null' && subCategories) { baseQuery.andWhere('product.sub_categories_id = :id', { id: subCategories, }); } const data = await baseQuery .offset(page * 1000) .limit(1000) .getRawMany(); const totalData = await baseQuery.getCount(); return { data, count: totalData, }; } async findAllForPartner( page: number, pageSize: number, subCategories: string, username: string, ) { const user = await this.usersService.findOneByUsername(username); const supplier = await this.supplierService.findByActive(); const baseQuery = this.productRepository .createQueryBuilder('product') .leftJoin('product.sub_categories', 'sub_categories') .where( `product.supplier_id = :supplier_id and product.status = 'ACTIVE'`, { supplier_id: supplier.id, }, ) .innerJoinAndMapOne( 'product.currentPrice', 'product.priceHistory', 'current_price', 'current_price.partner_id = :id_partner and current_price.end_date is NULL', { id_partner: user.partner.id, }, ) .select(['product.id']) .addSelect(['product.name', 'product.code', 'sub_categories.name']) .addSelect( '(current_price.price + current_price.mark_up_price) as price', ); if ( subCategories != 'null' && subCategories && subCategories != 'undefined' ) { baseQuery.where('product.sub_categories_id = :id', { id: subCategories, }); } const data = await baseQuery .offset(page * pageSize) .limit(pageSize) .getRawMany(); const totalData = await baseQuery.getCount(); return { data, count: totalData, }; } async findOne(code: string) { try { return await this.productRepository.findOneOrFail({ relations: ['supplier'], where: { code: code, }, }); } catch (e) { if (e instanceof EntityNotFoundError) { throw new HttpException( { statusCode: HttpStatus.NOT_FOUND, error: 'Product not found', }, HttpStatus.NOT_FOUND, ); } else { throw e; } } } async findOneById(id: string) { try { return await this.productRepository.findOneOrFail({ relations: ['supplier'], where: { id: id, }, }); } catch (e) { if (e instanceof EntityNotFoundError) { throw new HttpException( { statusCode: HttpStatus.NOT_FOUND, error: 'Product not found', }, HttpStatus.NOT_FOUND, ); } else { throw e; } } } async update(id: string, updateProductDto: UpdateProductDto) { try { await this.productRepository.findOneOrFail(id); } catch (e) { if (e instanceof EntityNotFoundError) { throw new HttpException( { statusCode: HttpStatus.NOT_FOUND, error: 'Product not found', }, HttpStatus.NOT_FOUND, ); } else { throw e; } } const subCategories = await this.productSubCategoriesService.findOne( updateProductDto.subCategoriesId, ); const result = await this.productRepository.update(id, { name: updateProductDto.name, code: updateProductDto.code, status: updateProductDto.status, sub_categories: subCategories, }); return this.productRepository.findOneOrFail(id); } async updatePrice( code: string, updatePriceProductDto: UpdatePriceProductDto, ) { const product = await this.findOne(code); await this.productHistoryPrice.insert({ product: product, type: updatePriceProductDto.type, price: updatePriceProductDto.price, mark_up_price: updatePriceProductDto.markUpPrice, startDate: updatePriceProductDto.startDate, endDate: updatePriceProductDto.endDate, }); return true; } async remove(id: string) { try { await this.productRepository.findOneOrFail(id); } catch (e) { if (e instanceof EntityNotFoundError) { throw new HttpException( { statusCode: HttpStatus.NOT_FOUND, error: 'Product not found', }, HttpStatus.NOT_FOUND, ); } else { throw e; } } await this.productRepository.delete(id); } }