fix: base currency from organization metadata.

This commit is contained in:
a.bouhuolia
2021-09-11 13:36:14 +02:00
parent 4bac4685d0
commit 6d79fe3498
19 changed files with 175 additions and 184 deletions

View File

@@ -58,11 +58,6 @@ export default class ItemsController extends BaseController {
asyncMiddleware(this.deleteItem.bind(this)),
this.handlerServiceErrors
);
router.get(
'/auto-complete',
this.autocompleteQuerySchema,
this.asyncMiddleware(this.autocompleteList.bind(this)),
);
router.get(
'/:id',
[...this.validateSpecificItemSchema],
@@ -325,10 +320,10 @@ export default class ItemsController extends BaseController {
const { tenantId } = req;
try {
const storedItem = await this.itemsService.getItem(tenantId, itemId);
const item = await this.itemsService.getItem(tenantId, itemId);
return res.status(200).send({
item: this.transfromToResponse(storedItem)
item: this.transfromToResponse(item)
});
} catch (error) {
next(error);
@@ -369,38 +364,6 @@ export default class ItemsController extends BaseController {
}
}
/**
* Auto-complete list.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async autocompleteList(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const filter = {
filterRoles: [],
sortOrder: 'asc',
columnSortBy: 'name',
limit: 10,
keyword: '',
...this.matchedQueryData(req),
};
if (filter.stringifiedFilterRoles) {
filter.filterRoles = JSON.parse(filter.stringifiedFilterRoles);
}
try {
const items = await this.itemsService.autocompleteItems(
tenantId,
filter
);
return res.status(200).send({
items: this.transfromToResponse(items),
});
} catch (error) {
next(error);
}
}
/**
* Handles service errors.
* @param {Error} error

View File

@@ -2,6 +2,14 @@ import moment from "moment";
import { isEmpty, isObject, isUndefined } from 'lodash';
export class Transformer {
meta: any;
setMeta(meta) {
this.meta = meta;
return this;
}
/**
* Includeded attributes.
* @returns
@@ -23,7 +31,7 @@ export class Transformer {
};
/**
*
* Transformes the given item to desired output.
* @param item
* @returns
*/

View File

@@ -3,7 +3,6 @@ import { IAccount } from 'interfaces';
import { Transformer } from 'lib/Transformer/Transformer';
import { formatNumber } from 'utils';
@Service()
export default class AccountTransformer extends Transformer {
/**
* Include these attributes to sale invoice object.
@@ -12,6 +11,7 @@ export default class AccountTransformer extends Transformer {
protected includeAttributes = (): string[] => {
return [
'formattedAmount',
'currencyCode'
];
};
@@ -22,7 +22,15 @@ export default class AccountTransformer extends Transformer {
*/
protected formattedAmount = (account: IAccount): string => {
return formatNumber(account.amount, {
currencyCode: account.currencyCode,
currencyCode: this.meta.baseCurrency,
});
};
/**
* Retrieve account currency code.
* @returns {string}
*/
protected currencyCode = (): string => {
return this.meta.baseCurrency;
};
}

View File

@@ -24,6 +24,7 @@ import { ERRORS } from './constants';
import { flatToNestedArray } from 'utils';
import I18nService from 'services/I18n/I18nService';
import AccountTransformer from './AccountTransform';
import { Tenant } from 'system/models';
@Service()
export default class AccountsService {
@@ -331,9 +332,13 @@ export default class AccountsService {
public async getAccount(tenantId: number, accountId: number) {
const account = await this.getAccountOrThrowError(tenantId, accountId);
return this.accountTransformer.transform(
this.transformAccountResponse(tenantId, account)
);
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
return new AccountTransformer()
.setMeta({ baseCurrency: tenant.metadata.baseCurrency })
.transform(account);
}
/**
@@ -649,8 +654,12 @@ export default class AccountsService {
builder.modify('inactiveMode', filter.inactiveMode);
});
const transformedAccounts = await this.transformAccountsResponse(
tenantId,
accounts
);
return {
accounts: this.transformAccountsResponse(tenantId, accounts),
accounts: transformedAccounts,
filterMeta: dynamicList.getResponseMeta(),
};
}
@@ -733,21 +742,26 @@ export default class AccountsService {
/**
* Transformes the accounts models to accounts response.
*/
private transformAccountsResponse(tenantId: number, accounts: IAccount[]) {
const settings = this.tenancy.settings(tenantId);
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
});
private async transformAccountsResponse(
tenantId: number,
accounts: IAccount[]
) {
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
const transformed = new AccountTransformer()
.setMeta({
baseCurrency: tenant.metadata?.baseCurrency,
})
.transform(accounts);
const _accounts = this.accountTransformer.transform(
accounts.map((account) => ({
...account.toJSON(),
currencyCode: baseCurrency,
}))
);
return flatToNestedArray(
this.i18nService.i18nMapper(_accounts, ['account_type_label'], tenantId),
this.i18nService.i18nMapper(
transformed,
['account_type_label'],
tenantId
),
{
id: 'id',
parentId: 'parent_account_id',

View File

@@ -4,7 +4,6 @@ import { Transformer } from 'lib/Transformer/Transformer';
import { formatNumber } from 'utils';
import { IContact } from 'interfaces';
@Service()
export default class ContactTransfromer extends Transformer {
/**
* Retrieve formatted expense amount.

View File

@@ -1,7 +1,6 @@
import { Service } from 'typedi';
import ContactTransfromer from '../ContactTransformer';
@Service()
export default class CustomerTransfromer extends ContactTransfromer {
/**
* Include these attributes to expense object.
@@ -11,7 +10,16 @@ export default class CustomerTransfromer extends ContactTransfromer {
return [
'formattedBalance',
'formattedOpeningBalance',
'formattedOpeningBalanceAt'
'formattedOpeningBalanceAt',
'customerType'
];
};
/**
* Retrieve customer type.
* @returns {string}
*/
protected customerType = (customer): string => {
return customer.contactType;
};
}

View File

@@ -62,9 +62,6 @@ export default class CustomersService {
@Inject('SalesEstimates')
estimatesService: ISalesEstimatesService;
@Inject()
customerTransformer: CustomerTransfromer;
/**
* Converts customer to contact DTO.
* @param {ICustomerNewDTO|ICustomerEditDTO} customerDTO
@@ -266,10 +263,7 @@ export default class CustomersService {
customerId,
'customer'
);
return R.compose(
this.customerTransformer.transform,
this.transformContactToCustomer,
)(contact);
return new CustomerTransfromer().transform(contact);
}
/**
@@ -315,8 +309,10 @@ export default class CustomersService {
})
.pagination(filter.page - 1, filter.pageSize);
const transformedCustomers = new CustomerTransfromer().transform(results);
return {
customers: results.map(this.transformContactToCustomer),
customers: transformedCustomers,
pagination,
filterMeta: dynamicList.getResponseMeta(),
};

View File

@@ -1,7 +1,6 @@
import { Service } from 'typedi';
import ContactTransfromer from '../ContactTransformer';
@Service()
export default class VendorTransfromer extends ContactTransfromer {
/**
* Include these attributes to expense object.

View File

@@ -52,9 +52,6 @@ export default class VendorsService {
@Inject('BillPayments')
billPaymentsService: IBillPaymentsService;
@Inject()
vendorTransformer: VendorTransfromer;
/**
* Converts vendor to contact DTO.
* @param {IVendorNewDTO|IVendorEditDTO} vendorDTO
@@ -202,9 +199,13 @@ export default class VendorsService {
* @param {number} vendorId
*/
public async getVendor(tenantId: number, vendorId: number) {
const vendor = await this.contactService.getContact(tenantId, vendorId, 'vendor');
const vendor = await this.contactService.getContact(
tenantId,
vendorId,
'vendor'
);
return this.vendorTransformer.transform(vendor);
return new VendorTransfromer().transform(vendor);
}
/**
@@ -302,8 +303,11 @@ export default class VendorsService {
})
.pagination(filter.page - 1, filter.pageSize);
// Transform the vendors.
const transformedVendors = new VendorTransfromer().transform(results);
return {
vendors: this.vendorTransformer.transform(results),
vendors: transformedVendors,
pagination,
filterMeta: dynamicList.getResponseMeta(),
};

View File

@@ -0,0 +1,8 @@
export default class FinancialReportService {
transformOrganizationMeta(tenant) {
return {
organizationName: tenant.metadata?.name,
baseCurrency: tenant.metadata?.baseCurrency,
};
}
}

View File

@@ -0,0 +1,34 @@
import { Transformer } from 'lib/Transformer/Transformer';
import { formatNumber } from 'utils';
export default class ItemTransformer extends Transformer {
/**
* Include these attributes to sale invoice object.
* @returns {Array}
*/
protected includeAttributes = (): string[] => {
return ['sellPriceFormatted', 'costPriceFormatted'];
};
/**
* Formatted sell price.
* @param item
* @returns {string}
*/
public sellPriceFormatted(item): string {
return formatNumber(item.sellPrice, {
currencyCode: this.meta.baseCurrency,
});
}
/**
* Formatted cost price.
* @param item
* @returns {string}
*/
public costPriceFormatted(item): string {
return formatNumber(item.costPrice, {
currencyCode: this.meta.baseCurrency,
});
}
}

View File

@@ -23,7 +23,8 @@ import {
ACCOUNT_TYPE,
} from 'data/AccountTypes';
import { ERRORS } from './constants';
import { formatNumber } from 'utils';
import ItemTransformer from './ItemTransformer';
import { Tenant } from 'system/models';
@Service()
export default class ItemsService implements IItemsService {
@@ -506,11 +507,6 @@ export default class ItemsService implements IItemsService {
public async getItem(tenantId: number, itemId: number): Promise<IItem> {
const { Item } = this.tenancy.models(tenantId);
this.logger.info('[items] trying to get the specific item.', {
tenantId,
itemId,
});
const item = await Item.query()
.findById(itemId)
.withGraphFetched('sellAccount')
@@ -518,10 +514,19 @@ export default class ItemsService implements IItemsService {
.withGraphFetched('category')
.withGraphFetched('costAccount');
// Can't continue if the item not found.
if (!item) {
throw new ServiceError(ERRORS.NOT_FOUND);
}
return this.transformItemToResponse(tenantId, item);
// Retrieve tenant and tenant information.
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
const baseCurrency = tenant.metadata?.baseCurrency;
// Transformes the item model.
return new ItemTransformer().setMeta({ baseCurrency }).transform(item);
}
/**
@@ -529,9 +534,7 @@ export default class ItemsService implements IItemsService {
* @param {} filterDTO - Filter DTO.
*/
private parseItemsListFilterDTO(filterDTO) {
return R.compose(
this.dynamicListService.parseStringifiedFilter,
)(filterDTO);
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
}
/**
@@ -564,45 +567,24 @@ export default class ItemsService implements IItemsService {
})
.pagination(filter.page - 1, filter.pageSize);
const results = items.map((item) =>
this.transformItemToResponse(tenantId, item)
);
// Retrieve tenant and tenant information.
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
const transformedItems = new ItemTransformer()
.setMeta({
baseCurrency: tenant?.metadata.baseCurrency,
})
.transform(items);
return {
items: results,
items: transformedItems,
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};
}
/**
* Retrieve auto-complete items list.
* @param {number} tenantId -
* @param {IItemsAutoCompleteFilter} itemsFilter -
*/
public async autocompleteItems(
tenantId: number,
itemsFilter: IItemsAutoCompleteFilter
) {
const { Item } = this.tenancy.models(tenantId);
const dynamicFilter = await this.dynamicListService.dynamicList(
tenantId,
Item,
itemsFilter
);
const items = await Item.query().onBuild((builder) => {
builder.withGraphFetched('category');
dynamicFilter.buildQuery()(builder);
builder.limit(itemsFilter.limit);
if (itemsFilter.keyword) {
builder.where('name', 'LIKE', `%${itemsFilter.keyword}%`);
}
});
return items.map((item) => this.transformItemToResponse(tenantId, item));
}
/**
* Validates the given item or items have no associated invoices or bills.
* @param {number} tenantId - Tenant id.
@@ -647,24 +629,4 @@ export default class ItemsService implements IItemsService {
throw new ServiceError(ERRORS.ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT);
}
}
/**
* Transformes the item object to response.
* @param {number} tenantId -
* @param {IItem} item -
* @returns
*/
private transformItemToResponse(tenantId: number, item: IItem) {
// Settings tenant service.
const settings = this.tenancy.settings(tenantId);
const currencyCode = settings.get({
group: 'organization', key: 'base_currency',
});
return {
...item.toObject ? item.toObject() : item,
sellPriceFormatted: formatNumber(item.sellPrice, { currencyCode }),
costPriceFormatted: formatNumber(item.costPrice, { currencyCode }),
};
}
}

View File

@@ -25,6 +25,7 @@ import JournalPosterService from 'services/Sales/JournalPosterService';
import AutoIncrementOrdersService from 'services/Sales/AutoIncrementOrdersService';
import { ERRORS } from './constants';
import ManualJournalTransfromer from './ManualJournalTransformer';
import { Tenant } from 'system/models';
@Service()
export default class ManualJournalsService implements IManualJournalsService {
@@ -363,7 +364,8 @@ export default class ManualJournalsService implements IManualJournalsService {
private transformNewDTOToModel(
tenantId,
manualJournalDTO: IManualJournalDTO,
authorizedUser: ISystemUser
authorizedUser: ISystemUser,
tenantMetadata
) {
const amount = sumBy(manualJournalDTO.entries, 'credit') || 0;
const date = moment(manualJournalDTO.date).format('YYYY-MM-DD');
@@ -373,12 +375,6 @@ export default class ManualJournalsService implements IManualJournalsService {
const journalNumber = manualJournalDTO.journalNumber || autoNextNumber;
// Settings tenant service.
const settings = this.tenancy.settings(tenantId);
const currencyCode = settings.get({
group: 'organization',
key: 'base_currency',
});
// Validate manual journal number require.
this.validateJournalNoRequire(journalNumber);
@@ -388,7 +384,7 @@ export default class ManualJournalsService implements IManualJournalsService {
? { publishedAt: moment().toMySqlDateTime() }
: {}),
amount,
currencyCode,
currencyCode: tenantMetadata.baseCurrency,
date,
journalNumber,
userId: authorizedUser.id,
@@ -431,11 +427,17 @@ export default class ManualJournalsService implements IManualJournalsService {
): Promise<{ manualJournal: IManualJournal }> {
const { ManualJournal } = this.tenancy.models(tenantId);
//
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
// Transformes the next DTO to model.
const manualJournalObj = this.transformNewDTOToModel(
tenantId,
manualJournalDTO,
authorizedUser
authorizedUser,
tenant.metadata,
);
// Validate the total credit should equals debit.
this.valdiateCreditDebitTotalEquals(manualJournalDTO);
@@ -657,13 +659,11 @@ export default class ManualJournalsService implements IManualJournalsService {
manualJournalsIds
);
// Filters the not published journals.
const notPublishedJournals = this.getNonePublishedManualJournals(
oldManualJournals
);
const notPublishedJournals =
this.getNonePublishedManualJournals(oldManualJournals);
// Filters the published journals.
const publishedJournals = this.getPublishedManualJournals(
oldManualJournals
);
const publishedJournals =
this.getPublishedManualJournals(oldManualJournals);
// Mappes the not-published journals to get id.
const notPublishedJournalsIds = map(notPublishedJournals, 'id');
@@ -775,18 +775,16 @@ export default class ManualJournalsService implements IManualJournalsService {
/**
* Parses filter DTO of the manual journals list.
* @param filterDTO
* @param filterDTO
*/
private parseListFilterDTO(filterDTO) {
return R.compose(
this.dynamicListService.parseStringifiedFilter
)(filterDTO);
private parseListFilterDTO(filterDTO) {
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
}
/**
* Retrieve manual journals datatable list.
* @param {number} tenantId -
* @param {IManualJournalsFilter} filter -
* @param {number} tenantId -
* @param {IManualJournalsFilter} filter -
*/
public async getManualJournals(
tenantId: number,

View File

@@ -3,7 +3,6 @@ import { ISaleEstimate } from 'interfaces';
import { Transformer } from 'lib/Transformer/Transformer';
import { formatNumber } from 'utils';
@Service()
export default class SaleEstimateTransfromer extends Transformer {
/**
* Include these attributes to sale invoice object.

View File

@@ -3,7 +3,6 @@ import { IPaymentReceive } from 'interfaces';
import { Transformer } from 'lib/Transformer/Transformer';
import { formatNumber } from 'utils';
@Service()
export default class PaymentReceiveTransfromer extends Transformer {
/**
* Include these attributes to payment receive object.

View File

@@ -69,9 +69,6 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
@EventDispatcher()
eventDispatcher: EventDispatcherInterface;
@Inject()
paymentReceiveTransformer: PaymentReceiveTransfromer;
/**
* Validates the payment receive number existance.
* @param {number} tenantId -
@@ -587,8 +584,7 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
if (!paymentReceive) {
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
}
return this.paymentReceiveTransformer.transform(paymentReceive);
return new PaymentReceiveTransfromer().transform(paymentReceive);
}
/**
@@ -664,8 +660,10 @@ export default class PaymentReceiveService implements IPaymentsReceiveService {
filter.pageSize
);
const transformedPayments = new PaymentReceiveTransfromer().transform(results);
return {
paymentReceives: this.paymentReceiveTransformer.transform(results),
paymentReceives: transformedPayments,
pagination,
filterMeta: dynamicList.getResponseMeta(),
};

View File

@@ -1,9 +1,7 @@
import { Service } from 'typedi';
import { ISaleInvoice } from 'interfaces';
import { Service } from 'typedi';;
import { Transformer } from 'lib/Transformer/Transformer';
import { formatNumber } from 'utils';
@Service()
export default class SaleInvoiceTransformer extends Transformer {
/**
* Include these attributes to sale invoice object.

View File

@@ -23,7 +23,7 @@ import CustomersService from 'services/Contacts/CustomersService';
import moment from 'moment';
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
import { ERRORS } from './constants';
import SaleEstimateTransfromer from './Estimates/SaleEstimateTransformer';
import SaleEstimateTransformer from './Estimates/SaleEstimateTransformer';
/**
* Sale estimate service.
@@ -52,9 +52,6 @@ export default class SaleEstimateService implements ISalesEstimatesService{
@Inject()
autoIncrementOrdersService: AutoIncrementOrdersService;
@Inject()
saleEstimateTransformer: SaleEstimateTransfromer;
/**
* Retrieve sale estimate or throw service error.
* @param {number} tenantId
@@ -401,7 +398,7 @@ export default class SaleEstimateService implements ISalesEstimatesService{
if (!estimate) {
throw new ServiceError(ERRORS.SALE_ESTIMATE_NOT_FOUND);
}
return this.saleEstimateTransformer.transform(estimate);
return new SaleEstimateTransformer().transform(estimate);
}
/**
@@ -447,8 +444,10 @@ export default class SaleEstimateService implements ISalesEstimatesService{
})
.pagination(filter.page - 1, filter.pageSize);
const transformedEstimates = new SaleEstimateTransformer().transform(results);
return {
salesEstimates: this.saleEstimateTransformer.transform(results),
salesEstimates: transformedEstimates,
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};

View File

@@ -33,8 +33,8 @@ import CustomersService from 'services/Contacts/CustomersService';
import SaleEstimateService from 'services/Sales/SalesEstimate';
import JournalPosterService from './JournalPosterService';
import AutoIncrementOrdersService from './AutoIncrementOrdersService';
import SaleInvoiceTransfromer from './SaleInvoiceTransformer';
import { ERRORS } from './constants';
import SaleInvoiceTransformer from './SaleInvoiceTransformer';
/**
* Sales invoices service
@@ -75,9 +75,6 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
@Inject()
autoIncrementOrdersService: AutoIncrementOrdersService;
@Inject()
saleInvoiceTransformer: SaleInvoiceTransfromer;
/**
* Validate whether sale invoice number unqiue on the storage.
*/
@@ -649,7 +646,7 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
if (!saleInvoice) {
throw new ServiceError(ERRORS.SALE_INVOICE_NOT_FOUND);
}
return this.saleInvoiceTransformer.transform(saleInvoice);
return new SaleInvoiceTransformer().transform(saleInvoice);
}
/**
@@ -702,7 +699,7 @@ export default class SaleInvoicesService implements ISalesInvoicesService {
.pagination(filter.page - 1, filter.pageSize);
return {
salesInvoices: this.saleInvoiceTransformer.transform(results),
salesInvoices: new SaleInvoiceTransformer().transform(results),
pagination,
filterMeta: dynamicFilter.getResponseMeta(),
};