diff --git a/.all-contributorsrc b/.all-contributorsrc index a2e0d8553..fa713e515 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -150,6 +150,15 @@ "contributions": [ "bug" ] + }, + { + "login": "Champetaman", + "name": "Camilo Oviedo", + "avatar_url": "https://avatars.githubusercontent.com/u/64604272?v=4", + "profile": "https://www.camilooviedo.com/", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CHANGELOG.md b/CHANGELOG.md index d70e73457..9949feb54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ All notable changes to Bigcapital server-side will be in this file. +## [0.19.4] - 18-08-2024 + +* fix: Allow multi-lines to statements transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/594 +* feat: Add amount comparators to amount bank rule field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/595 +* fix: Transaction type and description do not show in general ledger. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/596 +* fix: Refresh accounts and account transactions. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/597 +* fix: Typo payments made by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/598 +* fix: Typo categories list by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/600 +* fix: Autofill the quick created customer/vendor by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/601 +* fix: Remove views tabs from receipts list by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/602 +* fix: Typo payment receive messages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/599 +* fix: Enhance Dropzone visual of accept and reject modes by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/603 +* fix: Matching bank transactions should create associate payment transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/606 +* fix: Change Dropzone title and subtitle by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/607 +* fix: Inconsistance page size of paginated data tables by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/604 +* fix: Database connection lost error by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/611 +* fix: Language typos by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/613 +* Fix: Correctly display Date, Published At, and Created At in ExpenseDrawerHeader by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/612 +* fix: Delete bank account with uncategorized transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/614 +* feat: activate/inactivate account from drawer details by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/615 + ## [v0.18.0] - 10-08-2024 * feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511 diff --git a/README.md b/README.md index 463957cf3..061f908b5 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Denis
Denis

🐛 Sachin Mittal
Sachin Mittal

🐛 + Camilo Oviedo
Camilo Oviedo

💻 diff --git a/packages/server/package.json b/packages/server/package.json index 07329cb60..817247e8c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -69,9 +69,8 @@ "is-my-json-valid": "^2.20.5", "js-money": "^0.6.3", "jsonwebtoken": "^8.5.1", - "knex": "^0.95.15", + "knex": "^3.1.0", "knex-cleaner": "^1.3.0", - "knex-db-manager": "^0.6.1", "libphonenumber-js": "^1.9.6", "lodash": "^4.17.15", "lru-cache": "^6.0.0", diff --git a/packages/server/src/api/controllers/Banking/BankAccountsController.ts b/packages/server/src/api/controllers/Banking/BankAccountsController.ts index 424c28857..e02aad096 100644 --- a/packages/server/src/api/controllers/Banking/BankAccountsController.ts +++ b/packages/server/src/api/controllers/Banking/BankAccountsController.ts @@ -1,9 +1,10 @@ import { Inject, Service } from 'typedi'; import { NextFunction, Request, Response, Router } from 'express'; +import { param, query } from 'express-validator'; import BaseController from '@/api/controllers/BaseController'; import { GetBankAccountSummary } from '@/services/Banking/BankAccounts/GetBankAccountSummary'; import { BankAccountsApplication } from '@/services/Banking/BankAccounts/BankAccountsApplication'; -import { param } from 'express-validator'; +import { GetPendingBankAccountTransactions } from '@/services/Cashflow/GetPendingBankAccountTransaction'; @Service() export class BankAccountsController extends BaseController { @@ -13,6 +14,9 @@ export class BankAccountsController extends BaseController { @Inject() private bankAccountsApp: BankAccountsApplication; + @Inject() + private getPendingTransactionsService: GetPendingBankAccountTransactions; + /** * Router constructor. */ @@ -20,6 +24,16 @@ export class BankAccountsController extends BaseController { const router = Router(); router.get('/:bankAccountId/meta', this.getBankAccountSummary.bind(this)); + router.get( + '/pending_transactions', + [ + query('account_id').optional().isNumeric().toInt(), + query('page').optional().isNumeric().toInt(), + query('page_size').optional().isNumeric().toInt(), + ], + this.validationResult, + this.getBankAccountsPendingTransactions.bind(this) + ); router.post( '/:bankAccountId/disconnect', this.disconnectBankAccount.bind(this) @@ -27,17 +41,13 @@ export class BankAccountsController extends BaseController { router.post('/:bankAccountId/update', this.refreshBankAccount.bind(this)); router.post( '/:bankAccountId/pause_feeds', - [ - param('bankAccountId').exists().isNumeric().toInt(), - ], + [param('bankAccountId').exists().isNumeric().toInt()], this.validationResult, this.pauseBankAccountFeeds.bind(this) ); router.post( '/:bankAccountId/resume_feeds', - [ - param('bankAccountId').exists().isNumeric().toInt(), - ], + [param('bankAccountId').exists().isNumeric().toInt()], this.validationResult, this.resumeBankAccountFeeds.bind(this) ); @@ -72,6 +82,32 @@ export class BankAccountsController extends BaseController { } } + /** + * Retrieves the bank account pending transactions. + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + */ + async getBankAccountsPendingTransactions( + req: Request, + res: Response, + next: NextFunction + ) { + const { tenantId } = req; + const query = this.matchedQueryData(req); + + try { + const data = + await this.getPendingTransactionsService.getPendingTransactions( + tenantId, + query + ); + return res.status(200).send(data); + } catch (error) { + next(error); + } + } + /** * Disonnect the given bank account. * @param {Request} req @@ -128,9 +164,9 @@ export class BankAccountsController extends BaseController { /** * Resumes the bank account feeds sync. - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next * @returns {Promise} */ async resumeBankAccountFeeds( @@ -155,9 +191,9 @@ export class BankAccountsController extends BaseController { /** * Pauses the bank account feeds sync. - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next * @returns {Promise} */ async pauseBankAccountFeeds( diff --git a/packages/server/src/api/controllers/Banking/BankingRulesController.ts b/packages/server/src/api/controllers/Banking/BankingRulesController.ts index e7608f56d..008b753d9 100644 --- a/packages/server/src/api/controllers/Banking/BankingRulesController.ts +++ b/packages/server/src/api/controllers/Banking/BankingRulesController.ts @@ -33,7 +33,16 @@ export class BankingRulesController extends BaseController { body('conditions.*.field').exists().isIn(['description', 'amount']), body('conditions.*.comparator') .exists() - .isIn(['equals', 'contains', 'not_contain']) + .isIn([ + 'equals', + 'equal', + 'contains', + 'not_contain', + 'bigger', + 'bigger_or_equal', + 'smaller', + 'smaller_or_equal', + ]) .default('contain') .trim(), body('conditions.*.value').exists().trim(), diff --git a/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts b/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts index fea9b84c9..0ddf5861a 100644 --- a/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts +++ b/packages/server/src/api/controllers/Cashflow/NewCashflowTransaction.ts @@ -1,5 +1,5 @@ import { Service, Inject } from 'typedi'; -import { ValidationChain, check, param, query } from 'express-validator'; +import { ValidationChain, body, check, param, query } from 'express-validator'; import { Router, Request, Response, NextFunction } from 'express'; import { omit } from 'lodash'; import BaseController from '../BaseController'; @@ -43,6 +43,16 @@ export default class NewCashflowTransactionController extends BaseController { this.asyncMiddleware(this.newCashflowTransaction), this.catchServiceErrors ); + router.post( + '/transactions/uncategorize/bulk', + [ + body('ids').isArray({ min: 1 }), + body('ids.*').exists().isNumeric().toInt(), + ], + this.validationResult, + this.uncategorizeBulkTransactions.bind(this), + this.catchServiceErrors + ); router.post( '/transactions/:id/uncategorize', this.revertCategorizedCashflowTransaction, @@ -184,6 +194,34 @@ export default class NewCashflowTransactionController extends BaseController { } }; + /** + * Uncategorize the given transactions in bulk. + * @param {Request<{}>} req + * @param {Response} res + * @param {NextFunction} next + * @returns {Promise} + */ + private uncategorizeBulkTransactions = async ( + req: Request<{}>, + res: Response, + next: NextFunction + ) => { + const { tenantId } = req; + const { ids: uncategorizedTransactionIds } = this.matchedBodyData(req); + + try { + await this.cashflowApplication.uncategorizeTransactions( + tenantId, + uncategorizedTransactionIds + ); + return res.status(200).send({ + message: 'The given transactions have been uncategorized successfully.', + }); + } catch (error) { + next(error); + } + }; + /** * Categorize the cashflow transaction. * @param {Request} req diff --git a/packages/server/src/api/controllers/Sales/PaymentReceives.ts b/packages/server/src/api/controllers/Sales/PaymentReceives.ts index b1a40dd7b..10cbb0c28 100644 --- a/packages/server/src/api/controllers/Sales/PaymentReceives.ts +++ b/packages/server/src/api/controllers/Sales/PaymentReceives.ts @@ -9,9 +9,9 @@ import { } from '@/interfaces'; import BaseController from '@/api/controllers/BaseController'; import asyncMiddleware from '@/api/middleware/asyncMiddleware'; -import PaymentReceivesPages from '@/services/Sales/PaymentReceives/PaymentReceivesPages'; +import PaymentsReceivedPages from '@/services/Sales/PaymentReceived/PaymentsReceivedPages'; +import { PaymentReceivesApplication } from '@/services/Sales/PaymentReceived/PaymentReceivedApplication'; import DynamicListingService from '@/services/DynamicListing/DynamicListService'; -import { PaymentReceivesApplication } from '@/services/Sales/PaymentReceives/PaymentReceivesApplication'; import CheckPolicies from '@/api/middleware/CheckPolicies'; import { ServiceError } from '@/exceptions'; import { ACCEPT_TYPE } from '@/interfaces/Http'; @@ -22,7 +22,7 @@ export default class PaymentReceivesController extends BaseController { private paymentReceiveApplication: PaymentReceivesApplication; @Inject() - private PaymentReceivesPages: PaymentReceivesPages; + private PaymentsReceivedPages: PaymentsReceivedPages; @Inject() private dynamicListService: DynamicListingService; @@ -229,7 +229,7 @@ export default class PaymentReceivesController extends BaseController { try { const storedPaymentReceive = - await this.paymentReceiveApplication.createPaymentReceive( + await this.paymentReceiveApplication.createPaymentReceived( tenantId, paymentReceive, user @@ -376,7 +376,7 @@ export default class PaymentReceivesController extends BaseController { const { customerId } = this.matchedQueryData(req); try { - const entries = await this.PaymentReceivesPages.getNewPageEntries( + const entries = await this.PaymentsReceivedPages.getNewPageEntries( tenantId, customerId ); @@ -404,7 +404,7 @@ export default class PaymentReceivesController extends BaseController { try { const { paymentReceive, entries } = - await this.PaymentReceivesPages.getPaymentReceiveEditPage( + await this.PaymentsReceivedPages.getPaymentReceiveEditPage( tenantId, paymentReceiveId, user diff --git a/packages/server/src/config/index.ts b/packages/server/src/config/index.ts index a7ef51e93..128da484f 100644 --- a/packages/server/src/config/index.ts +++ b/packages/server/src/config/index.ts @@ -32,7 +32,7 @@ module.exports = { */ tenant: { db_client: process.env.TENANT_DB_CLIENT || process.env.DB_CLIENT || 'mysql', - db_name_prefix: process.env.TENANT_DB_NAME_PERFIX, + db_name_prefix: process.env.TENANT_DB_NAME_PERFIX || 'bigcapital_tenant_', db_host: process.env.TENANT_DB_HOST || process.env.DB_HOST, db_user: process.env.TENANT_DB_USER || process.env.DB_USER, db_password: process.env.TENANT_DB_PASSWORD || process.env.DB_PASSWORD, diff --git a/packages/server/src/database/migrations/20240811121028_add_pending_column_to_uncategorized_transactions_table.js b/packages/server/src/database/migrations/20240811121028_add_pending_column_to_uncategorized_transactions_table.js new file mode 100644 index 000000000..73713b98b --- /dev/null +++ b/packages/server/src/database/migrations/20240811121028_add_pending_column_to_uncategorized_transactions_table.js @@ -0,0 +1,13 @@ +exports.up = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.boolean('pending').defaultTo(false); + table.string('pending_plaid_transaction_id').nullable(); + }); +}; + +exports.down = function (knex) { + return knex.schema.table('uncategorized_cashflow_transactions', (table) => { + table.dropColumn('pending'); + table.dropColumn('pending_plaid_transaction_id'); + }); +}; diff --git a/packages/server/src/interfaces/CashFlow.ts b/packages/server/src/interfaces/CashFlow.ts index 4fe1e90c1..0b3298929 100644 --- a/packages/server/src/interfaces/CashFlow.ts +++ b/packages/server/src/interfaces/CashFlow.ts @@ -268,6 +268,8 @@ export interface CreateUncategorizedTransactionDTO { description?: string; referenceNo?: string | null; plaidTransactionId?: string | null; + pending?: boolean; + pendingPlaidTransactionId?: string | null; batch?: string; } @@ -283,3 +285,17 @@ export interface IUncategorizedTransactionCreatedEventPayload { createUncategorizedTransactionDTO: CreateUncategorizedTransactionDTO; trx: Knex.Transaction; } + +export interface IPendingTransactionRemovingEventPayload { + tenantId: number; + uncategorizedTransactionId: number; + pendingTransaction: IUncategorizedCashflowTransaction; + trx?: Knex.Transaction; +} + +export interface IPendingTransactionRemovedEventPayload { + tenantId: number; + uncategorizedTransactionId: number; + pendingTransaction: IUncategorizedCashflowTransaction; + trx?: Knex.Transaction; +} diff --git a/packages/server/src/interfaces/GeneralLedgerSheet.ts b/packages/server/src/interfaces/GeneralLedgerSheet.ts index 12f1e0883..68835a59e 100644 --- a/packages/server/src/interfaces/GeneralLedgerSheet.ts +++ b/packages/server/src/interfaces/GeneralLedgerSheet.ts @@ -30,7 +30,7 @@ export interface IGeneralLedgerSheetAccountTransaction { currencyCode: string; note?: string; - transactionType?: string; + transactionTypeFormatted: string; transactionNumber: string; referenceId?: number; diff --git a/packages/server/src/interfaces/PaymentReceive.ts b/packages/server/src/interfaces/PaymentReceive.ts index 68ecb0eb6..b7c216abd 100644 --- a/packages/server/src/interfaces/PaymentReceive.ts +++ b/packages/server/src/interfaces/PaymentReceive.ts @@ -8,7 +8,7 @@ import { ILedgerEntry } from './Ledger'; import { ISaleInvoice } from './SaleInvoice'; import { AttachmentLinkDTO } from './Attachments'; -export interface IPaymentReceive { +export interface IPaymentReceived { id?: number; customerId: number; paymentDate: Date; @@ -19,14 +19,14 @@ export interface IPaymentReceive { depositAccountId: number; paymentReceiveNo: string; statement: string; - entries: IPaymentReceiveEntry[]; + entries: IPaymentReceivedEntry[]; userId: number; createdAt: Date; updatedAt: Date; localAmount?: number; branchId?: number; } -export interface IPaymentReceiveCreateDTO { +export interface IPaymentReceivedCreateDTO { customerId: number; paymentDate: Date; amount: number; @@ -35,13 +35,13 @@ export interface IPaymentReceiveCreateDTO { depositAccountId: number; paymentReceiveNo?: string; statement: string; - entries: IPaymentReceiveEntryDTO[]; + entries: IPaymentReceivedEntryDTO[]; branchId?: number; attachments?: AttachmentLinkDTO[]; } -export interface IPaymentReceiveEditDTO { +export interface IPaymentReceivedEditDTO { customerId: number; paymentDate: Date; amount: number; @@ -50,12 +50,12 @@ export interface IPaymentReceiveEditDTO { depositAccountId: number; paymentReceiveNo?: string; statement: string; - entries: IPaymentReceiveEntryDTO[]; + entries: IPaymentReceivedEntryDTO[]; branchId?: number; attachments?: AttachmentLinkDTO[]; } -export interface IPaymentReceiveEntry { +export interface IPaymentReceivedEntry { id?: number; paymentReceiveId: number; invoiceId: number; @@ -64,15 +64,15 @@ export interface IPaymentReceiveEntry { invoice?: ISaleInvoice; } -export interface IPaymentReceiveEntryDTO { +export interface IPaymentReceivedEntryDTO { id?: number; index: number; - paymentReceiveId: number; + paymentReceiveId?: number; invoiceId: number; paymentAmount: number; } -export interface IPaymentReceivesFilter extends IDynamicListFilterDTO { +export interface IPaymentsReceivedFilter extends IDynamicListFilterDTO { stringifiedFilterRoles?: string; } @@ -88,65 +88,65 @@ export interface IPaymentReceivePageEntry { date: Date | string; } -export interface IPaymentReceiveEditPage { - paymentReceive: IPaymentReceive; +export interface IPaymentReceivedEditPage { + paymentReceive: IPaymentReceived; entries: IPaymentReceivePageEntry[]; } -export interface IPaymentsReceiveService { +export interface IPaymentsReceivedService { validateCustomerHasNoPayments( tenantId: number, customerId: number ): Promise; } -export interface IPaymentReceiveSmsDetails { +export interface IPaymentReceivedSmsDetails { customerName: string; customerPhoneNumber: string; smsMessage: string; } -export interface IPaymentReceiveCreatingPayload { +export interface IPaymentReceivedCreatingPayload { tenantId: number; - paymentReceiveDTO: IPaymentReceiveCreateDTO; + paymentReceiveDTO: IPaymentReceivedCreateDTO; trx: Knex.Transaction; } -export interface IPaymentReceiveCreatedPayload { +export interface IPaymentReceivedCreatedPayload { tenantId: number; - paymentReceive: IPaymentReceive; + paymentReceive: IPaymentReceived; paymentReceiveId: number; authorizedUser: ISystemUser; - paymentReceiveDTO: IPaymentReceiveCreateDTO; + paymentReceiveDTO: IPaymentReceivedCreateDTO; trx: Knex.Transaction; } -export interface IPaymentReceiveEditedPayload { +export interface IPaymentReceivedEditedPayload { tenantId: number; paymentReceiveId: number; - paymentReceive: IPaymentReceive; - oldPaymentReceive: IPaymentReceive; - paymentReceiveDTO: IPaymentReceiveEditDTO; + paymentReceive: IPaymentReceived; + oldPaymentReceive: IPaymentReceived; + paymentReceiveDTO: IPaymentReceivedEditDTO; authorizedUser: ISystemUser; trx: Knex.Transaction; } -export interface IPaymentReceiveEditingPayload { +export interface IPaymentReceivedEditingPayload { tenantId: number; - oldPaymentReceive: IPaymentReceive; - paymentReceiveDTO: IPaymentReceiveEditDTO; + oldPaymentReceive: IPaymentReceived; + paymentReceiveDTO: IPaymentReceivedEditDTO; trx: Knex.Transaction; } -export interface IPaymentReceiveDeletingPayload { +export interface IPaymentReceivedDeletingPayload { tenantId: number; - oldPaymentReceive: IPaymentReceive; + oldPaymentReceive: IPaymentReceived; trx: Knex.Transaction; } -export interface IPaymentReceiveDeletedPayload { +export interface IPaymentReceivedDeletedPayload { tenantId: number; paymentReceiveId: number; - oldPaymentReceive: IPaymentReceive; + oldPaymentReceive: IPaymentReceived; authorizedUser: ISystemUser; trx: Knex.Transaction; } diff --git a/packages/server/src/interfaces/Tenancy.ts b/packages/server/src/interfaces/Tenancy.ts index 423812b55..3c8ae2f71 100644 --- a/packages/server/src/interfaces/Tenancy.ts +++ b/packages/server/src/interfaces/Tenancy.ts @@ -51,5 +51,4 @@ export interface ISystemService { cache(); repositories(); knex(); - dbManager(); } \ No newline at end of file diff --git a/packages/server/src/jobs/ComputeItemCost.ts b/packages/server/src/jobs/ComputeItemCost.ts index 07479258d..af6a19cbe 100644 --- a/packages/server/src/jobs/ComputeItemCost.ts +++ b/packages/server/src/jobs/ComputeItemCost.ts @@ -21,7 +21,7 @@ export default class ComputeItemCostJob { agenda.define( 'compute-item-cost', - { priority: 'high', concurrency: 1 }, + { priority: 'high', concurrency: 20 }, this.handler.bind(this) ); this.agenda.on('start:compute-item-cost', this.onJobStart.bind(this)); diff --git a/packages/server/src/jobs/OrganizationSetup.ts b/packages/server/src/jobs/OrganizationSetup.ts index 1945b6e2b..0d94d69e7 100644 --- a/packages/server/src/jobs/OrganizationSetup.ts +++ b/packages/server/src/jobs/OrganizationSetup.ts @@ -8,7 +8,7 @@ export default class OrganizationSetupJob { constructor(agenda) { agenda.define( 'organization-setup', - { priority: 'high', concurrency: 1 }, + { priority: 'high', concurrency: 20 }, this.handler ); } diff --git a/packages/server/src/jobs/WriteInvoicesJEntries.ts b/packages/server/src/jobs/WriteInvoicesJEntries.ts index d7a57d63a..738c47eee 100644 --- a/packages/server/src/jobs/WriteInvoicesJEntries.ts +++ b/packages/server/src/jobs/WriteInvoicesJEntries.ts @@ -15,7 +15,7 @@ export default class WriteInvoicesJournalEntries { agenda.define( eventName, - { priority: 'normal', concurrency: 1 }, + { priority: 'normal', concurrency: 20 }, this.handler.bind(this) ); agenda.on(`complete:${eventName}`, this.onJobCompleted.bind(this)); diff --git a/packages/server/src/lib/Transformer/Transformer.ts b/packages/server/src/lib/Transformer/Transformer.ts index 20efb4415..23613f748 100644 --- a/packages/server/src/lib/Transformer/Transformer.ts +++ b/packages/server/src/lib/Transformer/Transformer.ts @@ -164,6 +164,10 @@ export class Transformer { return date ? moment(date).format(this.dateFormat) : ''; } + protected formatDateFromNow(date){ + return date ? moment(date).fromNow(true) : ''; + } + /** * * @param number diff --git a/packages/server/src/loaders/dbManager.ts b/packages/server/src/loaders/dbManager.ts deleted file mode 100644 index 6aaf31ed2..000000000 --- a/packages/server/src/loaders/dbManager.ts +++ /dev/null @@ -1,7 +0,0 @@ -import knexManager from 'knex-db-manager'; -import { systemKnexConfig, systemDbManager } from 'config/knexConfig'; - -export default () => knexManager.databaseManagerFactory({ - knex: systemKnexConfig, - dbManager: systemDbManager, -}); \ No newline at end of file diff --git a/packages/server/src/loaders/dependencyInjector.ts b/packages/server/src/loaders/dependencyInjector.ts index 2c42690dc..7befb88c5 100644 --- a/packages/server/src/loaders/dependencyInjector.ts +++ b/packages/server/src/loaders/dependencyInjector.ts @@ -3,7 +3,6 @@ import LoggerInstance from '@/loaders/logger'; import agendaFactory from '@/loaders/agenda'; import SmsClientLoader from '@/loaders/smsClient'; import mailInstance from '@/loaders/mail'; -import dbManagerFactory from '@/loaders/dbManager'; import i18n from '@/loaders/i18n'; import repositoriesLoader from '@/loaders/systemRepositories'; import Cache from '@/services/Cache'; @@ -16,7 +15,6 @@ export default ({ mongoConnection, knex }) => { try { const agendaInstance = agendaFactory({ mongoConnection }); const smsClientInstance = SmsClientLoader(config.easySMSGateway.api_key); - const dbManager = dbManagerFactory(knex); const cacheInstance = new Cache(); Container.set('logger', LoggerInstance); @@ -24,7 +22,6 @@ export default ({ mongoConnection, knex }) => { Container.set('SMSClient', smsClientInstance); Container.set('mail', mailInstance); - Container.set('dbManager', dbManager); LoggerInstance.info( '[DI] Database manager has been injected into container.' ); diff --git a/packages/server/src/loaders/eventEmitter.ts b/packages/server/src/loaders/eventEmitter.ts index f303b4527..5a5b02d03 100644 --- a/packages/server/src/loaders/eventEmitter.ts +++ b/packages/server/src/loaders/eventEmitter.ts @@ -115,6 +115,7 @@ import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/E import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize'; import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted'; import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber'; +import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting'; export default () => { return new EventPublisher(); @@ -277,6 +278,7 @@ export const susbcribers = () => { // Plaid RecognizeSyncedBankTranasctions, DisconnectPlaidItemOnAccountDeleted, + DeleteUncategorizedTransactionsOnAccountDeleting, // Loops LoopsEventsSubscriber diff --git a/packages/server/src/loaders/events.ts b/packages/server/src/loaders/events.ts index f402a6087..9591e6b90 100644 --- a/packages/server/src/loaders/events.ts +++ b/packages/server/src/loaders/events.ts @@ -34,4 +34,4 @@ // import 'services/Sales/SaleInvoiceWriteoffSubscriber'; // import 'subscribers/SaleInvoices/SendSmsNotificationToCustomer'; // import 'subscribers/SaleReceipt/SendNotificationToCustomer'; -// import 'services/Sales/PaymentReceives/PaymentReceiveSmsSubscriber'; \ No newline at end of file +// import 'services/Sales/PaymentReceived/PaymentReceiveSmsSubscriber'; \ No newline at end of file diff --git a/packages/server/src/loaders/jobs.ts b/packages/server/src/loaders/jobs.ts index d3a4dbc1a..3ca6ec25f 100644 --- a/packages/server/src/loaders/jobs.ts +++ b/packages/server/src/loaders/jobs.ts @@ -9,7 +9,7 @@ import { SendSaleInvoiceMailJob } from '@/services/Sales/Invoices/SendSaleInvoic import { SendSaleInvoiceReminderMailJob } from '@/services/Sales/Invoices/SendSaleInvoiceMailReminderJob'; import { SendSaleEstimateMailJob } from '@/services/Sales/Estimates/SendSaleEstimateMailJob'; import { SaleReceiptMailNotificationJob } from '@/services/Sales/Receipts/SaleReceiptMailNotificationJob'; -import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob'; +import { PaymentReceivedMailNotificationJob } from '@/services/Sales/PaymentReceived/PaymentReceivedMailNotificationJob'; import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob'; import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob'; import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob'; @@ -28,7 +28,7 @@ export default ({ agenda }: { agenda: Agenda }) => { new SendSaleInvoiceReminderMailJob(agenda); new SendSaleEstimateMailJob(agenda); new SaleReceiptMailNotificationJob(agenda); - new PaymentReceiveMailNotificationJob(agenda); + new PaymentReceivedMailNotificationJob(agenda); new PlaidFetchTransactionsJob(agenda); new ImportDeleteExpiredFilesJobs(agenda); new SendVerifyMailJob(agenda); diff --git a/packages/server/src/models/BillPayment.ts b/packages/server/src/models/BillPayment.ts index d3f0dfe21..ab4b41bec 100644 --- a/packages/server/src/models/BillPayment.ts +++ b/packages/server/src/models/BillPayment.ts @@ -3,7 +3,7 @@ import TenantModel from 'models/TenantModel'; import ModelSetting from './ModelSetting'; import BillPaymentSettings from './BillPayment.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; -import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceives/constants'; +import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceived/constants'; import ModelSearchable from './ModelSearchable'; export default class BillPayment extends mixin(TenantModel, [ diff --git a/packages/server/src/models/Item.Settings.ts b/packages/server/src/models/Item.Settings.ts index af8968366..2c88be0d5 100644 --- a/packages/server/src/models/Item.Settings.ts +++ b/packages/server/src/models/Item.Settings.ts @@ -257,25 +257,25 @@ export default { name: 'item.field.sell_price', fieldType: 'number', }, - cost_price: { + costPrice: { name: 'item.field.cost_price', fieldType: 'number', }, - costAccount: { + costAccountId: { name: 'item.field.cost_account', fieldType: 'relation', relationModel: 'Account', relationImportMatch: ['name', 'code'], importHint: 'Matches the account name or code.', }, - sellAccount: { + sellAccountId: { name: 'item.field.sell_account', fieldType: 'relation', relationModel: 'Account', relationImportMatch: ['name', 'code'], importHint: 'Matches the account name or code.', }, - inventoryAccount: { + inventoryAccountId: { name: 'item.field.inventory_account', fieldType: 'relation', relationModel: 'Account', diff --git a/packages/server/src/models/Pagination.ts b/packages/server/src/models/Pagination.ts index 7d1b89921..5d308e90e 100644 --- a/packages/server/src/models/Pagination.ts +++ b/packages/server/src/models/Pagination.ts @@ -1,4 +1,5 @@ import { Model } from 'objection'; +import { castArray, omit, pick } from 'lodash'; import { isEmpty } from 'lodash'; import { ServiceError } from '@/exceptions'; @@ -16,7 +17,15 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { }); } - queryAndThrowIfHasRelations = ({ type, message }) => { + queryAndThrowIfHasRelations = ({ + type, + message, + excludeRelations = [], + includedRelations = [], + }) => { + const _excludeRelations = castArray(excludeRelations); + const _includedRelations = castArray(includedRelations); + const model = this.modelClass(); const modelRelations = Object.keys(model.relationMappings).filter( (relation) => @@ -25,9 +34,20 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { ) !== -1 ); const relations = model.secureDeleteRelations || modelRelations; + const filteredByIncluded = relations.filter((r) => + _includedRelations.includes(r) + ); + const filteredByExcluded = relations.filter( + (r) => !excludeRelations.includes(r) + ); + const filteredRelations = !isEmpty(_includedRelations) + ? filteredByIncluded + : !isEmpty(_excludeRelations) + ? filteredByExcluded + : relations; this.runAfter((model, query) => { - const nonEmptyRelations = relations.filter( + const nonEmptyRelations = filteredRelations.filter( (relation) => !isEmpty(model[relation]) ); if (nonEmptyRelations.length > 0) { @@ -36,7 +56,7 @@ export default class PaginationQueryBuilder extends Model.QueryBuilder { return model; }); return this.onBuild((query) => { - relations.forEach((relation) => { + filteredRelations.forEach((relation) => { query.withGraphFetched(`${relation}(selectId)`).modifiers({ selectId(builder) { builder.select('id'); diff --git a/packages/server/src/models/PaymentReceive.ts b/packages/server/src/models/PaymentReceive.ts index b9c2b87bf..0fc013962 100644 --- a/packages/server/src/models/PaymentReceive.ts +++ b/packages/server/src/models/PaymentReceive.ts @@ -3,7 +3,7 @@ import TenantModel from 'models/TenantModel'; import ModelSetting from './ModelSetting'; import PaymentReceiveSettings from './PaymentReceive.Settings'; import CustomViewBaseModel from './CustomViewBaseModel'; -import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceives/constants'; +import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceived/constants'; import ModelSearchable from './ModelSearchable'; export default class PaymentReceive extends mixin(TenantModel, [ diff --git a/packages/server/src/models/TaxRate.settings.ts b/packages/server/src/models/TaxRate.settings.ts new file mode 100644 index 000000000..a25ce2940 --- /dev/null +++ b/packages/server/src/models/TaxRate.settings.ts @@ -0,0 +1,69 @@ +export default { + defaultSort: { + sortOrder: 'DESC', + sortField: 'created_at', + }, + exportable: true, + importable: true, + print: { + pageTitle: 'Tax Rates', + }, + columns: { + name: { + name: 'Tax Rate Name', + type: 'text', + accessor: 'name', + }, + code: { + name: 'Code', + type: 'text', + accessor: 'code', + }, + rate: { + name: 'Rate', + type: 'text', + }, + description: { + name: 'Description', + type: 'text', + }, + isNonRecoverable: { + name: 'Is Non Recoverable', + type: 'boolean', + }, + active: { + name: 'Active', + type: 'boolean', + }, + }, + field: {}, + fields2: { + name: { + name: 'Tax name', + fieldType: 'name', + required: true, + }, + code: { + name: 'Code', + fieldType: 'code', + required: true, + }, + rate: { + name: 'Rate', + fieldType: 'number', + required: true, + }, + description: { + name: 'Description', + fieldType: 'text', + }, + isNonRecoverable: { + name: 'Is Non Recoverable', + fieldType: 'boolean', + }, + active: { + name: 'Active', + fieldType: 'boolean', + }, + }, +}; diff --git a/packages/server/src/models/TaxRate.ts b/packages/server/src/models/TaxRate.ts index e294b897a..13863e233 100644 --- a/packages/server/src/models/TaxRate.ts +++ b/packages/server/src/models/TaxRate.ts @@ -2,8 +2,13 @@ import { mixin, Model, raw } from 'objection'; import TenantModel from 'models/TenantModel'; import ModelSearchable from './ModelSearchable'; import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder'; +import TaxRateMeta from './TaxRate.settings'; +import ModelSetting from './ModelSetting'; -export default class TaxRate extends mixin(TenantModel, [ModelSearchable]) { +export default class TaxRate extends mixin(TenantModel, [ + ModelSetting, + ModelSearchable, +]) { /** * Table name */ @@ -25,6 +30,13 @@ export default class TaxRate extends mixin(TenantModel, [ModelSearchable]) { return ['createdAt', 'updatedAt']; } + /** + * Retrieves the tax rate meta. + */ + static get meta() { + return TaxRateMeta; + } + /** * Virtual attributes. */ diff --git a/packages/server/src/models/UncategorizedCashflowTransaction.ts b/packages/server/src/models/UncategorizedCashflowTransaction.ts index 1040fe0ca..e1a98b0c0 100644 --- a/packages/server/src/models/UncategorizedCashflowTransaction.ts +++ b/packages/server/src/models/UncategorizedCashflowTransaction.ts @@ -1,9 +1,8 @@ /* eslint-disable global-require */ import moment from 'moment'; -import { Model, ModelOptions, QueryContext, mixin } from 'objection'; +import { Model, mixin } from 'objection'; import TenantModel from 'models/TenantModel'; import ModelSettings from './ModelSetting'; -import Account from './Account'; import UncategorizedCashflowTransactionMeta from './UncategorizedCashflowTransaction.meta'; export default class UncategorizedCashflowTransaction extends mixin( @@ -21,6 +20,7 @@ export default class UncategorizedCashflowTransaction extends mixin( plaidTransactionId!: string; recognizedTransactionId!: number; excludedAt: Date; + pending: boolean; /** * Table name. @@ -47,6 +47,7 @@ export default class UncategorizedCashflowTransaction extends mixin( 'isWithdrawalTransaction', 'isRecognized', 'isExcluded', + 'isPending', ]; } @@ -99,6 +100,14 @@ export default class UncategorizedCashflowTransaction extends mixin( return !!this.excludedAt; } + /** + * Detarmines whether the transaction is pending. + * @returns {boolean} + */ + public get isPending(): boolean { + return !!this.pending; + } + /** * Model modifiers. */ diff --git a/packages/server/src/services/Accounting/Ledger.ts b/packages/server/src/services/Accounting/Ledger.ts index 3926ebe8f..155a61e10 100644 --- a/packages/server/src/services/Accounting/Ledger.ts +++ b/packages/server/src/services/Accounting/Ledger.ts @@ -249,6 +249,7 @@ export default class Ledger implements ILedger { transactionId: entry.referenceId, transactionType: entry.referenceType, + transactionSubType: entry.transactionType, transactionNumber: entry.transactionNumber, referenceNumber: entry.referenceNumber, @@ -262,6 +263,8 @@ export default class Ledger implements ILedger { taxRateId: entry.taxRateId, taxRate: entry.taxRate, + + note: entry.note, }; } diff --git a/packages/server/src/services/Accounts/AccountsApplication.ts b/packages/server/src/services/Accounts/AccountsApplication.ts index b90eb37e9..8a06b410f 100644 --- a/packages/server/src/services/Accounts/AccountsApplication.ts +++ b/packages/server/src/services/Accounts/AccountsApplication.ts @@ -43,8 +43,8 @@ export class AccountsApplication { /** * Creates a new account. - * @param {number} tenantId - * @param {IAccountCreateDTO} accountDTO + * @param {number} tenantId + * @param {IAccountCreateDTO} accountDTO * @returns {Promise} */ public createAccount = ( @@ -108,8 +108,8 @@ export class AccountsApplication { /** * Retrieves the account details. - * @param {number} tenantId - * @param {number} accountId + * @param {number} tenantId + * @param {number} accountId * @returns {Promise} */ public getAccount = (tenantId: number, accountId: number) => { diff --git a/packages/server/src/services/Accounts/DeleteAccount.ts b/packages/server/src/services/Accounts/DeleteAccount.ts index d8d499c58..632c78f62 100644 --- a/packages/server/src/services/Accounts/DeleteAccount.ts +++ b/packages/server/src/services/Accounts/DeleteAccount.ts @@ -73,6 +73,7 @@ export class DeleteAccount { .throwIfNotFound() .queryAndThrowIfHasRelations({ type: ERRORS.ACCOUNT_HAS_ASSOCIATED_TRANSACTIONS, + excludeRelations: ['uncategorizedTransactions', 'plaidItem'] }); // Authorize before delete account. await this.authorize(tenantId, accountId, oldAccount); diff --git a/packages/server/src/services/Attachments/events/AttachmentsOnPaymentsReceived.ts b/packages/server/src/services/Attachments/events/AttachmentsOnPaymentsReceived.ts index f4e876c6a..311fbb8ea 100644 --- a/packages/server/src/services/Attachments/events/AttachmentsOnPaymentsReceived.ts +++ b/packages/server/src/services/Attachments/events/AttachmentsOnPaymentsReceived.ts @@ -1,10 +1,10 @@ import { Inject, Service } from 'typedi'; import { isEmpty } from 'lodash'; import { - IPaymentReceiveCreatedPayload, - IPaymentReceiveCreatingPayload, - IPaymentReceiveDeletingPayload, - IPaymentReceiveEditedPayload, + IPaymentReceivedCreatedPayload, + IPaymentReceivedCreatingPayload, + IPaymentReceivedDeletingPayload, + IPaymentReceivedEditedPayload, } from '@/interfaces'; import events from '@/subscribers/events'; import { LinkAttachment } from '../LinkAttachment'; @@ -50,13 +50,13 @@ export class AttachmentsOnPaymentsReceived { /** * Validates the attachment keys on creating payment. - * @param {IPaymentReceiveCreatingPayload} + * @param {IPaymentReceivedCreatingPayload} * @returns {Promise} */ private async validateAttachmentsOnPaymentCreate({ paymentReceiveDTO, tenantId, - }: IPaymentReceiveCreatingPayload): Promise { + }: IPaymentReceivedCreatingPayload): Promise { if (isEmpty(paymentReceiveDTO.attachments)) { return; } @@ -67,7 +67,7 @@ export class AttachmentsOnPaymentsReceived { /** * Handles linking the attachments of the created payment. - * @param {IPaymentReceiveCreatedPayload} + * @param {IPaymentReceivedCreatedPayload} * @returns {Promise} */ private async handleAttachmentsOnPaymentCreated({ @@ -75,7 +75,7 @@ export class AttachmentsOnPaymentsReceived { paymentReceiveDTO, paymentReceive, trx, - }: IPaymentReceiveCreatedPayload): Promise { + }: IPaymentReceivedCreatedPayload): Promise { if (isEmpty(paymentReceiveDTO.attachments)) return; const keys = paymentReceiveDTO.attachments?.map( @@ -92,14 +92,14 @@ export class AttachmentsOnPaymentsReceived { /** * Handles unlinking all the unpresented keys of the edited payment. - * @param {IPaymentReceiveEditedPayload} + * @param {IPaymentReceivedEditedPayload} */ private async handleUnlinkUnpresentedKeysOnPaymentEdited({ tenantId, paymentReceiveDTO, oldPaymentReceive, trx, - }: IPaymentReceiveEditedPayload) { + }: IPaymentReceivedEditedPayload) { const keys = paymentReceiveDTO.attachments?.map( (attachment) => attachment.key ); @@ -114,7 +114,7 @@ export class AttachmentsOnPaymentsReceived { /** * Handles linking all the presented keys of the edited payment. - * @param {IPaymentReceiveEditedPayload} + * @param {IPaymentReceivedEditedPayload} * @returns {Promise} */ private async handleLinkPresentedKeysOnPaymentEdited({ @@ -122,7 +122,7 @@ export class AttachmentsOnPaymentsReceived { paymentReceiveDTO, oldPaymentReceive, trx, - }: IPaymentReceiveEditedPayload) { + }: IPaymentReceivedEditedPayload) { if (isEmpty(paymentReceiveDTO.attachments)) return; const keys = paymentReceiveDTO.attachments?.map( @@ -146,7 +146,7 @@ export class AttachmentsOnPaymentsReceived { tenantId, oldPaymentReceive, trx, - }: IPaymentReceiveDeletingPayload) { + }: IPaymentReceivedDeletingPayload) { await this.unlinkAttachmentService.unlinkAllModelKeys( tenantId, 'PaymentReceive', diff --git a/packages/server/src/services/Banking/BankAccounts/GetBankAccountSummary.ts b/packages/server/src/services/Banking/BankAccounts/GetBankAccountSummary.ts index 29c68abb6..4d7b7885a 100644 --- a/packages/server/src/services/Banking/BankAccounts/GetBankAccountSummary.ts +++ b/packages/server/src/services/Banking/BankAccounts/GetBankAccountSummary.ts @@ -52,6 +52,9 @@ export class GetBankAccountSummary { q.withGraphJoined('matchedBankTransactions'); q.whereNull('matchedBankTransactions.id'); + // Exclude the pending transactions. + q.modify('notPending'); + // Count the results. q.count('uncategorized_cashflow_transactions.id as total'); q.first(); @@ -65,16 +68,32 @@ export class GetBankAccountSummary { q.withGraphJoined('recognizedTransaction'); q.whereNotNull('recognizedTransaction.id'); + // Exclude the pending transactions. + q.modify('notPending'); + // Count the results. q.count('uncategorized_cashflow_transactions.id as total'); q.first(); }); - + // Retrieves excluded transactions count. const excludedTransactionsCount = await UncategorizedCashflowTransaction.query().onBuild((q) => { q.where('accountId', bankAccountId); q.modify('excluded'); + // Exclude the pending transactions. + q.modify('notPending'); + + // Count the results. + q.count('uncategorized_cashflow_transactions.id as total'); + q.first(); + }); + // Retrieves the pending transactions count. + const pendingTransactionsCount = + await UncategorizedCashflowTransaction.query().onBuild((q) => { + q.where('accountId', bankAccountId); + q.modify('pending'); + // Count the results. q.count('uncategorized_cashflow_transactions.id as total'); q.first(); @@ -83,14 +102,15 @@ export class GetBankAccountSummary { const totalUncategorizedTransactions = uncategorizedTranasctionsCount?.total || 0; const totalRecognizedTransactions = recognizedTransactionsCount?.total || 0; - const totalExcludedTransactions = excludedTransactionsCount?.total || 0; + const totalPendingTransactions = pendingTransactionsCount?.total || 0; return { name: bankAccount.name, totalUncategorizedTransactions, totalRecognizedTransactions, totalExcludedTransactions, + totalPendingTransactions, }; } } diff --git a/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts b/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts new file mode 100644 index 000000000..f2d9202ee --- /dev/null +++ b/packages/server/src/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting.ts @@ -0,0 +1,78 @@ +import { Inject, Service } from 'typedi'; +import { initialize } from 'objection'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import events from '@/subscribers/events'; +import { IAccountEventDeletePayload } from '@/interfaces'; +import { DeleteBankRulesService } from '../../Rules/DeleteBankRules'; +import { RevertRecognizedTransactions } from '../../RegonizeTranasctions/RevertRecognizedTransactions'; + +@Service() +export class DeleteUncategorizedTransactionsOnAccountDeleting { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private deleteBankRules: DeleteBankRulesService; + + @Inject() + private revertRecognizedTransactins: RevertRecognizedTransactions; + + /** + * Constructor method. + */ + public attach(bus) { + bus.subscribe( + events.accounts.onDelete, + this.handleDeleteBankRulesOnAccountDeleting.bind(this) + ); + } + + /** + * Handles revert the recognized transactions and delete all the bank rules + * associated to the deleted bank account. + * @param {IAccountEventDeletePayload} + */ + private async handleDeleteBankRulesOnAccountDeleting({ + tenantId, + oldAccount, + trx, + }: IAccountEventDeletePayload) { + const knex = this.tenancy.knex(tenantId); + const { + BankRule, + UncategorizedCashflowTransaction, + MatchedBankTransaction, + RecognizedBankTransaction, + } = this.tenancy.models(tenantId); + + const foundAssociatedRules = await BankRule.query(trx).where( + 'applyIfAccountId', + oldAccount.id + ); + const foundAssociatedRulesIds = foundAssociatedRules.map((rule) => rule.id); + + await initialize(knex, [ + UncategorizedCashflowTransaction, + RecognizedBankTransaction, + MatchedBankTransaction, + ]); + // Revert the recognized transactions of the given bank rules. + await this.revertRecognizedTransactins.revertRecognizedTransactions( + tenantId, + foundAssociatedRulesIds, + null, + trx + ); + // Delete the associated uncategorized transactions. + await UncategorizedCashflowTransaction.query(trx) + .where('accountId', oldAccount.id) + .delete(); + + // Delete the given bank rules. + await this.deleteBankRules.deleteBankRules( + tenantId, + foundAssociatedRulesIds, + trx + ); + } +} diff --git a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts index 89f02da57..4ecf71e81 100644 --- a/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts +++ b/packages/server/src/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted.ts @@ -51,6 +51,7 @@ export class DisconnectPlaidItemOnAccountDeleted { .findOne('plaidItemId', oldAccount.plaidItemId) .delete(); + // Remove Plaid item once the transaction resolve. if (oldPlaidItem) { const plaidInstance = PlaidClientWrapper.getClient(); diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts index c7caf1325..cf46790a3 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts @@ -64,7 +64,7 @@ export class GetMatchedTransactions { .whereIn('id', uncategorizedTransactionIds) .throwIfNotFound(); - const totalPending = Math.abs(sumBy(uncategorizedTransactions, 'amount')); + const totalPending = sumBy(uncategorizedTransactions, 'amount'); const filtered = filter.transactionType ? this.registered.filter((item) => item.type === filter.transactionType) diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts index 394fecba5..b9b85cb8c 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts @@ -1,18 +1,25 @@ import { Inject, Service } from 'typedi'; import { initialize } from 'objection'; +import { Knex } from 'knex'; +import { first } from 'lodash'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { GetMatchedTransactionBillsTransformer } from './GetMatchedTransactionBillsTransformer'; -import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types'; -import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { + GetMatchedTransactionsFilter, + IMatchTransactionDTO, + MatchedTransactionPOJO, +} from './types'; import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType'; +import { CreateBillPayment } from '@/services/Purchases/BillPayments/CreateBillPayment'; +import { IBillPaymentDTO } from '@/interfaces'; @Service() export class GetMatchedTransactionsByBills extends GetMatchedTransactionsByType { @Inject() - private tenancy: HasTenancyService; + private transformer: TransformerInjectable; @Inject() - private transformer: TransformerInjectable; + private createPaymentMadeService: CreateBillPayment; /** * Retrieves the matched transactions. @@ -71,4 +78,62 @@ export class GetMatchedTransactionsByBills extends GetMatchedTransactionsByType new GetMatchedTransactionBillsTransformer() ); } + + /** + * Creates the common matched transaction. + * @param {number} tenantId + * @param {Array} uncategorizedTransactionIds + * @param {IMatchTransactionDTO} matchTransactionDTO + * @param {Knex.Transaction} trx + */ + public async createMatchedTransaction( + tenantId: number, + uncategorizedTransactionIds: Array, + matchTransactionDTO: IMatchTransactionDTO, + trx?: Knex.Transaction + ): Promise { + await super.createMatchedTransaction( + tenantId, + uncategorizedTransactionIds, + matchTransactionDTO, + trx + ); + const { Bill, UncategorizedCashflowTransaction, MatchedBankTransaction } = + this.tenancy.models(tenantId); + + const uncategorizedTransactionId = first(uncategorizedTransactionIds); + const uncategorizedTransaction = + await UncategorizedCashflowTransaction.query(trx) + .findById(uncategorizedTransactionId) + .throwIfNotFound(); + + const bill = await Bill.query(trx) + .findById(matchTransactionDTO.referenceId) + .throwIfNotFound(); + + const createPaymentMadeDTO: IBillPaymentDTO = { + vendorId: bill.vendorId, + paymentAccountId: uncategorizedTransaction.accountId, + paymentDate: uncategorizedTransaction.date, + exchangeRate: 1, + entries: [ + { + paymentAmount: bill.dueAmount, + billId: bill.id, + }, + ], + branchId: bill.branchId, + }; + // Create a new bill payment associated to the matched bill. + const billPayment = await this.createPaymentMadeService.createBillPayment( + tenantId, + createPaymentMadeDTO, + trx + ); + // Link the create bill payment with matched transaction. + await super.createMatchedTransaction(tenantId, uncategorizedTransactionIds, { + referenceType: 'BillPayment', + referenceId: billPayment.id, + }, trx); + } } diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts index 88cc72c1e..1d7baee7a 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts @@ -1,22 +1,26 @@ import { Inject, Service } from 'typedi'; import { initialize } from 'objection'; +import { Knex } from 'knex'; +import { first } from 'lodash'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer'; import { GetMatchedTransactionsFilter, + IMatchTransactionDTO, MatchedTransactionPOJO, MatchedTransactionsPOJO, } from './types'; -import HasTenancyService from '@/services/Tenancy/TenancyService'; import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType'; +import { CreatePaymentReceived } from '@/services/Sales/PaymentReceived/CreatePaymentReceived'; +import { IPaymentReceivedCreateDTO } from '@/interfaces'; @Service() export class GetMatchedTransactionsByInvoices extends GetMatchedTransactionsByType { @Inject() - protected tenancy: HasTenancyService; + protected transformer: TransformerInjectable; @Inject() - protected transformer: TransformerInjectable; + protected createPaymentReceivedService: CreatePaymentReceived; /** * Retrieves the matched transactions. @@ -78,4 +82,64 @@ export class GetMatchedTransactionsByInvoices extends GetMatchedTransactionsByTy new GetMatchedTransactionInvoicesTransformer() ); } + + /** + * Creates the common matched transaction. + * @param {number} tenantId + * @param {Array} uncategorizedTransactionIds + * @param {IMatchTransactionDTO} matchTransactionDTO + * @param {Knex.Transaction} trx + */ + public async createMatchedTransaction( + tenantId: number, + uncategorizedTransactionIds: Array, + matchTransactionDTO: IMatchTransactionDTO, + trx?: Knex.Transaction + ) { + await super.createMatchedTransaction( + tenantId, + uncategorizedTransactionIds, + matchTransactionDTO, + trx + ); + const { SaleInvoice, UncategorizedCashflowTransaction, MatchedBankTransaction } = + this.tenancy.models(tenantId); + + const uncategorizedTransactionId = first(uncategorizedTransactionIds); + const uncategorizedTransaction = + await UncategorizedCashflowTransaction.query(trx) + .findById(uncategorizedTransactionId) + .throwIfNotFound(); + + const invoice = await SaleInvoice.query(trx) + .findById(matchTransactionDTO.referenceId) + .throwIfNotFound(); + + const createPaymentReceivedDTO: IPaymentReceivedCreateDTO = { + customerId: invoice.customerId, + paymentDate: uncategorizedTransaction.date, + amount: invoice.dueAmount, + depositAccountId: uncategorizedTransaction.accountId, + entries: [ + { + index: 1, + invoiceId: invoice.id, + paymentAmount: invoice.dueAmount, + }, + ], + branchId: invoice.branchId, + }; + // Create a payment received associated to the matched invoice. + const paymentReceived = await this.createPaymentReceivedService.createPaymentReceived( + tenantId, + createPaymentReceivedDTO, + {}, + trx + ); + // Link the create payment received with matched invoice transaction. + await super.createMatchedTransaction(tenantId, uncategorizedTransactionIds, { + referenceType: 'PaymentReceive', + referenceId: paymentReceived.id, + }, trx) + } } diff --git a/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentMadeDelete.ts b/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentMadeDelete.ts index c57c28729..e0f3572e8 100644 --- a/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentMadeDelete.ts +++ b/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentMadeDelete.ts @@ -1,7 +1,7 @@ import { Inject, Service } from 'typedi'; import { IBillPaymentEventDeletedPayload, - IPaymentReceiveDeletedPayload, + IPaymentReceivedDeletedPayload, } from '@/interfaces'; import { ValidateTransactionMatched } from '../ValidateTransactionsMatched'; import events from '@/subscribers/events'; @@ -23,7 +23,7 @@ export class ValidateMatchingOnPaymentMadeDelete { /** * Validates the payment made transaction whether matched with bank transaction on deleting. - * @param {IPaymentReceiveDeletedPayload} + * @param {IPaymentReceivedDeletedPayload} */ public async validateMatchingOnPaymentMadeDeleting({ tenantId, diff --git a/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentReceivedDelete.ts b/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentReceivedDelete.ts index 446f3d5f1..385f55512 100644 --- a/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentReceivedDelete.ts +++ b/packages/server/src/services/Banking/Matching/events/ValidateMatchingOnPaymentReceivedDelete.ts @@ -1,5 +1,5 @@ import { Inject, Service } from 'typedi'; -import { IPaymentReceiveDeletedPayload } from '@/interfaces'; +import { IPaymentReceivedDeletedPayload } from '@/interfaces'; import { ValidateTransactionMatched } from '../ValidateTransactionsMatched'; import events from '@/subscribers/events'; @@ -20,13 +20,13 @@ export class ValidateMatchingOnPaymentReceivedDelete { /** * Validates the payment received transaction whether matched with bank transaction on deleting. - * @param {IPaymentReceiveDeletedPayload} + * @param {IPaymentReceivedDeletedPayload} */ public async validateMatchingOnPaymentReceivedDeleting({ tenantId, oldPaymentReceive, trx, - }: IPaymentReceiveDeletedPayload) { + }: IPaymentReceivedDeletedPayload) { await this.validateNoMatchingLinkedService.validateTransactionNoMatchLinking( tenantId, 'PaymentReceive', diff --git a/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts b/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts index aed2fc945..4c1973f8b 100644 --- a/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts +++ b/packages/server/src/services/Banking/Plaid/PlaidSyncDB.ts @@ -25,6 +25,7 @@ import { Knex } from 'knex'; import uniqid from 'uniqid'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; +import { RemovePendingUncategorizedTransaction } from '@/services/Cashflow/RemovePendingUncategorizedTransaction'; const CONCURRENCY_ASYNC = 10; @@ -40,7 +41,7 @@ export class PlaidSyncDb { private cashflowApp: CashflowApplication; @Inject() - private deleteCashflowTransactionService: DeleteCashflowTransaction; + private removePendingTransaction: RemovePendingUncategorizedTransaction; @Inject() private eventPublisher: EventPublisher; @@ -185,21 +186,22 @@ export class PlaidSyncDb { plaidTransactionsIds: string[], trx?: Knex.Transaction ) { - const { CashflowTransaction } = this.tenancy.models(tenantId); + const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId); - const cashflowTransactions = await CashflowTransaction.query(trx).whereIn( - 'plaidTransactionId', - plaidTransactionsIds - ); - const cashflowTransactionsIds = cashflowTransactions.map( + const uncategorizedTransactions = + await UncategorizedCashflowTransaction.query(trx).whereIn( + 'plaidTransactionId', + plaidTransactionsIds + ); + const uncategorizedTransactionsIds = uncategorizedTransactions.map( (trans) => trans.id ); await bluebird.map( - cashflowTransactionsIds, - (transactionId: number) => - this.deleteCashflowTransactionService.deleteCashflowTransaction( + uncategorizedTransactionsIds, + (uncategorizedTransactionId: number) => + this.removePendingTransaction.removePendingTransaction( tenantId, - transactionId, + uncategorizedTransactionId, trx ), { concurrency: CONCURRENCY_ASYNC } diff --git a/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts b/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts index 3265cc2ba..c987dd84f 100644 --- a/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts +++ b/packages/server/src/services/Banking/Plaid/PlaidUpdateTransactions.ts @@ -1,10 +1,10 @@ -import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { Knex } from 'knex'; import { Inject, Service } from 'typedi'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; import { PlaidClientWrapper } from '@/lib/Plaid/Plaid'; import { PlaidSyncDb } from './PlaidSyncDB'; import { PlaidFetchedTransactionsUpdates } from '@/interfaces'; import UnitOfWork from '@/services/UnitOfWork'; -import { Knex } from 'knex'; @Service() export class PlaidUpdateTransactions { @@ -19,9 +19,9 @@ export class PlaidUpdateTransactions { /** * Handles sync the Plaid item to Bigcaptial under UOW. - * @param {number} tenantId - * @param {number} plaidItemId - * @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>} + * @param {number} tenantId - Tenant id. + * @param {number} plaidItemId - Plaid item id. + * @returns {Promise<{ addedCount: number; modifiedCount: number; removedCount: number; }>} */ public async updateTransactions(tenantId: number, plaidItemId: string) { return this.uow.withTransaction(tenantId, (trx: Knex.Transaction) => { @@ -73,6 +73,12 @@ export class PlaidUpdateTransactions { item, trx ); + // Sync removed transactions. + await this.plaidSync.syncRemoveTransactions( + tenantId, + removed?.map((r) => r.transaction_id), + trx + ); // Sync bank account transactions. await this.plaidSync.syncAccountsTransactions( tenantId, diff --git a/packages/server/src/services/Banking/Plaid/utils.ts b/packages/server/src/services/Banking/Plaid/utils.ts index 395b4346f..2804279c5 100644 --- a/packages/server/src/services/Banking/Plaid/utils.ts +++ b/packages/server/src/services/Banking/Plaid/utils.ts @@ -3,11 +3,11 @@ import { Item as PlaidItem, Institution as PlaidInstitution, AccountBase as PlaidAccount, + TransactionBase as PlaidTransactionBase, } from 'plaid'; import { CreateUncategorizedTransactionDTO, IAccountCreateDTO, - PlaidTransaction, } from '@/interfaces'; /** @@ -48,7 +48,7 @@ export const transformPlaidAccountToCreateAccount = R.curry( export const transformPlaidTrxsToCashflowCreate = R.curry( ( cashflowAccountId: number, - plaidTranasction: PlaidTransaction + plaidTranasction: PlaidTransactionBase ): CreateUncategorizedTransactionDTO => { return { date: plaidTranasction.date, @@ -64,6 +64,8 @@ export const transformPlaidTrxsToCashflowCreate = R.curry( accountId: cashflowAccountId, referenceNo: plaidTranasction.payment_meta?.reference_number, plaidTransactionId: plaidTranasction.transaction_id, + pending: plaidTranasction.pending, + pendingPlaidTransactionId: plaidTranasction.pending_transaction_id, }; } ); diff --git a/packages/server/src/services/Banking/RegonizeTranasctions/_utils.ts b/packages/server/src/services/Banking/RegonizeTranasctions/_utils.ts index 1eaa546ec..4ca0d8b9f 100644 --- a/packages/server/src/services/Banking/RegonizeTranasctions/_utils.ts +++ b/packages/server/src/services/Banking/RegonizeTranasctions/_utils.ts @@ -33,17 +33,29 @@ const matchNumberCondition = ( transaction: UncategorizedCashflowTransaction, condition: IBankRuleCondition ) => { + const conditionValue = parseFloat(condition.value); + const transactionAmount = + condition.field === 'amount' + ? Math.abs(transaction[condition.field]) + : (transaction[condition.field] as unknown as number); + switch (condition.comparator) { case BankRuleConditionComparator.Equals: - return transaction[condition.field] === condition.value; - case BankRuleConditionComparator.Contains: - return transaction[condition.field] - ?.toString() - .includes(condition.value.toString()); - case BankRuleConditionComparator.NotContain: - return !transaction[condition.field] - ?.toString() - .includes(condition.value.toString()); + case BankRuleConditionComparator.Equal: + return transactionAmount === conditionValue; + + case BankRuleConditionComparator.BiggerOrEqual: + return transactionAmount >= conditionValue; + + case BankRuleConditionComparator.Bigger: + return transactionAmount > conditionValue; + + case BankRuleConditionComparator.Smaller: + return transactionAmount < conditionValue; + + case BankRuleConditionComparator.SmallerOrEqual: + return transactionAmount <= conditionValue; + default: return false; } @@ -53,18 +65,19 @@ const matchTextCondition = ( transaction: UncategorizedCashflowTransaction, condition: IBankRuleCondition ): boolean => { + const transactionValue = transaction[condition.field] as string; + switch (condition.comparator) { case BankRuleConditionComparator.Equals: - return transaction[condition.field] === condition.value; + case BankRuleConditionComparator.Equal: + return transactionValue === condition.value; case BankRuleConditionComparator.Contains: - const fieldValue = lowerCase(transaction[condition.field]); + const fieldValue = lowerCase(transactionValue); const conditionValue = lowerCase(condition.value); return fieldValue.includes(conditionValue); case BankRuleConditionComparator.NotContain: - return !transaction[condition.field]?.includes( - condition.value.toString() - ); + return !transactionValue?.includes(condition.value.toString()); default: return false; } @@ -101,8 +114,8 @@ const determineFieldType = (field: string): string => { case 'amount': return 'number'; case 'description': - return 'text'; + case 'payee': default: - return 'unknown'; + return 'text'; } -}; \ No newline at end of file +}; diff --git a/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts b/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts index 0ee073a46..466f0b3b1 100644 --- a/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts +++ b/packages/server/src/services/Banking/RegonizeTranasctions/events/TriggerRecognizedTransactions.ts @@ -1,4 +1,5 @@ import { Inject, Service } from 'typedi'; +import { isEqual, omit } from 'lodash'; import events from '@/subscribers/events'; import { IBankRuleEventCreatedPayload, @@ -55,10 +56,22 @@ export class TriggerRecognizedTransactions { private async recognizedTransactionsOnRuleEdited({ tenantId, editRuleDTO, + oldBankRule, + bankRule, ruleId, }: IBankRuleEventEditedPayload) { const payload = { tenantId, ruleId }; + // Cannot continue if the new and old bank rule values are the same, + // after excluding `createdAt` and `updatedAt` dates. + if ( + isEqual( + omit(bankRule, ['createdAt', 'updatedAt']), + omit(oldBankRule, ['createdAt', 'updatedAt']) + ) + ) { + return; + } await this.agenda.now( 'rerecognize-uncategorized-transactions-job', payload diff --git a/packages/server/src/services/Banking/Rules/DeleteBankRule.ts b/packages/server/src/services/Banking/Rules/DeleteBankRule.ts index c02ab6686..3981059c9 100644 --- a/packages/server/src/services/Banking/Rules/DeleteBankRule.ts +++ b/packages/server/src/services/Banking/Rules/DeleteBankRule.ts @@ -26,31 +26,39 @@ export class DeleteBankRuleSerivce { * @param {number} ruleId * @returns {Promise} */ - public async deleteBankRule(tenantId: number, ruleId: number): Promise { + public async deleteBankRule( + tenantId: number, + ruleId: number, + trx?: Knex.Transaction + ): Promise { const { BankRule, BankRuleCondition } = this.tenancy.models(tenantId); const oldBankRule = await BankRule.query() .findById(ruleId) .throwIfNotFound(); - return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { - // Triggers `onBankRuleDeleting` event. - await this.eventPublisher.emitAsync(events.bankRules.onDeleting, { - tenantId, - oldBankRule, - ruleId, - trx, - } as IBankRuleEventDeletingPayload); + return this.uow.withTransaction( + tenantId, + async (trx: Knex.Transaction) => { + // Triggers `onBankRuleDeleting` event. + await this.eventPublisher.emitAsync(events.bankRules.onDeleting, { + tenantId, + oldBankRule, + ruleId, + trx, + } as IBankRuleEventDeletingPayload); - await BankRuleCondition.query(trx).where('ruleId', ruleId).delete(); - await BankRule.query(trx).findById(ruleId).delete(); + await BankRuleCondition.query(trx).where('ruleId', ruleId).delete() + await BankRule.query(trx).findById(ruleId).delete(); - // Triggers `onBankRuleDeleted` event. - await await this.eventPublisher.emitAsync(events.bankRules.onDeleted, { - tenantId, - ruleId, - trx, - } as IBankRuleEventDeletedPayload); - }); + // Triggers `onBankRuleDeleted` event. + await await this.eventPublisher.emitAsync(events.bankRules.onDeleted, { + tenantId, + ruleId, + trx, + } as IBankRuleEventDeletedPayload); + }, + trx + ); } } diff --git a/packages/server/src/services/Banking/Rules/DeleteBankRules.ts b/packages/server/src/services/Banking/Rules/DeleteBankRules.ts new file mode 100644 index 000000000..98d17e9cd --- /dev/null +++ b/packages/server/src/services/Banking/Rules/DeleteBankRules.ts @@ -0,0 +1,34 @@ +import { Knex } from 'knex'; +import { Inject, Service } from 'typedi'; +import PromisePool from '@supercharge/promise-pool'; +import { castArray, uniq } from 'lodash'; +import { DeleteBankRuleSerivce } from './DeleteBankRule'; + +@Service() +export class DeleteBankRulesService { + @Inject() + private deleteBankRuleService: DeleteBankRuleSerivce; + + /** + * Delete bank rules. + * @param {number} tenantId + * @param {number | Array} bankRuleId + */ + async deleteBankRules( + tenantId: number, + bankRuleId: number | Array, + trx?: Knex.Transaction + ) { + const bankRulesIds = uniq(castArray(bankRuleId)); + + const results = await PromisePool.withConcurrency(1) + .for(bankRulesIds) + .process(async (bankRuleId: number) => { + await this.deleteBankRuleService.deleteBankRule( + tenantId, + bankRuleId, + trx + ); + }); + } +} diff --git a/packages/server/src/services/Banking/Rules/EditBankRule.ts b/packages/server/src/services/Banking/Rules/EditBankRule.ts index 2d6d868f4..67536ab82 100644 --- a/packages/server/src/services/Banking/Rules/EditBankRule.ts +++ b/packages/server/src/services/Banking/Rules/EditBankRule.ts @@ -47,6 +47,7 @@ export class EditBankRuleService { const oldBankRule = await BankRule.query() .findById(ruleId) + .withGraphFetched('conditions') .throwIfNotFound(); const tranformDTO = this.transformDTO(editRuleDTO); @@ -64,15 +65,15 @@ export class EditBankRuleService { } as IBankRuleEventEditingPayload); // Updates the given bank rule. - await BankRule.query(trx).upsertGraphAndFetch({ + const bankRule = await BankRule.query(trx).upsertGraphAndFetch({ ...tranformDTO, id: ruleId, }); - // Triggers `onBankRuleEdited` event. await this.eventPublisher.emitAsync(events.bankRules.onEdited, { tenantId, oldBankRule, + bankRule, ruleId, editRuleDTO, trx, diff --git a/packages/server/src/services/Banking/Rules/types.ts b/packages/server/src/services/Banking/Rules/types.ts index d54cb9388..49e71abf5 100644 --- a/packages/server/src/services/Banking/Rules/types.ts +++ b/packages/server/src/services/Banking/Rules/types.ts @@ -1,15 +1,20 @@ import { Knex } from 'knex'; export enum BankRuleConditionField { - Amount = 'Amount', - Description = 'Description', - Payee = 'Payee', + Amount = 'amount', + Description = 'description', + Payee = 'payee', } export enum BankRuleConditionComparator { Contains = 'contains', Equals = 'equals', + Equal = 'equal', NotContain = 'not_contain', + Bigger = 'bigger', + BiggerOrEqual = 'bigger_or_equal', + Smaller = 'smaller', + SmallerOrEqual = 'smaller_or_equal', } export interface IBankRuleCondition { @@ -56,7 +61,15 @@ export enum BankRuleAssignCategory { export interface IBankRuleConditionDTO { id?: number; field: string; - comparator: string; + comparator: + | 'contains' + | 'equals' + | 'not_contains' + | 'equal' + | 'bigger' + | 'bigger_or_equal' + | 'smaller' + | 'smaller_or_equal'; value: number; } @@ -99,6 +112,8 @@ export interface IBankRuleEventEditingPayload { export interface IBankRuleEventEditedPayload { tenantId: number; ruleId: number; + oldBankRule: IBankRule; + bankRule: IBankRule; editRuleDTO: IEditBankRuleDTO; trx?: Knex.Transaction; } diff --git a/packages/server/src/services/Branches/Subscribers/Validators/PaymentReceiveBranchSubscriber.ts b/packages/server/src/services/Branches/Subscribers/Validators/PaymentReceiveBranchSubscriber.ts index e08b78489..8aa9b5d6e 100644 --- a/packages/server/src/services/Branches/Subscribers/Validators/PaymentReceiveBranchSubscriber.ts +++ b/packages/server/src/services/Branches/Subscribers/Validators/PaymentReceiveBranchSubscriber.ts @@ -1,8 +1,8 @@ import { Inject, Service } from 'typedi'; import events from '@/subscribers/events'; import { - IPaymentReceiveCreatingPayload, - IPaymentReceiveEditingPayload, + IPaymentReceivedCreatingPayload, + IPaymentReceivedEditingPayload, } from '@/interfaces'; import { ValidateBranchExistance } from '../../Integrations/ValidateBranchExistance'; @@ -28,12 +28,12 @@ export class PaymentReceiveBranchValidateSubscriber { /** * Validate branch existance on estimate creating. - * @param {IPaymentReceiveCreatingPayload} payload + * @param {IPaymentReceivedCreatingPayload} payload */ private validateBranchExistanceOnPaymentCreating = async ({ tenantId, paymentReceiveDTO, - }: IPaymentReceiveCreatingPayload) => { + }: IPaymentReceivedCreatingPayload) => { await this.validateBranchExistance.validateTransactionBranchWhenActive( tenantId, paymentReceiveDTO.branchId @@ -42,12 +42,12 @@ export class PaymentReceiveBranchValidateSubscriber { /** * Validate branch existance once estimate editing. - * @param {IPaymentReceiveEditingPayload} payload + * @param {IPaymentReceivedEditingPayload} payload */ private validateBranchExistanceOnPaymentEditing = async ({ paymentReceiveDTO, tenantId, - }: IPaymentReceiveEditingPayload) => { + }: IPaymentReceivedEditingPayload) => { await this.validateBranchExistance.validateTransactionBranchWhenActive( tenantId, paymentReceiveDTO.branchId diff --git a/packages/server/src/services/Cashflow/CashflowAccountTransformer.ts b/packages/server/src/services/Cashflow/CashflowAccountTransformer.ts index e89104655..c78730041 100644 --- a/packages/server/src/services/Cashflow/CashflowAccountTransformer.ts +++ b/packages/server/src/services/Cashflow/CashflowAccountTransformer.ts @@ -8,7 +8,12 @@ export class CashflowAccountTransformer extends Transformer { * @returns {string[]} */ public includeAttributes = (): string[] => { - return ['formattedAmount']; + return [ + 'formattedAmount', + 'lastFeedsUpdatedAt', + 'lastFeedsUpdatedAtFormatted', + 'lastFeedsUpdatedFromNow', + ]; }; /** @@ -29,7 +34,7 @@ export class CashflowAccountTransformer extends Transformer { /** * Retrieve formatted account amount. - * @param {IAccount} invoice + * @param {IAccount} invoice * @returns {string} */ protected formattedAmount = (account: IAccount): string => { @@ -37,4 +42,22 @@ export class CashflowAccountTransformer extends Transformer { currencyCode: account.currencyCode, }); }; + + /** + * Retrieves the last feeds update at formatted date. + * @param {IAccount} account + * @returns {string} + */ + protected lastFeedsUpdatedAtFormatted(account: IAccount): string { + return this.formatDate(account.lastFeedsUpdatedAt); + } + + /** + * Retrieves the last feeds updated from now. + * @param {IAccount} account + * @returns {string} + */ + protected lastFeedsUpdatedFromNow(account: IAccount): string { + return this.formatDateFromNow(account.lastFeedsUpdatedAt); + } } diff --git a/packages/server/src/services/Cashflow/CashflowApplication.ts b/packages/server/src/services/Cashflow/CashflowApplication.ts index f54935db7..5773c1ee1 100644 --- a/packages/server/src/services/Cashflow/CashflowApplication.ts +++ b/packages/server/src/services/Cashflow/CashflowApplication.ts @@ -21,6 +21,7 @@ import GetCashflowAccountsService from './GetCashflowAccountsService'; import { GetCashflowTransactionService } from './GetCashflowTransactionsService'; import { GetRecognizedTransactionsService } from './GetRecongizedTransactions'; import { GetRecognizedTransactionService } from './GetRecognizedTransaction'; +import { UncategorizeCashflowTransactionsBulk } from './UncategorizeCashflowTransactionsBulk'; @Service() export class CashflowApplication { @@ -39,6 +40,9 @@ export class CashflowApplication { @Inject() private uncategorizeTransactionService: UncategorizeCashflowTransaction; + @Inject() + private uncategorizeTransasctionsService: UncategorizeCashflowTransactionsBulk; + @Inject() private categorizeTransactionService: CategorizeCashflowTransaction; @@ -155,6 +159,22 @@ export class CashflowApplication { ); } + /** + * Uncategorize the given transactions in bulk. + * @param {number} tenantId + * @param {number | Array} transactionId + * @returns + */ + public uncategorizeTransactions( + tenantId: number, + transactionId: number | Array + ) { + return this.uncategorizeTransasctionsService.uncategorizeBulk( + tenantId, + transactionId + ); + } + /** * Categorize the given cashflow transaction. * @param {number} tenantId @@ -241,9 +261,9 @@ export class CashflowApplication { /** * Retrieves the recognized transaction of the given uncategorized transaction. - * @param {number} tenantId - * @param {number} uncategorizedTransactionId - * @returns + * @param {number} tenantId + * @param {number} uncategorizedTransactionId + * @returns */ public getRecognizedTransaction( tenantId: number, diff --git a/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts b/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts new file mode 100644 index 000000000..7f19af6a1 --- /dev/null +++ b/packages/server/src/services/Cashflow/GetPendingBankAccountTransaction.ts @@ -0,0 +1,53 @@ +import { Inject } from 'typedi'; +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import HasTenancyService from '../Tenancy/TenancyService'; +import { GetPendingBankAccountTransactionTransformer } from './GetPendingBankAccountTransactionTransformer'; + +export class GetPendingBankAccountTransactions { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + /** + * Retrieves the given bank accounts pending transaction. + * @param {number} tenantId - Tenant id. + * @param {GetPendingTransactionsQuery} filter - Pending transactions query. + */ + async getPendingTransactions( + tenantId: number, + filter?: GetPendingTransactionsQuery + ) { + const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId); + + const _filter = { + page: 1, + pageSize: 20, + ...filter, + }; + const { results, pagination } = + await UncategorizedCashflowTransaction.query() + .onBuild((q) => { + q.modify('pending'); + + if (_filter?.accountId) { + q.where('accountId', _filter.accountId); + } + }) + .pagination(_filter.page - 1, _filter.pageSize); + + const data = await this.transformer.transform( + tenantId, + results, + new GetPendingBankAccountTransactionTransformer() + ); + return { data, pagination }; + } +} + +interface GetPendingTransactionsQuery { + page?: number; + pageSize?: number; + accountId?: number; +} diff --git a/packages/server/src/services/Cashflow/GetPendingBankAccountTransactionTransformer.ts b/packages/server/src/services/Cashflow/GetPendingBankAccountTransactionTransformer.ts new file mode 100644 index 000000000..d388546a4 --- /dev/null +++ b/packages/server/src/services/Cashflow/GetPendingBankAccountTransactionTransformer.ts @@ -0,0 +1,73 @@ +import { Transformer } from '@/lib/Transformer/Transformer'; +import { formatNumber } from '@/utils'; + +export class GetPendingBankAccountTransactionTransformer extends Transformer { + /** + * Include these attributes to sale invoice object. + * @returns {string[]} + */ + public includeAttributes = (): string[] => { + return [ + 'formattedAmount', + 'formattedDate', + 'formattedDepositAmount', + 'formattedWithdrawalAmount', + ]; + }; + + /** + * Exclude all attributes. + * @returns {Array} + */ + public excludeAttributes = (): string[] => { + return []; + }; + + /** + * Formattes the transaction date. + * @param transaction + * @returns {string} + */ + public formattedDate(transaction) { + return this.formatDate(transaction.date); + } + + /** + * Formatted amount. + * @param transaction + * @returns {string} + */ + public formattedAmount(transaction) { + return formatNumber(transaction.amount, { + currencyCode: transaction.currencyCode, + }); + } + + /** + * Formatted deposit amount. + * @param transaction + * @returns {string} + */ + protected formattedDepositAmount(transaction) { + if (transaction.isDepositTransaction) { + return formatNumber(transaction.deposit, { + currencyCode: transaction.currencyCode, + }); + } + return ''; + } + + /** + * Formatted withdrawal amount. + * @param transaction + * @returns {string} + */ + protected formattedWithdrawalAmount(transaction) { + if (transaction.isWithdrawalTransaction) { + return formatNumber(transaction.withdrawal, { + currencyCode: transaction.currencyCode, + }); + } + return ''; + } +} diff --git a/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts b/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts index 1fec81968..7665180d0 100644 --- a/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts +++ b/packages/server/src/services/Cashflow/GetRecongizedTransactions.ts @@ -34,8 +34,13 @@ export class GetRecognizedTransactionsService { q.withGraphFetched('recognizedTransaction.assignAccount'); q.withGraphFetched('recognizedTransaction.bankRule'); q.whereNotNull('recognizedTransactionId'); + + // Exclude the excluded transactions. q.modify('notExcluded'); + // Exclude the pending transactions. + q.modify('notPending'); + if (_query.accountId) { q.where('accountId', _query.accountId); } @@ -51,6 +56,9 @@ export class GetRecognizedTransactionsService { if (_query.maxAmount) { q.modify('maxAmount', _query.maxAmount); } + if (_query.accountId) { + q.where('accountId', _query.accountId); + } }) .pagination(_query.page - 1, _query.pageSize); diff --git a/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts b/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts index 7d8da347b..afc2d058b 100644 --- a/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts +++ b/packages/server/src/services/Cashflow/GetUncategorizedTransactions.ts @@ -51,7 +51,9 @@ export class GetUncategorizedTransactions { .onBuild((q) => { q.where('accountId', accountId); q.where('categorized', false); + q.modify('notExcluded'); + q.modify('notPending'); q.withGraphFetched('account'); q.withGraphFetched('recognizedTransaction.assignAccount'); diff --git a/packages/server/src/services/Cashflow/RemovePendingUncategorizedTransaction.ts b/packages/server/src/services/Cashflow/RemovePendingUncategorizedTransaction.ts new file mode 100644 index 000000000..101a914cf --- /dev/null +++ b/packages/server/src/services/Cashflow/RemovePendingUncategorizedTransaction.ts @@ -0,0 +1,72 @@ +import { Knex } from 'knex'; +import { Inject, Service } from 'typedi'; +import HasTenancyService from '../Tenancy/TenancyService'; +import UnitOfWork from '../UnitOfWork'; +import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; +import events from '@/subscribers/events'; +import { ServiceError } from '@/exceptions'; +import { ERRORS } from './constants'; +import { + IPendingTransactionRemovedEventPayload, + IPendingTransactionRemovingEventPayload, +} from '@/interfaces'; + +@Service() +export class RemovePendingUncategorizedTransaction { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private uow: UnitOfWork; + + @Inject() + private eventPublisher: EventPublisher; + + /** + * REmoves the pending uncategorized transaction. + * @param {number} tenantId - + * @param {number} uncategorizedTransactionId - + * @param {Knex.Transaction} trx - + * @returns {Promise} + */ + public async removePendingTransaction( + tenantId: number, + uncategorizedTransactionId: number, + trx?: Knex.Transaction + ): Promise { + const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId); + + const pendingTransaction = await UncategorizedCashflowTransaction.query(trx) + .findById(uncategorizedTransactionId) + .throwIfNotFound(); + + if (!pendingTransaction.isPending) { + throw new ServiceError(ERRORS.TRANSACTION_NOT_PENDING); + } + return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { + await this.eventPublisher.emitAsync( + events.bankTransactions.onPendingRemoving, + { + tenantId, + uncategorizedTransactionId, + pendingTransaction, + trx, + } as IPendingTransactionRemovingEventPayload + ); + // Removes the pending uncategorized transaction. + await UncategorizedCashflowTransaction.query(trx) + .findById(uncategorizedTransactionId) + .delete(); + + await this.eventPublisher.emitAsync( + events.bankTransactions.onPendingRemoved, + { + tenantId, + uncategorizedTransactionId, + pendingTransaction, + trx, + } as IPendingTransactionRemovedEventPayload + ); + }); + } +} diff --git a/packages/server/src/services/Cashflow/UncategorizeCashflowTransactionsBulk.ts b/packages/server/src/services/Cashflow/UncategorizeCashflowTransactionsBulk.ts new file mode 100644 index 000000000..e5f9ceee2 --- /dev/null +++ b/packages/server/src/services/Cashflow/UncategorizeCashflowTransactionsBulk.ts @@ -0,0 +1,37 @@ +import PromisePool from '@supercharge/promise-pool'; +import { castArray } from 'lodash'; +import { Service, Inject } from 'typedi'; +import HasTenancyService from '../Tenancy/TenancyService'; +import { UncategorizeCashflowTransaction } from './UncategorizeCashflowTransaction'; + +@Service() +export class UncategorizeCashflowTransactionsBulk { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private uncategorizeTransaction: UncategorizeCashflowTransaction; + + /** + * Uncategorize the given bank transactions in bulk. + * @param {number} tenantId + * @param {number} uncategorizedTransactionId + */ + public async uncategorizeBulk( + tenantId: number, + uncategorizedTransactionId: number | Array + ) { + const uncategorizedTransactionIds = castArray(uncategorizedTransactionId); + + const result = await PromisePool.withConcurrency(MIGRATION_CONCURRENCY) + .for(uncategorizedTransactionIds) + .process(async (_uncategorizedTransactionId: number, index, pool) => { + await this.uncategorizeTransaction.uncategorize( + tenantId, + _uncategorizedTransactionId + ); + }); + } +} + +const MIGRATION_CONCURRENCY = 1; diff --git a/packages/server/src/services/Cashflow/constants.ts b/packages/server/src/services/Cashflow/constants.ts index f9b8e24e8..50b35526a 100644 --- a/packages/server/src/services/Cashflow/constants.ts +++ b/packages/server/src/services/Cashflow/constants.ts @@ -15,10 +15,10 @@ export const ERRORS = { 'UNCATEGORIZED_TRANSACTION_TYPE_INVALID', CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED: 'CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED', - - CANNOT_CATEGORIZE_EXCLUDED_TRANSACTION: 'CANNOT_CATEGORIZE_EXCLUDED_TRANSACTION', - TRANSACTION_NOT_CATEGORIZED: 'TRANSACTION_NOT_CATEGORIZED' - + CANNOT_CATEGORIZE_EXCLUDED_TRANSACTION: + 'CANNOT_CATEGORIZE_EXCLUDED_TRANSACTION', + TRANSACTION_NOT_CATEGORIZED: 'TRANSACTION_NOT_CATEGORIZED', + TRANSACTION_NOT_PENDING: 'TRANSACTION_NOT_PENDING', }; export enum CASHFLOW_DIRECTION { diff --git a/packages/server/src/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize.ts b/packages/server/src/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize.ts index a1570a390..bd008967b 100644 --- a/packages/server/src/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize.ts +++ b/packages/server/src/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize.ts @@ -1,11 +1,11 @@ import { Inject, Service } from 'typedi'; +import PromisePool from '@supercharge/promise-pool'; import events from '@/subscribers/events'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { ICashflowTransactionCategorizedPayload, ICashflowTransactionUncategorizedPayload, } from '@/interfaces'; -import PromisePool from '@supercharge/promise-pool'; @Service() export class DecrementUncategorizedTransactionOnCategorize { @@ -36,13 +36,17 @@ export class DecrementUncategorizedTransactionOnCategorize { public async decrementUnCategorizedTransactionsOnCategorized({ tenantId, uncategorizedTransactions, - trx + trx, }: ICashflowTransactionCategorizedPayload) { const { Account } = this.tenancy.models(tenantId); await PromisePool.withConcurrency(1) .for(uncategorizedTransactions) .process(async (uncategorizedTransaction) => { + // Cannot continue if the transaction is still pending. + if (uncategorizedTransaction.isPending) { + return; + } await Account.query(trx) .findById(uncategorizedTransaction.accountId) .decrement('uncategorizedTransactions', 1); @@ -56,13 +60,17 @@ export class DecrementUncategorizedTransactionOnCategorize { public async incrementUnCategorizedTransactionsOnUncategorized({ tenantId, uncategorizedTransactions, - trx + trx, }: ICashflowTransactionUncategorizedPayload) { const { Account } = this.tenancy.models(tenantId); await PromisePool.withConcurrency(1) .for(uncategorizedTransactions) .process(async (uncategorizedTransaction) => { + // Cannot continue if the transaction is still pending. + if (uncategorizedTransaction.isPending) { + return; + } await Account.query(trx) .findById(uncategorizedTransaction.accountId) .increment('uncategorizedTransactions', 1); @@ -82,6 +90,9 @@ export class DecrementUncategorizedTransactionOnCategorize { if (!uncategorizedTransaction.accountId) return; + // Cannot continue if the transaction is still pending. + if (uncategorizedTransaction.isPending) return; + await Account.query(trx) .findById(uncategorizedTransaction.accountId) .increment('uncategorizedTransactions', 1); diff --git a/packages/server/src/services/CreditNotes/CreditNoteApplyToInvoices.ts b/packages/server/src/services/CreditNotes/CreditNoteApplyToInvoices.ts index 5156dbd56..12ffc95b3 100644 --- a/packages/server/src/services/CreditNotes/CreditNoteApplyToInvoices.ts +++ b/packages/server/src/services/CreditNotes/CreditNoteApplyToInvoices.ts @@ -9,7 +9,7 @@ import { } from '@/interfaces'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import UnitOfWork from '@/services/UnitOfWork'; -import { PaymentReceiveValidators } from '../Sales/PaymentReceives/PaymentReceiveValidators'; +import { PaymentReceivedValidators } from '../Sales/PaymentReceived/PaymentReceivedValidators'; import BaseCreditNotes from './CreditNotes'; import { IApplyCreditToInvoicesDTO, @@ -26,7 +26,7 @@ export default class CreditNoteApplyToInvoices extends BaseCreditNotes { private tenancy: HasTenancyService; @Inject() - private paymentReceiveValidators: PaymentReceiveValidators; + private paymentReceiveValidators: PaymentReceivedValidators; @Inject() private uow: UnitOfWork; diff --git a/packages/server/src/services/Expenses/CRUD/ExpenseTransformer.ts b/packages/server/src/services/Expenses/CRUD/ExpenseTransformer.ts index 3f566b87b..fa9bec75a 100644 --- a/packages/server/src/services/Expenses/CRUD/ExpenseTransformer.ts +++ b/packages/server/src/services/Expenses/CRUD/ExpenseTransformer.ts @@ -16,6 +16,7 @@ export class ExpenseTransfromer extends Transformer { 'formattedAllocatedCostAmount', 'formattedDate', 'formattedCreatedAt', + 'formattedPublishedAt', 'categories', 'attachments', ]; @@ -91,4 +92,13 @@ export class ExpenseTransfromer extends Transformer { protected attachments = (expense: IExpense) => { return this.item(expense.attachments, new AttachmentTransformer()); }; + + /** + * Retrieve formatted published at date. + * @param {IExpense} expense + * @returns {string} + */ + protected formattedPublishedAt = (expense: IExpense): string => { + return this.formatDate(expense.publishedAt); + } } diff --git a/packages/server/src/services/Export/ExportResources.ts b/packages/server/src/services/Export/ExportResources.ts index b252bfab1..fd3fce276 100644 --- a/packages/server/src/services/Export/ExportResources.ts +++ b/packages/server/src/services/Export/ExportResources.ts @@ -9,12 +9,13 @@ import { SaleInvoicesExportable } from '../Sales/Invoices/SaleInvoicesExportable import { SaleEstimatesExportable } from '../Sales/Estimates/SaleEstimatesExportable'; import { SaleReceiptsExportable } from '../Sales/Receipts/SaleReceiptsExportable'; import { BillsExportable } from '../Purchases/Bills/BillsExportable'; -import { PaymentsReceivedExportable } from '../Sales/PaymentReceives/PaymentsReceivedExportable'; +import { PaymentsReceivedExportable } from '../Sales/PaymentReceived/PaymentsReceivedExportable'; import { BillPaymentExportable } from '../Purchases/BillPayments/BillPaymentExportable'; import { ManualJournalsExportable } from '../ManualJournals/ManualJournalExportable'; import { CreditNotesExportable } from '../CreditNotes/CreditNotesExportable'; import { VendorCreditsExportable } from '../Purchases/VendorCredits/VendorCreditsExportable'; import { ItemCategoriesExportable } from '../ItemCategories/ItemCategoriesExportable'; +import { TaxRatesExportable } from '../TaxRates/TaxRatesExportable'; @Service() export class ExportableResources { @@ -46,6 +47,7 @@ export class ExportableResources { { resource: 'ManualJournal', exportable: ManualJournalsExportable }, { resource: 'CreditNote', exportable: CreditNotesExportable }, { resource: 'VendorCredit', exportable: VendorCreditsExportable }, + { resource: 'TaxRate', exportable: TaxRatesExportable }, ]; /** diff --git a/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedger.ts b/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedger.ts index 5ae2bba76..3a8a69bf0 100644 --- a/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedger.ts +++ b/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedger.ts @@ -15,6 +15,7 @@ import { FinancialSheetStructure } from '../FinancialSheetStructure'; import { flatToNestedArray } from '@/utils'; import Ledger from '@/services/Accounting/Ledger'; import { calculateRunningBalance } from './_utils'; +import { getTransactionTypeLabel } from '@/utils/transactions-types'; /** * General ledger sheet. @@ -90,11 +91,13 @@ export default class GeneralLedgerSheet extends R.compose( date: entry.date, dateFormatted: moment(entry.date).format('YYYY MMM DD'), - transactionNumber: entry.transactionNumber, - referenceType: entry.referenceType, - referenceId: entry.referenceId, - referenceTypeFormatted: this.i18n.__(entry.referenceTypeFormatted), + referenceType: entry.transactionType, + referenceId: entry.transactionId, + transactionNumber: entry.transactionNumber, + transactionTypeFormatted: this.i18n.__( + getTransactionTypeLabel(entry.transactionType, entry.transactionSubType) + ), contactName: get(contact, 'displayName'), contactType: get(contact, 'contactService'), diff --git a/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedgerTable.ts b/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedgerTable.ts index 80f05f77b..4ecb898e9 100644 --- a/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedgerTable.ts +++ b/packages/server/src/services/FinancialStatements/GeneralLedger/GeneralLedgerTable.ts @@ -67,7 +67,7 @@ export class GeneralLedgerTable extends R.compose( return [ { key: 'date', accessor: 'dateFormatted' }, { key: 'account_name', accessor: 'account.name' }, - { key: 'reference_type', accessor: 'referenceTypeFormatted' }, + { key: 'reference_type', accessor: 'transactionTypeFormatted' }, { key: 'reference_number', accessor: 'transactionNumber' }, { key: 'description', accessor: 'note' }, { key: 'credit', accessor: 'formattedCredit' }, diff --git a/packages/server/src/services/Import/ImportableResources.ts b/packages/server/src/services/Import/ImportableResources.ts index 33f2f10f3..f79ceb36f 100644 --- a/packages/server/src/services/Import/ImportableResources.ts +++ b/packages/server/src/services/Import/ImportableResources.ts @@ -13,9 +13,10 @@ import { SaleInvoicesImportable } from '../Sales/Invoices/SaleInvoicesImportable import { SaleEstimatesImportable } from '../Sales/Estimates/SaleEstimatesImportable'; import { BillPaymentsImportable } from '../Purchases/BillPayments/BillPaymentsImportable'; import { VendorCreditsImportable } from '../Purchases/VendorCredits/VendorCreditsImportable'; -import { PaymentReceivesImportable } from '../Sales/PaymentReceives/PaymentReceivesImportable'; +import { PaymentsReceivedImportable } from '../Sales/PaymentReceived/PaymentsReceivedImportable'; import { CreditNotesImportable } from '../CreditNotes/CreditNotesImportable'; import { SaleReceiptsImportable } from '../Sales/Receipts/SaleReceiptsImportable'; +import { TaxRatesImportable } from '../TaxRates/TaxRatesImportable'; @Service() export class ImportableResources { @@ -44,10 +45,11 @@ export class ImportableResources { { resource: 'SaleInvoice', importable: SaleInvoicesImportable }, { resource: 'SaleEstimate', importable: SaleEstimatesImportable }, { resource: 'BillPayment', importable: BillPaymentsImportable }, - { resource: 'PaymentReceive', importable: PaymentReceivesImportable }, + { resource: 'PaymentReceive', importable: PaymentsReceivedImportable }, { resource: 'VendorCredit', importable: VendorCreditsImportable }, { resource: 'CreditNote', importable: CreditNotesImportable }, - { resource: 'SaleReceipt', importable: SaleReceiptsImportable } + { resource: 'SaleReceipt', importable: SaleReceiptsImportable }, + { resource: 'TaxRate', importable: TaxRatesImportable }, ]; public get registry() { diff --git a/packages/server/src/services/Items/CreateItem.ts b/packages/server/src/services/Items/CreateItem.ts index 5de01aec9..3ce935ee3 100644 --- a/packages/server/src/services/Items/CreateItem.ts +++ b/packages/server/src/services/Items/CreateItem.ts @@ -43,12 +43,22 @@ export class CreateItem { itemDTO.sellAccountId ); } + // Validate the income account id existance if the item is sellable. + this.validators.validateIncomeAccountExistance( + itemDTO.sellable, + itemDTO.sellAccountId + ); if (itemDTO.costAccountId) { await this.validators.validateItemCostAccountExistance( tenantId, itemDTO.costAccountId ); } + // Validate the cost account id existance if the item is purchasable. + this.validators.validateCostAccountExistance( + itemDTO.purchasable, + itemDTO.costAccountId + ); if (itemDTO.inventoryAccountId) { await this.validators.validateItemInventoryAccountExistance( tenantId, diff --git a/packages/server/src/services/Items/EditItem.ts b/packages/server/src/services/Items/EditItem.ts index 6e230921f..17ac8240e 100644 --- a/packages/server/src/services/Items/EditItem.ts +++ b/packages/server/src/services/Items/EditItem.ts @@ -55,6 +55,11 @@ export class EditItem { itemDTO.categoryId ); } + // Validate the income account id existance if the item is sellable. + this.validators.validateIncomeAccountExistance( + itemDTO.sellable, + itemDTO.sellAccountId + ); // Validate the sell account existance on the storage. if (itemDTO.sellAccountId) { await this.validators.validateItemSellAccountExistance( @@ -62,6 +67,11 @@ export class EditItem { itemDTO.sellAccountId ); } + // Validate the cost account id existance if the item is purchasable. + this.validators.validateCostAccountExistance( + itemDTO.purchasable, + itemDTO.costAccountId + ); // Validate the cost account existance on the storage. if (itemDTO.costAccountId) { await this.validators.validateItemCostAccountExistance( diff --git a/packages/server/src/services/Items/ItemValidators.ts b/packages/server/src/services/Items/ItemValidators.ts index c81ac9c7e..3468ad1d0 100644 --- a/packages/server/src/services/Items/ItemValidators.ts +++ b/packages/server/src/services/Items/ItemValidators.ts @@ -85,6 +85,42 @@ export class ItemsValidators { } } + /** + * Validates income account existance. + * @param {number|null} sellable - Detarmines if the item sellable. + * @param {number|null} incomeAccountId - Income account id. + * @throws {ServiceError(ERRORS.INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM)} + */ + public validateIncomeAccountExistance( + sellable?: boolean, + incomeAccountId?: number + ) { + if (sellable && !incomeAccountId) { + throw new ServiceError( + ERRORS.INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM, + 'Income account is require with sellable item.' + ); + } + } + + /** + * Validates the cost account existance. + * @param {boolean|null} purchasable - Detarmines if the item purchasble. + * @param {number|null} costAccountId - Cost account id. + * @throws {ServiceError(ERRORS.COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM)} + */ + public validateCostAccountExistance( + purchasable: boolean, + costAccountId?: number + ) { + if (purchasable && !costAccountId) { + throw new ServiceError( + ERRORS.COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM, + 'The cost account is required with purchasable item.' + ); + } + } + /** * Validate item inventory account existance and type. * @param {number} tenantId diff --git a/packages/server/src/services/Items/constants.ts b/packages/server/src/services/Items/constants.ts index 3d571c527..9b4089687 100644 --- a/packages/server/src/services/Items/constants.ts +++ b/packages/server/src/services/Items/constants.ts @@ -26,6 +26,11 @@ export const ERRORS = { PURCHASE_TAX_RATE_NOT_FOUND: 'PURCHASE_TAX_RATE_NOT_FOUND', SELL_TAX_RATE_NOT_FOUND: 'SELL_TAX_RATE_NOT_FOUND', + + INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM: + 'INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM', + COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM: + 'COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM', }; export const DEFAULT_VIEW_COLUMNS = []; diff --git a/packages/server/src/services/Sales/Estimates/SaleEstimateSmsNotify.ts b/packages/server/src/services/Sales/Estimates/SaleEstimateSmsNotify.ts index 3757b15d7..d90d11769 100644 --- a/packages/server/src/services/Sales/Estimates/SaleEstimateSmsNotify.ts +++ b/packages/server/src/services/Sales/Estimates/SaleEstimateSmsNotify.ts @@ -6,7 +6,7 @@ import SaleNotifyBySms from '../SaleNotifyBySms'; import SmsNotificationsSettingsService from '@/services/Settings/SmsNotificationsSettings'; import { ICustomer, - IPaymentReceiveSmsDetails, + IPaymentReceivedSmsDetails, ISaleEstimate, SMS_NOTIFICATION_KEY, } from '@/interfaces'; @@ -173,12 +173,12 @@ export class SaleEstimateNotifyBySms { * Retrieve the SMS details of the given payment receive transaction. * @param {number} tenantId * @param {number} saleEstimateId - * @returns {Promise} + * @returns {Promise} */ public smsDetails = async ( tenantId: number, saleEstimateId: number - ): Promise => { + ): Promise => { const { SaleEstimate } = this.tenancy.models(tenantId); // Retrieve the sale invoice or throw not found service error. diff --git a/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts b/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts index f1c7b3cdf..cce664b0e 100644 --- a/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts +++ b/packages/server/src/services/Sales/Estimates/SaleEstimatesApplication.ts @@ -3,7 +3,7 @@ import { CreateSaleEstimate } from './CreateSaleEstimate'; import { IFilterMeta, IPaginationMeta, - IPaymentReceiveSmsDetails, + IPaymentReceivedSmsDetails, ISaleEstimate, ISaleEstimateDTO, ISalesEstimatesFilter, @@ -191,12 +191,12 @@ export class SaleEstimatesApplication { * Retrieve the SMS details of the given payment receive transaction. * @param {number} tenantId * @param {number} saleEstimateId - * @returns {Promise} + * @returns {Promise} */ public getSaleEstimateSmsDetails = ( tenantId: number, saleEstimateId: number - ): Promise => { + ): Promise => { return this.saleEstimateNotifyBySmsService.smsDetails( tenantId, saleEstimateId diff --git a/packages/server/src/services/Sales/Invoices/InvoicePaymentsGLRewrite.ts b/packages/server/src/services/Sales/Invoices/InvoicePaymentsGLRewrite.ts index 3e3e9ffda..9e9998647 100644 --- a/packages/server/src/services/Sales/Invoices/InvoicePaymentsGLRewrite.ts +++ b/packages/server/src/services/Sales/Invoices/InvoicePaymentsGLRewrite.ts @@ -2,7 +2,7 @@ import { Knex } from 'knex'; import async from 'async'; import { Inject, Service } from 'typedi'; import HasTenancyService from '@/services/Tenancy/TenancyService'; -import { PaymentReceiveGLEntries } from '../PaymentReceives/PaymentReceiveGLEntries'; +import { PaymentReceivedGLEntries } from '../PaymentReceived/PaymentReceivedGLEntries'; @Service() export class InvoicePaymentsGLEntriesRewrite { @@ -10,7 +10,7 @@ export class InvoicePaymentsGLEntriesRewrite { public tenancy: HasTenancyService; @Inject() - public paymentGLEntries: PaymentReceiveGLEntries; + public paymentGLEntries: PaymentReceivedGLEntries; /** * Rewrites the payment GL entries task. diff --git a/packages/server/src/services/Sales/PaymentReceives/CreatePaymentReceive.ts b/packages/server/src/services/Sales/PaymentReceived/CreatePaymentReceived.ts similarity index 84% rename from packages/server/src/services/Sales/PaymentReceives/CreatePaymentReceive.ts rename to packages/server/src/services/Sales/PaymentReceived/CreatePaymentReceived.ts index 7649177d3..89af2dd6e 100644 --- a/packages/server/src/services/Sales/PaymentReceives/CreatePaymentReceive.ts +++ b/packages/server/src/services/Sales/PaymentReceived/CreatePaymentReceived.ts @@ -2,23 +2,23 @@ import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; import { ICustomer, - IPaymentReceiveCreateDTO, - IPaymentReceiveCreatedPayload, - IPaymentReceiveCreatingPayload, + IPaymentReceivedCreateDTO, + IPaymentReceivedCreatedPayload, + IPaymentReceivedCreatingPayload, ISystemUser, } from '@/interfaces'; -import { PaymentReceiveValidators } from './PaymentReceiveValidators'; +import { PaymentReceivedValidators } from './PaymentReceivedValidators'; import events from '@/subscribers/events'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import UnitOfWork from '@/services/UnitOfWork'; -import { PaymentReceiveDTOTransformer } from './PaymentReceiveDTOTransformer'; +import { PaymentReceiveDTOTransformer } from './PaymentReceivedDTOTransformer'; import { TenantMetadata } from '@/system/models'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; @Service() -export class CreatePaymentReceive { +export class CreatePaymentReceived { @Inject() - private validators: PaymentReceiveValidators; + private validators: PaymentReceivedValidators; @Inject() private eventPublisher: EventPublisher; @@ -37,11 +37,11 @@ export class CreatePaymentReceive { * with associated invoices payment and journal transactions. * @async * @param {number} tenantId - Tenant id. - * @param {IPaymentReceive} paymentReceive + * @param {IPaymentReceived} paymentReceive */ - public async createPaymentReceive( + public async createPaymentReceived( tenantId: number, - paymentReceiveDTO: IPaymentReceiveCreateDTO, + paymentReceiveDTO: IPaymentReceivedCreateDTO, authorizedUser: ISystemUser, trx?: Knex.Transaction ) { @@ -97,7 +97,7 @@ export class CreatePaymentReceive { trx, paymentReceiveDTO, tenantId, - } as IPaymentReceiveCreatingPayload); + } as IPaymentReceivedCreatingPayload); // Inserts the payment receive transaction. const paymentReceive = await PaymentReceive.query( @@ -113,7 +113,7 @@ export class CreatePaymentReceive { paymentReceiveDTO, authorizedUser, trx, - } as IPaymentReceiveCreatedPayload); + } as IPaymentReceivedCreatedPayload); return paymentReceive; }, @@ -125,13 +125,13 @@ export class CreatePaymentReceive { * Transform the create payment receive DTO. * @param {number} tenantId * @param {ICustomer} customer - * @param {IPaymentReceiveCreateDTO} paymentReceiveDTO + * @param {IPaymentReceivedCreateDTO} paymentReceiveDTO * @returns */ private transformCreateDTOToModel = async ( tenantId: number, customer: ICustomer, - paymentReceiveDTO: IPaymentReceiveCreateDTO + paymentReceiveDTO: IPaymentReceivedCreateDTO ) => { return this.transformer.transformPaymentReceiveDTOToModel( tenantId, diff --git a/packages/server/src/services/Sales/PaymentReceives/DeletePaymentReceive.ts b/packages/server/src/services/Sales/PaymentReceived/DeletePaymentReceived.ts similarity index 89% rename from packages/server/src/services/Sales/PaymentReceives/DeletePaymentReceive.ts rename to packages/server/src/services/Sales/PaymentReceived/DeletePaymentReceived.ts index 3a7d211a7..d1bc62590 100644 --- a/packages/server/src/services/Sales/PaymentReceives/DeletePaymentReceive.ts +++ b/packages/server/src/services/Sales/PaymentReceived/DeletePaymentReceived.ts @@ -1,8 +1,8 @@ import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; import { - IPaymentReceiveDeletedPayload, - IPaymentReceiveDeletingPayload, + IPaymentReceivedDeletedPayload, + IPaymentReceivedDeletingPayload, ISystemUser, } from '@/interfaces'; import UnitOfWork from '@/services/UnitOfWork'; @@ -11,7 +11,7 @@ import events from '@/subscribers/events'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; @Service() -export class DeletePaymentReceive { +export class DeletePaymentReceived { @Inject() private eventPublisher: EventPublisher; @@ -33,7 +33,7 @@ export class DeletePaymentReceive { * @async * @param {number} tenantId - Tenant id. * @param {Integer} paymentReceiveId - Payment receive id. - * @param {IPaymentReceive} paymentReceive - Payment receive object. + * @param {IPaymentReceived} paymentReceive - Payment receive object. */ public async deletePaymentReceive( tenantId: number, @@ -56,7 +56,7 @@ export class DeletePaymentReceive { tenantId, oldPaymentReceive, trx, - } as IPaymentReceiveDeletingPayload); + } as IPaymentReceivedDeletingPayload); // Deletes the payment receive associated entries. await PaymentReceiveEntry.query(trx) @@ -73,7 +73,7 @@ export class DeletePaymentReceive { oldPaymentReceive, authorizedUser, trx, - } as IPaymentReceiveDeletedPayload); + } as IPaymentReceivedDeletedPayload); }); } } diff --git a/packages/server/src/services/Sales/PaymentReceives/EditPaymentReceive.ts b/packages/server/src/services/Sales/PaymentReceived/EditPaymentReceived.ts similarity index 87% rename from packages/server/src/services/Sales/PaymentReceives/EditPaymentReceive.ts rename to packages/server/src/services/Sales/PaymentReceived/EditPaymentReceived.ts index b0f757baa..1e508c094 100644 --- a/packages/server/src/services/Sales/PaymentReceives/EditPaymentReceive.ts +++ b/packages/server/src/services/Sales/PaymentReceived/EditPaymentReceived.ts @@ -2,14 +2,14 @@ import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; import { ICustomer, - IPaymentReceive, - IPaymentReceiveEditDTO, - IPaymentReceiveEditedPayload, - IPaymentReceiveEditingPayload, + IPaymentReceived, + IPaymentReceivedEditDTO, + IPaymentReceivedEditedPayload, + IPaymentReceivedEditingPayload, ISystemUser, } from '@/interfaces'; -import { PaymentReceiveDTOTransformer } from './PaymentReceiveDTOTransformer'; -import { PaymentReceiveValidators } from './PaymentReceiveValidators'; +import { PaymentReceiveDTOTransformer } from './PaymentReceivedDTOTransformer'; +import { PaymentReceivedValidators } from './PaymentReceivedValidators'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import events from '@/subscribers/events'; import UnitOfWork from '@/services/UnitOfWork'; @@ -17,12 +17,12 @@ import HasTenancyService from '@/services/Tenancy/TenancyService'; import { TenantMetadata } from '@/system/models'; @Service() -export class EditPaymentReceive { +export class EditPaymentReceived { @Inject() private transformer: PaymentReceiveDTOTransformer; @Inject() - private validators: PaymentReceiveValidators; + private validators: PaymentReceivedValidators; @Inject() private eventPublisher: EventPublisher; @@ -46,12 +46,12 @@ export class EditPaymentReceive { * @async * @param {number} tenantId - * @param {Integer} paymentReceiveId - - * @param {IPaymentReceive} paymentReceive - + * @param {IPaymentReceived} paymentReceive - */ public async editPaymentReceive( tenantId: number, paymentReceiveId: number, - paymentReceiveDTO: IPaymentReceiveEditDTO, + paymentReceiveDTO: IPaymentReceivedEditDTO, authorizedUser: ISystemUser ) { const { PaymentReceive, Contact } = this.tenancy.models(tenantId); @@ -131,7 +131,7 @@ export class EditPaymentReceive { tenantId, oldPaymentReceive, paymentReceiveDTO, - } as IPaymentReceiveEditingPayload); + } as IPaymentReceivedEditingPayload); // Update the payment receive transaction. const paymentReceive = await PaymentReceive.query( @@ -149,7 +149,7 @@ export class EditPaymentReceive { paymentReceiveDTO, authorizedUser, trx, - } as IPaymentReceiveEditedPayload); + } as IPaymentReceivedEditedPayload); return paymentReceive; }); @@ -159,15 +159,15 @@ export class EditPaymentReceive { * Transform the edit payment receive DTO. * @param {number} tenantId * @param {ICustomer} customer - * @param {IPaymentReceiveEditDTO} paymentReceiveDTO - * @param {IPaymentReceive} oldPaymentReceive + * @param {IPaymentReceivedEditDTO} paymentReceiveDTO + * @param {IPaymentReceived} oldPaymentReceive * @returns */ private transformEditDTOToModel = async ( tenantId: number, customer: ICustomer, - paymentReceiveDTO: IPaymentReceiveEditDTO, - oldPaymentReceive: IPaymentReceive + paymentReceiveDTO: IPaymentReceivedEditDTO, + oldPaymentReceive: IPaymentReceived ) => { return this.transformer.transformPaymentReceiveDTOToModel( tenantId, diff --git a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReceive.ts b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceived.ts similarity index 83% rename from packages/server/src/services/Sales/PaymentReceives/GetPaymentReceive.ts rename to packages/server/src/services/Sales/PaymentReceived/GetPaymentReceived.ts index c89697588..36ba5c06b 100644 --- a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReceive.ts +++ b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceived.ts @@ -1,13 +1,13 @@ import { ServiceError } from '@/exceptions'; -import { IPaymentReceive } from '@/interfaces'; +import { IPaymentReceived } from '@/interfaces'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { Inject, Service } from 'typedi'; import { ERRORS } from './constants'; -import { PaymentReceiveTransfromer } from './PaymentReceiveTransformer'; +import { PaymentReceiveTransfromer } from './PaymentReceivedTransformer'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; @Service() -export class GetPaymentReceive { +export class GetPaymentReceived { @Inject() private tenancy: HasTenancyService; @@ -18,12 +18,12 @@ export class GetPaymentReceive { * Retrieve payment receive details. * @param {number} tenantId - Tenant id. * @param {number} paymentReceiveId - Payment receive id. - * @return {Promise} + * @return {Promise} */ public async getPaymentReceive( tenantId: number, paymentReceiveId: number - ): Promise { + ): Promise { const { PaymentReceive } = this.tenancy.models(tenantId); const paymentReceive = await PaymentReceive.query() diff --git a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReceiveInvoices.ts b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedInvoices.ts similarity index 86% rename from packages/server/src/services/Sales/PaymentReceives/GetPaymentReceiveInvoices.ts rename to packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedInvoices.ts index 901c6b492..d573406ce 100644 --- a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReceiveInvoices.ts +++ b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedInvoices.ts @@ -1,14 +1,14 @@ import { Inject, Service } from 'typedi'; import HasTenancyService from '@/services/Tenancy/TenancyService'; -import { PaymentReceiveValidators } from './PaymentReceiveValidators'; +import { PaymentReceivedValidators } from './PaymentReceivedValidators'; @Service() -export class GetPaymentReceiveInvoices { +export class GetPaymentReceivedInvoices { @Inject() private tenancy: HasTenancyService; @Inject() - private validators: PaymentReceiveValidators; + private validators: PaymentReceivedValidators; /** * Retrieve sale invoices that assocaited to the given payment receive. diff --git a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReeceivePdf.ts b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedPdf.ts similarity index 83% rename from packages/server/src/services/Sales/PaymentReceives/GetPaymentReeceivePdf.ts rename to packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedPdf.ts index e3d3cfb26..0a357e5ea 100644 --- a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReeceivePdf.ts +++ b/packages/server/src/services/Sales/PaymentReceived/GetPaymentReceivedPdf.ts @@ -1,10 +1,10 @@ import { Inject, Service } from 'typedi'; import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy'; import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable'; -import { GetPaymentReceive } from './GetPaymentReceive'; +import { GetPaymentReceived } from './GetPaymentReceived'; @Service() -export default class GetPaymentReceivePdf { +export default class GetPaymentReceivedPdf { @Inject() private chromiumlyTenancy: ChromiumlyTenancy; @@ -12,12 +12,12 @@ export default class GetPaymentReceivePdf { private templateInjectable: TemplateInjectable; @Inject() - private getPaymentService: GetPaymentReceive; + private getPaymentService: GetPaymentReceived; /** * Retrieve sale invoice pdf content. * @param {number} tenantId - - * @param {IPaymentReceive} paymentReceive - + * @param {IPaymentReceived} paymentReceive - * @returns {Promise} */ async getPaymentReceivePdf( diff --git a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReceives.ts b/packages/server/src/services/Sales/PaymentReceived/GetPaymentsReceived.ts similarity index 88% rename from packages/server/src/services/Sales/PaymentReceives/GetPaymentReceives.ts rename to packages/server/src/services/Sales/PaymentReceived/GetPaymentsReceived.ts index 3c076b442..4ff97f7ed 100644 --- a/packages/server/src/services/Sales/PaymentReceives/GetPaymentReceives.ts +++ b/packages/server/src/services/Sales/PaymentReceived/GetPaymentsReceived.ts @@ -3,10 +3,10 @@ import * as R from 'ramda'; import { IFilterMeta, IPaginationMeta, - IPaymentReceive, - IPaymentReceivesFilter, + IPaymentReceived, + IPaymentsReceivedFilter, } from '@/interfaces'; -import { PaymentReceiveTransfromer } from './PaymentReceiveTransformer'; +import { PaymentReceiveTransfromer } from './PaymentReceivedTransformer'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import DynamicListingService from '@/services/DynamicListing/DynamicListService'; @@ -25,13 +25,13 @@ export class GetPaymentReceives { /** * Retrieve payment receives paginated and filterable list. * @param {number} tenantId - * @param {IPaymentReceivesFilter} paymentReceivesFilter + * @param {IPaymentsReceivedFilter} paymentReceivesFilter */ public async getPaymentReceives( tenantId: number, - filterDTO: IPaymentReceivesFilter + filterDTO: IPaymentsReceivedFilter ): Promise<{ - paymentReceives: IPaymentReceive[]; + paymentReceives: IPaymentReceived[]; pagination: IPaginationMeta; filterMeta: IFilterMeta; }> { diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceivesApplication.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts similarity index 69% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceivesApplication.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts index 0d5669bf8..27e92f69e 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceivesApplication.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedApplication.ts @@ -1,44 +1,44 @@ import { IFilterMeta, IPaginationMeta, - IPaymentReceive, - IPaymentReceiveCreateDTO, - IPaymentReceiveEditDTO, - IPaymentReceiveSmsDetails, - IPaymentReceivesFilter, + IPaymentReceived, + IPaymentReceivedCreateDTO, + IPaymentReceivedEditDTO, + IPaymentReceivedSmsDetails, + IPaymentsReceivedFilter, ISystemUser, PaymentReceiveMailOptsDTO, } from '@/interfaces'; import { Inject, Service } from 'typedi'; -import { CreatePaymentReceive } from './CreatePaymentReceive'; -import { EditPaymentReceive } from './EditPaymentReceive'; -import { DeletePaymentReceive } from './DeletePaymentReceive'; -import { GetPaymentReceives } from './GetPaymentReceives'; -import { GetPaymentReceive } from './GetPaymentReceive'; -import { GetPaymentReceiveInvoices } from './GetPaymentReceiveInvoices'; -import { PaymentReceiveNotifyBySms } from './PaymentReceiveSmsNotify'; -import GetPaymentReceivePdf from './GetPaymentReeceivePdf'; -import { SendPaymentReceiveMailNotification } from './PaymentReceiveMailNotification'; +import { CreatePaymentReceived } from './CreatePaymentReceived'; +import { EditPaymentReceived } from './EditPaymentReceived'; +import { DeletePaymentReceived } from './DeletePaymentReceived'; +import { GetPaymentReceives } from './GetPaymentsReceived'; +import { GetPaymentReceived } from './GetPaymentReceived'; +import { GetPaymentReceivedInvoices } from './GetPaymentReceivedInvoices'; +import { PaymentReceiveNotifyBySms } from './PaymentReceivedSmsNotify'; +import GetPaymentReceivedPdf from './GetPaymentReceivedPdf'; +import { SendPaymentReceiveMailNotification } from './PaymentReceivedMailNotification'; @Service() export class PaymentReceivesApplication { @Inject() - private createPaymentReceiveService: CreatePaymentReceive; + private createPaymentReceivedService: CreatePaymentReceived; @Inject() - private editPaymentReceiveService: EditPaymentReceive; + private editPaymentReceivedService: EditPaymentReceived; @Inject() - private deletePaymentReceiveService: DeletePaymentReceive; + private deletePaymentReceivedService: DeletePaymentReceived; @Inject() - private getPaymentReceivesService: GetPaymentReceives; + private getPaymentsReceivedService: GetPaymentReceives; @Inject() - private getPaymentReceiveService: GetPaymentReceive; + private getPaymentReceivedService: GetPaymentReceived; @Inject() - private getPaymentReceiveInvoicesService: GetPaymentReceiveInvoices; + private getPaymentReceiveInvoicesService: GetPaymentReceivedInvoices; @Inject() private paymentSmsNotify: PaymentReceiveNotifyBySms; @@ -47,21 +47,21 @@ export class PaymentReceivesApplication { private paymentMailNotify: SendPaymentReceiveMailNotification; @Inject() - private getPaymentReceivePdfService: GetPaymentReceivePdf; + private getPaymentReceivePdfService: GetPaymentReceivedPdf; /** * Creates a new payment receive. * @param {number} tenantId - * @param {IPaymentReceiveCreateDTO} paymentReceiveDTO + * @param {IPaymentReceivedCreateDTO} paymentReceiveDTO * @param {ISystemUser} authorizedUser * @returns */ - public createPaymentReceive( + public createPaymentReceived( tenantId: number, - paymentReceiveDTO: IPaymentReceiveCreateDTO, + paymentReceiveDTO: IPaymentReceivedCreateDTO, authorizedUser: ISystemUser ) { - return this.createPaymentReceiveService.createPaymentReceive( + return this.createPaymentReceivedService.createPaymentReceived( tenantId, paymentReceiveDTO, authorizedUser @@ -72,17 +72,17 @@ export class PaymentReceivesApplication { * Edit details the given payment receive with associated entries. * @param {number} tenantId * @param {number} paymentReceiveId - * @param {IPaymentReceiveEditDTO} paymentReceiveDTO + * @param {IPaymentReceivedEditDTO} paymentReceiveDTO * @param {ISystemUser} authorizedUser * @returns */ public editPaymentReceive( tenantId: number, paymentReceiveId: number, - paymentReceiveDTO: IPaymentReceiveEditDTO, + paymentReceiveDTO: IPaymentReceivedEditDTO, authorizedUser: ISystemUser ) { - return this.editPaymentReceiveService.editPaymentReceive( + return this.editPaymentReceivedService.editPaymentReceive( tenantId, paymentReceiveId, paymentReceiveDTO, @@ -102,7 +102,7 @@ export class PaymentReceivesApplication { paymentReceiveId: number, authorizedUser: ISystemUser ) { - return this.deletePaymentReceiveService.deletePaymentReceive( + return this.deletePaymentReceivedService.deletePaymentReceive( tenantId, paymentReceiveId, authorizedUser @@ -112,18 +112,18 @@ export class PaymentReceivesApplication { /** * Retrieve payment receives paginated and filterable. * @param {number} tenantId - * @param {IPaymentReceivesFilter} filterDTO + * @param {IPaymentsReceivedFilter} filterDTO * @returns */ public async getPaymentReceives( tenantId: number, - filterDTO: IPaymentReceivesFilter + filterDTO: IPaymentsReceivedFilter ): Promise<{ - paymentReceives: IPaymentReceive[]; + paymentReceives: IPaymentReceived[]; pagination: IPaginationMeta; filterMeta: IFilterMeta; }> { - return this.getPaymentReceivesService.getPaymentReceives( + return this.getPaymentsReceivedService.getPaymentReceives( tenantId, filterDTO ); @@ -133,13 +133,13 @@ export class PaymentReceivesApplication { * Retrieves the given payment receive. * @param {number} tenantId * @param {number} paymentReceiveId - * @returns {Promise} + * @returns {Promise} */ public async getPaymentReceive( tenantId: number, paymentReceiveId: number - ): Promise { - return this.getPaymentReceiveService.getPaymentReceive( + ): Promise { + return this.getPaymentReceivedService.getPaymentReceive( tenantId, paymentReceiveId ); @@ -175,7 +175,7 @@ export class PaymentReceivesApplication { public getPaymentSmsDetails = async ( tenantId: number, paymentReceiveId: number - ): Promise => { + ): Promise => { return this.paymentSmsNotify.smsDetails(tenantId, paymentReceiveId); }; diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveDTOTransformer.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedDTOTransformer.ts similarity index 69% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveDTOTransformer.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedDTOTransformer.ts index a84569ac9..e9eed9110 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveDTOTransformer.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedDTOTransformer.ts @@ -3,22 +3,22 @@ import { Inject, Service } from 'typedi'; import { omit, sumBy } from 'lodash'; import { ICustomer, - IPaymentReceive, - IPaymentReceiveCreateDTO, - IPaymentReceiveEditDTO, + IPaymentReceived, + IPaymentReceivedCreateDTO, + IPaymentReceivedEditDTO, } from '@/interfaces'; -import { PaymentReceiveValidators } from './PaymentReceiveValidators'; -import { PaymentReceiveIncrement } from './PaymentReceiveIncrement'; +import { PaymentReceivedValidators } from './PaymentReceivedValidators'; +import { PaymentReceivedIncrement } from './PaymentReceivedIncrement'; import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform'; import { formatDateFields } from '@/utils'; @Service() export class PaymentReceiveDTOTransformer { @Inject() - private validators: PaymentReceiveValidators; + private validators: PaymentReceivedValidators; @Inject() - private increments: PaymentReceiveIncrement; + private increments: PaymentReceivedIncrement; @Inject() private branchDTOTransform: BranchTransactionDTOTransform; @@ -26,16 +26,16 @@ export class PaymentReceiveDTOTransformer { /** * Transformes the create payment receive DTO to model object. * @param {number} tenantId - * @param {IPaymentReceiveCreateDTO|IPaymentReceiveEditDTO} paymentReceiveDTO - Payment receive DTO. - * @param {IPaymentReceive} oldPaymentReceive - - * @return {IPaymentReceive} + * @param {IPaymentReceivedCreateDTO|IPaymentReceivedEditDTO} paymentReceiveDTO - Payment receive DTO. + * @param {IPaymentReceived} oldPaymentReceive - + * @return {IPaymentReceived} */ public async transformPaymentReceiveDTOToModel( tenantId: number, customer: ICustomer, - paymentReceiveDTO: IPaymentReceiveCreateDTO | IPaymentReceiveEditDTO, - oldPaymentReceive?: IPaymentReceive - ): Promise { + paymentReceiveDTO: IPaymentReceivedCreateDTO | IPaymentReceivedEditDTO, + oldPaymentReceive?: IPaymentReceived + ): Promise { const amount = paymentReceiveDTO.amount ?? sumBy(paymentReceiveDTO.entries, 'paymentAmount'); @@ -65,7 +65,7 @@ export class PaymentReceiveDTOTransformer { })), }; return R.compose( - this.branchDTOTransform.transformDTO(tenantId) + this.branchDTOTransform.transformDTO(tenantId) )(initialDTO); } } diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveEntryTransformer.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedEntryTransformer.ts similarity index 91% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveEntryTransformer.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedEntryTransformer.ts index 7a7af28c1..b20d8801d 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveEntryTransformer.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedEntryTransformer.ts @@ -2,7 +2,7 @@ import { Transformer } from '@/lib/Transformer/Transformer'; import { SaleInvoiceTransformer } from '../Invoices/SaleInvoiceTransformer'; import { formatNumber } from '@/utils'; -export class PaymentReceiveEntryTransfromer extends Transformer { +export class PaymentReceivedEntryTransfromer extends Transformer { /** * Include these attributes to payment receive entry object. * @returns {Array} diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveGLEntries.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedGLEntries.ts similarity index 92% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveGLEntries.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedGLEntries.ts index e804733c0..8c30c10f4 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveGLEntries.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedGLEntries.ts @@ -4,7 +4,7 @@ import { Knex } from 'knex'; import Ledger from '@/services/Accounting/Ledger'; import TenancyService from '@/services/Tenancy/TenancyService'; import { - IPaymentReceive, + IPaymentReceived, ILedgerEntry, AccountNormal, IPaymentReceiveGLCommonEntry, @@ -13,7 +13,7 @@ import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; import { TenantMetadata } from '@/system/models'; @Service() -export class PaymentReceiveGLEntries { +export class PaymentReceivedGLEntries { @Inject() private tenancy: TenancyService; @@ -93,14 +93,14 @@ export class PaymentReceiveGLEntries { /** * Retrieves the payment receive general ledger. * @param {number} tenantId - - * @param {IPaymentReceive} paymentReceive - + * @param {IPaymentReceived} paymentReceive - * @param {string} baseCurrencyCode - * @param {Knex.Transaction} trx - * @returns {Ledger} */ public getPaymentReceiveGLedger = async ( tenantId: number, - paymentReceive: IPaymentReceive, + paymentReceive: IPaymentReceived, baseCurrencyCode: string, trx?: Knex.Transaction ): Promise => { @@ -132,7 +132,7 @@ export class PaymentReceiveGLEntries { * @returns {number} */ private getPaymentExGainOrLoss = ( - paymentReceive: IPaymentReceive + paymentReceive: IPaymentReceived ): number => { return sumBy(paymentReceive.entries, (entry) => { const paymentLocalAmount = @@ -145,11 +145,11 @@ export class PaymentReceiveGLEntries { /** * Retrieves the common entry of payment receive. - * @param {IPaymentReceive} paymentReceive + * @param {IPaymentReceived} paymentReceive * @returns {} */ private getPaymentReceiveCommonEntry = ( - paymentReceive: IPaymentReceive + paymentReceive: IPaymentReceived ): IPaymentReceiveGLCommonEntry => { return { debit: 0, @@ -174,14 +174,14 @@ export class PaymentReceiveGLEntries { /** * Retrieves the payment exchange gain/loss entry. - * @param {IPaymentReceive} paymentReceive - + * @param {IPaymentReceived} paymentReceive - * @param {number} ARAccountId - * @param {number} exchangeGainOrLossAccountId - * @param {string} baseCurrencyCode - * @returns {ILedgerEntry[]} */ private getPaymentExchangeGainLossEntry = ( - paymentReceive: IPaymentReceive, + paymentReceive: IPaymentReceived, ARAccountId: number, exchangeGainOrLossAccountId: number, baseCurrencyCode: string @@ -219,11 +219,11 @@ export class PaymentReceiveGLEntries { /** * Retrieves the payment deposit GL entry. - * @param {IPaymentReceive} paymentReceive + * @param {IPaymentReceived} paymentReceive * @returns {ILedgerEntry} */ private getPaymentDepositGLEntry = ( - paymentReceive: IPaymentReceive + paymentReceive: IPaymentReceived ): ILedgerEntry => { const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive); @@ -238,12 +238,12 @@ export class PaymentReceiveGLEntries { /** * Retrieves the payment receivable entry. - * @param {IPaymentReceive} paymentReceive + * @param {IPaymentReceived} paymentReceive * @param {number} ARAccountId * @returns {ILedgerEntry} */ private getPaymentReceivableEntry = ( - paymentReceive: IPaymentReceive, + paymentReceive: IPaymentReceived, ARAccountId: number ): ILedgerEntry => { const commonJournal = this.getPaymentReceiveCommonEntry(paymentReceive); @@ -267,14 +267,14 @@ export class PaymentReceiveGLEntries { * - Payment account [current asset] -> Credit * * @param {number} tenantId - * @param {IPaymentReceive} paymentRecieve - Payment receive model. + * @param {IPaymentReceived} paymentRecieve - Payment receive model. * @param {number} ARAccountId - A/R account id. * @param {number} exGainOrLossAccountId - Exchange gain/loss account id. * @param {string} baseCurrency - Base currency code. * @returns {Promise} */ public getPaymentReceiveGLEntries = ( - paymentReceive: IPaymentReceive, + paymentReceive: IPaymentReceived, ARAccountId: number, exGainOrLossAccountId: number, baseCurrency: string diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveIncrement.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedIncrement.ts similarity index 95% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveIncrement.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedIncrement.ts index 804fb1fbc..01cef7f48 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveIncrement.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedIncrement.ts @@ -2,7 +2,7 @@ import { Inject, Service } from 'typedi'; import AutoIncrementOrdersService from '../AutoIncrementOrdersService'; @Service() -export class PaymentReceiveIncrement { +export class PaymentReceivedIncrement { @Inject() private autoIncrementOrdersService: AutoIncrementOrdersService; diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveInvoiceSync.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedInvoiceSync.ts similarity index 84% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveInvoiceSync.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedInvoiceSync.ts index 0f7831237..4cc51e60c 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveInvoiceSync.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedInvoiceSync.ts @@ -1,11 +1,11 @@ import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; -import { IPaymentReceiveEntryDTO } from '@/interfaces'; +import { IPaymentReceivedEntryDTO } from '@/interfaces'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { entriesAmountDiff } from '@/utils'; @Service() -export class PaymentReceiveInvoiceSync { +export class PaymentReceivedInvoiceSync { @Inject() private tenancy: HasTenancyService; @@ -19,8 +19,8 @@ export class PaymentReceiveInvoiceSync { */ public async saveChangeInvoicePaymentAmount( tenantId: number, - newPaymentReceiveEntries: IPaymentReceiveEntryDTO[], - oldPaymentReceiveEntries?: IPaymentReceiveEntryDTO[], + newPaymentReceiveEntries: IPaymentReceivedEntryDTO[], + oldPaymentReceiveEntries?: IPaymentReceivedEntryDTO[], trx?: Knex.Transaction ): Promise { const { SaleInvoice } = this.tenancy.models(tenantId); diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts similarity index 97% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts index bd8d4fa64..5429b4441 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotification.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotification.ts @@ -11,7 +11,7 @@ import { DEFAULT_PAYMENT_MAIL_CONTENT, DEFAULT_PAYMENT_MAIL_SUBJECT, } from './constants'; -import { GetPaymentReceive } from './GetPaymentReceive'; +import { GetPaymentReceived } from './GetPaymentReceived'; import { ContactMailNotification } from '@/services/MailNotification/ContactMailNotification'; import { parseAndValidateMailOptions } from '@/services/MailNotification/utils'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; @@ -23,7 +23,7 @@ export class SendPaymentReceiveMailNotification { private tenancy: HasTenancyService; @Inject() - private getPaymentService: GetPaymentReceive; + private getPaymentService: GetPaymentReceived; @Inject() private contactMailNotification: ContactMailNotification; diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotificationJob.ts similarity index 91% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotificationJob.ts index b29570d42..4614ce9b4 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedMailNotificationJob.ts @@ -1,8 +1,8 @@ import Container, { Service } from 'typedi'; -import { SendPaymentReceiveMailNotification } from './PaymentReceiveMailNotification'; +import { SendPaymentReceiveMailNotification } from './PaymentReceivedMailNotification'; @Service() -export class PaymentReceiveMailNotificationJob { +export class PaymentReceivedMailNotificationJob { /** * Constructor method. */ diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveSmsNotify.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedSmsNotify.ts similarity index 91% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveSmsNotify.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedSmsNotify.ts index 67810aa85..961bec572 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveSmsNotify.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedSmsNotify.ts @@ -2,17 +2,17 @@ import { Service, Inject } from 'typedi'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import events from '@/subscribers/events'; import { - IPaymentReceiveSmsDetails, + IPaymentReceivedSmsDetails, SMS_NOTIFICATION_KEY, - IPaymentReceive, - IPaymentReceiveEntry, + IPaymentReceived, + IPaymentReceivedEntry, } from '@/interfaces'; import SmsNotificationsSettingsService from '@/services/Settings/SmsNotificationsSettings'; import { formatNumber, formatSmsMessage } from 'utils'; import { TenantMetadata } from '@/system/models'; import SaleNotifyBySms from '../SaleNotifyBySms'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; -import { PaymentReceiveValidators } from './PaymentReceiveValidators'; +import { PaymentReceivedValidators } from './PaymentReceivedValidators'; @Service() export class PaymentReceiveNotifyBySms { @@ -29,7 +29,7 @@ export class PaymentReceiveNotifyBySms { private saleSmsNotification: SaleNotifyBySms; @Inject() - private validators: PaymentReceiveValidators; + private validators: PaymentReceivedValidators; /** * Notify customer via sms about payment receive details. @@ -71,12 +71,12 @@ export class PaymentReceiveNotifyBySms { /** * Sends the payment details sms notification of the given customer. * @param {number} tenantId - * @param {IPaymentReceive} paymentReceive + * @param {IPaymentReceived} paymentReceive * @param {ICustomer} customer */ private sendSmsNotification = async ( tenantId: number, - paymentReceive: IPaymentReceive + paymentReceive: IPaymentReceived ) => { const smsClient = this.tenancy.smsClient(tenantId); const tenantMetadata = await TenantMetadata.query().findOne({ tenantId }); @@ -116,12 +116,12 @@ export class PaymentReceiveNotifyBySms { /** * Formates the payment receive details sms message. * @param {number} tenantId - - * @param {IPaymentReceive} payment - + * @param {IPaymentReceived} payment - * @param {ICustomer} customer - */ private formattedPaymentDetailsMessage = ( tenantId: number, - payment: IPaymentReceive, + payment: IPaymentReceived, tenantMetadata: TenantMetadata ) => { const notification = this.smsNotificationsSettings.getSmsNotificationMeta( @@ -138,14 +138,14 @@ export class PaymentReceiveNotifyBySms { /** * Formattes the payment details sms notification messafge. * @param {string} smsMessage - * @param {IPaymentReceive} payment + * @param {IPaymentReceived} payment * @param {ICustomer} customer * @param {TenantMetadata} tenantMetadata * @returns {string} */ private formatPaymentDetailsMessage = ( smsMessage: string, - payment: IPaymentReceive, + payment: IPaymentReceived, tenantMetadata: any ): string => { const invoiceNumbers = this.stringifyPaymentInvoicesNumber(payment); @@ -167,12 +167,12 @@ export class PaymentReceiveNotifyBySms { /** * Stringify payment receive invoices to numbers as string. - * @param {IPaymentReceive} payment + * @param {IPaymentReceived} payment * @returns {string} */ - private stringifyPaymentInvoicesNumber(payment: IPaymentReceive) { + private stringifyPaymentInvoicesNumber(payment: IPaymentReceived) { const invoicesNumberes = payment.entries.map( - (entry: IPaymentReceiveEntry) => entry.invoice.invoiceNo + (entry: IPaymentReceivedEntry) => entry.invoice.invoiceNo ); return invoicesNumberes.join(', '); } @@ -185,7 +185,7 @@ export class PaymentReceiveNotifyBySms { public smsDetails = async ( tenantId: number, paymentReceiveid: number - ): Promise => { + ): Promise => { const { PaymentReceive } = this.tenancy.models(tenantId); // Retrieve the payment receive or throw not found service error. diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveSmsSubscriber.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedSmsSubscriber.ts similarity index 81% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveSmsSubscriber.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedSmsSubscriber.ts index 3793ca5ed..c6f4af9e3 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveSmsSubscriber.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedSmsSubscriber.ts @@ -1,8 +1,7 @@ import { Container } from 'typedi'; import { On, EventSubscriber } from 'event-dispatch'; import events from '@/subscribers/events'; -import SaleReceiptNotifyBySms from '@/services/Sales/SaleReceiptNotifyBySms'; -import PaymentReceiveNotifyBySms from './PaymentReceiveSmsNotify'; +import { PaymentReceiveNotifyBySms } from './PaymentReceivedSmsNotify'; @EventSubscriber() export default class SendSmsNotificationPaymentReceive { diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveTransformer.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedTransformer.ts similarity index 62% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveTransformer.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedTransformer.ts index eaf4dfe9e..2ab49dfe7 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveTransformer.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedTransformer.ts @@ -1,7 +1,7 @@ -import { IPaymentReceive, IPaymentReceiveEntry } from '@/interfaces'; +import { IPaymentReceived, IPaymentReceivedEntry } from '@/interfaces'; import { Transformer } from '@/lib/Transformer/Transformer'; import { formatNumber } from 'utils'; -import { PaymentReceiveEntryTransfromer } from './PaymentReceiveEntryTransformer'; +import { PaymentReceivedEntryTransfromer } from './PaymentReceivedEntryTransformer'; export class PaymentReceiveTransfromer extends Transformer { /** @@ -24,25 +24,25 @@ export class PaymentReceiveTransfromer extends Transformer { * @param {ISaleInvoice} invoice * @returns {String} */ - protected formattedPaymentDate = (payment: IPaymentReceive): string => { + protected formattedPaymentDate = (payment: IPaymentReceived): string => { return this.formatDate(payment.paymentDate); }; /** * Retrieves the formatted created at date. - * @param {IPaymentReceive} payment + * @param {IPaymentReceived} payment * @returns {string} */ - protected formattedCreatedAt = (payment: IPaymentReceive): string => { + protected formattedCreatedAt = (payment: IPaymentReceived): string => { return this.formatDate(payment.createdAt); }; /** * Retrieve the formatted payment subtotal. - * @param {IPaymentReceive} payment + * @param {IPaymentReceived} payment * @returns {string} */ - protected subtotalFormatted = (payment: IPaymentReceive): string => { + protected subtotalFormatted = (payment: IPaymentReceived): string => { return formatNumber(payment.amount, { currencyCode: payment.currencyCode, money: false, @@ -54,25 +54,25 @@ export class PaymentReceiveTransfromer extends Transformer { * @param {ISaleInvoice} invoice * @returns {string} */ - protected formattedAmount = (payment: IPaymentReceive): string => { + protected formattedAmount = (payment: IPaymentReceived): string => { return formatNumber(payment.amount, { currencyCode: payment.currencyCode }); }; /** * Retrieve the formatted exchange rate. - * @param {IPaymentReceive} payment + * @param {IPaymentReceived} payment * @returns {string} */ - protected formattedExchangeRate = (payment: IPaymentReceive): string => { + protected formattedExchangeRate = (payment: IPaymentReceived): string => { return formatNumber(payment.exchangeRate, { money: false }); }; /** * Retrieves the payment entries. - * @param {IPaymentReceive} payment - * @returns {IPaymentReceiveEntry[]} + * @param {IPaymentReceived} payment + * @returns {IPaymentReceivedEntry[]} */ - protected entries = (payment: IPaymentReceive): IPaymentReceiveEntry[] => { - return this.item(payment.entries, new PaymentReceiveEntryTransfromer()); + protected entries = (payment: IPaymentReceived): IPaymentReceivedEntry[] => { + return this.item(payment.entries, new PaymentReceivedEntryTransfromer()); }; } diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveValidators.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedValidators.ts similarity index 90% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceiveValidators.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentReceivedValidators.ts index 35899e057..4adbe8f6d 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceiveValidators.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentReceivedValidators.ts @@ -2,10 +2,10 @@ import { Inject, Service } from 'typedi'; import { difference, sumBy } from 'lodash'; import { IAccount, - IPaymentReceive, - IPaymentReceiveEditDTO, - IPaymentReceiveEntry, - IPaymentReceiveEntryDTO, + IPaymentReceived, + IPaymentReceivedEditDTO, + IPaymentReceivedEntry, + IPaymentReceivedEntryDTO, ISaleInvoice, } from '@/interfaces'; import { ServiceError } from '@/exceptions'; @@ -15,7 +15,7 @@ import { ACCOUNT_TYPE } from '@/data/AccountTypes'; import { PaymentReceive } from '@/models'; @Service() -export class PaymentReceiveValidators { +export class PaymentReceivedValidators { @Inject() private tenancy: HasTenancyService; @@ -58,7 +58,7 @@ export class PaymentReceiveValidators { * Validates the invoices IDs existance. * @param {number} tenantId - * @param {number} customerId - - * @param {IPaymentReceiveEntryDTO[]} paymentReceiveEntries - + * @param {IPaymentReceivedEntryDTO[]} paymentReceiveEntries - */ public async validateInvoicesIDsExistance( tenantId: number, @@ -100,12 +100,12 @@ export class PaymentReceiveValidators { */ public async validateInvoicesPaymentsAmount( tenantId: number, - paymentReceiveEntries: IPaymentReceiveEntryDTO[], - oldPaymentEntries: IPaymentReceiveEntry[] = [] + paymentReceiveEntries: IPaymentReceivedEntryDTO[], + oldPaymentEntries: IPaymentReceivedEntry[] = [] ) { const { SaleInvoice } = this.tenancy.models(tenantId); const invoicesIds = paymentReceiveEntries.map( - (e: IPaymentReceiveEntryDTO) => e.invoiceId + (e: IPaymentReceivedEntryDTO) => e.invoiceId ); const storedInvoices = await SaleInvoice.query().whereIn('id', invoicesIds); @@ -124,7 +124,7 @@ export class PaymentReceiveValidators { const hasWrongPaymentAmount: any[] = []; paymentReceiveEntries.forEach( - (entry: IPaymentReceiveEntryDTO, index: number) => { + (entry: IPaymentReceivedEntryDTO, index: number) => { const entryInvoice = storedInvoicesMap.get(entry.invoiceId); const { dueAmount } = entryInvoice; @@ -140,9 +140,9 @@ export class PaymentReceiveValidators { /** * Validate the payment receive number require. - * @param {IPaymentReceive} paymentReceiveObj + * @param {IPaymentReceived} paymentReceiveObj */ - public validatePaymentReceiveNoRequire(paymentReceiveObj: IPaymentReceive) { + public validatePaymentReceiveNoRequire(paymentReceiveObj: IPaymentReceived) { if (!paymentReceiveObj.paymentReceiveNo) { throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NO_IS_REQUIRED); } @@ -152,12 +152,12 @@ export class PaymentReceiveValidators { * Validate the payment receive entries IDs existance. * @param {number} tenantId * @param {number} paymentReceiveId - * @param {IPaymentReceiveEntryDTO[]} paymentReceiveEntries + * @param {IPaymentReceivedEntryDTO[]} paymentReceiveEntries */ public async validateEntriesIdsExistance( tenantId: number, paymentReceiveId: number, - paymentReceiveEntries: IPaymentReceiveEntryDTO[] + paymentReceiveEntries: IPaymentReceivedEntryDTO[] ) { const { PaymentReceiveEntry } = this.tenancy.models(tenantId); @@ -189,12 +189,12 @@ export class PaymentReceiveValidators { /** * Validate the payment customer whether modified. - * @param {IPaymentReceiveEditDTO} paymentReceiveDTO - * @param {IPaymentReceive} oldPaymentReceive + * @param {IPaymentReceivedEditDTO} paymentReceiveDTO + * @param {IPaymentReceived} oldPaymentReceive */ public validateCustomerNotModified( - paymentReceiveDTO: IPaymentReceiveEditDTO, - oldPaymentReceive: IPaymentReceive + paymentReceiveDTO: IPaymentReceivedEditDTO, + oldPaymentReceive: IPaymentReceived ) { if (paymentReceiveDTO.customerId !== oldPaymentReceive.customerId) { throw new ServiceError(ERRORS.PAYMENT_CUSTOMER_SHOULD_NOT_UPDATE); @@ -230,7 +230,7 @@ export class PaymentReceiveValidators { async getPaymentReceiveOrThrowError( tenantId: number, paymentReceiveId: number - ): Promise { + ): Promise { const { PaymentReceive } = this.tenancy.models(tenantId); const paymentReceive = await PaymentReceive.query() .withGraphFetched('entries') diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentsReceivedExportable.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedExportable.ts similarity index 69% rename from packages/server/src/services/Sales/PaymentReceives/PaymentsReceivedExportable.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedExportable.ts index 932c0f5f3..0a2b62e33 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentsReceivedExportable.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedExportable.ts @@ -1,7 +1,7 @@ import { Inject, Service } from 'typedi'; -import { IAccountsStructureType, IPaymentReceivesFilter } from '@/interfaces'; +import { IAccountsStructureType, IPaymentsReceivedFilter } from '@/interfaces'; import { Exportable } from '@/services/Export/Exportable'; -import { PaymentReceivesApplication } from './PaymentReceivesApplication'; +import { PaymentReceivesApplication } from './PaymentReceivedApplication'; @Service() export class PaymentsReceivedExportable extends Exportable { @@ -11,17 +11,17 @@ export class PaymentsReceivedExportable extends Exportable { /** * Retrieves the accounts data to exportable sheet. * @param {number} tenantId - * @param {IPaymentReceivesFilter} query - + * @param {IPaymentsReceivedFilter} query - * @returns */ - public exportable(tenantId: number, query: IPaymentReceivesFilter) { + public exportable(tenantId: number, query: IPaymentsReceivedFilter) { const parsedQuery = { sortOrder: 'desc', columnSortBy: 'created_at', inactiveMode: false, ...query, structure: IAccountsStructureType.Flat, - } as IPaymentReceivesFilter; + } as IPaymentsReceivedFilter; return this.paymentReceivedApp .getPaymentReceives(tenantId, parsedQuery) diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceivesImportable.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedImportable.ts similarity index 74% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceivesImportable.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedImportable.ts index 540f50824..eeefd6ca0 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceivesImportable.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedImportable.ts @@ -1,14 +1,14 @@ import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; -import { IPaymentReceiveCreateDTO } from '@/interfaces'; +import { IPaymentReceivedCreateDTO } from '@/interfaces'; import { Importable } from '@/services/Import/Importable'; -import { CreatePaymentReceive } from './CreatePaymentReceive'; +import { CreatePaymentReceived } from './CreatePaymentReceived'; import { PaymentsReceiveSampleData } from './constants'; @Service() -export class PaymentReceivesImportable extends Importable { +export class PaymentsReceivedImportable extends Importable { @Inject() - private createPaymentReceiveService: CreatePaymentReceive; + private createPaymentReceiveService: CreatePaymentReceived; /** * Importing to account service. @@ -18,10 +18,10 @@ export class PaymentReceivesImportable extends Importable { */ public importable( tenantId: number, - createPaymentDTO: IPaymentReceiveCreateDTO, + createPaymentDTO: IPaymentReceivedCreateDTO, trx?: Knex.Transaction ) { - return this.createPaymentReceiveService.createPaymentReceive( + return this.createPaymentReceiveService.createPaymentReceived( tenantId, createPaymentDTO, {}, diff --git a/packages/server/src/services/Sales/PaymentReceives/PaymentReceivesPages.ts b/packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedPages.ts similarity index 94% rename from packages/server/src/services/Sales/PaymentReceives/PaymentReceivesPages.ts rename to packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedPages.ts index 72068225c..6fd52882f 100644 --- a/packages/server/src/services/Sales/PaymentReceives/PaymentReceivesPages.ts +++ b/packages/server/src/services/Sales/PaymentReceived/PaymentsReceivedPages.ts @@ -3,7 +3,7 @@ import { omit } from 'lodash'; import { ISaleInvoice, IPaymentReceivePageEntry, - IPaymentReceive, + IPaymentReceived, ISystemUser, } from '@/interfaces'; import TenancyService from '@/services/Tenancy/TenancyService'; @@ -14,12 +14,9 @@ import { ERRORS } from './constants'; * Payment receives edit/new pages service. */ @Service() -export default class PaymentReceivesPages { +export default class PaymentsReceivedPages { @Inject() - tenancy: TenancyService; - - @Inject('logger') - logger: any; + private tenancy: TenancyService; /** * Retrive page invoices entries from the given sale invoices models. @@ -68,7 +65,7 @@ export default class PaymentReceivesPages { tenantId: number, paymentReceiveId: number ): Promise<{ - paymentReceive: Omit; + paymentReceive: Omit; entries: IPaymentReceivePageEntry[]; }> { const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId); diff --git a/packages/server/src/services/Sales/PaymentReceives/constants.ts b/packages/server/src/services/Sales/PaymentReceived/constants.ts similarity index 100% rename from packages/server/src/services/Sales/PaymentReceives/constants.ts rename to packages/server/src/services/Sales/PaymentReceived/constants.ts diff --git a/packages/server/src/services/TaxRates/CommandTaxRatesValidators.ts b/packages/server/src/services/TaxRates/CommandTaxRatesValidators.ts index 9d7c2558e..decd41857 100644 --- a/packages/server/src/services/TaxRates/CommandTaxRatesValidators.ts +++ b/packages/server/src/services/TaxRates/CommandTaxRatesValidators.ts @@ -1,9 +1,10 @@ -import { ServiceError } from '@/exceptions'; +import { Knex } from 'knex'; import { Inject, Service } from 'typedi'; +import { difference } from 'lodash'; +import { ServiceError } from '@/exceptions'; import HasTenancyService from '../Tenancy/TenancyService'; import { IItemEntryDTO, ITaxRate } from '@/interfaces'; import { ERRORS } from './constants'; -import { difference } from 'lodash'; @Service() export class CommandTaxRatesValidators { @@ -44,11 +45,16 @@ export class CommandTaxRatesValidators { * Validates the tax code uniquiness. * @param {number} tenantId * @param {string} taxCode + * @param {Knex.Transaction} trx - */ - public async validateTaxCodeUnique(tenantId: number, taxCode: string) { + public async validateTaxCodeUnique( + tenantId: number, + taxCode: string, + trx?: Knex.Transaction + ) { const { TaxRate } = this.tenancy.models(tenantId); - const foundTaxCode = await TaxRate.query().findOne({ code: taxCode }); + const foundTaxCode = await TaxRate.query(trx).findOne({ code: taxCode }); if (foundTaxCode) { throw new ServiceError(ERRORS.TAX_CODE_NOT_UNIQUE); diff --git a/packages/server/src/services/TaxRates/CreateTaxRate.ts b/packages/server/src/services/TaxRates/CreateTaxRate.ts index a7795a3c7..9da05dded 100644 --- a/packages/server/src/services/TaxRates/CreateTaxRate.ts +++ b/packages/server/src/services/TaxRates/CreateTaxRate.ts @@ -1,3 +1,4 @@ +import { Inject, Service } from 'typedi'; import { Knex } from 'knex'; import { ICreateTaxRateDTO, @@ -7,7 +8,6 @@ import { import UnitOfWork from '../UnitOfWork'; import { EventPublisher } from '@/lib/EventPublisher/EventPublisher'; import HasTenancyService from '../Tenancy/TenancyService'; -import { Inject, Service } from 'typedi'; import events from '@/subscribers/events'; import { CommandTaxRatesValidators } from './CommandTaxRatesValidators'; @@ -32,36 +32,41 @@ export class CreateTaxRate { */ public async createTaxRate( tenantId: number, - createTaxRateDTO: ICreateTaxRateDTO + createTaxRateDTO: ICreateTaxRateDTO, + trx?: Knex.Transaction ) { const { TaxRate } = this.tenancy.models(tenantId); // Validates the tax code uniquiness. await this.validators.validateTaxCodeUnique( tenantId, - createTaxRateDTO.code + createTaxRateDTO.code, + trx ); - return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { - // Triggers `onTaxRateCreating` event. - await this.eventPublisher.emitAsync(events.taxRates.onCreating, { - createTaxRateDTO, - tenantId, - trx, - } as ITaxRateCreatingPayload); + return this.uow.withTransaction( + tenantId, + async (trx: Knex.Transaction) => { + // Triggers `onTaxRateCreating` event. + await this.eventPublisher.emitAsync(events.taxRates.onCreating, { + createTaxRateDTO, + tenantId, + trx, + } as ITaxRateCreatingPayload); - const taxRate = await TaxRate.query(trx).insertAndFetch({ - ...createTaxRateDTO, - }); + const taxRate = await TaxRate.query(trx).insertAndFetch({ + ...createTaxRateDTO, + }); + // Triggers `onTaxRateCreated` event. + await this.eventPublisher.emitAsync(events.taxRates.onCreated, { + createTaxRateDTO, + taxRate, + tenantId, + trx, + } as ITaxRateCreatedPayload); - // Triggers `onTaxRateCreated` event. - await this.eventPublisher.emitAsync(events.taxRates.onCreated, { - createTaxRateDTO, - taxRate, - tenantId, - trx, - } as ITaxRateCreatedPayload); - - return taxRate; - }); + return taxRate; + }, + trx + ); } } diff --git a/packages/server/src/services/TaxRates/TaxRatesExportable.ts b/packages/server/src/services/TaxRates/TaxRatesExportable.ts new file mode 100644 index 000000000..096a36698 --- /dev/null +++ b/packages/server/src/services/TaxRates/TaxRatesExportable.ts @@ -0,0 +1,18 @@ +import { Inject, Service } from 'typedi'; +import { Exportable } from '../Export/Exportable'; +import { TaxRatesApplication } from './TaxRatesApplication'; + +@Service() +export class TaxRatesExportable extends Exportable { + @Inject() + private taxRatesApplication: TaxRatesApplication; + + /** + * Retrieves the accounts data to exportable sheet. + * @param {number} tenantId + * @returns + */ + public exportable(tenantId: number) { + return this.taxRatesApplication.getTaxRates(tenantId); + } +} diff --git a/packages/server/src/services/TaxRates/TaxRatesImportable.SampleData.ts b/packages/server/src/services/TaxRates/TaxRatesImportable.SampleData.ts new file mode 100644 index 000000000..0a7fe389d --- /dev/null +++ b/packages/server/src/services/TaxRates/TaxRatesImportable.SampleData.ts @@ -0,0 +1,18 @@ +export const TaxRatesSampleData = [ + { + 'Tax Name': 'Value Added Tax', + Code: 'VAT-STD', + Rate: '20', + Description: 'Standard VAT rate applied to most goods and services.', + 'Is Non Recoverable': 'F', + Active: 'T', + }, + { + 'Tax Name': 'Luxury Goods Tax', + Code: 'TAX-LUXURY', + Rate: '25', + Description: 'Tax imposed on the sale of luxury items.', + 'Is Non Recoverable': 'T', + Active: 'T', + }, +]; diff --git a/packages/server/src/services/TaxRates/TaxRatesImportable.ts b/packages/server/src/services/TaxRates/TaxRatesImportable.ts new file mode 100644 index 000000000..5f0ab4185 --- /dev/null +++ b/packages/server/src/services/TaxRates/TaxRatesImportable.ts @@ -0,0 +1,46 @@ +import { Inject, Service } from 'typedi'; +import { Knex } from 'knex'; +import { ICreateTaxRateDTO } from '@/interfaces'; +import { CreateTaxRate } from './CreateTaxRate'; +import { Importable } from '../Import/Importable'; +import { TaxRatesSampleData } from './TaxRatesImportable.SampleData'; + +@Service() +export class TaxRatesImportable extends Importable { + @Inject() + private createTaxRateService: CreateTaxRate; + + /** + * Importing to tax rate creating service. + * @param {number} tenantId - + * @param {ICreateTaxRateDTO} ICreateTaxRateDTO - + * @param {Knex.Transaction} trx - + * @returns + */ + public importable( + tenantId: number, + createAccountDTO: ICreateTaxRateDTO, + trx?: Knex.Transaction + ) { + return this.createTaxRateService.createTaxRate( + tenantId, + createAccountDTO, + trx + ); + } + + /** + * Concurrrency controlling of the importing process. + * @returns {number} + */ + public get concurrency() { + return 1; + } + + /** + * Retrieves the sample data that used to download accounts sample sheet. + */ + public sampleData(): any[] { + return TaxRatesSampleData; + } +} diff --git a/packages/server/src/services/Tenancy/SystemService.ts b/packages/server/src/services/Tenancy/SystemService.ts index dac5a4320..d03aec13c 100644 --- a/packages/server/src/services/Tenancy/SystemService.ts +++ b/packages/server/src/services/Tenancy/SystemService.ts @@ -18,8 +18,4 @@ export default class HasSystemService implements SystemService { cache() { return this.container('cache'); } - - dbManager() { - return this.container('dbManager'); - } } diff --git a/packages/server/src/services/Tenancy/TenantDBManager.ts b/packages/server/src/services/Tenancy/TenantDBManager.ts index 981256f9c..a234e5c3b 100644 --- a/packages/server/src/services/Tenancy/TenantDBManager.ts +++ b/packages/server/src/services/Tenancy/TenantDBManager.ts @@ -1,18 +1,16 @@ import { Container } from 'typedi'; -import Knex from 'knex'; +import { Knex, knex } from 'knex'; import { knexSnakeCaseMappers } from 'objection'; import { tenantKnexConfig, tenantSeedConfig } from '@/config/knexConfig'; import config from '@/config'; import { ITenant, ITenantDBManager } from '@/interfaces'; import SystemService from '@/services/Tenancy/SystemService'; import { TenantDBAlreadyExists } from '@/exceptions'; +import { sanitizeDatabaseName } from '@/utils/sanitizers'; export default class TenantDBManager implements ITenantDBManager { static knexCache: { [key: string]: Knex } = {}; - // System database manager. - dbManager: any; - // System knex instance. sysKnex: Knex; @@ -23,7 +21,6 @@ export default class TenantDBManager implements ITenantDBManager { constructor() { const systemService = Container.get(SystemService); - this.dbManager = systemService.dbManager(); this.sysKnex = systemService.knex(); } @@ -32,7 +29,9 @@ export default class TenantDBManager implements ITenantDBManager { * @return {string} */ private getDatabaseName(tenant: ITenant) { - return `${config.tenant.db_name_prefix}${tenant.organizationId}`; + return sanitizeDatabaseName( + `${config.tenant.db_name_prefix}${tenant.organizationId}` + ); } /** @@ -59,7 +58,9 @@ export default class TenantDBManager implements ITenantDBManager { await this.throwErrorIfTenantDBExists(tenant); const databaseName = this.getDatabaseName(tenant); - await this.dbManager.createDb(databaseName); + await this.sysKnex.raw( + `CREATE DATABASE ${databaseName} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci` + ); } /** @@ -72,7 +73,6 @@ export default class TenantDBManager implements ITenantDBManager { if (!isExists) { return; } - await this.dropDatabase(tenant); } @@ -83,7 +83,7 @@ export default class TenantDBManager implements ITenantDBManager { public async dropDatabase(tenant: ITenant) { const databaseName = this.getDatabaseName(tenant); - await this.dbManager.dropDb(databaseName); + await this.sysKnex.raw(`DROP DATABASE IF EXISTS ${databaseName}`); } /** @@ -118,7 +118,7 @@ export default class TenantDBManager implements ITenantDBManager { let knexInstance = TenantDBManager.knexCache[key]; if (!knexInstance) { - knexInstance = Knex({ + knexInstance = knex({ ...tenantKnexConfig(tenant), ...knexSnakeCaseMappers({ upperCase: true }), }); diff --git a/packages/server/src/services/TransactionsLocking/SalesTransactionLockingGuardSubscriber.ts b/packages/server/src/services/TransactionsLocking/SalesTransactionLockingGuardSubscriber.ts index a2e55fc21..9b80bca26 100644 --- a/packages/server/src/services/TransactionsLocking/SalesTransactionLockingGuardSubscriber.ts +++ b/packages/server/src/services/TransactionsLocking/SalesTransactionLockingGuardSubscriber.ts @@ -5,9 +5,9 @@ import { ISaleInvoiceCreatingPaylaod, ISaleReceiptDeletingPayload, ICreditNoteDeletingPayload, - IPaymentReceiveCreatingPayload, + IPaymentReceivedCreatingPayload, IRefundCreditNoteDeletingPayload, - IPaymentReceiveDeletingPayload, + IPaymentReceivedDeletingPayload, ISaleEstimateDeletingPayload, ISaleEstimateCreatingPayload, ISaleEstimateEditingPayload, @@ -17,7 +17,7 @@ import { ISaleInvoiceWrittenOffCancelPayload, ICreditNoteEditingPayload, ISaleReceiptEditingPayload, - IPaymentReceiveEditingPayload, + IPaymentReceivedEditingPayload, ISaleReceiptEventClosingPayload, ICreditNoteCreatingPayload, } from '@/interfaces'; @@ -454,13 +454,13 @@ export default class SalesTransactionLockingGuardSubscriber { /** * Transaction locking guard on payment receive editing. - * @param {IPaymentReceiveEditingPayload} + * @param {IPaymentReceivedEditingPayload} */ private transactionLockingGuardOnPaymentEditing = async ({ tenantId, oldPaymentReceive, paymentReceiveDTO, - }: IPaymentReceiveEditingPayload) => { + }: IPaymentReceivedEditingPayload) => { // Validate the old payment date. await this.salesLockingGuard.transactionLockingGuard( tenantId, @@ -475,12 +475,12 @@ export default class SalesTransactionLockingGuardSubscriber { /** * Transaction locking guard on payment creating. - * @param {IPaymentReceiveCreatingPayload} + * @param {IPaymentReceivedCreatingPayload} */ private transactionLockingGuardOnPaymentCreating = async ({ tenantId, paymentReceiveDTO, - }: IPaymentReceiveCreatingPayload) => { + }: IPaymentReceivedCreatingPayload) => { await this.salesLockingGuard.transactionLockingGuard( tenantId, paymentReceiveDTO.paymentDate @@ -489,12 +489,12 @@ export default class SalesTransactionLockingGuardSubscriber { /** * Transaction locking guard on payment deleting. - * @param {IPaymentReceiveDeletingPayload} payload - + * @param {IPaymentReceivedDeletingPayload} payload - */ private transactionLockingGuardPaymentDeleting = async ({ oldPaymentReceive, tenantId, - }: IPaymentReceiveDeletingPayload) => { + }: IPaymentReceivedDeletingPayload) => { await this.salesLockingGuard.transactionLockingGuard( tenantId, oldPaymentReceive.paymentDate diff --git a/packages/server/src/subscribers/PaymentReceive/AutoSerialIncrement.ts b/packages/server/src/subscribers/PaymentReceive/AutoSerialIncrement.ts index f8cdceba2..89f60a9e6 100644 --- a/packages/server/src/subscribers/PaymentReceive/AutoSerialIncrement.ts +++ b/packages/server/src/subscribers/PaymentReceive/AutoSerialIncrement.ts @@ -1,13 +1,13 @@ import { Service, Inject } from 'typedi'; import events from '@/subscribers/events'; import { EventSubscriber } from '@/lib/EventPublisher/EventPublisher'; -import { PaymentReceiveIncrement } from '@/services/Sales/PaymentReceives/PaymentReceiveIncrement'; -import { IPaymentReceiveCreatedPayload } from '@/interfaces'; +import { PaymentReceivedIncrement } from '@/services/Sales/PaymentReceived/PaymentReceivedIncrement'; +import { IPaymentReceivedCreatedPayload } from '@/interfaces'; @Service() export default class PaymentReceiveAutoSerialSubscriber extends EventSubscriber { @Inject() - private paymentIncrement: PaymentReceiveIncrement; + private paymentIncrement: PaymentReceivedIncrement; /** * Attaches the events with handles. @@ -22,13 +22,13 @@ export default class PaymentReceiveAutoSerialSubscriber extends EventSubscriber /** * Handles increment next number of payment receive once be created. - * @param {IPaymentReceiveCreatedPayload} payload - + * @param {IPaymentReceivedCreatedPayload} payload - */ private handlePaymentNextNumberIncrement = async ({ tenantId, paymentReceiveId, trx, - }: IPaymentReceiveCreatedPayload) => { + }: IPaymentReceivedCreatedPayload) => { await this.paymentIncrement.incrementNextPaymentReceiveNumber(tenantId); }; } diff --git a/packages/server/src/subscribers/PaymentReceive/PaymentReceiveSyncInvoices.ts b/packages/server/src/subscribers/PaymentReceive/PaymentReceiveSyncInvoices.ts index d257de18c..643ca94d4 100644 --- a/packages/server/src/subscribers/PaymentReceive/PaymentReceiveSyncInvoices.ts +++ b/packages/server/src/subscribers/PaymentReceive/PaymentReceiveSyncInvoices.ts @@ -1,16 +1,16 @@ import { Inject, Service } from 'typedi'; import events from '@/subscribers/events'; -import { PaymentReceiveInvoiceSync } from '@/services/Sales/PaymentReceives/PaymentReceiveInvoiceSync'; +import { PaymentReceivedInvoiceSync } from '@/services/Sales/PaymentReceived/PaymentReceivedInvoiceSync'; import { - IPaymentReceiveCreatedPayload, - IPaymentReceiveDeletedPayload, - IPaymentReceiveEditedPayload, + IPaymentReceivedCreatedPayload, + IPaymentReceivedDeletedPayload, + IPaymentReceivedEditedPayload, } from '@/interfaces'; @Service() export default class PaymentReceiveSyncInvoicesSubscriber { @Inject() - private paymentSyncInvoice: PaymentReceiveInvoiceSync; + private paymentSyncInvoice: PaymentReceivedInvoiceSync; /** * Attaches the events to handles. @@ -39,7 +39,7 @@ export default class PaymentReceiveSyncInvoicesSubscriber { tenantId, paymentReceive, trx, - }: IPaymentReceiveCreatedPayload) => { + }: IPaymentReceivedCreatedPayload) => { await this.paymentSyncInvoice.saveChangeInvoicePaymentAmount( tenantId, paymentReceive.entries, @@ -57,7 +57,7 @@ export default class PaymentReceiveSyncInvoicesSubscriber { paymentReceive, oldPaymentReceive, trx, - }: IPaymentReceiveEditedPayload) => { + }: IPaymentReceivedEditedPayload) => { await this.paymentSyncInvoice.saveChangeInvoicePaymentAmount( tenantId, paymentReceive.entries, @@ -74,7 +74,7 @@ export default class PaymentReceiveSyncInvoicesSubscriber { paymentReceiveId, oldPaymentReceive, trx, - }: IPaymentReceiveDeletedPayload) => { + }: IPaymentReceivedDeletedPayload) => { await this.paymentSyncInvoice.saveChangeInvoicePaymentAmount( tenantId, oldPaymentReceive.entries.map((entry) => ({ diff --git a/packages/server/src/subscribers/PaymentReceive/SendSmsNotificationToCustomer.ts b/packages/server/src/subscribers/PaymentReceive/SendSmsNotificationToCustomer.ts index 988260856..840698831 100644 --- a/packages/server/src/subscribers/PaymentReceive/SendSmsNotificationToCustomer.ts +++ b/packages/server/src/subscribers/PaymentReceive/SendSmsNotificationToCustomer.ts @@ -1,7 +1,7 @@ import { Service, Inject } from 'typedi'; import events from '@/subscribers/events'; -import { PaymentReceiveNotifyBySms } from '@/services/Sales/PaymentReceives/PaymentReceiveSmsNotify'; -import { IPaymentReceiveCreatedPayload } from '@/interfaces'; +import { PaymentReceiveNotifyBySms } from '@/services/Sales/PaymentReceived/PaymentReceivedSmsNotify'; +import { IPaymentReceivedCreatedPayload } from '@/interfaces'; import { runAfterTransaction } from '@/services/UnitOfWork/TransactionsHooks'; @Service() @@ -26,7 +26,7 @@ export default class SendSmsNotificationPaymentReceive { tenantId, paymentReceiveId, trx, - }: IPaymentReceiveCreatedPayload) => { + }: IPaymentReceivedCreatedPayload) => { // Notify via Sms after transactions complete running. runAfterTransaction(trx, async () => { try { @@ -34,7 +34,7 @@ export default class SendSmsNotificationPaymentReceive { tenantId, paymentReceiveId ); - } catch (error) {} + } catch (error) { } }); }; } diff --git a/packages/server/src/subscribers/PaymentReceive/WriteGLEntries.ts b/packages/server/src/subscribers/PaymentReceive/WriteGLEntries.ts index bffe4260f..b1b197a96 100644 --- a/packages/server/src/subscribers/PaymentReceive/WriteGLEntries.ts +++ b/packages/server/src/subscribers/PaymentReceive/WriteGLEntries.ts @@ -1,16 +1,16 @@ import { Inject, Service } from 'typedi'; import { - IPaymentReceiveCreatedPayload, - IPaymentReceiveDeletedPayload, - IPaymentReceiveEditedPayload, + IPaymentReceivedCreatedPayload, + IPaymentReceivedDeletedPayload, + IPaymentReceivedEditedPayload, } from '@/interfaces'; import events from '@/subscribers/events'; -import { PaymentReceiveGLEntries } from '@/services/Sales/PaymentReceives/PaymentReceiveGLEntries'; +import { PaymentReceivedGLEntries } from '@/services/Sales/PaymentReceived/PaymentReceivedGLEntries'; @Service() export default class PaymentReceivesWriteGLEntriesSubscriber { @Inject() - private paymentReceiveGLEntries: PaymentReceiveGLEntries; + private paymentReceiveGLEntries: PaymentReceivedGLEntries; /** * Attaches events with handlers. @@ -37,7 +37,7 @@ export default class PaymentReceivesWriteGLEntriesSubscriber { tenantId, paymentReceiveId, trx, - }: IPaymentReceiveCreatedPayload) => { + }: IPaymentReceivedCreatedPayload) => { await this.paymentReceiveGLEntries.writePaymentGLEntries( tenantId, paymentReceiveId, @@ -52,7 +52,7 @@ export default class PaymentReceivesWriteGLEntriesSubscriber { tenantId, paymentReceive, trx, - }: IPaymentReceiveEditedPayload) => { + }: IPaymentReceivedEditedPayload) => { await this.paymentReceiveGLEntries.rewritePaymentGLEntries( tenantId, paymentReceive.id, @@ -67,7 +67,7 @@ export default class PaymentReceivesWriteGLEntriesSubscriber { tenantId, paymentReceiveId, trx, - }: IPaymentReceiveDeletedPayload) => { + }: IPaymentReceivedDeletedPayload) => { await this.paymentReceiveGLEntries.revertPaymentGLEntries( tenantId, paymentReceiveId, diff --git a/packages/server/src/subscribers/events.ts b/packages/server/src/subscribers/events.ts index c72a2e7d3..2299df01b 100644 --- a/packages/server/src/subscribers/events.ts +++ b/packages/server/src/subscribers/events.ts @@ -659,6 +659,9 @@ export default { onUnexcluding: 'onBankTransactionUnexcluding', onUnexcluded: 'onBankTransactionUnexcluded', + + onPendingRemoving: 'onBankTransactionPendingRemoving', + onPendingRemoved: 'onBankTransactionPendingRemoved', }, bankAccount: { diff --git a/packages/server/src/utils/sanitizers.ts b/packages/server/src/utils/sanitizers.ts new file mode 100644 index 000000000..4ca07a5d8 --- /dev/null +++ b/packages/server/src/utils/sanitizers.ts @@ -0,0 +1,4 @@ +export function sanitizeDatabaseName(dbName: string) { + // Replace any character that is not alphanumeric or an underscore with an underscore + return dbName.replace(/[^a-zA-Z0-9_]/g, ''); +} diff --git a/packages/webapp/package.json b/packages/webapp/package.json index e252ff5ca..38c1a8c0c 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -121,7 +121,7 @@ "yup": "^0.28.1" }, "scripts": { - "dev": "PORT=4000 craco start", + "dev": "cross-env PORT=4000 craco start", "build": "craco build", "test": "node scripts/test.js", "storybook": "start-storybook -p 6006" diff --git a/packages/webapp/src/components/BankAccounts/index.tsx b/packages/webapp/src/components/BankAccounts/index.tsx index 98e411a4c..02f5f5697 100644 --- a/packages/webapp/src/components/BankAccounts/index.tsx +++ b/packages/webapp/src/components/BankAccounts/index.tsx @@ -62,6 +62,7 @@ export function BankAccount({ balance, loading = false, updatedBeforeText, + uncategorizedTransactionsCount, ...restProps }) { return ( @@ -77,17 +78,19 @@ export function BankAccount({ - {false && ( + {uncategorizedTransactionsCount > 0 && ( + )} + {updatedBeforeText && ( + )} - diff --git a/packages/webapp/src/components/Customers/CustomersSelect.tsx b/packages/webapp/src/components/Customers/CustomersSelect.tsx index 067011792..3dde07f3e 100644 --- a/packages/webapp/src/components/Customers/CustomersSelect.tsx +++ b/packages/webapp/src/components/Customers/CustomersSelect.tsx @@ -1,9 +1,11 @@ // @ts-nocheck import React from 'react'; import * as R from 'ramda'; +import { useFormikContext } from 'formik'; import { createNewItemFromQuery, createNewItemRenderer } from './utils'; import { FSelect } from '../Forms'; import withDrawerActions from '@/containers/Drawer/withDrawerActions'; +import { useCreateAutofillListener } from '@/hooks/state/autofill'; import { DRAWERS } from '@/constants/drawers'; /** @@ -17,6 +19,7 @@ function CustomerSelectRoot({ // #ownProps items, allowCreate, + name, ...props }) { // Maybe inject create new item props to suggest component. @@ -24,14 +27,21 @@ function CustomerSelectRoot({ const maybeCreateNewItemFromQuery = allowCreate ? createNewItemFromQuery : null; + const { setFieldValue } = useFormikContext(); + // Creates autofill listener once the quick customer drawer submit the form. + const autofillRef = useCreateAutofillListener((payload: any) => { + setFieldValue(name, payload.customerId); + }); // Handles the create item click. - const handleCreateItemClick = () => { - openDrawer(DRAWERS.QUICK_CREATE_CUSTOMER); + const handleCreateItemClick = (item) => { + const displayName = item.name; + openDrawer(DRAWERS.QUICK_CREATE_CUSTOMER, { autofillRef, displayName }); }; return ( {label} -
{children}
+
{children}
); } diff --git a/packages/webapp/src/components/DialogsContainer.tsx b/packages/webapp/src/components/DialogsContainer.tsx index 284c3cfbc..8da29ab37 100644 --- a/packages/webapp/src/components/DialogsContainer.tsx +++ b/packages/webapp/src/components/DialogsContainer.tsx @@ -4,7 +4,6 @@ import UserFormDialog from '@/containers/Dialogs/UserFormDialog'; import ItemCategoryDialog from '@/containers/Dialogs/ItemCategoryDialog'; import CurrencyFormDialog from '@/containers/Dialogs/CurrencyFormDialog'; import InventoryAdjustmentDialog from '@/containers/Dialogs/InventoryAdjustmentFormDialog'; -import PaymentViaVoucherDialog from '@/containers/Dialogs/PaymentViaVoucherDialog'; import KeyboardShortcutsDialog from '@/containers/Dialogs/keyboardShortcutsDialog'; import ContactDuplicateDialog from '@/containers/Dialogs/ContactDuplicateDialog'; import QuickPaymentReceiveFormDialog from '@/containers/Dialogs/QuickPaymentReceiveFormDialog'; @@ -49,7 +48,7 @@ import InvoiceExchangeRateChangeDialog from '@/containers/Sales/Invoices/Invoice import InvoiceMailDialog from '@/containers/Sales/Invoices/InvoiceMailDialog/InvoiceMailDialog'; import EstimateMailDialog from '@/containers/Sales/Estimates/EstimateMailDialog/EstimateMailDialog'; import ReceiptMailDialog from '@/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog'; -import PaymentMailDialog from '@/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog'; +import PaymentMailDialog from '@/containers/Sales/PaymentsReceived/PaymentMailDialog/PaymentMailDialog'; import { ExportDialog } from '@/containers/Dialogs/ExportDialog'; import { RuleFormDialog } from '@/containers/Banking/Rules/RuleFormDialog/RuleFormDialog'; import { DisconnectBankAccountDialog } from '@/containers/CashFlow/AccountTransactions/dialogs/DisconnectBankAccountDialog/DisconnectBankAccountDialog'; @@ -68,7 +67,6 @@ export default function DialogsContainer() { - - + diff --git a/packages/webapp/src/components/Dropzone/Dropzone.module.css b/packages/webapp/src/components/Dropzone/Dropzone.module.css index 63a207805..907edbf30 100644 --- a/packages/webapp/src/components/Dropzone/Dropzone.module.css +++ b/packages/webapp/src/components/Dropzone/Dropzone.module.css @@ -1,5 +1,3 @@ - - .root { padding: 20px; border: 2px dotted #c5cbd3; @@ -9,4 +7,14 @@ flex-direction: column; background: #fff; position: relative; + transition: background-color 0.3s ease, border-color 0.3s ease; + + &.dropzoneAccept { + border-color: rgb(0, 82, 204); + background: rgba(0, 82, 204, 0.05); + } + &.dropzoneReject { + border-color: #AC2F33; + background: rgba(172, 47, 51, 0.05) + } } \ No newline at end of file diff --git a/packages/webapp/src/components/Dropzone/Dropzone.tsx b/packages/webapp/src/components/Dropzone/Dropzone.tsx index 8682a499b..bc0d45b46 100644 --- a/packages/webapp/src/components/Dropzone/Dropzone.tsx +++ b/packages/webapp/src/components/Dropzone/Dropzone.tsx @@ -235,7 +235,14 @@ export const Dropzone = (_props: DropzoneProps) => { > {
{children}
@@ -268,8 +275,6 @@ Dropzone.Idle = DropzoneIdle; Dropzone.Reject = DropzoneReject; - - type PossibleRef = Ref | undefined; export function assignRef(ref: PossibleRef, value: T) { diff --git a/packages/webapp/src/components/Vendors/VendorsSelect.tsx b/packages/webapp/src/components/Vendors/VendorsSelect.tsx index eba8fc082..6387ee845 100644 --- a/packages/webapp/src/components/Vendors/VendorsSelect.tsx +++ b/packages/webapp/src/components/Vendors/VendorsSelect.tsx @@ -1,9 +1,11 @@ // @ts-nocheck import React from 'react'; import * as R from 'ramda'; +import { useFormikContext } from 'formik'; import withDrawerActions from '@/containers/Drawer/withDrawerActions'; import { createNewItemFromQuery, createNewItemRenderer } from './utils'; import { FSelect } from '../Forms'; +import { useCreateAutofillListener } from '@/hooks/state/autofill'; import { DRAWERS } from '@/constants/drawers'; /** @@ -15,6 +17,7 @@ function VendorsSelectRoot({ openDrawer, // #ownProps + name, items, allowCreate, @@ -25,14 +28,24 @@ function VendorsSelectRoot({ const maybeCreateNewItemFromQuery = allowCreate ? createNewItemFromQuery : null; + const { setFieldValue } = useFormikContext(); + + // Creates a new autofill listener once the quick vendor drawer submits the form. + const autofillRef = useCreateAutofillListener((payload: any) => { + setFieldValue(name, payload.vendorId); + }); // Handles the create item click. - const handleCreateItemClick = () => { - openDrawer(DRAWERS.QUICK_WRITE_VENDOR); + const handleCreateItemClick = (item) => { + openDrawer(DRAWERS.QUICK_WRITE_VENDOR, { + autofillRef, + displayName: item.name, + }); }; return ( ), - link: '/payment-receives', + link: '/payments-received', subject: AbilitySubject.PaymentReceive, ability: PaymentReceiveAction.View, }, @@ -88,7 +88,7 @@ export const accountsPayable = [ { title: , description: , - link: '/payment-mades', + link: '/payments-made', subject: AbilitySubject.PaymentMade, ability: PaymentMadeAction.View, }, diff --git a/packages/webapp/src/constants/query-keys/banking.ts b/packages/webapp/src/constants/query-keys/banking.ts index 7164470e2..26ec3135b 100644 --- a/packages/webapp/src/constants/query-keys/banking.ts +++ b/packages/webapp/src/constants/query-keys/banking.ts @@ -7,4 +7,7 @@ export const BANK_QUERY_KEY = { 'RECOGNIZED_BANK_TRANSACTIONS_INFINITY', BANK_ACCOUNT_SUMMARY_META: 'BANK_ACCOUNT_SUMMARY_META', AUTOFILL_CATEGORIZE_BANK_TRANSACTION: 'AUTOFILL_CATEGORIZE_BANK_TRANSACTION', + PENDING_BANK_ACCOUNT_TRANSACTIONS: 'PENDING_BANK_ACCOUNT_TRANSACTIONS', + PENDING_BANK_ACCOUNT_TRANSACTIONS_INFINITY: + 'PENDING_BANK_ACCOUNT_TRANSACTIONS_INFINITY', }; diff --git a/packages/webapp/src/constants/sidebarMenu.tsx b/packages/webapp/src/constants/sidebarMenu.tsx index 260a23def..f81322c42 100644 --- a/packages/webapp/src/constants/sidebarMenu.tsx +++ b/packages/webapp/src/constants/sidebarMenu.tsx @@ -74,7 +74,7 @@ export const SidebarMenu = [ }, }, { - text: , + text: , href: '/items/categories', type: ISidebarMenuItemType.Link, permission: { @@ -179,8 +179,8 @@ export const SidebarMenu = [ type: ISidebarMenuItemType.Link, }, { - text: , - href: '/payment-receives', + text: , + href: '/payments-received', type: ISidebarMenuItemType.Link, permission: { subject: AbilitySubject.PaymentReceive, @@ -226,8 +226,8 @@ export const SidebarMenu = [ type: ISidebarMenuItemType.Link, }, { - text: , - href: '/payment-receives/new', + text: , + href: '/payment-received/new', type: ISidebarMenuItemType.Link, permission: { subject: AbilitySubject.PaymentReceive, @@ -265,8 +265,8 @@ export const SidebarMenu = [ type: ISidebarMenuItemType.Link, }, { - text: , - href: '/payment-mades', + text: , + href: '/payments-made', type: ISidebarMenuItemType.Link, permission: { subject: AbilitySubject.PaymentMade, @@ -299,7 +299,7 @@ export const SidebarMenu = [ }, { text: , - href: '/payment-mades/new', + href: '/payments-made/new', type: ISidebarMenuItemType.Link, permission: { subject: AbilitySubject.PaymentMade, diff --git a/packages/webapp/src/constants/subscriptionModels.tsx b/packages/webapp/src/constants/subscriptionModels.tsx index e1a6ba35c..736fa6b35 100644 --- a/packages/webapp/src/constants/subscriptionModels.tsx +++ b/packages/webapp/src/constants/subscriptionModels.tsx @@ -44,9 +44,9 @@ export const SubscriptionPlans = [ }, { text: 'Unlimited User Seats' }, ], - monthlyPrice: '$10', + monthlyPrice: '$20', monthlyPriceLabel: 'Per month', - annuallyPrice: '$7.5', + annuallyPrice: '$15', annuallyPriceLabel: 'Per month', monthlyVariantId: '446152', // monthlyVariantId: '450016', @@ -78,9 +78,9 @@ export const SubscriptionPlans = [ { text: 'Smart Financial Reports' }, { text: 'Advanced Inventory Reports' }, ], - monthlyPrice: '$20', + monthlyPrice: '$40', monthlyPriceLabel: 'Per month', - annuallyPrice: '$15', + annuallyPrice: '$30', annuallyPriceLabel: 'Per month', // monthlyVariantId: '450028', monthlyVariantId: '446155', @@ -101,9 +101,9 @@ export const SubscriptionPlans = [ }, { text: 'Analysis Cost Center' }, ], - monthlyPrice: '$25', + monthlyPrice: '$55', monthlyPriceLabel: 'Per month', - annuallyPrice: '$19', + annuallyPrice: '$40', annuallyPriceLabel: 'Per month', featured: true, // monthlyVariantId: '450031', @@ -128,9 +128,9 @@ export const SubscriptionPlans = [ hint: 'Track the organization inventory in multiple warehouses and transfer goods between them.', }, ], - monthlyPrice: '$40', + monthlyPrice: '$60', monthlyPriceLabel: 'Per month', - annuallyPrice: '$30', + annuallyPrice: '$45', annuallyPriceLabel: 'Per month', // monthlyVariantId: '450024', monthlyVariantId: '446167', diff --git a/packages/webapp/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.tsx b/packages/webapp/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.tsx index 9aad7d99c..8d2134b9c 100644 --- a/packages/webapp/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.tsx +++ b/packages/webapp/src/containers/Accounting/JournalsLanding/ManualJournalsDataTable.tsx @@ -125,8 +125,9 @@ function ManualJournalsDataTable({ loading={isManualJournalsLoading} headerLoading={isManualJournalsLoading} progressBarLoading={isManualJournalsFetching} - pagesCount={pagination.pagesCount} pagination={true} + initialPageSize={manualJournalsTableState.pageSize} + pagesCount={pagination.pagesCount} autoResetSortBy={false} autoResetPage={false} TableLoadingRenderer={TableSkeletonRows} diff --git a/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalFormFooterLeft.tsx b/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalFormFooterLeft.tsx index 8912f1de8..22d4cba38 100644 --- a/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalFormFooterLeft.tsx +++ b/packages/webapp/src/containers/Accounting/MakeJournal/MakeJournalFormFooterLeft.tsx @@ -15,6 +15,8 @@ export function MakeJournalFormFooterLeft() { diff --git a/packages/webapp/src/containers/Accounts/utils.tsx b/packages/webapp/src/containers/Accounts/utils.tsx index 6e535fd28..a0b4aaaf7 100644 --- a/packages/webapp/src/containers/Accounts/utils.tsx +++ b/packages/webapp/src/containers/Accounts/utils.tsx @@ -27,7 +27,7 @@ export const accountNameAccessor = (account) => { export const handleDeleteErrors = (errors) => { if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) { AppToaster.show({ - message: intl.get('you_could_not_delete_predefined_accounts'), + message: intl.get('cannot_delete_predefined_accounts'), intent: Intent.DANGER, }); } diff --git a/packages/webapp/src/containers/Alerts/ExchangeRates/ExchangeRateBulkDeleteAlert.tsx b/packages/webapp/src/containers/Alerts/ExchangeRates/ExchangeRateBulkDeleteAlert.tsx deleted file mode 100644 index cc04530f8..000000000 --- a/packages/webapp/src/containers/Alerts/ExchangeRates/ExchangeRateBulkDeleteAlert.tsx +++ /dev/null @@ -1,73 +0,0 @@ -// @ts-nocheck -import React, { useState } from 'react'; -import { FormattedMessage as T } from '@/components'; -import intl from 'react-intl-universal'; -import { Intent, Alert } from '@blueprintjs/core'; -import { size } from 'lodash'; -import { AppToaster } from '@/components'; - -import withAlertStoreConnect from '@/containers/Alert/withAlertStoreConnect'; -import withAlertActions from '@/containers/Alert/withAlertActions'; - -import { compose } from '@/utils'; - -/** - * Exchange rate bulk delete alert. - */ -function ExchangeRateBulkDeleteAlert({ - name, - - // #withAlertStoreConnect - isOpen, - payload: { exchangeRatesIds }, - - // #withAlertActions - closeAlert, -}) { - // handle cancel item bulk delete alert. - const handleCancelBulkDelete = () => { - closeAlert(name); - }; - - // handle confirm Exchange Rates bulk delete. - // const handleConfirmBulkDelete = () => { - // bulkDeleteExchangeRate(exchangeRatesIds) - // .then(() => { - // AppToaster.show({ - // message: formatMessage({ - // id: 'the_exchange_rates_has_been_successfully_deleted', - // }), - // intent: Intent.SUCCESS, - // }); - // }) - // .catch(({ errors }) => { - // handleDeleteErrors(errors); - // }); - // }; - - return ( - } - confirmButtonText={ - - } - icon="trash" - intent={Intent.DANGER} - isOpen={isOpen} - onCancel={handleCancelBulkDelete} - // onConfirm={} - // loading={isLoading} - > -

- -

-
- ); -} - -export default compose( - withAlertStoreConnect(), - withAlertActions, -)(ExchangeRateBulkDeleteAlert); diff --git a/packages/webapp/src/containers/Alerts/ExchangeRates/ExchangeRateDeleteAlert.tsx b/packages/webapp/src/containers/Alerts/ExchangeRates/ExchangeRateDeleteAlert.tsx deleted file mode 100644 index 098d2ca8f..000000000 --- a/packages/webapp/src/containers/Alerts/ExchangeRates/ExchangeRateDeleteAlert.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// @ts-nocheck -import React from 'react'; -import intl from 'react-intl-universal'; -import { - AppToaster, - FormattedMessage as T, - FormattedHTMLMessage, -} from '@/components'; -import { Intent, Alert } from '@blueprintjs/core'; - -import { useDeleteExchangeRate } from '@/hooks/query'; -import withAlertStoreConnect from '@/containers/Alert/withAlertStoreConnect'; -import withAlertActions from '@/containers/Alert/withAlertActions'; - -import { compose } from '@/utils'; - -/** - * exchange rate delete alerts. - */ -function ExchangeRateDeleteAlert({ - name, - - // #withAlertStoreConnect - isOpen, - payload: { exchangeRateId }, - - // #withAlertActions - closeAlert, -}) { - const { mutateAsync: deleteExchangeRate, isLoading } = - useDeleteExchangeRate(); - - // Handle cancel delete exchange rate alert. - const handleCancelExchangeRateDelete = () => closeAlert(name); - - const handelConfirmExchangeRateDelete = () => { - deleteExchangeRate(exchangeRateId) - .then((response) => { - AppToaster.show({ - message: intl.get('the_exchange_rates_has_been_deleted_successfully'), - intent: Intent.SUCCESS, - }); - closeAlert(name); - }) - .catch(() => { - closeAlert(name); - }); - }; - - return ( - } - confirmButtonText={} - intent={Intent.DANGER} - isOpen={isOpen} - onCancel={handleCancelExchangeRateDelete} - onConfirm={handelConfirmExchangeRateDelete} - loading={isLoading} - > -

- -

-
- ); -} - -export default compose( - withAlertStoreConnect(), - withAlertActions, -)(ExchangeRateDeleteAlert); diff --git a/packages/webapp/src/containers/Alerts/PaymentReceives/ClearingAllLinesAlert.tsx b/packages/webapp/src/containers/Alerts/PaymentReceived/ClearingAllLinesAlert.tsx similarity index 100% rename from packages/webapp/src/containers/Alerts/PaymentReceives/ClearingAllLinesAlert.tsx rename to packages/webapp/src/containers/Alerts/PaymentReceived/ClearingAllLinesAlert.tsx diff --git a/packages/webapp/src/containers/Alerts/PaymentReceives/PaymentReceiveDeleteAlert.tsx b/packages/webapp/src/containers/Alerts/PaymentReceived/PaymentReceivedDeleteAlert.tsx similarity index 88% rename from packages/webapp/src/containers/Alerts/PaymentReceives/PaymentReceiveDeleteAlert.tsx rename to packages/webapp/src/containers/Alerts/PaymentReceived/PaymentReceivedDeleteAlert.tsx index 553bfb2c7..11ce47773 100644 --- a/packages/webapp/src/containers/Alerts/PaymentReceives/PaymentReceiveDeleteAlert.tsx +++ b/packages/webapp/src/containers/Alerts/PaymentReceived/PaymentReceivedDeleteAlert.tsx @@ -21,7 +21,7 @@ import { DRAWERS } from '@/constants/drawers'; /** * Payment receive delete alert. */ -function PaymentReceiveDeleteAlert({ +function PaymentReceivedDeleteAlert({ name, // #withAlertStoreConnect @@ -48,11 +48,11 @@ function PaymentReceiveDeleteAlert({ .then(() => { AppToaster.show({ message: intl.get( - 'the_payment_receive_has_been_deleted_successfully', + 'the_payment_received_has_been_deleted_successfully', ), intent: Intent.SUCCESS, }); - closeDrawer(DRAWERS.PAYMENT_RECEIVE_DETAILS); + closeDrawer(DRAWERS.PAYMENT_RECEIVED_DETAILS); }) .catch( ({ @@ -81,7 +81,7 @@ function PaymentReceiveDeleteAlert({ >

@@ -92,4 +92,4 @@ export default compose( withAlertStoreConnect(), withAlertActions, withDrawerActions, -)(PaymentReceiveDeleteAlert); +)(PaymentReceivedDeleteAlert); diff --git a/packages/webapp/src/containers/Alerts/PaymentReceives/_utils.ts b/packages/webapp/src/containers/Alerts/PaymentReceived/_utils.ts similarity index 100% rename from packages/webapp/src/containers/Alerts/PaymentReceives/_utils.ts rename to packages/webapp/src/containers/Alerts/PaymentReceived/_utils.ts diff --git a/packages/webapp/src/containers/AlertsContainer/registered.tsx b/packages/webapp/src/containers/AlertsContainer/registered.tsx index f585c398f..ba2d44e41 100644 --- a/packages/webapp/src/containers/AlertsContainer/registered.tsx +++ b/packages/webapp/src/containers/AlertsContainer/registered.tsx @@ -6,9 +6,9 @@ import InventoryAdjustmentsAlerts from '@/containers/InventoryAdjustments/Invent import EstimatesAlerts from '@/containers/Sales/Estimates/EstimatesAlerts'; import InvoicesAlerts from '@/containers/Sales/Invoices/InvoicesAlerts'; import ReceiptsAlerts from '@/containers/Sales/Receipts/ReceiptsAlerts'; -import PaymentReceiveAlerts from '@/containers/Sales/PaymentReceives/PaymentReceiveAlerts'; +import PaymentsReceivedAlerts from '@/containers/Sales/PaymentsReceived/PaymentsReceivedAlerts'; import BillsAlerts from '@/containers/Purchases/Bills/BillsLanding/BillsAlerts'; -import PaymentMadesAlerts from '@/containers/Purchases/PaymentMades/PaymentMadesAlerts'; +import PaymentsMadeAlerts from '@/containers/Purchases/PaymentsMade/PaymentsMadeAlerts'; import CustomersAlerts from '@/containers/Customers/CustomersAlerts'; import VendorsAlerts from '@/containers/Vendors/VendorsAlerts'; import ManualJournalsAlerts from '@/containers/Accounting/JournalsLanding/ManualJournalsAlerts'; @@ -38,9 +38,9 @@ export default [ ...EstimatesAlerts, ...InvoicesAlerts, ...ReceiptsAlerts, - ...PaymentReceiveAlerts, + ...PaymentsReceivedAlerts, ...BillsAlerts, - ...PaymentMadesAlerts, + ...PaymentsMadeAlerts, ...CustomersAlerts, ...VendorsAlerts, ...ManualJournalsAlerts, diff --git a/packages/webapp/src/containers/Attachments/UploadAttachmentsPopoverContent.tsx b/packages/webapp/src/containers/Attachments/UploadAttachmentsPopoverContent.tsx index d7a5b8c1e..f755b42b4 100644 --- a/packages/webapp/src/containers/Attachments/UploadAttachmentsPopoverContent.tsx +++ b/packages/webapp/src/containers/Attachments/UploadAttachmentsPopoverContent.tsx @@ -120,6 +120,7 @@ export function UploadAttachmentsPopoverContent({ uploadIcon={null} value={null} title={''} + subtitle={'Drag and drop file here or choose file'} classNames={{ root: styles.dropzoneRoot }} onChange={handleChangeDropzone} dropzoneProps={{ diff --git a/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormContentForm.tsx b/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormContentForm.tsx index 96b5f4364..f3b172b7f 100644 --- a/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormContentForm.tsx +++ b/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/RuleFormContentForm.tsx @@ -1,6 +1,7 @@ // @ts-nocheck import { useCallback, useMemo } from 'react'; import { Form, Formik, FormikHelpers, useFormikContext } from 'formik'; +import { get } from 'lodash'; import { Button, Classes, Intent, Radio, Tag } from '@blueprintjs/core'; import * as R from 'ramda'; import { CreateRuleFormSchema } from './RuleFormContentForm.schema'; @@ -17,11 +18,12 @@ import { } from '@/components'; import { useCreateBankRule, useEditBankRule } from '@/hooks/query/bank-rules'; import { - FieldCondition, Fields, RuleFormValues, TransactionTypeOptions, getAccountRootFromMoneyCategory, + getDefaultFieldConditionByFieldKey, + getFieldConditionsByFieldKey, initialValues, } from './_utils'; import { useRuleFormDialogBoot } from './RuleFormBoot'; @@ -175,6 +177,13 @@ function RuleFormConditions() { setFieldValue('conditions', _conditions); }; + const handleConditionFieldChange = R.curry((index, item) => { + const defaultComparator = getDefaultFieldConditionByFieldKey(item.value); + + setFieldValue(`conditions[${index}].field`, item.value); + setFieldValue(`conditions[${index}].comparator`, defaultComparator); + }); + return ( @@ -190,6 +199,7 @@ function RuleFormConditions() { name={`conditions[${index}].field`} items={Fields} popoverProps={{ minimal: true, inline: false }} + onItemChange={handleConditionFieldChange(index)} fastField /> @@ -202,8 +212,13 @@ function RuleFormConditions() { > diff --git a/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/_utils.ts b/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/_utils.ts index db77154e9..e68418d39 100644 --- a/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/_utils.ts +++ b/packages/webapp/src/containers/Banking/Rules/RuleFormDialog/_utils.ts @@ -42,11 +42,25 @@ export const Fields = [ { value: 'amount', text: 'Amount' }, { value: 'payee', text: 'Payee' }, ]; -export const FieldCondition = [ + +export const TextFieldConditions = [ { value: 'contains', text: 'Contains' }, { value: 'equals', text: 'Equals' }, { value: 'not_contains', text: 'Not Contains' }, ]; +export const NumberFieldConditions = [ + { value: 'equal', text: 'Equal' }, + { value: 'bigger', text: 'Bigger' }, + { value: 'bigger_or_equal', text: 'Bigger or Equal' }, + { value: 'smaller', text: 'Smaller' }, + { value: 'smaller_or_equal', text: 'Smaller or Equal' }, +]; + +export const FieldCondition = [ + ...TextFieldConditions, + ...NumberFieldConditions, +]; + export const AssignTransactionTypeOptions = [ { value: 'expense', text: 'Expense' }, ]; @@ -56,3 +70,21 @@ export const getAccountRootFromMoneyCategory = (category: string): string[] => { return get(MoneyCategoryPerCreditAccountRootType, _category) || []; }; + +export const getFieldConditionsByFieldKey = (fieldKey?: string) => { + switch (fieldKey) { + case 'amount': + return NumberFieldConditions; + default: + return TextFieldConditions; + } +}; + +export const getDefaultFieldConditionByFieldKey = (fieldKey?: string) => { + switch (fieldKey) { + case 'amount': + return 'bigger_or_equal'; + default: + return 'contains'; + } +}; diff --git a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx index cf267da57..be05ce22e 100644 --- a/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx +++ b/packages/webapp/src/containers/CashFlow/AccountTransactions/AccountTransactionsActionsBar.tsx @@ -32,7 +32,7 @@ import { getAddMoneyOutOptions, getAddMoneyInOptions, } from '@/constants/cashflowOptions'; -import { useRefreshCashflowTransactionsInfinity } from '@/hooks/query'; +import { useRefreshCashflowTransactions } from '@/hooks/query'; import { useAccountTransactionsContext } from './AccountTransactionsProvider'; import withDialogActions from '@/containers/Dialog/withDialogActions'; @@ -64,6 +64,7 @@ function AccountTransactionsActionsBar({ uncategorizedTransationsIdsSelected, excludedTransactionsIdsSelected, openMatchingTransactionAside, + categorizedTransactionsSelected, // #withBankingActions enableMultipleCategorization, @@ -75,7 +76,7 @@ function AccountTransactionsActionsBar({ const { accountId, currentAccount } = useAccountTransactionsContext(); // Refresh cashflow infinity transactions hook. - const { refresh } = useRefreshCashflowTransactionsInfinity(); + const { refresh } = useRefreshCashflowTransactions(); const { mutateAsync: updateBankAccount } = useUpdateBankAccount(); @@ -140,7 +141,7 @@ function AccountTransactionsActionsBar({ }; // Handle the refresh button click. const handleRefreshBtnClick = () => { - refresh(); + refresh(accountId); }; const { @@ -194,7 +195,7 @@ function AccountTransactionsActionsBar({ // Handle multi select transactions for categorization or matching. const handleMultipleCategorizingSwitch = (event) => { enableMultipleCategorization(event.currentTarget.checked); - } + }; // Handle resume bank feeds syncing. const handleResumeFeedsSyncing = () => { openAlert('resume-feeds-syncing-bank-accounnt', { @@ -207,6 +208,18 @@ function AccountTransactionsActionsBar({ bankAccountId: accountId, }); }; + // Handles uncategorize the categorized transactions in bulk. + const handleUncategorizeCategorizedBulkBtnClick = () => { + openAlert('uncategorize-transactions-bulk', { + uncategorizeTransactionsIds: categorizedTransactionsSelected, + }); + }; + // Handles the delete account button click. + const handleDeleteAccountClick = () => { + openAlert('account-delete', { + accountId, + }); + }; return ( @@ -297,6 +310,14 @@ function AccountTransactionsActionsBar({ disabled={isUnexcludingLoading} /> )} + {!isEmpty(categorizedTransactionsSelected) && ( + - - - - - - ); -} - -export default compose(withDialogActions)(PaymentViaLicenseForm); diff --git a/packages/webapp/src/containers/Dialogs/PaymentViaVoucherDialog/index.tsx b/packages/webapp/src/containers/Dialogs/PaymentViaVoucherDialog/index.tsx deleted file mode 100644 index f9c9c57a0..000000000 --- a/packages/webapp/src/containers/Dialogs/PaymentViaVoucherDialog/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// @ts-nocheck -import React, { lazy } from 'react'; -import { Dialog, DialogSuspense, FormattedMessage as T } from '@/components'; - -import withDialogRedux from '@/components/DialogReduxConnect'; - -import { compose } from '@/utils'; - -// Lazy loading the content. -const PaymentViaLicenseDialogContent = lazy( - () => import('./PaymentViaVoucherDialogContent'), -); - -/** - * Payment via license dialog. - */ -function PaymentViaLicenseDialog({ dialogName, payload, isOpen }) { - return ( - } - className={'dialog--payment-via-voucher'} - autoFocus={true} - canEscapeKeyClose={true} - isOpen={isOpen} - > - - - - - ); -} - -export default compose(withDialogRedux())(PaymentViaLicenseDialog); diff --git a/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/utils.tsx b/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/utils.tsx index 03470ff50..23d5b832c 100644 --- a/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/utils.tsx +++ b/packages/webapp/src/containers/Dialogs/QuickPaymentMadeFormDialog/utils.tsx @@ -7,7 +7,7 @@ import { useFormikContext } from 'formik'; import { Intent } from '@blueprintjs/core'; import { AppToaster } from '@/components'; import { useQuickPaymentMadeContext } from './QuickPaymentMadeFormProvider'; -import { PAYMENT_MADE_ERRORS } from '@/containers/Purchases/PaymentMades/constants'; +import { PAYMENT_MADE_ERRORS } from '@/containers/Purchases/PaymentsMade/constants'; // Default initial values of payment made. export const defaultPaymentMade = { diff --git a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceive.schema.tsx b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceive.schema.tsx index ef8c20c2a..46a159e23 100644 --- a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceive.schema.tsx +++ b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceive.schema.tsx @@ -9,7 +9,7 @@ const Schema = Yup.object().shape({ .required() .nullable() .max(DATATYPES_LENGTH.STRING) - .label(intl.get('payment_receive_no_')), + .label(intl.get('payment_received_no_')), payment_date: Yup.date().required().label(intl.get('payment_date_')), deposit_account_id: Yup.number() .required() diff --git a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveForm.tsx b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveForm.tsx index b2326c525..2b9d83115 100644 --- a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveForm.tsx +++ b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/QuickPaymentReceiveForm.tsx @@ -70,7 +70,7 @@ function QuickPaymentReceiveForm({ // Handle request response success. const onSaved = (response) => { AppToaster.show({ - message: intl.get('the_payment_receive_transaction_has_been_created'), + message: intl.get('the_payment_received_transaction_has_been_created'), intent: Intent.SUCCESS, }); closeDialog(dialogName); diff --git a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/utils.tsx b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/utils.tsx index c6e7a8bb9..b02e74295 100644 --- a/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/utils.tsx +++ b/packages/webapp/src/containers/Dialogs/QuickPaymentReceiveFormDialog/utils.tsx @@ -34,7 +34,7 @@ export const transformErrors = (errors, { setFieldError }) => { if (getError('PAYMENT_RECEIVE_NO_REQUIRED')) { setFieldError( 'payment_receive_no', - intl.get('payment_receive_number_required'), + intl.get('payment_received_number_required'), ); } if (getError('INVALID_PAYMENT_AMOUNT')) { diff --git a/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerActionBar.tsx b/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerActionBar.tsx index fe2e67644..bce3d1991 100644 --- a/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerActionBar.tsx +++ b/packages/webapp/src/containers/Drawers/AccountDrawer/AccountDrawerActionBar.tsx @@ -6,6 +6,11 @@ import { NavbarGroup, Intent, NavbarDivider, + Popover, + Menu, + MenuItem, + PopoverInteractionKind, + Position, } from '@blueprintjs/core'; import { DashboardActionsBar, @@ -23,6 +28,7 @@ import withAlertsActions from '@/containers/Alert/withAlertActions'; import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils'; import { useAccountDrawerContext } from './AccountDrawerProvider'; import { compose, safeCallback } from '@/utils'; +import { CLASSES } from '@/constants'; /** * Account drawer action bar. @@ -56,6 +62,14 @@ function AccountDrawerActionBar({ const onDeleteAccount = () => { openAlert('account-delete', { accountId: account.id }); }; + // Handle inactivate button click. + const handleInactivateBtnClick = () => { + openAlert('account-inactivate', { accountId: account.id }); + }; + // Handle activate button click. + const handleActivateBtnClick = () => { + openAlert('account-activate', { accountId: account.id }); + }; return ( @@ -85,6 +99,43 @@ function AccountDrawerActionBar({ onClick={safeCallback(onDeleteAccount)} /> + {!account.active && ( + <> + +