460 lines
13 KiB
TypeScript
460 lines
13 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';
|
|
|
|
export class ProductService {
|
|
constructor(
|
|
@InjectRepository(Product)
|
|
private productRepository: Repository<Product>,
|
|
@InjectRepository(ProductHistoryPrice)
|
|
private productHistoryPrice: Repository<ProductHistoryPrice>,
|
|
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);
|
|
}
|
|
}
|