Merge branch 'development' into 'devops-staging'

Development

See merge request empatnusabangsa/ppob/ppob-backend!24
This commit is contained in:
Catur Bagaskara 2021-12-16 03:20:24 +00:00
commit 2bcadc5455
13 changed files with 241 additions and 57 deletions

View File

@ -12,7 +12,7 @@ export class CommissionService {
private commissionRepository: Repository<CommissionSetting>,
) {}
findAllRoles(page) {
findAllCommission(page) {
return this.commissionRepository.findAndCount({
skip: page * 10,
take: 10,
@ -22,6 +22,28 @@ export class CommissionService {
});
}
async findOne(role: string) {
try {
return await this.commissionRepository.findOneOrFail({
where: {
role: role,
},
});
} catch (e) {
if (e instanceof EntityNotFoundError) {
throw new HttpException(
{
statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found',
},
HttpStatus.NOT_FOUND,
);
} else {
throw e;
}
}
}
async updateCommission(id: string, request) {
try {
await this.commissionRepository.findOneOrFail(id);

View File

@ -37,7 +37,7 @@ export class ConfigurableController {
@Get('/commission')
async findCommission(@Query('page') page: number) {
const [data, count] = await this.commissionService.findAllRoles(page);
const [data, count] = await this.commissionService.findAllCommission(page);
return {
data,

View File

@ -10,6 +10,6 @@ import { CommissionSetting } from './entities/commission_setting.entity';
imports: [TypeOrmModule.forFeature([Roles, CommissionSetting])],
controllers: [ConfigurableController],
providers: [RoleService, CommissionService],
exports: [RoleService],
exports: [RoleService, CommissionService],
})
export class ConfigurableModule {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HistoryPriceService } from './history-price.service';
describe('HistoryPriceService', () => {
let service: HistoryPriceService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [HistoryPriceService],
}).compile();
service = module.get<HistoryPriceService>(HistoryPriceService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,37 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { EntityNotFoundError, IsNull, Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { ProductCategories } from '../entities/product-category.entity';
import { ProductHistoryPrice } from '../entities/product-history-price.entity';
@Injectable()
export class ProductHistoryPriceService {
constructor(
@InjectRepository(ProductHistoryPrice)
private productHistoryPriceService: Repository<ProductHistoryPrice>,
) {}
async findOne(product: string, partner: string) {
try {
return await this.productHistoryPriceService.findOneOrFail({
where: {
product: product,
endDate: IsNull(),
partner: partner ? partner : IsNull(),
},
});
} catch (e) {
if (e instanceof EntityNotFoundError) {
throw new HttpException(
{
statusCode: HttpStatus.NOT_FOUND,
error: 'Data not found',
},
HttpStatus.NOT_FOUND,
);
} else {
throw e;
}
}
}
}

View File

@ -20,6 +20,7 @@ import { ProductSubCategoriesService } from './product-sub-categories.service';
import { CreateSubCategoriesProductDto } from './dto/sub-categories/create-sub-categories-product.dto';
import { CreateProductDto } from './dto/product/create-product.dto';
import { UpdateProductDto } from './dto/product/update-product.dto';
import { ProductHistoryPriceService } from './history-price/history-price.service';
@Controller({
path: 'product',
@ -30,6 +31,7 @@ export class ProductController {
private readonly productService: ProductService,
private readonly productCategoriesService: ProductCategoriesService,
private readonly productSubCategoriesService: ProductSubCategoriesService,
private readonly productHistoryPriceService: ProductHistoryPriceService,
) {}
@Post()
@ -79,6 +81,20 @@ export class ProductController {
};
}
@Get('test')
async test(@Request() req) {
const data = await this.productHistoryPriceService.findOne(
'4d3b328c-3ce4-4c84-84bb-cd9c36e85a45',
'27effb3e-0351-428a-ba7f-08a21c54e16a',
);
return {
data,
statusCode: HttpStatus.OK,
message: 'success',
};
}
@Get('by-categories-all')
async findByCategoriesAll(
@Query('page') page: number,

View File

@ -9,6 +9,7 @@ import { ProductHistoryPrice } from './entities/product-history-price.entity';
import { ProductSubCategories } from './entities/product-sub-category.entity';
import { ProductSubCategoriesService } from './product-sub-categories.service';
import { UsersModule } from '../users/users.module';
import { ProductHistoryPriceService } from './history-price/history-price.service';
@Module({
imports: [
@ -25,7 +26,8 @@ import { UsersModule } from '../users/users.module';
ProductService,
ProductCategoriesService,
ProductSubCategoriesService,
ProductHistoryPriceService,
],
exports: [ProductService],
exports: [ProductService, ProductHistoryPriceService],
})
export class ProductModule {}

View File

@ -13,7 +13,6 @@ 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 { Raw } from 'typeorm/browser';
import { UsersService } from '../users/users.service';
import { SupplierService } from '../users/supplier/supplier.service';
@ -132,7 +131,12 @@ export class ProductService {
async findOne(code: string) {
try {
return await this.productRepository.findOneOrFail({ code: code });
return await this.productRepository.findOneOrFail({
relations: ['supplier'],
where: {
code: code,
},
});
} catch (e) {
if (e instanceof EntityNotFoundError) {
throw new HttpException(

View File

@ -12,6 +12,8 @@ import {
} from 'typeorm';
import { BaseModel } from '../../config/basemodel.entity';
import { statusTransaction, typeTransaction } from '../../helper/enum-list';
import { Partner } from '../../users/entities/partner.entity';
import { ProductHistoryPrice } from '../../product/entities/product-history-price.entity';
@Entity()
export class Transactions extends BaseModel {
@ -31,4 +33,9 @@ export class Transactions extends BaseModel {
nullable: true,
})
user_destination: string;
@ManyToOne(() => ProductHistoryPrice, (product) => product.id)
product_price: ProductHistoryPrice;
mark_up_price: number;
}

View File

@ -8,6 +8,7 @@ import {
Delete,
Request,
HttpStatus,
Query,
} from '@nestjs/common';
import { TransactionService } from './transaction.service';
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
@ -73,4 +74,18 @@ export class TransactionController {
req.user,
);
}
@Get('history')
async findByCategories(@Query('page') page: number, @Request() req) {
const data = await this.transactionService.transactionHistoryByUser(
page,
req.user.userId,
);
return {
...data,
statusCode: HttpStatus.OK,
message: 'success',
};
}
}

View File

@ -9,11 +9,13 @@ import { Transactions } from './entities/transactions.entity';
import { CoaService } from './coa.service';
import { ProductModule } from '../product/product.module';
import { UsersModule } from 'src/users/users.module';
import { ConfigurableModule } from '../configurable/configurable.module';
@Module({
imports: [
TypeOrmModule.forFeature([COA, TransactionJournal, Transactions]),
ProductModule,
ConfigurableModule,
forwardRef(() => UsersModule),
],
controllers: [TransactionController, PpobCallbackController],

View File

@ -21,6 +21,8 @@ import { CreateJournalDto } from './dto/create-journal.dto';
import { UsersService } from 'src/users/users.service';
import { AddSaldoSupplier } from './dto/add-saldo-supplier.dto';
import { SupplierService } from '../users/supplier/supplier.service';
import { ProductHistoryPriceService } from '../product/history-price/history-price.service';
import { CommissionService } from '../configurable/commission.service';
interface JournalEntry {
coa_id: string;
@ -39,7 +41,9 @@ export class TransactionService {
private coaRepository: Repository<COA>,
private coaService: CoaService,
private productService: ProductService,
private productHistoryPriceService: ProductHistoryPriceService,
private userService: UsersService,
private commissionService: CommissionService,
private supplierService: SupplierService,
private connection: Connection,
) {}
@ -119,6 +123,7 @@ export class TransactionService {
const userData = await this.userService.findByUsername(
currentUser.username,
);
if (userData.roles.name != 'Admin') {
throw new HttpException(
{
@ -268,33 +273,32 @@ export class TransactionService {
orderTransactionDto: OrderTransactionDto,
currentUser: any,
) {
//GET USER
const userData = await this.userService.findByUsername(
currentUser.username,
);
//GET PRODUCT
const product = await this.productService.findOne(
orderTransactionDto.productCode,
);
//GET USER
const userData = await this.userService.findByUsername(
currentUser.username,
const product_price = await this.productHistoryPriceService.findOne(
product.id,
userData.partner?.id,
);
let supervisorData = [];
if (userData.superior != null) {
supervisorData.push(
await this.userService.findByUsername(currentUser.username),
const profit = product_price.mark_up_price - product_price.price;
if (!userData.partner) {
//GET SALES
supervisorData = await this.calculateCommission(
supervisorData,
profit,
userData,
);
if (supervisorData[0].superior != null) {
supervisorData.push(
await this.userService.findByUsername(currentUser.username),
);
if (supervisorData[0].superior != null) {
supervisorData.push(
await this.userService.findByUsername(currentUser.username),
);
}
}
}
//GET COA
@ -304,11 +308,11 @@ export class TransactionService {
);
const coaInventory = await this.coaService.findByName(
`${coaType[coaType.INVENTORY]}-IRS`,
`${coaType[coaType.INVENTORY]}-${product.supplier.code}`,
);
const coaCostOfSales = await this.coaService.findByName(
`${coaType[coaType.COST_OF_SALES]}-SYSTEM`,
`${coaType[coaType.COST_OF_SALES]}-${product.supplier.code}`,
);
const coaSales = await this.coaService.findByName(
@ -319,18 +323,6 @@ export class TransactionService {
`${coaType[coaType.EXPENSE]}-SYSTEM`,
);
supervisorData = supervisorData.map(async (it) => {
const coaAccount = await this.coaService.findByUser(
it.id,
coaType.WALLET,
);
return {
coa_id: coaAccount.id,
credit: 0,
};
});
if (coaAccount.amount <= product.price) {
throw new HttpException(
{
@ -346,10 +338,11 @@ export class TransactionService {
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = product.price;
transactionData.amount = product_price.mark_up_price;
transactionData.user = userData.id;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DISTRIBUTION;
transactionData.type = typeTransaction.ORDER;
transactionData.product_price = product_price;
await manager.insert(Transactions, transactionData);
await this.accountingTransaction({
@ -360,23 +353,23 @@ export class TransactionService {
journals: [
{
coa_id: coaInventory.id,
credit: product.basePrice,
credit: product_price.price,
},
{
coa_id: coaCostOfSales.id,
debit: product.basePrice,
debit: product_price.price,
},
{
coa_id: coaAccount.id,
debit: product.price,
debit: product_price.mark_up_price,
},
{
coa_id: coaSales.id,
credit: product.price,
credit: product_price.mark_up_price,
},
{
coa_id: coaExpense.id,
credit: 0,
credit: userData.partner ? 0 : profit,
},
].concat(supervisorData),
});
@ -388,6 +381,71 @@ export class TransactionService {
return true;
}
async transactionHistoryByUser(page: number, user: string) {
const baseQuery = this.transactionRepository
.createQueryBuilder('transaction')
.select('transaction.id', 'id')
.addSelect('transaction.created_at', 'created_at')
.where('transaction.user = :id and transaction.type = 1', {
id: user,
})
.leftJoin('transaction.product_price', 'product_price')
.leftJoin('product_price.product', 'product')
.addSelect('product_price.mark_up_price', 'mark_up_price')
.addSelect('product.name', 'name')
.addSelect('product.id', 'product_id');
// .leftJoinAndSelect('transaction.product_price', 'product_price')
// .leftJoinAndSelect('product_price.product', 'product');
const data = await baseQuery
.skip(page * 10)
.take(10)
.getRawMany();
const totalData = await baseQuery.getCount();
return {
data,
count: totalData,
};
}
async calculateCommission(data, totalPrice, userData) {
const supervisorData = [];
supervisorData.push(
await this.userService.findByUsername(userData.superior.username),
);
//GET Supervisor
supervisorData.push(
await this.userService.findByUsername(
supervisorData[0].superior.username,
),
);
//GET Admin
supervisorData.push(
await this.userService.findByUsername(
supervisorData[1].superior.username,
),
);
return supervisorData.map(async (it) => {
const coaAccount = await this.coaService.findByUser(
it.id,
coaType.WALLET,
);
const commissionValue = await this.commissionService.findOne(it.role.id);
return {
coa_id: coaAccount.id,
credit: totalPrice * commissionValue.commission,
};
});
}
async accountingTransaction(createJournalDto: CreateJournalDto) {
const creditSum = createJournalDto.journals
.map((it) => {
@ -473,17 +531,20 @@ export class TransactionService {
return a.plus(b);
}, new Decimal(0));
let coa = coas.find(
(it) => it.id.toLowerCase() === coaId.toLowerCase(),
);
const coa = coas.find((it) => {
return it.id.toLowerCase() === coaId.toLowerCase();
});
let balance = new Decimal(coa.amount);
if (coa.balanceType == balanceType.DEBIT) {
balance = balance.plus(debitSum.minus(creditSum));
} else if (coa.balanceType == balanceType.CREDIT) {
balance = balance.plus(creditSum.minus(debitSum));
}
const diff = balance.minus(new Decimal(coa.amount));
return createJournalDto.transactionalEntityManager
.createQueryBuilder()
.update(COA)

View File

@ -83,6 +83,14 @@ export class UsersService {
dataCoaWallet.coaEntityManager = manager;
await this.coaService.create(dataCoaWallet);
const dataCoaAR = new InputCoaDto();
dataCoaAR.user = userData;
dataCoaAR.balanceType = balanceType.DEBIT;
dataCoaAR.relatedUserId = superior.id;
dataCoaAR.type = coaType.ACCOUNT_RECEIVABLE;
dataCoaAR.coaEntityManager = manager;
await this.coaService.create(dataCoaAR);
if (createUserDto.superior) {
const dataCoaAP = new InputCoaDto();
dataCoaAP.user = userData;
@ -91,14 +99,6 @@ export class UsersService {
dataCoaAP.type = coaType.ACCOUNT_PAYABLE;
dataCoaAP.coaEntityManager = manager;
await this.coaService.create(dataCoaAP);
const dataCoaAR = new InputCoaDto();
dataCoaAR.user = userData;
dataCoaAR.balanceType = balanceType.DEBIT;
dataCoaAR.relatedUserId = superior.id;
dataCoaAR.type = coaType.ACCOUNT_RECEIVABLE;
dataCoaAR.coaEntityManager = manager;
await this.coaService.create(dataCoaAR);
}
});
@ -169,7 +169,7 @@ export class UsersService {
where: {
username: username,
},
relations: ['roles'],
relations: ['superior', 'roles', 'partner'],
});
} catch (e) {
if (e instanceof EntityNotFoundError) {