mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
fix: handle make journal errors with contacts.
This commit is contained in:
@@ -12,6 +12,7 @@ const ERROR = {
|
|||||||
RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS: 'RECEIVABLE.ENTRIES.HAS.NO.CUSTOMERS',
|
RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS: 'RECEIVABLE.ENTRIES.HAS.NO.CUSTOMERS',
|
||||||
CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO:
|
CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO:
|
||||||
'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
|
'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
|
||||||
|
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Transform API errors in toasts messages.
|
// Transform API errors in toasts messages.
|
||||||
@@ -27,14 +28,6 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
|
|||||||
newErrors = setWith(newErrors, `entries.[${index}].${prop}`, message);
|
newErrors = setWith(newErrors, `entries.[${index}].${prop}`, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((error = getError(ERROR.PAYABLE_ENTRIES_HAS_NO_VENDORS))) {
|
|
||||||
toastMessages.push(
|
|
||||||
formatMessage({
|
|
||||||
id: 'vendors_should_selected_with_payable_account_only',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
|
||||||
}
|
|
||||||
if ((error = getError(ERROR.RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS))) {
|
if ((error = getError(ERROR.RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS))) {
|
||||||
toastMessages.push(
|
toastMessages.push(
|
||||||
formatMessage({
|
formatMessage({
|
||||||
@@ -43,21 +36,20 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
|
|||||||
);
|
);
|
||||||
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
||||||
}
|
}
|
||||||
if ((error = getError(ERROR.CUSTOMERS_NOT_WITH_RECEVIABLE_ACC))) {
|
if ((error = getError(ERROR.ENTRIES_SHOULD_ASSIGN_WITH_CONTACT))) {
|
||||||
|
if (error.meta.contact_type === 'customer') {
|
||||||
toastMessages.push(
|
toastMessages.push(
|
||||||
formatMessage({
|
formatMessage({
|
||||||
id: 'customers_should_selected_with_receivable_account_only',
|
id: 'receivable_accounts_should_assign_with_customers',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
setEntriesErrors(error.indexes, 'account_id', 'error');
|
|
||||||
}
|
}
|
||||||
if ((error = getError(ERROR.VENDORS_NOT_WITH_PAYABLE_ACCOUNT))) {
|
if (error.meta.contact_type === 'vendor') {
|
||||||
toastMessages.push(
|
toastMessages.push(
|
||||||
formatMessage({
|
formatMessage({ id: 'payable_accounts_should_assign_with_vendors' }),
|
||||||
id: 'vendors_should_selected_with_payable_account_only',
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
setEntriesErrors(error.indexes, 'account_id', 'error');
|
}
|
||||||
|
setEntriesErrors(error.meta.indexes, 'contact_id', 'error');
|
||||||
}
|
}
|
||||||
if ((error = getError(ERROR.JOURNAL_NUMBER_ALREADY_EXISTS))) {
|
if ((error = getError(ERROR.JOURNAL_NUMBER_ALREADY_EXISTS))) {
|
||||||
newErrors = setWith(
|
newErrors = setWith(
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.getManualJournalsList.bind(this)),
|
asyncMiddleware(this.getManualJournalsList.bind(this)),
|
||||||
this.dynamicListService.handlerErrorsToResponse,
|
this.dynamicListService.handlerErrorsToResponse,
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/:id',
|
'/:id',
|
||||||
asyncMiddleware(this.getManualJournal.bind(this)),
|
asyncMiddleware(this.getManualJournal.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/publish', [
|
'/publish', [
|
||||||
@@ -43,7 +43,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.publishManualJournals.bind(this)),
|
asyncMiddleware(this.publishManualJournals.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/:id/publish', [
|
'/:id/publish', [
|
||||||
@@ -51,7 +51,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.publishManualJournal.bind(this)),
|
asyncMiddleware(this.publishManualJournal.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/:id', [
|
'/:id', [
|
||||||
@@ -60,7 +60,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.editManualJournal.bind(this)),
|
asyncMiddleware(this.editManualJournal.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:id', [
|
'/:id', [
|
||||||
@@ -68,7 +68,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.deleteManualJournal.bind(this)),
|
asyncMiddleware(this.deleteManualJournal.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
'/', [
|
'/', [
|
||||||
@@ -76,7 +76,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.deleteBulkManualJournals.bind(this)),
|
asyncMiddleware(this.deleteBulkManualJournals.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/', [
|
'/', [
|
||||||
@@ -84,7 +84,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
asyncMiddleware(this.makeJournalEntries.bind(this)),
|
asyncMiddleware(this.makeJournalEntries.bind(this)),
|
||||||
this.catchServiceErrors,
|
this.catchServiceErrors.bind(this),
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
.escape()
|
.escape()
|
||||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||||
check('description')
|
check('description')
|
||||||
.optional()
|
.optional({ nullable: true })
|
||||||
.isString()
|
.isString()
|
||||||
.trim()
|
.trim()
|
||||||
.escape()
|
.escape()
|
||||||
@@ -153,7 +153,7 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
.toFloat(),
|
.toFloat(),
|
||||||
check('entries.*.account_id').isInt({ max: DATATYPES_LENGTH.INT_10 }).toInt(),
|
check('entries.*.account_id').isInt({ max: DATATYPES_LENGTH.INT_10 }).toInt(),
|
||||||
check('entries.*.note')
|
check('entries.*.note')
|
||||||
.optional()
|
.optional({ nullable: true })
|
||||||
.isString()
|
.isString()
|
||||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
||||||
check('entries.*.contact_id')
|
check('entries.*.contact_id')
|
||||||
@@ -368,43 +368,45 @@ export default class ManualJournalsController extends BaseController {
|
|||||||
if (error.errorType === 'credit_debit_not_equal_zero') {
|
if (error.errorType === 'credit_debit_not_equal_zero') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'Credit and debit should not be equal zero.',
|
'Credit and debit should not be equal zero.',
|
||||||
{ errors: [{ type: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO', code: 400, }] }
|
{ errors: [{ type: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO', code: 200, }] }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (error.errorType === 'credit_debit_not_equal') {
|
if (error.errorType === 'credit_debit_not_equal') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'Credit and debit should be equal.',
|
'Credit and debit should be equal.',
|
||||||
{ errors: [{ type: 'CREDIT.DEBIT.NOT.EQUALS', code: 100 }] }
|
{ errors: [{ type: 'CREDIT.DEBIT.NOT.EQUALS', code: 300 }] }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (error.errorType === 'acccounts_ids_not_found') {
|
if (error.errorType === 'acccounts_ids_not_found') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'Journal entries some of accounts ids not exists.',
|
'Journal entries some of accounts ids not exists.',
|
||||||
{ errors: [{ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200 }] }
|
{ errors: [{ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 400 }] }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (error.errorType === 'journal_number_exists') {
|
if (error.errorType === 'journal_number_exists') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'Journal number should be unique.',
|
'Journal number should be unique.',
|
||||||
{ errors: [{ type: 'JOURNAL.NUMBER.ALREADY.EXISTS', code: 300 }] },
|
{ errors: [{ type: 'JOURNAL.NUMBER.ALREADY.EXISTS', code: 500 }] },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'payabel_entries_have_no_vendors') {
|
if (error.errorType === 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'',
|
'',
|
||||||
{ errors: [{ type: '' }] },
|
{
|
||||||
);
|
errors: [
|
||||||
|
{
|
||||||
|
type: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
||||||
|
code: 600,
|
||||||
|
meta: this.transfromToResponse(error.payload),
|
||||||
}
|
}
|
||||||
if (error.errorType === 'receivable_entries_have_no_customers') {
|
]
|
||||||
return res.boom.badRequest(
|
},
|
||||||
'',
|
|
||||||
{ errors: [{ type: '' }] },
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (error.errorType === 'contacts_not_found') {
|
if (error.errorType === 'contacts_not_found') {
|
||||||
return res.boom.badRequest(
|
return res.boom.badRequest(
|
||||||
'',
|
'',
|
||||||
{ errors: [{ type: '' }] },
|
{ errors: [{ type: 'CONTACTS_NOT_FOUND', code: 700 }] },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
export default class ServiceError {
|
export default class ServiceError {
|
||||||
errorType: string;
|
errorType: string;
|
||||||
message: string;
|
message: string;
|
||||||
|
payload: any;
|
||||||
|
|
||||||
constructor(errorType: string, message?: string) {
|
constructor(errorType: string, message?: string, payload?: any) {
|
||||||
this.errorType = errorType;
|
this.errorType = errorType;
|
||||||
this.message = message || null;
|
this.message = message || null;
|
||||||
|
|
||||||
|
this.payload = payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { difference, sumBy, omit } from 'lodash';
|
import { difference, sumBy, omit, groupBy } from 'lodash';
|
||||||
import { Service, Inject } from "typedi";
|
import { Service, Inject } from 'typedi';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { ServiceError } from "exceptions";
|
import { ServiceError } from 'exceptions';
|
||||||
import {
|
import {
|
||||||
IManualJournalDTO,
|
IManualJournalDTO,
|
||||||
IManualJournalsService,
|
IManualJournalsService,
|
||||||
@@ -27,11 +27,15 @@ const ERRORS = {
|
|||||||
CREDIT_DEBIT_NOT_EQUAL: 'credit_debit_not_equal',
|
CREDIT_DEBIT_NOT_EQUAL: 'credit_debit_not_equal',
|
||||||
ACCCOUNTS_IDS_NOT_FOUND: 'acccounts_ids_not_found',
|
ACCCOUNTS_IDS_NOT_FOUND: 'acccounts_ids_not_found',
|
||||||
JOURNAL_NUMBER_EXISTS: 'journal_number_exists',
|
JOURNAL_NUMBER_EXISTS: 'journal_number_exists',
|
||||||
RECEIVABLE_ENTRIES_NO_CUSTOMERS: 'receivable_entries_have_no_customers',
|
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
||||||
PAYABLE_ENTRIES_NO_VENDORS: 'payabel_entries_have_no_vendors',
|
|
||||||
CONTACTS_NOT_FOUND: 'contacts_not_found',
|
CONTACTS_NOT_FOUND: 'contacts_not_found',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CONTACTS_CONFIG = [
|
||||||
|
{ accountBySlug: 'accounts-receivable', contactService: 'customer', assignRequired: false, },
|
||||||
|
{ accountBySlug: 'accounts-payable', contactService: 'vendor', assignRequired: true },
|
||||||
|
];
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class ManualJournalsService implements IManualJournalsService {
|
export default class ManualJournalsService implements IManualJournalsService {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -51,14 +55,23 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} manualJournalId
|
* @param {number} manualJournalId
|
||||||
*/
|
*/
|
||||||
private async validateManualJournalExistance(tenantId: number, manualJournalId: number) {
|
private async validateManualJournalExistance(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalId: number
|
||||||
|
) {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
this.logger.info('[manual_journal] trying to validate existance.', { tenantId, manualJournalId });
|
this.logger.info('[manual_journal] trying to validate existance.', {
|
||||||
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
|
});
|
||||||
const manualJournal = await ManualJournal.query().findById(manualJournalId);
|
const manualJournal = await ManualJournal.query().findById(manualJournalId);
|
||||||
|
|
||||||
if (!manualJournal) {
|
if (!manualJournal) {
|
||||||
this.logger.warn('[manual_journal] not exists on the storage.', { tenantId, manualJournalId });
|
this.logger.warn('[manual_journal] not exists on the storage.', {
|
||||||
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
|
});
|
||||||
throw new ServiceError(ERRORS.NOT_FOUND);
|
throw new ServiceError(ERRORS.NOT_FOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,10 +82,16 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number[]} manualJournalsIds
|
* @param {number[]} manualJournalsIds
|
||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
*/
|
*/
|
||||||
private async validateManualJournalsExistance(tenantId: number, manualJournalsIds: number[]) {
|
private async validateManualJournalsExistance(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalsIds: number[]
|
||||||
|
) {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
const manualJournals = await ManualJournal.query().whereIn('id', manualJournalsIds);
|
const manualJournals = await ManualJournal.query().whereIn(
|
||||||
|
'id',
|
||||||
|
manualJournalsIds
|
||||||
|
);
|
||||||
|
|
||||||
const notFoundManualJournals = difference(
|
const notFoundManualJournals = difference(
|
||||||
manualJournalsIds,
|
manualJournalsIds,
|
||||||
@@ -100,11 +119,15 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (totalCredit <= 0 || totalDebit <= 0) {
|
if (totalCredit <= 0 || totalDebit <= 0) {
|
||||||
this.logger.info('[manual_journal] the total credit and debit equals zero.');
|
this.logger.info(
|
||||||
|
'[manual_journal] the total credit and debit equals zero.'
|
||||||
|
);
|
||||||
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL_ZERO);
|
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL_ZERO);
|
||||||
}
|
}
|
||||||
if (totalCredit !== totalDebit) {
|
if (totalCredit !== totalDebit) {
|
||||||
this.logger.info('[manual_journal] the total credit not equals total debit.');
|
this.logger.info(
|
||||||
|
'[manual_journal] the total credit not equals total debit.'
|
||||||
|
);
|
||||||
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL);
|
throw new ServiceError(ERRORS.CREDIT_DEBIT_NOT_EQUAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,9 +137,12 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
*/
|
*/
|
||||||
private async validateAccountsExistance(tenantId: number, manualJournalDTO: IManualJournalDTO) {
|
private async validateAccountsExistance(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalDTO: IManualJournalDTO
|
||||||
|
) {
|
||||||
const { Account } = this.tenancy.models(tenantId);
|
const { Account } = this.tenancy.models(tenantId);
|
||||||
const manualAccountsIds = manualJournalDTO.entries.map(e => e.accountId);
|
const manualAccountsIds = manualJournalDTO.entries.map((e) => e.accountId);
|
||||||
|
|
||||||
const accounts = await Account.query()
|
const accounts = await Account.query()
|
||||||
.whereIn('id', manualAccountsIds)
|
.whereIn('id', manualAccountsIds)
|
||||||
@@ -125,7 +151,10 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
const storedAccountsIds = accounts.map((account) => account.id);
|
const storedAccountsIds = accounts.map((account) => account.id);
|
||||||
|
|
||||||
if (difference(manualAccountsIds, storedAccountsIds).length > 0) {
|
if (difference(manualAccountsIds, storedAccountsIds).length > 0) {
|
||||||
this.logger.info('[manual_journal] some entries accounts not exist.', { tenantId, manualAccountsIds });
|
this.logger.info('[manual_journal] some entries accounts not exist.', {
|
||||||
|
tenantId,
|
||||||
|
manualAccountsIds,
|
||||||
|
});
|
||||||
throw new ServiceError(ERRORS.ACCCOUNTS_IDS_NOT_FOUND);
|
throw new ServiceError(ERRORS.ACCCOUNTS_IDS_NOT_FOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,12 +164,15 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
*/
|
*/
|
||||||
private async validateManualJournalNoUnique(tenantId: number, manualJournalDTO: IManualJournalDTO, notId?: numebr) {
|
private async validateManualJournalNoUnique(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalDTO: IManualJournalDTO,
|
||||||
|
notId?: number
|
||||||
|
) {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
const journalNumber = await ManualJournal.query().where(
|
const journalNumber = await ManualJournal.query()
|
||||||
'journal_number',
|
.where('journal_number', manualJournalDTO.journalNumber)
|
||||||
manualJournalDTO.journalNumber,
|
.onBuild((builder) => {
|
||||||
).onBuild((builder) => {
|
|
||||||
if (notId) {
|
if (notId) {
|
||||||
builder.whereNot('id', notId);
|
builder.whereNot('id', notId);
|
||||||
}
|
}
|
||||||
@@ -151,39 +183,57 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate entries that have receivable account should have customer type.
|
*
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId
|
||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
|
* @param {string} accountBySlug
|
||||||
|
* @param {string} contactType
|
||||||
*/
|
*/
|
||||||
private async validateReceivableEntries(tenantId: number, manualJournalDTO: IManualJournalDTO): Promise<void> {
|
private async validateAccountsWithContactType(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalDTO: IManualJournalDTO,
|
||||||
|
accountBySlug: string,
|
||||||
|
contactType: string,
|
||||||
|
contactRequired: boolean = true,
|
||||||
|
): Promise<void> {
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
const receivableAccount = await accountRepository.getBySlug('accounts-receivable');
|
const payableAccount = await accountRepository.getBySlug(accountBySlug);
|
||||||
|
const entriesHasNoVendorContact = manualJournalDTO.entries.filter(
|
||||||
const entriesHasNoReceivableAccount = manualJournalDTO.entries.filter(
|
(e) =>
|
||||||
(e) => (e.accountId === receivableAccount.id) &&
|
e.accountId === payableAccount.id &&
|
||||||
(!e.contactId || e.contactType !== 'customer')
|
((!e.contactId && contactRequired) || e.contactType !== contactType)
|
||||||
);
|
);
|
||||||
if (entriesHasNoReceivableAccount.length > 0) {
|
if (entriesHasNoVendorContact.length > 0) {
|
||||||
throw new ServiceError(ERRORS.RECEIVABLE_ENTRIES_NO_CUSTOMERS);
|
const indexes = entriesHasNoVendorContact.map(e => e.index);
|
||||||
|
|
||||||
|
throw new ServiceError(ERRORS.ENTRIES_SHOULD_ASSIGN_WITH_CONTACT, '', {
|
||||||
|
contactType,
|
||||||
|
accountBySlug,
|
||||||
|
indexes
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates payable entries should have vendor type.
|
* Dynamic validates accounts with contacts.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId
|
||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
*/
|
*/
|
||||||
private async validatePayableEntries(tenantId: number, manualJournalDTO: IManualJournalDTO): Promise<void> {
|
private async dynamicValidateAccountsWithContactType(
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
tenantId: number,
|
||||||
const payableAccount = await accountRepository.getBySlug('accounts-payable');
|
manualJournalDTO: IManualJournalDTO,
|
||||||
|
): Promise<any>{
|
||||||
const entriesHasNoVendorContact = manualJournalDTO.entries.filter(
|
return Promise.all(
|
||||||
(e) => (e.accountId === payableAccount.id) &&
|
CONTACTS_CONFIG.map(({ accountBySlug, contactService, assignRequired }) =>
|
||||||
(!e.contactId || e.contactType !== 'vendor')
|
this.validateAccountsWithContactType(
|
||||||
|
tenantId,
|
||||||
|
manualJournalDTO,
|
||||||
|
accountBySlug,
|
||||||
|
contactService,
|
||||||
|
assignRequired
|
||||||
|
),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
if (entriesHasNoVendorContact.length > 0) {
|
|
||||||
throw new ServiceError(ERRORS.PAYABLE_ENTRIES_NO_VENDORS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,20 +241,43 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
*/
|
*/
|
||||||
private async validateContactsExistance(tenantId: number, manualJournalDTO: IManualJournalDTO) {
|
private async validateContactsExistance(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalDTO: IManualJournalDTO,
|
||||||
|
) {
|
||||||
const { contactRepository } = this.tenancy.repositories(tenantId);
|
const { contactRepository } = this.tenancy.repositories(tenantId);
|
||||||
const manualJCotactsIds = manualJournalDTO.entries
|
|
||||||
.filter((entry) => entry.contactId)
|
|
||||||
.map((entry) => entry.contactId);
|
|
||||||
|
|
||||||
if (manualJCotactsIds.length > 0) {
|
// Filters the entries that have contact only.
|
||||||
const storedContacts = await contactRepository.findByIds(manualJCotactsIds);
|
const entriesContactPairs = manualJournalDTO.entries
|
||||||
const storedContactsIds = storedContacts.map((c) => c.id);
|
.filter((entry) => entry.contactId);
|
||||||
|
|
||||||
const notFoundContactsIds = difference(manualJCotactsIds, storedContactsIds);
|
if (entriesContactPairs.length > 0) {
|
||||||
|
const entriesContactsIds = entriesContactPairs.map(entry => entry.contactId);
|
||||||
|
|
||||||
|
// Retrieve all stored contacts on the storage from contacts entries.
|
||||||
|
const storedContacts = await contactRepository.findByIds(
|
||||||
|
entriesContactsIds,
|
||||||
|
);
|
||||||
|
// Converts the stored contacts to map with id as key and entry as value.
|
||||||
|
const storedContactsMap = new Map(storedContacts.map(contact => [contact.id, contact]));
|
||||||
|
const notFoundContactsIds = [];
|
||||||
|
|
||||||
|
entriesContactPairs.forEach((contactEntry) => {
|
||||||
|
const storedContact = storedContactsMap.get(contactEntry.contactId);
|
||||||
|
|
||||||
|
// in case the contact id not found or contact type no equals pushed to
|
||||||
|
// not found contacts.
|
||||||
|
if (
|
||||||
|
!storedContact ||
|
||||||
|
storedContact.contactService !== contactEntry.contactType
|
||||||
|
) {
|
||||||
|
notFoundContactsIds.push(storedContact);
|
||||||
|
}
|
||||||
|
});
|
||||||
if (notFoundContactsIds.length > 0) {
|
if (notFoundContactsIds.length > 0) {
|
||||||
throw new ServiceError(ERRORS.CONTACTS_NOT_FOUND);
|
throw new ServiceError(ERRORS.CONTACTS_NOT_FOUND, '', {
|
||||||
|
contactsIds: notFoundContactsIds,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +287,10 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
* @param {ISystemUser} authorizedUser
|
* @param {ISystemUser} authorizedUser
|
||||||
*/
|
*/
|
||||||
private transformDTOToModel(manualJournalDTO: IManualJournalDTO, user: ISystemUser): IManualJournal {
|
private transformDTOToModel(
|
||||||
|
manualJournalDTO: IManualJournalDTO,
|
||||||
|
user: ISystemUser
|
||||||
|
): IManualJournal {
|
||||||
const amount = sumBy(manualJournalDTO.entries, 'credit') || 0;
|
const amount = sumBy(manualJournalDTO.entries, 'credit') || 0;
|
||||||
const date = moment(manualJournalDTO.date).format('YYYY-MM-DD');
|
const date = moment(manualJournalDTO.date).format('YYYY-MM-DD');
|
||||||
|
|
||||||
@@ -235,7 +311,7 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
return entries.map((entry: IManualJournalEntryDTO) => ({
|
return entries.map((entry: IManualJournalEntryDTO) => ({
|
||||||
...omit(entry, ['accountId']),
|
...omit(entry, ['accountId']),
|
||||||
account: entry.accountId,
|
account: entry.accountId,
|
||||||
}))
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -251,28 +327,41 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
): Promise<{ manualJournal: IManualJournal }> {
|
): Promise<{ manualJournal: IManualJournal }> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Validate the total credit should equals debit.
|
||||||
this.valdiateCreditDebitTotalEquals(manualJournalDTO);
|
this.valdiateCreditDebitTotalEquals(manualJournalDTO);
|
||||||
|
|
||||||
await this.validateReceivableEntries(tenantId, manualJournalDTO);
|
// Validate the contacts existance.
|
||||||
await this.validatePayableEntries(tenantId, manualJournalDTO);
|
|
||||||
await this.validateContactsExistance(tenantId, manualJournalDTO);
|
await this.validateContactsExistance(tenantId, manualJournalDTO);
|
||||||
|
|
||||||
|
// Validate entries accounts existance.
|
||||||
await this.validateAccountsExistance(tenantId, manualJournalDTO);
|
await this.validateAccountsExistance(tenantId, manualJournalDTO);
|
||||||
|
|
||||||
|
// Validate manual journal uniquiness on the storage.
|
||||||
await this.validateManualJournalNoUnique(tenantId, manualJournalDTO);
|
await this.validateManualJournalNoUnique(tenantId, manualJournalDTO);
|
||||||
|
|
||||||
this.logger.info('[manual_journal] trying to save manual journal to the storage.', { tenantId, manualJournalDTO });
|
// Validate accounts with contact type from the given config.
|
||||||
const manualJournalObj = this.transformDTOToModel(manualJournalDTO, authorizedUser);
|
await this.dynamicValidateAccountsWithContactType(tenantId, manualJournalDTO);
|
||||||
|
|
||||||
const storedManualJournal = await ManualJournal.query().insert({
|
this.logger.info(
|
||||||
|
'[manual_journal] trying to save manual journal to the storage.',
|
||||||
|
{ tenantId, manualJournalDTO }
|
||||||
|
);
|
||||||
|
const manualJournalObj = this.transformDTOToModel(
|
||||||
|
manualJournalDTO,
|
||||||
|
authorizedUser
|
||||||
|
);
|
||||||
|
const manualJournal = await ManualJournal.query().insertAndFetch({
|
||||||
...omit(manualJournalObj, ['entries']),
|
...omit(manualJournalObj, ['entries']),
|
||||||
});
|
});
|
||||||
const manualJournal: IManualJournal = { ...manualJournalObj, id: storedManualJournal.id };
|
|
||||||
|
|
||||||
// Triggers `onManualJournalCreated` event.
|
// Triggers `onManualJournalCreated` event.
|
||||||
this.eventDispatcher.dispatch(events.manualJournals.onCreated, {
|
this.eventDispatcher.dispatch(events.manualJournals.onCreated, {
|
||||||
tenantId, manualJournal,
|
tenantId,
|
||||||
|
manualJournal,
|
||||||
});
|
});
|
||||||
this.logger.info('[manual_journal] the manual journal inserted successfully.', { tenantId });
|
this.logger.info(
|
||||||
|
'[manual_journal] the manual journal inserted successfully.',
|
||||||
|
{ tenantId }
|
||||||
|
);
|
||||||
|
|
||||||
return { manualJournal };
|
return { manualJournal };
|
||||||
}
|
}
|
||||||
@@ -292,24 +381,46 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
): Promise<{ manualJournal: IManualJournal }> {
|
): Promise<{ manualJournal: IManualJournal }> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Validates the manual journal existance on the storage.
|
||||||
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
||||||
|
|
||||||
|
// Validates the total credit and debit to be equals.
|
||||||
this.valdiateCreditDebitTotalEquals(manualJournalDTO);
|
this.valdiateCreditDebitTotalEquals(manualJournalDTO);
|
||||||
|
|
||||||
|
// Validate the contacts existance.
|
||||||
|
await this.validateContactsExistance(tenantId, manualJournalDTO);
|
||||||
|
|
||||||
|
// Validates entries accounts existance.
|
||||||
await this.validateAccountsExistance(tenantId, manualJournalDTO);
|
await this.validateAccountsExistance(tenantId, manualJournalDTO);
|
||||||
await this.validateManualJournalNoUnique(tenantId, manualJournalDTO, manualJournalId);
|
|
||||||
|
|
||||||
const manualJournalObj = this.transformDTOToModel(manualJournalDTO, authorizedUser);
|
// Validates the manual journal number uniquiness.
|
||||||
|
await this.validateManualJournalNoUnique(
|
||||||
|
tenantId,
|
||||||
|
manualJournalDTO,
|
||||||
|
manualJournalId
|
||||||
|
);
|
||||||
|
// Validate accounts with contact type from the given config.
|
||||||
|
await this.dynamicValidateAccountsWithContactType(tenantId, manualJournalDTO);
|
||||||
|
|
||||||
const storedManualJournal = await ManualJournal.query().where('id', manualJournalId)
|
const manualJournalObj = this.transformDTOToModel(
|
||||||
|
manualJournalDTO,
|
||||||
|
authorizedUser
|
||||||
|
);
|
||||||
|
|
||||||
|
const storedManualJournal = await ManualJournal.query()
|
||||||
|
.where('id', manualJournalId)
|
||||||
.patch({
|
.patch({
|
||||||
...omit(manualJournalObj, ['entries']),
|
...omit(manualJournalObj, ['entries']),
|
||||||
});
|
});
|
||||||
const manualJournal: IManualJournal = { ...manualJournalObj, id: manualJournalId };
|
const manualJournal: IManualJournal = {
|
||||||
|
...manualJournalObj,
|
||||||
|
id: manualJournalId,
|
||||||
|
};
|
||||||
|
|
||||||
// Triggers `onManualJournalEdited` event.
|
// Triggers `onManualJournalEdited` event.
|
||||||
this.eventDispatcher.dispatch(events.manualJournals.onEdited, {
|
this.eventDispatcher.dispatch(events.manualJournals.onEdited, {
|
||||||
tenantId, manualJournal,
|
tenantId,
|
||||||
|
manualJournal,
|
||||||
});
|
});
|
||||||
return { manualJournal };
|
return { manualJournal };
|
||||||
}
|
}
|
||||||
@@ -320,18 +431,28 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} manualJournalId
|
* @param {number} manualJournalId
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async deleteManualJournal(tenantId: number, manualJournalId: number): Promise<void> {
|
public async deleteManualJournal(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalId: number
|
||||||
|
): Promise<void> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
||||||
|
|
||||||
this.logger.info('[manual_journal] trying to delete the manual journal.', { tenantId, manualJournalId });
|
this.logger.info('[manual_journal] trying to delete the manual journal.', {
|
||||||
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
|
});
|
||||||
await ManualJournal.query().findById(manualJournalId).delete();
|
await ManualJournal.query().findById(manualJournalId).delete();
|
||||||
|
|
||||||
// Triggers `onManualJournalDeleted` event.
|
// Triggers `onManualJournalDeleted` event.
|
||||||
this.eventDispatcher.dispatch(events.manualJournals.onDeleted, {
|
this.eventDispatcher.dispatch(events.manualJournals.onDeleted, {
|
||||||
tenantId, manualJournalId,
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
});
|
});
|
||||||
this.logger.info('[manual_journal] the given manual journal deleted successfully.', { tenantId, manualJournalId });
|
this.logger.info(
|
||||||
|
'[manual_journal] the given manual journal deleted successfully.',
|
||||||
|
{ tenantId, manualJournalId }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -340,18 +461,28 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number[]} manualJournalsIds
|
* @param {number[]} manualJournalsIds
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async deleteManualJournals(tenantId: number, manualJournalsIds: number[]): Promise<void> {
|
public async deleteManualJournals(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalsIds: number[]
|
||||||
|
): Promise<void> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
await this.validateManualJournalsExistance(tenantId, manualJournalsIds);
|
await this.validateManualJournalsExistance(tenantId, manualJournalsIds);
|
||||||
|
|
||||||
this.logger.info('[manual_journal] trying to delete the manual journals.', { tenantId, manualJournalsIds });
|
this.logger.info('[manual_journal] trying to delete the manual journals.', {
|
||||||
|
tenantId,
|
||||||
|
manualJournalsIds,
|
||||||
|
});
|
||||||
await ManualJournal.query().whereIn('id', manualJournalsIds).delete();
|
await ManualJournal.query().whereIn('id', manualJournalsIds).delete();
|
||||||
|
|
||||||
// Triggers `onManualJournalDeletedBulk` event.
|
// Triggers `onManualJournalDeletedBulk` event.
|
||||||
this.eventDispatcher.dispatch(events.manualJournals.onDeletedBulk, {
|
this.eventDispatcher.dispatch(events.manualJournals.onDeletedBulk, {
|
||||||
tenantId, manualJournalsIds,
|
tenantId,
|
||||||
|
manualJournalsIds,
|
||||||
});
|
});
|
||||||
this.logger.info('[manual_journal] the given manual journals deleted successfully.', { tenantId, manualJournalsIds });
|
this.logger.info(
|
||||||
|
'[manual_journal] the given manual journals deleted successfully.',
|
||||||
|
{ tenantId, manualJournalsIds }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -359,18 +490,30 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number[]} manualJournalsIds
|
* @param {number[]} manualJournalsIds
|
||||||
*/
|
*/
|
||||||
public async publishManualJournals(tenantId: number, manualJournalsIds: number[]): Promise<void> {
|
public async publishManualJournals(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalsIds: number[]
|
||||||
|
): Promise<void> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
await this.validateManualJournalsExistance(tenantId, manualJournalsIds);
|
await this.validateManualJournalsExistance(tenantId, manualJournalsIds);
|
||||||
|
|
||||||
this.logger.info('[manual_journal] trying to publish the manual journal.', { tenantId, manualJournalsIds });
|
this.logger.info('[manual_journal] trying to publish the manual journal.', {
|
||||||
await ManualJournal.query().whereIn('id', manualJournalsIds).patch({ status: 1, });
|
tenantId,
|
||||||
|
manualJournalsIds,
|
||||||
|
});
|
||||||
|
await ManualJournal.query()
|
||||||
|
.whereIn('id', manualJournalsIds)
|
||||||
|
.patch({ status: 1 });
|
||||||
|
|
||||||
// Triggers `onManualJournalPublishedBulk` event.
|
// Triggers `onManualJournalPublishedBulk` event.
|
||||||
this.eventDispatcher.dispatch(events.manualJournals.onPublishedBulk, {
|
this.eventDispatcher.dispatch(events.manualJournals.onPublishedBulk, {
|
||||||
tenantId, manualJournalsIds,
|
tenantId,
|
||||||
|
manualJournalsIds,
|
||||||
});
|
});
|
||||||
this.logger.info('[manual_journal] the given manula journal published successfully.', { tenantId, manualJournalId });
|
this.logger.info(
|
||||||
|
'[manual_journal] the given manula journal published successfully.',
|
||||||
|
{ tenantId, manualJournalId }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -378,18 +521,28 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {number} manualJournalId
|
* @param {number} manualJournalId
|
||||||
*/
|
*/
|
||||||
public async publishManualJournal(tenantId: number, manualJournalId: number): Promise<void> {
|
public async publishManualJournal(
|
||||||
|
tenantId: number,
|
||||||
|
manualJournalId: number
|
||||||
|
): Promise<void> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
||||||
|
|
||||||
this.logger.info('[manual_journal] trying to publish the manual journal.', { tenantId, manualJournalId });
|
this.logger.info('[manual_journal] trying to publish the manual journal.', {
|
||||||
await ManualJournal.query().findById(manualJournalId).patch({ status: 1, });
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
|
});
|
||||||
|
await ManualJournal.query().findById(manualJournalId).patch({ status: 1 });
|
||||||
|
|
||||||
// Triggers `onManualJournalPublishedBulk` event.
|
// Triggers `onManualJournalPublishedBulk` event.
|
||||||
this.eventDispatcher.dispatch(events.manualJournals.onPublished, {
|
this.eventDispatcher.dispatch(events.manualJournals.onPublished, {
|
||||||
tenantId, manualJournalId,
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
});
|
});
|
||||||
this.logger.info('[manual_journal] the given manula journal published successfully.', { tenantId, manualJournalId });
|
this.logger.info(
|
||||||
|
'[manual_journal] the given manula journal published successfully.',
|
||||||
|
{ tenantId, manualJournalId }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -400,15 +553,28 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
public async getManualJournals(
|
public async getManualJournals(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
filter: IManualJournalsFilter
|
filter: IManualJournalsFilter
|
||||||
): Promise<{ manualJournals: IManualJournal, pagination: IPaginationMeta, filterMeta: IFilterMeta }> {
|
): Promise<{
|
||||||
|
manualJournals: IManualJournal;
|
||||||
|
pagination: IPaginationMeta;
|
||||||
|
filterMeta: IFilterMeta;
|
||||||
|
}> {
|
||||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||||
const dynamicList = await this.dynamicListService.dynamicList(tenantId, ManualJournal, filter);
|
const dynamicList = await this.dynamicListService.dynamicList(
|
||||||
|
tenantId,
|
||||||
|
ManualJournal,
|
||||||
|
filter
|
||||||
|
);
|
||||||
|
|
||||||
this.logger.info('[manual_journals] trying to get manual journals list.', { tenantId, filter });
|
this.logger.info('[manual_journals] trying to get manual journals list.', {
|
||||||
const { results, pagination } = await ManualJournal.query().onBuild((builder) => {
|
tenantId,
|
||||||
|
filter,
|
||||||
|
});
|
||||||
|
const { results, pagination } = await ManualJournal.query()
|
||||||
|
.onBuild((builder) => {
|
||||||
dynamicList.buildQuery()(builder);
|
dynamicList.buildQuery()(builder);
|
||||||
builder.withGraphFetched('entries.account');
|
builder.withGraphFetched('entries.account');
|
||||||
}).pagination(filter.page - 1, filter.pageSize);
|
})
|
||||||
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
manualJournals: results,
|
manualJournals: results,
|
||||||
@@ -427,7 +593,10 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
|
|
||||||
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
await this.validateManualJournalExistance(tenantId, manualJournalId);
|
||||||
|
|
||||||
this.logger.info('[manual_journals] trying to get specific manual journal.', { tenantId, manualJournalId });
|
this.logger.info(
|
||||||
|
'[manual_journals] trying to get specific manual journal.',
|
||||||
|
{ tenantId, manualJournalId }
|
||||||
|
);
|
||||||
const manualJournal = await ManualJournal.query()
|
const manualJournal = await ManualJournal.query()
|
||||||
.findById(manualJournalId)
|
.findById(manualJournalId)
|
||||||
.withGraphFetched('entries')
|
.withGraphFetched('entries')
|
||||||
@@ -446,25 +615,34 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
public async writeJournalEntries(
|
public async writeJournalEntries(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
manualJournalId: number,
|
manualJournalId: number,
|
||||||
manualJournalObj?: IManualJournal|null,
|
manualJournalObj?: IManualJournal | null,
|
||||||
override?: Boolean,
|
override?: Boolean
|
||||||
) {
|
) {
|
||||||
const journal = new JournalPoster(tenantId);
|
const journal = new JournalPoster(tenantId);
|
||||||
const journalCommands = new JournalCommands(journal);
|
const journalCommands = new JournalCommands(journal);
|
||||||
|
|
||||||
if (override) {
|
if (override) {
|
||||||
this.logger.info('[manual_journal] trying to revert journal entries.', { tenantId, manualJournalId });
|
this.logger.info('[manual_journal] trying to revert journal entries.', {
|
||||||
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
|
});
|
||||||
await journalCommands.revertJournalEntries(manualJournalId, 'Journal');
|
await journalCommands.revertJournalEntries(manualJournalId, 'Journal');
|
||||||
}
|
}
|
||||||
if (manualJournalObj) {
|
if (manualJournalObj) {
|
||||||
journalCommands.manualJournal(manualJournalObj, manualJournalId);
|
journalCommands.manualJournal(manualJournalObj, manualJournalId);
|
||||||
}
|
}
|
||||||
this.logger.info('[manual_journal] trying to save journal entries.', { tenantId, manualJournalId });
|
this.logger.info('[manual_journal] trying to save journal entries.', {
|
||||||
|
tenantId,
|
||||||
|
manualJournalId,
|
||||||
|
});
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
journal.saveBalance(),
|
journal.saveBalance(),
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
journal.saveEntries(),
|
journal.saveEntries(),
|
||||||
]);
|
]);
|
||||||
this.logger.info('[manual_journal] the journal entries saved successfully.', { tenantId, manualJournalId });
|
this.logger.info(
|
||||||
|
'[manual_journal] the journal entries saved successfully.',
|
||||||
|
{ tenantId, manualJournalId }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user