fix(server): tax tracking on sale invoices

This commit is contained in:
Ahmed Bouhuolia
2023-09-23 14:19:42 +02:00
parent 92ac0dbd25
commit 1148fef9ad
6 changed files with 48 additions and 4 deletions

View File

@@ -19,6 +19,7 @@ exports.up = (knex) => {
.unsigned() .unsigned()
.references('id') .references('id')
.inTable('tax_rates'); .inTable('tax_rates');
table.decimal('tax_rate').unsigned();
}) })
.table('sales_invoices', (table) => { .table('sales_invoices', (table) => {
table.boolean('is_inclusive_tax').defaultTo(false); table.boolean('is_inclusive_tax').defaultTo(false);

View File

@@ -14,7 +14,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
ModelSearchable, ModelSearchable,
]) { ]) {
public taxAmountWithheld: number; public taxAmountWithheld: number;
public amount: number; public balance: number;
public paymentAmount: number; public paymentAmount: number;
public exchangeRate: number; public exchangeRate: number;
public writtenoffAmount: number; public writtenoffAmount: number;
@@ -74,6 +74,23 @@ export default class SaleInvoice extends mixin(TenantModel, [
]; ];
} }
/**
* Invoice amount.
* @todo Sugger attribute to balance, we need to rename the balance to amount.
* @returns {number}
*/
get amount() {
return this.balance;
}
/**
* Invoice amount in base currency.
* @returns {number}
*/
get amountLocal() {
return this.amount * this.exchangeRate;
}
/** /**
* Subtotal. (Tax inclusive) if the tax inclusive is enabled. * Subtotal. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number} * @returns {number}
@@ -87,7 +104,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
* @returns {number} * @returns {number}
*/ */
get subtotalLocal() { get subtotalLocal() {
return this.amount * this.exchangeRate; return this.amountLocal;
} }
/** /**

View File

@@ -153,7 +153,7 @@ export class SalesTaxLiabilitySummaryTable extends R.compose(
key: 'collectedTax', key: 'collectedTax',
}, },
{ {
label: 'Tax Rate', label: 'Tax Amount',
key: 'taxRate', key: 'taxRate',
}, },
]); ]);

View File

@@ -71,6 +71,8 @@ export class CommandSaleInvoiceDTOTransformer {
...entry, ...entry,
})); }));
const asyncEntries = await composeAsync( const asyncEntries = await composeAsync(
// Associate tax rate from tax id to entries.
this.taxDTOTransformer.assocTaxRateFromTaxIdToEntries(tenantId),
// Associate tax rate id from tax code to entries. // Associate tax rate id from tax code to entries.
this.taxDTOTransformer.assocTaxRateIdFromCodeToEntries(tenantId), this.taxDTOTransformer.assocTaxRateIdFromCodeToEntries(tenantId),
// Sets default cost and sell account to invoice items entries. // Sets default cost and sell account to invoice items entries.

View File

@@ -181,7 +181,7 @@ export class SaleInvoiceGLEntries {
index: number index: number
): ILedgerEntry => { ): ILedgerEntry => {
const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice); const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice);
const localAmount = entry.amount * saleInvoice.exchangeRate; const localAmount = entry.amountExludingTax * saleInvoice.exchangeRate;
return { return {
...commonEntry, ...commonEntry,

View File

@@ -2,6 +2,7 @@ import { Inject, Service } from 'typedi';
import { keyBy, sumBy } from 'lodash'; import { keyBy, sumBy } from 'lodash';
import { ItemEntry } from '@/models'; import { ItemEntry } from '@/models';
import HasTenancyService from '../Tenancy/TenancyService'; import HasTenancyService from '../Tenancy/TenancyService';
import { IItem, IItemEntry, IItemEntryDTO } from '@/interfaces';
@Service() @Service()
export class ItemEntriesTaxTransactions { export class ItemEntriesTaxTransactions {
@@ -45,4 +46,27 @@ export class ItemEntriesTaxTransactions {
return entry; return entry;
}); });
}; };
/**
* Associates tax rate from tax id to entries.
* @param {number} tenantId
* @returns {Promise<IItemEntry[]>}
*/
public assocTaxRateFromTaxIdToEntries =
(tenantId: number) => async (entries: IItemEntry[]) => {
const entriesWithId = entries.filter((e) => e.taxRateId);
const taxRateIds = entriesWithId.map((e) => e.taxRateId);
const { TaxRate } = this.tenancy.models(tenantId);
const foundTaxes = await TaxRate.query().whereIn('id', taxRateIds);
const taxRatesMap = keyBy(foundTaxes, 'id');
return entries.map((entry) => {
if (entry.taxRateId) {
entry.taxRate = taxRatesMap[entry.taxRateId]?.rate;
}
return entry;
});
};
} }