From 1148fef9add36f6d65c14b0fcd0c1c1c86c7c255 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sat, 23 Sep 2023 14:19:42 +0200 Subject: [PATCH] fix(server): tax tracking on sale invoices --- .../20230810191606_create_tax_rates.js | 1 + packages/server/src/models/SaleInvoice.ts | 21 ++++++++++++++-- .../SalesTaxLiabilitySummaryTable.ts | 2 +- .../CommandSaleInvoiceDTOTransformer.ts | 2 ++ .../Sales/Invoices/InvoiceGLEntries.ts | 2 +- .../TaxRates/ItemEntriesTaxTransactions.ts | 24 +++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/server/src/database/migrations/20230810191606_create_tax_rates.js b/packages/server/src/database/migrations/20230810191606_create_tax_rates.js index f0270034a..881a9db31 100644 --- a/packages/server/src/database/migrations/20230810191606_create_tax_rates.js +++ b/packages/server/src/database/migrations/20230810191606_create_tax_rates.js @@ -19,6 +19,7 @@ exports.up = (knex) => { .unsigned() .references('id') .inTable('tax_rates'); + table.decimal('tax_rate').unsigned(); }) .table('sales_invoices', (table) => { table.boolean('is_inclusive_tax').defaultTo(false); diff --git a/packages/server/src/models/SaleInvoice.ts b/packages/server/src/models/SaleInvoice.ts index 828fb08c7..0e64fd7ee 100644 --- a/packages/server/src/models/SaleInvoice.ts +++ b/packages/server/src/models/SaleInvoice.ts @@ -14,7 +14,7 @@ export default class SaleInvoice extends mixin(TenantModel, [ ModelSearchable, ]) { public taxAmountWithheld: number; - public amount: number; + public balance: number; public paymentAmount: number; public exchangeRate: 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. * @returns {number} @@ -87,7 +104,7 @@ export default class SaleInvoice extends mixin(TenantModel, [ * @returns {number} */ get subtotalLocal() { - return this.amount * this.exchangeRate; + return this.amountLocal; } /** diff --git a/packages/server/src/services/FinancialStatements/SalesTaxLiabilitySummary/SalesTaxLiabilitySummaryTable.ts b/packages/server/src/services/FinancialStatements/SalesTaxLiabilitySummary/SalesTaxLiabilitySummaryTable.ts index add98b368..58fa2bc23 100644 --- a/packages/server/src/services/FinancialStatements/SalesTaxLiabilitySummary/SalesTaxLiabilitySummaryTable.ts +++ b/packages/server/src/services/FinancialStatements/SalesTaxLiabilitySummary/SalesTaxLiabilitySummaryTable.ts @@ -153,7 +153,7 @@ export class SalesTaxLiabilitySummaryTable extends R.compose( key: 'collectedTax', }, { - label: 'Tax Rate', + label: 'Tax Amount', key: 'taxRate', }, ]); diff --git a/packages/server/src/services/Sales/Invoices/CommandSaleInvoiceDTOTransformer.ts b/packages/server/src/services/Sales/Invoices/CommandSaleInvoiceDTOTransformer.ts index c8fcb867d..e6f4c054e 100644 --- a/packages/server/src/services/Sales/Invoices/CommandSaleInvoiceDTOTransformer.ts +++ b/packages/server/src/services/Sales/Invoices/CommandSaleInvoiceDTOTransformer.ts @@ -71,6 +71,8 @@ export class CommandSaleInvoiceDTOTransformer { ...entry, })); 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. this.taxDTOTransformer.assocTaxRateIdFromCodeToEntries(tenantId), // Sets default cost and sell account to invoice items entries. diff --git a/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts b/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts index e53f05958..d816672c2 100644 --- a/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts +++ b/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts @@ -181,7 +181,7 @@ export class SaleInvoiceGLEntries { index: number ): ILedgerEntry => { const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice); - const localAmount = entry.amount * saleInvoice.exchangeRate; + const localAmount = entry.amountExludingTax * saleInvoice.exchangeRate; return { ...commonEntry, diff --git a/packages/server/src/services/TaxRates/ItemEntriesTaxTransactions.ts b/packages/server/src/services/TaxRates/ItemEntriesTaxTransactions.ts index a9bd5c683..5eaa7b980 100644 --- a/packages/server/src/services/TaxRates/ItemEntriesTaxTransactions.ts +++ b/packages/server/src/services/TaxRates/ItemEntriesTaxTransactions.ts @@ -2,6 +2,7 @@ import { Inject, Service } from 'typedi'; import { keyBy, sumBy } from 'lodash'; import { ItemEntry } from '@/models'; import HasTenancyService from '../Tenancy/TenancyService'; +import { IItem, IItemEntry, IItemEntryDTO } from '@/interfaces'; @Service() export class ItemEntriesTaxTransactions { @@ -45,4 +46,27 @@ export class ItemEntriesTaxTransactions { return entry; }); }; + + /** + * Associates tax rate from tax id to entries. + * @param {number} tenantId + * @returns {Promise} + */ + 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; + }); + }; }