journaling
This commit is contained in:
parent
679bb758d8
commit
d65af44a52
31
package-lock.json
generated
31
package-lock.json
generated
|
@ -1108,6 +1108,15 @@
|
|||
"uuid": "8.3.2"
|
||||
}
|
||||
},
|
||||
"@nestjs/jwt": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-8.0.0.tgz",
|
||||
"integrity": "sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==",
|
||||
"requires": {
|
||||
"@types/jsonwebtoken": "8.5.4",
|
||||
"jsonwebtoken": "8.5.1"
|
||||
}
|
||||
},
|
||||
"@nestjs/mapped-types": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz",
|
||||
|
@ -1514,6 +1523,14 @@
|
|||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jsonwebtoken": {
|
||||
"version": "8.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz",
|
||||
"integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
|
@ -1523,8 +1540,7 @@
|
|||
"@types/node": {
|
||||
"version": "16.11.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz",
|
||||
"integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw=="
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
|
@ -1541,6 +1557,17 @@
|
|||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/passport-jwt": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.6.tgz",
|
||||
"integrity": "sha512-cmAAMIRTaEwpqxlrZyiEY9kdibk94gP5KTF8AT1Ra4rWNZYHNMreqhKUEeC5WJtuN5SJZjPQmV+XO2P5PlnvNQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/jsonwebtoken": "*",
|
||||
"@types/passport-strategy": "*"
|
||||
}
|
||||
},
|
||||
"@types/passport-local": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz",
|
||||
|
|
|
@ -17,6 +17,13 @@ export enum productType {
|
|||
export enum coaType {
|
||||
WALLET,
|
||||
INCOME,
|
||||
INVENTORY,
|
||||
COST_OF_SALES,
|
||||
SALES,
|
||||
BANK,
|
||||
EXPENSE,
|
||||
ACCOUNT_RECEIVABLE,
|
||||
ACCOUNT_PAYABLE
|
||||
}
|
||||
|
||||
export enum balanceType {
|
||||
|
|
|
@ -1,15 +1,34 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { COA } from './entities/coa.entity';
|
||||
import { coaType } from '../helper/enum-list';
|
||||
import { InputCoaDto } from './dto/input-coa.dto';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
|
||||
export class CoaService {
|
||||
constructor(
|
||||
@InjectRepository(COA)
|
||||
private coaRepository: Repository<COA>,
|
||||
@Inject(forwardRef(() => UsersService))
|
||||
private userService: UsersService,
|
||||
) {}
|
||||
|
||||
async create(inputCoaDto: InputCoaDto) {
|
||||
const user = await this.userService.findExist(inputCoaDto.userId)
|
||||
|
||||
const result = await this.coaRepository.insert({
|
||||
user:user.id,
|
||||
name: inputCoaDto.balanceType + '-' + user.username,
|
||||
balanceType:inputCoaDto.balanceType,
|
||||
type:inputCoaDto.type
|
||||
});
|
||||
|
||||
return this.coaRepository.findOneOrFail(
|
||||
result.identifiers[0].id,
|
||||
);
|
||||
}
|
||||
|
||||
async findByUser(id: string, typeOfCoa: coaType) {
|
||||
try {
|
||||
return await this.coaRepository.findOneOrFail({ user: id, type: typeOfCoa });
|
||||
|
|
35
src/transaction/dto/create-journal.dto.ts
Normal file
35
src/transaction/dto/create-journal.dto.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { balanceType, coaType, statusTransaction, typeTransaction } from 'src/helper/enum-list';
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
interface JournalEntry {
|
||||
coa_id: string;
|
||||
debit?: number;
|
||||
credit?: number;
|
||||
}
|
||||
|
||||
export class CreateJournalDto {
|
||||
@IsNotEmpty()
|
||||
transactionalEntityManager: EntityManager;
|
||||
|
||||
@IsNotEmpty()
|
||||
createTransaction?: boolean;
|
||||
|
||||
@IsNotEmpty()
|
||||
userId?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
transactionId?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
type?: typeTransaction;
|
||||
|
||||
@IsNotEmpty()
|
||||
amount?: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
transactionStatus?: statusTransaction;
|
||||
|
||||
@IsNotEmpty()
|
||||
journals: JournalEntry[]
|
||||
}
|
13
src/transaction/dto/input-coa.dto.ts
Normal file
13
src/transaction/dto/input-coa.dto.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { balanceType, coaType } from 'src/helper/enum-list';
|
||||
|
||||
export class InputCoaDto {
|
||||
@IsUUID()
|
||||
userId: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
type: coaType;
|
||||
|
||||
@IsNotEmpty()
|
||||
balanceType: balanceType;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import { TransactionController } from './transaction.controller';
|
||||
import { PpobCallbackController } from './ppob_callback.controller';
|
||||
|
@ -10,6 +10,7 @@ import { TransactionJournal } from './entities/transaction-journal.entity';
|
|||
import { Transactions } from './entities/transactions.entity';
|
||||
import { CoaService } from './coa.service';
|
||||
import { ProductModule } from '../product/product.module';
|
||||
import { UsersModule } from 'src/users/users.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -21,6 +22,7 @@ import { ProductModule } from '../product/product.module';
|
|||
Transactions,
|
||||
]),
|
||||
ProductModule,
|
||||
forwardRef(() => UsersModule),
|
||||
],
|
||||
controllers: [TransactionController, PpobCallbackController],
|
||||
providers: [TransactionService, CoaService],
|
||||
|
|
|
@ -3,11 +3,14 @@ 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, Repository } from 'typeorm';
|
||||
import { Connection, EntityManager, Repository } from 'typeorm';
|
||||
import { COA } from './entities/coa.entity';
|
||||
import { TransactionType } from './entities/transaction-type.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,
|
||||
|
@ -16,6 +19,14 @@ import {
|
|||
} from '../helper/enum-list';
|
||||
import { ProductService } from '../product/product.service';
|
||||
import * as irsService from '../helper/irs-service';
|
||||
import { CreateJournalDto } from './dto/create-journal.dto';
|
||||
|
||||
interface JournalEntry {
|
||||
coa_id: string;
|
||||
debit?: string;
|
||||
credit?: string;
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class TransactionService {
|
||||
|
@ -145,4 +156,80 @@ export class TransactionService {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
async accountingTransaction(createJournalDto: CreateJournalDto ) {
|
||||
|
||||
createJournalDto.transactionId = createJournalDto.transactionId ?? uuid.v4();
|
||||
|
||||
let creditSum = createJournalDto.journals.map(it => it.credit).filter(it => it).reduce((a, b) => a.plus(b), new Decimal(0));
|
||||
let debitSum = createJournalDto.journals.map(it => it.debit).filter(it => it).reduce((a, b) => a.plus(b), new Decimal(0));
|
||||
let coaIds = uniq(createJournalDto.journals.map(it => it.coa_id));
|
||||
|
||||
if (!creditSum.equals(debitSum)) {
|
||||
throw new Error(`credit and debit doesn't match`);
|
||||
}
|
||||
|
||||
const coas = await this.coaRepository.findByIds(coaIds);
|
||||
|
||||
let transaction: Transactions;
|
||||
|
||||
if(createJournalDto.createTransaction) {
|
||||
transaction = new Transactions();
|
||||
transaction.id = createJournalDto.transactionId;
|
||||
transaction.type = createJournalDto.type;
|
||||
transaction.amount = createJournalDto.amount;
|
||||
transaction.user = createJournalDto.userId;
|
||||
transaction.status = createJournalDto.transactionStatus;
|
||||
|
||||
await this.transactionRepository.save(transaction);
|
||||
} else {
|
||||
transaction = await this.transactionRepository.findOneOrFail(createJournalDto.transactionId);
|
||||
}
|
||||
|
||||
await Promise.all(createJournalDto.journals.map(journal => {
|
||||
const coa = coas.find(it => 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 = transaction;
|
||||
|
||||
return this.transactionJournalRepository.save(journalEntry);
|
||||
}));
|
||||
|
||||
await Promise.all(coaIds.map(coaId => {
|
||||
let journalPerCoa = createJournalDto.journals.filter(journal => journal.coa_id == coaId);
|
||||
|
||||
let creditSum = journalPerCoa.map(it => it.credit).filter(it => it).reduce((a, b) => a.plus(b), new Decimal(0));
|
||||
let debitSum = journalPerCoa.map(it => it.debit).filter(it => it).reduce((a, b) => a.plus(b), new Decimal(0));
|
||||
|
||||
let coa = coas.find(it => 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: () => "amount + " + diff.toString()
|
||||
})
|
||||
.where("id = :id", { id: coa.id })
|
||||
.execute();
|
||||
}));
|
||||
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
|
@ -7,7 +7,7 @@ import { TransactionModule } from 'src/transaction/transaction.module';
|
|||
import { ConfigurableModule } from 'src/configurable/configurable.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User]), TransactionModule, ConfigurableModule],
|
||||
imports: [TypeOrmModule.forFeature([User]), forwardRef(() => TransactionModule), ConfigurableModule],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
exports: [UsersService],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
|
@ -7,14 +7,16 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { hashPassword } from '../helper/hash_password';
|
||||
import { CoaService } from 'src/transaction/coa.service';
|
||||
import { coaType } from 'src/helper/enum-list';
|
||||
import { balanceType, coaType } from 'src/helper/enum-list';
|
||||
import { RoleService } from 'src/configurable/roles.service';
|
||||
import { InputCoaDto } from 'src/transaction/dto/input-coa.dto';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private usersRepository: Repository<User>,
|
||||
@Inject(forwardRef(() => CoaService))
|
||||
private coaService: CoaService,
|
||||
private roleService: RoleService
|
||||
) {}
|
||||
|
@ -32,6 +34,25 @@ export class UsersService {
|
|||
roles:roles
|
||||
});
|
||||
|
||||
let dataCoaWallet = new InputCoaDto();
|
||||
dataCoaWallet.userId = result.identifiers[0].id;
|
||||
dataCoaWallet.balanceType = balanceType.CREDIT;
|
||||
dataCoaWallet.type = coaType.WALLET;
|
||||
|
||||
let dataCoaAR = new InputCoaDto();
|
||||
dataCoaAR.userId = result.identifiers[0].id;
|
||||
dataCoaAR.balanceType = balanceType.CREDIT;
|
||||
dataCoaAR.type = coaType.ACCOUNT_RECEIVABLE;
|
||||
|
||||
let dataCoaPayable = new InputCoaDto();
|
||||
dataCoaPayable.userId = result.identifiers[0].id;
|
||||
dataCoaPayable.balanceType = balanceType.CREDIT;
|
||||
dataCoaPayable.type = coaType.ACCOUNT_PAYABLE;
|
||||
|
||||
await this.coaService.create(dataCoaWallet);
|
||||
await this.coaService.create(dataCoaAR);
|
||||
await this.coaService.create(dataCoaPayable);
|
||||
|
||||
return this.usersRepository.findOneOrFail(result.identifiers[0].id);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user