mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 05:10:31 +00:00
218 lines
6.5 KiB
JavaScript
218 lines
6.5 KiB
JavaScript
import { check, query, validationResult } from 'express-validator';
|
|
import express from 'express';
|
|
import { difference } from 'lodash';
|
|
import Account from '@/models/Account';
|
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
|
import JournalPoster from '@/services/Accounting/JournalPoster';
|
|
import JournalEntry from '@/services/Accounting/JournalEntry';
|
|
import ManualJournal from '@/models/JournalEntry';
|
|
|
|
export default {
|
|
/**
|
|
* Router constructor.
|
|
*/
|
|
router() {
|
|
const router = express.Router();
|
|
router.use(JWTAuth);
|
|
|
|
router.post('/make-journal-entries',
|
|
this.makeJournalEntries.validation,
|
|
asyncMiddleware(this.makeJournalEntries.handler));
|
|
|
|
router.post('/recurring-journal-entries',
|
|
this.recurringJournalEntries.validation,
|
|
asyncMiddleware(this.recurringJournalEntries.handler));
|
|
|
|
router.post('quick-journal-entries',
|
|
this.quickJournalEntries.validation,
|
|
asyncMiddleware(this.quickJournalEntries.handler));
|
|
|
|
return router;
|
|
},
|
|
|
|
/**
|
|
* Make journal entrires.
|
|
*/
|
|
makeJournalEntries: {
|
|
validation: [
|
|
check('date').isISO8601(),
|
|
check('reference').exists(),
|
|
check('entries').isArray({ min: 1 }),
|
|
check('entries.*.credit').isNumeric().toInt(),
|
|
check('entries.*.debit').isNumeric().toInt(),
|
|
check('entries.*.account_id').isNumeric().toInt(),
|
|
check('entries.*.note').optional(),
|
|
],
|
|
async handler(req, res) {
|
|
const validationErrors = validationResult(req);
|
|
|
|
if (!validationErrors.isEmpty()) {
|
|
return res.boom.badData(null, {
|
|
code: 'validation_error', ...validationErrors,
|
|
});
|
|
}
|
|
const form = {
|
|
date: new Date(),
|
|
...req.body,
|
|
};
|
|
const errorReasons = [];
|
|
let totalCredit = 0;
|
|
let totalDebit = 0;
|
|
|
|
form.entries.forEach((entry) => {
|
|
if (entry.credit > 0) {
|
|
totalCredit += entry.credit;
|
|
}
|
|
if (entry.debit > 0) {
|
|
totalDebit += entry.debit;
|
|
}
|
|
});
|
|
if (totalCredit <= 0 || totalDebit <= 0) {
|
|
errorReasons.push({
|
|
type: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
|
|
code: 400,
|
|
});
|
|
}
|
|
if (totalCredit !== totalDebit) {
|
|
errorReasons.push({ type: 'CREDIT.DEBIT.NOT.EQUALS', code: 100 });
|
|
}
|
|
const accountsIds = form.entries.map((entry) => entry.account_id);
|
|
const accounts = await Account.query().whereIn('id', accountsIds)
|
|
.withGraphFetched('type');
|
|
|
|
const storedAccountsIds = accounts.map((account) => account.id);
|
|
|
|
if (difference(accountsIds, storedAccountsIds).length > 0) {
|
|
errorReasons.push({ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200 });
|
|
}
|
|
|
|
const journalReference = await ManualJournal.query().where('reference', form.reference);
|
|
|
|
if (journalReference.length > 0) {
|
|
errorReasons.push({ type: 'REFERENCE.ALREADY.EXISTS', code: 300 });
|
|
}
|
|
if (errorReasons.length > 0) {
|
|
return res.status(400).send({ errors: errorReasons });
|
|
}
|
|
const journalPoster = new JournalPoster();
|
|
|
|
form.entries.forEach((entry) => {
|
|
const account = accounts.find((a) => a.id === entry.account_id);
|
|
|
|
const jouranlEntry = new JournalEntry({
|
|
date: entry.date,
|
|
debit: entry.debit,
|
|
credit: entry.credit,
|
|
account: account.id,
|
|
accountNormal: account.type.normal,
|
|
note: entry.note,
|
|
});
|
|
if (entry.debit) {
|
|
journalPoster.debit(jouranlEntry);
|
|
} else {
|
|
journalPoster.credit(jouranlEntry);
|
|
}
|
|
});
|
|
|
|
// Saves the journal entries and accounts balance changes.
|
|
await Promise.all([
|
|
journalPoster.saveEntries(),
|
|
journalPoster.saveBalance(),
|
|
]);
|
|
return res.status(200).send();
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Saves recurring journal entries template.
|
|
*/
|
|
recurringJournalEntries: {
|
|
validation: [
|
|
check('template_name').exists(),
|
|
check('recurrence').exists(),
|
|
check('active').optional().isBoolean().toBoolean(),
|
|
check('entries').isArray({ min: 1 }),
|
|
check('entries.*.credit').isNumeric().toInt(),
|
|
check('entries.*.debit').isNumeric().toInt(),
|
|
check('entries.*.account_id').isNumeric().toInt(),
|
|
check('entries.*.note').optional(),
|
|
],
|
|
async handler(req, res) {
|
|
const validationErrors = validationResult(req);
|
|
|
|
if (!validationErrors.isEmpty()) {
|
|
return res.boom.badData(null, {
|
|
code: 'validation_error', ...validationErrors,
|
|
});
|
|
}
|
|
|
|
},
|
|
},
|
|
|
|
recurringJournalsList: {
|
|
validation: [
|
|
query('page').optional().isNumeric().toInt(),
|
|
query('page_size').optional().isNumeric().toInt(),
|
|
query('template_name').optional(),
|
|
],
|
|
async handler(req, res) {
|
|
const validationErrors = validationResult(req);
|
|
|
|
if (!validationErrors.isEmpty()) {
|
|
return res.boom.badData(null, {
|
|
code: 'validation_error', ...validationErrors,
|
|
});
|
|
}
|
|
},
|
|
},
|
|
|
|
quickJournalEntries: {
|
|
validation: [
|
|
check('date').exists().isISO8601(),
|
|
check('amount').exists().isNumeric().toFloat(),
|
|
check('credit_account_id').exists().isNumeric().toInt(),
|
|
check('debit_account_id').exists().isNumeric().toInt(),
|
|
check('transaction_type').exists(),
|
|
check('note').optional(),
|
|
],
|
|
async handler(req, res) {
|
|
const validationErrors = validationResult(req);
|
|
|
|
if (!validationErrors.isEmpty()) {
|
|
return res.boom.badData(null, {
|
|
code: 'validation_error', ...validationErrors,
|
|
});
|
|
}
|
|
const errorReasons = [];
|
|
const form = { ...req.body };
|
|
|
|
const foundAccounts = await Account.query()
|
|
.where('id', form.credit_account_id)
|
|
.orWhere('id', form.debit_account_id);
|
|
|
|
const creditAccount = foundAccounts.find((a) => a.id === form.credit_account_id);
|
|
const debitAccount = foundAccounts.find((a) => a.id === form.debit_account_id);
|
|
|
|
if (!creditAccount) {
|
|
errorReasons.push({ type: 'CREDIT_ACCOUNT.NOT.EXIST', code: 100 });
|
|
}
|
|
if (!debitAccount) {
|
|
errorReasons.push({ type: 'DEBIT_ACCOUNT.NOT.EXIST', code: 200 });
|
|
}
|
|
if (errorReasons.length > 0) {
|
|
return res.status(400).send({ errors: errorReasons });
|
|
}
|
|
|
|
// const journalPoster = new JournalPoster();
|
|
// const journalCredit = new JournalEntry({
|
|
// debit:
|
|
// account: debitAccount.id,
|
|
// referenceId:
|
|
// })
|
|
|
|
return res.status(200).send();
|
|
},
|
|
},
|
|
};
|