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, admin_price: it[8], partner_fee: it[9], }); } 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, type: it[7] == 'postpaid' ? 'postpaid' : 'prepaid', }); 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, admin_price: it[8], partner_fee: it[9], }); } }); 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.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', 'price') .addSelect('current_price.partner_fee', 'partner_fee') .addSelect('current_price.admin_price', 'admin_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') .orderBy('price', 'ASC'); 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, ) { let filterSubCategories; const user = await this.usersService.findOneByUsername(username); if (subCategories) { filterSubCategories = subCategories.split(',').map((data) => { return data.trim(); }); } if (user.partner === null) { throw new HttpException( { statusCode: HttpStatus.NOT_FOUND, error: 'Partner id not found', }, HttpStatus.NOT_FOUND, ); } const baseQuery = await this.productRepository .createQueryBuilder('product') .leftJoin('product.sub_categories', 'sub_categories') .leftJoinAndSelect( 'product.supplier', 'supplier', 'supplier.status = true', ) .where(`product.status = 'ACTIVE'`) .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', 'product.type', 'product.status', 'sub_categories.name', 'current_price.admin_price as admin_price', 'current_price.mark_up_price as markup_price', 'current_price.partner_fee as partner_fee', 'current_price.price as price', ]) // .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, // }); // } if (subCategories && filterSubCategories.length > 0) { baseQuery.where('product.sub_categories_id IN (:...subCategoryId)', { subCategoryId: filterSubCategories, }).andWhere(`product.status = 'ACTIVE'`) } const newData = [] const data = await baseQuery .offset(page * pageSize) .limit(pageSize) .getRawMany(); data.map((dataa) => { let actualPrice = 0 if (dataa.product_type === 'prepaid') { actualPrice = Number(dataa['price']) + Number(dataa['markup_price']) } if (dataa.product_type === 'postpaid') { actualPrice = Number(dataa['admin_price'])- (Number(dataa['partner_fee']) + Number(dataa['markup_price'])) } dataa.price = actualPrice newData.push({ ...dataa }) }) const totalData = await baseQuery.getCount(); return { data, count: totalData, }; } async findOne(code: string, type: 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, updatePriceProductDto.productType); 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); } }