diff --git a/packages/server/src/database/seeds/data/accounts.js b/packages/server/src/database/seeds/data/accounts.js index 5d21686c3..35430bbed 100644 --- a/packages/server/src/database/seeds/data/accounts.js +++ b/packages/server/src/database/seeds/data/accounts.js @@ -1,3 +1,14 @@ +export const OtherExpensesAccount = { + name: 'Other Expenses', + slug: 'other-expenses', + account_type: 'other-expense', + code: '40011', + description: '', + active: 1, + index: 1, + predefined: 1, +}; + export const TaxPayableAccount = { name: 'Tax Payable', slug: 'tax-payable', @@ -39,8 +50,38 @@ export const StripeClearingAccount = { code: '100020', active: true, index: 1, - predefined: true, -} + predefined: true, +}; + +export const DiscountExpenseAccount = { + name: 'Discount', + slug: 'discount', + account_type: 'other-income', + code: '40008', + active: true, + index: 1, + predefined: true, +}; + +export const PurchaseDiscountAccount = { + name: 'Purchase Discount', + slug: 'purchase-discount', + account_type: 'other-expense', + code: '40009', + active: true, + index: 1, + predefined: true, +}; + +export const OtherChargesAccount = { + name: 'Other Charges', + slug: 'other-charges', + account_type: 'other-income', + code: '40010', + active: true, + index: 1, + predefined: true, +}; export default [ { @@ -231,17 +272,7 @@ export default [ }, // Expenses - { - name: 'Other Expenses', - slug: 'other-expenses', - account_type: 'other-expense', - parent_account_id: null, - code: '40001', - description: '', - active: 1, - index: 1, - predefined: 1, - }, + OtherExpensesAccount, { name: 'Cost of Goods Sold', slug: 'cost-of-goods-sold', @@ -358,4 +389,7 @@ export default [ }, UnearnedRevenueAccount, PrepardExpenses, + DiscountExpenseAccount, + PurchaseDiscountAccount, + OtherChargesAccount, ]; diff --git a/packages/server/src/interfaces/SaleInvoice.ts b/packages/server/src/interfaces/SaleInvoice.ts index 897ac8499..b7f208ac0 100644 --- a/packages/server/src/interfaces/SaleInvoice.ts +++ b/packages/server/src/interfaces/SaleInvoice.ts @@ -80,6 +80,13 @@ export interface ISaleInvoice { pdfTemplateId?: number; paymentMethods?: Array; + + adjustment?: number; + adjustmentLocal?: number | null; + + discount?: number; + discountAmount?: number; + discountAmountLocal?: number | null; } export enum DiscountType { diff --git a/packages/server/src/interfaces/SaleReceipt.ts b/packages/server/src/interfaces/SaleReceipt.ts index ec0d2a726..8d854d46b 100644 --- a/packages/server/src/interfaces/SaleReceipt.ts +++ b/packages/server/src/interfaces/SaleReceipt.ts @@ -39,6 +39,9 @@ export interface ISaleReceipt { discountPercentage?: number | null; adjustment?: number; + adjustmentLocal?: number | null; + + discountAmountLocal?: number | null; } export interface ISalesReceiptsFilter { diff --git a/packages/server/src/models/Bill.ts b/packages/server/src/models/Bill.ts index 41e6364d4..e813ac52e 100644 --- a/packages/server/src/models/Bill.ts +++ b/packages/server/src/models/Bill.ts @@ -1,6 +1,7 @@ import { Model, raw, mixin } from 'objection'; import { castArray, defaultTo, difference } from 'lodash'; import moment from 'moment'; +import * as R from 'ramda'; import TenantModel from 'models/TenantModel'; import BillSettings from './Bill.Settings'; import ModelSetting from './ModelSetting'; @@ -55,8 +56,11 @@ export default class Bill extends mixin(TenantModel, [ 'amountLocal', 'discountAmount', + 'discountAmountLocal', 'discountPercentage', + 'adjustmentLocal', + 'subtotal', 'subtotalLocal', 'subtotalExludingTax', @@ -118,6 +122,15 @@ export default class Bill extends mixin(TenantModel, [ : this.subtotal * (this.discount / 100); } + /** + * Discount amount in local currency. + * @returns {number | null} + */ + get discountAmountLocal() { + return this.discountAmount ? this.discountAmount * this.exchangeRate : null; + } + + /** /** * Discount percentage. * @returns {number | null} @@ -126,6 +139,14 @@ export default class Bill extends mixin(TenantModel, [ return this.discountType === DiscountType.Percentage ? this.discount : null; } + /** + * Adjustment amount in local currency. + * @returns {number | null} + */ + get adjustmentLocal() { + return this.adjustment ? this.adjustment * this.exchangeRate : null; + } + /** * Invoice total. (Tax included) * @returns {number} @@ -133,12 +154,11 @@ export default class Bill extends mixin(TenantModel, [ get total() { const adjustmentAmount = defaultTo(this.adjustment, 0); - return this.isInclusiveTax - ? this.subtotal - this.discountAmount - adjustmentAmount - : this.subtotal + - this.taxAmountWithheld - - this.discountAmount - - adjustmentAmount; + return R.compose( + R.add(adjustmentAmount), + R.subtract(R.__, this.discountAmount), + R.when(R.always(this.isInclusiveTax), R.add(this.taxAmountWithheld)) + )(this.subtotal); } /** diff --git a/packages/server/src/models/CreditNote.ts b/packages/server/src/models/CreditNote.ts index b2cee8572..ab5f6c878 100644 --- a/packages/server/src/models/CreditNote.ts +++ b/packages/server/src/models/CreditNote.ts @@ -51,10 +51,13 @@ export default class CreditNote extends mixin(TenantModel, [ 'subtotalLocal', 'discountAmount', + 'discountAmountLocal', 'discountPercentage', 'total', 'totalLocal', + + 'adjustmentLocal', ]; } @@ -92,14 +95,28 @@ export default class CreditNote extends mixin(TenantModel, [ : this.subtotal * (this.discount / 100); } + /** + * Discount amount in local currency. + * @returns {number} + */ + get discountAmountLocal() { + return this.discountAmount ? this.discountAmount * this.exchangeRate : null; + } + /** * Discount percentage. * @returns {number | null} */ get discountPercentage(): number | null { - return this.discountType === DiscountType.Percentage - ? this.discount - : null; + return this.discountType === DiscountType.Percentage ? this.discount : null; + } + + /** + * Adjustment amount in local currency. + * @returns {number} + */ + get adjustmentLocal() { + return this.adjustment ? this.adjustment * this.exchangeRate : null; } /** @@ -107,7 +124,7 @@ export default class CreditNote extends mixin(TenantModel, [ * @returns {number} */ get total() { - return this.subtotal - this.discountAmount - this.adjustment; + return this.subtotal - this.discountAmount + this.adjustment; } /** diff --git a/packages/server/src/models/SaleEstimate.ts b/packages/server/src/models/SaleEstimate.ts index 83df38c1e..fce0f334b 100644 --- a/packages/server/src/models/SaleEstimate.ts +++ b/packages/server/src/models/SaleEstimate.ts @@ -116,7 +116,7 @@ export default class SaleEstimate extends mixin(TenantModel, [ get total() { const adjustmentAmount = defaultTo(this.adjustment, 0); - return this.subtotal - this.discountAmount - adjustmentAmount; + return this.subtotal - this.discountAmount + adjustmentAmount; } /** diff --git a/packages/server/src/models/SaleInvoice.ts b/packages/server/src/models/SaleInvoice.ts index 7e874d2de..dfcb0a954 100644 --- a/packages/server/src/models/SaleInvoice.ts +++ b/packages/server/src/models/SaleInvoice.ts @@ -1,4 +1,5 @@ import { mixin, Model, raw } from 'objection'; +import * as R from 'ramda'; import { castArray, defaultTo, takeWhile } from 'lodash'; import moment from 'moment'; import TenantModel from 'models/TenantModel'; @@ -72,12 +73,14 @@ export default class SaleInvoice extends mixin(TenantModel, [ 'taxAmountWithheldLocal', 'discountAmount', + 'discountAmountLocal', 'discountPercentage', 'total', 'totalLocal', 'writtenoffAmountLocal', + 'adjustmentLocal', ]; } @@ -142,14 +145,28 @@ export default class SaleInvoice extends mixin(TenantModel, [ : this.subtotal * (this.discount / 100); } + /** + * Local discount amount. + * @returns {number | null} + */ + get discountAmountLocal() { + return this.discountAmount ? this.discountAmount * this.exchangeRate : null; + } + /** * Discount percentage. * @returns {number | null} */ get discountPercentage(): number | null { - return this.discountType === DiscountType.Percentage - ? this.discount - : null; + return this.discountType === DiscountType.Percentage ? this.discount : null; + } + + /** + * Adjustment amount in local currency. + * @returns {number | null} + */ + get adjustmentLocal(): number | null { + return this.adjustment ? this.adjustment * this.exchangeRate : null; } /** @@ -158,11 +175,12 @@ export default class SaleInvoice extends mixin(TenantModel, [ */ get total() { const adjustmentAmount = defaultTo(this.adjustment, 0); - const differencies = this.discountAmount + adjustmentAmount; - return this.isInclusiveTax - ? this.subtotal - differencies - : this.subtotal + this.taxAmountWithheld - differencies; + return R.compose( + R.add(adjustmentAmount), + R.subtract(R.__, this.discountAmount), + R.when(R.always(this.isInclusiveTax), R.add(this.taxAmountWithheld)) + )(this.subtotal); } /** diff --git a/packages/server/src/models/SaleReceipt.ts b/packages/server/src/models/SaleReceipt.ts index 8bef046ed..95abf7430 100644 --- a/packages/server/src/models/SaleReceipt.ts +++ b/packages/server/src/models/SaleReceipt.ts @@ -49,9 +49,16 @@ export default class SaleReceipt extends mixin(TenantModel, [ 'total', 'totalLocal', + 'adjustment', + 'adjustmentLocal', + 'discountAmount', + 'discountAmountLocal', 'discountPercentage', + 'paid', + 'paidLocal', + 'isClosed', 'isDraft', ]; @@ -91,14 +98,20 @@ export default class SaleReceipt extends mixin(TenantModel, [ : this.subtotal * (this.discount / 100); } + /** + * Discount amount in local currency. + * @returns {number | null} + */ + get discountAmountLocal() { + return this.discountAmount ? this.discountAmount * this.exchangeRate : null; + } + /** * Discount percentage. * @returns {number | null} */ get discountPercentage(): number | null { - return this.discountType === DiscountType.Percentage - ? this.discount - : null; + return this.discountType === DiscountType.Percentage ? this.discount : null; } /** @@ -108,7 +121,7 @@ export default class SaleReceipt extends mixin(TenantModel, [ get total() { const adjustmentAmount = defaultTo(this.adjustment, 0); - return this.subtotal - this.discountAmount - adjustmentAmount; + return this.subtotal - this.discountAmount + adjustmentAmount; } /** @@ -119,6 +132,30 @@ export default class SaleReceipt extends mixin(TenantModel, [ return this.total * this.exchangeRate; } + /** + * Adjustment amount in local currency. + * @returns {number} + */ + get adjustmentLocal() { + return this.adjustment * this.exchangeRate; + } + + /** + * Receipt paid amount. + * @returns {number} + */ + get paid() { + return this.total; + } + + /** + * Receipt paid amount in local currency. + * @returns {number} + */ + get paidLocal() { + return this.paid * this.exchangeRate; + } + /** * Detarmine whether the sale receipt closed. * @return {boolean} diff --git a/packages/server/src/models/VendorCredit.ts b/packages/server/src/models/VendorCredit.ts index 24f96afa8..75473060e 100644 --- a/packages/server/src/models/VendorCredit.ts +++ b/packages/server/src/models/VendorCredit.ts @@ -60,6 +60,14 @@ export default class VendorCredit extends mixin(TenantModel, [ : this.subtotal * (this.discount / 100); } + /** + * Discount amount in local currency. + * @returns {number | null} + */ + get discountAmountLocal() { + return this.discountAmount ? this.discountAmount * this.exchangeRate : null; + } + /** * Discount percentage. * @returns {number | null} @@ -68,12 +76,20 @@ export default class VendorCredit extends mixin(TenantModel, [ return this.discountType === DiscountType.Percentage ? this.discount : null; } + /** + * Adjustment amount in local currency. + * @returns {number | null} + */ + get adjustmentLocal() { + return this.adjustment ? this.adjustment * this.exchangeRate : null; + } + /** * Vendor credit total. * @returns {number} */ get total() { - return this.subtotal - this.discountAmount - this.adjustment; + return this.subtotal - this.discountAmount + this.adjustment; } /** @@ -180,8 +196,11 @@ export default class VendorCredit extends mixin(TenantModel, [ 'localAmount', 'discountAmount', + 'discountAmountLocal', 'discountPercentage', + 'adjustmentLocal', + 'total', 'totalLocal', ]; diff --git a/packages/server/src/repositories/AccountRepository.ts b/packages/server/src/repositories/AccountRepository.ts index 53b26e284..43e320b22 100644 --- a/packages/server/src/repositories/AccountRepository.ts +++ b/packages/server/src/repositories/AccountRepository.ts @@ -3,7 +3,11 @@ import TenantRepository from '@/repositories/TenantRepository'; import { IAccount } from '@/interfaces'; import { Knex } from 'knex'; import { + DiscountExpenseAccount, + OtherChargesAccount, + OtherExpensesAccount, PrepardExpenses, + PurchaseDiscountAccount, StripeClearingAccount, TaxPayableAccount, UnearnedRevenueAccount, @@ -188,9 +192,9 @@ export default class AccountRepository extends TenantRepository { /** * Finds or creates the unearned revenue. - * @param {Record} extraAttrs - * @param {Knex.Transaction} trx - * @returns + * @param {Record} extraAttrs + * @param {Knex.Transaction} trx + * @returns */ public async findOrCreateUnearnedRevenue( extraAttrs: Record = {}, @@ -219,9 +223,9 @@ export default class AccountRepository extends TenantRepository { /** * Finds or creates the prepard expenses account. - * @param {Record} extraAttrs - * @param {Knex.Transaction} trx - * @returns + * @param {Record} extraAttrs + * @param {Knex.Transaction} trx + * @returns */ public async findOrCreatePrepardExpenses( extraAttrs: Record = {}, @@ -249,12 +253,11 @@ export default class AccountRepository extends TenantRepository { return result; } - /** * Finds or creates the stripe clearing account. - * @param {Record} extraAttrs - * @param {Knex.Transaction} trx - * @returns + * @param {Record} extraAttrs + * @param {Knex.Transaction} trx + * @returns */ public async findOrCreateStripeClearing( extraAttrs: Record = {}, @@ -281,4 +284,114 @@ export default class AccountRepository extends TenantRepository { } return result; } + + /** + * Finds or creates the discount expense account. + * @param {Record} extraAttrs + * @param {Knex.Transaction} trx + * @returns + */ + public async findOrCreateDiscountAccount( + extraAttrs: Record = {}, + trx?: Knex.Transaction + ) { + // Retrieves the given tenant metadata. + const tenantMeta = await TenantMetadata.query().findOne({ + tenantId: this.tenantId, + }); + const _extraAttrs = { + currencyCode: tenantMeta.baseCurrency, + ...extraAttrs, + }; + + let result = await this.model + .query(trx) + .findOne({ slug: DiscountExpenseAccount.slug, ..._extraAttrs }); + + if (!result) { + result = await this.model.query(trx).insertAndFetch({ + ...DiscountExpenseAccount, + ..._extraAttrs, + }); + } + return result; + } + + public async findOrCreatePurchaseDiscountAccount( + extraAttrs: Record = {}, + trx?: Knex.Transaction + ) { + // Retrieves the given tenant metadata. + const tenantMeta = await TenantMetadata.query().findOne({ + tenantId: this.tenantId, + }); + const _extraAttrs = { + currencyCode: tenantMeta.baseCurrency, + ...extraAttrs, + }; + + let result = await this.model + .query(trx) + .findOne({ slug: PurchaseDiscountAccount.slug, ..._extraAttrs }); + + if (!result) { + result = await this.model.query(trx).insertAndFetch({ + ...PurchaseDiscountAccount, + ..._extraAttrs, + }); + } + return result; + } + + public async findOrCreateOtherChargesAccount( + extraAttrs: Record = {}, + trx?: Knex.Transaction + ) { + // Retrieves the given tenant metadata. + const tenantMeta = await TenantMetadata.query().findOne({ + tenantId: this.tenantId, + }); + const _extraAttrs = { + currencyCode: tenantMeta.baseCurrency, + ...extraAttrs, + }; + + let result = await this.model + .query(trx) + .findOne({ slug: OtherChargesAccount.slug, ..._extraAttrs }); + + if (!result) { + result = await this.model.query(trx).insertAndFetch({ + ...OtherChargesAccount, + ..._extraAttrs, + }); + } + return result; + } + + public async findOrCreateOtherExpensesAccount( + extraAttrs: Record = {}, + trx?: Knex.Transaction + ) { + // Retrieves the given tenant metadata. + const tenantMeta = await TenantMetadata.query().findOne({ + tenantId: this.tenantId, + }); + const _extraAttrs = { + currencyCode: tenantMeta.baseCurrency, + ...extraAttrs, + }; + + let result = await this.model + .query(trx) + .findOne({ slug: OtherExpensesAccount.slug, ..._extraAttrs }); + + if (!result) { + result = await this.model.query(trx).insertAndFetch({ + ...OtherExpensesAccount, + ..._extraAttrs, + }); + } + return result; + } } diff --git a/packages/server/src/services/Accounting/Ledger.ts b/packages/server/src/services/Accounting/Ledger.ts index 155a61e10..6e7519bc8 100644 --- a/packages/server/src/services/Accounting/Ledger.ts +++ b/packages/server/src/services/Accounting/Ledger.ts @@ -238,6 +238,7 @@ export default class Ledger implements ILedger { return { credit: defaultTo(entry.credit, 0), debit: defaultTo(entry.debit, 0), + exchangeRate: entry.exchangeRate, currencyCode: entry.currencyCode, diff --git a/packages/server/src/services/Accounting/LedgerEntriesStorage.ts b/packages/server/src/services/Accounting/LedgerEntriesStorage.ts index dd192b811..8f789e3cf 100644 --- a/packages/server/src/services/Accounting/LedgerEntriesStorage.ts +++ b/packages/server/src/services/Accounting/LedgerEntriesStorage.ts @@ -9,15 +9,18 @@ import { import HasTenancyService from '@/services/Tenancy/TenancyService'; import { transformLedgerEntryToTransaction } from './utils'; +// Filter the blank entries. +const filterBlankEntry = (entry: ILedgerEntry) => Boolean(entry.credit || entry.debit); + @Service() export class LedgerEntriesStorage { @Inject() - tenancy: HasTenancyService; + private tenancy: HasTenancyService; /** * Saves entries of the given ledger. - * @param {number} tenantId - * @param {ILedger} ledger - * @param {Knex.Transaction} knex + * @param {number} tenantId + * @param {ILedger} ledger + * @param {Knex.Transaction} knex * @returns {Promise} */ public saveEntries = async ( @@ -26,7 +29,7 @@ export class LedgerEntriesStorage { trx?: Knex.Transaction ) => { const saveEntryQueue = async.queue(this.saveEntryTask, 10); - const entries = ledger.getEntries(); + const entries = ledger.filter(filterBlankEntry).getEntries(); entries.forEach((entry) => { saveEntryQueue.push({ tenantId, entry, trx }); @@ -57,8 +60,8 @@ export class LedgerEntriesStorage { /** * Saves the ledger entry to the account transactions repository. - * @param {number} tenantId - * @param {ILedgerEntry} entry + * @param {number} tenantId + * @param {ILedgerEntry} entry * @returns {Promise} */ private saveEntry = async ( diff --git a/packages/server/src/services/CreditNotes/CreditNoteGLEntries.ts b/packages/server/src/services/CreditNotes/CreditNoteGLEntries.ts index 697072f3c..08734d37a 100644 --- a/packages/server/src/services/CreditNotes/CreditNoteGLEntries.ts +++ b/packages/server/src/services/CreditNotes/CreditNoteGLEntries.ts @@ -12,6 +12,7 @@ import { import HasTenancyService from '@/services/Tenancy/TenancyService'; import Ledger from '@/services/Accounting/Ledger'; import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; +import { SaleReceipt } from '@/models'; @Service() export default class CreditNoteGLEntries { @@ -29,11 +30,15 @@ export default class CreditNoteGLEntries { */ private getCreditNoteGLedger = ( creditNote: ICreditNote, - receivableAccount: number + receivableAccount: number, + discountAccount: number, + adjustmentAccount: number ): Ledger => { const ledgerEntries = this.getCreditNoteGLEntries( creditNote, - receivableAccount + receivableAccount, + discountAccount, + adjustmentAccount ); return new Ledger(ledgerEntries); }; @@ -49,9 +54,16 @@ export default class CreditNoteGLEntries { tenantId: number, creditNote: ICreditNote, payableAccount: number, + discountAccount: number, + adjustmentAccount: number, trx?: Knex.Transaction ): Promise => { - const ledger = this.getCreditNoteGLedger(creditNote, payableAccount); + const ledger = this.getCreditNoteGLedger( + creditNote, + payableAccount, + discountAccount, + adjustmentAccount + ); await this.ledgerStorage.commit(tenantId, ledger, trx); }; @@ -98,11 +110,18 @@ export default class CreditNoteGLEntries { const ARAccount = await accountRepository.findOrCreateAccountReceivable( creditNoteWithItems.currencyCode ); + const discountAccount = await accountRepository.findOrCreateDiscountAccount( + {} + ); + const adjustmentAccount = + await accountRepository.findOrCreateOtherChargesAccount({}); // Saves the credit note GL entries. await this.saveCreditNoteGLEntries( tenantId, creditNoteWithItems, ARAccount.id, + discountAccount.id, + adjustmentAccount.id, trx ); }; @@ -169,7 +188,7 @@ export default class CreditNoteGLEntries { return { ...commonEntry, - credit: creditNote.localAmount, + credit: creditNote.totalLocal, accountId: ARAccountId, contactId: creditNote.customerId, index: 1, @@ -206,6 +225,50 @@ export default class CreditNoteGLEntries { } ); + /** + * Retrieves the credit note discount entry. + * @param {ICreditNote} creditNote + * @param {number} discountAccountId + * @returns {ILedgerEntry} + */ + private getDiscountEntry = ( + creditNote: ICreditNote, + discountAccountId: number + ): ILedgerEntry => { + const commonEntry = this.getCreditNoteCommonEntry(creditNote); + + return { + ...commonEntry, + credit: creditNote.discountAmountLocal, + accountId: discountAccountId, + index: 1, + accountNormal: AccountNormal.CREDIT, + }; + }; + + /** + * Retrieves the credit note adjustment entry. + * @param {ICreditNote} creditNote + * @param {number} adjustmentAccountId + * @returns {ILedgerEntry} + */ + private getAdjustmentEntry = ( + creditNote: ICreditNote, + adjustmentAccountId: number + ): ILedgerEntry => { + const commonEntry = this.getCreditNoteCommonEntry(creditNote); + const adjustmentAmount = Math.abs(creditNote.adjustmentLocal); + + return { + ...commonEntry, + credit: creditNote.adjustmentLocal < 0 ? adjustmentAmount : 0, + debit: creditNote.adjustmentLocal > 0 ? adjustmentAmount : 0, + accountId: adjustmentAccountId, + accountNormal: AccountNormal.CREDIT, + index: 1, + }; + }; + /** * Retrieve the credit note GL entries. * @param {ICreditNote} creditNote - Credit note. @@ -214,13 +277,21 @@ export default class CreditNoteGLEntries { */ public getCreditNoteGLEntries = ( creditNote: ICreditNote, - ARAccountId: number + ARAccountId: number, + discountAccountId: number, + adjustmentAccountId: number ): ILedgerEntry[] => { const AREntry = this.getCreditNoteAREntry(creditNote, ARAccountId); const getItemEntry = this.getCreditNoteItemEntry(creditNote); const itemsEntries = creditNote.entries.map(getItemEntry); - return [AREntry, ...itemsEntries]; + const discountEntry = this.getDiscountEntry(creditNote, discountAccountId); + const adjustmentEntry = this.getAdjustmentEntry( + creditNote, + adjustmentAccountId + ); + + return [AREntry, discountEntry, adjustmentEntry, ...itemsEntries]; }; } diff --git a/packages/server/src/services/CreditNotes/CreditNoteTransformer.ts b/packages/server/src/services/CreditNotes/CreditNoteTransformer.ts index 611d006ad..86e84681d 100644 --- a/packages/server/src/services/CreditNotes/CreditNoteTransformer.ts +++ b/packages/server/src/services/CreditNotes/CreditNoteTransformer.ts @@ -18,11 +18,18 @@ export class CreditNoteTransformer extends Transformer { 'formattedAmount', 'formattedCreditsUsed', 'formattedSubtotal', + 'discountAmountFormatted', + 'discountAmountLocalFormatted', + 'discountPercentageFormatted', + 'adjustmentFormatted', + 'adjustmentLocalFormatted', + 'totalFormatted', 'totalLocalFormatted', + 'entries', 'attachments', ]; @@ -39,7 +46,7 @@ export class CreditNoteTransformer extends Transformer { /** * Retrieve formatted created at date. - * @param credit + * @param credit * @returns {string} */ protected formattedCreatedAt = (credit): string => { @@ -90,7 +97,7 @@ export class CreditNoteTransformer extends Transformer { /** * Retrieves formatted discount amount. - * @param credit + * @param credit * @returns {string} */ protected discountAmountFormatted = (credit): string => { @@ -100,20 +107,30 @@ export class CreditNoteTransformer extends Transformer { }); }; + /** + * Retrieves the formatted discount amount in local currency. + * @param {ICreditNote} credit + * @returns {string} + */ + protected discountAmountLocalFormatted = (credit): string => { + return formatNumber(credit.discountAmountLocal, { + currencyCode: credit.currencyCode, + excerptZero: true, + }); + }; + /** * Retrieves formatted discount percentage. - * @param credit + * @param credit * @returns {string} */ protected discountPercentageFormatted = (credit): string => { - return credit.discountPercentage - ? `${credit.discountPercentage}%` - : ''; + return credit.discountPercentage ? `${credit.discountPercentage}%` : ''; }; /** * Retrieves formatted adjustment amount. - * @param credit + * @param credit * @returns {string} */ protected adjustmentFormatted = (credit): string => { @@ -123,9 +140,21 @@ export class CreditNoteTransformer extends Transformer { }); }; + /** + * Retrieves the formatted adjustment amount in local currency. + * @param {ICreditNote} credit + * @returns {string} + */ + protected adjustmentLocalFormatted = (credit): string => { + return formatNumber(credit.adjustmentLocal, { + currencyCode: this.context.organization.baseCurrency, + excerptZero: true, + }); + }; + /** * Retrieves the formatted total. - * @param credit + * @param credit * @returns {string} */ protected totalFormatted = (credit): string => { @@ -136,7 +165,7 @@ export class CreditNoteTransformer extends Transformer { /** * Retrieves the formatted total in local currency. - * @param credit + * @param credit * @returns {string} */ protected totalLocalFormatted = (credit): string => { diff --git a/packages/server/src/services/Purchases/Bills/BillGLEntries.ts b/packages/server/src/services/Purchases/Bills/BillGLEntries.ts index 9c1352e9a..a735b9833 100644 --- a/packages/server/src/services/Purchases/Bills/BillGLEntries.ts +++ b/packages/server/src/services/Purchases/Bills/BillGLEntries.ts @@ -52,10 +52,18 @@ export class BillGLEntries { {}, trx ); + // Find or create other expenses account. + const otherExpensesAccount = + await accountRepository.findOrCreateOtherExpensesAccount({}, trx); + // Find or create purchase discount account. + const purchaseDiscountAccount = + await accountRepository.findOrCreatePurchaseDiscountAccount({}, trx); const billLedger = this.getBillLedger( bill, APAccount.id, - taxPayableAccount.id + taxPayableAccount.id, + purchaseDiscountAccount.id, + otherExpensesAccount.id ); // Commit the GL enties on the storage. await this.ledgerStorage.commit(tenantId, billLedger, trx); @@ -102,6 +110,7 @@ export class BillGLEntries { return { debit: 0, credit: 0, + currencyCode: bill.currencyCode, exchangeRate: bill.exchangeRate || 1, @@ -240,6 +249,52 @@ export class BillGLEntries { return nonZeroTaxEntries.map(transformTaxEntry); }; + /** + * Retrieves the purchase discount GL entry. + * @param {IBill} bill + * @param {number} purchaseDiscountAccountId + * @returns {ILedgerEntry} + */ + private getPurchaseDiscountEntry = ( + bill: IBill, + purchaseDiscountAccountId: number + ) => { + const commonEntry = this.getBillCommonEntry(bill); + + return { + ...commonEntry, + credit: bill.discountAmountLocal, + accountId: purchaseDiscountAccountId, + accountNormal: AccountNormal.DEBIT, + index: 1, + indexGroup: 40, + }; + }; + + /** + * Retrieves the purchase other charges GL entry. + * @param {IBill} bill + * @param {number} otherChargesAccountId + * @returns {ILedgerEntry} + */ + private getAdjustmentEntry = ( + bill: IBill, + otherExpensesAccountId: number + ) => { + const commonEntry = this.getBillCommonEntry(bill); + const adjustmentAmount = Math.abs(bill.adjustmentLocal); + + return { + ...commonEntry, + debit: bill.adjustmentLocal > 0 ? adjustmentAmount : 0, + credit: bill.adjustmentLocal < 0 ? adjustmentAmount : 0, + accountId: otherExpensesAccountId, + accountNormal: AccountNormal.DEBIT, + index: 1, + indexGroup: 40, + }; + }; + /** * Retrieves the given bill GL entries. * @param {IBill} bill @@ -249,7 +304,9 @@ export class BillGLEntries { private getBillGLEntries = ( bill: IBill, payableAccountId: number, - taxPayableAccountId: number + taxPayableAccountId: number, + purchaseDiscountAccountId: number, + otherExpensesAccountId: number ): ILedgerEntry[] => { const payableEntry = this.getBillPayableEntry(payableAccountId, bill); @@ -262,8 +319,24 @@ export class BillGLEntries { ); const taxEntries = this.getBillTaxEntries(bill, taxPayableAccountId); + const purchaseDiscountEntry = this.getPurchaseDiscountEntry( + bill, + purchaseDiscountAccountId + ); + const adjustmentEntry = this.getAdjustmentEntry( + bill, + otherExpensesAccountId + ); + // Allocate cost entries journal entries. - return [payableEntry, ...itemsEntries, ...landedCostEntries, ...taxEntries]; + return [ + payableEntry, + ...itemsEntries, + ...landedCostEntries, + ...taxEntries, + purchaseDiscountEntry, + adjustmentEntry, + ]; }; /** @@ -275,14 +348,17 @@ export class BillGLEntries { private getBillLedger = ( bill: IBill, payableAccountId: number, - taxPayableAccountId: number + taxPayableAccountId: number, + purchaseDiscountAccountId: number, + otherExpensesAccountId: number ) => { const entries = this.getBillGLEntries( bill, payableAccountId, - taxPayableAccountId + taxPayableAccountId, + purchaseDiscountAccountId, + otherExpensesAccountId ); - return new Ledger(entries); }; } diff --git a/packages/server/src/services/Purchases/Bills/PurchaseInvoiceTransformer.ts b/packages/server/src/services/Purchases/Bills/PurchaseInvoiceTransformer.ts index a6766a0db..05aa0311d 100644 --- a/packages/server/src/services/Purchases/Bills/PurchaseInvoiceTransformer.ts +++ b/packages/server/src/services/Purchases/Bills/PurchaseInvoiceTransformer.ts @@ -20,13 +20,21 @@ export class PurchaseInvoiceTransformer extends Transformer { 'formattedBalance', 'formattedDueAmount', 'formattedExchangeRate', + 'subtotalFormatted', 'subtotalLocalFormatted', + 'subtotalExcludingTaxFormatted', 'taxAmountWithheldLocalFormatted', + 'discountAmountFormatted', + 'discountAmountLocalFormatted', + 'discountPercentageFormatted', + 'adjustmentFormatted', + 'adjustmentLocalFormatted', + 'totalFormatted', 'totalLocalFormatted', 'taxes', @@ -175,15 +183,25 @@ export class PurchaseInvoiceTransformer extends Transformer { }); }; + /** + * Retrieves the formatted discount amount in local currency. + * @param {IBill} bill + * @returns {string} + */ + protected discountAmountLocalFormatted = (bill): string => { + return formatNumber(bill.discountAmountLocal, { + currencyCode: this.context.organization.baseCurrency, + excerptZero: true, + }); + }; + /** * Retrieves the formatted discount percentage. * @param {IBill} bill * @returns {string} */ protected discountPercentageFormatted = (bill): string => { - return bill.discountPercentage - ? `${bill.discountPercentage}%` - : ''; + return bill.discountPercentage ? `${bill.discountPercentage}%` : ''; }; /** @@ -198,6 +216,18 @@ export class PurchaseInvoiceTransformer extends Transformer { }); }; + /** + * Retrieves the formatted adjustment amount in local currency. + * @param {IBill} bill + * @returns {string} + */ + protected adjustmentLocalFormatted = (bill): string => { + return formatNumber(bill.adjustmentLocal, { + currencyCode: this.context.organization.baseCurrency, + excerptZero: true, + }); + }; + /** * Retrieves the total formatted. * @param {IBill} bill diff --git a/packages/server/src/services/Purchases/VendorCredits/VendorCreditGLEntries.ts b/packages/server/src/services/Purchases/VendorCredits/VendorCreditGLEntries.ts index fe269cef5..0747a5845 100644 --- a/packages/server/src/services/Purchases/VendorCredits/VendorCreditGLEntries.ts +++ b/packages/server/src/services/Purchases/VendorCredits/VendorCreditGLEntries.ts @@ -56,7 +56,7 @@ export default class VendorCreditGLEntries { return { ...commonEntity, - debit: vendorCredit.localAmount, + debit: vendorCredit.totalLocal, accountId: APAccountId, contactId: vendorCredit.vendorId, accountNormal: AccountNormal.CREDIT, @@ -94,6 +94,52 @@ export default class VendorCreditGLEntries { } ); + /** + * Retrieves the vendor credit discount GL entry. + * @param {IVendorCredit} vendorCredit + * @param {number} discountAccountId + * @returns {ILedgerEntry} + */ + public getDiscountEntry = ( + vendorCredit: IVendorCredit, + purchaseDiscountAccountId: number + ) => { + const commonEntry = this.getVendorCreditGLCommonEntry(vendorCredit); + + return { + ...commonEntry, + debit: vendorCredit.discountAmountLocal, + accountId: purchaseDiscountAccountId, + accountNormal: AccountNormal.DEBIT, + index: 1, + indexGroup: 40, + }; + }; + + /** + * Retrieves the vendor credit adjustment GL entry. + * @param {IVendorCredit} vendorCredit + * @param {number} adjustmentAccountId + * @returns {ILedgerEntry} + */ + public getAdjustmentEntry = ( + vendorCredit: IVendorCredit, + otherExpensesAccountId: number + ) => { + const commonEntry = this.getVendorCreditGLCommonEntry(vendorCredit); + const adjustmentAmount = Math.abs(vendorCredit.adjustmentLocal); + + return { + ...commonEntry, + credit: vendorCredit.adjustmentLocal > 0 ? adjustmentAmount : 0, + debit: vendorCredit.adjustmentLocal < 0 ? adjustmentAmount : 0, + accountId: otherExpensesAccountId, + accountNormal: AccountNormal.DEBIT, + index: 1, + indexGroup: 40, + }; + }; + /** * Retrieve the vendor credit GL entries. * @param {IVendorCredit} vendorCredit - @@ -102,7 +148,9 @@ export default class VendorCreditGLEntries { */ public getVendorCreditGLEntries = ( vendorCredit: IVendorCredit, - payableAccountId: number + payableAccountId: number, + purchaseDiscountAccountId: number, + otherExpensesAccountId: number ): ILedgerEntry[] => { const payableEntry = this.getVendorCreditPayableGLEntry( vendorCredit, @@ -111,7 +159,15 @@ export default class VendorCreditGLEntries { const getItemEntry = this.getVendorCreditGLItemEntry(vendorCredit); const itemsEntries = vendorCredit.entries.map(getItemEntry); - return [payableEntry, ...itemsEntries]; + const discountEntry = this.getDiscountEntry( + vendorCredit, + purchaseDiscountAccountId + ); + const adjustmentEntry = this.getAdjustmentEntry( + vendorCredit, + otherExpensesAccountId + ); + return [payableEntry, discountEntry, adjustmentEntry, ...itemsEntries]; }; /** @@ -158,10 +214,17 @@ export default class VendorCreditGLEntries { {}, trx ); + const purchaseDiscountAccount = + await accountRepository.findOrCreatePurchaseDiscountAccount({}, trx); + + const otherExpensesAccount = + await accountRepository.findOrCreateOtherExpensesAccount({}, trx); // Saves the vendor credit GL entries. const ledgerEntries = this.getVendorCreditGLEntries( vendorCredit, - APAccount.id + APAccount.id, + purchaseDiscountAccount.id, + otherExpensesAccount.id ); const ledger = new Ledger(ledgerEntries); diff --git a/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts b/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts index b42c90ea0..61e4d6736 100644 --- a/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts +++ b/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts @@ -17,9 +17,15 @@ export class VendorCreditTransformer extends Transformer { 'formattedCreatedAt', 'formattedCreditsRemaining', 'formattedInvoicedAmount', + 'discountAmountFormatted', 'discountPercentageFormatted', + 'discountAmountLocalFormatted', + 'adjustmentFormatted', + 'adjustmentLocalFormatted', + + 'totalFormatted', 'entries', 'attachments', ]; @@ -86,6 +92,18 @@ export class VendorCreditTransformer extends Transformer { }); }; + /** + * Retrieves the formatted discount amount in local currency. + * @param {IVendorCredit} credit + * @returns {string} + */ + protected discountAmountLocalFormatted = (credit): string => { + return formatNumber(credit.discountAmountLocal, { + currencyCode: this.context.organization.baseCurrency, + excerptZero: true, + }); + }; + /** * Retrieves the formatted discount percentage. * @param {IVendorCredit} credit @@ -107,6 +125,18 @@ export class VendorCreditTransformer extends Transformer { }); }; + /** + * Retrieves the formatted adjustment amount in local currency. + * @param {IVendorCredit} credit + * @returns {string} + */ + protected adjustmentLocalFormatted = (credit): string => { + return formatNumber(credit.adjustmentLocal, { + currencyCode: this.context.organization.baseCurrency, + excerptZero: true, + }); + }; + /** * Retrieves the formatted invoiced amount. * @param credit @@ -118,6 +148,15 @@ export class VendorCreditTransformer extends Transformer { }); }; + /** + * Retrieves the formatted total. + * @param {IVendorCredit} credit + * @returns {string} + */ + protected totalFormatted = (credit) => { + return formatNumber(credit.total, { currencyCode: credit.currencyCode }); + }; + /** * Retrieves the entries of the bill. * @param {IVendorCredit} vendorCredit diff --git a/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts b/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts index 61c575f67..c5ca66946 100644 --- a/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts +++ b/packages/server/src/services/Sales/Invoices/InvoiceGLEntries.ts @@ -44,18 +44,31 @@ export class SaleInvoiceGLEntries { // Find or create the A/R account. const ARAccount = await accountRepository.findOrCreateAccountReceivable( - saleInvoice.currencyCode, {}, trx + saleInvoice.currencyCode, + {}, + trx ); // Find or create tax payable account. const taxPayableAccount = await accountRepository.findOrCreateTaxPayable( {}, trx ); + // Find or create the discount expense account. + const discountAccount = await accountRepository.findOrCreateDiscountAccount( + {}, + trx + ); + // Find or create the other charges account. + const otherChargesAccount = + await accountRepository.findOrCreateOtherChargesAccount({}, trx); + // Retrieves the ledger of the invoice. const ledger = this.getInvoiceGLedger( saleInvoice, ARAccount.id, - taxPayableAccount.id + taxPayableAccount.id, + discountAccount.id, + otherChargesAccount.id ); // Commits the ledger entries to the storage as UOW. await this.ledegrRepository.commit(tenantId, ledger, trx); @@ -107,12 +120,16 @@ export class SaleInvoiceGLEntries { public getInvoiceGLedger = ( saleInvoice: ISaleInvoice, ARAccountId: number, - taxPayableAccountId: number + taxPayableAccountId: number, + discountAccountId: number, + otherChargesAccountId: number ): ILedger => { const entries = this.getInvoiceGLEntries( saleInvoice, ARAccountId, - taxPayableAccountId + taxPayableAccountId, + discountAccountId, + otherChargesAccountId ); return new Ledger(entries); }; @@ -127,6 +144,7 @@ export class SaleInvoiceGLEntries { ): Partial => ({ credit: 0, debit: 0, + currencyCode: saleInvoice.currencyCode, exchangeRate: saleInvoice.exchangeRate, @@ -249,6 +267,50 @@ export class SaleInvoiceGLEntries { return nonZeroTaxEntries.map(transformTaxEntry); }; + /** + * Retrieves the invoice discount GL entry. + * @param {ISaleInvoice} saleInvoice + * @param {number} discountAccountId + * @returns {ILedgerEntry} + */ + private getInvoiceDiscountEntry = ( + saleInvoice: ISaleInvoice, + discountAccountId: number + ): ILedgerEntry => { + const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice); + + return { + ...commonEntry, + debit: saleInvoice.discountAmountLocal, + accountId: discountAccountId, + accountNormal: AccountNormal.CREDIT, + index: 1, + } as ILedgerEntry; + }; + + /** + * Retrieves the invoice adjustment GL entry. + * @param {ISaleInvoice} saleInvoice + * @param {number} adjustmentAccountId + * @returns {ILedgerEntry} + */ + private getAdjustmentEntry = ( + saleInvoice: ISaleInvoice, + otherChargesAccountId: number + ): ILedgerEntry => { + const commonEntry = this.getInvoiceGLCommonEntry(saleInvoice); + const adjustmentAmount = Math.abs(saleInvoice.adjustmentLocal); + + return { + ...commonEntry, + debit: saleInvoice.adjustmentLocal < 0 ? adjustmentAmount : 0, + credit: saleInvoice.adjustmentLocal > 0 ? adjustmentAmount : 0, + accountId: otherChargesAccountId, + accountNormal: AccountNormal.CREDIT, + index: 1, + }; + }; + /** * Retrieves the invoice GL entries. * @param {ISaleInvoice} saleInvoice @@ -258,7 +320,9 @@ export class SaleInvoiceGLEntries { public getInvoiceGLEntries = ( saleInvoice: ISaleInvoice, ARAccountId: number, - taxPayableAccountId: number + taxPayableAccountId: number, + discountAccountId: number, + otherChargesAccountId: number ): ILedgerEntry[] => { const receivableEntry = this.getInvoiceReceivableEntry( saleInvoice, @@ -271,6 +335,20 @@ export class SaleInvoiceGLEntries { saleInvoice, taxPayableAccountId ); - return [receivableEntry, ...creditEntries, ...taxEntries]; + const discountEntry = this.getInvoiceDiscountEntry( + saleInvoice, + discountAccountId + ); + const adjustmentEntry = this.getAdjustmentEntry( + saleInvoice, + otherChargesAccountId + ); + return [ + receivableEntry, + ...creditEntries, + ...taxEntries, + discountEntry, + adjustmentEntry, + ]; }; } diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptGLEntries.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptGLEntries.ts index d354141e9..8dc7bf803 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptGLEntries.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptGLEntries.ts @@ -31,13 +31,27 @@ export class SaleReceiptGLEntries { trx?: Knex.Transaction ): Promise => { const { SaleReceipt } = this.tenancy.models(tenantId); + const { accountRepository } = this.tenancy.repositories(tenantId); const saleReceipt = await SaleReceipt.query(trx) .findById(saleReceiptId) .withGraphFetched('entries.item'); + // Find or create the discount expense account. + const discountAccount = await accountRepository.findOrCreateDiscountAccount( + {}, + trx + ); + // Find or create the other charges account. + const otherChargesAccount = + await accountRepository.findOrCreateOtherChargesAccount({}, trx); + // Retrieve the income entries ledger. - const incomeLedger = this.getIncomeEntriesLedger(saleReceipt); + const incomeLedger = this.getIncomeEntriesLedger( + saleReceipt, + discountAccount.id, + otherChargesAccount.id + ); // Commits the ledger entries to the storage. await this.ledgerStorage.commit(tenantId, incomeLedger, trx); @@ -87,8 +101,16 @@ export class SaleReceiptGLEntries { * @param {ISaleReceipt} saleReceipt * @returns {Ledger} */ - private getIncomeEntriesLedger = (saleReceipt: ISaleReceipt): Ledger => { - const entries = this.getIncomeGLEntries(saleReceipt); + private getIncomeEntriesLedger = ( + saleReceipt: ISaleReceipt, + discountAccountId: number, + otherChargesAccountId: number + ): Ledger => { + const entries = this.getIncomeGLEntries( + saleReceipt, + discountAccountId, + otherChargesAccountId + ); return new Ledger(entries); }; @@ -161,24 +183,76 @@ export class SaleReceiptGLEntries { return { ...commonEntry, - debit: saleReceipt.localAmount, + debit: saleReceipt.totalLocal, accountId: saleReceipt.depositAccountId, index: 1, accountNormal: AccountNormal.DEBIT, }; }; + /** + * Retrieves the discount GL entry. + * @param {ISaleReceipt} saleReceipt + * @param {number} discountAccountId + * @returns {ILedgerEntry} + */ + private getDiscountEntry = ( + saleReceipt: ISaleReceipt, + discountAccountId: number + ): ILedgerEntry => { + const commonEntry = this.getIncomeGLCommonEntry(saleReceipt); + + return { + ...commonEntry, + debit: saleReceipt.discountAmountLocal, + accountId: discountAccountId, + index: 1, + accountNormal: AccountNormal.CREDIT, + }; + }; + + /** + * Retrieves the adjustment GL entry. + * @param {ISaleReceipt} saleReceipt + * @param {number} adjustmentAccountId + * @returns {ILedgerEntry} + */ + private getAdjustmentEntry = ( + saleReceipt: ISaleReceipt, + adjustmentAccountId: number + ): ILedgerEntry => { + const commonEntry = this.getIncomeGLCommonEntry(saleReceipt); + const adjustmentAmount = Math.abs(saleReceipt.adjustmentLocal); + + return { + ...commonEntry, + debit: saleReceipt.adjustmentLocal < 0 ? adjustmentAmount : 0, + credit: saleReceipt.adjustmentLocal > 0 ? adjustmentAmount : 0, + accountId: adjustmentAccountId, + accountNormal: AccountNormal.CREDIT, + index: 1, + }; + }; + /** * Retrieves the income GL entries. * @param {ISaleReceipt} saleReceipt - * @returns {ILedgerEntry[]} */ - private getIncomeGLEntries = (saleReceipt: ISaleReceipt): ILedgerEntry[] => { + private getIncomeGLEntries = ( + saleReceipt: ISaleReceipt, + discountAccountId: number, + otherChargesAccountId: number + ): ILedgerEntry[] => { const getItemEntry = this.getReceiptIncomeItemEntry(saleReceipt); const creditEntries = saleReceipt.entries.map(getItemEntry); const depositEntry = this.getReceiptDepositEntry(saleReceipt); - - return [depositEntry, ...creditEntries]; + const discountEntry = this.getDiscountEntry(saleReceipt, discountAccountId); + const adjustmentEntry = this.getAdjustmentEntry( + saleReceipt, + otherChargesAccountId + ); + return [depositEntry, ...creditEntries, discountEntry, adjustmentEntry]; }; } diff --git a/packages/server/src/services/Sales/Receipts/SaleReceiptTransformer.ts b/packages/server/src/services/Sales/Receipts/SaleReceiptTransformer.ts index adafab5d8..eb833b344 100644 --- a/packages/server/src/services/Sales/Receipts/SaleReceiptTransformer.ts +++ b/packages/server/src/services/Sales/Receipts/SaleReceiptTransformer.ts @@ -15,15 +15,22 @@ export class SaleReceiptTransformer extends Transformer { return [ 'discountAmountFormatted', 'discountPercentageFormatted', + 'discountAmountLocalFormatted', + 'subtotalFormatted', 'subtotalLocalFormatted', + 'totalFormatted', 'totalLocalFormatted', + 'adjustmentFormatted', + 'adjustmentLocalFormatted', + 'formattedAmount', 'formattedReceiptDate', 'formattedClosedAtDate', 'formattedCreatedAt', + 'paidFormatted', 'entries', 'attachments', ]; @@ -130,6 +137,18 @@ export class SaleReceiptTransformer extends Transformer { return receipt.discountPercentage ? `${receipt.discountPercentage}%` : ''; }; + /** + * Retrieves formatted paid amount. + * @param receipt + * @returns {string} + */ + protected paidFormatted = (receipt: ISaleReceipt): string => { + return formatNumber(receipt.paid, { + currencyCode: receipt.currencyCode, + excerptZero: true, + }); + }; + /** * Retrieves formatted adjustment amount. * @param receipt diff --git a/packages/webapp/src/containers/Drawers/BillDrawer/BillDetailTableFooter.tsx b/packages/webapp/src/containers/Drawers/BillDrawer/BillDetailTableFooter.tsx index 558d0f1ca..eb6846afb 100644 --- a/packages/webapp/src/containers/Drawers/BillDrawer/BillDetailTableFooter.tsx +++ b/packages/webapp/src/containers/Drawers/BillDrawer/BillDetailTableFooter.tsx @@ -42,7 +42,7 @@ export function BillDetailTableFooter() { textStyle={TotalLineTextStyle.Regular} /> )} - {bill.adjustment > 0 && ( + {bill.adjustment_formatted && ( )} - {creditNote.adjustment > 0 && ( + {creditNote.adjustment_formatted && ( + )} - {receipt.adjustment > 0 && ( + {receipt.adjustment_formatted && ( } - value={receipt.formatted_amount} - borderStyle={TotalLineBorderStyle.DoubleDark} + value={receipt.paid_formatted} + borderStyle={TotalLineBorderStyle.SingleDark} /> } diff --git a/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerFooter.tsx b/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerFooter.tsx index f7b56487d..17c9a4192 100644 --- a/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerFooter.tsx +++ b/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailDrawerFooter.tsx @@ -45,7 +45,7 @@ export default function VendorCreditDetailDrawerFooter() { )} } - value={vendorCredit.formatted_amount} + value={vendorCredit.total_formatted} borderStyle={TotalLineBorderStyle.DoubleDark} textStyle={TotalLineTextStyle.Bold} /> diff --git a/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailHeader.tsx b/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailHeader.tsx index 989b1b245..fac1f8494 100644 --- a/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailHeader.tsx +++ b/packages/webapp/src/containers/Drawers/VendorCreditDetailDrawer/VendorCreditDetailHeader.tsx @@ -5,7 +5,6 @@ import styled from 'styled-components'; import { defaultTo } from 'lodash'; import { - FormatDate, T, Row, Col, @@ -29,13 +28,14 @@ export default function VendorCreditDetailHeader() { - {vendorCredit.formatted_amount} + {vendorCredit.total_formatted} + diff --git a/packages/webapp/src/containers/Purchases/Bills/BillForm/utils.tsx b/packages/webapp/src/containers/Purchases/Bills/BillForm/utils.tsx index 35296368a..b570b132c 100644 --- a/packages/webapp/src/containers/Purchases/Bills/BillForm/utils.tsx +++ b/packages/webapp/src/containers/Purchases/Bills/BillForm/utils.tsx @@ -415,7 +415,7 @@ export const useBillTotal = () => { return R.compose( R.when(R.always(isExclusiveTax), R.add(totalTaxAmount)), R.subtract(R.__, discountAmount), - R.subtract(R.__, adjustmentAmount), + R.add(R.__, adjustmentAmount), )(subtotal); }; diff --git a/packages/webapp/src/containers/Purchases/CreditNotes/CreditNoteForm/utils.tsx b/packages/webapp/src/containers/Purchases/CreditNotes/CreditNoteForm/utils.tsx index e796d85d3..7724629be 100644 --- a/packages/webapp/src/containers/Purchases/CreditNotes/CreditNoteForm/utils.tsx +++ b/packages/webapp/src/containers/Purchases/CreditNotes/CreditNoteForm/utils.tsx @@ -259,7 +259,10 @@ export const useVendorCreditTotal = () => { const discountAmount = useVendorCreditDiscountAmount(); const adjustment = useVendorCreditAdjustment(); - return subtotal - discountAmount - adjustment; + return R.compose( + R.subtract(R.__, discountAmount), + R.add(R.__, adjustment), + )(subtotal); }; /** diff --git a/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/utils.tsx b/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/utils.tsx index 8a772f4ce..470cc3f5c 100644 --- a/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/utils.tsx +++ b/packages/webapp/src/containers/Sales/CreditNotes/CreditNoteForm/utils.tsx @@ -263,7 +263,10 @@ export const useCreditNoteTotal = () => { const discountAmount = useCreditNoteDiscountAmount(); const adjustmentAmount = useCreditNoteAdjustmentAmount(); - return subtotal - discountAmount - adjustmentAmount; + return R.compose( + R.subtract(R.__, discountAmount), + R.add(R.__, adjustmentAmount), + )(subtotal); }; /** diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/utils.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/utils.tsx index 3669bf868..1bca08342 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimateForm/utils.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimateForm/utils.tsx @@ -299,7 +299,10 @@ export const useEstimateTotal = () => { const discount = useEstimateDiscount(); const adjustment = useEstimateAdjustment(); - return subtotal - discount - adjustment; + return R.compose( + R.subtract(R.__, discount), + R.add(R.__, adjustment), + )(subtotal); }; /** diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/utils.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/utils.tsx index 93fb58841..c069fd903 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/utils.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceForm/utils.tsx @@ -455,7 +455,7 @@ export const useInvoiceTotal = () => { return R.compose( R.when(R.always(isExclusiveTax), R.add(totalTaxAmount)), R.subtract(R.__, discountAmount), - R.subtract(R.__, adjustmentAmount), + R.add(adjustmentAmount), )(subtotal); }; diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/utils.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/utils.tsx index c7365c680..bca99c9f6 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/utils.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptForm/utils.tsx @@ -284,7 +284,10 @@ export const useReceiptTotal = () => { const adjustmentAmount = useReceiptAdjustmentAmount(); const discountAmount = useReceiptDiscountAmount(); - return subtotal - discountAmount - adjustmentAmount; + return R.compose( + R.add(R.__, adjustmentAmount), + R.subtract(R.__, discountAmount), + )(subtotal); }; /**