WIP server side.

This commit is contained in:
Ahmed Bouhuolia
2020-01-22 02:09:45 +02:00
parent de905d7e7c
commit 488709088b
123 changed files with 14885 additions and 771 deletions

View File

@@ -0,0 +1,10 @@
export default class JournalEntry {
constructor(entry) {
const defaults = {
credit: 0,
debit: 0,
};
this.entry = { ...defaults, ...entry };
}
}

View File

@@ -0,0 +1,241 @@
import { pick } from 'lodash';
import moment from 'moment';
import JournalEntry from '@/services/Accounting/JournalEntry';
import AccountTransaction from '@/models/AccountTransaction';
import AccountBalance from '@/models/AccountBalance';
export default class JournalPoster {
/**
* Journal poster constructor.
*/
constructor() {
this.entries = [];
this.balancesChange = {};
}
/**
* Writes the credit entry for the given account.
* @param {JournalEntry} entry -
*/
credit(entryModel) {
if (entryModel instanceof JournalEntry === false) {
throw new Error('The entry is not instance of JournalEntry.');
}
this.entries.push(entryModel.entry);
this.setAccountBalanceChange(entryModel.entry, 'credit');
}
/**
* Writes the debit entry for the given account.
* @param {JournalEntry} entry -
*/
debit(entryModel) {
if (entryModel instanceof JournalEntry === false) {
throw new Error('The entry is not instance of JournalEntry.');
}
this.entries.push(entryModel.entry);
this.setAccountBalanceChange(entryModel.entry, 'debit');
}
/**
* Sets account balance change.
* @param {JournalEntry} entry
* @param {String} type
*/
setAccountBalanceChange(entry, type) {
if (!this.balancesChange[entry.account]) {
this.balancesChange[entry.account] = 0;
}
let change = 0;
if (entry.accountNormal === 'credit') {
change = (type === 'credit') ? entry.credit : -1 * entry.debit;
} else if (entry.accountNormal === 'debit') {
change = (type === 'debit') ? entry.debit : -1 * entry.credit;
}
this.balancesChange[entry.account] += change;
}
/**
* Mapping the balance change to list.
*/
mapBalanceChangesToList() {
const mappedList = [];
Object.keys(this.balancesChange).forEach((accountId) => {
const balance = this.balancesChange[accountId];
mappedList.push({
account_id: accountId,
amount: balance,
});
});
return mappedList;
}
/**
* Saves the balance change of journal entries.
*/
async saveBalance() {
const balancesList = this.mapBalanceChangesToList();
const balanceUpdateOpers = [];
const balanceInsertOpers = [];
const balanceFindOneOpers = [];
let balanceAccounts = [];
balancesList.forEach((balance) => {
const oper = AccountBalance.query().findOne('account_id', balance.account_id);
balanceFindOneOpers.push(oper);
});
balanceAccounts = await Promise.all(balanceFindOneOpers);
balancesList.forEach((balance) => {
const method = balance.amount < 0 ? 'decrement' : 'increment';
// Detarmine if the account balance is already exists or not.
const foundAccBalance = balanceAccounts.some((account) => (
account && account.account_id === balance.account_id
));
if (foundAccBalance) {
const query = AccountBalance
.query()[method]('amount', Math.abs(balance.amount))
.where('account_id', balance.account_id);
balanceUpdateOpers.push(query);
} else {
const query = AccountBalance.query().insert({
account_id: balance.account_id,
amount: balance.amount,
currency_code: 'USD',
});
balanceInsertOpers.push(query);
}
});
await Promise.all([
...balanceUpdateOpers, ...balanceInsertOpers,
]);
}
/**
* Saves the stacked journal entries to the storage.
*/
async saveEntries() {
const saveOperations = [];
this.entries.forEach((entry) => {
const oper = AccountTransaction.query().insert({
accountId: entry.account,
...pick(entry, ['credit', 'debit', 'transactionType',
'referenceType', 'referenceId', 'note']),
});
saveOperations.push(oper);
});
await Promise.all(saveOperations);
}
/**
* Reverses the stacked journal entries.
*/
reverseEntries() {
const reverseEntries = [];
this.entries.forEach((entry) => {
const reverseEntry = { ...entry };
if (entry.credit) {
reverseEntry.debit = entry.credit;
}
if (entry.debit) {
reverseEntry.credit = entry.debit;
}
reverseEntries.push(reverseEntry);
});
this.entries = reverseEntries;
}
/**
* Delete the given or all stacked entries.
* @param {Array} ids -
*/
async deleteEntries(ids) {
const entriesIds = ids || this.entries.map((e) => e.id);
if (entriesIds.length > 0) {
await AccountTransaction.query().whereIn('id', entriesIds).delete();
}
}
/**
* Retrieve the closing balance for the given account and closing date.
* @param {Number} accountId -
* @param {Date} closingDate -
*/
getClosingBalance(accountId, closingDate, dateType = 'day') {
let closingBalance = 0;
const momentClosingDate = moment(closingDate);
this.entries.forEach((entry) => {
// Can not continue if not before or event same closing date.
if ((!momentClosingDate.isAfter(entry.date, dateType)
&& !momentClosingDate.isSame(entry.date, dateType))
|| (entry.account !== accountId && accountId)) {
return;
}
if (entry.accountNormal === 'credit') {
closingBalance += (entry.credit) ? entry.credit : -1 * entry.debit;
} else if (entry.accountNormal === 'debit') {
closingBalance += (entry.debit) ? entry.debit : -1 * entry.credit;
}
});
return closingBalance;
}
/**
* Retrieve the credit/debit sumation for the given account and date.
* @param {Number} account -
* @param {Date|String} closingDate -
*/
getTrialBalance(accountId, closingDate, dateType) {
const momentClosingDate = moment(closingDate);
const result = {
credit: 0,
debit: 0,
balance: 0,
};
this.entries.forEach((entry) => {
if ((!momentClosingDate.isAfter(entry.date, dateType)
&& !momentClosingDate.isSame(entry.date, dateType))
|| (entry.account !== accountId && accountId)) {
return;
}
result.credit += entry.credit;
result.debit += entry.debit;
if (entry.accountNormal === 'credit') {
result.balance += (entry.credit) ? entry.credit : -1 * entry.debit;
} else if (entry.accountNormal === 'debit') {
result.balance += (entry.debit) ? entry.debit : -1 * entry.credit;
}
});
return result;
}
/**
* Load fetched accounts journal entries.
* @param {Array} entries -
*/
loadEntries(entries) {
entries.forEach((entry) => {
this.entries.push({
...entry,
account: entry.account ? entry.account.id : entry.accountId,
accountNormal: (entry.account && entry.account.type)
? entry.account.type.normal : entry.accountNormal,
});
});
}
static loadAccounts() {
}
}

View File

@@ -0,0 +1,6 @@
import Moment from 'moment';
import { extendMoment } from 'moment-range';
const moment = extendMoment(Moment);
export default moment;

View File

@@ -3,12 +3,14 @@ import { difference } from 'lodash';
import Role from '@/models/Role';
export default {
cacheKey: 'ratteb.cache,',
cacheExpirationTime: null,
permissions: [],
cache: null,
/**
* Initialize the cache.
*/
initializeCache() {
if (!this.cache) {
this.cache = new cache.Cache();

View File

@@ -0,0 +1,13 @@
import SessionModel from '@/services/SessionModel';
export default class SessionQueryBuilder extends SessionModel.QueryBuilder {
/**
* Add a custom method that stores a session object to the query context.
* @param {*} session -
*/
session(session) {
return this.mergeContext({
session,
});
}
}

View File

@@ -0,0 +1,24 @@
import SessionQueryBuilder from '@/services/SessionModel/SessionQueryBuilder';
export default class SessionModel {
/**
* Constructor method.
* @param {Object} options -
*/
constructor(options) {
this.options = { ...options, ...SessionModel.defaultOptions };
}
static get defaultOptions() {
return {
setModifiedBy: true,
setModifiedAt: true,
setCreatedBy: true,
setCreatedAt: true,
};
}
static get QueryBuilder() {
return SessionQueryBuilder;
}
}