fix: transactions by customers/vendors report localization.

This commit is contained in:
a.bouhuolia
2021-08-07 08:14:10 +02:00
parent c21236c4ae
commit 37bbd66249
15 changed files with 175 additions and 109 deletions

View File

@@ -34,6 +34,9 @@ function loadLocales(currentLocale) {
return import(`../lang/${currentLocale}/index.json`);
}
/**
* Loads the localization data of yup validation library.
*/
function loadYupLocales(currentLocale) {
return import(`../lang/${currentLocale}/locale`);
}

View File

@@ -1,18 +1,20 @@
import { Router, Request, Response, NextFunction } from 'express';
import { query } from 'express-validator';
import { Inject } from 'typedi';
import { Inject, Service } from 'typedi';
import { ITransactionsByCustomersStatement } from 'interfaces';
import asyncMiddleware from 'api/middleware/asyncMiddleware';
import BaseFinancialReportController from '../BaseFinancialReportController';
import TransactionsByCustomersService from 'services/FinancialStatements/TransactionsByCustomer/TransactionsByCustomersService';
import TransactionsByCustomersTableRows from 'services/FinancialStatements/TransactionsByCustomer/TransactionsByCustomersTableRows';
import HasTenancyService from 'services/Tenancy/TenancyService';
@Service()
export default class TransactionsByCustomersReportController extends BaseFinancialReportController {
@Inject()
transactionsByCustomersService: TransactionsByCustomersService;
@Inject()
transactionsByCustomersTableRows: TransactionsByCustomersTableRows;
tenancy: HasTenancyService;
/**
* Router constructor.
@@ -51,12 +53,18 @@ export default class TransactionsByCustomersReportController extends BaseFinanci
* Transformes the statement to table rows response.
* @param {ITransactionsByCustomersStatement} statement -
*/
private transformToTableResponse({
data,
}: ITransactionsByCustomersStatement) {
private transformToTableResponse(
customersTransactions,
tenantId
) {
const i18n = this.tenancy.i18n(tenantId);
const table = new TransactionsByCustomersTableRows(
customersTransactions,
i18n
);
return {
table: {
rows: this.transactionsByCustomersTableRows.tableRows(data),
rows: table.tableRows(),
},
};
}
@@ -65,10 +73,10 @@ export default class TransactionsByCustomersReportController extends BaseFinanci
* Transformes the statement to json response.
* @param {ITransactionsByCustomersStatement} statement -
*/
private transfromToJsonResponse({
private transfromToJsonResponse(
data,
columns,
}: ITransactionsByCustomersStatement) {
columns
): ITransactionsByCustomersStatement {
return {
data: this.transfromToResponse(data),
columns: this.transfromToResponse(columns),
@@ -91,7 +99,7 @@ export default class TransactionsByCustomersReportController extends BaseFinanci
const filter = this.matchedQueryData(req);
try {
const transactionsByCustomers =
const report =
await this.transactionsByCustomersService.transactionsByCustomers(
tenantId,
filter
@@ -108,7 +116,9 @@ export default class TransactionsByCustomersReportController extends BaseFinanci
default:
return res
.status(200)
.send(this.transformToTableResponse(transactionsByCustomers));
.send(
this.transformToTableResponse(report.data, tenantId)
);
}
} catch (error) {
next(error);

View File

@@ -6,13 +6,14 @@ import BaseFinancialReportController from '../BaseFinancialReportController';
import TransactionsByVendorsTableRows from 'services/FinancialStatements/TransactionsByVendor/TransactionsByVendorTableRows';
import TransactionsByVendorsService from 'services/FinancialStatements/TransactionsByVendor/TransactionsByVendorService';
import { ITransactionsByVendorsStatement } from 'interfaces';
import HasTenancyService from 'services/Tenancy/TenancyService';
export default class TransactionsByVendorsReportController extends BaseFinancialReportController {
@Inject()
transactionsByVendorsService: TransactionsByVendorsService;
@Inject()
transactionsByVendorsTableRows: TransactionsByVendorsTableRows;
tenancy: HasTenancyService;
/**
* Router constructor.
@@ -52,10 +53,16 @@ export default class TransactionsByVendorsReportController extends BaseFinancial
* Transformes the report statement to table rows.
* @param {ITransactionsByVendorsStatement} statement -
*/
private transformToTableRows({ data }: ITransactionsByVendorsStatement) {
private transformToTableRows(
tenantId: number,
transactions: any[]
) {
const i18n = this.tenancy.i18n(tenantId);
const table = new TransactionsByVendorsTableRows(transactions, i18n);
return {
table: {
data: this.transactionsByVendorsTableRows.tableRows(data),
data: table.tableRows(),
},
};
}
@@ -87,7 +94,7 @@ export default class TransactionsByVendorsReportController extends BaseFinancial
const filter = this.matchedQueryData(req);
try {
const transactionsByVendors =
const report =
await this.transactionsByVendorsService.transactionsByVendors(
tenantId,
filter
@@ -99,12 +106,12 @@ export default class TransactionsByVendorsReportController extends BaseFinancial
case 'application/json+table':
return res
.status(200)
.send(this.transformToTableRows(transactionsByVendors));
.send(this.transformToTableRows(tenantId, report.data));
case 'json':
default:
return res
.status(200)
.send(this.transformToJsonResponse(transactionsByVendors));
.send(this.transformToJsonResponse(report));
}
} catch (error) {
next(error);

View File

@@ -53,7 +53,7 @@ export interface IInventoryDetailsItemTransaction {
nodeType: string;
date: IInventoryDetailsDate;
transactionType: string;
transactionNumber: string;
transactionNumber?: string;
quantityMovement: IInventoryDetailsNumber;
valueMovement: IInventoryDetailsNumber;

View File

@@ -49,7 +49,8 @@ export default class InventoryDetails extends FinancialSheet {
openingBalanceTransactions: IInventoryTransaction[],
inventoryTransactions: IInventoryTransaction[],
query: IInventoryDetailsQuery,
baseCurrency: string
baseCurrency: string,
i18n: any,
) {
super();
@@ -65,6 +66,7 @@ export default class InventoryDetails extends FinancialSheet {
this.numberFormat = this.query.numberFormat;
this.items = items;
this.baseCurrency = baseCurrency;
this.i18n = i18n;
}
/**
@@ -203,8 +205,8 @@ export default class InventoryDetails extends FinancialSheet {
return {
nodeType: INodeTypes.TRANSACTION,
date: this.getDateMeta(transaction.date),
transactionType: transaction.transcationTypeFormatted,
transactionNumber: transaction.meta.transactionNumber,
transactionType: this.i18n.__(transaction.transcationTypeFormatted),
transactionNumber: transaction?.meta?.transactionNumber,
direction: transaction.direction,
quantityMovement: this.getNumberMeta(quantityMovement),

View File

@@ -82,6 +82,8 @@ export default class InventoryDetailsService extends FinancialSheet {
): Promise<IInvetoryItemDetailDOO> {
// Settings tenant service.
const settings = this.tenancy.settings(tenantId);
const i18n = this.tenancy.i18n(tenantId);
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
@@ -110,7 +112,8 @@ export default class InventoryDetailsService extends FinancialSheet {
openingBalanceTransactions,
inventoryTransactions,
filter,
baseCurrency
baseCurrency,
i18n
);
return {

View File

@@ -38,22 +38,22 @@ export default class InventoryDetailsTable {
* @param {IInventoryDetailsItem} item
* @returns {ITableRow}
*/
private itemNodeMapper(item: IInventoryDetailsItem) {
private itemNodeMapper = (item: IInventoryDetailsItem) => {
const columns = [{ key: 'item_name', accessor: 'name' }];
return tableRowMapper(item, columns, {
rowTypes: [IROW_TYPE.ITEM],
});
}
};
/**
* Mappes the item inventory transaction to table row.
* @param {IInventoryDetailsItemTransaction} transaction
* @returns {ITableRow}
*/
private itemTransactionNodeMapper(
private itemTransactionNodeMapper = (
transaction: IInventoryDetailsItemTransaction
) {
) => {
const columns = [
{ key: 'date', accessor: 'date.formattedDate' },
{ key: 'transaction_type', accessor: 'transactionType' },
@@ -75,17 +75,19 @@ export default class InventoryDetailsTable {
return tableRowMapper(transaction, columns, {
rowTypes: [IROW_TYPE.TRANSACTION],
});
}
};
/**
* Opening balance transaction mapper to table row.
* @param {IInventoryDetailsOpening} transaction
* @returns {ITableRow}
*/
private openingNodeMapper(transaction: IInventoryDetailsOpening): ITableRow {
private openingNodeMapper = (
transaction: IInventoryDetailsOpening
): ITableRow => {
const columns = [
{ key: 'date', accessor: 'date.formattedDate' },
{ key: 'closing', value: 'Opening Balance' },
{ key: 'closing', value: this.i18n.__('Opening balance') },
{ key: 'empty' },
{ key: 'quantity', accessor: 'quantity.formattedNumber' },
{ key: 'empty' },
@@ -95,17 +97,19 @@ export default class InventoryDetailsTable {
return tableRowMapper(transaction, columns, {
rowTypes: [IROW_TYPE.OPENING_ENTRY],
});
}
};
/**
* Closing balance transaction mapper to table raw.
* @param {IInventoryDetailsClosing} transaction
* @returns {ITableRow}
*/
private closingNodeMapper(transaction: IInventoryDetailsClosing): ITableRow {
private closingNodeMapper = (
transaction: IInventoryDetailsClosing
): ITableRow => {
const columns = [
{ key: 'date', accessor: 'date.formattedDate' },
{ key: 'closing', value: 'Closing Balance' },
{ key: 'closing', value: this.i18n.__('Closing balance') },
{ key: 'empty' },
{ key: 'quantity', accessor: 'quantity.formattedNumber' },
{ key: 'empty' },
@@ -117,7 +121,7 @@ export default class InventoryDetailsTable {
return tableRowMapper(transaction, columns, {
rowTypes: [IROW_TYPE.CLOSING_ENTRY],
});
}
};
/**
* Detarmines the ginve inventory details node type.
@@ -125,16 +129,19 @@ export default class InventoryDetailsTable {
* @param {IInventoryDetailsNode} node
* @returns {boolean}
*/
private isNodeTypeEquals(type: string, node: IInventoryDetailsNode): boolean {
private isNodeTypeEquals = (
type: string,
node: IInventoryDetailsNode
): boolean => {
return node.nodeType === type;
}
};
/**
* Mappes the given item or transactions node to table rows.
* @param {IInventoryDetailsNode} node -
* @return {ITableRow}
*/
private itemMapper(node: IInventoryDetailsNode): ITableRow {
private itemMapper = (node: IInventoryDetailsNode): ITableRow => {
return R.compose(
R.when(
R.curry(this.isNodeTypeEquals)('OPENING_ENTRY'),
@@ -147,33 +154,33 @@ export default class InventoryDetailsTable {
R.when(R.curry(this.isNodeTypeEquals)('item'), this.itemNodeMapper),
R.when(
R.curry(this.isNodeTypeEquals)('transaction'),
this.itemTransactionNodeMapper.bind(this)
this.itemTransactionNodeMapper
)
)(node);
}
};
/**
* Mappes the items nodes to table rows.
* @param {IInventoryDetailsItem[]} items
* @returns {ITableRow[]}
*/
private itemsMapper(items: IInventoryDetailsItem[]): ITableRow[] {
return mapValuesDeep(items, this.itemMapper.bind(this), MAP_CONFIG);
}
private itemsMapper = (items: IInventoryDetailsItem[]): ITableRow[] => {
return mapValuesDeep(items, this.itemMapper, MAP_CONFIG);
};
/**
* Retrieve the table rows of the inventory item details.
* @returns {ITableRow[]}
*/
public tableData(): ITableRow[] {
public tableData = (): ITableRow[] => {
return this.itemsMapper(this.report.data);
}
};
/**
* Retrieve the table columns of inventory details report.
* @returns {ITableColumn[]}
*/
public tableColumns(): ITableColumn[] {
public tableColumns = (): ITableColumn[] => {
return [
{ key: 'date', label: this.i18n.__('Date') },
{ key: 'transaction_type', label: this.i18n.__('Transaction type') },
@@ -186,5 +193,5 @@ export default class InventoryDetailsTable {
{ key: 'running_quantity', label: this.i18n.__('Running quantity') },
{ key: 'running_value', label: this.i18n.__('Running Value') },
];
}
};
}

View File

@@ -24,15 +24,15 @@ export default class TransactionsByContact extends FinancialSheet {
entry: ILedgerEntry,
): Omit<ITransactionsByContactsTransaction, 'runningBalance'> {
const account = this.accountsGraph.getNodeData(entry.accountId);
const currencyCode = 'USD';
const currencyCode = this.baseCurrency;
return {
credit: this.getContactAmount(entry.credit, currencyCode),
debit: this.getContactAmount(entry.debit, currencyCode),
accountName: account.name,
currencyCode: 'USD',
currencyCode: this.baseCurrency,
transactionNumber: entry.transactionNumber,
transactionType: entry.referenceTypeFormatted,
transactionType: this.i18n.__(entry.referenceTypeFormatted),
date: entry.date,
createdAt: entry.createdAt,
};

View File

@@ -1,10 +1,7 @@
import moment from 'moment';
import * as R from 'ramda';
import { tableMapper, tableRowMapper } from 'utils';
import {
ITransactionsByContactsContact,
ITableRow,
} from 'interfaces';
import { ITransactionsByContactsContact, ITableRow } from 'interfaces';
enum ROW_TYPE {
OPENING_BALANCE = 'OPENING_BALANCE',
@@ -14,19 +11,18 @@ enum ROW_TYPE {
}
export default class TransactionsByContactsTableRows {
private dateAccessor(value) {
private dateAccessor = (value): string => {
return moment(value.date).format('YYYY MMM DD');
}
};
/**
* Retrieve the table rows of contact transactions.
* @param {ITransactionsByCustomersCustomer} contact
* @returns {ITableRow[]}
*/
protected contactTransactions(
protected contactTransactions = (
contact: ITransactionsByContactsContact
): ITableRow[] {
): ITableRow[] => {
const columns = [
{ key: 'date', accessor: this.dateAccessor },
{ key: 'account', accessor: 'accountName' },
@@ -39,18 +35,18 @@ export default class TransactionsByContactsTableRows {
return tableMapper(contact.transactions, columns, {
rowTypes: [ROW_TYPE.TRANSACTION],
});
}
};
/**
* Retrieve the table row of contact opening balance.
* @param {ITransactionsByCustomersCustomer} contact
* @returns {ITableRow}
*/
protected contactOpeningBalance(
protected contactOpeningBalance = (
contact: ITransactionsByContactsContact
): ITableRow {
): ITableRow => {
const columns = [
{ key: 'openingBalanceLabel', value: 'Opening balance' },
{ key: 'openingBalanceLabel', value: this.i18n.__('Opening balance') },
...R.repeat({ key: 'empty', value: '' }, 5),
{
key: 'openingBalanceValue',
@@ -60,18 +56,18 @@ export default class TransactionsByContactsTableRows {
return tableRowMapper(contact, columns, {
rowTypes: [ROW_TYPE.OPENING_BALANCE],
});
}
};
/**
* Retrieve the table row of contact closing balance.
* @param {ITransactionsByCustomersCustomer} contact -
* @returns {ITableRow}
*/
protected contactClosingBalance(
protected contactClosingBalance = (
contact: ITransactionsByContactsContact
): ITableRow {
): ITableRow => {
const columns = [
{ key: 'closingBalanceLabel', value: 'Closing balance' },
{ key: 'closingBalanceLabel', value: this.i18n.__('Closing balance') },
...R.repeat({ key: 'empty', value: '' }, 5),
{
key: 'closingBalanceValue',
@@ -81,5 +77,5 @@ export default class TransactionsByContactsTableRows {
return tableRowMapper(contact, columns, {
rowTypes: [ROW_TYPE.CLOSING_BALANCE],
});
}
};
}

View File

@@ -31,7 +31,8 @@ export default class TransactionsByCustomers extends TransactionsByContact {
accountsGraph: any,
ledger: Ledger,
filter: ITransactionsByCustomersFilter,
baseCurrency: string
baseCurrency: string,
i18n
) {
super();
@@ -41,6 +42,7 @@ export default class TransactionsByCustomers extends TransactionsByContact {
this.baseCurrency = baseCurrency;
this.filter = filter;
this.numberFormat = this.filter.numberFormat;
this.i18n = i18n;
}
/**
@@ -80,17 +82,12 @@ export default class TransactionsByCustomers extends TransactionsByContact {
transactions,
openingBalance
);
const currencyCode = this.baseCurrency;
return {
customerName: customer.displayName,
openingBalance: this.getTotalAmountMeta(
openingBalance,
customer.currencyCode
),
closingBalance: this.getTotalAmountMeta(
closingBalance,
customer.currencyCode
),
openingBalance: this.getTotalAmountMeta(openingBalance, currencyCode),
closingBalance: this.getTotalAmountMeta(closingBalance, currencyCode),
transactions,
};
}

View File

@@ -83,8 +83,8 @@ export default class TransactionsByCustomersService
*/
private async getCustomersPeriodsEntries(
tenantId: number,
fromDate: Date|string,
toDate: Date|string,
fromDate: Date | string,
toDate: Date | string
): Promise<ILedgerEntry[]> {
const transactions =
await this.reportRepository.getCustomersPeriodTransactions(
@@ -115,6 +115,8 @@ export default class TransactionsByCustomersService
// Settings tenant service.
const settings = this.tenancy.settings(tenantId);
const i18n = this.tenancy.i18n(tenantId);
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
@@ -127,7 +129,10 @@ export default class TransactionsByCustomersService
const accountsGraph = await accountRepository.getDependencyGraph();
// Retrieve the report customers.
const customers = await this.reportRepository.getCustomers(tenantId, filter.customersIds);
const customers = await this.reportRepository.getCustomers(
tenantId,
filter.customersIds
);
const openingBalanceDate = moment(filter.fromDate)
.subtract(1, 'days')
@@ -157,7 +162,8 @@ export default class TransactionsByCustomersService
accountsGraph,
journal,
filter,
baseCurrency
baseCurrency,
i18n
);
return {

View File

@@ -11,16 +11,34 @@ enum ROW_TYPE {
}
export default class TransactionsByCustomersTableRows extends TransactionsByContactsTableRows {
private customersTransactions: ITransactionsByCustomersCustomer[];
/**
* Constructor method.
* @param {ITransactionsByCustomersCustomer[]} customersTransactions - Customers transactions.
*/
constructor(
customersTransactions: ITransactionsByCustomersCustomer[],
i18n
) {
super();
this.customersTransactions = customersTransactions;
this.i18n = i18n;
}
/**
* Retrieve the table row of customer details.
* @param {ITransactionsByCustomersCustomer} customer -
* @returns {ITableRow[]}
*/
private customerDetails(customer: ITransactionsByCustomersCustomer) {
private customerDetails = (customer: ITransactionsByCustomersCustomer) => {
const columns = [
{ key: 'customerName', accessor: 'customerName' },
...R.repeat({ key: 'empty', value: '' }, 5),
{ key: 'closingBalanceValue', accessor: 'closingBalance.formattedAmount' },
{
key: 'closingBalanceValue',
accessor: 'closingBalance.formattedAmount',
},
];
return {
@@ -30,29 +48,31 @@ export default class TransactionsByCustomersTableRows extends TransactionsByCont
R.always(customer.transactions.length > 0),
R.pipe(
R.concat(this.contactTransactions(customer)),
R.prepend(this.contactOpeningBalance(customer)),
),
R.prepend(this.contactOpeningBalance(customer))
)
),
R.append(this.contactClosingBalance(customer))
)([]),
};
}
};
/**
* Retrieve the table rows of the customer section.
* @param {ITransactionsByCustomersCustomer} customer
* @returns {ITableRow[]}
*/
private customerRowsMapper(customer: ITransactionsByCustomersCustomer) {
return R.pipe(this.customerDetails).bind(this)(customer);
}
private customerRowsMapper = (customer: ITransactionsByCustomersCustomer) => {
return R.pipe(this.customerDetails)(customer);
};
/**
* Retrieve the table rows of transactions by customers report.
* @param {ITransactionsByCustomersCustomer[]} customers
* @returns {ITableRow[]}
*/
public tableRows(customers: ITransactionsByCustomersCustomer[]): ITableRow[] {
return R.map(this.customerRowsMapper.bind(this))(customers);
}
public tableRows = (): ITableRow[] => {
return R.map(this.customerRowsMapper.bind(this))(
this.customersTransactions
);
};
}

View File

@@ -34,7 +34,8 @@ export default class TransactionsByVendors extends TransactionsByContact {
accountsGraph: any,
ledger: ILedger,
filter: ITransactionsByVendorsFilter,
baseCurrency: string
baseCurrency: string,
i18n
) {
super();
@@ -44,6 +45,7 @@ export default class TransactionsByVendors extends TransactionsByContact {
this.baseCurrency = baseCurrency;
this.filter = filter;
this.numberFormat = this.filter.numberFormat;
this.i18n = i18n;
}
/**
@@ -81,17 +83,12 @@ export default class TransactionsByVendors extends TransactionsByContact {
transactions,
openingBalance
);
const currencyCode = this.baseCurrency;
return {
vendorName: vendor.displayName,
openingBalance: this.getTotalAmountMeta(
openingBalance,
vendor.currencyCode
),
closingBalance: this.getTotalAmountMeta(
closingBalance,
vendor.currencyCode
),
openingBalance: this.getTotalAmountMeta(openingBalance, currencyCode),
closingBalance: this.getTotalAmountMeta(closingBalance, currencyCode),
transactions,
};
}

View File

@@ -137,6 +137,8 @@ export default class TransactionsByVendorsService
// Settings tenant service.
const settings = this.tenancy.settings(tenantId);
const i18n = this.tenancy.i18n(tenantId);
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
@@ -166,7 +168,8 @@ export default class TransactionsByVendorsService
accountsGraph,
journal,
filter,
baseCurrency
baseCurrency,
i18n
);
return {
data: reportInstance.reportData(),

View File

@@ -11,12 +11,27 @@ enum ROW_TYPE {
}
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) {
private vendorDetails = (vendor: ITransactionsByVendorsVendor) => {
const columns = [
{ key: 'vendorName', accessor: 'vendorName' },
...R.repeat({ key: 'empty', value: '' }, 5),
@@ -33,29 +48,29 @@ export default class TransactionsByVendorsTableRows extends TransactionsByContac
R.always(vendor.transactions.length > 0),
R.pipe(
R.concat(this.contactTransactions(vendor)),
R.prepend(this.contactOpeningBalance(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).bind(this)(vendor);
}
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(vendors: ITransactionsByVendorsVendor[]): ITableRow[] {
return R.map(this.vendorRowsMapper.bind(this))(vendors);
}
public tableRows = (): ITableRow[] => {
return R.map(this.vendorRowsMapper)(this.vendorsTransactions);
};
}