WIP Financial accounting module.

This commit is contained in:
Ahmed Bouhuolia
2020-01-25 23:34:08 +02:00
parent 488709088b
commit 77c67cc4cb
26 changed files with 1414 additions and 354 deletions

View File

@@ -0,0 +1,97 @@
import {
request,
expect,
create,
login,
} from '~/testInit';
import Expense from '@/models/Expense';
import ResourceCustomFieldRepository from '@/services/CustomFields/ResourceCustomFieldRepository';
import ResourceFieldMetadata from '@/models/ResourceFieldMetadata';
let loginRes;
describe('ResourceCustomFieldRepository', () => {
beforeEach(async () => {
loginRes = await login();
});
afterEach(() => {
loginRes = null;
});
describe('constructor()', () => {
it('Should take the resource name from model class name', () => {
const customFieldsRepo = new ResourceCustomFieldRepository(Expense);
expect(customFieldsRepo.resourceName).equals('Expense');
});
});
describe('fetchCustomFields', () => {
it('Should fetches all custom fields that associated with the resource.', async () => {
const resource = await create('resource', { name: 'Expense' });
const resourceField = await create('resource_field', { resource_id: resource.id });
const resourceField2 = await create('resource_field', { resource_id: resource.id });
const customFieldsRepo = new ResourceCustomFieldRepository(Expense);
await customFieldsRepo.fetchCustomFields();
expect(customFieldsRepo.customFields.length).equals(2);
});
});
describe.only('fetchCustomFieldsMetadata', () => {
it('Should fetches all custom fields metadata that associated to the resource and resource item.', async () => {
const resource = await create('resource', { name: 'Expense' });
const resourceField = await create('resource_field', { resource_id: resource.id });
const resourceField2 = await create('resource_field', { resource_id: resource.id });
const expense = await create('expense');
const fieldMetadata = await create('resource_custom_field_metadata', {
resource_id: resource.id, resource_item_id: expense.id,
});
const customFieldsRepo = new ResourceCustomFieldRepository(Expense);
await customFieldsRepo.fetchCustomFields();
await customFieldsRepo.fetchCustomFieldsMetadata(expense.id);
expect(customFieldsRepo.metadata[expense.id].metadata.length).equals(1);
});
});
describe('fillCustomFields', () => {
});
describe('saveCustomFields', () => {
it('Should save the given custom fields metadata to the resource item.', () => {
const resource = await create('resource');
const resourceField = await create('resource_field', { resource_field: resource.id });
const expense = await create('expense');
const fieldMetadata = await create('resource_custom_field_metadata', {
resource_id: resource.id, resource_item_id: expense.id,
});
const customFieldsRepo = new ResourceCustomFieldRepository(Expense);
await customFieldsRepo.load();
customFieldsRepo.fillCustomFields(expense.id, [
{ key: resourceField.slug, value: 'Hello World' },
]);
await customFieldsRepo.saveCustomFields();
const updateResourceFieldData = await ResourceFieldMetadata.query()
.where('resource_id', resource.id)
.where('resource_item_id', expense.id)
.first();
expect(updateResourceFieldData.value).equals('Hello World');
});
});
// describe('validateExistCustomFields', () => {
// });
});

View File

@@ -1,55 +1,152 @@
import { request, expect, create } from '~/testInit';
import { request, expect, create, login } from '~/testInit';
import ManualJournal from '../../src/models/ManualJournal';
import AccountTransaction from '@/models/AccountTransaction';
let loginRes;
describe('routes: `/accountOpeningBalance`', () => {
beforeEach(async () => {
loginRes = await login();
});
afterEach(() => {
loginRes = null;
});
describe('POST `/accountOpeningBalance`', () => {
it('Should `accounts` be array type.', async () => {
const res = await request().post('/api/accountOpeningBalance').send({
accounts: 1000,
});
const res = await request()
.post('/api/accounts_opening_balances')
.send({
accounts: 1000,
});
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
expect(res.body.code).equals('validation_error');
});
it('Should `accounts.*.id` be integer', async () => {
const res = await request().post('/api/accountOpeningBalance').send({
accounts: 1000,
});
const res = await request()
.post('/api/accounts_opening_balances')
.send({
accounts: [
{ id: 'id' },
]
});
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.that.deep.equals({
value: 'id',
msg: 'Invalid value',
param: 'accounts[0].id',
location: 'body',
});
});
it('Should `accounts.*.debit` be numeric.', async () => {
const res = await request().post('/api/accountOpeningBalance').send({
accounts: [{ id: 'id' }],
});
const res = await request()
.post('/api/accounts_opening_balances')
.send({
balance_adjustment_account: 10,
accounts: [{ id: 100, debit: 'id' }],
});
expect(res.status).equals(422);
});
it('Should `accounts.*.id` be exist in the storage.', async () => {
const res = await request().post('/api/accountOpeningBalance').send({
accounts: [
{ id: 100, credit: 100, debit: 100 },
],
});
const res = await request()
.post('/api/accounts_opening_balances')
.send({
balance_adjustment_account: 10,
accounts: [
{ id: 100, credit: 100 },
],
});
expect(res.status).equals(422);
expect(res.body.errors).include.something.that.deep.equals({
type: 'NOT_FOUND_ACCOUNT', code: 100, ids: [100],
});
});
it('Should store the given credit and debit to the account balance in the storage.', async () => {
const account = await create('account');
const res = await request().post('/api/accountOpeningBalance').send({
accounts: [
{ id: account.id, credit: 100, debit: 2 },
],
});
it('Should response bad request in case balance adjustment account was not exist.', async () => {
const debitAccount = await create('account');
const creditAccount = await create('account');
console.log(res.status);
const res = await request()
.post('/api/accounts_opening_balances')
.send({
balance_adjustment_account: 10,
accounts: [
{ id: debitAccount.id, credit: 100, debit: 2 },
{ id: creditAccount.id, credit: 0, debit: 100 },
]
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'BALANCE.ADJUSTMENT.ACCOUNT.NOT.EXIST', code: 300,
});
});
it('Should store the manual transaction to the storage.', async () => {
const debitAccount = await create('account');
const creditAccount = await create('account');
const balance = await create('account');
const res = await request()
.post('/api/accounts_opening_balances')
.set('x-access-token', loginRes.body.token)
.send({
balance_adjustment_account: balance.id,
accounts: [
{ id: debitAccount.id, credit: 100, debit: 2 },
{ id: creditAccount.id, credit: 0, debit: 100 },
]
});
const manualJournal = await ManualJournal.query().findById(res.body.id);
expect(manualJournal.amount).equals(100);
expect(manualJournal.transaction_type).equals('OpeningBalance');
});
it('Should store the jouranl entries of account balance transaction.', async () => {
const debitAccount = await create('account');
const creditAccount = await create('account');
const balance = await create('account');
const res = await request()
.post('/api/accounts_opening_balances')
.set('x-access-token', loginRes.body.token)
.send({
balance_adjustment_account: balance.id,
accounts: [
{ id: debitAccount.id, credit: 100, debit: 2 },
{ id: creditAccount.id, credit: 0, debit: 100 },
]
});
const transactions = await AccountTransaction.query()
.where('reference_type', 'OpeningBalance')
.where('reference_id', res.body.id);
expect(transactions.length).equals(2);
});
it('Should adjustment with balance adjustment account with bigger than zero.', async () => {
const debitAccount = await create('account');
const balance = await create('account');
const res = await request()
.post('/api/accounts_opening_balances')
.set('x-access-token', loginRes.body.token)
.send({
balance_adjustment_account: balance.id,
accounts: [
{ id: debitAccount.id, credit: 0, debit: 100 },
]
});
const transactions = await AccountTransaction.query()
.where('reference_type', 'OpeningBalance')
.where('reference_id', res.body.id);
expect(transactions.length).equals(2);
});
});
});

View File

@@ -247,7 +247,7 @@ describe('routes: `/budget`', () => {
});
});
describe.only('GET: `/budget`', () => {
describe('GET: `/budget`', () => {
it('Should retrieve all budgets with pagination metadata.', async () => {
const res = await request()
.get('/api/budget')

View File

@@ -14,7 +14,7 @@ describe('routes: `/budget_reports`', () => {
loginRes = null;
});
describe.only('GET: `/budget_verses_actual/:reportId`', () => {
describe('GET: `/budget_verses_actual/:reportId`', () => {
it('Should retrieve columns of budget year range with quarter period.', async () => {
const budget = await create('budget', { period: 'quarter' });
const budgetEntry = await create('budget_entry', { budget_id: budget.id });

View File

@@ -1,4 +1,9 @@
import { request, expect, create, login } from '~/testInit';
import {
request,
expect,
create,
login,
} from '~/testInit';
import AccountTransaction from '@/models/AccountTransaction';
import Expense from '@/models/Expense';
@@ -78,7 +83,7 @@ describe('routes: /expenses/', () => {
payment_account_id: 100,
amount: 100,
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'EXPENSE.ACCOUNT.NOT.FOUND', code: 200,
});
@@ -92,14 +97,14 @@ describe('routes: /expenses/', () => {
payment_account_id: 100,
amount: 100,
});
expect(res.body.errors).include.something.that.deep.equals({
type: 'PAYMENT.ACCOUNT.NOT.FOUND', code: 100,
});
});
it('Should response success with valid required data.', async () => {
const res = await request().post('/api/expenses')
const res = await request().post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.send({
expense_account_id: expenseAccount.id,
@@ -111,7 +116,8 @@ describe('routes: /expenses/', () => {
});
it('Should record journal entries of expense transaction.', async () => {
const res = await request().post('/api/expenses')
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.send({
expense_account_id: expenseAccount.id,
@@ -122,37 +128,63 @@ describe('routes: /expenses/', () => {
const expensesEntries = await AccountTransaction.query()
.where('reference_type', 'Expense')
.where('reference_id', res.body.id);
expect(expensesEntries.length).equals(2);
});
it('Should save expense transaction to the storage.', () => {
it('Should save expense transaction to the storage.', async () => {
const res = await request()
.post('/api/expenses')
.set('x-access-token', loginRes.body.token)
.send({
expense_account_id: expenseAccount.id,
payment_account_id: cashAccount.id,
amount: 100,
});
const expenseTransaction = await Expense.query().where('id', res.body.id);
expect(expenseTransaction.amount).equals(100);
});
it('Should response bad request in case custom field slug was not exists in the storage.', () => {
});
it('Should save expense custom fields to the storage.', () => {
});
});
describe('POST: `/expenses/:id`', () => {
it('Should response unauthorized in case user was not authorized.', () => {
});
it('Should response not found in case expense was not exist.', () => {
});
it('Should update the expense transaction.', () => {
});
});
describe('DELETE: `/expenses/:id`', () => {
it('Should response not found in case expense not found.', async () => {
const res = await request()
.delete('/api/expense/1000')
.delete('/api/expenses/1000')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.body.reasons).include.something.that.deep.equals({
type: 'EXPENSE.NOT.FOUND', code: 100,
expect(res.body.errors).include.something.that.deep.equals({
type: 'EXPENSE.TRANSACTION.NOT.FOUND', code: 100,
});
});
it('Should response success in case expense transaction was exist.', async () => {
const expense = await create('expense');
const res = await request()
.delete(`/api/expense/${expense.id}`)
.delete(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.send();
@@ -162,7 +194,7 @@ describe('routes: /expenses/', () => {
it('Should delete the expense transaction from the storage.', async () => {
const expense = await create('expense');
await request()
.delete(`/api/expense/${expense.id}`)
.delete(`/api/expenses/${expense.id}`)
.set('x-access-token', loginRes.body.token)
.send();
@@ -187,6 +219,10 @@ describe('routes: /expenses/', () => {
it('Should reverse accounts balance that associated to expense transaction.', () => {
});
it('Should delete the custom fields that associated to resource and resource item.', () => {
});
});
describe('POST: `/expenses/bulk`', () => {
@@ -296,6 +332,75 @@ describe('routes: /expenses/', () => {
});
});
describe('POST: `/expenses/:id/publish`', () => {
it('Should response not found in case the expense id was not exist.', async () => {
const expense = await create('expense', { published: false });
const res = await request()
.post('/api/expenses/100/publish')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'EXPENSE.NOT.FOUND', code: 100,
});
});
it('Should response bad request in case expense is already published.', async () => {
const expense = await create('expense', { published: true });
const res = await request()
.post(`/api/expenses/${expense.id}/publish`)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(400);
expect(res.body.errors).include.something.that.deep.equals({
type: 'EXPENSE.ALREADY.PUBLISHED', code: 200,
});
});
it('Should publish the expense transaction.', async () => {
const expense = await create('expense', { published: false });
const res = await request()
.post(`/api/expenses/${expense.id}/publish`)
.set('x-access-token', loginRes.body.token)
.send();
const storedExpense = await Expense.query().findById(expense.id);
expect(storedExpense.published).equals(1);
});
it('Should publish the journal entries that associated to the given expense transaction.', async () => {
const expense = await create('expense', { published: false });
const transaction = await create('account_transaction', {
reference_id: expense.id,
reference_type: 'Expense',
});
const res = await request()
.post(`/api/expenses/${expense.id}/publish`)
.set('x-access-token', loginRes.body.token)
.send();
const entries = await AccountTransaction.query()
.where('reference_id', expense.id)
.where('reference_type', 'Expense');
entries.forEach((entry) => {
expect(entry.draft).equals(0);
});
});
it('Should response success in case expense was exist and not published.', async () => {
const expense = await create('expense', { published: false });
const res = await request()
.post(`/api/expenses/${expense.id}/publish`)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(200);
});
});
describe('GET: `/expenses/:id`', () => {
it('Should response view not found in case the custom view id was not exist.', async () => {
const res = await request()
@@ -305,5 +410,9 @@ describe('routes: /expenses/', () => {
console.log(res.status);
});
it('Should retrieve custom fields metadata.', () => {
});
});
});
});

View File

@@ -1,42 +1,93 @@
import { create, expect, request } from '~/testInit';
import {
create,
expect,
request,
login,
} from '~/testInit';
import knex from '@/database/knex';
import ResourceField from '@/models/ResourceField';
import e from 'express';
import Fields from '../../src/http/controllers/Fields';
let loginRes;
describe('route: `/fields`', () => {
beforeEach(async () => {
loginRes = await login();
});
afterEach(() => {
loginRes = null;
});
describe('POST: `/fields/:resource_id`', () => {
it('Should `label` be required.', async () => {
const resource = await create('resource');
const res = await request().post(`/api/fields/resource/${resource.resource_id}`).send();
it('Should response unauthorized in case the user was not authorized.', async () => {
const res = await request()
.post('/api/fields/resource/items')
.send();
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
expect(res.body.code).equals('validation_error');
});
const paramsErrors = res.body.errors.map((er) => er.param);
expect(paramsErrors).to.include('label');
it('Should response bad request in case resource name was not exist.', async () => {
const res = await request()
.post('/api/fields/resource/not_found_resource')
.set('x-access-token', loginRes.body.token)
.send({
label: 'Extra Field',
data_type: 'text',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.deep.equals({
type: 'RESOURCE_NOT_FOUND', code: 100,
});
});
it('Should `label` be required.', async () => {
const resource = await create('resource');
const res = await request()
.post(`/api/fields/resource/${resource.resource_name}`)
.set('x-access-token', loginRes.body.token)
.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: 'label', location: 'body',
});
});
it('Should `data_type` be required.', async () => {
const resource = await create('resource');
const res = await request().post(`/api/fields/resource/${resource.resource_id}`);
const res = await request()
.post(`/api/fields/resource/${resource.resource_id}`)
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
});
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
const paramsErrors = res.body.errors.map((er) => er.param);
expect(paramsErrors).to.include('data_type');
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'data_type', location: 'body',
});
});
it('Should `data_type` be one in the list.', async () => {
const resource = await create('resource');
const res = await request().post(`/api/fields/resource/${resource.resource_id}`).send({
label: 'Field label',
data_type: 'invalid_type',
});
const res = await request()
.post(`/api/fields/resource/${resource.resource_id}`)
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'invalid_type',
});
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
const paramsErrors = res.body.errors.map((er) => er.param);
expect(paramsErrors).to.include('data_type');
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'data_type', location: 'body', value: 'invalid_type',
});
});
it('Should `value` be boolean valid value in case `data_type` was `boolean`.', () => {
@@ -63,13 +114,16 @@ describe('route: `/fields`', () => {
});
it('Should response not found in case resource id was not exist.', async () => {
const res = await request().post('/api/fields/resource/100').send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
it('Should response not found in case resource name was not exist.', async () => {
const res = await request()
.post('/api/fields/resource/resource_not_found')
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
@@ -77,63 +131,81 @@ describe('route: `/fields`', () => {
});
});
it('Should response success with valid data.', async () => {
const resource = await create('resource');
const res = await request().post(`/api/fields/resource/${resource.id}`).send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
const res = await request()
.post(`/api/fields/resource/${resource.name}`)
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
expect(res.status).equals(200);
});
it('Should store the given field details to the storage.', async () => {
const resource = await create('resource');
await request().post(`/api/fields/resource/${resource.id}`).send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
options: ['option 1', 'option 2'],
});
const res = await request()
.post(`/api/fields/resource/${resource.name}`)
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
options: ['option 1', 'option 2'],
});
const foundField = await knex('resource_fields').first();
const foundField = await ResourceField.query().findById(res.body.id);
expect(foundField.label_name).equals('Field label');
expect(foundField.data_type).equals('text');
expect(foundField.labelName).equals('Field label');
expect(foundField.dataType).equals('text');
expect(foundField.default).equals('default value');
expect(foundField.help_text).equals('help text');
expect(foundField.options).equals.deep([
{ key: 1, value: 'option 1' },
{ key: 2, value: 'option 2' },
]);
expect(foundField.helpText).equals('help text');
expect(foundField.options.length).equals(2);
});
});
describe('POST: `/fields/:field_id`', () => {
it('Should `label` be required.', async () => {
it('Should response unauthorized in case the user was not authorized.', async () => {
const field = await create('resource_field');
const res = await request().post(`/api/fields/${field.id}`).send();
const res = await request()
.post(`/api/fields/${field.id}`)
.send();
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
expect(res.body.code).equals('validation_error');
});
const paramsErrors = res.body.errors.map((er) => er.param);
expect(paramsErrors).to.include('label');
it('Should `label` be required.', async () => {
const field = await create('resource_field');
const res = await request()
.post(`/api/fields/${field.id}`)
.set('x-access-token', loginRes.body.token)
.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: 'label', location: 'body',
})
});
it('Should `data_type` be required.', async () => {
const field = await create('resource_field');
const res = await request().post(`/api/fields/${field.id}`);
const res = await request()
.post(`/api/fields/${field.id}`)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
const paramsErrors = res.body.errors.map((er) => er.param);
expect(paramsErrors).to.include('data_type');
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'data_type', location: 'body',
});
});
it('Should `data_type` be one in the list.', async () => {
@@ -144,28 +216,78 @@ describe('route: `/fields`', () => {
});
expect(res.status).equals(422);
expect(res.body.code).equals('VALIDATION_ERROR');
const paramsErrors = res.body.errors.map((er) => er.param);
expect(paramsErrors).to.include('data_type');
expect(res.body.code).equals('validation_error');
expect(res.body.errors).include.something.deep.equals({
value: 'invalid_type',
msg: 'Invalid value',
param: 'data_type',
location: 'body',
});
});
it('Should response not found in case resource id was not exist.', async () => {
const res = await request().post('/api/fields/100').send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
it('Should response not found in case resource field id was not exist.', async () => {
const res = await request()
.post('/api/fields/100')
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
expect(res.status).equals(404);
expect(res.body.errors).include.something.that.deep.equals({
type: 'FIELD_NOT_FOUND', code: 100,
});
});
it('Should update details of the given resource field.', async () => {
const field = await create('resource_field');
const res = await request()
.post(`/api/fields/${field.id}`)
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
});
it('Should save the new options of the field in the storage.', async () => {
const updateField = await ResourceField.query().findById(res.body.id);
expect(updateField.labelName).equals('Field label');
expect(updateField.dataType).equals('text');
expect(updateField.default).equals('default value');
expect(updateField.helpText).equals('help text');
});
it('Should save the new options of the field with exist ones in the storage.', async () => {
const field = await create('resource_field', {
options: JSON.stringify([{ key: 1, value: 'Option 1' }]),
});
const res = await request()
.post(`/api/fields/${field.id}`)
.set('x-access-token', loginRes.body.token)
.send({
label: 'Field label',
data_type: 'text',
default: 'default value',
help_text: 'help text',
options: [
{ key: 1, value: 'Value Key 1' },
{ key: 2, value: 'Value Key 2' },
],
});
const updateField = await ResourceField.query().findById(res.body.id);
expect(updateField.options.length).equals(2);
expect(updateField.options[0].key).equals(1);
expect(updateField.options[1].key).equals(2);
expect(updateField.options[0].value).equals('Value Key 1');
expect(updateField.options[1].value).equals('Value Key 2');
});
});
@@ -178,10 +300,12 @@ describe('route: `/fields`', () => {
it('Should change status activation of the given field.', async () => {
const field = await create('resource_field');
await request().post(`/api/fields/status/${field.id}`).send({
active: false,
});
const res = await request()
.post(`/api/fields/status/${field.id}`)
.set('x-access-token', loginRes.body.token)
.send({
active: false,
});
const storedField = await knex('resource_fields').where('id', field.id).first();
expect(storedField.active).equals(0);
});

View File

@@ -5,11 +5,23 @@ import {
login,
} from '~/testInit';
import knex from '@/database/knex';
import Item from '@/models/Item';
let loginRes;
describe('routes: `/items`', () => {
beforeEach(async () => {
loginRes = await login();
});
afterEach(() => {
loginRes = null;
});
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();
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(401);
expect(res.body.message).equals('unauthorized');
@@ -24,71 +36,101 @@ describe('routes: `/items`', () => {
});
it('Should `name` be required.', async () => {
const res = await request().post('/api/items').send();
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const foundNameParam = res.body.errors.find((error) => error.param === 'name');
expect(!!foundNameParam).equals(true);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'name', location: 'body',
});
});
it('Should `type_id` be required.', async () => {
const res = await request().post('/api/items').send();
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const foundTypeParam = res.body.errors.find((error) => error.param === 'type_id');
expect(!!foundTypeParam).equals(true);
expect(res.body.errors).include.something.deep.equals({
msg: 'Invalid value', param: 'type_id', location: 'body',
});
});
it('Should `buy_price` be numeric.', async () => {
const res = await request().post('/api/items').send({
buy_price: 'not_numeric',
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
buy_price: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const foundBuyPrice = res.body.errors.find((error) => error.param === 'buy_price');
expect(!!foundBuyPrice).equals(true);
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'buy_price',
location: 'body',
});
});
it('Should `cost_price` be numeric.', async () => {
const res = await request().post('/api/items').send({
cost_price: 'not_numeric',
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
cost_price: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const foundCostParam = res.body.errors.find((error) => error.param === 'cost_price');
expect(!!foundCostParam).equals(true);
expect(res.body.errors).include.something.deep.equals({
value: 'not_numeric',
msg: 'Invalid value',
param: 'cost_price',
location: 'body',
});
});
it('Should `buy_account_id` be integer.', async () => {
const res = await request().post('/api/items').send({
buy_account_id: 'not_numeric',
});
it('Should `sell_account_id` be integer.', async () => {
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
sell_account_id: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const foundAccount = res.body.errors.find((error) => error.param === 'buy_account_id');
expect(!!foundAccount).equals(true);
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 res = await request().post('/api/items').send({
cost_account_id: 'not_numeric',
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
cost_account_id: 'not_numeric',
});
expect(res.status).equals(422);
expect(res.body.code).equals('validation_error');
const foundAccount = res.body.errors.find((error) => error.param === 'cost_account_id');
expect(!!foundAccount).equals(true);
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 required if `cost_price` was presented.', async () => {
@@ -100,14 +142,17 @@ describe('routes: `/items`', () => {
});
it('Should response bad request in case cost account was not exist.', async () => {
const res = await request().post('/api/items').send({
name: 'Item Name',
type_id: 1,
buy_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
name: 'Item Name',
type_id: 1,
buy_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({
@@ -116,14 +161,17 @@ describe('routes: `/items`', () => {
});
it('Should response bad request in case sell account was not exist.', async () => {
const res = await request().post('/api/items').send({
name: 'Item Name',
type_id: 1,
buy_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
name: 'Item Name',
type_id: 1,
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({
@@ -132,15 +180,18 @@ describe('routes: `/items`', () => {
});
it('Should response not category found in case item category was not exist.', async () => {
const res = await request().post('/api/items').send({
name: 'Item Name',
type_id: 1,
buy_price: 10.2,
cost_price: 20.2,
sell_account_id: 10,
cost_account_id: 20,
category_id: 20,
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
name: 'Item Name',
type_id: 1,
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({
@@ -153,40 +204,248 @@ describe('routes: `/items`', () => {
const anotherAccount = await create('account');
const itemCategory = await create('item_category');
const res = await request().post('/api/items').send({
name: 'Item Name',
type_id: 1,
buy_price: 10.2,
cost_price: 20.2,
sell_account_id: account.id,
cost_account_id: anotherAccount.id,
category_id: itemCategory.id,
});
const res = await request()
.post('/api/items')
.set('x-access-token', loginRes.body.token)
.send({
name: 'Item Name',
type_id: 1,
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);
});
});
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)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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 create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.send({
name: 'Item Name',
type: 'product',
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 update details of the given item.', async () => {
const account = await create('account');
const anotherAccount = await create('account');
const itemCategory = await create('item_category');
const item = await create('item');
const res = await request()
.post(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.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.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').send();
const res = await request()
.delete('/api/items/10')
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(404);
});
it('Should response success in case was exist.', async () => {
const item = await create('item');
const res = await request().delete(`/api/items/${item.id}`);
const res = await request()
.delete(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.send();
expect(res.status).equals(200);
});
it('Should delete the given item from the storage.', async () => {
const item = await create('item');
await request().delete(`/api/items/${item.id}`);
await request()
.delete(`/api/items/${item.id}`)
.set('x-access-token', loginRes.body.token)
.send();
const storedItem = await knex('items').where('id', item.id);
expect(storedItem).to.have.lengthOf(0);
const storedItems = await Item.query().where('id', item.id);
expect(storedItems).to.have.lengthOf(0);
});
});
});