diff --git a/packages/server/src/interfaces/Ledger.ts b/packages/server/src/interfaces/Ledger.ts index 49419e680..4e021718a 100644 --- a/packages/server/src/interfaces/Ledger.ts +++ b/packages/server/src/interfaces/Ledger.ts @@ -4,6 +4,8 @@ export interface ILedger { getEntries(): ILedgerEntry[]; + filter(cb: (entry: ILedgerEntry) => boolean): ILedger; + whereAccountId(accountId: number): ILedger; whereContactId(contactId: number): ILedger; whereFromDate(fromDate: Date | string): ILedger; diff --git a/packages/server/src/services/Accounting/LedgerContactStorage.ts b/packages/server/src/services/Accounting/LedgerContactStorage.ts index 06b5f9269..f8a484122 100644 --- a/packages/server/src/services/Accounting/LedgerContactStorage.ts +++ b/packages/server/src/services/Accounting/LedgerContactStorage.ts @@ -1,9 +1,14 @@ import { Service, Inject } from 'typedi'; import async from 'async'; import { Knex } from 'knex'; -import { ILedger, ISaleContactsBalanceQueuePayload } from '@/interfaces'; +import { + ILedger, + ILedgerEntry, + ISaleContactsBalanceQueuePayload, +} from '@/interfaces'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { TenantMetadata } from '@/system/models'; +import { ACCOUNT_TYPE } from '@/data/AccountTypes'; @Service() export class LedgerContactsBalanceStorage { @@ -49,6 +54,29 @@ export class LedgerContactsBalanceStorage { await this.saveContactBalance(tenantId, ledger, contactId, trx); }; + /** + * Filters AP/AR ledger entries. + * @param {number} tenantId + * @param {Knex.Transaction} trx + * @returns {Promise<(entry: ILedgerEntry) => boolean>} + */ + private filterARAPLedgerEntris = async ( + tenantId: number, + trx?: Knex.Transaction + ): Promise<(entry: ILedgerEntry) => boolean> => { + const { Account } = this.tenancy.models(tenantId); + + const ARAPAcounts = await Account.query(trx).whereIn('accountType', [ + ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE, + ACCOUNT_TYPE.ACCOUNTS_PAYABLE, + ]); + const ARAPAcountsIds = ARAPAcounts.map((a) => a.id); + + return (entry: ILedgerEntry) => { + return ARAPAcountsIds.indexOf(entry.accountId) !== -1; + }; + }; + /** * * @param {number} tenantId @@ -63,16 +91,24 @@ export class LedgerContactsBalanceStorage { trx?: Knex.Transaction ): Promise => { const { Contact } = this.tenancy.models(tenantId); - const contact = await Contact.query().findById(contactId); + const contact = await Contact.query(trx).findById(contactId); // Retrieves the given tenant metadata. const tenantMeta = await TenantMetadata.query().findOne({ tenantId }); - + // Detarmines whether the contact has foreign currency. const isForeignContact = contact.currencyCode !== tenantMeta.baseCurrency; // Filters the ledger base on the given contact id. - const contactLedger = ledger.whereContactId(contactId); + const filterARAPLedgerEntris = await this.filterARAPLedgerEntris( + tenantId, + trx + ); + const contactLedger = ledger + // Filter entries only that have contact id. + .whereContactId(contactId) + // Filter entries on AR/AP accounts. + .filter(filterARAPLedgerEntris); const closingBalance = isForeignContact ? contactLedger diff --git a/packages/server/src/services/Contacts/Customers/CRUD/CreateCustomer.ts b/packages/server/src/services/Contacts/Customers/CRUD/CreateCustomer.ts index 0465ecd5f..6969360ff 100644 --- a/packages/server/src/services/Contacts/Customers/CRUD/CreateCustomer.ts +++ b/packages/server/src/services/Contacts/Customers/CRUD/CreateCustomer.ts @@ -55,7 +55,7 @@ export class CreateCustomer { } as ICustomerEventCreatingPayload); // Creates a new contact as customer. - const customer = await Contact.query().insertAndFetch({ + const customer = await Contact.query(trx).insertAndFetch({ ...customerObj, }); // Triggers `onCustomerCreated` event. diff --git a/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx b/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx index 8e42a6630..5259a554e 100644 --- a/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx +++ b/packages/webapp/src/containers/Accounting/MakeJournal/utils.tsx @@ -28,6 +28,8 @@ const ERROR = { CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO', ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT', + COULD_NOT_ASSIGN_DIFFERENT_CURRENCY_TO_ACCOUNTS: + 'COULD_NOT_ASSIGN_DIFFERENT_CURRENCY_TO_ACCOUNTS', }; export const MIN_LINES_NUMBER = 1; @@ -161,6 +163,15 @@ export const transformErrors = (resErrors, { setErrors, errors }) => { intl.get('journal_number_is_already_used'), ); } + if ( + (error = getError(ERROR.COULD_NOT_ASSIGN_DIFFERENT_CURRENCY_TO_ACCOUNTS)) + ) { + toastMessages.push( + intl.get( + 'make_journal.errors.should_add_accounts_in_same_currency_or_base_currency', + ), + ); + } setErrors({ ...newErrors }); if (toastMessages.length > 0) { diff --git a/packages/webapp/src/containers/Customers/CustomersLanding/components.tsx b/packages/webapp/src/containers/Customers/CustomersLanding/components.tsx index 5fef5a9ff..c93bcb31f 100644 --- a/packages/webapp/src/containers/Customers/CustomersLanding/components.tsx +++ b/packages/webapp/src/containers/Customers/CustomersLanding/components.tsx @@ -38,7 +38,6 @@ export function ActionsMenu({ /> - } text={intl.get('edit_customer')} @@ -47,7 +46,7 @@ export function ActionsMenu({ } + icon={} text={intl.get('duplicate')} onClick={safeCallback(onDuplicate, original)} /> diff --git a/packages/webapp/src/containers/Dialogs/CustomerOpeningBalanceDialog/CustomerOpeningBalanceFormFloatingActions.tsx b/packages/webapp/src/containers/Dialogs/CustomerOpeningBalanceDialog/CustomerOpeningBalanceFormFloatingActions.tsx index adb94ae44..6c07f7d79 100644 --- a/packages/webapp/src/containers/Dialogs/CustomerOpeningBalanceDialog/CustomerOpeningBalanceFormFloatingActions.tsx +++ b/packages/webapp/src/containers/Dialogs/CustomerOpeningBalanceDialog/CustomerOpeningBalanceFormFloatingActions.tsx @@ -8,7 +8,6 @@ import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormPr import withDialogActions from '@/containers/Dialog/withDialogActions'; import { compose } from '@/utils'; - /** * Customer Opening balance floating actions. * @returns @@ -31,6 +30,9 @@ function CustomerOpeningBalanceFormFloatingActions({ return (
+ -
); diff --git a/packages/webapp/src/containers/Vendors/VendorsLanding/components.tsx b/packages/webapp/src/containers/Vendors/VendorsLanding/components.tsx index 719b75a8e..9cf89c65a 100644 --- a/packages/webapp/src/containers/Vendors/VendorsLanding/components.tsx +++ b/packages/webapp/src/containers/Vendors/VendorsLanding/components.tsx @@ -48,7 +48,7 @@ export function ActionsMenu({
} + icon={} text={intl.get('duplicate')} onClick={safeCallback(onDuplicate, original)} /> diff --git a/packages/webapp/src/lang/en/index.json b/packages/webapp/src/lang/en/index.json index 35867a51b..d8d490541 100644 --- a/packages/webapp/src/lang/en/index.json +++ b/packages/webapp/src/lang/en/index.json @@ -703,6 +703,7 @@ "email_is_already_used": "The email is already used.", "the_item_categories_has_been_deleted_successfully": "The item categories has been deleted successfully .", "receivable_accounts_should_assign_with_customers": "Receivable accounts should assign with customers.", + "make_journal.errors.should_add_accounts_in_same_currency_or_base_currency": "You can only add accounts that have the same selected currency or base currency.", "delivered": "Delivered", "save_and_deliver": "Save & Deliver", "deliver_and_new": "Deliver and new",