ppob-backend/src/transaction/transaction.service.ts
2021-12-31 01:13:45 +07:00

1219 lines
33 KiB
TypeScript

import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { DistributeTransactionDto } from './dto/distribute-transaction.dto';
import { OrderTransactionDto } from './dto/order-transaction.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Transactions } from './entities/transactions.entity';
import { Connection, EntityNotFoundError, Repository } from 'typeorm';
import { COA } from './entities/coa.entity';
import { TransactionJournal } from './entities/transaction-journal.entity';
import { CoaService } from './coa.service';
import * as uuid from 'uuid';
import { uniq } from 'lodash';
import { Decimal } from 'decimal.js';
import {
balanceType,
coaType,
statusTransaction,
typeTransaction,
} from '../helper/enum-list';
import { ProductService } from '../product/product.service';
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';
import { DepositReturnDto } from './dto/deposit_return.dto';
import { UserDetail } from '../users/entities/user_detail.entity';
import { doTransaction } from '../helper/irs-api';
import { ProductHistoryPrice } from '../product/entities/product-history-price.entity';
@Injectable()
export class TransactionService {
private readonly logger = new Logger(TransactionService.name);
constructor(
@InjectRepository(Transactions)
private transactionRepository: Repository<Transactions>,
@InjectRepository(TransactionJournal)
private transactionJournalRepository: Repository<TransactionJournal>,
@InjectRepository(COA)
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,
) {}
async addSupplierSaldo(addSaldoSupplier: AddSaldoSupplier, currentUser: any) {
const supplier = await this.supplierService.findByCode(
addSaldoSupplier.supplier,
);
// GET COA
const coaBank = await this.coaService.findByName(
`${coaType[coaType.BANK]}-SYSTEM`,
);
const coaInventory = await this.coaService.findByName(
`${coaType[coaType.INVENTORY]}-${supplier.code}`,
);
const coaBudget = await this.coaService.findByName(
`${coaType[coaType.BUDGET]}-${supplier.code}`,
);
const coaContraBudget = await this.coaService.findByName(
`${coaType[coaType.CONTRA_BUDGET]}-${supplier.code}`,
);
//GET USER
const userData = await this.userService.findByUsername(
currentUser.username,
);
await this.connection.transaction(async (manager) => {
//INSERT TRANSACTION
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = addSaldoSupplier.amount;
transactionData.user = userData.id;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DEPOSIT_SUPPLIER;
await manager.insert(Transactions, transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaBank.id,
credit: transactionData.amount,
},
{
coa_id: coaInventory.id,
debit: transactionData.amount,
},
{
coa_id: coaBudget.id,
debit: transactionData.amount,
},
{
coa_id: coaContraBudget.id,
credit: transactionData.amount,
},
],
});
});
return true;
}
async distributeFromAdmin(
distributeTransactionDto: DistributeTransactionDto,
currentUser: any,
) {
//GET USER
const userData = await this.userService.findByUsername(
currentUser.username,
);
const supplier = await this.supplierService.findByActive();
try {
if (userData.roles.name != 'Admin') {
throw new HttpException(
{
statusCode: HttpStatus.NOT_ACCEPTABLE,
error: 'Roles Not Admin',
},
HttpStatus.NOT_ACCEPTABLE,
);
}
// GET COA
const coaBudget = await this.coaService.findByName(
`${coaType[coaType.BUDGET]}-${supplier.code}`,
);
if (coaBudget.amount < distributeTransactionDto.amount) {
throw new HttpException(
{
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
error: `Transaction Failed because saldo not enough`,
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
const coaContraBudget = await this.coaService.findByName(
`${coaType[coaType.CONTRA_BUDGET]}-${supplier.code}`,
);
const coaAR = await this.coaService.findByTwoUser(
distributeTransactionDto.destination,
currentUser.userId,
coaType.ACCOUNT_RECEIVABLE,
);
const coaWallet = await this.coaService.findByUser(
distributeTransactionDto.destination,
coaType.WALLET,
);
await this.connection.transaction(async (manager) => {
//INSERT TRANSACTION
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = distributeTransactionDto.amount;
transactionData.user = userData.id;
transactionData.user_destination = distributeTransactionDto.destination;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DISTRIBUTION;
await manager.insert(Transactions, transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaAR.id,
debit: transactionData.amount,
},
{
coa_id: coaWallet.id,
credit: transactionData.amount,
},
{
coa_id: coaBudget.id,
credit: transactionData.amount,
},
{
coa_id: coaContraBudget.id,
debit: transactionData.amount,
},
],
});
});
return true;
} catch (e) {
throw e;
}
}
async distributeDeposit(
distributeTransactionDto: DistributeTransactionDto,
currentUser: any,
) {
//GET USER
const userData = await this.userService.findByUsername(
currentUser.username,
);
// GET COA
const coaSenderWallet = await this.coaService.findByUser(
userData.id,
coaType.WALLET,
);
const coaAP = await this.coaService.findByUserWithRelated(
distributeTransactionDto.destination,
userData.id,
coaType.ACCOUNT_PAYABLE,
);
const coaReceiverWallet = await this.coaService.findByUser(
distributeTransactionDto.destination,
coaType.WALLET,
);
const coaAR = await this.coaService.findByUserWithRelated(
distributeTransactionDto.destination,
userData.id,
coaType.ACCOUNT_RECEIVABLE,
);
if (coaSenderWallet.amount < distributeTransactionDto.amount) {
throw new HttpException(
{
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
error: `Transaction Failed because saldo not enough`,
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
await this.connection.transaction(async (manager) => {
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = distributeTransactionDto.amount;
transactionData.user = userData.id;
transactionData.user_destination = distributeTransactionDto.destination;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.DISTRIBUTION;
await manager.insert(Transactions, transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaSenderWallet.id,
debit: transactionData.amount,
},
{
coa_id: coaReceiverWallet.id,
credit: transactionData.amount,
},
{
coa_id: coaAR.id,
debit: transactionData.amount,
},
{
coa_id: coaAP.id,
credit: transactionData.amount,
},
],
});
});
return true;
}
async orderTransaction(
orderTransactionDto: OrderTransactionDto,
currentUser: any,
) {
const trxId = Array(6)
.fill(null)
.map(() => {
return Math.round(Math.random() * 16).toString(16);
})
.join('');
//GET USER
const userData = await this.userService.findByUsername(
currentUser.username,
);
//GET PRODUCT
const product = await this.productService.findOne(
orderTransactionDto.productCode,
);
const product_price = await this.productHistoryPriceService.findOne(
product.id,
userData.partner?.id,
);
let supervisorData = [];
let profit = product_price.mark_up_price;
//GET COA
const coaAccount = await this.coaService.findByUser(
userData.id,
coaType.WALLET,
);
const coaInventory = await this.coaService.findByName(
`${coaType[coaType.INVENTORY]}-${product.supplier.code}`,
);
const coaCostOfSales = await this.coaService.findByName(
`${coaType[coaType.COST_OF_SALES]}-${product.supplier.code}`,
);
const coaSales = await this.coaService.findByName(
`${coaType[coaType.SALES]}-SYSTEM`,
);
const coaExpense = await this.coaService.findByName(
`${coaType[coaType.EXPENSE]}-SYSTEM`,
);
if (!userData.partner) {
//GET SALES
supervisorData = await this.calculateCommission(
supervisorData,
profit,
userData,
);
profit = supervisorData
.map((item) => {
return item.credit;
})
.reduce((prev, curr) => {
return prev + curr;
}, 0);
supervisorData = supervisorData.concat([
{
coa_id: coaExpense.id,
debit: profit,
},
]);
}
if (
coaAccount.amount <=
product_price.mark_up_price + product_price.price
) {
throw new HttpException(
{
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
error: `Transaction Failed because saldo not enough`,
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
try {
await this.connection.transaction(async (manager) => {
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount =
product_price.mark_up_price + product_price.price;
transactionData.user = userData.id;
transactionData.status = statusTransaction.SUCCESS;
transactionData.type = typeTransaction.ORDER;
transactionData.product_price = product_price;
transactionData.destination = orderTransactionDto.destination;
transactionData.partner_trx_id = orderTransactionDto.trx_id;
transactionData.supplier_trx_id = trxId;
await manager.insert(Transactions, transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaInventory.id,
credit: product_price.price,
},
{
coa_id: coaCostOfSales.id,
debit: product_price.price,
},
{
coa_id: coaAccount.id,
debit: product_price.mark_up_price + product_price.price,
},
{
// eslint-disable-next-line camelcase
coa_id: coaSales.id,
credit: product_price.mark_up_price + product_price.price,
},
].concat(supervisorData),
});
});
} catch (e) {
throw e;
}
return {
trx_id: trxId,
client_trx_id: orderTransactionDto.trx_id,
product: orderTransactionDto.productCode,
amount: product_price.mark_up_price + product_price.price,
status: statusTransaction[statusTransaction.SUCCESS],
};
}
async orderTransactionProd(
orderTransactionDto: OrderTransactionDto,
currentUser: any,
) {
//TODO GET USER DATA
const userData = await this.userService.findByUsername(
currentUser.username,
);
//TODO GET PRODUCT AND PRICE
const product = await this.productService.findOne(
orderTransactionDto.productCode,
);
const product_price = await this.productHistoryPriceService.findOne(
product.id,
userData.partner?.id,
);
const coaAccount = await this.coaService.findByUser(
userData.id,
coaType.WALLET,
);
if (
coaAccount.amount <=
product_price.mark_up_price + product_price.price
) {
throw new HttpException(
{
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
error: `Transaction Failed because saldo not enough`,
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
//TODO HIT API SUPPLIER
const trxId = Array(6)
.fill(null)
.map(() => {
return Math.round(Math.random() * 16).toString(16);
})
.join('');
const hitSupplier = await doTransaction(
orderTransactionDto.productCode,
orderTransactionDto.destination,
trxId,
);
this.logger.log({
responseAPISupplier: hitSupplier,
});
//TODO TRANSACTION DAT
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = product_price.mark_up_price + product_price.price;
transactionData.user = userData.id;
transactionData.type = typeTransaction.ORDER;
transactionData.product_price = product_price;
transactionData.destination = orderTransactionDto.destination;
transactionData.partner_trx_id = orderTransactionDto.trx_id;
transactionData.supplier_trx_id = trxId;
if (!hitSupplier.success) {
transactionData.status = statusTransaction.FAILED;
await this.transactionRepository.insert(transactionData);
throw new HttpException(
{
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
error: hitSupplier.msg,
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
} else {
transactionData.status = statusTransaction.PENDING;
await this.transactionRepository.insert(transactionData);
}
if (hitSupplier.harga > product_price.price) {
product_price.endDate = new Date();
let newProductPrice = new ProductHistoryPrice();
newProductPrice = product_price;
newProductPrice.id = uuid.v4();
newProductPrice.price = hitSupplier.harga;
await this.productHistoryPriceService.create(product_price);
await this.productHistoryPriceService.create(newProductPrice);
}
return {
trx_id: trxId,
client_trx_id: orderTransactionDto.trx_id,
product: orderTransactionDto.productCode,
amount: transactionData.amount,
status: statusTransaction[transactionData.status],
};
}
async createDepositReturn(currentUser, depositReturnDto: DepositReturnDto) {
const userData = await this.userService.findByUsername(
currentUser.username,
);
try {
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = depositReturnDto.amount;
transactionData.user = userData.id;
transactionData.user_destination = depositReturnDto.destination;
transactionData.status = statusTransaction.PENDING;
transactionData.type = typeTransaction.DEPOSIT_RETURN;
transactionData.image_prove = depositReturnDto.image_prove;
await this.connection.transaction(async (manager) => {
await manager.insert(Transactions, transactionData);
});
return transactionData;
} catch (e) {
throw e;
}
}
async confirmationDepositReturn(
id: string,
userData,
statusApproval: string,
) {
const transactionData = await this.findApprovalDepositReturn(id);
const coaSenderWallet = await this.coaService.findByUser(
transactionData.user,
coaType.WALLET,
);
const coaAP = await this.coaService.findByUserWithRelated(
transactionData.user,
userData.userId,
coaType.ACCOUNT_PAYABLE,
);
const coaReceiverWallet = await this.coaService.findByUser(
transactionData.user,
coaType.WALLET,
);
const coaAR = await this.coaService.findByUserWithRelated(
transactionData.user,
userData.userId,
coaType.ACCOUNT_RECEIVABLE,
);
try {
await this.connection.transaction(async (manager) => {
transactionData.status =
statusApproval === 'accept'
? statusTransaction.APPROVED
: statusTransaction.REJECTED;
await manager.save(transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaSenderWallet.id,
credit: transactionData.amount,
},
{
coa_id: coaReceiverWallet.id,
debit: transactionData.amount,
},
{
coa_id: coaAR.id,
credit: transactionData.amount,
},
{
coa_id: coaAP.id,
debit: transactionData.amount,
},
],
});
});
return transactionData;
} catch (e) {
throw e;
}
return transactionData;
}
async confirmationAdminDepositReturn(
id: string,
userData,
statusApproval: string,
) {
const transactionData = await this.findApprovalDepositReturn(id);
const coaAR = await this.coaService.findByUserWithRelated(
userData.userId,
transactionData.user_destination,
coaType.ACCOUNT_RECEIVABLE,
);
const coaBank = await this.coaService.findByName(
`${coaType[coaType.BANK]}-SYSTEM`,
);
try {
await this.connection.transaction(async (manager) => {
transactionData.status =
statusApproval === 'Accept'
? statusTransaction.APPROVED
: statusTransaction.REJECTED;
await manager.save(transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaAR.id,
credit: transactionData.amount,
},
{
coa_id: coaBank.id,
debit: transactionData.amount,
},
],
});
});
return transactionData;
} catch (e) {
throw e;
}
return transactionData;
}
async callbackOrderFailed(supplier_trx_id: string, callback: any) {
const dataTransaction = await this.transactionRepository.findOne({
where: {
supplier_trx_id: supplier_trx_id,
},
});
dataTransaction.status = statusTransaction.FAILED;
dataTransaction.callback_json = callback;
await this.transactionRepository.save(dataTransaction);
}
async callbackOrderSuccess(supplier_trx_id: string, callback: any) {
const dataTransaction = await this.transactionRepository.findOne({
where: {
supplier_trx_id: supplier_trx_id,
},
relations: ['product_price'],
});
dataTransaction.status = statusTransaction.SUCCESS;
dataTransaction.seri_number = callback['sn'];
dataTransaction.callback_json = callback;
const userData = await this.userService.findExist(dataTransaction.user);
let supervisorData = [];
const product_price = await this.productHistoryPriceService.findById(
dataTransaction.product_price.id,
);
const product = await this.productService.findOneById(
product_price.product.id,
);
let profit = product_price.mark_up_price;
//GET COA
const coaAccount = await this.coaService.findByUser(
userData.id,
coaType.WALLET,
);
const coaInventory = await this.coaService.findByName(
`${coaType[coaType.INVENTORY]}-${product.supplier.code}`,
);
const coaCostOfSales = await this.coaService.findByName(
`${coaType[coaType.COST_OF_SALES]}-${product.supplier.code}`,
);
const coaSales = await this.coaService.findByName(
`${coaType[coaType.SALES]}-SYSTEM`,
);
const coaExpense = await this.coaService.findByName(
`${coaType[coaType.EXPENSE]}-SYSTEM`,
);
if (userData.partner) {
//GET SALES
supervisorData = await this.calculateCommission(
supervisorData,
profit,
userData,
);
profit = supervisorData
.map((item) => {
return item.credit;
})
.reduce((prev, curr) => {
return prev + curr;
}, 0);
supervisorData = supervisorData.concat([
{
coa_id: coaExpense.id,
debit: profit,
},
]);
}
try {
await this.connection.transaction(async (manager) => {
await manager.save(dataTransaction);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: dataTransaction,
amount: dataTransaction.amount,
journals: [
{
coa_id: coaInventory.id,
credit: product_price.price,
},
{
coa_id: coaCostOfSales.id,
debit: product_price.price,
},
{
coa_id: coaAccount.id,
debit: product_price.mark_up_price + product_price.price,
},
{
coa_id: coaSales.id,
credit: product_price.mark_up_price + product_price.price,
},
].concat(supervisorData),
});
});
} catch (e) {
throw e;
}
}
async withdrawBenefit(user) {
const userData = await this.userService.findExist(user);
const coaProfit = await this.coaService.findByUser(user, coaType.PROFIT);
const coaBank = await this.coaService.findByName(
`${coaType[coaType.BANK]}-SYSTEM`,
);
try {
const transactionData = new Transactions();
transactionData.id = uuid.v4();
transactionData.amount = coaProfit.amount;
transactionData.user = userData.id;
transactionData.status = statusTransaction.APPROVED;
transactionData.type = typeTransaction.WITHDRAW;
await this.connection.transaction(async (manager) => {
await manager.insert(Transactions, transactionData);
await this.accountingTransaction({
createTransaction: false,
transactionalEntityManager: manager,
transaction: transactionData,
amount: transactionData.amount,
journals: [
{
coa_id: coaBank.id,
credit: transactionData.amount,
},
{
coa_id: coaProfit.id,
debit: transactionData.amount,
},
],
});
});
return transactionData;
} catch (e) {
throw e;
}
}
async transactionHistoryByUser(
page: number,
user: string,
startDate: string,
endDate: string,
pageSize?: number,
) {
const userData = await this.userService.findExist(user);
let userBySupperior = [];
if (
userData.roles.id != 'e4dfb6a3-2338-464a-8fb8-5cbc089d4209' &&
userData.roles.id != '21dceea2-416e-4b55-b74c-12605e1f8d1b'
) {
let roleNumber;
if (userData.roles.id == '3196cdf4-ae5f-4677-9bcd-98be35c72321') {
roleNumber = 3;
} else if (userData.roles.id == '3196cdf4-ae5f-4677-9bcd-98be35c72322') {
roleNumber = 2;
} else if (userData.roles.id == 'e4dfb6a3-2348-464a-8fb8-5cbc089d4209') {
roleNumber = 1;
}
userBySupperior = await this.userService.findAllSubordinate(
userData.id,
roleNumber,
);
} else {
userBySupperior.push(user);
}
const baseQuery = this.transactionRepository
.createQueryBuilder('transaction')
.select('transaction.id', 'id')
.addSelect('transaction.created_at', 'created_at')
.where('transaction.user IN (:...id) and transaction.type = 1', {
id: userBySupperior,
})
.leftJoinAndMapOne(
'transaction.userData',
UserDetail,
'userData',
'userData.user = transaction.user',
)
.leftJoin('transaction.product_price', 'product_price')
.leftJoin('product_price.product', 'product')
.addSelect('transaction.amount', 'price')
.addSelect('transaction.destination')
.addSelect('transaction.seri_number','seri_number')
.addSelect('transaction.supplier_trx_id', 'transaction_code')
.addSelect('transaction.status', 'status')
.addSelect('transaction.partner_trx_id', 'partner_transaction_code')
.addSelect('userData.name', 'buyer')
.addSelect('product.name', 'name')
.addSelect('product.id', 'product_id');
if (startDate && endDate) {
baseQuery.andWhere('transaction.created_at between :startDate and :enDate', {
startDate: new Date(startDate),
enDate: new Date(endDate),
});
}
const data = await baseQuery
.offset(page * (pageSize || 10))
.limit(pageSize || 10)
.getRawMany();
const totalData = await baseQuery.getCount();
return {
data,
count: totalData,
};
}
async topUpHistoryByUser(
page: number,
user: string,
destinationUser: string,
pageSize?: number,
) {
const baseQuery = this.transactionRepository
.createQueryBuilder('transaction')
.where(
'transaction.user = :id and transaction.type = 0 and transaction.user_destination = :destinationId',
{
id: user,
destinationId: destinationUser,
},
)
.select(['id', 'created_at as transaction_date', 'amount']);
const data = await baseQuery
.offset(page * (pageSize || 10))
.limit(pageSize || 10)
.getRawMany();
const totalData = await baseQuery.getCount();
return {
data,
count: totalData,
};
}
async findApprovalDepositReturn(id: string) {
try {
return await this.transactionRepository.findOneOrFail({
where: {
id: id,
type: typeTransaction.DEPOSIT_RETURN,
status: statusTransaction.PENDING,
},
});
} catch (e) {
if (e instanceof EntityNotFoundError) {
throw new HttpException(
{
statusCode: HttpStatus.NOT_FOUND,
error: 'Return Deposit not found',
},
HttpStatus.NOT_FOUND,
);
} else {
throw e;
}
}
}
async getAllDepositReturnFromUser(
user: string,
page: number,
pageSize?: number,
) {
return this.transactionRepository.findAndCount({
skip: page * (pageSize || 10),
take: pageSize || 10,
where: {
user: user,
type: typeTransaction.DEPOSIT_RETURN,
},
order: {
createdAt: 'DESC',
},
});
}
async getAllDepositReturnToUser(
user: string,
page: number,
pageSize?: number,
) {
const baseQuery = this.transactionRepository
.createQueryBuilder('transaction')
.where('transaction.user_destination = :id and transaction.type = 3', {
id: user,
})
.leftJoinAndMapOne(
'transaction.userData',
UserDetail,
'userData',
'userData.user = transaction.user',
)
.select('transaction.id', 'id')
.addSelect([
'transaction.created_at',
'image_prove',
'amount',
'status',
'userData.name',
]);
const data = await baseQuery
.offset(page * (pageSize || 10))
.limit(pageSize || 10)
.getRawMany();
const totalData = await baseQuery.getCount();
return {
data,
count: totalData,
};
return this.transactionRepository.findAndCount({
skip: page * (pageSize || 10),
take: pageSize || 10,
where: {
user_destination: user,
type: typeTransaction.DEPOSIT_RETURN,
},
order: {
createdAt: 'DESC',
},
});
}
async getTotalSell() {
const { total_amount } = await this.transactionRepository
.createQueryBuilder('transactions')
.select('SUM(transactions.amount) as total_amount')
.getRawOne();
return parseInt(total_amount);
}
async getTotalProfit() {
const { total_amount } = await this.transactionRepository
.createQueryBuilder('transactions')
.select('SUM(transactions.amount) as total_amount')
.getRawOne();
return parseInt(total_amount);
}
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,
),
);
return Promise.all(
supervisorData.map(async (it) => {
const coaAccount = await this.coaService.findByUser(
it.id,
coaType.PROFIT,
);
const commissionValue = await this.commissionService.findOne(
it.roles.id,
);
return {
coa_id: coaAccount.id,
credit: (totalPrice * commissionValue.commission) / 100,
};
}),
);
}
async accountingTransaction(createJournalDto: CreateJournalDto) {
const creditSum = createJournalDto.journals
.map((it) => {
return it.credit;
})
.filter((it) => {
return it;
})
.reduce((a, b) => {
return a.plus(b);
}, new Decimal(0));
const debitSum = createJournalDto.journals
.map((it) => {
return it.debit;
})
.filter((it) => {
return it;
})
.reduce((a, b) => {
return a.plus(b);
}, new Decimal(0));
const coaIds = uniq(
createJournalDto.journals.map((it) => {
return it.coa_id;
}),
);
if (!creditSum.equals(debitSum)) {
throw new Error(`credit and debit doesn't match`);
}
const coas = await this.coaRepository.findByIds(coaIds);
const transaction = createJournalDto.transaction;
await Promise.all(
createJournalDto.journals.map((journal) => {
const coa = coas.find((it) => {
return it.id === journal.coa_id;
});
if (!coa) {
throw new Error(`coa ${journal.coa_id} not found`);
}
const journalEntry = new TransactionJournal();
journalEntry.coa = coa;
journalEntry.type = journal.debit
? balanceType.DEBIT
: balanceType.CREDIT;
journalEntry.amount = journal.debit ? journal.debit : journal.credit;
journalEntry.transaction_head = transaction;
return createJournalDto.transactionalEntityManager.save(journalEntry);
}),
);
await Promise.all(
coaIds.map((coaId) => {
const journalPerCoa = createJournalDto.journals.filter((journal) => {
return journal.coa_id == coaId;
});
const creditSum = journalPerCoa
.map((it) => {
return it.credit;
})
.filter((it) => {
return it;
})
.reduce((a, b) => {
return a.plus(b);
}, new Decimal(0));
const debitSum = journalPerCoa
.map((it) => {
return it.debit;
})
.filter((it) => {
return it;
})
.reduce((a, b) => {
return a.plus(b);
}, new Decimal(0));
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)
.set({
amount: () => {
return `amount + ${diff.toString()}`;
},
})
.where('id = :id', { id: coa.id })
.execute();
}),
);
return transaction;
}
}