add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,887 @@
import {
request,
expect,
} from '~/testInit';
import moment from 'moment';
import ManualJournal from 'models/ManualJournal';
import AccountTransaction from 'models/AccountTransaction';
import AccountBalance from 'models/AccountBalance';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: `/accounting`', () => {
describe('route: `/accounting/make-journal-entries`', async () => {
it('Should sumation of credit or debit does not equal zero.', async () => {
const account = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '123',
reference: 'ASC',
entries: [
{
index: 1,
credit: 0,
debit: 0,
account_id: account.id,
},
{
index: 2,
credit: 0,
debit: 0,
account_id: account.id,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
code: 400,
});
});
it('Should all credit entries equal debit.', async () => {
const account = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '123',
entries: [
{
index: 1,
credit: 1000,
debit: 0,
account_id: account.id,
},
{
index: 2,
credit: 0,
debit: 500,
account_id: account.id,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'CREDIT.DEBIT.NOT.EQUALS',
code: 100,
});
});
it('Should journal reference be not exists.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const account = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: manualJournal.journalNumber,
entries: [
{
index: 1,
credit: 1000,
debit: 0,
account_id: account.id,
},
{
index: 2,
credit: 0,
debit: 1000,
account_id: account.id,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'JOURNAL.NUMBER.ALREADY.EXISTS',
code: 300,
});
});
it('Should response error in case account id not exists in one of the given entries.', async () => {
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '123',
entries: [
{
index: 1,
credit: 1000,
debit: 0,
account_id: 12,
},
{
index: 2,
credit: 0,
debit: 1000,
account_id: 12,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'ACCOUNTS.IDS.NOT.FOUND',
code: 200,
});
});
it('Should discard journal entries that has null credit and debit amount.', async () => {
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '1000',
entries: [
{
index: 1,
credit: null,
debit: 0,
account_id: account1.id,
},
{
index: 2,
credit: null,
debit: 0,
account_id: account2.id,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
code: 400,
});
});
it('Should validate the customers and vendors contact if were not found on the storage.', async () => {
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '1000',
entries: [
{
index: 1,
credit: null,
debit: 1000,
account_id: account1.id,
contact_type: 'customer',
contact_id: 100,
},
{
index: 2,
credit: 1000,
debit: 0,
account_id: account1.id,
contact_type: 'vendor',
contact_id: 300,
},
],
});
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMERS.CONTACTS.NOT.FOUND', code: 500, ids: [100],
});
expect(res.body.errors).include.something.deep.equals({
type: 'VENDORS.CONTACTS.NOT.FOUND', code: 600, ids: [300],
})
});
it('Should customer contact_type with receivable accounts type.', async () => {
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const customer = await tenantFactory.create('customer');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '1000',
entries: [
{
index: 1,
credit: null,
debit: 1000,
account_id: account1.id,
contact_type: 'customer',
contact_id: 100,
},
{
index: 2,
credit: 1000,
debit: 0,
account_id: account1.id,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMERS.NOT.WITH.RECEIVABLE.ACCOUNT',
code: 700,
indexes: [1]
});
});
it('Should account receivable entries has contact_id and contact_type customer.', async () => {
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '1000',
entries: [
{
index: 1,
credit: null,
debit: 1000,
account_id: 10,
},
{
index: 2,
credit: 1000,
debit: 0,
account_id: 1,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'RECEIVABLE.ENTRIES.HAS.NO.CUSTOMERS', code: 900, indexes: [1],
});
});
it('Should account payable entries has contact_id and contact_type vendor.', async () => {
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '1000',
entries: [
{
index: 1,
credit: null,
debit: 1000,
account_id: 10,
},
{
index: 2,
credit: 1000,
debit: 0,
account_id: 11,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'PAYABLE.ENTRIES.HAS.NO.VENDORS', code: 1000, indexes: [2]
});
});
it('Should retrieve account_id is not receivable in case contact_type equals customer.', async () => {
const customer = await tenantFactory.create('customer');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '1000',
entries: [
{
index: 1,
credit: null,
debit: 1000,
account_id: 2,
contact_id: customer.id,
contact_type: 'customer',
},
{
index: 2,
credit: 1000,
debit: 0,
account_id: 11,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMERS.NOT.WITH.RECEIVABLE.ACCOUNT', code: 700, indexes: [1],
});
});
it('Should store manual journal transaction to the storage.', async () => {
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date('2020-2-2').toISOString(),
journal_number: '1000',
reference: '2000',
description: 'Description here.',
entries: [
{
index: 1,
credit: 1000,
account_id: account1.id,
},
{
index: 2,
debit: 1000,
account_id: account2.id,
},
],
});
const foundManualJournal = await ManualJournal.tenant().query();
expect(foundManualJournal.length).equals(1);
expect(foundManualJournal[0].reference).equals('2000');
expect(foundManualJournal[0].journalNumber).equals('1000');
expect(foundManualJournal[0].transactionType).equals('Journal');
expect(foundManualJournal[0].amount).equals(1000);
expect(moment(foundManualJournal[0].date).format('YYYY-MM-DD')).equals('2020-02-02');
expect(foundManualJournal[0].description).equals('Description here.');
expect(foundManualJournal[0].userId).to.be.a('number');
});
it('Should store journal transactions to the storage.', async () => {
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const res = await request()
.post('/api/accounting/make-journal-entries')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
journal_number: '1',
date: new Date('2020-1-1').toISOString(),
reference: '1000',
memo: 'Description here.',
entries: [
{
index: 1,
credit: 1000,
account_id: account1.id,
note: 'First note',
},
{
index: 2,
debit: 1000,
account_id: account2.id,
note: 'Second note',
},
],
});
const foundAccountsTransactions = await AccountTransaction.tenant().query();
expect(foundAccountsTransactions.length).equals(2);
expect(foundAccountsTransactions[0].credit).equals(1000);
expect(foundAccountsTransactions[0].debit).equals(null);
expect(foundAccountsTransactions[0].accountId).equals(account1.id);
expect(foundAccountsTransactions[0].note).equals('First note');
expect(foundAccountsTransactions[0].referenceType).equals('Journal');
expect(foundAccountsTransactions[0].userId).equals(1);
expect(foundAccountsTransactions[1].credit).equals(null);
expect(foundAccountsTransactions[1].debit).equals(1000);
expect(foundAccountsTransactions[1].accountId).equals(account2.id);
expect(foundAccountsTransactions[1].note).equals('Second note');
expect(foundAccountsTransactions[1].referenceType).equals('Journal');
expect(foundAccountsTransactions[1].userId).equals(1);
});
});
describe('route: POST: `/accounting/manual-journal/:id`', () => {
it('Should response not found in case manual journal transaction was not exists.', async () => {
const res = await request()
.post('/api/manual-journal/1000')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
});
it('Should sumation of credit or debit be equal zero.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '123',
reference: 'ASC',
entries: [
{
credit: 0,
debit: 0,
account_id: 2000,
},
{
credit: 0,
debit: 0,
account_id: 2000,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
code: 400,
});
});
it('Should all credit and debit sumation be equal.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: new Date().toISOString(),
journal_number: '123',
reference: 'ASC',
entries: [
{
credit: 0,
debit: 2000,
account_id: 2000,
},
{
credit: 1000,
debit: 0,
account_id: 2000,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'CREDIT.DEBIT.NOT.EQUALS', code: 100,
});
});
it('Should response journal number already exists in case another one on the storage.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const manualJournal2 = await tenantFactory.create('manual_journal');
const jsonBody = {
date: new Date().toISOString(),
reference: 'ASC',
entries: [
{
credit: 0,
debit: 2000,
account_id: 2000,
},
{
credit: 1000,
debit: 0,
account_id: 2000,
},
],
};
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
...jsonBody,
journal_number: manualJournal2.journalNumber,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'JOURNAL.NUMBER.ALREADY.EXISTS', code: 300,
});
});
it('Should not response journal number exists in case was unique number.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const manualJournal2 = await tenantFactory.create('manual_journal');
const jsonBody = {
date: new Date().toISOString(),
reference: 'ASC',
entries: [
{
credit: 0,
debit: 2000,
account_id: 2000,
},
{
credit: 1000,
debit: 0,
account_id: 2000,
},
],
};
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
...jsonBody,
journal_number: manualJournal.journalNumber,
});
expect(res.status).equals(400);
expect(res.body.errors).not.include.something.that.deep.equal({
type: 'JOURNAL.NUMBER.ALREADY.EXISTS', code: 300,
});
})
it('Should response error in case account id not exists in one of the given entries.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const manualJournal2 = await tenantFactory.create('manual_journal');
const jsonBody = {
date: new Date().toISOString(),
reference: 'ASC',
entries: [
{
credit: 0,
debit: 1000,
account_id: 2000,
},
{
credit: 1000,
debit: 0,
account_id: 2000,
},
],
};
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
...jsonBody,
journal_number: manualJournal.journalNumber,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equal({
type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200,
});
});
it('Should update the given manual journal transaction in the storage.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
journal_number: '123',
date: new Date().toISOString(),
reference: 'ABC',
description: 'hello world',
entries: [
{
credit: 0,
debit: 1000,
account_id: account1.id,
},
{
credit: 1000,
debit: 0,
account_id: account2.id,
},
],
});
const foundManualJournal = await ManualJournal.tenant().query()
.where('id', manualJournal.id);
expect(foundManualJournal.length).equals(1);
expect(foundManualJournal[0].journalNumber).equals('123');
expect(foundManualJournal[0].reference).equals('ABC');
expect(foundManualJournal[0].description).equals('hello world');
});
it('Should update account transactions that associated to the manual journal transaction.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const account1 = await tenantFactory.create('account');
const account2 = await tenantFactory.create('account');
const transaction = await tenantFactory.create('account_transaction', {
reference_type: 'Journal',
reference_id: manualJournal.id,
});
const transaction2 = await tenantFactory.create('account_transaction', {
reference_type: 'Journal',
reference_id: manualJournal.id,
});
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
journal_number: '123',
date: new Date().toISOString(),
reference: 'ABC',
description: 'hello world',
entries: [
{
credit: 0,
debit: 1000,
account_id: account1.id,
note: 'hello 1',
},
{
credit: 1000,
debit: 0,
account_id: account2.id,
note: 'hello 2',
},
],
});
const foundTransactions = await AccountTransaction.tenant().query();
expect(foundTransactions.length).equals(2);
expect(foundTransactions[0].credit).equals(0);
expect(foundTransactions[0].debit).equals(1000);
expect(foundTransactions[0].accountId).equals(account1.id);
expect(foundTransactions[0].note).equals('hello 1');
expect(foundTransactions[1].credit).equals(1000);
expect(foundTransactions[1].debit).equals(0);
expect(foundTransactions[1].accountId).equals(account2.id);
expect(foundTransactions[1].note).equals('hello 2');
});
});
describe('route: DELETE `accounting/manual-journals/:id`', () => {
it('Should response not found in case the manual journal transaction was not found.', async() => {
const res = await request()
.delete('/api/accounting/manual-journals/1000')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equal({
type: 'MANUAL.JOURNAL.NOT.FOUND', code: 100,
});
});
it('Should delete manual journal transactions from storage.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const res = await request()
.delete(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const foundManualTransaction = await ManualJournal.tenant().query()
.where('id', manualJournal.id).first();
expect(foundManualTransaction).equals(undefined);
});
it('Should delete associated transactions of journal transaction.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const transaction1 = await tenantFactory.create('account_transaction', {
reference_type: 'Journal', reference_id: manualJournal.id,
});
const transaction2 = await tenantFactory.create('account_transaction', {
reference_type: 'Journal', reference_id: manualJournal.id,
});
const res = await request()
.delete(`/api/accounting/manual-journals/${manualJournal.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const foundTransactions = await AccountTransaction.tenant().query();
expect(foundTransactions.length).equals(0);
});
it('Should revert accounts balance after delete account transactions.', () => {
});
});
describe('route: GET `accounting/manual-journals/:id`', () => {
it('Should response not found in case manual transaction id was not exists.', async () => {
const res = await request()
.delete('/api/accounting/manual-journals/100')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'MANUAL.JOURNAL.NOT.FOUND', code: 100,
});
});
it('Should response manual transaction and transactions metadata.', async () => {
});
});
describe('route: `accounting/manual-journals`', async () => {
it('Should retrieve all manual journals with pagination meta.', async () => {
const manualJournal1 = await tenantFactory.create('manual_journal');
const manualJournal2 = await tenantFactory.create('manual_journal');
const res = await request()
.get('/api/accounting/manual-journals')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
expect(res.body.manualJournals.results).to.be.a('array');
expect(res.body.manualJournals.results.length).equals(2);
});
});
describe('route: POST `accounting/manual-journals/:id/publish`', () => {
it('Should response not found in case the manual journal id was not exists.', async () => {
const manualJournal = await tenantFactory.create('manual_journal');
const res = await request()
.post('/api/accounting/manual-journals/123/publish')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'MANUAL.JOURNAL.NOT.FOUND', code: 100,
});
});
it('Should response published ready.', async () => {
const manualJournal = await tenantFactory.create('manual_journal', { status: 1 });
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}/publish`)
.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: 'MANUAL.JOURNAL.PUBLISHED.ALREADY', code: 200,
});
});
it('Should update all accounts transactions to not draft.', async () => {
const manualJournal = await tenantFactory.create('manual_journal', { status: 0 });
const transaction = await tenantFactory.create('account_transaction', {
reference_type: 'Journal',
reference_id: manualJournal.id,
draft: 1,
});
const transaction2 = await tenantFactory.create('account_transaction', {
reference_type: 'Journal',
reference_id: manualJournal.id,
draft: 1,
});
const res = await request()
.post(`/api/accounting/manual-journals/${manualJournal.id}/publish`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const foundTransactions = await AccountTransaction.tenant().query()
.whereIn('id', [transaction.id, transaction2.id]);
expect(foundTransactions[0].draft).equals(0);
expect(foundTransactions[1].draft).equals(0);
});
it('Should increment/decrement accounts balance.', () => {
});
});
describe('route: `/accounting/quick-journal-entries`', async () => {
it('Shoud `credit_account_id` be required', () => {
});
it('Should `debit_account_id` be required.', () => {
});
it('Should `amount` be required.', () => {
});
it('Should credit account id be exists.', () => {
});
it('Should debit account id be exists.', () => {
});
it('Should store the quick journal entry to the storage.', () => {
});
});
});

View File

@@ -0,0 +1,758 @@
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);
});
});
});

View File

@@ -0,0 +1,288 @@
import { request, expect, createUser } from '~/testInit';
import { hashPassword } from 'utils';
import knex from '@/database/knex';
import {
tenantWebsite,
tenantFactory,
systemFactory,
loginRes
} from '~/dbInit';
import TenantUser from 'models/TenantUser';
import PasswordReset from '@/system/models/PasswordReset';
import SystemUser from '@/system/models/SystemUser';
describe('routes: /auth/', () => {
describe('POST `/api/auth/login`', () => {
it('Should `crediential` be required.', async () => {
const res = await request().post('/api/auth/login').send({});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('crediential');
});
it('Should `password` be required.', async () => {
const res = await request().post('/api/auth/login').send();
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('password');
});
it('Should the min length of the `password` be 5 ch.', async () => {
const res = await request().post('/api/auth/login').send({
crediential: 'admin@admin.com',
password: 'test',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('password');
});
it('Should be a valid email format in crediential attribute.', async () => {
const res = await request().post('/api/auth/login').send({
crediential: 'admin',
password: 'test',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('password');
});
it('Should not authenticate with wrong user email and password.', async () => {
const res = await request().post('/api/auth/login').send({
crediential: 'admin@admin.com',
password: 'admin',
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'INVALID_DETAILS', code: 100,
});
});
it('Should not authenticate in case user was not active.', async () => {
const user = await createUser(tenantWebsite, {
active: false,
email: 'admin@admin.com',
});
const res = await request().post('/api/auth/login').send({
crediential: 'admin@admin.com',
password: 'admin',
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'USER_INACTIVE', code: 110,
});
});
it('Should authenticate with correct email and password and active user.', async () => {
const user = await createUser(tenantWebsite, {
email: 'admin@admin.com',
});
const res = await request().post('/api/auth/login').send({
crediential: user.email,
password: 'admin',
});
expect(res.status).equals(200);
});
it('Should autheticate success with correct phone number and password.', async () => {
const password = await hashPassword('admin');
const user = await createUser(tenantWebsite, {
phone_number: '0920000000',
password,
});
const res = await request().post('/api/auth/login').send({
crediential: user.email,
password: 'admin',
});
expect(res.status).equals(200);
});
it('Should last login date be saved after success login.', async () => {
const user = await createUser(tenantWebsite, {
email: 'admin@admin.com',
});
const res = await request().post('/api/auth/login').send({
crediential: user.email,
password: 'admin',
});
const foundUserAfterUpdate = await TenantUser.tenant().query()
.where('email', user.email)
.where('first_name', user.first_name)
.first();
expect(res.status).equals(200);
expect(foundUserAfterUpdate.lastLoginAt).to.not.be.null;
});
});
describe('POST: `/auth/send_reset_password`', () => {
it('Should `email` be required.', async () => {
const res = await request().post('/api/auth/send_reset_password').send();
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
});
it('Should response unproccessable if the email address was invalid.', async () => {
const res = await request().post('/api/auth/send_reset_password').send({
email: 'invalid_email',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
});
it('Should response unproccessable if the email address was not exist.', async () => {
const res = await request().post('/api/auth/send_reset_password').send({
email: 'admin@admin.com',
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'EMAIL.NOT.REGISTERED', code: 200,
});
});
it('Should delete all already tokens that associate to the given email.', async () => {
const user = await createUser(tenantWebsite);
const token = '123123';
await knex('password_resets').insert({ email: user.email, token });
await request().post('/api/auth/send_reset_password').send({
email: user.email,
});
const oldPasswordToken = await knex('password_resets').where('token', token);
expect(oldPasswordToken).to.have.lengthOf(0);
});
it('Should store new token associate with the given email.', async () => {
const user = await createUser(tenantWebsite);
await request().post('/api/auth/send_reset_password').send({
email: user.email,
});
const token = await knex('password_resets').where('email', user.email);
expect(token).to.have.lengthOf(1);
});
it('Should response success if the email was exist.', async () => {
const user = await createUser(tenantWebsite);
const res = await request().post('/api/auth/send_reset_password').send({
email: user.email,
});
expect(res.status).equals(200);
});
});
describe('POST: `/auth/reset/:token`', () => {
// it('Should response forbidden if the token was invalid.', () => {
// });
it('Should response forbidden if the token was expired.', () => {
});
it('Should `password` be required.', async () => {
const user = await createUser(tenantWebsite);
const passwordReset = await systemFactory.create('password_reset', {
email: user.email,
});
const res = await request()
.post(`/api/auth/reset/${passwordReset.token}`)
.send();
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('password');
});
it('Should password and confirm_password be equal.', async () => {
const user = await createUser(tenantWebsite);
const passwordReset = await systemFactory.create('password_reset', {
email: user.email,
});
const res = await request()
.post(`/api/auth/reset/${passwordReset.token}`)
.send({
password: '123123',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('password');
});
it('Should response success with correct data form.', async () => {
const user = await createUser(tenantWebsite);
const passwordReset = await systemFactory.create('password_reset', {
email: user.email,
});
const res = await request()
.post(`/api/auth/reset/${passwordReset.token}`)
.send({
password: '123123',
confirm_password: '123123',
});
expect(res.status).equals(200);
});
it('Should token be deleted after success response.', async () => {
const user = await createUser(tenantWebsite);
const passwordReset = await systemFactory.create('password_reset', {
email: user.email,
});
await request()
.post(`/api/auth/reset/${passwordReset.token}`)
.send({
password: '123123',
confirm_password: '123123',
});
const foundTokens = await PasswordReset.query().where('email', passwordReset.email);
expect(foundTokens).to.have.lengthOf(0);
});
it('Should password be updated after success response.', async () => {
const user = await createUser(tenantWebsite);
const passwordReset = await systemFactory.create('password_reset', {
email: user.email,
});
const res = await request().post(`/api/auth/reset/${passwordReset.token}`).send({
password: '123123',
confirm_password: '123123',
});
const systemUserPasswordUpdated = await SystemUser.query()
.where('id', user.id).first();
expect(systemUserPasswordUpdated.id).equals(user.id);
expect(systemUserPasswordUpdated.password).not.equals(user.password);
});
});
});

View File

@@ -0,0 +1,541 @@
import moment from 'moment';
import {
request,
expect,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
import { iteratee } from 'lodash';
let creditAccount;
let debitAccount;
let incomeAccount;
let incomeType;
describe('routes: `/financial_statements`', () => {
beforeEach(async () => {
const accountTransactionMixied = { date: '2020-1-10' };
// Expense --
// 1000 Credit - Cash account
// 1000 Debit - Bank account.
await tenantFactory.create('account_transaction', {
credit: 1000, debit: 0, account_id: 2, referenceType: 'Expense',
referenceId: 1, ...accountTransactionMixied,
});
await tenantFactory.create('account_transaction', {
credit: 0, debit: 1000, account_id: 7, referenceType: 'Expense',
referenceId: 1, ...accountTransactionMixied,
});
// Jounral
// 4000 Credit - Opening balance account.
// 2000 Debit - Bank account
// 2000 Debit - Bank account
await tenantFactory.create('account_transaction', {
credit: 4000, debit: 0, account_id: 5, ...accountTransactionMixied,
});
await tenantFactory.create('account_transaction', {
debit: 2000, credit: 0, account_id: 2, ...accountTransactionMixied,
});
await tenantFactory.create('account_transaction', {
debit: 2000, credit: 0, account_id: 2, ...accountTransactionMixied,
});
// Income Journal.
// 2000 Credit - Income account.
// 2000 Debit - Bank account.
await tenantFactory.create('account_transaction', {
credit: 2000, account_id: 4, ...accountTransactionMixied
});
await tenantFactory.create('account_transaction', {
debit: 2000, credit: 0, account_id: 2, ...accountTransactionMixied,
});
// -----------------------------------------
// Bank account balance = 5000 | Opening balance account balance = 4000
// Expense account balance = 1000 | Income account balance = 2000
});
describe('routes: `financial_statements/balance_sheet`', () => {
it('Should response unauthorzied in case the user was not authorized.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.send();
expect(res.status).equals(401);
});
it('Should retrieve query of the balance sheet with default values.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'year',
from_date: '2020-01-01',
to_date: '2020-02-01',
})
.send();
expect(res.body.query.display_columns_by).equals('year');
expect(res.body.query.from_date).equals('2020-01-01');
expect(res.body.query.to_date).equals('2020-02-01');
expect(res.body.query.number_format.no_cents).equals(false);
expect(res.body.query.number_format.divide_1000).equals(false);
expect(res.body.query.none_zero).equals(false);
});
it('Should retrieve assets and liabilities/equity section.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'year',
})
.send();
expect(res.body.balance_sheet[0].name).equals('Assets');
expect(res.body.balance_sheet[1].name).equals('Liabilities and Equity');
expect(res.body.balance_sheet[0].section_type).equals('assets');
expect(res.body.balance_sheet[1].section_type).equals('liabilities_equity');
expect(res.body.balance_sheet[0].type).equals('section');
expect(res.body.balance_sheet[1].type).equals('section');
});
it('Should retrieve assets and liabilities/equity total of each section.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
to_date: '2020-12-10',
})
.send();
expect(res.body.balance_sheet[0].total.amount).equals(5000);
expect(res.body.balance_sheet[1].total.amount).equals(4000);
});
it('Should retrieve the asset and liabilities/equity accounts.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_type: 'total',
from_date: '2012-01-01',
to_date: '2032-02-02',
})
.send();
expect(res.body.balance_sheet[0].children).to.be.a('array');
expect(res.body.balance_sheet[0].children).to.be.a('array');
expect(res.body.balance_sheet[0].children.length).is.not.equals(0);
expect(res.body.balance_sheet[1].children.length).is.not.equals(0);
expect(res.body.balance_sheet[1].children[0].children.length).is.not.equals(0);
expect(res.body.balance_sheet[1].children[1].children.length).is.not.equals(0);
});
it('Should retrieve assets/liabilities total balance between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_type: 'total',
from_date: '2012-01-01',
to_date: '2032-02-02',
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: 1001,
index: null,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
children: [],
total: { formatted_amount: 5000, amount: 5000, date: '2032-02-02' }
});
expect(res.body.accounts[1].children).include.something.deep.equals({
id: 1000,
index: null,
name: creditAccount.name,
code: creditAccount.code,
parentAccountId: null,
children: [],
total: { formatted_amount: 4000, amount: 4000, date: '2032-02-02' }
});
});
it('Should retrieve asset/liabilities balance sheet with display columns by `year`.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'year',
display_columns_type: 'date_periods',
from_date: '2012-01-01',
to_date: '2018-02-02',
})
.send();
expect(res.body.accounts[0].children[0].total_periods.length).equals(7);
expect(res.body.accounts[1].children[0].total_periods.length).equals(7);
expect(res.body.accounts[0].children[0].total_periods).deep.equals([
{
amount: 0,
formatted_amount: 0,
date: '2012',
},
{
amount: 0,
formatted_amount: 0,
date: '2013',
},
{
amount: 0,
formatted_amount: 0,
date: '2014',
},
{
amount: 0,
formatted_amount: 0,
date: '2015',
},
{
amount: 0,
formatted_amount: 0,
date: '2016',
},
{
amount: 0,
formatted_amount: 0,
date: '2017',
},
{
amount: 0,
formatted_amount: 0,
date: '2018',
},
]);
});
it('Should retrieve balance sheet with display columns by `day`.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'day',
display_columns_type: 'date_periods',
from_date: '2020-01-08',
to_date: '2020-01-12',
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: debitAccount.id,
index: debitAccount.index,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
children: [],
total_periods: [
{ date: '2020-01-08', formatted_amount: 0, amount: 0 },
{ date: '2020-01-09', formatted_amount: 0, amount: 0 },
{ date: '2020-01-10', formatted_amount: 5000, amount: 5000 },
{ date: '2020-01-11', formatted_amount: 5000, amount: 5000 },
{ date: '2020-01-12', formatted_amount: 5000, amount: 5000 },
],
total: { formatted_amount: 5000, amount: 5000, date: '2020-01-12' }
});
expect(res.body.accounts[1].children).include.something.deep.equals({
id: creditAccount.id,
index: creditAccount.index,
name: creditAccount.name,
code: creditAccount.code,
parentAccountId: null,
children: [],
total_periods: [
{ date: '2020-01-08', formatted_amount: 0, amount: 0 },
{ date: '2020-01-09', formatted_amount: 0, amount: 0 },
{ date: '2020-01-10', formatted_amount: 4000, amount: 4000 },
{ date: '2020-01-11', formatted_amount: 4000, amount: 4000 },
{ date: '2020-01-12', formatted_amount: 4000, amount: 4000 }
],
total: { formatted_amount: 4000, amount: 4000, date: '2020-01-12' }
});
});
it('Should retrieve the balance sheet with display columns by `month`.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'month',
display_columns_type: 'date_periods',
from_date: '2019-07-01',
to_date: '2020-06-30',
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: debitAccount.id,
index: debitAccount.index,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
children: [],
total_periods: [
{ date: '2019-07', formatted_amount: 0, amount: 0 },
{ date: '2019-08', formatted_amount: 0, amount: 0 },
{ date: '2019-09', formatted_amount: 0, amount: 0 },
{ date: '2019-10', formatted_amount: 0, amount: 0 },
{ date: '2019-11', formatted_amount: 0, amount: 0 },
{ date: '2019-12', formatted_amount: 0, amount: 0 },
{ date: '2020-01', formatted_amount: 5000, amount: 5000 },
{ date: '2020-02', formatted_amount: 5000, amount: 5000 },
{ date: '2020-03', formatted_amount: 5000, amount: 5000 },
{ date: '2020-04', formatted_amount: 5000, amount: 5000 },
{ date: '2020-05', formatted_amount: 5000, amount: 5000 },
{ date: '2020-06', formatted_amount: 5000, amount: 5000 },
],
total: { formatted_amount: 5000, amount: 5000, date: '2020-06-30' }
});
});
it('Should retrieve the balance sheet with display columns `quarter`.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'quarter',
display_columns_type: 'date_periods',
from_date: '2020-01-01',
to_date: '2020-12-31',
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: debitAccount.id,
index: debitAccount.index,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
children: [],
total_periods: [
{ date: '2020-03', formatted_amount: 5000, amount: 5000 },
{ date: '2020-06', formatted_amount: 5000, amount: 5000 },
{ date: '2020-09', formatted_amount: 5000, amount: 5000 },
{ date: '2020-12', formatted_amount: 5000, amount: 5000 },
],
total: { formatted_amount: 5000, amount: 5000, date: '2020-12-31' },
});
});
it('Should retrieve the balance sheet amounts without cents.', async () => {
await tenantFactory.create('account_transaction', {
debit: 0.25, credit: 0, account_id: debitAccount.id, date: '2020-1-10',
});
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'quarter',
display_columns_type: 'date_periods',
from_date: '2020-01-01',
to_date: '2020-12-31',
number_format: {
no_cents: true,
},
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: debitAccount.id,
index: debitAccount.index,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
children: [],
total_periods: [
{ date: '2020-03', formatted_amount: 5000, amount: 5000.25 },
{ date: '2020-06', formatted_amount: 5000, amount: 5000.25 },
{ date: '2020-09', formatted_amount: 5000, amount: 5000.25 },
{ date: '2020-12', formatted_amount: 5000, amount: 5000.25 },
],
total: { formatted_amount: 5000, amount: 5000.25, date: '2020-12-31' },
});
});
it('Should retrieve the balance sheet amounts divided on 1000.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'quarter',
display_columns_type: 'date_periods',
from_date: '2020',
to_date: '2021',
number_format: {
divide_1000: true,
},
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: debitAccount.id,
index: debitAccount.index,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
children: [],
total_periods: [
{ date: '2020-03', formatted_amount: 5, amount: 5000 },
{ date: '2020-06', formatted_amount: 5, amount: 5000 },
{ date: '2020-09', formatted_amount: 5, amount: 5000 },
{ date: '2020-12', formatted_amount: 5, amount: 5000 },
{ date: '2021-03', formatted_amount: 5, amount: 5000 },
],
total: { formatted_amount: 5, amount: 5000, date: '2021' },
});
});
it('Should not retrieve accounts has no transactions between the given date range in case query none_zero is true.', async () => {
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
display_columns_by: 'quarter',
from_date: '2002',
to_date: '2003',
number_format: {
divide_1000: true,
},
none_zero: true,
})
.send();
expect(res.body.accounts[0].children.length).equals(0);
expect(res.body.accounts[1].children.length).equals(0);
});
it('Should retrieve accounts in nested structure parent and children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: debitAccount.id,
account_type_id: 1
});
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
none_zero: false,
account_ids: [childAccount.id, debitAccount.id]
})
.send();
expect(res.body.accounts[0].children).include.something.deep.equals({
id: debitAccount.id,
index: null,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
total: { formatted_amount: 5000, amount: 5000, date: '2020-12-31' },
children: [
{
id: childAccount.id,
index: null,
name: childAccount.name,
code: childAccount.code,
parentAccountId: debitAccount.id,
total: { formatted_amount: 0, amount: 0, date: '2020-12-31' },
children: [],
}
]
});
});
it('Should parent account balance sumation of total balane all children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: debitAccount.id,
account_type_id: 1
});
await tenantFactory.create('account_transaction', {
credit: 0, debit: 1000, account_id: childAccount.id, date: '2020-1-10'
});
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
none_zero: false,
account_ids: [childAccount.id, debitAccount.id]
})
.send();
expect(res.body.accounts[0].children[0].total.amount).equals(6000);
expect(res.body.accounts[0].children[0].total.formatted_amount).equals(6000);
});
it('Should parent account balance sumation of total periods amounts all children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: debitAccount.id,
account_type_id: 1
});
await tenantFactory.create('account_transaction', {
credit: 0, debit: 1000, account_id: childAccount.id, date: '2020-2-10'
});
const res = await request()
.get('/api/financial_statements/balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
none_zero: false,
account_ids: [childAccount.id, debitAccount.id],
display_columns_type: 'date_periods',
display_columns_by: 'month',
from_date: '2020-01-01',
to_date: '2020-12-12',
})
.send();
expect(res.body.accounts[0].children[0].total_periods).deep.equals([
{ amount: 5000, formatted_amount: 5000, date: '2020-01' },
{ amount: 6000, formatted_amount: 6000, date: '2020-02' },
{ amount: 6000, formatted_amount: 6000, date: '2020-03' },
{ amount: 6000, formatted_amount: 6000, date: '2020-04' },
{ amount: 6000, formatted_amount: 6000, date: '2020-05' },
{ amount: 6000, formatted_amount: 6000, date: '2020-06' },
{ amount: 6000, formatted_amount: 6000, date: '2020-07' },
{ amount: 6000, formatted_amount: 6000, date: '2020-08' },
{ amount: 6000, formatted_amount: 6000, date: '2020-09' },
{ amount: 6000, formatted_amount: 6000, date: '2020-10' },
{ amount: 6000, formatted_amount: 6000, date: '2020-11' },
{ amount: 6000, formatted_amount: 6000, date: '2020-12' }
])
});
});
});

View File

@@ -0,0 +1,113 @@
import {
request,
expect,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('route: `/api/purchases/bill_payments`', () => {
describe('POST: `/api/purchases/bill_payments`', () => {
it('Should `payment_date` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'payment_date',
location: 'body',
});
});
it('Should `payment_account_id` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'payment_account_id',
location: 'body',
});
});
it('Should `payment_number` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'payment_number',
location: 'body',
});
});
it('Should `entries.*.item_id` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].item_id',
location: 'body',
});
});
it('Should `payment_number` be unique on the storage.', () => {
});
it('Should `payment_account_id` be exists on the storage.', () => {
});
it('Should `entries.*.item_id` be exists on the storage.', () => {
});
it('Should store the given bill payment to the storage.', () => {
});
});
describe('POST: `/api/purchases/bill_payments/:id`', () => {
it('Should bill payment be exists on the storage.', () => {
});
});
describe('DELETE: `/api/purchases/bill_payments/:id`', () => {
it('Should bill payment be exists on the storage.', () => {
});
it('Should delete the given bill payment from the storage.', () => {
});
});
describe('GET: `/api/purchases/bill_payments/:id`', () => {
it('Should bill payment be exists on the storage.', () => {
});
});
});

View File

@@ -0,0 +1,217 @@
import {
request,
expect,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('route: `/api/purchases/bills`', () => {
describe('POST: `/api/purchases/bills`', () => {
it('Should `bill_number` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'bill_number',
location: 'body',
});
});
it('Should `vendor_id` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'vendor_id',
location: 'body',
});
});
it('Should `bill_date` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'bill_date',
location: 'body',
});
});
it('Should `entries` be minimum one', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries',
location: 'body',
});
});
it('Should `entries.*.item_id be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{
}]
});
expect(res.status).equals(422);
expecvt(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].item_id',
location: 'body'
});
});
it('Should `entries.*.rate` be required.', async () => {
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{
}]
});
expect(res.status).equals(422);
expecvt(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].rate',
location: 'body'
});
});
it('Should `entries.*.discount` be required.', () => {
});
it('Should entries.*.quantity be required.', () => {
});
it('Should vendor_id be exists on the storage.', async () => {
const vendor = await tenantFactory.create('vendor');
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
vendor_id: vendor.id,
bill_number: '123',
bill_date: '2020-02-02',
entries: [{
item_id: 1,
rate: 1,
quantity: 1,
}]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'VENDOR.ID.NOT.FOUND', code: 300,
})
});
it('Should entries.*.item_id be exists on the storage.', async () => {
const item = await tenantFactory.create('item');
const vendor = await tenantFactory.create('vendor');
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
vendor_id: vendor.id,
bill_number: '123',
bill_date: '2020-02-02',
entries: [{
item_id: 123123,
rate: 1,
quantity: 1,
}]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEMS.IDS.NOT.FOUND', code: 400,
});
});
it('Should validate the bill number is not exists on the storage.', async () => {
const item = await tenantFactory.create('item');
const vendor = await tenantFactory.create('vendor');
const bill = await tenantFactory.create('bill', { bill_number: '123' });
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
vendor_id: vendor.id,
bill_number: '123',
bill_date: '2020-02-02',
entries: [{
item_id: item.id,
rate: 1,
quantity: 1,
}]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'BILL.NUMBER.EXISTS', code: 500,
})
})
it('Should store the given bill details with associated entries to the storage.', async () => {
const item = await tenantFactory.create('item');
const vendor = await tenantFactory.create('vendor');
const res = await request()
.post('/api/purchases/bills')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
vendor_id: vendor.id,
bill_number: '123',
bill_date: '2020-02-02',
entries: [{
item_id: item.id,
rate: 1,
quantity: 1,
}]
});
expect(res.status).equals(200);
});
});
describe('DELETE: `/api/purchases/bills/:id`', () => {
});
});

View File

@@ -0,0 +1,191 @@
import {
request,
expect,
} from '~/testInit';
import Currency from 'models/Currency';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('route: /currencies/', () => {
describe('POST: `/api/currencies`', () => {
it('Should response unauthorized in case user was not logged in.', async () => {
const res = await request()
.post('/api/currencies')
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `currency_name` be required.', async () => {
const res = await request()
.post('/api/currencies')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'currency_name', location: 'body',
});
});
it('Should `currency_code` be required.', async () => {
const res = await request()
.post('/api/currencies')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'currency_code', location: 'body',
});
});
it('Should response currency code is duplicated.', async () => {
tenantFactory.create('currency', { currency_code: 'USD' });
const res = await request()
.post('/api/currencies')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
currency_code: 'USD',
currency_name: 'Dollar',
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CURRENCY.CODE.ALREADY.EXISTS', code: 100,
});
});
it('Should insert currency details to the storage.', async () => {
const res = await request()
.post('/api/currencies')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
currency_code: 'USD',
currency_name: 'Dollar',
});
const foundCurrency = await Currency.tenant().query().where('currency_code', 'USD');
expect(foundCurrency.length).equals(1);
expect(foundCurrency[0].currencyCode).equals('USD');
expect(foundCurrency[0].currencyName).equals('Dollar');
});
it('Should response success with correct data.', async () => {
const res = await request()
.post('/api/currencies')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
currency_code: 'USD',
currency_name: 'Dollar',
});
expect(res.status).equals(200);
});
});
describe('DELETE: `/api/currencies/:currency_code`', () => {
it('Should delete the given currency code from the storage.', async () => {
const currency = await tenantFactory.create('currency');
const res = await request()
.delete(`/api/currencies/${currency.currencyCode}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
const foundCurrency = await Currency.tenant().query().where('currency_code', 'USD');
expect(foundCurrency.length).equals(0);
});
});
describe('POST: `/api/currencies/:id`', () => {
it('Should `currency_name` be required.', async () => {
const currency = await tenantFactory.create('currency');
const res = await request()
.post(`/api/currencies/${currency.code}`)
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'currency_name', location: 'body',
});
});
it('Should `currency_code` be required.', async () => {
const currency = await tenantFactory.create('currency');
const res = await request()
.post(`/api/currencies/${currency.code}`)
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'currency_code', location: 'body',
});
});
it('Should response currency code is duplicated.', async () => {
const currency1 = await tenantFactory.create('currency');
const currency2 = await tenantFactory.create('currency');
const res = await request()
.post(`/api/currencies/${currency2.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
currency_code: currency1.currencyCode,
currency_name: 'Dollar',
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CURRENCY.CODE.ALREADY.EXISTS', code: 100,
});
});
it('Should update currency details of the given currency on the storage.', async () => {
const currency1 = await tenantFactory.create('currency');
const currency2 = await tenantFactory.create('currency');
const res = await request()
.post(`/api/currencies/${currency2.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
currency_code: 'ABC',
currency_name: 'Name',
});
const foundCurrency = await Currency.tenant().query().where('currency_code', 'ABC');
expect(foundCurrency.length).equals(1);
expect(foundCurrency[0].currencyCode).equals('ABC');
expect(foundCurrency[0].currencyName).equals('Name');
});
it('Should response success with correct data.', () => {
});
})
});

View File

@@ -0,0 +1,250 @@
import {
request,
expect,
} from '~/testInit';
import Currency from 'models/Currency';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
import Customer from '../../src/models/Customer';
describe('route: `/customers`', () => {
describe('POST: `/customers`', () => {
it('Should response unauthorized in case the user was not logged in.', async () => {
const res = await request()
.post('/api/customers')
.send({});
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `display_name` be required field.', async () => {
const res = await request()
.post('/api/customers')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'display_name', location: 'body',
})
});
it('Should `customer_type` be required field', async () => {
const res = await request()
.post('/api/customers')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'customer_type', location: 'body',
});
});
it('Should store the customer data to the storage.', async () => {
const res = await request()
.post('/api/customers')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_type: 'business',
first_name: 'Ahmed',
last_name: 'Bouhuolia',
company_name: 'Bigcapital',
display_name: 'Ahmed Bouhuolia, Bigcapital',
email: 'a.bouhuolia@live.com',
work_phone: '0927918381',
personal_phone: '0925173379',
billing_address_city: 'Tripoli',
billing_address_country: 'Libya',
billing_address_email: 'a.bouhuolia@live.com',
billing_address_state: 'State Tripoli',
billing_address_zipcode: '21892',
shipping_address_city: 'Tripoli',
shipping_address_country: 'Libya',
shipping_address_email: 'a.bouhuolia@live.com',
shipping_address_state: 'State Tripoli',
shipping_address_zipcode: '21892',
note: '__desc__',
active: true,
});
expect(res.status).equals(200);
const foundCustomer = await Customer.tenant().query().where('id', res.body.id);
expect(foundCustomer[0].customerType).equals('business');
expect(foundCustomer[0].firstName).equals('Ahmed');
expect(foundCustomer[0].lastName).equals('Bouhuolia');
expect(foundCustomer[0].companyName).equals('Bigcapital');
expect(foundCustomer[0].displayName).equals('Ahmed Bouhuolia, Bigcapital');
expect(foundCustomer[0].email).equals('a.bouhuolia@live.com');
expect(foundCustomer[0].workPhone).equals('0927918381');
expect(foundCustomer[0].personalPhone).equals('0925173379');
expect(foundCustomer[0].billingAddressCity).equals('Tripoli');
expect(foundCustomer[0].billingAddressCountry).equals('Libya');
expect(foundCustomer[0].billingAddressEmail).equals('a.bouhuolia@live.com');
expect(foundCustomer[0].billingAddressState).equals('State Tripoli');
expect(foundCustomer[0].billingAddressZipcode).equals('21892');
expect(foundCustomer[0].shippingAddressCity).equals('Tripoli');
expect(foundCustomer[0].shippingAddressCountry).equals('Libya');
expect(foundCustomer[0].shippingAddressEmail).equals('a.bouhuolia@live.com');
expect(foundCustomer[0].shippingAddressState).equals('State Tripoli');
expect(foundCustomer[0].shippingAddressZipcode).equals('21892');
});
});
describe('GET: `/customers/:id`', () => {
it('Should response not found in case the given customer id was not exists on the storage.', async () => {
const res = await request()
.get('/api/customers/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.NOT.FOUND', code: 200,
});
});
});
describe('GET: `customers`', () => {
it('Should response customers items', async () => {
await tenantFactory.create('customer');
await tenantFactory.create('customer');
const res = await request()
.get('/api/customers')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.customers.results.length).equals(2);
});
});
describe('DELETE: `/customers/:id`', () => {
it('Should response not found in case the given customer id was not exists on the storage.', async () => {
const res = await request()
.delete('/api/customers/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.NOT.FOUND', code: 200,
});
});
it('Should delete the given customer from the storage.', async () => {
const customer = await tenantFactory.create('customer');
const res = await request()
.delete(`/api/customers/${customer.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
const foundCustomer = await Customer.tenant().query().where('id', customer.id);
expect(foundCustomer.length).equals(0);
})
});
describe('POST: `/customers/:id`', () => {
it('Should response customer not found', async () => {
const res = await request()
.post('/api/customers/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_type: 'business',
display_name: 'Ahmed Bouhuolia, Bigcapital',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.NOT.FOUND', code: 200,
});
});
it('Should update details of the given customer.', async () => {
const customer = await tenantFactory.create('customer');
const res = await request()
.post(`/api/customers/${customer.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_type: 'business',
display_name: 'Ahmed Bouhuolia, Bigcapital',
});
expect(res.status).equals(200);
const foundCustomer = await Customer.tenant().query().where('id', res.body.id);
expect(foundCustomer.length).equals(1);
expect(foundCustomer[0].customerType).equals('business');
expect(foundCustomer[0].displayName).equals('Ahmed Bouhuolia, Bigcapital');
})
});
describe('DELETE: `customers`', () => {
it('Should response customers ids not found.', async () => {
const res = await request()
.delete('/api/customers')
.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.deep.equals({
type: 'CUSTOMERS.NOT.FOUND', code: 200,
});
});
it('Should delete the given customers.', async () => {
const customer1 = await tenantFactory.create('customer');
const customer2 = await tenantFactory.create('customer');
const res = await request()
.delete('/api/customers')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [customer1.id, customer2.id],
})
.send();
const foundCustomers = await Customer.tenant().query()
.whereIn('id', [customer1.id, customer2.id]);
expect(res.status).equals(200);
expect(foundCustomers.length).equals(0);
});
})
});

View File

@@ -0,0 +1,230 @@
import moment from 'moment';
import {
request,
expect,
} from '~/testInit';
import ExchangeRate from '../../src/models/ExchangeRate';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('route: /exchange_rates/', () => {
describe('POST: `/api/exchange_rates`', () => {
it('Should response unauthorized in case the user was not logged in.', async () => {
const res = await request()
.post('/api/exchange_rates')
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `currency_code` be required.', async () => {
const res = await request()
.post('/api/exchange_rates')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'currency_code', location: 'body',
});
});
it('Should `exchange_rate` be required.', async () => {
const res = await request()
.post('/api/exchange_rates')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'exchange_rate', location: 'body',
});
});
it('Should date be required', async () => {
const res = await request()
.post('/api/exchange_rates')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'date', location: 'body',
});
});
it('Should response date and currency code is already exists.', async () => {
await tenantFactory.create('exchange_rate', {
date: '2020-02-02',
currency_code: 'USD',
exchange_rate: 4.4,
});
const res = await request()
.post('/api/exchange_rates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: '2020-02-02',
currency_code: 'USD',
exchange_rate: 4.4,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'EXCHANGE.RATE.DATE.PERIOD.DEFINED', code: 200,
});
});
it('Should save the given exchange rate to the storage.', async () => {
const res = await request()
.post('/api/exchange_rates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: '2020-02-02',
currency_code: 'USD',
exchange_rate: 4.4,
});
expect(res.status).equals(200);
const foundExchangeRate = await ExchangeRate.tenant().query()
.where('currency_code', 'USD');
expect(foundExchangeRate.length).equals(1);
expect(
moment(foundExchangeRate[0].date).format('YYYY-MM-DD'),
).equals('2020-02-02');
expect(foundExchangeRate[0].currencyCode).equals('USD');
expect(foundExchangeRate[0].exchangeRate).equals(4.4);
});
});
describe('GET: `/api/exchange_rates', () => {
it('Should retrieve all exchange rates with pagination meta.', async () => {
await tenantFactory.create('exchange_rate');
await tenantFactory.create('exchange_rate');
await tenantFactory.create('exchange_rate');
const res = await request()
.get('/api/exchange_rates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
expect(res.body.exchange_rates.results.length).equals(3);
});
});
describe('POST: `/api/exchange_rates/:id`', () => {
it('Should response the given exchange rate not found.', async () => {
const res = await request()
.post('/api/exchange_rates/100')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
date: '2020-02-02',
currency_code: 'USD',
exchange_rate: 4.4,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'EXCHANGE.RATE.NOT.FOUND', code: 200,
});
});
it('Should update exchange rate of the given id on the storage.', async () => {
const exRate = await tenantFactory.create('exchange_rate');
const res = await request()
.post(`/api/exchange_rates/${exRate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
exchange_rate: 4.4,
});
expect(res.status).equals(200);
const foundExchangeRate = await ExchangeRate.tenant().query()
.where('id', exRate.id);
expect(foundExchangeRate.length).equals(1);
expect(foundExchangeRate[0].exchangeRate).equals(4.4);
});
});
describe('DELETE: `/api/exchange_rates/:id`', () => {
it('Should response the given exchange rate id not found.', async () => {
const res = await request()
.delete('/api/exchange_rates/100')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'EXCHANGE.RATE.NOT.FOUND', code: 200,
});
});
it('Should delete the given exchange rate id from the storage.', async () => {
const exRate = await tenantFactory.create('exchange_rate');
const res = await request()
.delete(`/api/exchange_rates/${exRate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const foundRates = await ExchangeRate.tenant().query();
expect(foundRates.length).equals(0);
});
});
describe('DELETE: `/api/exchange_rates/bulk`', () => {
it('Should response the given exchange rates ids where not found.', async () => {
const res = await request()
.delete('/api/exchange_rates/bulk')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [12332, 32432],
})
.send();
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'EXCHANGE.RATES.IS.NOT.FOUND', code: 200, ids: [12332, 32432],
})
});
it('Should delete the given excahnge rates ids.', async () => {
const exRate = await tenantFactory.create('exchange_rate');
const exRate2 = await tenantFactory.create('exchange_rate');
const res = await request()
.delete('/api/exchange_rates/bulk')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [exRate.id, exRate2.id],
})
.send();
const foundExchangeRate = await ExchangeRate.tenant().query()
.whereIn('id', [exRate.id, exRate2.id]);
expect(foundExchangeRate.length).equals(0);
})
});
});

View File

@@ -0,0 +1,739 @@
import moment from 'moment';
import { pick } from 'lodash';
import {
request,
expect,
} from '~/testInit';
import Expense from 'models/Expense';
import ExpenseCategory from 'models/ExpenseCategory';
import AccountTransaction from 'models/AccountTransaction';
import {
tenantWebsite,
tenantFactory,
loginRes,
} from '~/dbInit';
describe('routes: /expenses/', () => {
describe('POST `/expenses`', () => {
it('Should retrieve unauthorized access if the user was not authorized.', async () => {
const res = await request()
.post('/api/expenses')
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should categories total not be equals zero.', async () => {
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
payment_date: moment().format('YYYY-MM-DD'),
reference_no: '',
payment_account_id: 0,
description: '',
publish: 1,
categories: [
{
index: 1,
expense_account_id: 33,
amount: 1000,
description: '',
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'EXPENSE.ACCOUNTS.IDS.NOT.STORED', code: 400, ids: [33]
});
});
it('Should expense accounts ids be stored in the storage.', async () => {
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
payment_date: moment().format('YYYY-MM-DD'),
reference_no: '',
payment_account_id: 0,
description: '',
publish: 1,
categories: [
{
index: 1,
expense_account_id: 22,
amount: 1000,
description: '',
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'EXPENSE.ACCOUNTS.IDS.NOT.STORED', code: 400, ids: [22],
});
});
it('Should `payment_account_id` be in the storage.', async () => {
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
payment_date: moment().format('YYYY-MM-DD'),
reference_no: '',
payment_account_id: 22,
description: '',
publish: 1,
categories: [
{
index: 1,
expense_account_id: 22,
amount: 1000,
description: '',
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 500,
});
});
it('Should payment_account be required.', async () => {
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
});
it('Should `categories.*.expense_account_id` be required.', async () => {
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
});
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'payment_account_id', location: 'body'
});
});
it('Should expense transactions be stored on the storage.', async () => {
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
payment_date: moment().format('YYYY-MM-DD'),
reference_no: 'ABC',
payment_account_id: paymentAccount.id,
description: 'desc',
publish: 1,
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 1000,
description: '',
},
],
});
const foundExpense = await Expense.tenant().query().where('id', res.body.id);
expect(foundExpense.length).equals(1);
expect(foundExpense[0].referenceNo).equals('ABC');
expect(foundExpense[0].paymentAccountId).equals(paymentAccount.id);
expect(foundExpense[0].description).equals('desc');
expect(foundExpense[0].totalAmount).equals(1000);
});
it('Should expense categories transactions be stored on the storage.', async () => {
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
payment_date: moment().format('YYYY-MM-DD'),
reference_no: 'ABC',
payment_account_id: paymentAccount.id,
description: 'desc',
publish: 1,
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 1000,
description: 'category desc',
},
],
});
const foundCategories = await ExpenseCategory.tenant().query().where('id', res.body.id);
expect(foundCategories.length).equals(1);
expect(foundCategories[0].index).equals(1);
expect(foundCategories[0].expenseAccountId).equals(expenseAccount.id);
expect(foundCategories[0].amount).equals(1000);
expect(foundCategories[0].description).equals('category desc');
});
it('Should save journal entries that associate to the expense transaction.', async () => {
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
payment_date: moment().format('YYYY-MM-DD'),
reference_no: 'ABC',
payment_account_id: paymentAccount.id,
description: 'desc',
publish: 1,
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 1000,
description: 'category desc',
},
],
});
const transactions = await AccountTransaction.tenant().query()
.where('reference_id', res.body.id)
.where('reference_type', 'Expense');
const mappedTransactions = transactions.map(tr => ({
...pick(tr, ['credit', 'debit', 'referenceId', 'referenceType']),
}));
expect(mappedTransactions[0]).deep.equals({
credit: 1000,
debit: 0,
referenceType: 'Expense',
referenceId: res.body.id,
});
expect(mappedTransactions[1]).deep.equals({
credit: 0,
debit: 1000,
referenceType: 'Expense',
referenceId: res.body.id,
});
expect(transactions.length).equals(2);
})
});
describe('GET: `/expenses`', () => {
it('Should response unauthorized if the user was not logged in.', async () => {
const res = await request()
.post('/api/expenses')
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should retrieve expenses with pagination meta.', async () => {
await tenantFactory.create('expense');
await tenantFactory.create('expense');
const res = await request()
.get('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.expenses).that.is.an('object');
expect(res.body.expenses.results).that.is.an('array');
});
it('Should retrieve expenses based on view roles conditions of the custom view.', () => {
});
it('Should sort expenses based on the given `column_sort_order` column on ASC direction.', () => {
});
});
describe('DELETE: `/expenses/:id`', () => {
it('Should response unauthorized if the user was not logged in.', async () => {
const res = await request()
.delete('/api/expenses')
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response not found in case expense id was not exists on the storage.', async () => {
const res = await request()
.delete('/api/expenses/123321')
.set('organization-id', tenantWebsite.organizationId)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'EXPENSE.NOT.FOUND', code: 200,
});
});
it('Should delete the given expense transactions with associated categories.', async () => {
const expense = await tenantFactory.create('expense');
const res = await request()
.delete(`/api/expenses/${expense.id}`)
.set('organization-id', tenantWebsite.organizationId)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(200);
const storedExpense = await Expense.tenant().query().where('id', expense.id);
const storedExpenseCategories = await ExpenseCategory.tenant().query().where('expense_id', expense.id);
expect(storedExpense.length).equals(0);
expect(storedExpenseCategories.length).equals(0);
});
it('Should delete all journal entries that associated to the given expense.', async () => {
const expense = await tenantFactory.create('expense');
const trans = { reference_id: expense.id, reference_type: 'Expense' };
await tenantFactory.create('account_transaction', trans);
await tenantFactory.create('account_transaction', trans);
const res = await request()
.delete(`/api/expenses/${expense.id}`)
.set('organization-id', tenantWebsite.organizationId)
.set('x-access-token', loginRes.body.token)
.send();
const foundTransactions = await AccountTransaction.tenant().query()
.where('reference_type', 'Expense')
.where('reference_id', expense.id);
expect(foundTransactions.length).equals(0);
});
});
describe('GET: `/expenses/:id`', () => {
it('Should response unauthorized if the user was not logged in.', async () => {
const res = await request()
.get('/api/expenses/123')
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response not found in case the given expense id was not exists in the storage.', async () => {
const res = await request()
.get(`/api/expenses/321`)
.set('organization-id', tenantWebsite.organizationId)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(404);
});
it('Should retrieve expense metadata and associated expense categories.', async () => {
const expense = await tenantFactory.create('expense');
const expenseCategory = await tenantFactory.create('expense_category', {
expense_id: expense.id,
})
const res = await request()
.get(`/api/expenses/${expense.id}`)
.set('organization-id', tenantWebsite.organizationId)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(200);
expect(res.body.expense.id).is.a('number');
expect(res.body.expense.paymentAccountId).is.a('number');
expect(res.body.expense.totalAmount).is.a('number');
expect(res.body.expense.userId).is.a('number');
expect(res.body.expense.referenceNo).is.a('string');
expect(res.body.expense.description).is.a('string');
expect(res.body.expense.categories).is.a('array');
expect(res.body.expense.categories[0].id).is.a('number');
expect(res.body.expense.categories[0].description).is.a('string');
expect(res.body.expense.categories[0].expenseAccountId).is.a('number');
});
it('Should retrieve journal entries with expense metadata.', async () => {
const expense = await tenantFactory.create('expense');
const expenseCategory = await tenantFactory.create('expense_category', {
expense_id: expense.id,
});
const trans = { reference_id: expense.id, reference_type: 'Expense' };
await tenantFactory.create('account_transaction', trans);
await tenantFactory.create('account_transaction', trans);
const res = await request()
.get(`/api/expenses/${expense.id}`)
.set('organization-id', tenantWebsite.organizationId)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.body.expense.journalEntries).is.an('array');
expect(res.body.expense.journalEntries.length).equals(2);
});
});
describe('POST: `expenses/:id`', () => {
it('Should response unauthorized in case the user was not logged in.', async () => {
const expense = await tenantFactory.create('expense');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response the given expense id not exists on the storage.', async () => {
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post('/api/expenses/1233')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment().format('YYYY-MM-DD'),
payment_account_id: 321,
publish: true,
categories: [
{
expense_account_id: expenseAccount.id,
index: 1,
amount: 1000,
description: '',
},
],
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'EXPENSE.NOT.FOUND', code: 200,
});
});
it('Should response the given `payment_account_id` not exists.', async () => {
const expense = await tenantFactory.create('expense');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment().format('YYYY-MM-DD'),
payment_account_id: 321,
publish: true,
categories: [
{
expense_account_id: expenseAccount.id,
index: 1,
amount: 1000,
description: '',
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 400,
});
});
it('Should response the given `categories.*.expense_account_id` not exists.', async () => {
const paymentAccount = await tenantFactory.create('account');
const expense = await tenantFactory.create('expense');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment().format('YYYY-MM-DD'),
payment_account_id: paymentAccount.id,
publish: true,
categories: [
{
index: 1,
expense_account_id: 100,
amount: 1000,
description: '',
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'EXPENSE.ACCOUNTS.IDS.NOT.FOUND', code: 600, ids: [100],
});
});
it('Should response the total amount equals zero.', async () => {
const expense = await tenantFactory.create('expense');
const expenseAccount = await tenantFactory.create('account');
const paymentAccount = await tenantFactory.create('account');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment().format('YYYY-MM-DD'),
payment_account_id: paymentAccount.id,
publish: true,
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 0,
description: '',
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'TOTAL.AMOUNT.EQUALS.ZERO', code: 500,
});
});
it('Should update the expense transaction.', async () => {
const expense = await tenantFactory.create('expense');
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment('2009-01-02').format('YYYY-MM-DD'),
payment_account_id: paymentAccount.id,
publish: true,
description: 'Updated description',
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 3000,
description: '',
},
],
});
expect(res.status).equals(200);
const updatedExpense = await Expense.tenant().query()
.where('id', expense.id).first();
expect(updatedExpense.id).equals(expense.id);
expect(updatedExpense.referenceNo).equals('123');
expect(updatedExpense.description).equals('Updated description');
expect(updatedExpense.totalAmount).equals(3000);
expect(updatedExpense.paymentAccountId).equals(paymentAccount.id);
});
it('Should delete the expense categories that associated to the expense transaction.', async () => {
const expense = await tenantFactory.create('expense');
const expenseCategory = await tenantFactory.create('expense_category', {
expense_id: expense.id,
});
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment('2009-01-02').format('YYYY-MM-DD'),
payment_account_id: paymentAccount.id,
publish: true,
description: 'Updated description',
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 3000,
description: '',
},
],
});
const foundExpenseCategories = await ExpenseCategory.tenant()
.query().where('id', expenseCategory.id)
expect(foundExpenseCategories.length).equals(0);
});
it('Should insert the expense categories to associated to the expense transaction.', async () => {
const expense = await tenantFactory.create('expense');
const expenseCategory = await tenantFactory.create('expense_category', {
expense_id: expense.id,
});
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment('2009-01-02').format('YYYY-MM-DD'),
payment_account_id: paymentAccount.id,
publish: true,
description: 'Updated description',
categories: [
{
index: 1,
expense_account_id: expenseAccount.id,
amount: 3000,
description: '__desc__',
},
],
});
const foundExpenseCategories = await ExpenseCategory.tenant()
.query()
.where('expense_id', expense.id)
expect(foundExpenseCategories.length).equals(1);
expect(foundExpenseCategories[0].id).not.equals(expenseCategory.id);
});
it('Should update the expense categories that associated to the expense transactions.', async () => {
const expense = await tenantFactory.create('expense');
const expenseCategory = await tenantFactory.create('expense_category', {
expense_id: expense.id,
});
const paymentAccount = await tenantFactory.create('account');
const expenseAccount = await tenantFactory.create('account');
const res = await request()
.post(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
reference_no: '123',
payment_date: moment('2009-01-02').format('YYYY-MM-DD'),
payment_account_id: paymentAccount.id,
publish: true,
description: 'Updated description',
categories: [
{
id: expenseCategory.id,
index: 1,
expense_account_id: expenseAccount.id,
amount: 3000,
description: '__desc__',
},
],
});
const foundExpenseCategory = await ExpenseCategory.tenant().query()
.where('id', expenseCategory.id);
expect(foundExpenseCategory.length).equals(1);
expect(foundExpenseCategory[0].expenseAccountId).equals(expenseAccount.id);
expect(foundExpenseCategory[0].description).equals('__desc__');
expect(foundExpenseCategory[0].amount).equals(3000);
});
});
describe('DELETE: `/api/expenses`', () => {
it('Should response not found expenses ids.', async () => {
const res = await request()
.delete('/api/expenses')
.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.deep.equals({
type: 'EXPENSES.NOT.FOUND', code: 200,
});
});
it('Should delete the given expenses ids.', async () => {
const expense1 = await tenantFactory.create('expense');
const expense2 = await tenantFactory.create('expense');
const res = await request()
.delete('/api/expenses')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [expense1.id, expense2.id],
})
.send({});
const foundExpenses = await Expense.tenant().query()
.whereIn('id', [expense1.id, expense2.id]);
expect(res.status).equals(200);
expect(foundExpenses.length).equals(0);
})
});
describe('POST: `/api/expenses/:id/publish`', () => {
it('Should publish the given expense.', async () => {
const expense = await tenantFactory.create('expense', {
published: 0,
});
const res = await request()
.post(`/api/expenses/${expense.id}/publish`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const foundExpense = await Expense.tenant().query()
.where('id', expense.id).first();
expect(res.status).equals(200);
expect(foundExpense.published).equals(1);
});
});
});

View File

@@ -0,0 +1,956 @@
import moment from 'moment';
import {
request,
expect,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
import { iteratee } from 'lodash';
let creditAccount;
let debitAccount;
let incomeAccount;
let incomeType;
describe('routes: `/financial_statements`', () => {
beforeEach(async () => {
// Balance sheet types.
const assetType = await tenantFactory.create('account_type', { normal: 'debit', balance_sheet: true });
const liabilityType = await tenantFactory.create('account_type', { normal: 'credit', balance_sheet: true });
// Income statement types.
incomeType = await tenantFactory.create('account_type', { normal: 'credit', income_sheet: true });
const expenseType = await tenantFactory.create('account_type', { normal: 'debit', income_sheet: true });
// Assets & liabilites accounts.
creditAccount = await tenantFactory.create('account', { account_type_id: liabilityType.id });
debitAccount = await tenantFactory.create('account', { account_type_id: assetType.id });
// Income && expenses accounts.
incomeAccount = await tenantFactory.create('account', { account_type_id: incomeType.id });
const expenseAccount = await tenantFactory.create('account', { account_type_id: expenseType.id });
// const income2Account = await tenantFactory.create('account', { account_type_id: incomeType.id });
const accountTransactionMixied = { date: '2020-1-10' };
// Expense --
// 1000 Credit - Credit account
// 1000 Debit - expense account.
await tenantFactory.create('account_transaction', {
credit: 1000, debit: 0, account_id: debitAccount.id, referenceType: 'Expense',
referenceId: 1, ...accountTransactionMixied,
});
await tenantFactory.create('account_transaction', {
credit: 0, debit: 1000, account_id: expenseAccount.id, referenceType: 'Expense',
referenceId: 1, ...accountTransactionMixied,
});
// Jounral
// 4000 Credit - liability account.
// 2000 Debit - Asset account
// 2000 Debit - Asset account
await tenantFactory.create('account_transaction', {
credit: 4000, debit: 0, account_id: creditAccount.id, ...accountTransactionMixied,
});
await tenantFactory.create('account_transaction', {
debit: 2000, credit: 0, account_id: debitAccount.id, ...accountTransactionMixied,
});
await tenantFactory.create('account_transaction', {
debit: 2000, credit: 0, account_id: debitAccount.id, ...accountTransactionMixied,
});
// Income Journal.
// 2000 Credit - Income account.
// 2000 Debit - Asset account.
await tenantFactory.create('account_transaction', {
credit: 2000, account_id: incomeAccount.id, ...accountTransactionMixied
});
await tenantFactory.create('account_transaction', {
debit: 2000, credit: 0, account_id: debitAccount.id, ...accountTransactionMixied,
});
// -----------------------------------------
// Assets account balance = 5000 | Libility account balance = 4000
// Expense account balance = 1000 | Income account balance = 2000
});
describe('routes: `/financial_statements/journal`', () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.send();
expect(res.status).equals(401);
});
it('Should retrieve ledger sheet transactions grouped by reference type and id.', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
expect(res.body.journal.length).to.be.at.least(1);
expect(res.body.journal[0].credit).to.be.a('number');
expect(res.body.journal[0].debit).to.be.a('number');
expect(res.body.journal[0].entries).to.be.a('array');
expect(res.body.journal[0].id).to.be.a('string');
expect(res.body.journal[0].entries[0].credit).to.be.a('number');
expect(res.body.journal[0].entries[0].debit).to.be.a('number');
});
it('Should retrieve transactions between date range.', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2018-01-01',
to_date: '2019-01-01',
})
.send();
expect(res.body.journal.length).equals(0);
});
it('Should retrieve transactions that associated to the queried accounts.', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
account_ids: [creditAccount.id],
})
.send();
expect(res.body.journal[0].entries.length).equals(1);
expect(res.body.journal[0].entries[0].account_id).equals(creditAccount.id);
expect(res.body.journal.length).equals(1);
});
it('Should retrieve tranasactions with the given types.', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
transaction_types: ['Expense'],
});
expect(res.body.journal.length).equals(1);
});
it('Should retrieve transactions with range amount.', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_range: 2000,
to_range: 2000,
})
.send();
expect(res.body.journal[0].credit).satisfy((credit) => {
return credit === 0 || credit >= 2000;
});
expect(res.body.journal[0].debit).satisfy((debit) => {
return debit === 0 || debit >= 2000;
});
});
it('Should format credit and debit to no cents of retrieved transactions.', async () => {
});
it('Should divide credit/debit amount on 1000', async () => {
const res = await request()
.get('/api/financial_statements/journal')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
number_format: {
divide_1000: true,
},
})
.send();
const journal = res.body.journal.find((j) => j.id === '1-Expense');
expect(journal.formatted_credit).equals(1);
expect(journal.formatted_debit).equals(1);
});
});
describe('routes: `/financial_statements/general_ledger`', () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.send();
expect(res.status).equals(401);
});
it('Should retrieve request query meta on response schema.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.query.from_date).equals(moment().startOf('year').format('YYYY-MM-DD'));
expect(res.body.query.to_date).equals(moment().endOf('year').format('YYYY-MM-DD'));
expect(res.body.query.basis).equals('cash');
expect(res.body.query.number_format.no_cents).equals(false);
expect(res.body.query.number_format.divide_1000).equals(false);
expect(res.body.query.none_zero).equals(false);
expect(res.body.query.accounts_ids).to.be.an('array');
});
it('Should retrieve the general ledger accounts with associated transactions and opening/closing balance.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.accounts).is.an('array');
expect(res.body.accounts[0].id).to.be.an('number');
expect(res.body.accounts[0].name).to.be.a('string');
expect(res.body.accounts[0].code).to.be.a('string');
expect(res.body.accounts[0].transactions).to.be.a('array');
expect(res.body.accounts[0].opening).to.be.a('object');
expect(res.body.accounts[0].opening.amount).to.be.a('number');
expect(res.body.accounts[0].opening.date).to.be.a('string');
expect(res.body.accounts[0].closing).to.be.a('object');
expect(res.body.accounts[0].closing.amount).to.be.a('number');
expect(res.body.accounts[0].closing.date).to.be.a('string');
});
it('Should retrieve opening and closing balance.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const targetAccount = res.body.accounts.find((a) => a.id === creditAccount.id);
expect(targetAccount).to.be.an('object');
expect(targetAccount.opening).to.deep.equal({
amount: 0, formatted_amount: 0, date: '2020-01-01',
});
expect(targetAccount.closing).to.deep.equal({
amount: 4000, formatted_amount: 4000, date: '2020-12-31',
});
});
it('Should retrieve opening and closing balance between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2018-01-01',
to_date: '2020-03-30',
// none_zero: true,
})
.send();
const targetAccount = res.body.accounts.find((a) => a.id === creditAccount.id);
expect(targetAccount).to.be.an('object');
expect(targetAccount.opening).to.deep.equal({
amount: 0, formatted_amount: 0, date: '2018-01-01',
});
expect(targetAccount.closing).to.deep.equal({
amount: 4000, formatted_amount: 4000, date: '2020-03-30',
});
});
it('Should retrieve accounts with associated transactions.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
none_zero: true,
})
.send();
})
it('Should retrieve accounts transactions only that between date range.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2020-03-30',
none_zero: true,
})
.send();
// console.log(res.body.accounts);
});
it('Should retrieve no accounts with given date period has not transactions.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-20',
to_date: '2020-03-30',
none_zero: true,
})
.send();
expect(res.body.accounts.length).equals(0);
});
it('Should retrieve all accounts even it have no transactions in the given date range when `none_zero` is `true`', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2020-03-30',
none_zero: true,
})
.send();
const accountsNoTransactions = res.body.accounts.filter(a => a.transactions.length === 0);
const accountsWithTransactions = res.body.accounts.filter(a => a.transactions.length > 0);
expect(accountsNoTransactions.length).equals(0);
expect(accountsWithTransactions.length).not.equals(0);
});
it('Should amount transactions divided on `1000` when `number_format.none_zero` is `true`.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2020-03-30',
accounts_ids: [creditAccount.id],
number_format: {
divide_1000: true,
},
})
.send();
expect(res.body.accounts).include.something.deep.equals({
id: creditAccount.id,
name: creditAccount.name,
code: creditAccount.code,
index: null,
parentAccountId: null,
children: [],
transactions: [
{
id: 1002,
note: null,
transactionType: null,
referenceType: null,
referenceId: null,
date: '2020-01-09T22:00:00.000Z',
createdAt: null,
formatted_amount: 4,
amount: 4000,
}
],
opening: { date: '2020-01-01', formatted_amount: 0, amount: 0 },
closing: { date: '2020-03-30', formatted_amount: 4, amount: 4000 }
});
});
it('Should amount transactions rounded with no decimals when `number_format.no_cents` is `true`.', async () => {
await tenantFactory.create('account_transaction', {
debit: 0.25, credit: 0, account_id: debitAccount.id, date: '2020-1-10',
});
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2020-03-30',
number_format: {
divide_1000: true,
no_cents: true,
},
accounts_ids: [debitAccount.id]
})
.send();
expect(res.body.accounts[0].transactions[2].formatted_amount).equal(2);
});
it('Should retrieve only accounts that given in the query.', async () => {
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2020-03-30',
none_zero: true,
accounts_ids: [creditAccount.id],
})
.send();
expect(res.body.accounts.length).equals(1);
});
it('Should retrieve accounts in nested array structure as parent/children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: debitAccount.id,
account_type_id: 1
});
const res = await request()
.get('/api/financial_statements/general_ledger')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
accounts_ids: [childAccount.id, debitAccount.id],
})
.send();
expect(res.body.accounts[0].children.length).equals(1);
expect(res.body.accounts[0].children[0].id).equals(childAccount.id);
});
});
describe('routes: `/financial_statements/trial_balance`', () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.send();
expect(res.status).equals(401);
});
it('Should retrieve the trial balance of accounts.', async () => {
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.accounts).include.something.deep.equals({
id: debitAccount.id,
name: debitAccount.name,
code: debitAccount.code,
parentAccountId: null,
accountNormal: 'debit',
credit: 1000,
debit: 6000,
balance: 5000,
formatted_credit: 1000,
formatted_debit: 6000,
formatted_balance: 5000,
children: [],
});
expect(res.body.accounts).include.something.deep.equals({
id: creditAccount.id,
name: creditAccount.name,
code: creditAccount.code,
accountNormal: 'credit',
parentAccountId: null,
credit: 4000,
debit: 0,
balance: 4000,
formatted_credit: 4000,
formatted_debit: 0,
formatted_balance: 4000,
children: [],
});
});
it('Should not retrieve accounts has no transactions between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
// There is no transactions between these dates.
from_date: '2002-01-01',
to_date: '2003-01-01',
none_zero: true,
})
.send();
expect(res.body.accounts.length).equals(0);
});
it('Should retrieve trial balance of accounts between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
// There is no transactions between these dates.
from_date: '2020-01-05',
to_date: '2020-01-10',
none_zero: true,
})
.send();
expect(res.body.accounts).include.something.deep.equals({
id: creditAccount.id,
name: creditAccount.name,
code: creditAccount.code,
accountNormal: 'credit',
parentAccountId: null,
credit: 4000,
debit: 0,
balance: 4000,
formatted_credit: 4000,
formatted_debit: 0,
formatted_balance: 4000,
children: []
});
});
it('Should credit, debit and balance amount be divided on 1000.', async () => {
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
// There is no transactions between these dates.
from_date: '2020-01-05',
to_date: '2020-01-10',
number_format: {
divide_1000: true,
},
})
.send();
expect(res.body.accounts).include.something.deep.equals({
id: creditAccount.id,
name: creditAccount.name,
code: creditAccount.code,
accountNormal: 'credit',
parentAccountId: null,
credit: 4000,
debit: 0,
balance: 4000,
formatted_credit: 4,
formatted_debit: 0,
formatted_balance: 4,
children: [],
});
});
it('Should credit, debit and balance amount rounded without cents.', async () => {
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
// There is no transactions between these dates.
from_date: '2020-01-05',
to_date: '2020-01-10',
number_format: {
no_cents: true,
},
})
.send();
});
it('Should retrieve associated account details in accounts list.', async () => {
});
it('Should retrieve account with nested array structure as parent/children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: debitAccount.id,
account_type_id: 1
});
const res = await request()
.get('/api/financial_statements/trial_balance_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
account_ids: [debitAccount.id, childAccount.id],
})
.send();
expect(res.body.accounts[0].children.length).equals(1);
expect(res.body.accounts[0].children[0].id).equals(childAccount.id);
});
});
describe('routes: `/api/financial_statements/profit_loss_sheet`', () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loos_sheet')
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should retrieve columns when display type `date_periods` and columns by `month` between date range.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2020-12-12',
display_columns_type: 'date_periods',
display_columns_by: 'month',
})
.send();
expect(res.body.columns.length).equals(12);
expect(res.body.columns).deep.equals([
'2020-01', '2020-02',
'2020-03', '2020-04',
'2020-05', '2020-06',
'2020-07', '2020-08',
'2020-09', '2020-10',
'2020-11', '2020-12',
]);
});
it('Should retrieve columns when display type `date_periods` and columns by `quarter`.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: moment().startOf('year').format('YYYY-MM-DD'),
to_date: moment().endOf('year').format('YYYY-MM-DD'),
display_columns_type: 'date_periods',
display_columns_by: 'quarter',
})
.send();
expect(res.body.columns.length).equals(4);
expect(res.body.columns).deep.equals([
'2020-03', '2020-06', '2020-09', '2020-12',
]);
});
it('Should retrieve columns when display type `date_periods` and columns by `day` between date range.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: moment('2020-01-01').startOf('month').format('YYYY-MM-DD'),
to_date: moment('2020-01-01').endOf('month').format('YYYY-MM-DD'),
display_columns_type: 'date_periods',
display_columns_by: 'day',
})
.send();
expect(res.body.columns.length).equals(31);
expect(res.body.columns).deep.equals([
'2020-01-01', '2020-01-02', '2020-01-03',
'2020-01-04', '2020-01-05', '2020-01-06',
'2020-01-07', '2020-01-08', '2020-01-09',
'2020-01-10', '2020-01-11', '2020-01-12',
'2020-01-13', '2020-01-14', '2020-01-15',
'2020-01-16', '2020-01-17', '2020-01-18',
'2020-01-19', '2020-01-20', '2020-01-21',
'2020-01-22', '2020-01-23', '2020-01-24',
'2020-01-25', '2020-01-26', '2020-01-27',
'2020-01-28', '2020-01-29', '2020-01-30',
'2020-01-31',
]);
});
it('Should retrieve all income accounts even it has no transactions.', async () => {
const zeroAccount = await tenantFactory.create('account', { account_type_id: incomeType.id });
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: moment('2020-01-01').startOf('month').format('YYYY-MM-DD'),
to_date: moment('2020-01-31').endOf('month').format('YYYY-MM-DD'),
display_columns_type: 'total',
display_columns_by: 'month',
none_zero: false,
})
.send();
expect(res.body.profitLoss.income.accounts).include.something.deep.equals({
id: zeroAccount.id,
index: zeroAccount.index,
name: zeroAccount.name,
code: zeroAccount.code,
parentAccountId: null,
children: [],
total: { amount: 0, date: '2020-01-31', formatted_amount: 0 },
});
});
it('Should retrieve total of each income account when display columns by `total`.', async () => {
const toDate = moment('2020-01-01').endOf('month').format('YYYY-MM-DD');
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: moment('2020-01-01').startOf('month').format('YYYY-MM-DD'),
to_date: toDate,
})
.send();
expect(res.body.profitLoss.income.accounts).to.be.an('array');
expect(res.body.profitLoss.income.accounts.length).not.equals(0);
expect(res.body.profitLoss.income.accounts[0].id).to.be.an('number');
expect(res.body.profitLoss.income.accounts[0].name).to.be.an('string');
expect(res.body.profitLoss.income.accounts[0].total).to.be.an('object');
expect(res.body.profitLoss.income.accounts[0].total.amount).to.be.an('number');
expect(res.body.profitLoss.income.accounts[0].total.formatted_amount).to.be.an('number');
expect(res.body.profitLoss.income.accounts[0].total.date).equals(toDate);
});
it('Should retrieve credit sumation of income accounts.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2021-01-01',
})
.send();
expect(res.body.profitLoss.income.total).to.be.an('object');
expect(res.body.profitLoss.income.total.amount).equals(2000);
expect(res.body.profitLoss.income.total.formatted_amount).equals(2000);
expect(res.body.profitLoss.income.total.date).equals('2021-01-01');
});
it('Should retrieve debit sumation of expenses accounts.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2021-01-01',
})
.send();
expect(res.body.profitLoss.expenses.total).to.be.an('object');
expect(res.body.profitLoss.expenses.total.amount).equals(1000);
expect(res.body.profitLoss.expenses.total.formatted_amount).equals(1000);
expect(res.body.profitLoss.expenses.total.date).equals('2021-01-01');
});
it('Should retrieve credit total of income accounts with `date_periods` columns between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2019-12-01',
to_date: '2020-12-01',
display_columns_type: 'date_periods',
display_columns_by: 'month',
})
.send();
expect(res.body.profitLoss.income.total_periods[0].amount).equals(0);
expect(res.body.profitLoss.income.total_periods[1].amount).equals(2000);
expect(res.body.profitLoss.income.total_periods[2].amount).equals(2000);
});
it('Should retrieve debit total of expenses accounts with `date_periods` columns between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2019-12-01',
to_date: '2020-12-01',
display_columns_type: 'date_periods',
display_columns_by: 'month',
})
.send();
expect(res.body.profitLoss.expenses.total_periods[0].amount).equals(0);
expect(res.body.profitLoss.expenses.total_periods[1].amount).equals(1000);
expect(res.body.profitLoss.expenses.total_periods[2].amount).equals(1000);
});
it('Should retrieve total net income with `total column display between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2019-12-01',
to_date: '2020-12-01',
display_columns_type: 'total',
})
.send();
expect(res.body.profitLoss.net_income.total.amount).equals(1000);
expect(res.body.profitLoss.net_income.total.formatted_amount).equals(1000);
expect(res.body.profitLoss.net_income.total.date).equals('2020-12-01');
});
it('Should retrieve total net income with `date_periods` columns between the given date range.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2019-12-01',
to_date: '2020-12-01',
display_columns_type: 'date_periods',
display_columns_by: 'quarter',
})
.send();
expect(res.body.profitLoss.net_income).deep.equals({
total_periods: [
{ date: '2019-12', amount: 0, formatted_amount: 0 },
{ date: '2020-03', amount: 1000, formatted_amount: 1000 },
{ date: '2020-06', amount: 1000, formatted_amount: 1000 },
{ date: '2020-09', amount: 1000, formatted_amount: 1000 },
{ date: '2020-12', amount: 1000, formatted_amount: 1000 }
],
});
});
it('Should not retrieve income or expenses accounts that has no transactions between the given date range in case none_zero equals true.', async () => {
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
from_date: '2020-01-01',
to_date: '2021-01-01',
display_columns_by: 'month',
display_columns_type: 'date_periods',
none_zero: true,
})
.send();
expect(res.body.profitLoss.income.accounts.length).equals(1);
});
it('Should retrieve accounts in nested array structure as parent/children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: incomeAccount.id,
account_type_id: 7
});
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
account_ids: [childAccount.id, incomeAccount.id],
})
.send();
expect(res.body.profitLoss.income.accounts.length).equals(1);
expect(res.body.profitLoss.income.accounts[0].children.length).equals(1);
expect(res.body.profitLoss.income.accounts[0].children[0].id).equals(childAccount.id);
});
it('Should parent account credit/debit sumation of total periods amounts all children accounts.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: incomeAccount.id,
account_type_id: 7,
});
await tenantFactory.create('account_transaction', {
credit: 1000, debit: 0, account_id: childAccount.id, date: '2020-2-10'
});
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
account_ids: [childAccount.id, incomeAccount.id],
from_date: '2020-01-01',
to_date: '2020-12-12',
})
.send();
expect(res.body.profitLoss.income.accounts[0].total).deep.equals({
amount: 3000, date: '2020-12-12', formatted_amount: 3000
});
});
it('Should parent account credit/debit sumation of total date periods.', async () => {
const childAccount = await tenantFactory.create('account', {
parent_account_id: incomeAccount.id,
account_type_id: 7,
});
await tenantFactory.create('account_transaction', {
credit: 1000, debit: 0, account_id: childAccount.id, date: '2020-2-10'
});
const res = await request()
.get('/api/financial_statements/profit_loss_sheet')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
account_ids: [childAccount.id, incomeAccount.id],
display_columns_type: 'date_periods',
display_columns_by: 'month',
from_date: '2020-01-01',
to_date: '2020-12-12',
})
.send();
const periods = [
{ date: '2020-01', amount: 2000, formatted_amount: 2000 },
{ date: '2020-02', amount: 3000, formatted_amount: 3000 },
{ date: '2020-03', amount: 3000, formatted_amount: 3000 },
{ date: '2020-04', amount: 3000, formatted_amount: 3000 },
{ date: '2020-05', amount: 3000, formatted_amount: 3000 },
{ date: '2020-06', amount: 3000, formatted_amount: 3000 },
{ date: '2020-07', amount: 3000, formatted_amount: 3000 },
{ date: '2020-08', amount: 3000, formatted_amount: 3000 },
{ date: '2020-09', amount: 3000, formatted_amount: 3000 },
{ date: '2020-10', amount: 3000, formatted_amount: 3000 },
{ date: '2020-11', amount: 3000, formatted_amount: 3000 },
{ date: '2020-12', amount: 3000, formatted_amount: 3000 }
];
expect(res.body.profitLoss.income.accounts[0].periods).deep.equals(periods);
expect(res.body.profitLoss.income.total_periods).deep.equals(periods);
});
});
});

View File

@@ -0,0 +1,259 @@
import knex from '@/database/knex';
import {
request,
expect,
createUser,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
import Invite from '@/system/models/Invite'
import TenantUser from 'models/TenantUser';
import SystemUser from '@/system/models/SystemUser';
describe('routes: `/api/invite_users`', () => {
describe('POST: `/api/invite_users/send`', () => {
it('Should response unauthorized if the user was not authorized.', async () => {
const res = await request().get('/api/invite_users/send');
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should email be required.', async () => {
const res = await request()
.post('/api/invite/send')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'email', location: 'body',
});
});
it('Should email not be already registered in the system database.', async () => {
const user = await createUser(tenantWebsite, {
active: false,
email: 'admin@admin.com',
});
const res = await request()
.post('/api/invite/send')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
email: 'admin@admin.com',
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'USER.EMAIL.ALREADY.REGISTERED', code: 100,
});
});
it('Should invite token be inserted to the master database.', async () => {
const res = await request()
.post('/api/invite/send')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
email: 'admin@admin.com'
});
const foundInviteToken = await Invite.query()
.where('email', 'admin@admin.com').first();
expect(foundInviteToken).is.not.null;
expect(foundInviteToken.token).is.not.null;
});
it('Should invite email be insereted to users tenant database.', async () => {
const res = await request()
.post('/api/invite/send')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
email: 'admin@admin.com'
});
const foundTenantUser = await TenantUser.tenant().query()
.where('email', 'admin@admin.com').first();
expect(foundTenantUser).is.not.null;
expect(foundTenantUser.email).equals('admin@admin.com');
expect(foundTenantUser.firstName).equals('admin@admin.com');
expect(foundTenantUser.createdAt).is.not.null;
});
});
describe('POST: `/api/invite/accept/:token`', () => {
let sendInviteRes;
let inviteUser;
beforeEach(async () => {
sendInviteRes = await request()
.post('/api/invite/send')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
email: 'admin@admin.com'
});
inviteUser = await Invite.query()
.where('email', 'admin@admin.com')
.first();
});
it('Should the given token be valid.', async () => {
const res = await request()
.post('/api/invite/accept/invalid_token')
.send({
first_name: 'Ahmed',
last_name: 'Bouhuolia',
password: 'hard-password',
phone_number: '0927918381',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'INVITE.TOKEN.NOT.FOUND', code: 300,
});
});
it('Should first_name be required.', async () => {
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'first_name', location: 'body'
});
});
it('Should last_name be required.', async () => {
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'last_name', location: 'body'
});
});
it('Should phone_number be required.', async () => {
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'phone_number', location: 'body'
});
});
it('Should password be required.', async () => {
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'password', location: 'body'
});
});
it('Should phone number not be already registered.', async () => {
const user = await createUser(tenantWebsite);
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send({
first_name: 'Ahmed',
last_name: 'Bouhuolia',
password: 'hard-password',
phone_number: user.phone_number,
})
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'PHONE_MUMNER.ALREADY.EXISTS', code: 400,
});
});
it('Should tenant user details updated after invite accept.', async () => {
const user = await createUser(tenantWebsite);
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send({
first_name: 'Ahmed',
last_name: 'Bouhuolia',
password: 'hard-password',
phone_number: '0927918381',
});
const foundTenantUser = await TenantUser.tenant().query()
.where('email', 'admin@admin.com').first();
expect(foundTenantUser).is.not.null;
expect(foundTenantUser.id).is.not.null;
expect(foundTenantUser.email).equals('admin@admin.com');
expect(foundTenantUser.firstName).equals('Ahmed');
expect(foundTenantUser.lastName).equals('Bouhuolia');
expect(foundTenantUser.active).equals(1);
expect(foundTenantUser.inviteAcceptedAt).is.not.null;
expect(foundTenantUser.createdAt).is.not.null;
expect(foundTenantUser.updatedAt).is.not.null;
});
it('Should user details be insereted to the system database', async () => {
const user = await createUser(tenantWebsite);
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send({
first_name: 'Ahmed',
last_name: 'Bouhuolia',
password: 'hard-password',
phone_number: '0927918381',
});
const foundSystemUser = await SystemUser.query()
.where('email', 'admin@admin.com').first();
expect(foundSystemUser).is.not.null;
expect(foundSystemUser.id).is.not.null;
expect(foundSystemUser.tenantId).equals(inviteUser.tenantId);
expect(foundSystemUser.email).equals('admin@admin.com');
expect(foundSystemUser.firstName).equals('Ahmed');
expect(foundSystemUser.lastName).equals('Bouhuolia');
expect(foundSystemUser.active).equals(1);
expect(foundSystemUser.lastLoginAt).is.null;
expect(foundSystemUser.createdAt).is.not.null;
expect(foundSystemUser.updatedAt).is.null;
});
it('Should invite token be deleted after invite accept.', async () => {
const res = await request()
.post(`/api/invite/accept/${inviteUser.token}`)
.send({
first_name: 'Ahmed',
last_name: 'Bouhuolia',
password: 'hard-password',
phone_number: '0927918381',
});
const foundInviteToken = await Invite.query().where('token', inviteUser.token);
expect(foundInviteToken.length).equals(0);
});
});
describe('GET: `/api/invite_users/:token`', () => {
it('Should response token invalid.', () => {
});
});
});

View File

@@ -0,0 +1,729 @@
import {
request,
expect,
} from '~/testInit';
import Item from 'models/Item';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: `/items`', () => {
describe('POST: `/items`', () => {
it('Should not create a new item if the user was not authorized.', async () => {
const res = await request()
.post('/api/items')
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `name` be required.', async () => {
const res = await request()
.post('/api/items')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'name', location: 'body',
});
});
it('Should `type` be required.', async () => {
const res = await request()
.post('/api/items')
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'type', location: 'body',
});
});
it('Should `type` be one of defined words.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
type: 'not-defined',
});
expect(res.body.errors).include.something.deep.equals({
value: 'not-defined',
msg: 'Invalid value',
param: 'type',
location: 'body',
});
});
it('Should `buy_price` be numeric.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
cost_price: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'cost_price',
location: 'body',
});
});
it('Should `sell_price` be numeric.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
sell_price: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'sell_price',
location: 'body',
});
});
it('Should `sell_account_id` be integer.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
cost_account_id: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'cost_account_id',
location: 'body',
});
});
it('Should `cost_account_id` be integer.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
sell_account_id: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'sell_account_id',
location: 'body',
});
});
it('Should `cost_account_id` be required if `cost_price` was presented.', async () => {
});
it('Should `buy_account_id` be required if `buy_price` was presented.', async () => {
});
it('Should `inventory_account_id` be required if type was `inventory` item.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'inventory',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'inventory_account_id',
location: 'body',
});
});
it('Should `inventory_account_id` be not required if type was not `inventory`.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
expect(res.body.errors).include.something.deep.not.equals({
msg: 'Invalid value',
param: 'inventory_account_id',
location: 'body',
});
});
it('Should response bad request in case `cost account` was not exist.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'COST_ACCOUNT_NOT_FOUND', code: 100,
});
});
it('Should response bad request in case sell account was not exist.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'SELL_ACCOUNT_NOT_FOUND', code: 120,
});
});
it('Should response not category found in case item category was not exist.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
category_id: 20,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'ITEM_CATEGORY_NOT_FOUND', code: 140,
});
});
it('Should response success with correct data format.', async () => {
const account = await tenantFactory.create('account');
const anotherAccount = await tenantFactory.create('account');
const itemCategory = await tenantFactory.create('item_category');
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: account.id,
cost_account_id: anotherAccount.id,
category_id: itemCategory.id,
});
expect(res.status).equals(200);
});
it('Should store the given item details to the storage.', async () => {
const account = await tenantFactory.create('account');
const anotherAccount = await tenantFactory.create('account');
const itemCategory = await tenantFactory.create('item_category');
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sku: 'SKU CODE',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: account.id,
cost_account_id: anotherAccount.id,
category_id: itemCategory.id,
note: 'note about item'
});
const storedItem = await Item.tenant().query().where('id', res.body.id).first();
expect(storedItem.name).equals('Item Name');
expect(storedItem.type).equals('service');
expect(storedItem.sellPrice).equals(10.2);
expect(storedItem.costPrice).equals(20.2);
expect(storedItem.sellAccountId).equals(account.id);
expect(storedItem.costAccountId).equals(anotherAccount.id);
expect(storedItem.categoryId).equals(itemCategory.id);
expect(storedItem.sku).equals('SKU CODE');
expect(storedItem.note).equals('note about item');
expect(storedItem.userId).is.not.null;
});
});
describe('POST: `items/:id`', () => {
it('Should response item not found in case item id was not exist.', async () => {
const res = await request()
.post('/api/items/100')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'product',
cost_price: 100,
sell_price: 200,
sell_account_id: 1,
cost_account_id: 2,
category_id: 2,
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEM.NOT.FOUND', code: 100,
});
});
it('Should `name` be required.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'name', location: 'body',
});
});
it('Should `type` be required.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'type', location: 'body',
});
});
it('Should `sell_price` be numeric.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
sell_price: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'sell_price',
location: 'body',
});
});
it('Should `cost_price` be numeric.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
cost_price: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'cost_price',
location: 'body',
});
});
it('Should `sell_account_id` be integer.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
sell_account_id: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'sell_account_id',
location: 'body',
});
});
it('Should `cost_account_id` be integer.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
cost_account_id: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'cost_account_id',
location: 'body',
});
});
it ('Should response bad request in case cost account was not exist.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'COST_ACCOUNT_NOT_FOUND', code: 100,
});
});
it('Should response bad request in case sell account was not exist.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Item Name',
type: 'product',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: 1000000,
cost_account_id: 1000000,
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'SELL_ACCOUNT_NOT_FOUND', code: 120,
});
});
it('Should update details of the given item.', async () => {
const account = await tenantFactory.create('account');
const anotherAccount = await tenantFactory.create('account');
const itemCategory = await tenantFactory.create('item_category');
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'New Item Name',
type: 'service',
sell_price: 10.2,
cost_price: 20.2,
sell_account_id: account.id,
cost_account_id: anotherAccount.id,
category_id: itemCategory.id,
});
const updatedItem = await Item.tenant().query().findById(item.id);
expect(updatedItem.name).equals('New Item Name');
expect(updatedItem.type).equals('service');
expect(updatedItem.sellPrice).equals(10.2);
expect(updatedItem.costPrice).equals(20.2);
expect(updatedItem.sellAccountId).equals(account.id);
expect(updatedItem.costAccountId).equals(anotherAccount.id);
expect(updatedItem.categoryId).equals(itemCategory.id);
});
});
describe('DELETE: `items/:id`', () => {
it('Should response not found in case the item was not exist.', async () => {
const res = await request()
.delete('/api/items/10')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
});
it('Should response success in case was exist.', async () => {
const item = await tenantFactory.create('item');
const res = await request()
.delete(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
});
it('Should delete the given item from the storage.', async () => {
const item = await tenantFactory.create('item');
await request()
.delete(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const storedItems = await Item.tenant().query().where('id', item.id);
expect(storedItems).to.have.lengthOf(0);
});
});
describe('DELETE: `items?ids=`', () => {
it('Should response in case one of items ids where not exists.', async () => {
const res = await request()
.delete('/api/items')
.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: 'ITEMS.NOT.FOUND', code: 200, ids: [100, 200],
});
});
it('Should delete the given items from the storage.', async () => {
const item1 = await tenantFactory.create('item');
const item2 = await tenantFactory.create('item');
const res = await request()
.delete('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [item1.id, item2.id],
})
.send();
const foundItems = await Item.tenant().query();
expect(res.status).equals(200);
expect(foundItems.length).equals(0)
});
});
describe('GET: `items`', () => {
it('Should response unauthorized access in case the user not authenticated.', async () => {
const res = await request()
.get('/api/items')
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should retrieve items list with associated accounts.', async () => {
await tenantFactory.create('resource', { name: 'items' });
await tenantFactory.create('item');
const res = await request()
.get('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
expect(res.body.items).to.be.a('object');
expect(res.body.items.results).to.be.a('array');
expect(res.body.items.results.length).equals(1);
expect(res.body.items.results[0].cost_account).to.be.an('object');
expect(res.body.items.results[0].sell_account).to.be.an('object');
expect(res.body.items.results[0].inventory_account).to.be.an('object');
expect(res.body.items.results[0].category).to.be.an('object');
});
it('Should retrieve ordered items based on the given `column_sort_order` and `sort_order` query.', async () => {
await tenantFactory.create('item', { name: 'ahmed' });
await tenantFactory.create('item', { name: 'mohamed' });
const res = await request()
.get('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
column_sort_order: 'name',
sort_order: 'desc',
})
.send();
expect(res.body.items.results.length).equals(2);
expect(res.body.items.results[0].name).equals('mohamed');
expect(res.body.items.results[1].name).equals('ahmed');
});
it('Should retrieve pagination meta of items list.', async () => {
await tenantFactory.create('resource', { name: 'items' });
const res = await request()
.get('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.items.results).to.be.a('array');
expect(res.body.items.results.length).equals(0);
expect(res.body.items.pagination).to.be.a('object');
expect(res.body.items.pagination.total).to.be.a('number');
expect(res.body.items.pagination.total).equals(0)
});
it('Should retrieve filtered items based on custom view conditions.', async () => {
const item1 = await tenantFactory.create('item', { type: 'service' });
const item2 = await tenantFactory.create('item', { type: 'service' });
const item3 = await tenantFactory.create('item', { type: 'inventory' });
const item4 = await tenantFactory.create('item', { type: 'inventory' });
const view = await tenantFactory.create('view', {
name: 'Items Inventory',
resource_id: 2,
roles_logic_expression: '1',
});
const viewCondition = await tenantFactory.create('view_role', {
view_id: view.id,
index: 1,
field_id: 12,
value: 'inventory',
comparator: 'equals',
});
const res = await request()
.get('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
custom_view_id: view.id,
})
.send();
expect(res.body.customViewId).equals(view.id);
expect(res.body.viewColumns).to.be.a('array');
expect(res.body.viewConditions).to.be.a('array');
expect(res.body.items.results.length).equals(2);
expect(res.body.items.results[0].type).equals('inventory');
expect(res.body.items.results[1].type).equals('inventory');
});
it('Should retrieve filtered items based on filtering conditions.', async () => {
const item1 = await tenantFactory.create('item', { type: 'service' });
const item2 = await tenantFactory.create('item', { type: 'service', name: 'target' });
const item3 = await tenantFactory.create('item', { type: 'inventory' });
const item4 = await tenantFactory.create('item', { type: 'inventory' });
const res = await request()
.get('/api/items')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
stringified_filter_roles: JSON.stringify([
{
condition: 'AND',
field_key: 'type',
comparator: 'equals',
value: 'inventory',
},
{
condition: 'OR',
field_key: 'name',
comparator: 'equals',
value: 'target',
},
]),
})
.send();
expect(res.body.items.results.length).equals(3);
expect(res.body.items.results[0].name).equals('target');
expect(res.body.items.results[1].type).equals('inventory');
expect(res.body.items.results[2].type).equals('inventory');
});
});
});

View File

@@ -0,0 +1,311 @@
import {
request,
expect,
} from '~/testInit';
import ItemCategory from 'models/ItemCategory';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: /item_categories/', () => {
describe('POST `/items_categories``', async () => {
it('Should not create a item category if the user was not authorized.', async () => {
const res = await request().post('/api/item_categories').send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `name` be required.', async () => {
const res = await request()
.post('/api/item_categories')
.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 `parent_category_id` be exist in the storage.', async () => {
const res = await request()
.post('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Clothes',
parent_category_id: 10,
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'PARENT_CATEGORY_NOT_FOUND', code: 100,
});
});
it('Should response success with correct form data.', async () => {
const res = await request()
.post('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Clothes',
description: 'Here is description',
});
expect(res.status).equals(200);
expect(res.body.category).to.be.a('object');
expect(res.body.category.id).to.be.a('number');
expect(res.body.category.name).to.be.a('string');
expect(res.body.category.description).to.be.a('string');
});
it('Should item category data be saved to the storage.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.post('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Clothes',
description: 'Here is description',
parent_category_id: category.id,
});
expect(res.status).equals(200);
const storedCategory = await ItemCategory.tenant().query()
.where('id', res.body.category.id)
.first();
expect(storedCategory.name).equals('Clothes');
expect(storedCategory.description).equals('Here is description');
expect(storedCategory.parentCategoryId).equals(category.id);
expect(storedCategory.userId).to.be.a('number');
});
});
describe('POST `/items_category/{id}`', () => {
it('Should not update a item category if the user was not authorized.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.post(`/api/item_categories/${category.id}`)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `name` be required.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.post(`/api/item_categories/${category.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 `parent_category_id` be exist in the storage.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.post(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Name',
parent_category_id: 10,
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'PARENT_CATEGORY_NOT_FOUND', code: 100,
});
});
it('Should response success with correct data format.', async () => {
const category = await tenantFactory.create('item_category');
const anotherCategory = await tenantFactory.create('item_category');
const res = await request()
.post(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Name',
parent_category_id: anotherCategory.id,
description: 'updated description',
});
expect(res.status).equals(200);
});
it('Should item category data be update in the storage.', async () => {
const category = await tenantFactory.create('item_category');
const anotherCategory = await tenantFactory.create('item_category');
const res = await request()
.post(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'Name',
parent_category_id: anotherCategory.id,
description: 'updated description',
});
const storedCategory = await ItemCategory.tenant().query()
.where('id', res.body.id)
.first();
expect(storedCategory.name).equals('Name');
expect(storedCategory.description).equals('updated description');
expect(storedCategory.parentCategoryId).equals(anotherCategory.id);
});
});
describe('DELETE: `/items_categories`', async () => {
it('Should not delete the give item category if the user was not authorized.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.delete(`/api/item_categories/${category.id}`)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should not delete if the item category was not found.', async () => {
const res = await request()
.delete('/api/item_categories/10')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
});
it('Should response success after delete the given item category.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.delete(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
});
it('Should delete the give item category from the storage.', async () => {
const category = await tenantFactory.create('item_category');
const res = await request()
.delete(`/api/item_categories/${category.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const categories = await ItemCategory.tenant().query()
.where('id', category.id);
expect(categories).to.have.lengthOf(0);
});
});
describe('GET: `/item_categories`', () => {
it('Should retrieve list of item categories.', async () => {
const category1 = await tenantFactory.create('item_category');
const category2 = await tenantFactory.create('item_category', { parent_category_id: category1.id });
const res = await request()
.get('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.categories).to.be.a('array');
expect(res.body.categories.length).equals(2);
expect(res.body.categories[0].id).to.be.a('number');
expect(res.body.categories[0].name).to.be.a('string');
expect(res.body.categories[0].parent_category_id).to.be.a('null');
expect(res.body.categories[0].description).to.be.a('string');
expect(res.body.categories[1].parent_category_id).to.be.a('number');
});
it('Should retrieve of related items.', async () => {
const category1 = await tenantFactory.create('item_category');
const category2 = await tenantFactory.create('item_category', { parent_category_id: category1.id });
await tenantFactory.create('item', { category_id: category1.id });
const res = await request()
.get('/api/item_categories')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.categories[0].count).to.be.a('number');
expect(res.body.categories[0].count).equals(1);
});
});
describe('GET `/items_category/{id}', () => {
it('Should response not found with incorrect item category ID.', () => {
});
it('Should response success with exist item category.', () => {
});
it('Should response data of item category.', () => {
});
});
describe('DELETE: `/items_cateogires`', () => {
it('Should response bad request in case one of item categories id not exists in the storage.', async () => {
const res = await request()
.delete('/api/item_categories/bulk')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [1020, 2020],
})
.send();
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEM.CATEGORIES.IDS.NOT.FOUND', code: 200
});
});
it('Should delete the given item categories.', async () => {
const itemCategory = await tenantFactory.create('item_category');
const itemCategory2 = await tenantFactory.create('item_category');
const res = await request()
.delete('/api/item_categories/bulk')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
ids: [itemCategory.id, itemCategory2.id],
})
.send();
const deleteItemCategories = await ItemCategory.tenant().query()
.whereIn('id', [itemCategory.id, itemCategory2.id]);
expect(deleteItemCategories.length).equals(0);
});
});
});

View File

@@ -0,0 +1,120 @@
import {
request,
expect,
} from '~/testInit';
import Option from 'models/Option';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: `/options`', () => {
describe('POST: `/options/`', () => {
it('Should response unauthorized if the user was not logged in.', async () => {
const res = await request()
.post('/api/options')
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response the options key and group is not defined.', async () => {
const res = await request()
.post('/api/options')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
options: [
{
key: 'key',
value: 'hello world',
group: 'group',
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'OPTIONS.KEY.NOT.DEFINED',
code: 200,
keys: [
{ key: 'key', group: 'group' },
],
});
});
it('Should save options to the storage.', async () => {
const res = await request()
.post('/api/options')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
options: [{
key: 'name',
group: 'organization',
value: 'hello world',
}],
});
expect(res.status).equals(200);
const storedOptions = await Option.tenant().query()
.where('group', 'organization')
.where('key', 'name');
expect(storedOptions.metadata.length).equals(1);
});
});
describe('GET: `/options`', () => {
it('Should response unauthorized if the user was not unauthorized.', async () => {
const res = await request()
.get('/api/options')
.query({
group: 'organization',
})
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should retrieve options the associated to the given group.', async () => {
await tenantFactory.create('option', { group: 'organization', key: 'name' });
await tenantFactory.create('option', { group: 'organization', key: 'base_currency' });
const res = await request()
.get('/api/options')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
group: 'organization',
})
.send();
expect(res.status).equals(200);
expect(res.body.options).is.an('array');
expect(res.body.options.length).equals(2);
});
it('Should retrieve options that associated to the given key.', async () => {
await tenantFactory.create('option', { group: 'organization', key: 'base_currency' });
await tenantFactory.create('option', { group: 'organization', key: 'name' });
const res = await request()
.get('/api/options')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
key: 'name',
})
.send();
expect(res.status).equals(200);
expect(res.body.options).is.an('array');
expect(res.body.options.length).equals(1);
});
});
});

View File

@@ -0,0 +1,274 @@
import {
request,
expect,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
import {
PaymentReceive,
PaymentReceiveEntry,
} from 'models';
describe('route: `/sales/payment_receives`', () => {
describe('POST: `/sales/payment_receives`', () => {
it('Should `customer_id` be required.', async () => {
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'customer_id',
location: 'body',
});
});
it('Should `payment_date` be required.', async () => {
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'payment_date',
location: 'body',
});
});
it('Should `deposit_account_id` be required.', async () => {
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'deposit_account_id',
location: 'body',
});
});
it('Should `payment_receive_no` be required.', async () => {
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'payment_receive_no',
location: 'body',
});
});
it('Should invoices IDs be required.', async () => {
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'payment_receive_no',
location: 'body',
});
});
it('Should `customer_id` be exists on the storage.', async () => {
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 123,
payment_date: '2020-02-02',
reference_no: '123',
deposit_account_id: 100,
payment_receive_no: '123',
entries: [
{
invoice_id: 1,
payment_amount: 1000,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.ID.NOT.EXISTS', code: 200,
});
});
it('Should `deposit_account_id` be exists on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
payment_date: '2020-02-02',
reference_no: '123',
deposit_account_id: 10000,
payment_receive_no: '123',
entries: [
{
invoice_id: 1,
payment_amount: 1000,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'DEPOSIT.ACCOUNT.NOT.EXISTS', code: 300,
});
});
it('Should invoices IDs be exist on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const account = await tenantFactory.create('account');
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
payment_date: '2020-02-02',
reference_no: '123',
deposit_account_id: account.id,
payment_receive_no: '123',
entries: [
{
invoice_id: 1,
payment_amount: 1000,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'DEPOSIT.ACCOUNT.NOT.EXISTS', code: 300,
});
});
it('Should payment receive number be unique on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const account = await tenantFactory.create('account');
const paymentReceive = await tenantFactory.create('payment_receive', {
payment_receive_no: '123',
});
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
payment_date: '2020-02-02',
reference_no: '123',
deposit_account_id: account.id,
payment_receive_no: '123',
entries: [
{
invoice_id: 1,
payment_amount: 1000,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'PAYMENT.RECEIVE.NUMBER.EXISTS', code: 400,
});
});
it('Should store the payment receive details with associated entries.', async () => {
const customer = await tenantFactory.create('customer');
const account = await tenantFactory.create('account');
const invoice = await tenantFactory.create('sale_invoice');
const res = await request()
.post('/api/sales/payment_receives')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
payment_date: '2020-02-02',
reference_no: '123',
deposit_account_id: account.id,
payment_receive_no: '123',
entries: [
{
invoice_id: invoice.id,
payment_amount: 1000,
}
],
});
const storedPaymentReceived = await PaymentReceive.tenant().query().where('id', res.body.id).first();
expect(res.status).equals(200);
expect(storedPaymentReceived.customerId).equals(customer.id)
expect(storedPaymentReceived.referenceNo).equals('123');
expect(storedPaymentReceived.paymentReceiveNo).equals('123');
});
});
describe('POST: `/sales/payment_receives/:id`', () => {
it('Should update the payment receive details with associated entries.', async () => {
const paymentReceive = await tenantFactory.create('payment_receive');
const customer = await tenantFactory.create('customer');
const account = await tenantFactory.create('account');
const invoice = await tenantFactory.create('sale_invoice');
const res = await request()
.post(`/api/sales/payment_receives/${paymentReceive.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
payment_date: '2020-02-02',
reference_no: '123',
deposit_account_id: account.id,
payment_receive_no: '123',
entries: [
{
invoice_id: invoice.id,
payment_amount: 1000,
}
],
});
expect(res.status).equals(200);
});
});
describe('DELETE: `/sales/payment_receives/:id`', () => {
it('Should response the given payment receive is not exists on the storage.', async () => {
const res = await request()
.delete(`/api/sales/payment_receives/123`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'PAYMENT.RECEIVE.NO.EXISTS', code: 600,
});
});
});
});

View File

@@ -0,0 +1,234 @@
import {
request,
expect,
} from '~/testInit';
import Item from 'models/Item';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: `/financial_statements/receivable_aging_summary`', () => {
it('Should retrieve customers list.', async () => {
const customer1 = await tenantFactory.create('customer', { display_name: 'Ahmed' });
const customer2 = await tenantFactory.create('customer', { display_name: 'Mohamed' });
const res = await request()
.get('/api/financial_statements/receivable_aging_summary')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
expect(res.body.aging.customers).is.an('array');
expect(res.body.aging.customers.length).equals(2);
expect(res.body.aging.customers[0].customer_name).equals('Ahmed');
expect(res.body.aging.customers[1].customer_name).equals('Mohamed');
});
it('Should respon se the customers ids not found.', async () => {
const res = await request()
.get('/api/financial_statements/receivable_aging_summary')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
customer_ids: [3213, 3322],
})
.send();
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMERS.IDS.NOT.FOUND', code: 300, ids: [3213, 3322]
})
});
it('Should retrieve aging report columns.', async () => {
const customer1 = await tenantFactory.create('customer', { display_name: 'Ahmed' });
const customer2 = await tenantFactory.create('customer', { display_name: 'Mohamed' });
const res = await request()
.get('/api/financial_statements/receivable_aging_summary')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
as_date: '2020-06-01',
aging_days_before: 30,
aging_periods: 6,
})
.send();
expect(res.body.columns).length(6);
expect(res.body.columns[0].before_days).equals(0);
expect(res.body.columns[0].to_days).equals(30);
expect(res.body.columns[1].before_days).equals(31);
expect(res.body.columns[1].to_days).equals(60);
expect(res.body.columns[2].before_days).equals(61);
expect(res.body.columns[2].to_days).equals(90);
expect(res.body.columns[3].before_days).equals(91);
expect(res.body.columns[3].to_days).equals(120);
expect(res.body.columns[4].before_days).equals(121);
expect(res.body.columns[4].to_days).equals(150);
expect(res.body.columns[5].before_days).equals(151);
expect(res.body.columns[5].to_days).equals(null);
});
it('Should retrieve receivable total of the customers.', async () => {
const customer1 = await tenantFactory.create('customer', { display_name: 'Ahmed' });
const customer2 = await tenantFactory.create('customer', { display_name: 'Mohamed' });
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 10000,
credit: 0,
account_id: 10,
date: '2020-01-01',
});
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 1000,
credit: 0,
account_id: 10,
date: '2020-03-15',
});
// Receive
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 0,
credit: 8000,
account_id: 10,
date: '2020-06-01',
});
const res = await request()
.get('/api/financial_statements/receivable_aging_summary')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
as_date: '2020-06-01',
aging_days_before: 30,
aging_periods: 6,
})
.send();
expect(res.body.aging.total[0].total).equals(0);
expect(res.body.aging.total[1].total).equals(0);
expect(res.body.aging.total[2].total).equals(1000);
expect(res.body.aging.total[3].total).equals(0);
expect(res.body.aging.total[4].total).equals(0);
expect(res.body.aging.total[5].total).equals(2000);
});
it('Should retrieve customer aging.', async () => {
const customer1 = await tenantFactory.create('customer', { display_name: 'Ahmed' });
const customer2 = await tenantFactory.create('customer', { display_name: 'Mohamed' });
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 10000,
credit: 0,
account_id: 10,
date: '2020-01-14',
});
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 1000,
credit: 0,
account_id: 10,
date: '2020-03-15',
});
// Receive
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 0,
credit: 8000,
account_id: 10,
date: '2020-06-01',
});
const res = await request()
.get('/api/financial_statements/receivable_aging_summary')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
as_date: '2020-06-01',
aging_days_before: 30,
aging_periods: 6,
})
.send();
expect(res.body.aging.customers[0].aging[0].total).equals(0);
expect(res.body.aging.customers[0].aging[1].total).equals(0);
expect(res.body.aging.customers[0].aging[2].total).equals(1000);
expect(res.body.aging.customers[0].aging[3].total).equals(0);
expect(res.body.aging.customers[0].aging[4].total).equals(2000);
expect(res.body.aging.customers[0].aging[5].total).equals(0);
});
it('Should retrieve the queried customers ids only.', async () => {
const customer1 = await tenantFactory.create('customer', { display_name: 'Ahmed' });
const customer2 = await tenantFactory.create('customer', { display_name: 'Mohamed' });
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 10000,
credit: 0,
account_id: 10,
date: '2020-01-14',
});
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 1000,
credit: 0,
account_id: 10,
date: '2020-03-15',
});
// Receive
await tenantFactory.create('account_transaction', {
contact_id: customer1.id,
contact_type: 'customer',
debit: 0,
credit: 8000,
account_id: 10,
date: '2020-06-01',
});
const res = await request()
.get('/api/financial_statements/receivable_aging_summary')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({
as_date: '2020-06-01',
aging_days_before: 30,
aging_periods: 6,
customer_ids: [customer1.id],
})
.send();
expect(res.body.aging.customers.length).equals(1);
})
});

View File

@@ -0,0 +1,439 @@
const { iteratee } = require('lodash');
import { tenantWebsite, tenantFactory, loginRes } from '~/dbInit';
import { request, expect } from '~/testInit';
import { SaleEstimate, SaleEstimateEntry } from '../../src/models';
describe('route: `/sales/estimates`', () => {
describe('POST: `/sales/estimates`', () => {
it('Should `customer_id` be required.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'customer_id',
location: 'body',
});
});
it('Should `estimate_date` be required.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'estimate_date',
location: 'body',
});
});
it('Should `estimate_number` be required.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'estimate_number',
location: 'body',
});
});
it('Should `entries` be atleast one entry.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
value: [],
msg: 'Invalid value',
param: 'entries',
location: 'body',
});
});
it('Should `entries.*.item_id` be required.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].item_id',
location: 'body',
});
});
it('Should `entries.*.quantity` be required.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].quantity',
location: 'body',
});
});
it('Should be `entries.*.rate` be required.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].rate',
location: 'body',
});
});
it('Should `customer_id` be exists on the storage.', async () => {
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 10,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: '1',
entries: [
{
item_id: 1,
rate: 1,
quantity: 2,
}
],
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.ID.NOT.FOUND', code: 200,
});
});
it('Should `estimate_number` be unique on the storage.', async () => {
const saleEstimate = await tenantFactory.create('sale_estimate');
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: saleEstimate.customerId,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: saleEstimate.estimateNumber,
entries: [
{
item_id: 1,
rate: 1,
quantity: 2,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ESTIMATE.NUMBER.IS.NOT.UNQIUE', code: 300,
});
});
it('Should `entries.*.item_id` be exists on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: '12',
entries: [
{
item_id: 1,
rate: 1,
quantity: 2,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEMS.IDS.NOT.EXISTS', code: 400,
});
});
it('Should store the given details on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const item = await tenantFactory.create('item');
const res = await request()
.post('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: '12',
reference: 'reference',
note: 'note here',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: item.id,
rate: 1,
quantity: 2,
description: 'desc..'
}
],
});
expect(res.status).equals(200);
const storedEstimate = await SaleEstimate.tenant().query().where('id', res.body.id).first();
const storedEstimateEntry = await SaleEstimateEntry.tenant().query().where('estimate_id', res.body.id).first();
expect(storedEstimate.id).equals(res.body.id);
expect(storedEstimate.customerId).equals(customer.id);
expect(storedEstimate.reference).equals('reference')
expect(storedEstimate.note).equals('note here');
expect(storedEstimate.termsConditions).equals('terms and conditions');
expect(storedEstimate.estimateNumber).equals('12');
expect(storedEstimateEntry.itemId).equals(item.id);
expect(storedEstimateEntry.rate).equals(1);
expect(storedEstimateEntry.quantity).equals(2);
expect(storedEstimateEntry.description).equals('desc..');
});
});
describe('DELETE: `/sales/estimates/:id`', () => {
it('Should estimate id be exists on the storage.', async () => {
const estimate = await tenantFactory.create('sale_estimate');
const res = await request()
.delete(`/api/sales/estimates/123`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.ESTIMATE.ID.NOT.FOUND', code: 200
});
});
it('Should delete the given estimate with associated entries from the storage.', async () => {
const estimate = await tenantFactory.create('sale_estimate');
const estimateEntry = await tenantFactory.create('sale_estimate_entry', { estimate_id: estimate.id });
const res = await request()
.delete(`/api/sales/estimates/${estimate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const foundEstimate = await SaleEstimate.tenant().query().where('id', estimate.id);
const foundEstimateEntry = await SaleEstimateEntry.tenant().query().where('estimate_id', estimate.id);
expect(res.status).equals(200);
expect(foundEstimate.length).equals(0);
expect(foundEstimateEntry.length).equals(0);
});
});
describe('POST: `/sales/estimates/:id`', () => {
it('Should estimate id be exists on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/sales/estimates/123`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: '12',
reference: 'reference',
note: 'note here',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: item.id,
rate: 1,
quantity: 2,
description: 'desc..'
}
],
})
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.ESTIMATE.ID.NOT.FOUND', code: 200
});
});
it('Should `entries.*.item_id` be exists on the storage.', async () => {
const saleEstimate = await tenantFactory.create('sale_estimate');
const customer = await tenantFactory.create('customer');
const res = await request()
.post(`/api/sales/estimates/${saleEstimate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: '12',
entries: [
{
item_id: 1,
rate: 1,
quantity: 2,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEMS.IDS.NOT.EXISTS', code: 400
});
});
it('Should sale estimate number unique on the storage.', async () => {
const saleEstimate = await tenantFactory.create('sale_estimate');
const saleEstimate2 = await tenantFactory.create('sale_estimate');
const res = await request()
.post(`/api/sales/estimates/${saleEstimate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: saleEstimate.customerId,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: saleEstimate2.estimateNumber,
entries: [
{
item_id: 1,
rate: 1,
quantity: 2,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ESTIMATE.NUMBER.IS.NOT.UNQIUE', code: 300,
});
});
it('Should sale estimate entries IDs be exists on the storage and associated to the sale estimate.', async () => {
const item = await tenantFactory.create('item');
const saleEstimate = await tenantFactory.create('sale_estimate');
const saleEstimate2 = await tenantFactory.create('sale_estimate');
const res = await request()
.post(`/api/sales/estimates/${saleEstimate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: saleEstimate.customerId,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: saleEstimate.estimateNumber,
entries: [
{
id: 100,
item_id: item.id,
rate: 1,
quantity: 2,
}
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ESTIMATE.NOT.FOUND.ENTRIES.IDS', code: 500,
});
});
it('Should update the given sale estimates with associated entries.', async () => {
const customer = await tenantFactory.create('customer');
const item = await tenantFactory.create('item');
const saleEstimate = await tenantFactory.create('sale_estimate');
const saleEstimateEntry = await tenantFactory.create('sale_estimate_entry', {
estimate_id: saleEstimate.id,
});
const res = await request()
.post(`/api/sales/estimates/${saleEstimate.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
estimate_date: '2020-02-02',
expiration_date: '2020-03-03',
estimate_number: '123',
entries: [
{
id: saleEstimateEntry.id,
item_id: item.id,
rate: 100,
quantity: 200,
}
],
});
expect(res.status).equals(200);
});
});
describe('GET: `/sales/estimates`', () => {
it('Should retrieve sales estimates.', async () => {
const res = await request()
.get('/api/sales/estimates')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
console.log(res.status, res.body);
});
});
});

View File

@@ -0,0 +1,494 @@
import { tenantWebsite, tenantFactory, loginRes } from '~/dbInit';
import { request, expect } from '~/testInit';
import { SaleInvoice } from 'models';
import { SaleInvoiceEntry } from '../../src/models';
describe('route: `/sales/invoices`', () => {
describe('POST: `/sales/invoices`', () => {
it('Should `customer_id` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'customer_id',
location: 'body',
});
});
it('Should `invoice_date` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'invoice_date',
location: 'body',
});
});
it('Should `due_date` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'due_date',
location: 'body',
});
});
it('Should `invoice_no` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'invoice_no',
location: 'body',
});
});
it('Should `status` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'status',
location: 'body',
});
});
it('Should `entries.*.item_id` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].item_id',
location: 'body',
});
});
it('Should `entries.*.quantity` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].quantity',
location: 'body',
});
});
it('Should `entries.*.rate` be required.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].rate',
location: 'body',
});
});
it('Should `customer_id` be exists on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 123,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '123',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: 1,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.ID.NOT.EXISTS', code: 200,
});
});
it('Should `invoice_date` be bigger than `due_date`.', async () => {
});
it('Should `invoice_no` be unique on the storage.', async () => {
const saleInvoice = await tenantFactory.create('sale_invoice', {
invoice_no: '123',
});
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 123,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '123',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: 1,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.INVOICE.NUMBER.IS.EXISTS', code: 200
});
});
it('Should `entries.*.item_id` be exists on the storage.', async () => {
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 123,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '123',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: 1,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEMS.IDS.NOT.EXISTS', code: 300,
});
});
it('Should save the given sale invoice details with associated entries.', async () => {
const customer = await tenantFactory.create('customer');
const item = await tenantFactory.create('item');
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '123',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: item.id,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(200);
});
});
describe('POST: `/api/sales/invoices/:id`', () => {
it('Should `customer_id` be required.', async () => {
const res = await request()
.post('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'customer_id',
location: 'body',
});
});
it('Should `invoice_date` be required.', async () => {
const res = await request()
.post('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'invoice_date',
location: 'body',
});
});
it('Should `status` be required.', async () => {
const res = await request()
.post('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'status',
location: 'body',
});
});
it('Should `entries.*.item_id` be required.', async () => {
const res = await request()
.post('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].item_id',
location: 'body',
});
});
it('Should `entries.*.quantity` be required.', async () => {
const res = await request()
.post('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].quantity',
location: 'body',
});
});
it('Should `entries.*.rate` be required.', async () => {
const res = await request()
.post('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
entries: [{}],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'entries[0].rate',
location: 'body',
});
});
it('Should `customer_id` be exists on the storage.', async () => {
const customer = await tenantFactory.create('customer');
const saleInvoice = await tenantFactory.create('sale_invoice');
const res = await request()
.post(`/api/sales/invoices/${saleInvoice.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 123,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '123',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: 1,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.ID.NOT.EXISTS', code: 200,
});
});
it('Should `invoice_date` be bigger than `due_date`.', async () => {
});
it('Should `invoice_no` be unique on the storage.', async () => {
const saleInvoice = await tenantFactory.create('sale_invoice', {
invoice_no: '123',
});
const res = await request()
.post('/api/sales/invoices')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: 123,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '123',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: 1,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.INVOICE.NUMBER.IS.EXISTS', code: 200
});
});
it('Should update the sale invoice details with associated entries.', async () => {
const saleInvoice = await tenantFactory.create('sale_invoice');
const customer = await tenantFactory.create('customer');
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/sales/invoices/${saleInvoice.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
customer_id: customer.id,
invoice_date: '2020-02-02',
due_date: '2020-03-03',
invoice_no: '1',
reference_no: '123',
status: 'published',
invoice_message: 'Invoice message...',
terms_conditions: 'terms and conditions',
entries: [
{
item_id: item.id,
rate: 1,
quantity: 1,
discount: 1,
}
]
});
expect(res.status).equals(200);
});
});
describe('DELETE: `/sales/invoices/:id`', () => {
it('Should retrieve sale invoice not found.', async () => {
const res = await request()
.delete('/api/sales/invoices/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.INVOICE.NOT.FOUND', code: 200,
});
});
it('Should delete the given sale invoice with assocaited entries.', async () => {
const saleInvoice = await tenantFactory.create('sale_invoice');
const saleInvoiceEntey = await tenantFactory.create('sale_invoice_entry', {
sale_invoice_id: saleInvoice.id,
});
const res = await request()
.delete(`/api/sales/invoices/${saleInvoice.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const storedSaleInvoice = await SaleInvoice.tenant().query().where('id', saleInvoice.id);
const storedSaleInvoiceEntry = await SaleInvoiceEntry.tenant().query().where('id', saleInvoiceEntey.id);
expect(res.status).equals(200);
expect(storedSaleInvoice.length).equals(0);
expect(storedSaleInvoiceEntry.length).equals(0);
});
});
});

View File

@@ -0,0 +1,294 @@
import { tenantWebsite, tenantFactory, loginRes } from '~/dbInit';
import { request, expect } from '~/testInit';
import { SaleReceipt } from 'models';
describe('route: `/sales/receipts`', () => {
describe('POST: `/sales/receipts`', () => {
it('Should `deposit_account_id` be required.', async () => {
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'deposit_account_id',
location: 'body',
});
});
it('Should `customer_id` be required.', async () => {
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'customer_id',
location: 'body',
});
});
it('should `receipt_date` be required.', async () => {
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'receipt_date',
location: 'body',
});
});
it('Should `entries.*.item_id` be required.', async () => {});
it('Should `deposit_account_id` be exists.', async () => {
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
deposit_account_id: 12220,
customer_id: 1,
receipt_date: '2020-02-02',
reference_no: '123',
entries: [
{
item_id: 1,
quantity: 1,
rate: 2,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'DEPOSIT.ACCOUNT.NOT.EXISTS',
code: 300,
});
});
it('Should `customer_id` be exists.', async () => {
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
deposit_account_id: 12220,
customer_id: 1001,
receipt_date: '2020-02-02',
reference_no: '123',
entries: [
{
item_id: 1,
quantity: 1,
rate: 2,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'CUSTOMER.ID.NOT.EXISTS',
code: 200,
});
});
it('Should all `entries.*.item_id` be exists on the storage.', async () => {
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
deposit_account_id: 12220,
customer_id: 1001,
receipt_date: '2020-02-02',
reference_no: '123',
entries: [
{
item_id: 1000,
quantity: 1,
rate: 2,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ITEMS.IDS.NOT.EXISTS',
code: 400,
});
});
it('Should store the sale receipt details with entries to the storage.', async () => {
const item = await tenantFactory.create('item');
const customer = await tenantFactory.create('customer');
const account = await tenantFactory.create('account');
const res = await request()
.post('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
deposit_account_id: account.id,
customer_id: customer.id,
receipt_date: '2020-02-02',
reference_no: '123',
receipt_message: 'Receipt message...',
statement: 'Receipt statement...',
entries: [
{
item_id: item.id,
quantity: 1,
rate: 2,
},
],
});
const storedSaleReceipt = await SaleReceipt.tenant()
.query()
.where('id', res.body.id)
.first();
expect(res.status).equals(200);
expect(storedSaleReceipt.depositAccountId).equals(account.id);
expect(storedSaleReceipt.referenceNo).equals('123');
expect(storedSaleReceipt.customerId).equals(customer.id);
expect(storedSaleReceipt.receiptMessage).equals('Receipt message...');
expect(storedSaleReceipt.statement).equals('Receipt statement...');
});
});
describe('DELETE: `/sales/receipts/:id`', () => {
it('Should the given sale receipt id be exists on the storage.', async () => {
const res = await request()
.delete('/api/sales/receipts/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.RECEIPT.NOT.FOUND',
code: 200,
});
});
it('Should delete the sale receipt with associated entries and journal transactions.', async () => {
const saleReceipt = await tenantFactory.create('sale_receipt');
const saleReceiptEntry = await tenantFactory.create(
'sale_receipt_entry',
{
sale_receipt_id: saleReceipt.id,
}
);
const res = await request()
.delete(`/api/sales/receipts/${saleReceipt.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const storedSaleReceipt = await SaleReceipt.tenant()
.query()
.where('id', saleReceipt.id);
const storedSaleReceiptEntries = await SaleReceipt.tenant()
.query()
.where('id', saleReceiptEntry.id);
expect(res.status).equals(200);
expect(storedSaleReceipt.length).equals(0);
expect(storedSaleReceiptEntries.length).equals(0);
});
});
describe('POST: `/sales/receipts/:id`', () => {
it('Should the given sale receipt id be exists on the storage.', async () => {
const res = await request()
.post('/api/sales/receipts/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
deposit_account_id: 123,
customer_id: 123,
receipt_date: '2020-02-02',
reference_no: '123',
receipt_message: 'Receipt message...',
statement: 'Receipt statement...',
entries: [
{
item_id: 123,
quantity: 1,
rate: 2,
},
],
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'SALE.RECEIPT.NOT.FOUND',
code: 200,
});
});
it('Should update the sale receipt details with associated entries.', async () => {
const saleReceipt = await tenantFactory.create('sale_receipt');
const depositAccount = await tenantFactory.create('account');
const customer = await tenantFactory.create('customer');
const item = await tenantFactory.create('item');
const res = await request()
.post(`/api/sales/receipts/${saleReceipt.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
deposit_account_id: depositAccount.id,
customer_id: customer.id,
receipt_date: '2020-02-02',
reference_no: '123',
receipt_message: 'Receipt message...',
statement: 'Receipt statement...',
entries: [
{
id: 100,
item_id: item.id,
quantity: 1,
rate: 2,
},
],
});
expect(res.status).equals(400);
expect(res.body.errors).include.something.deep.equals({
type: 'ENTRIES.IDS.NOT.FOUND', code: 500,
});
});
});
describe('GET: `/sales/receipts`', () => {
it('Should response the custom view id not exists on the storage.', async () => {
const res = await request()
.get('/api/sales/receipts')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
});
console.log(res.status, res.body);
});
it('Should retrieve all sales receipts on the storage with pagination meta.', () => {
});
});
});

View File

@@ -0,0 +1,203 @@
import knex from '@/database/knex';
import {
request,
expect,
} from '~/testInit';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: `/routes`', () => {
describe('GET: `/users`', () => {
it('Should response unauthorized if the user was not authorized.', async () => {
const res = await request().get('/api/users');
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should retrieve the stored users with pagination meta.', async () => {
await tenantFactory.create('user');
const res = await request()
.get('/api/users')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.users.results.length).equals(2);
expect(res.body.users.total).equals(2);
});
});
describe('POST: `/users/:id`', () => {
it('Should create a new user if the user was not authorized.', async () => {
const user = await tenantFactory.create('user');
const res = await request()
.post(`/api/users/${user.id}`);
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `first_name` be required.', async () => {
const user = await  tenantFactory.create('user');
const res = await request()
.post(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
const foundFirstNameParam = res.body.errors.find((error) => error.param === 'first_name');
expect(!!foundFirstNameParam).equals(true);
});
it('Should `last_name` be required.', async () => {
const user = await tenantFactory.create('user');
const res = await request()
.post(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
const foundFirstNameParam = res.body.errors.find((error) => error.param === 'last_name');
expect(!!foundFirstNameParam).equals(true);
});
it('Should `email` be required.', async () => {
const user = await tenantFactory.create('user');
const res = await request()
.post(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(422);
const foundEmailParam = res.body.errors.find((error) => error.param === 'email');
expect(!!foundEmailParam).equals(true);
});
it('Should be `email` be valid format.', async () => {
const user = await tenantFactory.create('user');
const res = await request()
.post(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
first_name: user.first_name,
last_name: user.last_name,
email: 'email',
phone_number: user.phone_number,
status: 1,
});
expect(res.status).equals(422);
const foundEmailParam = res.body.errors.find((error) => error.param === 'email');
expect(!!foundEmailParam).equals(true);
});
it('Should `phone_number` be valid format.', async () => {
const user = tenantFactory.create('user');
const res = await request()
.post(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
first_name: user.first_name,
last_name: user.last_name,
email: user.email,
phone_number: 'phone_number',
status: 1,
});
expect(res.status).equals(422);
const phoneNumberParam = res.body.errors.find((error) => error.param === 'phone_number');
expect(!!phoneNumberParam).equals(true);
});
});
describe('GET: `/users/:id`', () => {
it('Should not success if the user was not authorized.', async () => {
const res = await request().get('/api/users/1');
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response not found if the user was not exist.', async () => {
const res = await request()
.get('/api/users/10')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
});
it('Should response success if the user was exist.', async () => {
const user = await tenantFactory.create('user');
const res = await request()
.get(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
});
});
describe('DELETE: `/users/:id`', () => {
it('Should not success if the user was not authorized.', async () => {
const res = await request().delete('/api/users/1');
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response not found if the user was not exist.', async () => {
const res = await request()
.delete('/api/users/10')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'USER_NOT_FOUND', code: 100,
});
});
it('Should response success if the user was exist.', async () => {
const user = await tenantFactory.create('user');
const res = await request()
.delete(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
});
it('Should delete the give user from the storage.', async () => {
const user = await tenantFactory.create('user');
await request()
.delete(`/api/users/${user.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
const storedUsers = await knex('users').where('id', user.id);
expect(storedUsers).to.have.lengthOf(0);
});
});
});

View File

@@ -0,0 +1,193 @@
import {
request,
expect,
} from '~/testInit';
import Currency from 'models/Currency';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
import Vendor from 'models/Vendor';
describe('route: `/vendors`', () => {
describe('POST: `/vendors`', () => {
it('Should response unauthorized in case the user was not logged in.', async () => {
const res = await request()
.post('/api/vendors')
.send({});
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `display_name` be required field.', async () => {
const res = await request()
.post('/api/vendors')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'display_name', location: 'body',
})
});
it('Should store the vendor data to the storage.', async () => {
const res = await request()
.post('/api/vendors')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
first_name: 'Ahmed',
last_name: 'Bouhuolia',
company_name: 'Bigcapital',
display_name: 'Ahmed Bouhuolia, Bigcapital',
email: 'a.bouhuolia@live.com',
work_phone: '0927918381',
personal_phone: '0925173379',
billing_address_city: 'Tripoli',
billing_address_country: 'Libya',
billing_address_email: 'a.bouhuolia@live.com',
billing_address_state: 'State Tripoli',
billing_address_zipcode: '21892',
shipping_address_city: 'Tripoli',
shipping_address_country: 'Libya',
shipping_address_email: 'a.bouhuolia@live.com',
shipping_address_state: 'State Tripoli',
shipping_address_zipcode: '21892',
note: '__desc__',
active: true,
});
expect(res.status).equals(200);
const foundVendor = await Vendor.tenant().query().where('id', res.body.id);
expect(foundVendor[0].firstName).equals('Ahmed');
expect(foundVendor[0].lastName).equals('Bouhuolia');
expect(foundVendor[0].companyName).equals('Bigcapital');
expect(foundVendor[0].displayName).equals('Ahmed Bouhuolia, Bigcapital');
expect(foundVendor[0].email).equals('a.bouhuolia@live.com');
expect(foundVendor[0].workPhone).equals('0927918381');
expect(foundVendor[0].personalPhone).equals('0925173379');
expect(foundVendor[0].billingAddressCity).equals('Tripoli');
expect(foundVendor[0].billingAddressCountry).equals('Libya');
expect(foundVendor[0].billingAddressEmail).equals('a.bouhuolia@live.com');
expect(foundVendor[0].billingAddressState).equals('State Tripoli');
expect(foundVendor[0].billingAddressZipcode).equals('21892');
expect(foundVendor[0].shippingAddressCity).equals('Tripoli');
expect(foundVendor[0].shippingAddressCountry).equals('Libya');
expect(foundVendor[0].shippingAddressEmail).equals('a.bouhuolia@live.com');
expect(foundVendor[0].shippingAddressState).equals('State Tripoli');
expect(foundVendor[0].shippingAddressZipcode).equals('21892');
});
});
describe('GET: `/vendors/:id`', () => {
it('Should response not found in case the given vendor id was not exists on the storage.', async () => {
const res = await request()
.get('/api/vendors/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'VENDOR.NOT.FOUND', code: 200,
});
});
});
describe('GET: `vendors`', () => {
it('Should response vendors items', async () => {
await tenantFactory.create('vendor');
await tenantFactory.create('vendor');
const res = await request()
.get('/api/vendors')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.vendors.results.length).equals(2);
});
});
describe('DELETE: `/vendors/:id`', () => {
it('Should response not found in case the given vendor id was not exists on the storage.', async () => {
const res = await request()
.delete('/api/vendors/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'VENDOR.NOT.FOUND', code: 200,
});
});
it('Should delete the given vendor from the storage.', async () => {
const vendor = await tenantFactory.create('vendor');
const res = await request()
.delete(`/api/vendors/${vendor.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
const foundVendor = await Vendor.tenant().query().where('id', vendor.id);
expect(foundVendor.length).equals(0);
})
});
describe('POST: `/vendors/:id`', () => {
it('Should response vendor not found', async () => {
const res = await request()
.post('/api/vendors/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
display_name: 'Ahmed Bouhuolia, Bigcapital',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'VENDOR.NOT.FOUND', code: 200,
});
});
it('Should update details of the given vendor.', async () => {
const vendor = await tenantFactory.create('vendor');
const res = await request()
.post(`/api/vendors/${vendor.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
display_name: 'Ahmed Bouhuolia, Bigcapital',
});
expect(res.status).equals(200);
const foundVendor = await Vendor.tenant().query().where('id', res.body.id);
expect(foundVendor.length).equals(1);
expect(foundVendor[0].displayName).equals('Ahmed Bouhuolia, Bigcapital');
})
});
});

View File

@@ -0,0 +1,936 @@
import {
request,
expect,
} from '~/testInit';
import View from 'models/View';
import ViewRole from 'models/ViewRole';
import 'models/ResourceField';
import ViewColumn from '../../src/models/ViewColumn';
import {
tenantWebsite,
tenantFactory,
loginRes
} from '~/dbInit';
describe('routes: `/views`', () => {
describe('GET: `/views`', () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const res = await request().get('/api/views');
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should retrieve all views of the given resource name.', async () => {
const resource = await tenantFactory.create('resource', { name: 'resource_name' });
const resourceFields = await tenantFactory.create('view', {
name: 'Resource View',
resource_id: resource.id,
roles_logic_expression: '',
});
const res = await request()
.get('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.query({ resource_name: 'resource_name' })
.send();
expect(res.status).equals(200);
expect(res.body.views.length).equals(1);
});
});
describe('GET `/views/:id`', () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const resource = await tenantFactory.create('resource', { name: 'resource_name' });
const resourceView = await tenantFactory.create('view', {
name: 'Resource View',
resource_id: resource.id,
roles_logic_expression: '',
});
const res = await request()
.get(`/api/views/${resourceView.id}`)
.query({ resource_name: 'resource_name' })
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should response not found in case the given view was not found.', async () => {
const resource = await tenantFactory.create('resource', { name: 'resource_name' });
const resourceView = await tenantFactory.create('view', {
name: 'Resource View',
resource_id: resource.id,
roles_logic_expression: '',
});
const res = await request()
.get('/api/views/123')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors[0]).deep.equals({
type: 'VIEW_NOT_FOUND', code: 100,
});
});
it('Should retrieve details of the given view with associated graphs.', async () => {
const resource = await tenantFactory.create('resource', { name: 'resource_name' });
const resourceView = await tenantFactory.create('view', {
name: 'Resource View',
resource_id: resource.id,
roles_logic_expression: '1 AND 2',
});
const resourceField = await tenantFactory.create('resource_field', {
label_name: 'Expense Account',
key: 'expense_account',
data_type: 'integer',
resource_id: resource.id,
active: true,
predefined: true,
builtin: true,
});
const viewRole = await tenantFactory.create('view_role', {
view_id: resourceView.id,
index: 1,
field_id: resourceField.id,
value: '12',
comparator: 'equals',
});
const res = await request()
.get(`/api/views/${resourceView.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(200);
expect(res.body.view.name).equals(resourceView.name);
expect(res.body.view.resource_id).equals(resourceView.resourceId);
expect(res.body.view.roles_logic_expression).equals(resourceView.rolesLogicExpression);
expect(res.body.view.roles.length).equals(1);
expect(res.body.view.roles[0].view_id).equals(viewRole.viewId);
});
});
describe('POST: `/views`', () => {
it('Should response unauthorzied in case the user was not authorized.', async () => {
const res = await request().post('/api/views');
expect(res.status).equals(401);
expect(res.body.message).equals('Unauthorized');
});
it('Should `name` be required.', async () => {
await tenantFactory.create('resource');
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId);
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'name', location: 'body',
});
});
it('Should `resource_name` be required.', async () => {
await tenantFactory.create('resource');
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId);
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'resource_name', location: 'body',
});
});
it('Should `columns` be minimum limited', async () => {
await tenantFactory.create('resource');
const res = await request()
.post('/api/views', {
label: 'View Label',
columns: [],
})
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId);
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'columns', location: 'body',
});
});
it('Should `columns` be array.', async () => {
await tenantFactory.create('resource');
const res = await request()
.post('/api/views', {
label: 'View Label',
columns: 'not_array',
})
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId);
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'columns', location: 'body',
});
});
it('Should `roles.*.field_key` be required.', async () => {
const resource = await tenantFactory.create('resource');
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
label: 'View Label',
roles: [{}],
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'roles[0].field_key', location: 'body',
});
});
it('Should `roles.*.comparator` be valid.', async () => {
const resource = await tenantFactory.create('resource');
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
label: 'View Label',
roles: [{}],
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const paramsErrors = res.body.errors.map((error) => error.param);
expect(paramsErrors).to.include('roles[0].comparator');
});
it('Should `roles.*.index` be number as integer.', async () => {
const resource = await tenantFactory.create('resource');
const res = await request()
.post('/api/views')
.send({
resource_name: resource.name,
label: 'View Label',
roles: [
{ index: 'not_numeric' },
],
})
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId);
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'roles[0].index',
location: 'body',
});
});
it('Should response not found in case resource was not exist.', async () => {
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: 'not_found',
name: 'View Label',
columns: [
{ key: 'amount', index: 1 },
{ key: 'thumbnail', index: 1 },
{ key: 'status', index: 1 },
],
roles: [{
index: 1,
field_key: 'amount',
comparator: 'equals',
value: '100',
}],
logic_expression: '1',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'RESOURCE_NOT_FOUND', code: 100,
});
});
it('Should response invalid logic expression.', async () =>{
const resource = await tenantFactory.create('resource');
await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
logic_expression: '100 && 100',
name: 'View Label',
columns: [
{ key: 'amount', index: 1 },
],
roles: [{
index: 1,
field_key: 'amount',
comparator: 'equals',
value: '100',
}],
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'VIEW.ROLES.LOGIC.EXPRESSION.INVALID', code: 400,
});
});
it('Should response the roles fields not exist in case role field was not exist.', async () => {
const resource = await tenantFactory.create('resource');
await tenantFactory.create('resource_field', { resource_id: resource.id, label_name: 'Amount' });
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
name: 'View Label',
logic_expression: '1',
columns: [
{ key: 'amount', index: 1 },
{ key: 'thumbnail', index: 1 },
{ key: 'status', index: 1 },
],
roles: [{
index: 1,
field_key: 'price',
comparator: 'equals',
value: '100',
}],
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'RESOURCE_FIELDS_NOT_EXIST', code: 100, fields: ['price'],
});
});
it('Should response the columns that not exists in case column was not exist.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
name: 'View Label',
logic_expression: '1',
columns: [
{ key: 'amount', index: 1 },
{ key: 'thumbnail', index: 2 },
{ key: 'status', index: 3 },
],
roles: [{
index: 1,
field_key: 'price',
comparator: 'equals',
value: '100',
}],
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'COLUMNS_NOT_EXIST', code: 200, columns: ['thumbnail', 'status'],
});
});
it('Should save the given details of the view.', async () => {
const resource = await tenantFactory.create('resource');
await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
name: 'View Label',
logic_expression: '1',
columns: [
{ key: 'amount', index: 1 },
],
roles: [{
index: 1,
field_key: 'amount',
comparator: 'equals',
value: '100',
}],
});
const storedView = await View.tenant().query().where('name', 'View Label').first();
expect(storedView.name).equals('View Label');
expect(storedView.predefined).equals(0);
expect(storedView.resourceId).equals(resource.id);
});
it('Should save the given details of view fields that associated to the given view id.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
name: 'View Label',
columns: [{ key: 'amount', index: 1 }],
logic_expression: '1',
roles: [{
index: 1,
field_key: 'amount',
comparator: 'equals',
value: '100',
}],
});
const viewRoles = await ViewRole.tenant().query().where('view_id', res.body.id);
expect(viewRoles.length).equals(1);
expect(viewRoles[0].index).equals(1);
expect(viewRoles[0].fieldId).equals(resourceField.id);
expect(viewRoles[0].value).equals('100');
expect(viewRoles[0].comparator).equals('equals');
});
it('Should save columns that associated to the given view.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const res = await request()
.post('/api/views')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
resource_name: resource.name,
name: 'View Label',
logic_expression: '1',
columns: [
{ key: 'amount', index: 1 },
],
roles: [{
index: 1,
field_key: 'amount',
comparator: 'equals',
value: '100',
}],
});
const viewColumns = await ViewColumn.tenant().query().where('view_id', res.body.id);
expect(viewColumns.length).equals(1);
});
});
describe('POST: `/views/:view_id`', () => {
it('Should `name` be required.', async () => {
const view = await tenantFactory.create('view');
const res = await request()
.post(`/api/views/${view.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'name', location: 'body',
});
});
it('Should columns be minimum limited', async () => {
const view = await tenantFactory.create('view');
const res = await request()
.post(`/api/views/${view.id}`, {
label: 'View Label',
columns: [],
})
.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');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'columns', location: 'body',
});
});
it('Should columns be array.', async () => {
const view = await tenantFactory.create('view');
const res = await request()
.post(`/api/views/${view.id}`, {
label: 'View Label',
columns: 'not_array',
})
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
columns: 'columns'
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'columns', location: 'body', value: 'columns',
});
});
it('Should `roles.*.field_key` be required.', async () => {
const view = await tenantFactory.create('view');
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
label: 'View Label',
roles: [{}],
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'roles[0].field_key', location: 'body',
});
});
it('Should `roles.*.comparator` be required.', async () => {
const view = await tenantFactory.create('view');
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
label: 'View Label',
roles: [{}],
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'roles[0].comparator', location: 'body',
});
});
it('Should `roles.*.index` be number as integer.', async () => {
const view = await tenantFactory.create('view');
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
label: 'View Label',
roles: [{ index: 'not_numeric' }],
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value',
param: 'roles[0].index',
location: 'body',
value: 'not_numeric',
});
});
it('Should response the roles fields not exist in case role field was not exist.', async () => {
const view = await tenantFactory.create('view');
await tenantFactory.create('resource_field', {
resource_id: view.resource_id,
label_name: 'Amount',
});
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: 'amount',
index: 1,
}, {
key: 'thumbnail',
index: 2,
}, {
key: 'status',
index: 3,
}],
roles: [{
index: 1,
field_key: 'price',
comparator: 'equals',
value: '100',
}],
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'RESOURCE_FIELDS_NOT_EXIST', code: 100, fields: ['price'],
});
});
it('Should response the resource columns not exists in case the column keys was not exist.', async () => {
const view = await tenantFactory.create('view');
await tenantFactory.create('resource_field', {
resource_id: view.resource_id,
label_name: 'Amount',
});
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: 'amount',
index: 1,
}, {
key: 'thumbnail',
index: 2,
}, {
key: 'status',
index: 3,
}],
roles: [{
index: 1,
field_key: 'price',
comparator: 'equals',
value: '100',
}],
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'RESOURCE_COLUMNS_NOT_EXIST',
code: 200,
columns: ['amount', 'thumbnail', 'status'],
});
});
it('Should validate the logic expressions with the given conditions.', () => {
});
it('Should delete the view roles that not presented the post data.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const view = await tenantFactory.create('view', { resource_id: resource.id });
const viewRole = await tenantFactory.create('view_role', {
view_id: view.id,
field_id: resourceField.id,
});
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: resourceField.key,
index: 1,
}],
roles: [{
index: 1,
field_key: resourceField.key,
comparator: 'equals',
value: '100',
}],
});
const foundViewRole = await ViewRole.tenant().query().where('id', viewRole.id);
expect(foundViewRole.length).equals(0);
});
it('Should update the view roles that presented in the given data.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const view = await tenantFactory.create('view', { resource_id: resource.id });
const viewRole = await tenantFactory.create('view_role', {
view_id: view.id,
field_id: resourceField.id,
});
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: resourceField.key,
index: 1,
}],
roles: [{
id: viewRole.id,
index: 1,
field_key: resourceField.key,
comparator: 'equals',
value: '100',
}],
});
const foundViewRole = await ViewRole.tenant().query().where('id', viewRole.id);
expect(foundViewRole.length).equals(1);
expect(foundViewRole[0].id).equals(viewRole.id);
expect(foundViewRole[0].index).equals(1);
expect(foundViewRole[0].value).equals('100');
expect(foundViewRole[0].comparator).equals('equals');
});
it('Should response not found roles ids in case not exists in the storage.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const view = await tenantFactory.create('view', { resource_id: resource.id });
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: resourceField.key,
index: 1,
}],
roles: [{
id: 1,
index: 1,
field_key: resourceField.key,
comparator: 'equals',
value: '100',
}],
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'VIEW.ROLES.IDS.NOT.FOUND', code: 500, ids: [1],
});
});
it('Should delete columns from storage in case view columns ids not presented.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const view = await tenantFactory.create('view', { resource_id: resource.id });
const viewColumn = await tenantFactory.create('view_column', { view_id: view.id });
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: resourceField.key,
index: 1,
}],
roles: [{
index: 1,
field_key: resourceField.key,
comparator: 'equals',
value: '100',
}],
});
const foundViewColumns = await ViewColumn.tenant().query().where('id', viewColumn.id);
expect(foundViewColumns.length).equals(0);
});
it('Should insert columns to the storage if where new columns', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const view = await tenantFactory.create('view', { resource_id: resource.id });
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
key: resourceField.key,
index: 1,
}],
roles: [{
index: 1,
field_key: resourceField.key,
comparator: 'equals',
value: '100',
}],
});
const foundViewColumns = await ViewColumn.tenant().query().where('view_id', view.id);
expect(foundViewColumns.length).equals(1);
expect(foundViewColumns[0].viewId).equals(view.id);
expect(foundViewColumns[0].index).equals(1);
expect(foundViewColumns[0].fieldId).equals(resourceField.id);
});
it('Should update columns on the storage.', async () => {
const resource = await tenantFactory.create('resource');
const resourceField = await tenantFactory.create('resource_field', {
resource_id: resource.id,
label_name: 'Amount',
key: 'amount',
});
const view = await tenantFactory.create('view', { resource_id: resource.id });
const viewColumn = await tenantFactory.create('view_column', { view_id: view.id });
const res = await request()
.post(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send({
name: 'View Label',
logic_expression: '1',
columns: [{
id: viewColumn.id,
key: resourceField.key,
index: 10,
}],
roles: [{
index: 1,
field_key: resourceField.key,
comparator: 'equals',
value: '100',
}],
});
console.log(res.body)
const foundViewColumns = await ViewColumn.tenant().query().where('id', viewColumn.id);
expect(foundViewColumns.length).equals(1);
expect(foundViewColumns[0].id).equals(viewColumn.id);
expect(foundViewColumns[0].viewId).equals(view.id);
expect(foundViewColumns[0].index).equals(10);
// expect(foundViewColumns[0].fieldId).equals();
})
});
describe('DELETE: `/views/:resource_id`', () => {
it('Should not delete predefined view.', async () => {
const view = await tenantFactory.create('view', { predefined: true });
const res = await request()
.delete(`/api/views/${view.id}`)
.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: 'PREDEFINED_VIEW', code: 200,
});
});
it('Should response not found in case view was not exist.', async () => {
const res = await request()
.delete('/api/views/100')
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'VIEW_NOT_FOUND', code: 100,
});
});
it('Should delete the given view and associated view columns and roles.', async () => {
const view = await tenantFactory.create('view', { predefined: false });
await tenantFactory.create('view_role', { view_id: view.id });
await tenantFactory.create('view_column', { view_id: view.id });
const res = await request()
.delete(`/api/views/${view.id}`)
.set('x-access-token', loginRes.body.token)
.set('organization-id', tenantWebsite.organizationId)
.send();
expect(res.body.id).equals(view.id);
const foundViews = await View.tenant().query().where('id', view.id);
const foundViewRoles = await ViewRole.tenant().query().where('view_id', view.id);
expect(foundViews).to.have.lengthOf(0);
expect(foundViewRoles).to.have.lengthOf(0);
});
});
});