add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
import * as R from 'ramda';
import { isEmpty, sumBy } from 'lodash';
import {
ITransactionsByContactsTransaction,
ITransactionsByVendorsFilter,
ITransactionsByVendorsTransaction,
ITransactionsByVendorsVendor,
ITransactionsByVendorsData,
ILedger,
INumberFormatQuery,
IVendor,
} from '@/interfaces';
import TransactionsByContact from '../TransactionsByContact/TransactionsByContact';
const VENDOR_NORMAL = 'credit';
export default class TransactionsByVendors extends TransactionsByContact {
readonly contacts: IVendor[];
readonly transactionsByContact: any;
readonly filter: ITransactionsByVendorsFilter;
readonly baseCurrency: string;
readonly numberFormat: INumberFormatQuery;
readonly accountsGraph: any;
readonly ledger: ILedger;
/**
* Constructor method.
* @param {IVendor} vendors
* @param {Map<number, IAccountTransaction[]>} transactionsByContact
* @param {string} baseCurrency
*/
constructor(
vendors: IVendor[],
accountsGraph: any,
ledger: ILedger,
filter: ITransactionsByVendorsFilter,
baseCurrency: string,
i18n
) {
super();
this.contacts = vendors;
this.accountsGraph = accountsGraph;
this.ledger = ledger;
this.baseCurrency = baseCurrency;
this.filter = filter;
this.numberFormat = this.filter.numberFormat;
this.i18n = i18n;
}
/**
* Retrieve the vendor transactions from the given vendor id and opening balance.
* @param {number} vendorId - Vendor id.
* @param {number} openingBalance - Opening balance amount.
* @returns {ITransactionsByVendorsTransaction[]}
*/
private vendorTransactions(
vendorId: number,
openingBalance: number
): ITransactionsByVendorsTransaction[] {
const openingBalanceLedger = this.ledger
.whereContactId(vendorId)
.whereFromDate(this.filter.fromDate)
.whereToDate(this.filter.toDate);
const openingEntries = openingBalanceLedger.getEntries();
return R.compose(
R.curry(this.contactTransactionRunningBalance)(openingBalance, 'credit'),
R.map(this.contactTransactionMapper.bind(this))
).bind(this)(openingEntries);
}
/**
* Vendor section mapper.
* @param {IVendor} vendor
* @returns {ITransactionsByVendorsVendor}
*/
private vendorMapper(vendor: IVendor): ITransactionsByVendorsVendor {
const openingBalance = this.getContactOpeningBalance(vendor.id);
const transactions = this.vendorTransactions(vendor.id, openingBalance);
const closingBalance = this.getVendorClosingBalance(
transactions,
openingBalance
);
const currencyCode = this.baseCurrency;
return {
vendorName: vendor.displayName,
openingBalance: this.getTotalAmountMeta(openingBalance, currencyCode),
closingBalance: this.getTotalAmountMeta(closingBalance, currencyCode),
transactions,
};
}
/**
* Retrieve the vendor closing balance from the given customer transactions.
* @param {ITransactionsByContactsTransaction[]} customerTransactions
* @param {number} openingBalance
* @returns
*/
private getVendorClosingBalance(
customerTransactions: ITransactionsByContactsTransaction[],
openingBalance: number
) {
return this.getContactClosingBalance(
customerTransactions,
VENDOR_NORMAL,
openingBalance
);
}
/**
* Detarmines whether the vendors post filter is active.
* @returns {boolean}
*/
private isVendorsPostFilter = (): boolean => {
return isEmpty(this.filter.vendorsIds);
};
/**
* Retrieve the vendors sections of the report.
* @param {IVendor[]} vendors
* @returns {ITransactionsByVendorsVendor[]}
*/
private vendorsMapper(vendors: IVendor[]): ITransactionsByVendorsVendor[] {
return R.compose(
R.when(this.isVendorsPostFilter, this.contactsFilter),
R.map(this.vendorMapper.bind(this))
).bind(this)(vendors);
}
/**
* Retrieve the report data.
* @returns {ITransactionsByVendorsData}
*/
public reportData(): ITransactionsByVendorsData {
return this.vendorsMapper(this.contacts);
}
/**
* Retrieve the report columns.
*/
public reportColumns() {
return [];
}
}

View File

@@ -0,0 +1,101 @@
import { Inject, Service } from 'typedi';
import { isEmpty, map } from 'lodash';
import { IVendor, IAccount, IAccountTransaction } from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
@Service()
export default class TransactionsByVendorRepository {
@Inject()
tenancy: HasTenancyService;
/**
* Retrieve the report vendors.
* @param {number} tenantId
* @returns {Promise<IVendor[]>}
*/
public getVendors(
tenantId: number,
vendorsIds?: number[]
): Promise<IVendor[]> {
const { Vendor } = this.tenancy.models(tenantId);
return Vendor.query().onBuild((q) => {
q.orderBy('displayName');
if (!isEmpty(vendorsIds)) {
q.whereIn('id', vendorsIds);
}
});
}
/**
* Retrieve the accounts receivable.
* @param {number} tenantId
* @returns {Promise<IAccount[]>}
*/
private async getPayableAccounts(tenantId: number): Promise<IAccount[]> {
const { Account } = this.tenancy.models(tenantId);
const accounts = await Account.query().where(
'accountType',
ACCOUNT_TYPE.ACCOUNTS_PAYABLE
);
return accounts;
}
/**
* Retrieve the customers opening balance transactions.
* @param {number} tenantId
* @param {number} openingDate
* @param {number} customersIds
* @returns {}
*/
public async getVendorsOpeningBalance(
tenantId: number,
openingDate: Date,
customersIds?: number[]
): Promise<IAccountTransaction[]> {
const { AccountTransaction } = this.tenancy.models(tenantId);
const payableAccounts = await this.getPayableAccounts(tenantId);
const payableAccountsIds = map(payableAccounts, 'id');
const openingTransactions = await AccountTransaction.query().modify(
'contactsOpeningBalance',
openingDate,
payableAccountsIds,
customersIds
);
return openingTransactions;
}
/**
* Retrieve vendors periods transactions.
* @param {number} tenantId
* @param {Date|string} openingDate
* @param {number[]} customersIds
*/
public async getVendorsPeriodTransactions(
tenantId: number,
fromDate: Date,
toDate: Date
): Promise<IAccountTransaction[]> {
const { AccountTransaction } = this.tenancy.models(tenantId);
const receivableAccounts = await this.getPayableAccounts(tenantId);
const receivableAccountsIds = map(receivableAccounts, 'id');
const transactions = await AccountTransaction.query().onBuild((query) => {
// Filter by date.
query.modify('filterDateRange', fromDate, toDate);
// Filter by customers.
query.whereNot('contactId', null);
// Filter by accounts.
query.whereIn('accountId', receivableAccountsIds);
});
return transactions;
}
}

View File

@@ -0,0 +1,178 @@
import { Inject } from 'typedi';
import moment from 'moment';
import * as R from 'ramda';
import { map } from 'lodash';
import TenancyService from '@/services/Tenancy/TenancyService';
import {
ITransactionsByVendorsService,
ITransactionsByVendorsFilter,
ITransactionsByVendorsStatement,
ILedgerEntry,
} from '@/interfaces';
import TransactionsByVendor from './TransactionsByVendor';
import Ledger from '@/services/Accounting/Ledger';
import TransactionsByVendorRepository from './TransactionsByVendorRepository';
import { Tenant } from '@/system/models';
export default class TransactionsByVendorsService
implements ITransactionsByVendorsService
{
@Inject()
tenancy: TenancyService;
@Inject('logger')
logger: any;
@Inject()
reportRepository: TransactionsByVendorRepository;
/**
* Defaults balance sheet filter query.
* @return {IVendorBalanceSummaryQuery}
*/
get defaultQuery(): ITransactionsByVendorsFilter {
return {
fromDate: moment().format('YYYY-MM-DD'),
toDate: moment().format('YYYY-MM-DD'),
numberFormat: {
precision: 2,
divideOn1000: false,
showZero: false,
formatMoney: 'total',
negativeFormat: 'mines',
},
comparison: {
percentageOfColumn: true,
},
noneZero: false,
noneTransactions: true,
vendorsIds: [],
};
}
/**
* Retrieve the customers opening balance transactions.
* @param {number} tenantId
* @param {number} openingDate
* @param {number} customersIds
* @returns {Promise<ILedgerEntry[]>}
*/
private async getVendorsOpeningBalanceEntries(
tenantId: number,
openingDate: Date,
customersIds?: number[]
): Promise<ILedgerEntry[]> {
const openingTransactions =
await this.reportRepository.getVendorsOpeningBalance(
tenantId,
openingDate,
customersIds
);
return R.compose(
R.map(R.assoc('date', openingDate)),
R.map(R.assoc('accountNormal', 'credit'))
)(openingTransactions);
}
/**
*
* @param {number} tenantId
* @param {Date|string} openingDate
* @param {number[]} customersIds
*/
private async getVendorsPeriodEntries(
tenantId: number,
fromDate: Date,
toDate: Date
): Promise<ILedgerEntry[]> {
const transactions =
await this.reportRepository.getVendorsPeriodTransactions(
tenantId,
fromDate,
toDate
);
return R.compose(
R.map(R.assoc('accountNormal', 'credit')),
R.map((trans) => ({
...trans,
referenceTypeFormatted: trans.referenceTypeFormatted,
}))
)(transactions);
}
/**
* Retrieve the report ledger entries from repository.
* @param {number} tenantId
* @param {Date} fromDate
* @param {Date} toDate
* @returns {Promise<ILedgerEntry[]>}
*/
private async getReportEntries(
tenantId: number,
fromDate: Date,
toDate: Date
): Promise<ILedgerEntry[]> {
const openingBalanceDate = moment(fromDate).subtract(1, 'days').toDate();
return [
...(await this.getVendorsOpeningBalanceEntries(
tenantId,
openingBalanceDate
)),
...(await this.getVendorsPeriodEntries(tenantId, fromDate, toDate)),
];
}
/**
* Retrieve transactions by by the customers.
* @param {number} tenantId
* @param {ITransactionsByVendorsFilter} query
* @return {Promise<ITransactionsByVendorsStatement>}
*/
public async transactionsByVendors(
tenantId: number,
query: ITransactionsByVendorsFilter
): Promise<ITransactionsByVendorsStatement> {
const { accountRepository } = this.tenancy.repositories(tenantId);
const i18n = this.tenancy.i18n(tenantId);
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
const filter = { ...this.defaultQuery, ...query };
// Retrieve the report vendors.
const vendors = await this.reportRepository.getVendors(
tenantId,
filter.vendorsIds
);
// Retrieve the accounts graph.
const accountsGraph = await accountRepository.getDependencyGraph();
// Journal transactions.
const reportEntries = await this.getReportEntries(
tenantId,
filter.fromDate,
filter.toDate
);
// Ledger collection.
const journal = new Ledger(reportEntries);
// Transactions by customers data mapper.
const reportInstance = new TransactionsByVendor(
vendors,
accountsGraph,
journal,
filter,
tenant.metadata.baseCurrency,
i18n
);
return {
data: reportInstance.reportData(),
columns: reportInstance.reportColumns(),
query: filter,
};
}
}

View File

@@ -0,0 +1,76 @@
import * as R from 'ramda';
import { tableRowMapper } from 'utils';
import { ITransactionsByVendorsVendor, ITableRow } from '@/interfaces';
import TransactionsByContactsTableRows from '../TransactionsByContact/TransactionsByContactTableRows';
enum ROW_TYPE {
OPENING_BALANCE = 'OPENING_BALANCE',
CLOSING_BALANCE = 'CLOSING_BALANCE',
TRANSACTION = 'TRANSACTION',
VENDOR = 'VENDOR',
}
export default class TransactionsByVendorsTableRows extends TransactionsByContactsTableRows {
vendorsTransactions: ITransactionsByVendorsVendor[];
/**
* Constructor method.
*/
constructor(
vendorsTransactions: ITransactionsByVendorsVendor[],
i18n
) {
super();
this.vendorsTransactions = vendorsTransactions;
this.i18n = i18n;
}
/**
* Retrieve the table row of vendor details.
* @param {ITransactionsByVendorsVendor} vendor -
* @returns {ITableRow[]}
*/
private vendorDetails = (vendor: ITransactionsByVendorsVendor) => {
const columns = [
{ key: 'vendorName', accessor: 'vendorName' },
...R.repeat({ key: 'empty', value: '' }, 5),
{
key: 'closingBalanceValue',
accessor: 'closingBalance.formattedAmount',
},
];
return {
...tableRowMapper(vendor, columns, { rowTypes: [ROW_TYPE.VENDOR] }),
children: R.pipe(
R.when(
R.always(vendor.transactions.length > 0),
R.pipe(
R.concat(this.contactTransactions(vendor)),
R.prepend(this.contactOpeningBalance(vendor))
)
),
R.append(this.contactClosingBalance(vendor))
)([]),
};
};
/**
* Retrieve the table rows of the vendor section.
* @param {ITransactionsByVendorsVendor} vendor
* @returns {ITableRow[]}
*/
private vendorRowsMapper = (vendor: ITransactionsByVendorsVendor) => {
return R.pipe(this.vendorDetails)(vendor);
};
/**
* Retrieve the table rows of transactions by vendors report.
* @param {ITransactionsByVendorsVendor[]} vendors
* @returns {ITableRow[]}
*/
public tableRows = (): ITableRow[] => {
return R.map(this.vendorRowsMapper)(this.vendorsTransactions);
};
}