import { request, expect, } from '~/testInit'; import Account from 'models/Account'; import { tenantWebsite, tenantFactory, loginRes } from '~/dbInit'; describe('routes: /accounts/', () => { describe('POST `/accounts`', () => { it('Should `name` be required.', async () => { const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); expect(res.body.code).equals('validation_error'); }); it('Should `account_type_id` be required.', async () => { const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); expect(res.body.code).equals('validation_error'); }); it('Should max length of `code` be limited.', async () => { const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); expect(res.body.code).equals('validation_error'); }); it('Should response type not found in case `account_type_id` was not exist.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send({ name: 'Account Name', description: account.description, account_type_id: 22, // not found. code: 123, }); expect(res.status).equals(400); expect(res.body.errors).include.something.that.deep.equals({ type: 'NOT_EXIST_ACCOUNT_TYPE', code: 200, }); }); it('Should account code be unique in the storage.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send({ name: account.name, description: account.description, account_type_id: account.accountTypeId, code: account.code, }); expect(res.status).equals(400); expect(res.body.errors).include.something.that.deep.equals({ type: 'NOT_UNIQUE_CODE', code: 100, }); }); it('Should response success with correct data form.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send({ name: 'Name', description: 'description here', code: 100, account_type_id: account.accountTypeId, parent_account_id: account.id, }); expect(res.status).equals(200); }); it('Should store account data in the storage.', async () => { const account = await tenantFactory.create('account'); const res = await request().post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send({ name: 'Account Name', description: 'desc here', account_type_id: account.accountTypeId, parent_account_id: account.id, }); const accountModel = await Account.tenant().query() .where('name', 'Account Name') .first(); expect(accountModel).a.an('object'); expect(accountModel.description).equals('desc here'); expect(accountModel.accountTypeId).equals(account.accountTypeId); expect(accountModel.parentAccountId).equals(account.id); }); }); describe('POST `/accounts/:id`', () => { it('Should `name` be required.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post(`/api/accounts/${account.id}`) .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); expect(res.body.code).equals('validation_error'); }); it('Should `account_type_id` be required.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post(`/api/accounts/${account.id}`) .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); expect(res.body.code).equals('validation_error'); }); it('Should max length of `code` be limited.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post(`/api/accounts/${account.id}`) .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); expect(res.body.code).equals('validation_error'); }); it('Should response type not found in case `account_type_id` was not exist.', async () => { const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(422); }); it('Should account code be unique in the storage.', async () => { await tenantFactory.create('account', { code: 'ABCD' }); const account = await tenantFactory.create('account'); const res = await request() .post(`/api/accounts/${account.id}`) .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send({ name: 'name', code: 'ABCD', account_type_id: account.accountTypeId, }); expect(res.status).equals(400); expect(res.body.errors).include.something.that.deep.equals({ type: 'NOT_UNIQUE_CODE', code: 100, }); }); it('Should response success with correct data form.', async () => { const account = await tenantFactory.create('account'); const res = await request() .post('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send({ name: 'Name', description: 'description here', account_type_id: account.accountTypeId, parent_account_id: account.id, code: '123', }); expect(res.status).equals(200); }); }); describe('GET: `/accounts`', () => { it('Should retrieve chart of accounts', async () => { await tenantFactory.create('resource', { name: 'accounts' }); const account = await tenantFactory.create('account'); await tenantFactory.create('account', { parent_account_id: account.id }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(200); expect(res.body.accounts.length).above(0); }); it('Should retrieve accounts based on view roles conditionals of the custom view.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const accountTypeField = await tenantFactory.create('resource_field', { label_name: 'Account type', key: 'type', resource_id: resource.id, active: true, predefined: true, }); const accountNameField = await tenantFactory.create('resource_field', { label_name: 'Account Name', key: 'name', resource_id: resource.id, active: true, predefined: true, }); const accountsView = await tenantFactory.create('view', { name: 'Accounts View', resource_id: resource.id, roles_logic_expression: '1 AND 2', }); const accountType = await tenantFactory.create('account_type'); await tenantFactory.create('view_role', { view_id: accountsView.id, index: 1, field_id: accountTypeField.id, value: accountType.name, comparator: 'equals', }); await tenantFactory.create('view_role', { view_id: accountsView.id, index: 2, field_id: accountNameField.id, value: 'account', comparator: 'contains', }); await tenantFactory.create('account', { name: 'account-1', account_type_id: accountType.id }); await tenantFactory.create('account', { name: 'account-2', account_type_id: accountType.id }); await tenantFactory.create('account', { name: 'account-3' }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ custom_view_id: accountsView.id }) .send(); expect(res.body.accounts.length).equals(2); expect(res.body.accounts[0].name).equals('account-1'); expect(res.body.accounts[1].name).equals('account-2'); expect(res.body.accounts[0].account_type_id).equals(accountType.id); expect(res.body.accounts[1].account_type_id).equals(accountType.id); }); it('Should retrieve accounts based on view roles conditionals with relation join column.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const accountTypeField = await tenantFactory.create('resource_field', { label_name: 'Account type', key: 'type', resource_id: resource.id, active: true, predefined: true, }); const accountsView = await tenantFactory.create('view', { name: 'Accounts View', resource_id: resource.id, roles_logic_expression: '1', }); const accountType = await tenantFactory.create('account_type'); const accountsViewRole = await tenantFactory.create('view_role', { view_id: accountsView.id, index: 1, field_id: accountTypeField.id, value: accountType.name, comparator: 'equals', }); await tenantFactory.create('account', { account_type_id: accountType.id }); await tenantFactory.create('account'); await tenantFactory.create('account'); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ custom_view_id: accountsView.id }) .send(); expect(res.body.accounts.length).equals(1); expect(res.body.accounts[0].account_type_id).equals(accountType.id); }); it('Should retrieve accounts and child accounts in nested set graph.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const account1 = await tenantFactory.create('account'); const account2 = await tenantFactory.create('account', { parent_account_id: account1.id }); const account3 = await tenantFactory.create('account', { parent_account_id: account2.id }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(200); const foundAccount = res.body.accounts.find(a => a.id === account1.id); expect(foundAccount.id).equals(account1.id); expect(foundAccount.children[0].id).equals(account2.id); expect(foundAccount.children[0].children[0].id).equals(account3.id); }); it('Should retrieve bad request when `filter_roles.*.comparator` not associated to `field_key`.', () => { }); it('Should retrieve bad request when `filter_roles.*.field_key` not found in accounts resource.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const account1 = await tenantFactory.create('account', { name: 'ahmed' }); const account2 = await tenantFactory.create('account'); const account3 = await tenantFactory.create('account'); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ stringified_filter_roles: JSON.stringify([{ condition: 'AND', field_key: 'not_found', comparator: 'equals', value: 'ahmed', }, { condition: 'AND', field_key: 'mybe_found', comparator: 'equals', value: 'ahmed', }]), }); expect(res.body.errors).include.something.that.deep.equals({ type: 'ACCOUNTS.RESOURCE.HAS.NO.GIVEN.FIELDS', code: 500, }); }); it('Should retrieve bad request when `filter_roles.*.condition` is invalid.', async () => { }); it('Should retrieve filtered accounts according to the given account type filter condition.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const keyField = await tenantFactory.create('resource_field', { key: 'type', resource_id: resource.id, }); const nameFiled = await tenantFactory.create('resource_field', { key: 'name', resource_id: resource.id, }); const accountType = await tenantFactory.create('account_type'); const account1 = await tenantFactory.create('account', { name: 'ahmed', account_type_id: accountType.id }); const account2 = await tenantFactory.create('account'); const account3 = await tenantFactory.create('account'); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ stringified_filter_roles: JSON.stringify([{ condition: '&&', field_key: 'type', comparator: 'equals', value: accountType.name, }, { condition: '&&', field_key: 'name', comparator: 'equals', value: 'ahmed', }]), }); expect(res.body.accounts.length).equals(1); }); it('Shoud retrieve filtered accounts according to the given account description filter condition.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const resourceField = await tenantFactory.create('resource_field', { key: 'description', resource_id: resource.id, }); const account1 = await tenantFactory.create('account', { name: 'ahmed', description: 'here' }); const account2 = await tenantFactory.create('account'); const account3 = await tenantFactory.create('account'); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ stringified_filter_roles: JSON.stringify([{ condition: 'AND', field_key: resourceField.key, comparator: 'contain', value: 'here', }]), }); expect(res.body.accounts.length).equals(1); expect(res.body.accounts[0].description).equals('here'); }); it('Should retrieve filtered accounts based on given filter roles between OR conditions.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const resourceField = await tenantFactory.create('resource_field', { key: 'description', resource_id: resource.id, }); const resourceCodeField = await tenantFactory.create('resource_field', { key: 'code', resource_id: resource.id, }); const account1 = await tenantFactory.create('account', { name: 'ahmed', description: 'target' }); const account2 = await tenantFactory.create('account', { description: 'target' }); const account3 = await tenantFactory.create('account'); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ stringified_filter_roles: JSON.stringify([{ condition: '&&', field_key: resourceField.key, comparator: 'contain', value: 'target', }, { condition: '||', field_key: resourceCodeField.key, comparator: 'equals', value: 'ahmed', }]), }); expect(res.body.accounts.length).equals(2); expect(res.body.accounts[0].description).equals('target'); expect(res.body.accounts[1].description).equals('target'); expect(res.body.accounts[0].name).equals('ahmed'); }); it('Should retrieve filtered accounts from custom view and filter roles.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const accountTypeField = await tenantFactory.create('resource_field', { key: 'type', resource_id: resource.id, }); const accountDescriptionField = await tenantFactory.create('resource_field', { key: 'description', resource_id: resource.id, }); const accountType = await tenantFactory.create('account_type', { name: 'type-name' }); const account1 = await tenantFactory.create('account', { name: 'ahmed-1' }); const account2 = await tenantFactory.create('account', { name: 'ahmed-2', account_type_id: accountType.id, description: 'target' }); const account3 = await tenantFactory.create('account', { name: 'ahmed-3' }); const accountsView = await tenantFactory.create('view', { name: 'Accounts View', resource_id: resource.id, roles_logic_expression: '1', }); const accountsViewRole = await tenantFactory.create('view_role', { view_id: accountsView.id, field_id: accountTypeField.id, index: 1, value: 'type-name', comparator: 'equals', }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ custom_view_id: accountsView.id, stringified_filter_roles: JSON.stringify([{ condition: 'AND', field_key: 'description', comparator: 'contain', value: 'target', }]), }); expect(res.body.accounts.length).equals(1); expect(res.body.accounts[0].name).equals('ahmed-2'); expect(res.body.accounts[0].description).equals('target'); }); it('Should validate the given `column_sort_order` column on the accounts resource.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ column_sort_by: 'not_found', sort_order: 'desc', }); expect(res.body.errors).include.something.that.deep.equals({ type: 'COLUMN.SORT.ORDER.NOT.FOUND', code: 300, }); }); it('Should sorting the given `column_sort_order` column on asc direction,', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const resourceField = await tenantFactory.create('resource_field', { key: 'name', resource_id: resource.id, }); const accounts1 = await tenantFactory.create('account', { name: 'A' }); const accounts2 = await tenantFactory.create('account', { name: 'B' }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ column_sort_by: 'name', sort_order: 'asc', }); const AAccountIndex = res.body.accounts.findIndex(a => a.name === 'B'); const BAccountIndex = res.body.accounts.findIndex(a => a.name === 'A'); expect(AAccountIndex).above(BAccountIndex); }); it('Should sorting the given `column_sort_order` columnw with relation on another table on asc direction.', async () => { const resource = await tenantFactory.create('resource', { name: 'accounts' }); const resourceField = await tenantFactory.create('resource_field', { key: 'type', resource_id: resource.id, }); const accounts1 = await tenantFactory.create('account', { name: 'A' }); const accounts2 = await tenantFactory.create('account', { name: 'B' }); const res = await request() .get('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ column_sort_by: 'name', sort_order: 'asc', }); expect(res.body.accounts[0].name).equals('A'); expect(res.body.accounts[1].name).equals('B'); }); }); describe('DELETE: `/accounts`', () => { it('Should response not found in case account was not exist.', async () => { const res = await request() .delete('/api/accounts/10') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(404); }); it('Should delete the give account from the storage.', async () => { const account = await tenantFactory.create('account'); await request() .delete(`/api/accounts/${account.id}`) .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); const foundAccounts = await Account.tenant().query().where('id', account.id); expect(foundAccounts).to.have.lengthOf(0); }); it('Should not delete the given account in case account has associated transactions.', async () => { const accountTransaction = await tenantFactory.create('account_transaction'); const res = await request() .delete(`/api/accounts/${accountTransaction.accountId}`) .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .send(); expect(res.status).equals(400); expect(res.body.errors).include.something.that.deep.equals({ type: 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS', code: 100, }); }); }); describe('DELETE: `/accounts?ids=`', () => { it('Should response in case on of accounts ids was not exists.', async () => { const res = await request() .delete('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ ids: [100, 200], }) .send(); expect(res.status).equals(404); expect(res.body.errors).include.something.that.deep.equals({ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200, ids: [100, 200], }); }); it('Should response bad request in case one of accounts has transactions.', async () => { const accountTransaction = await tenantFactory.create('account_transaction'); const accountTransaction2 = await tenantFactory.create('account_transaction'); const res = await request() .delete('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ ids: [accountTransaction.accountId, accountTransaction2.accountId], }) .send(); expect(res.body.errors).include.something.that.deep.equals({ type: 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS', code: 300, ids: [accountTransaction.accountId, accountTransaction2.accountId], }); }); it('Should delete the given accounts from the storage.', async () => { const account1 = await tenantFactory.create('account'); const account2 = await tenantFactory.create('account'); const res = await request() .delete('/api/accounts') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ ids: [account1.id, account2.id], }) .send(); expect(res.status).equals(200); const foundAccounts = await Account.tenant().query() .whereIn('id', [account1.id, account2.id]); expect(foundAccounts.length).equals(0); }); }); describe('POST: `/api/accounts/bulk/activate|inactivate', () => { it('Should response if there one of accounts ids were not found.', async () => { const res = await request() .post('/api/accounts/bulk/activate') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ ids: [123123, 321321], }) .send(); expect(res.status).equals(400); expect(res.body.errors).include.something.that.deep.equals({ type: 'ACCOUNTS.NOT.FOUND', code: 200, }); }); it('Should activate all the given accounts.', async () => { const accountA = await tenantFactory.create('account', { active: 1 }); const accountB = await tenantFactory.create('account', { active: 1 }); const res = await request() .post('/api/accounts/bulk/inactivate') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ ids: [accountA.id, accountB.id], }) .send(); const updatedAccounts = await Account.tenant().query().whereIn('id', [accountA.id, accountB.id]); expect(updatedAccounts[0].active).equals(0); expect(updatedAccounts[1].active).equals(0); }); it('Should inactivate all the given accounts.', async () => { const accountA = await tenantFactory.create('account', { active: 0 }); const accountB = await tenantFactory.create('account', { active: 0 }); const res = await request() .post('/api/accounts/bulk/activate') .set('x-access-token', loginRes.body.token) .set('organization-id', tenantWebsite.organizationId) .query({ ids: [accountA.id, accountB.id], }) .send(); const updatedAccounts = await Account.tenant().query().whereIn('id', [accountA.id, accountB.id]); expect(updatedAccounts[0].active).equals(1); expect(updatedAccounts[1].active).equals(1); }); }); });