diff --git a/components/studentComboBox.vue b/components/studentComboBox.vue
index 09fa64a4..32de290d 100644
--- a/components/studentComboBox.vue
+++ b/components/studentComboBox.vue
@@ -54,12 +54,44 @@
@blur="onCrgBlur"
>
- Componentes Pendentes:
-
+
+ {{ !canEdit ? 'Verificar Pendências' : 'Editar Pendências' }}
+
+
+
+
@@ -242,12 +274,14 @@ export default {
ataCheck: false,
laudaCheck: false,
presCheck: false,
+ showPendencies: false,
ataDocument: {},
laudaDocument: {},
presDocument: {},
uploadFile: File,
crg: '',
- pendencies: '',
+ totalSubjects: [],
+ studentSubjects: [],
studentData: Object.assign({}, this.student),
isLoading: false,
defenseDate: new Date()
@@ -319,6 +353,12 @@ export default {
)
)
: null
+ this.$axios
+ .get(`/api/students/${this.student.id}/pendencies`)
+ .then(response => {
+ this.studentSubjects = response.data.map(pendency => pendency.subjectId)
+ })
+ .catch(e => this.openErrorNotification(e))
},
methods: {
@@ -375,6 +415,40 @@ export default {
toggleEdit() {
this.canEdit = !this.canEdit
},
+ getPendencies() {
+ this.$axios
+ .get(`/api/subjects`, {
+ params: {
+ paginate: 0
+ }
+ })
+ .then(response => {
+ this.totalSubjectsLength = response.headers['pagination-row-count']
+ this.totalSubjects = response.data
+ this.showPendencies = true
+ })
+ .catch(e => {
+ this.showPendencies = false
+ this.openErrorNotification(e)
+ })
+ },
+ updatePendencies() {
+ if (this.canEdit) {
+ this.$axios
+ .post(
+ `/api/students/${this.student.id}/pendencies/batch`,
+ this.studentSubjects
+ )
+ .then(response => {
+ this.$toast.open({
+ message: 'Pendências de aluno atualizadas com sucesso',
+ type: 'is-success'
+ })
+ })
+ .catch(e => this.openErrorNotification(e))
+ }
+ this.showPendencies = false
+ },
mapDocuments(documents) {
documents.forEach(element => {
if (element.type === 1) {
@@ -478,4 +552,13 @@ export default {
.icon {
margin-left: 1em;
}
+
+.scrollable {
+ overflow-y: scroll;
+}
+
+.bottom-sticky {
+ bottom: 0;
+ position: sticky;
+}
diff --git a/migrations/20190605024900_pendencies.js b/migrations/20190605024900_pendencies.js
new file mode 100644
index 00000000..3b635ba8
--- /dev/null
+++ b/migrations/20190605024900_pendencies.js
@@ -0,0 +1,26 @@
+exports.up = function(knex, Promise) {
+ return knex.schema.createTable('pendencies', table => {
+ table.increments('id').primary()
+ table
+ .integer('studentId')
+ .unsigned()
+ .notNullable()
+ table
+ .foreign('studentId')
+ .references('id')
+ .inTable('students')
+ table
+ .integer('subjectId')
+ .unsigned()
+ .notNullable()
+ table
+ .foreign('subjectId')
+ .references('id')
+ .inTable('subjects')
+ table.unique(['studentId', 'subjectId'])
+ })
+}
+
+exports.down = function(knex, Promise) {
+ return knex.schema.dropTable('pendencies')
+}
diff --git a/seeds/pendencies.js b/seeds/pendencies.js
new file mode 100644
index 00000000..49d44cef
--- /dev/null
+++ b/seeds/pendencies.js
@@ -0,0 +1,39 @@
+const Pendency = require('../server/models/Pendency')
+
+exports.seed = async function(knex, Promise) {
+ // Deletes ALL existing entries
+ await knex('pendencies').del()
+
+ const data = [
+ {
+ studentId: 1,
+ subjectId: 1
+ },
+ {
+ studentId: 1,
+ subjectId: 2
+ },
+ {
+ studentId: 1,
+ subjectId: 3
+ },
+ {
+ studentId: 2,
+ subjectId: 1
+ },
+ {
+ studentId: 3,
+ subjectId: 1
+ },
+ {
+ studentId: 3,
+ subjectId: 3
+ },
+ {
+ studentId: 5,
+ subjectId: 6
+ }
+ ]
+
+ return Promise.all(data.map(pendency => Pendency.forge(pendency).save()))
+}
diff --git a/server/controllers/pendencies/FromBatch.js b/server/controllers/pendencies/FromBatch.js
new file mode 100644
index 00000000..20184a40
--- /dev/null
+++ b/server/controllers/pendencies/FromBatch.js
@@ -0,0 +1,41 @@
+const errors = require('../../../shared/errors')
+const Students = require('../../models/Student')
+const Pendencies = require('../../models/Pendency')
+const { knex } = require('../../db')
+
+module.exports = async function pendenciesFromBatch(ctx) {
+ const { studentId } = ctx.params
+ const subjectsIdsReceived = ctx.request.body
+
+ if ((await Students.where('id', studentId).count()) === 0) {
+ ctx.status = 404
+ ctx.body = { code: errors.NOT_FOUND }
+ return
+ }
+
+ await knex.transaction(async trx => {
+ const pendenciesExisting = await Pendencies.where({ studentId }).fetchAll({
+ transacting: trx
+ })
+
+ const subjectsIdsExisting = pendenciesExisting.map(pend =>
+ pend.get('subjectId')
+ )
+
+ const addPendencies = subjectsIdsReceived.filter(
+ id => !subjectsIdsExisting.includes(id)
+ )
+ const delPendencies = pendenciesExisting.filter(
+ pendency => !subjectsIdsReceived.includes(pendency.get('subjectId'))
+ )
+ const addPromises = addPendencies.map(subjectId =>
+ new Pendencies({ subjectId, studentId }).save(null, { transacting: trx })
+ )
+ const delPromises = delPendencies.map(({ id }) =>
+ Pendencies.where({ id }).destroy({ transacting: trx })
+ )
+ return Promise.all(addPromises.concat(delPromises))
+ })
+
+ ctx.body = await Pendencies.where({ studentId }).fetchAll()
+}
diff --git a/server/controllers/pendencies/List.js b/server/controllers/pendencies/List.js
new file mode 100644
index 00000000..77527c19
--- /dev/null
+++ b/server/controllers/pendencies/List.js
@@ -0,0 +1,16 @@
+const errors = require('../../../shared/errors')
+
+const Students = require('../../models/Student')
+const Pendencies = require('../../models/Pendency')
+
+module.exports = async function listPendencies(ctx) {
+ const { studentId } = ctx.params
+
+ if ((await Students.where('id', studentId).count()) === 0) {
+ ctx.status = 404
+ ctx.body = { code: errors.NOT_FOUND }
+ return
+ }
+
+ ctx.body = await Pendencies.where({ studentId }).fetchAll()
+}
diff --git a/server/controllers/pendencies/Show.js b/server/controllers/pendencies/Show.js
new file mode 100644
index 00000000..2f48e645
--- /dev/null
+++ b/server/controllers/pendencies/Show.js
@@ -0,0 +1,24 @@
+const errors = require('../../../shared/errors')
+
+const Students = require('../../models/Student')
+const Pendencies = require('../../models/Pendency')
+
+module.exports = async function showPendency(ctx) {
+ const { studentId, id } = ctx.params
+
+ if ((await Students.where('id', studentId).count()) === 0) {
+ ctx.status = 404
+ ctx.body = { code: errors.NOT_FOUND }
+ return
+ }
+
+ const pendencyFind = await Pendencies.where({ id }).fetch()
+
+ if (pendencyFind === null) {
+ ctx.status = 404
+ ctx.body = { code: errors.NOT_FOUND }
+ return
+ }
+
+ ctx.body = pendencyFind
+}
diff --git a/server/controllers/pendencies/index.js b/server/controllers/pendencies/index.js
new file mode 100644
index 00000000..21b99a3c
--- /dev/null
+++ b/server/controllers/pendencies/index.js
@@ -0,0 +1,5 @@
+const Show = require('./Show')
+const List = require('./List')
+const FromBatch = require('./FromBatch')
+
+module.exports = { Show, List, FromBatch }
diff --git a/server/controllers/subjects/List.js b/server/controllers/subjects/List.js
index 2bd2b185..9f796f95 100644
--- a/server/controllers/subjects/List.js
+++ b/server/controllers/subjects/List.js
@@ -2,6 +2,10 @@ const Subject = require('../../models/Subject')
const utils = require('../../utils')
module.exports = async function listSubjects(ctx) {
- const { page = 1 } = ctx.request.query
+ const { page = 1, paginate } = ctx.request.query
+ if (paginate !== undefined && paginate === '0') {
+ ctx.body = await Subject.fetchAll()
+ return
+ }
utils.paginateContext(ctx, await Subject.fetchPage({ page }))
}
diff --git a/server/models/Pendency.js b/server/models/Pendency.js
new file mode 100644
index 00000000..3dda31e4
--- /dev/null
+++ b/server/models/Pendency.js
@@ -0,0 +1,7 @@
+const { bookshelf } = require('../db')
+
+const Pendency = bookshelf.model('Pendency', {
+ tableName: 'pendencies'
+})
+
+module.exports = Pendency
diff --git a/server/router.js b/server/router.js
index 9440af96..a3ce3f37 100644
--- a/server/router.js
+++ b/server/router.js
@@ -10,6 +10,7 @@ const students = require('./controllers/students')
const subjects = require('./controllers/subjects')
const auth = require('./controllers/auth')
const solicitations = require('./controllers/solicitations')
+const pendencies = require('./controllers/pendencies')
const router = new Router()
const api = new Router({ prefix: '/api' })
@@ -39,12 +40,23 @@ api.put(
students.UpdateAcademicHighlight
)
api.put('/students/:id', bodyJson, students.Update)
+
// Documents Routes
api.get('/students/:studentId/documents', documents.List)
api.get('/students/:studentId/documents/:id', documents.Show)
api.get('/students/:studentId/documents/:id/view', documents.View)
api.post('/students/:studentId/documents', bodyMultipart, documents.Upload)
api.post('/students/from-csv', bodyMultipart, students.FromCsv)
+
+// Pendencies Routes
+api.get('/students/:studentId/pendencies/:id', pendencies.Show)
+api.get('/students/:studentId/pendencies/', pendencies.List)
+api.post(
+ '/students/:studentId/pendencies/batch',
+ bodyJson,
+ pendencies.FromBatch
+)
+
// Subjects Routes
api.get('/subjects/', subjects.List)
api.get('/subjects/:id', subjects.Show)
diff --git a/test/server/controllers/pendencies.spec.js b/test/server/controllers/pendencies.spec.js
new file mode 100644
index 00000000..2f2a55ac
--- /dev/null
+++ b/test/server/controllers/pendencies.spec.js
@@ -0,0 +1,113 @@
+/**
+ * @jest-environment node
+ */
+
+const chai = require('chai')
+const chaiHttp = require('chai-http')
+chai.use(chaiHttp)
+
+const testUtils = require('../test-utils')
+const server = require('../../../server')
+const db = require('../../../server/db')
+const errors = require('../../../shared/errors')
+
+jest.useFakeTimers()
+describe('/api/students/:id/pendencies/', () => {
+ beforeEach(async () => {
+ await db.knex.migrate.rollback()
+ await db.knex.migrate.latest()
+ await db.knex.seed.run()
+ }, 100000)
+
+ test('GET /students/1/pendencies/', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .get('/api/students/1/pendencies/')
+ .set('Authorization', `Bearer ${token}`)
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(200)
+ expect(res.body.length).toEqual(3)
+ expect(res.body[0].studentId).toEqual(1)
+ expect(res.body[0].subjectId).toEqual(1)
+ expect(res.body[1].studentId).toEqual(1)
+ expect(res.body[1].subjectId).toEqual(2)
+ expect(res.body[2].studentId).toEqual(1)
+ expect(res.body[2].subjectId).toEqual(3)
+ done()
+ })
+
+ test('GET /students/1/pendencies/1', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .get('/api/students/1/pendencies/1')
+ .set('Authorization', `Bearer ${token}`)
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(200)
+ expect(res.body.subjectId).toEqual(1)
+ expect(res.body.studentId).toEqual(1)
+ done()
+ })
+
+ test('GET /students/1000/pendencies/1 NOT FOUND STUDENT', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .get('/api/students/1000/pendencies/1')
+ .set('Authorization', `Bearer ${token}`)
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(404)
+ expect(res.body.code).toEqual(errors.NOT_FOUND)
+ done()
+ })
+
+ test('GET /students/1/pendencies/1000 NOT FOUND PENDENCY', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .get('/api/students/1/pendencies/1000')
+ .set('Authorization', `Bearer ${token}`)
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(404)
+ expect(res.body.code).toEqual(errors.NOT_FOUND)
+ done()
+ })
+
+ test('GET /students/4/pendencies/ NO PENDENCIES', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .get('/api/students/4/pendencies/')
+ .set('Authorization', `Bearer ${token}`)
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(200)
+ expect(res.body.length).toEqual(0)
+ done()
+ })
+
+ test('POST /students/1/pendencies/batch PENDENCIES FROM BATCH', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .post('/api/students/1/pendencies/batch')
+ .set('Authorization', `Bearer ${token}`)
+ .send([2, 3, 4])
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(200)
+ expect(res.body.length).toEqual(3)
+ expect(res.body[0].studentId).toEqual(1)
+ expect(res.body[0].subjectId).toEqual(2)
+ expect(res.body[1].studentId).toEqual(1)
+ expect(res.body[1].subjectId).toEqual(3)
+ expect(res.body[2].studentId).toEqual(1)
+ expect(res.body[2].subjectId).toEqual(4)
+ done()
+ })
+})
diff --git a/test/server/controllers/subjects.spec.js b/test/server/controllers/subjects.spec.js
index a43a8439..2426d164 100644
--- a/test/server/controllers/subjects.spec.js
+++ b/test/server/controllers/subjects.spec.js
@@ -121,4 +121,21 @@ describe('/api/subjects', () => {
expect(res.status).toEqual(422)
done()
})
+
+ test('GET /subjects?paginate=0 ALL SUBJECTS', async done => {
+ const { token } = await testUtils.user('admin')
+ const res = await chai
+ .request(server.listen())
+ .get('/api/subjects?paginate=0')
+ .set('Authorization', `Bearer ${token}`)
+ expect(res.body).toBeDefined()
+ expect(res.type).toEqual('application/json')
+ expect(res.status).toEqual(200)
+ expect(res.body.length).toEqual(96)
+ expect(res.body[0].name).toEqual('ALGEBRA LINEAR PARA COMPUTACAO')
+ expect(res.body[95].name).toEqual(
+ '(ELETIVA) DISCIPLINA ELETIVA - SISTEMAS DE INFORMACAO'
+ )
+ done()
+ })
})
diff --git a/test/server/models/pendencies.spec.js b/test/server/models/pendencies.spec.js
new file mode 100644
index 00000000..6b22510f
--- /dev/null
+++ b/test/server/models/pendencies.spec.js
@@ -0,0 +1,36 @@
+/**
+ * @jest-environment node
+ */
+
+const chai = require('chai')
+const chaiHttp = require('chai-http')
+chai.use(chaiHttp)
+
+const db = require('../../../server/db')
+const Pendency = require('../../../server/models/Pendency')
+
+jest.useFakeTimers()
+
+describe('/api/students/:idStudent/pendencies', () => {
+ beforeEach(async done => {
+ await db.knex.migrate.rollback()
+ await db.knex.migrate.latest()
+ await db.knex.seed.run()
+ done()
+ }, 100000)
+
+ afterEach(() => {
+ return db.knex.migrate.rollback()
+ })
+
+ test('Create a Pendency', async done => {
+ const pendency = await Pendency.forge({
+ studentId: 1,
+ subjectId: 10
+ }).save()
+ expect(pendency.id).toBeDefined()
+ expect(pendency.attributes.studentId).toEqual(1)
+ expect(pendency.attributes.subjectId).toEqual(10)
+ done()
+ })
+})