mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: Currencies CRUD.
This commit is contained in:
@@ -243,6 +243,13 @@ factory.define('option', 'options', async () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
factory.define('currency', 'currencies', async () => {
|
||||||
|
return {
|
||||||
|
currency_name: faker.lorem.slug(),
|
||||||
|
currency_code: 'USD',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
factory.define('budget', 'budgets', async () => {
|
factory.define('budget', 'budgets', async () => {
|
||||||
return {
|
return {
|
||||||
name: faker.lorem.slug(),
|
name: faker.lorem.slug(),
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.createTable('currencies', table => {
|
||||||
|
table.increments();
|
||||||
|
table.string('currency_name');
|
||||||
|
table.string('currency_code', 4);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.dropTableIfExists('currencies');
|
||||||
|
};
|
||||||
@@ -1,45 +1,136 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { check, validationResult } from 'express-validator';
|
import { check, param, validationResult } from 'express-validator';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
|
import Currency from '@/models/Currency';
|
||||||
|
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
router() {
|
router() {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
router.use(jwtAuth);
|
||||||
|
|
||||||
router.get('/all',
|
router.get('/',
|
||||||
this.all.validation,
|
this.all.validation,
|
||||||
asyncMiddleware(this.all.handler));
|
asyncMiddleware(this.all.handler));
|
||||||
|
|
||||||
router.get('/registered',
|
router.post('/',
|
||||||
this.registered.validation,
|
this.newCurrency.validation,
|
||||||
asyncMiddleware(this.registered.handler));
|
asyncMiddleware(this.newCurrency.handler));
|
||||||
|
|
||||||
|
router.post('/:id',
|
||||||
|
this.editCurrency.validation,
|
||||||
|
asyncMiddleware(this.editCurrency.handler));
|
||||||
|
|
||||||
|
router.delete('/:currency_code',
|
||||||
|
this.deleteCurrecy.validation,
|
||||||
|
asyncMiddleware(this.deleteCurrecy.handler));
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all registered currency details.
|
||||||
|
*/
|
||||||
all: {
|
all: {
|
||||||
validation: [],
|
validation: [],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
|
const currencies = await Currency.query();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
currencies: [
|
currencies: [
|
||||||
{ currency_code: 'USD', currency_sign: '$' },
|
...currencies,
|
||||||
{ currency_code: 'LYD', currency_sign: '' },
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
registered: {
|
newCurrency: {
|
||||||
validation: [],
|
validation: [
|
||||||
|
check('currency_name').exists().trim().escape(),
|
||||||
|
check('currency_code').exists().trim().escape(),
|
||||||
|
],
|
||||||
async handler(req, res) {
|
async handler(req, res) {
|
||||||
|
const validationErrors = validationResult(req);
|
||||||
|
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
return res.boom.badData(null, {
|
||||||
|
code: 'validation_error', ...validationErrors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const form = { ...req.body };
|
||||||
|
|
||||||
|
const foundCurrency = await Currency.query()
|
||||||
|
.where('currency_code', form.currency_code);
|
||||||
|
|
||||||
|
if (foundCurrency.length > 0) {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [{ type: 'CURRENCY.CODE.ALREADY.EXISTS', code: 100 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await Currency.query()
|
||||||
|
.insert({ ...form });
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
currencies: [
|
currency: { ...form },
|
||||||
{ currency_code: 'USD', currency_sign: '$' },
|
});
|
||||||
{ currency_code: 'LYD', currency_sign: '' },
|
},
|
||||||
],
|
},
|
||||||
|
|
||||||
|
deleteCurrecy: {
|
||||||
|
validation: [
|
||||||
|
param('currency_code').exists().trim().escape(),
|
||||||
|
],
|
||||||
|
async handler(req, res) {
|
||||||
|
const validationErrors = validationResult(req);
|
||||||
|
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
return res.boom.badData(null, {
|
||||||
|
code: 'validation_error', ...validationErrors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { currency_code: currencyCode } = req.params;
|
||||||
|
|
||||||
|
await Currency.query()
|
||||||
|
.where('currency_code', currencyCode)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
return res.status(200).send({ currency_code: currencyCode });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
editCurrency: {
|
||||||
|
validation: [
|
||||||
|
param('id').exists().isNumeric().toInt(),
|
||||||
|
check('currency_name').exists().trim().escape(),
|
||||||
|
check('currency_code').exists().trim().escape(),
|
||||||
|
],
|
||||||
|
async handler(req, res) {
|
||||||
|
const validationErrors = validationResult(req);
|
||||||
|
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
return res.boom.badData(null, {
|
||||||
|
code: 'validation_error', ...validationErrors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const form = { ...req.body };
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const foundCurrency = await Currency.query()
|
||||||
|
.where('currency_code', form.currency_code)
|
||||||
|
.whereNot('id', id);
|
||||||
|
|
||||||
|
if (foundCurrency.length > 0) {
|
||||||
|
return res.status(400).send({
|
||||||
|
errors: [{ type: 'CURRENCY.CODE.ALREADY.EXISTS', code: 100 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await Currency.query()
|
||||||
|
.where('id', id)
|
||||||
|
.update({ ...form });
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
currency: { ...form },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
10
server/src/models/Currency.js
Normal file
10
server/src/models/Currency.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import BaseModel from '@/models/Model';
|
||||||
|
|
||||||
|
export default class Currency extends BaseModel {
|
||||||
|
/**
|
||||||
|
* Table name
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'currencies';
|
||||||
|
}
|
||||||
|
}
|
||||||
188
server/tests/routes/currencies.test.js
Normal file
188
server/tests/routes/currencies.test.js
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import {
|
||||||
|
request,
|
||||||
|
create,
|
||||||
|
expect,
|
||||||
|
login,
|
||||||
|
} from '~/testInit';
|
||||||
|
import Currency from '@/models/Currency';
|
||||||
|
|
||||||
|
let loginRes;
|
||||||
|
|
||||||
|
describe('route: /currencies/', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
loginRes = await login();
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
loginRes = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
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)
|
||||||
|
.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)
|
||||||
|
.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 () => {
|
||||||
|
create('currency', { currency_code: 'USD' });
|
||||||
|
|
||||||
|
const res = await request()
|
||||||
|
.post('/api/currencies')
|
||||||
|
.set('x-access-token', loginRes.body.token)
|
||||||
|
.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)
|
||||||
|
.send({
|
||||||
|
currency_code: 'USD',
|
||||||
|
currency_name: 'Dollar',
|
||||||
|
});
|
||||||
|
|
||||||
|
const foundCurrency = await Currency.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)
|
||||||
|
.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 create('currency');
|
||||||
|
const res = await request()
|
||||||
|
.delete(`/api/currencies/${currency.currencyCode}`)
|
||||||
|
.set('x-access-token', loginRes.body.token)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(res.status).equals(200);
|
||||||
|
|
||||||
|
const foundCurrency = await Currency.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 create('currency');
|
||||||
|
const res = await request()
|
||||||
|
.post(`/api/currencies/${currency.code}`)
|
||||||
|
.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: 'currency_name', location: 'body',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should `currency_code` be required.', async () => {
|
||||||
|
const currency = await create('currency');
|
||||||
|
const res = await request()
|
||||||
|
.post(`/api/currencies/${currency.code}`)
|
||||||
|
.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: 'currency_code', location: 'body',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should response currency code is duplicated.', async () => {
|
||||||
|
const currency1 = await create('currency');
|
||||||
|
const currency2 = await create('currency');
|
||||||
|
|
||||||
|
const res = await request()
|
||||||
|
.post(`/api/currencies/${currency2.id}`)
|
||||||
|
.set('x-access-token', loginRes.body.token)
|
||||||
|
.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 create('currency');
|
||||||
|
const currency2 = await create('currency');
|
||||||
|
|
||||||
|
const res = await request()
|
||||||
|
.post(`/api/currencies/${currency2.id}`)
|
||||||
|
.set('x-access-token', loginRes.body.token)
|
||||||
|
.send({
|
||||||
|
currency_code: 'ABC',
|
||||||
|
currency_name: 'Name',
|
||||||
|
});
|
||||||
|
|
||||||
|
const foundCurrency = await Currency.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.', () => {
|
||||||
|
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user