feat: sale invoice model tax attributes

This commit is contained in:
Ahmed Bouhuolia
2023-09-06 14:01:40 +02:00
parent ac072d29fc
commit 983ceb5cc6
14 changed files with 346 additions and 161 deletions

View File

@@ -169,7 +169,7 @@ export default class SaleInvoicesController extends BaseController {
check('branch_id').optional({ nullable: true }).isNumeric().toInt(), check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
check('project_id').optional({ nullable: true }).isNumeric().toInt(), check('project_id').optional({ nullable: true }).isNumeric().toInt(),
check('is_tax_exclusive').optional().isBoolean().toBoolean(), check('is_inclusive_tax').optional().isBoolean().toBoolean(),
check('entries').exists().isArray({ min: 1 }), check('entries').exists().isArray({ min: 1 }),
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),

View File

@@ -43,6 +43,9 @@ exports.up = (knex) => {
.references('id') .references('id')
.inTable('tax_rates'); .inTable('tax_rates');
table.decimal('tax_rate').unsigned(); table.decimal('tax_rate').unsigned();
})
.table('sales_invoices', (table) => {
table.rename('balance', 'amount');
}); });
}; };

View File

@@ -18,6 +18,11 @@ export interface IItemEntry {
rate: number; rate: number;
amount: number; amount: number;
total: number;
amountInclusingTax: number;
amountExludingTax: number;
discountAmount: number;
landedCost: number; landedCost: number;
allocatedCostAmount: number; allocatedCostAmount: number;
unallocatedCostAmount: number; unallocatedCostAmount: number;

View File

@@ -5,7 +5,8 @@ import { IItemEntry, IItemEntryDTO } from './ItemEntry';
export interface ISaleInvoice { export interface ISaleInvoice {
id: number; id: number;
balance: number; amount: number;
amountLocal?: number;
paymentAmount: number; paymentAmount: number;
currencyCode: string; currencyCode: string;
exchangeRate?: number; exchangeRate?: number;
@@ -27,15 +28,21 @@ export interface ISaleInvoice {
branchId?: number; branchId?: number;
projectId?: number; projectId?: number;
localAmount?: number; writtenoffAmount?: number;
writtenoffAmountLocal?: number;
localWrittenoffAmount?: number;
writtenoffExpenseAccountId?: number; writtenoffExpenseAccountId?: number;
writtenoffExpenseAccount?: IAccount; writtenoffExpenseAccount?: IAccount;
taxAmountWithheld: number; taxAmountWithheld: number;
taxes: ITaxTransaction[] taxAmountWithheldLocal: number;
taxes: ITaxTransaction[];
total: number;
totalLocal: number;
subtotal: number;
subtotalLocal: number;
subtotalExludingTax: number;
} }
export interface ISaleInvoiceDTO { export interface ISaleInvoiceDTO {
@@ -54,6 +61,8 @@ export interface ISaleInvoiceDTO {
warehouseId?: number | null; warehouseId?: number | null;
projectId?: number; projectId?: number;
branchId?: number | null; branchId?: number | null;
isInclusiveTax?: boolean;
} }
export interface ISaleInvoiceCreateDTO extends ISaleInvoiceDTO { export interface ISaleInvoiceCreateDTO extends ISaleInvoiceDTO {

View File

@@ -1,8 +1,7 @@
import moment from 'moment'; import moment from 'moment';
import * as R from 'ramda'; import * as R from 'ramda';
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash'; import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
import { formatNumber } from 'utils'; import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
import { isArrayLikeObject } from 'lodash/fp';
export class Transformer { export class Transformer {
public context: any; public context: any;
@@ -82,6 +81,7 @@ export class Transformer {
const normlizedItem = this.normalizeModelItem(item); const normlizedItem = this.normalizeModelItem(item);
return R.compose( return R.compose(
sortObjectKeysAlphabetically,
this.transform, this.transform,
R.when(this.hasExcludeAttributes, this.excludeAttributesTransformed), R.when(this.hasExcludeAttributes, this.excludeAttributesTransformed),
this.includeAttributesTransformed this.includeAttributesTransformed

View File

@@ -1,11 +1,17 @@
import { Model } from 'objection'; import { Model } from 'objection';
import TenantModel from 'models/TenantModel'; import TenantModel from 'models/TenantModel';
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate';
export default class ItemEntry extends TenantModel { export default class ItemEntry extends TenantModel {
public taxRate: number; public taxRate: number;
public discount: number;
public quantity: number;
public rate: number;
public isInclusiveTax: number;
/** /**
* Table name. * Table name.
* @returns {string}
*/ */
static get tableName() { static get tableName() {
return 'items_entries'; return 'items_entries';
@@ -13,24 +19,66 @@ export default class ItemEntry extends TenantModel {
/** /**
* Timestamps columns. * Timestamps columns.
* @returns {string[]}
*/ */
get timestamps() { get timestamps() {
return ['created_at', 'updated_at']; return ['created_at', 'updated_at'];
} }
/**
* Virtual attributes.
* @returns {string[]}
*/
static get virtualAttributes() { static get virtualAttributes() {
return ['amount', 'taxAmount']; return [
'amount',
'taxAmount',
'amountExludingTax',
'amountInclusingTax',
'total',
];
} }
/**
* Item entry total.
* Amount of item entry includes tax and subtracted discount amount.
* @returns {number}
*/
get total() {
return this.amountInclusingTax;
}
/**
* Item entry amount.
* Amount of item entry that may include or exclude tax.
* @returns {number}
*/
get amount() { get amount() {
return ItemEntry.calcAmount(this); return this.quantity * this.rate;
} }
static calcAmount(itemEntry) { /**
const { discount, quantity, rate } = itemEntry; * Item entry amount including tax.
const total = quantity * rate; * @returns {number}
*/
get amountInclusingTax() {
return this.isInclusiveTax ? this.amount : this.amount + this.taxAmount;
}
return discount ? total - total * discount * 0.01 : total; /**
* Item entry amount excluding tax.
* @returns {number}
*/
get amountExludingTax() {
return this.isInclusiveTax ? this.amount - this.taxAmount : this.amount;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.amount * (this.discount / 100);
} }
/** /**
@@ -46,9 +94,14 @@ export default class ItemEntry extends TenantModel {
* @returns {number} * @returns {number}
*/ */
get taxAmount() { get taxAmount() {
return this.amount * this.tagRateFraction; return this.isInclusiveTax
? getInclusiveTaxAmount(this.amount, this.taxRate)
: getExlusiveTaxAmount(this.amount, this.taxRate);
} }
/**
* Item entry relations.
*/
static get relationMappings() { static get relationMappings() {
const Item = require('models/Item'); const Item = require('models/Item');
const BillLandedCostEntry = require('models/BillLandedCostEntry'); const BillLandedCostEntry = require('models/BillLandedCostEntry');
@@ -104,6 +157,9 @@ export default class ItemEntry extends TenantModel {
}, },
}, },
/**
* Sale receipt reference.
*/
receipt: { receipt: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: SaleReceipt.default, modelClass: SaleReceipt.default,
@@ -114,7 +170,7 @@ export default class ItemEntry extends TenantModel {
}, },
/** /**
* * Project task reference.
*/ */
projectTaskRef: { projectTaskRef: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
@@ -126,7 +182,7 @@ export default class ItemEntry extends TenantModel {
}, },
/** /**
* * Project expense reference.
*/ */
projectExpenseRef: { projectExpenseRef: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
@@ -138,7 +194,7 @@ export default class ItemEntry extends TenantModel {
}, },
/** /**
* * Project bill reference.
*/ */
projectBillRef: { projectBillRef: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,

View File

@@ -1,5 +1,5 @@
import { mixin, Model, raw } from 'objection'; import { mixin, Model, raw } from 'objection';
import { castArray } from 'lodash'; import { castArray, takeWhile } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import TenantModel from 'models/TenantModel'; import TenantModel from 'models/TenantModel';
import ModelSetting from './ModelSetting'; import ModelSetting from './ModelSetting';
@@ -13,10 +13,16 @@ export default class SaleInvoice extends mixin(TenantModel, [
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
taxAmountWithheld: number; public taxAmountWithheld: number;
balance: number; public amount: number;
paymentAmount: number; public paymentAmount: number;
exchangeRate: number; public exchangeRate: number;
public writtenoffAmount: number;
public creditedAmount: number;
public isInclusiveTax: boolean;
public writtenoffAt: Date;
public dueDate: Date;
public deliveredAt: Date;
/** /**
* Table name * Table name
@@ -32,6 +38,9 @@ export default class SaleInvoice extends mixin(TenantModel, [
return ['created_at', 'updated_at']; return ['created_at', 'updated_at'];
} }
/**
*
*/
get pluralName() { get pluralName() {
return 'asdfsdf'; return 'asdfsdf';
} }
@@ -41,140 +50,82 @@ export default class SaleInvoice extends mixin(TenantModel, [
*/ */
static get virtualAttributes() { static get virtualAttributes() {
return [ return [
'localAmount',
'dueAmount',
'balanceAmount',
'isDelivered', 'isDelivered',
'isOverdue', 'isOverdue',
'isPartiallyPaid', 'isPartiallyPaid',
'isFullyPaid', 'isFullyPaid',
'isPaid',
'isWrittenoff', 'isWrittenoff',
'isPaid',
'dueAmount',
'balanceAmount',
'remainingDays', 'remainingDays',
'overdueDays', 'overdueDays',
'filterByBranches',
'subtotal',
'subtotalLocal',
'subtotalExludingTax',
'taxAmountWithheldLocal',
'total',
'totalLocal',
'writtenoffAmountLocal',
]; ];
} }
/** /**
* Invoice total FCY. * Subtotal. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number} * @returns {number}
*/ */
get totalFcy() { get subtotal() {
return this.amountFcy + this.taxAmountWithheldFcy; return this.amount;
} }
/** /**
* Invoice total BCY. * Subtotal in base currency. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number} * @returns {number}
*/ */
get totalBcy() { get subtotalLocal() {
return this.amountBcy + this.taxAmountWithheldBcy; return this.amount * this.exchangeRate;
} }
/** /**
* Tax amount withheld FCY. * Sale invoice amount excluding tax.
* @returns {number} * @returns {number}
*/ */
get taxAmountWithheldFcy() { get subtotalExludingTax() {
return this.taxAmountWithheld; return this.isInclusiveTax
? this.subtotal - this.taxAmountWithheld
: this.subtotal;
} }
/** /**
* Tax amount withheld BCY. * Tax amount withheld in base currency.
* @returns {number} * @returns {number}
*/ */
get taxAmountWithheldBcy() { get taxAmountWithheldLocal() {
return this.taxAmountWithheld; return this.taxAmountWithheld * this.exchangeRate;
} }
/** /**
* Subtotal FCY. * Invoice total. (Tax included)
* @returns {number} * @returns {number}
*/ */
get subtotalFcy() {
return this.amountFcy;
}
/**
* Subtotal BCY.
* @returns {number}
*/
get subtotalBcy() {
return this.amountBcy;
}
/**
* Invoice due amount FCY.
* @returns {number}
*/
get dueAmountFcy() {
return this.amountFcy - this.paymentAmountFcy;
}
/**
* Invoice due amount BCY.
* @returns {number}
*/
get dueAmountBcy() {
return this.amountBcy - this.paymentAmountBcy;
}
/**
* Invoice amount FCY.
* @returns {number}
*/
get amountFcy() {
return this.balance;
}
/**
* Invoice amount BCY.
* @returns {number}
*/
get amountBcy() {
return this.balance * this.exchangeRate;
}
/**
* Invoice payment amount FCY.
* @returns {number}
*/
get paymentAmountFcy() {
return this.paymentAmount;
}
/**
* Invoice payment amount BCY.
* @returns {number}
*/
get paymentAmountBcy() {
return this.paymentAmount * this.exchangeRate;
}
/**
*
*/
get total() { get total() {
return this.balance + this.taxAmountWithheld; return this.isInclusiveTax
? this.subtotal
: this.subtotal + this.taxAmountWithheld;
} }
/** /**
* Invoice amount in local currency. * Invoice total in local currency. (Tax included)
* @returns {number} * @returns {number}
*/ */
get localAmount() { get totalLocal() {
return this.total * this.exchangeRate; return this.total * this.exchangeRate;
} }
/**
* Invoice local written-off amount.
* @returns {number}
*/
get localWrittenoffAmount() {
return this.writtenoffAmount * this.exchangeRate;
}
/** /**
* Detarmines whether the invoice is delivered. * Detarmines whether the invoice is delivered.
* @return {boolean} * @return {boolean}
@@ -205,7 +156,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
* @return {boolean} * @return {boolean}
*/ */
get dueAmount() { get dueAmount() {
return Math.max(this.balance - this.balanceAmount, 0); return Math.max(this.total - this.balanceAmount, 0);
} }
/** /**
@@ -213,7 +164,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
* @return {boolean} * @return {boolean}
*/ */
get isPartiallyPaid() { get isPartiallyPaid() {
return this.dueAmount !== this.balance && this.dueAmount > 0; return this.dueAmount !== this.total && this.dueAmount > 0;
} }
/** /**
@@ -491,7 +442,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
}, },
/** /**
* * Invoice may has associated cost transactions.
*/ */
costTransactions: { costTransactions: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
@@ -506,7 +457,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
}, },
/** /**
* * Invoice may has associated payment entries.
*/ */
paymentEntries: { paymentEntries: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,
@@ -529,6 +480,9 @@ export default class SaleInvoice extends mixin(TenantModel, [
}, },
}, },
/**
* Invoice may has associated written-off expense account.
*/
writtenoffExpenseAccount: { writtenoffExpenseAccount: {
relation: Model.BelongsToOneRelation, relation: Model.BelongsToOneRelation,
modelClass: Account.default, modelClass: Account.default,
@@ -539,7 +493,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
}, },
/** /**
* * Invoice may has associated tax rate transactions.
*/ */
taxes: { taxes: {
relation: Model.HasManyRelation, relation: Model.HasManyRelation,

View File

@@ -13,17 +13,14 @@ import {
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform'; import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform'; import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
import ItemsEntriesService from '@/services/Items/ItemsEntriesService'; import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators'; import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators';
import { SaleInvoiceIncrement } from './SaleInvoiceIncrement'; import { SaleInvoiceIncrement } from './SaleInvoiceIncrement';
import { formatDateFields } from 'utils'; import { formatDateFields } from 'utils';
import { ItemEntriesTaxTransactions } from '@/services/TaxRates/ItemEntriesTaxTransactions'; import { ItemEntriesTaxTransactions } from '@/services/TaxRates/ItemEntriesTaxTransactions';
import { ItemEntry } from '@/models';
@Service() @Service()
export class CommandSaleInvoiceDTOTransformer { export class CommandSaleInvoiceDTOTransformer {
@Inject()
private tenancy: HasTenancyService;
@Inject() @Inject()
private branchDTOTransform: BranchTransactionDTOTransform; private branchDTOTransform: BranchTransactionDTOTransform;
@@ -55,11 +52,9 @@ export class CommandSaleInvoiceDTOTransformer {
authorizedUser: ITenantUser, authorizedUser: ITenantUser,
oldSaleInvoice?: ISaleInvoice oldSaleInvoice?: ISaleInvoice
): Promise<ISaleInvoice> { ): Promise<ISaleInvoice> {
const { ItemEntry } = this.tenancy.models(tenantId); const entriesModels = this.transformDTOEntriesToModels(saleInvoiceDTO);
const amount = this.getDueBalanceItemEntries(entriesModels);
const balance = sumBy(saleInvoiceDTO.entries, (e) =>
ItemEntry.calcAmount(e)
);
// Retreive the next invoice number. // Retreive the next invoice number.
const autoNextNumber = this.invoiceIncrement.getNextInvoiceNumber(tenantId); const autoNextNumber = this.invoiceIncrement.getNextInvoiceNumber(tenantId);
@@ -72,6 +67,7 @@ export class CommandSaleInvoiceDTOTransformer {
const initialEntries = saleInvoiceDTO.entries.map((entry) => ({ const initialEntries = saleInvoiceDTO.entries.map((entry) => ({
referenceType: 'SaleInvoice', referenceType: 'SaleInvoice',
isInclusiveTax: saleInvoiceDTO.isInclusiveTax,
...entry, ...entry,
})); }));
const entries = await composeAsync( const entries = await composeAsync(
@@ -87,7 +83,7 @@ export class CommandSaleInvoiceDTOTransformer {
['invoiceDate', 'dueDate'] ['invoiceDate', 'dueDate']
), ),
// Avoid rewrite the deliver date in edit mode when already published. // Avoid rewrite the deliver date in edit mode when already published.
balance, amount,
currencyCode: customer.currencyCode, currencyCode: customer.currencyCode,
exchangeRate: saleInvoiceDTO.exchangeRate || 1, exchangeRate: saleInvoiceDTO.exchangeRate || 1,
...(saleInvoiceDTO.delivered && ...(saleInvoiceDTO.delivered &&
@@ -107,4 +103,29 @@ export class CommandSaleInvoiceDTOTransformer {
this.warehouseDTOTransform.transformDTO<ISaleInvoice>(tenantId) this.warehouseDTOTransform.transformDTO<ISaleInvoice>(tenantId)
)(initialDTO); )(initialDTO);
} }
/**
* Transforms the DTO entries to invoice entries models.
* @param {ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO} entries
* @returns {IItemEntry[]}
*/
private transformDTOEntriesToModels = (
saleInvoiceDTO: ISaleInvoiceCreateDTO | ISaleInvoiceEditDTO
): ItemEntry[] => {
return saleInvoiceDTO.entries.map((entry) => {
return ItemEntry.fromJson({
...entry,
isInclusiveTax: saleInvoiceDTO.isInclusiveTax,
});
});
};
/**
* Gets the due balance from the invoice entries.
* @param {IItemEntry[]} entries
* @returns {number}
*/
private getDueBalanceItemEntries = (entries: ItemEntry[]) => {
return sumBy(entries, (e) => e.amount);
};
} }

View File

@@ -93,7 +93,7 @@ export class SaleInvoiceGLEntries {
'SaleInvoice', 'SaleInvoice',
trx trx
); );
}; };
/** /**
* Retrieves the given invoice ledger. * Retrieves the given invoice ledger.
@@ -156,7 +156,7 @@ export class SaleInvoiceGLEntries {
return { return {
...commonEntry, ...commonEntry,
debit: saleInvoice.totalBcy, debit: saleInvoice.totalLocal,
accountId: ARAccountId, accountId: ARAccountId,
contactId: saleInvoice.customerId, contactId: saleInvoice.customerId,
accountNormal: AccountNormal.DEBIT, accountNormal: AccountNormal.DEBIT,

View File

@@ -1,4 +1,7 @@
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '@/lib/Transformer/Transformer';
import { formatNumber } from '@/utils';
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate';
import { format } from 'mathjs';
export class SaleInvoiceTaxEntryTransformer extends Transformer { export class SaleInvoiceTaxEntryTransformer extends Transformer {
/** /**
@@ -6,7 +9,14 @@ export class SaleInvoiceTaxEntryTransformer extends Transformer {
* @returns {Array} * @returns {Array}
*/ */
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return ['name', 'taxRateCode', 'raxRate', 'taxRateId']; return [
'name',
'taxRateCode',
'taxRate',
'taxRateId',
'taxRateAmount',
'taxRateAmountFormatted',
];
}; };
/** /**
@@ -31,7 +41,7 @@ export class SaleInvoiceTaxEntryTransformer extends Transformer {
* @param taxEntry * @param taxEntry
* @returns {number} * @returns {number}
*/ */
protected raxRate = (taxEntry) => { protected taxRate = (taxEntry) => {
return taxEntry.taxAmount || taxEntry.taxRate.rate; return taxEntry.taxAmount || taxEntry.taxRate.rate;
}; };
@@ -43,4 +53,26 @@ export class SaleInvoiceTaxEntryTransformer extends Transformer {
protected name = (taxEntry) => { protected name = (taxEntry) => {
return taxEntry.taxRate.name; return taxEntry.taxRate.name;
}; };
/**
* Retrieve tax rate amount.
* @param taxEntry
*/
protected taxRateAmount = (taxEntry) => {
const taxRate = this.taxRate(taxEntry);
return this.options.isInclusiveTax
? getInclusiveTaxAmount(this.options.amount, taxRate)
: getExlusiveTaxAmount(this.options.amount, taxRate);
};
/**
* Retrieve formatted tax rate amount.
* @returns {string}
*/
protected taxRateAmountFormatted = (taxEntry) => {
return formatNumber(this.taxRateAmount(taxEntry), {
currencyCode: this.options.currencyCode,
});
};
} }

View File

@@ -9,13 +9,19 @@ export class SaleInvoiceTransformer extends Transformer {
*/ */
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return [ return [
'formattedInvoiceDate', 'invoiceDateFormatted',
'formattedDueDate', 'dueDateFormatted',
'formattedAmount', 'dueAmountFormatted',
'formattedDueAmount', 'paymentAmountFormatted',
'formattedPaymentAmount', 'balanceAmountFormatted',
'formattedBalanceAmount', 'exchangeRateFormatted',
'formattedExchangeRate', 'subtotalFormatted',
'subtotalLocalFormatted',
'subtotalExludingTaxFormatted',
'taxAmountWithheldFormatted',
'taxAmountWithheldLocalFormatted',
'totalFormatted',
'totalLocalFormatted',
'taxes', 'taxes',
]; ];
}; };
@@ -25,7 +31,7 @@ export class SaleInvoiceTransformer extends Transformer {
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
* @returns {String} * @returns {String}
*/ */
protected formattedInvoiceDate = (invoice): string => { protected invoiceDateFormatted = (invoice): string => {
return this.formatDate(invoice.invoiceDate); return this.formatDate(invoice.invoiceDate);
}; };
@@ -34,27 +40,16 @@ export class SaleInvoiceTransformer extends Transformer {
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedDueDate = (invoice): string => { protected dueDateFormatted = (invoice): string => {
return this.formatDate(invoice.dueDate); return this.formatDate(invoice.dueDate);
}; };
/**
* Retrieve formatted invoice amount.
* @param {ISaleInvoice} invoice
* @returns {string}
*/
protected formattedAmount = (invoice): string => {
return formatNumber(invoice.balance, {
currencyCode: invoice.currencyCode,
});
};
/** /**
* Retrieve formatted invoice due amount. * Retrieve formatted invoice due amount.
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedDueAmount = (invoice): string => { protected dueAmountFormatted = (invoice): string => {
return formatNumber(invoice.dueAmount, { return formatNumber(invoice.dueAmount, {
currencyCode: invoice.currencyCode, currencyCode: invoice.currencyCode,
}); });
@@ -65,7 +60,7 @@ export class SaleInvoiceTransformer extends Transformer {
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedPaymentAmount = (invoice): string => { protected paymentAmountFormatted = (invoice): string => {
return formatNumber(invoice.paymentAmount, { return formatNumber(invoice.paymentAmount, {
currencyCode: invoice.currencyCode, currencyCode: invoice.currencyCode,
}); });
@@ -76,7 +71,7 @@ export class SaleInvoiceTransformer extends Transformer {
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedBalanceAmount = (invoice): string => { protected balanceAmountFormatted = (invoice): string => {
return formatNumber(invoice.balanceAmount, { return formatNumber(invoice.balanceAmount, {
currencyCode: invoice.currencyCode, currencyCode: invoice.currencyCode,
}); });
@@ -87,15 +82,98 @@ export class SaleInvoiceTransformer extends Transformer {
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
* @returns {string} * @returns {string}
*/ */
protected formattedExchangeRate = (invoice): string => { protected exchangeRateFormatted = (invoice): string => {
return formatNumber(invoice.exchangeRate, { money: false }); return formatNumber(invoice.exchangeRate, { money: false });
}; };
/**
* Retrieves formatted subtotal in base currency.
* (Tax inclusive if the tax inclusive is enabled)
* @param invoice
* @returns {string}
*/
protected subtotalFormatted = (invoice): string => {
return formatNumber(invoice.subtotal, {
currencyCode: this.context.organization.baseCurrency,
});
};
/**
* Retrieves formatted subtotal in foreign currency.
* (Tax inclusive if the tax inclusive is enabled)
* @param invoice
* @returns {string}
*/
protected subtotalLocalFormatted = (invoice): string => {
return formatNumber(invoice.subtotalLocal, {
currencyCode: invoice.currencyCode,
});
};
/**
* Retrieves formatted subtotal excluding tax in foreign currency.
* @param invoice
* @returns {string}
*/
protected subtotalExludingTaxFormatted = (invoice): string => {
return formatNumber(invoice.subtotalExludingTax, {
currencyCode: invoice.currencyCode,
});
};
/**
* Retrieves formatted tax amount withheld in foreign currency.
* @param invoice
* @returns {string}
*/
protected taxAmountWithheldFormatted = (invoice): string => {
return formatNumber(invoice.taxAmountWithheld, {
currencyCode: invoice.currencyCode,
});
};
/**
* Retrieves formatted tax amount withheld in base currency.
* @param invoice
* @returns {string}
*/
protected taxAmountWithheldLocalFormatted = (invoice): string => {
return formatNumber(invoice.taxAmountWithheldLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};
/**
* Retrieves formatted total in foreign currency.
* @param invoice
* @returns {string}
*/
protected totalFormatted = (invoice): string => {
return formatNumber(invoice.total, {
currencyCode: invoice.currencyCode,
});
};
/**
* Retrieves formatted total in base currency.
* @param invoice
* @returns {string}
*/
protected totalLocalFormatted = (invoice): string => {
return formatNumber(invoice.totalLocal, {
currencyCode: this.context.organization.baseCurrency,
});
};
/** /**
* Retrieve the taxes lines of sale invoice. * Retrieve the taxes lines of sale invoice.
* @param {ISaleInvoice} invoice * @param {ISaleInvoice} invoice
*/ */
protected taxes = (invoice) => { protected taxes = (invoice) => {
return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer()); return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), {
amount: invoice.amount,
isInclusiveTax: invoice.isInclusiveTax,
currencyCode: invoice.currencyCode,
});
}; };
} }

View File

@@ -1,6 +1,5 @@
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { keyBy, sumBy } from 'lodash'; import { keyBy, sumBy } from 'lodash';
import * as R from 'ramda';
import { ItemEntry } from '@/models'; import { ItemEntry } from '@/models';
import HasTenancyService from '../Tenancy/TenancyService'; import HasTenancyService from '../Tenancy/TenancyService';

View File

@@ -471,6 +471,15 @@ const castCommaListEnvVarToArray = (envVar: string): Array<string> => {
return envVar ? envVar?.split(',')?.map(_.trim) : []; return envVar ? envVar?.split(',')?.map(_.trim) : [];
}; };
export const sortObjectKeysAlphabetically = (object) => {
return Object.keys(object)
.sort()
.reduce((objEntries, key) => {
objEntries[key] = object[key];
return objEntries;
}, {});
};
export { export {
templateRender, templateRender,
accumSum, accumSum,
@@ -503,5 +512,5 @@ export {
mergeObjectsBykey, mergeObjectsBykey,
nestedArrayToFlatten, nestedArrayToFlatten,
assocDepthLevelToObjectTree, assocDepthLevelToObjectTree,
castCommaListEnvVarToArray castCommaListEnvVarToArray,
}; };

View File

@@ -0,0 +1,19 @@
/**
* Get inclusive tax amount.
* @param {number} amount
* @param {number} taxRate
* @returns {number}
*/
export const getInclusiveTaxAmount = (amount: number, taxRate: number) => {
return (amount * taxRate) / (100 + taxRate);
};
/**
* Get exclusive tax amount.
* @param {number} amount
* @param {number} taxRate
* @returns {number}
*/
export const getExlusiveTaxAmount = (amount: number, taxRate: number) => {
return (amount * taxRate) / 100;
};