ppob-backend/src/product/product.service.ts

588 lines
16 KiB
TypeScript

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';
import { ProductHistoryStatus } from './entities/product-history-status.entity';
export class ProductService {
constructor(
@InjectRepository(Product)
private productRepository: Repository<Product>,
@InjectRepository(ProductHistoryPrice)
private productHistoryPrice: Repository<ProductHistoryPrice>,
@InjectRepository(ProductHistoryStatus)
private productHistoryStatus: Repository<ProductHistoryStatus>,
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.productHistoryStatus.insert({
product: productData,
partner: it[6] != '-' ? partnerData : null,
status: it[5] == 'active' ? 'ACTIVE' : 'NOT ACTIVE',
});
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: null,
sub_categories: subCategories,
supplier: supplierData,
type: it[7] == 'postpaid' ? 'postpaid' : 'prepaid',
});
await this.productHistoryStatus.insert({
product: savedProduct.identifiers[0],
partner: partnerData,
status: it[5] == 'active' ? 'ACTIVE' : 'NOT ACTIVE',
});
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',
)
.innerJoinAndMapOne(
'product.currentStatus',
'product.statusHistory',
'history_status',
'history_status.deleted_at is NULL'
)
.select(['product.id'])
.addSelect([
'product.name',
'product.code',
'sub_categories.name',
'supplier.name',
'category.name',
])
.addSelect('history_status.status', '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')
.leftJoin('product.statusHistory', 'status_history')
.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 status_history.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',
)
.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,
},
)
.innerJoinAndMapOne(
'product.currentStatus',
'product.statusHistory',
'history_status',
'history_status.deleted_at is NULL and history_status.partner_id = :id_partner and history_status.status = ACTIVE',
{
id_partner: user.partner.id,
},
)
.select(['product.id'])
.addSelect([
'product.name',
'product.code',
'product.type',
'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('history_status.status', 'status')
// .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(`history_status.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 findOneActive(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 dataStatus = await this.productHistoryStatus.findOne({
where: {
product: id,
},
});
await this.productHistoryStatus.update(dataStatus.id, {
status: updateProductDto.status,
});
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);
}
}