diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js index e077eea30..27e5169c9 100644 --- a/back/methods/dms/uploadFile.js +++ b/back/methods/dms/uploadFile.js @@ -63,7 +63,7 @@ module.exports = Self => { } try { - const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId); + const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId, myOptions); if (!hasWriteRole) throw new UserError(`You don't have enough privileges`); @@ -83,7 +83,7 @@ module.exports = Self => { const originPath = `${tempContainer.client.root}/${tempContainer.name}/${file.name}`; const destinationPath = `${container.client.root}/${pathHash}/${newDms.file}`; - fs.rename(originPath, destinationPath); + await fs.rename(originPath, destinationPath); addedDms.push(newDms); } diff --git a/back/models/account.js b/back/models/account.js index 00c583d31..92ec589c5 100644 --- a/back/models/account.js +++ b/back/models/account.js @@ -60,8 +60,8 @@ module.exports = Self => { * @param {String} name The role name * @return {Boolean} %true if user has the role, %false otherwise */ - Self.hasRole = async function(userId, name) { - let roles = await Self.getRoles(userId); + Self.hasRole = async function(userId, name, options) { + let roles = await Self.getRoles(userId, options); return roles.some(role => role == name); }; @@ -71,13 +71,13 @@ module.exports = Self => { * @param {Integer} userId The user id * @return {Object} User role list */ - Self.getRoles = async userId => { + Self.getRoles = async(userId, options) => { let result = await Self.rawSql( `SELECT r.name FROM account.user u JOIN account.roleRole rr ON rr.role = u.role JOIN account.role r ON r.id = rr.inheritsFrom - WHERE u.id = ?`, [userId]); + WHERE u.id = ?`, [userId], options); let roles = []; for (role of result) diff --git a/back/models/dmsType.js b/back/models/dmsType.js index f76b095df..267c905e9 100644 --- a/back/models/dmsType.js +++ b/back/models/dmsType.js @@ -5,17 +5,18 @@ module.exports = Self => { * * @param {Object} ctx - Request context * @param {Interger} id - DmsType id + * @param {Object} options - Query options * @return {Boolean} True for user with read privileges */ - Self.hasReadRole = async(ctx, id) => { + Self.hasReadRole = async(ctx, id, options) => { const models = Self.app.models; const dmsType = await models.DmsType.findById(id, { include: { relation: 'readRole' } - }); + }, options); - return await hasRole(ctx, dmsType); + return await hasRole(ctx, dmsType, options); }; /** @@ -24,17 +25,18 @@ module.exports = Self => { * * @param {Object} ctx - Request context * @param {Interger} id - DmsType id + * @param {Object} options - Query options * @return {Boolean} True for user with write privileges */ - Self.hasWriteRole = async(ctx, id) => { + Self.hasWriteRole = async(ctx, id, options) => { const models = Self.app.models; const dmsType = await models.DmsType.findById(id, { include: { relation: 'writeRole' } - }); + }, options); - return await hasRole(ctx, dmsType); + return await hasRole(ctx, dmsType, options); }; /** @@ -42,8 +44,9 @@ module.exports = Self => { * read or write privileges * @param {Object} ctx - Context * @param {Object} dmsType - Dms type [read/write] + * @param {Object} options - Query options */ - async function hasRole(ctx, dmsType) { + async function hasRole(ctx, dmsType, options) { const models = Self.app.models; const myUserId = ctx.req.accessToken.userId; @@ -51,8 +54,8 @@ module.exports = Self => { const writeRole = dmsType.writeRole() && dmsType.writeRole().name; const requiredRole = readRole || writeRole; - const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole); - const isRoot = await models.Account.hasRole(myUserId, 'root'); + const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole, options); + const isRoot = await models.Account.hasRole(myUserId, 'root', options); if (isRoot || hasRequiredRole) return true; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 86ee0a612..a437a4727 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1869,7 +1869,8 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa VALUES (1, 14, '1.txt', 'text/plain', 5, 1, 442, NULL, FALSE, 'Ticket:11', 'Ticket:11 dms for the ticket', CURDATE()), (2, 5, '2.txt', 'text/plain', 5, 1, 442, 1, TRUE, 'Client:104', 'Client:104 dms for the client', CURDATE()), - (3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()); + (3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()), + (4, 3, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', CURDATE()); INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`) VALUES @@ -1880,6 +1881,10 @@ INSERT INTO `vn`.`clientDms`(`clientFk`, `dmsFk`) (104, 2), (104, 3); +INSERT INTO `vn`.`workerDocument`(`id`, `worker`, `document`) + VALUES + (1, 106, 4); + INSERT INTO `vn`.`device` (`sn`, `model`, `userFk`) VALUES ('aaa', 'android', '9'); diff --git a/modules/claim/back/methods/claim-dms/removeFile.js b/modules/claim/back/methods/claim-dms/removeFile.js index 8de764db0..ac546455a 100644 --- a/modules/claim/back/methods/claim-dms/removeFile.js +++ b/modules/claim/back/methods/claim-dms/removeFile.js @@ -1,6 +1,6 @@ module.exports = Self => { Self.remoteMethodCtx('removeFile', { - description: 'Removes a ticket document', + description: 'Removes a claim document', accessType: 'WRITE', accepts: { arg: 'id', diff --git a/modules/client/back/methods/client-dms/removeFile.js b/modules/client/back/methods/client-dms/removeFile.js index 6cf7ccc41..5ff123630 100644 --- a/modules/client/back/methods/client-dms/removeFile.js +++ b/modules/client/back/methods/client-dms/removeFile.js @@ -20,7 +20,7 @@ module.exports = Self => { Self.removeFile = async(ctx, id) => { const models = Self.app.models; - const clientDms = await models.ClientDms.findById(id); + const clientDms = await Self.findById(id); await models.Dms.removeFile(ctx, clientDms.dmsFk); diff --git a/modules/worker/back/methods/worker-dms/allowedContentTypes.js b/modules/worker/back/methods/worker-dms/allowedContentTypes.js new file mode 100644 index 000000000..2f5183f92 --- /dev/null +++ b/modules/worker/back/methods/worker-dms/allowedContentTypes.js @@ -0,0 +1,23 @@ +module.exports = Self => { + Self.remoteMethodCtx('allowedContentTypes', { + description: 'Returns a list of allowed contentTypes', + accessType: 'READ', + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/allowedContentTypes`, + verb: 'GET' + } + }); + + Self.allowedContentTypes = async() => { + const storageConnector = Self.app.dataSources.storage.connector; + const allowedContentTypes = storageConnector.allowedContentTypes; + const modelAllowedContentTypes = Self.definition.settings.allowedContentTypes; + + return modelAllowedContentTypes || allowedContentTypes; + }; +}; + diff --git a/modules/worker/back/methods/worker-dms/removeFile.js b/modules/worker/back/methods/worker-dms/removeFile.js new file mode 100644 index 000000000..d0116c3c2 --- /dev/null +++ b/modules/worker/back/methods/worker-dms/removeFile.js @@ -0,0 +1,30 @@ +module.exports = Self => { + Self.remoteMethodCtx('removeFile', { + description: 'Removes a worker document', + accessType: 'WRITE', + accepts: { + arg: 'id', + type: 'Number', + description: 'The document id', + http: {source: 'path'} + }, + returns: { + type: 'Object', + root: true + }, + http: { + path: `/:id/removeFile`, + verb: 'POST' + } + }); + + Self.removeFile = async(ctx, id) => { + const models = Self.app.models; + const workerDms = await Self.findById(id); + + await models.Dms.removeFile(ctx, workerDms.dmsFk); + + return workerDms.destroy(); + }; +}; + diff --git a/modules/worker/back/methods/worker-dms/specs/removeFile.spec.js b/modules/worker/back/methods/worker-dms/specs/removeFile.spec.js new file mode 100644 index 000000000..7039d4f3e --- /dev/null +++ b/modules/worker/back/methods/worker-dms/specs/removeFile.spec.js @@ -0,0 +1,18 @@ +const app = require('vn-loopback/server/server'); + +describe('WorkerDms removeFile()', () => { + const workerDmsFk = 1; + it(`should return an error for a user without enough privileges`, async() => { + let clientId = 101; + let ctx = {req: {accessToken: {userId: clientId}}}; + + let error; + await app.models.WorkerDms.removeFile(ctx, workerDmsFk).catch(e => { + error = e; + }).finally(() => { + expect(error.message).toEqual(`You don't have enough privileges`); + }); + + expect(error).toBeDefined(); + }); +}); diff --git a/modules/worker/back/methods/worker/specs/uploadFile.spec.js b/modules/worker/back/methods/worker/specs/uploadFile.spec.js new file mode 100644 index 000000000..26a50252d --- /dev/null +++ b/modules/worker/back/methods/worker/specs/uploadFile.spec.js @@ -0,0 +1,19 @@ +const app = require('vn-loopback/server/server'); + +describe('Worker uploadFile()', () => { + it(`should return an error for a user without enough privileges`, async() => { + let workerId = 106; + let currentUserId = 102; + let hhrrDataId = 3; + let ctx = {req: {accessToken: {userId: currentUserId}}, args: {dmsTypeId: hhrrDataId}}; + + let error; + await app.models.Worker.uploadFile(ctx, workerId).catch(e => { + error = e; + }).finally(() => { + expect(error.message).toEqual(`You don't have enough privileges`); + }); + + expect(error).toBeDefined(); + }); +}); diff --git a/modules/worker/back/methods/worker/uploadFile.js b/modules/worker/back/methods/worker/uploadFile.js new file mode 100644 index 000000000..588cfe4bd --- /dev/null +++ b/modules/worker/back/methods/worker/uploadFile.js @@ -0,0 +1,76 @@ +module.exports = Self => { + Self.remoteMethodCtx('uploadFile', { + description: 'Upload and attach a file to a client', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'Number', + description: 'The worker id', + http: {source: 'path'} + }, { + arg: 'warehouseId', + type: 'Number', + description: 'The warehouse id', + required: true + }, { + arg: 'companyId', + type: 'Number', + description: 'The company id', + required: true + }, { + arg: 'dmsTypeId', + type: 'Number', + description: 'The dms type id', + required: true + }, { + arg: 'reference', + type: 'String', + required: true + }, { + arg: 'description', + type: 'String', + required: true + }, { + arg: 'hasFile', + type: 'Boolean', + description: 'True if has an attached file', + required: true + }], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/:id/uploadFile`, + verb: 'POST' + } + }); + + Self.uploadFile = async(ctx, id) => { + const models = Self.app.models; + const promises = []; + const tx = await Self.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const uploadedFiles = await models.Dms.uploadFile(ctx, options); + uploadedFiles.forEach(dms => { + const newWorkerDms = models.WorkerDms.create({ + workerFk: id, + dmsFk: dms.id + }, options); + + promises.push(newWorkerDms); + }); + const resolvedPromises = await Promise.all(promises); + + await tx.commit(); + + return resolvedPromises; + } catch (err) { + await tx.rollback(); + throw err; + } + }; +}; diff --git a/modules/worker/back/model-config.json b/modules/worker/back/model-config.json index 35a039d3c..4948231b9 100644 --- a/modules/worker/back/model-config.json +++ b/modules/worker/back/model-config.json @@ -20,6 +20,9 @@ "WorkCenterHoliday": { "dataSource": "vn" }, + "WorkerDms": { + "dataSource": "vn" + }, "Worker": { "dataSource": "vn" }, diff --git a/modules/worker/back/models/worker-dms.js b/modules/worker/back/models/worker-dms.js new file mode 100644 index 000000000..4504b4ed4 --- /dev/null +++ b/modules/worker/back/models/worker-dms.js @@ -0,0 +1,4 @@ +module.exports = Self => { + require('../methods/worker-dms/removeFile')(Self); + require('../methods/worker-dms/allowedContentTypes')(Self); +}; diff --git a/modules/worker/back/models/worker-dms.json b/modules/worker/back/models/worker-dms.json new file mode 100644 index 000000000..56cad65a6 --- /dev/null +++ b/modules/worker/back/models/worker-dms.json @@ -0,0 +1,47 @@ +{ + "name": "WorkerDms", + "base": "Loggable", + "log": { + "model":"ClientLog", + "relation": "worker", + "showField": "dmsFk" + }, + "options": { + "mysql": { + "table": "workerDocument" + } + }, + "properties": { + "id": { + "type": "Number", + "id": true + }, + "dmsFk": { + "type": "Number", + "required": true, + "mysql": { + "columnName": "document" + } + }, + "workerFk": { + "type": "Number", + "required": true, + "mysql": { + "columnName": "worker" + } + } + }, + "relations": { + "worker": { + "type": "belongsTo", + "model": "Worker", + "foreignKey": "workerFk" + }, + "dms": { + "type": "belongsTo", + "model": "Dms", + "foreignKey": "dmsFk" + } + } +} + diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 5abf65513..692c8c735 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -3,4 +3,5 @@ module.exports = Self => { require('../methods/worker/mySubordinates')(Self); require('../methods/worker/isSubordinate')(Self); require('../methods/worker/getWorkedHours')(Self); + require('../methods/worker/uploadFile')(Self); }; diff --git a/modules/worker/front/dms/create/index.html b/modules/worker/front/dms/create/index.html new file mode 100644 index 000000000..dcafa5986 --- /dev/null +++ b/modules/worker/front/dms/create/index.html @@ -0,0 +1,83 @@ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/modules/worker/front/dms/create/index.js b/modules/worker/front/dms/create/index.js new file mode 100644 index 000000000..e7bfe7bfd --- /dev/null +++ b/modules/worker/front/dms/create/index.js @@ -0,0 +1,114 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +class Controller extends Component { + constructor($element, $, vnConfig) { + super($element, $); + this.vnConfig = vnConfig; + this.dms = { + files: [], + hasFile: false, + hasFileAttached: false + }; + } + + get worker() { + return this._worker; + } + + set worker(value) { + this._worker = value; + + if (value) { + this.setDefaultParams(); + this.getAllowedContentTypes(); + } + } + + getAllowedContentTypes() { + this.$http.get('workerDms/allowedContentTypes').then(res => { + const contentTypes = res.data.join(', '); + this.allowedContentTypes = contentTypes; + }); + } + + get contentTypesInfo() { + return this.$translate.instant('ContentTypesInfo', { + allowedContentTypes: this.allowedContentTypes + }); + } + + setDefaultParams() { + const params = {filter: { + where: {code: 'hhrrData'} + }}; + this.$http.get('DmsTypes/findOne', {params}).then(res => { + const dmsType = res.data && res.data; + const companyId = this.vnConfig.companyFk; + const warehouseId = this.vnConfig.warehouseFk; + const defaultParams = { + reference: this.worker.id, + warehouseId: warehouseId, + companyId: companyId, + dmsTypeId: dmsType.id, + description: this.$translate.instant('WorkerFileDescription', { + dmsTypeName: dmsType.name, + workerId: this.worker.id, + workerName: this.worker.name + }).toUpperCase() + }; + + this.dms = Object.assign(this.dms, defaultParams); + }); + } + + onSubmit() { + const query = `Workers/${this.worker.id}/uploadFile`; + const options = { + method: 'POST', + url: query, + params: this.dms, + headers: { + 'Content-Type': undefined + }, + transformRequest: files => { + const formData = new FormData(); + + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.dms.files + }; + this.$http(options).then(res => { + if (res) { + this.vnApp.showSuccess(this.$translate.instant('Data saved!')); + this.$.watcher.updateOriginalData(); + this.$state.go('worker.card.dms.index'); + } + }); + } + + onFileChange(files) { + let hasFileAttached = false; + + if (files.length > 0) + hasFileAttached = true; + + this.$.$applyAsync(() => { + this.dms.hasFileAttached = hasFileAttached; + }); + } +} + +Controller.$inject = ['$element', '$scope', 'vnConfig']; + +ngModule.component('vnWorkerDmsCreate', { + template: require('./index.html'), + controller: Controller, + bindings: { + worker: '<' + } +}); diff --git a/modules/worker/front/dms/create/index.spec.js b/modules/worker/front/dms/create/index.spec.js new file mode 100644 index 000000000..41fe0e0ca --- /dev/null +++ b/modules/worker/front/dms/create/index.spec.js @@ -0,0 +1,77 @@ +import './index'; + +describe('Client', () => { + describe('Component vnWorkerDmsCreate', () => { + let $element; + let controller; + let $scope; + let $httpBackend; + let $httpParamSerializer; + + beforeEach(ngModule('worker')); + + beforeEach(angular.mock.inject(($compile, $rootScope, _$httpBackend_, _$httpParamSerializer_) => { + $scope = $rootScope.$new(); + $httpBackend = _$httpBackend_; + $httpParamSerializer = _$httpParamSerializer_; + $element = $compile(``)($rootScope); + controller = $element.controller('vnWorkerDmsCreate'); + controller._worker = {id: 101, name: 'Bruce wayne'}; + })); + + describe('worker() setter', () => { + it('should set the worker data and then call setDefaultParams() and getAllowedContentTypes()', () => { + spyOn(controller, 'setDefaultParams'); + spyOn(controller, 'getAllowedContentTypes'); + controller.worker = { + id: 15, + name: 'Bruce wayne' + }; + + expect(controller.worker).toBeDefined(); + expect(controller.setDefaultParams).toHaveBeenCalledWith(); + expect(controller.getAllowedContentTypes).toHaveBeenCalledWith(); + }); + }); + + describe('setDefaultParams()', () => { + it('should perform a GET query and define the dms property on controller', () => { + $httpBackend.whenRoute('GET', `DmsTypes`).respond({id: 12, code: 'hhrrData'}); + const params = {filter: { + where: {code: 'hhrrData'} + }}; + let serializedParams = $httpParamSerializer(params); + $httpBackend.when('GET', `DmsTypes/findOne?${serializedParams}`).respond({id: 12, code: 'hhrrData'}); + controller.setDefaultParams(); + $httpBackend.flush(); + + expect(controller.dms).toBeDefined(); + expect(controller.dms.reference).toEqual(101); + expect(controller.dms.dmsTypeId).toEqual(12); + }); + }); + + describe('onFileChange()', () => { + it('should set dms hasFileAttached property to true if has any files', () => { + const files = [{id: 1, name: 'MyFile'}]; + controller.onFileChange(files); + $scope.$apply(); + + expect(controller.dms.hasFileAttached).toBeTruthy(); + }); + }); + + describe('getAllowedContentTypes()', () => { + it('should make an HTTP GET request to get the allowed content types', () => { + const expectedResponse = ['image/png', 'image/jpg']; + $httpBackend.when('GET', `workerDms/allowedContentTypes`).respond(expectedResponse); + $httpBackend.expect('GET', `workerDms/allowedContentTypes`); + controller.getAllowedContentTypes(); + $httpBackend.flush(); + + expect(controller.allowedContentTypes).toBeDefined(); + expect(controller.allowedContentTypes).toEqual('image/png, image/jpg'); + }); + }); + }); +}); diff --git a/modules/worker/front/dms/create/style.scss b/modules/worker/front/dms/create/style.scss new file mode 100644 index 000000000..73f136fc1 --- /dev/null +++ b/modules/worker/front/dms/create/style.scss @@ -0,0 +1,7 @@ +vn-ticket-request { + .vn-textfield { + margin: 0!important; + max-width: 100px; + } +} + diff --git a/modules/worker/front/dms/edit/index.html b/modules/worker/front/dms/edit/index.html new file mode 100644 index 000000000..13bf6f953 --- /dev/null +++ b/modules/worker/front/dms/edit/index.html @@ -0,0 +1,82 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/modules/worker/front/dms/edit/index.js b/modules/worker/front/dms/edit/index.js new file mode 100644 index 000000000..1a593414a --- /dev/null +++ b/modules/worker/front/dms/edit/index.js @@ -0,0 +1,94 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +class Controller extends Component { + get worker() { + return this._worker; + } + + set worker(value) { + this._worker = value; + + if (value) { + this.setDefaultParams(); + this.getAllowedContentTypes(); + } + } + + getAllowedContentTypes() { + this.$http.get('WorkerDms/allowedContentTypes').then(res => { + const contentTypes = res.data.join(', '); + this.allowedContentTypes = contentTypes; + }); + } + + get contentTypesInfo() { + return this.$translate.instant('ContentTypesInfo', { + allowedContentTypes: this.allowedContentTypes + }); + } + + setDefaultParams() { + const path = `Dms/${this.$params.dmsId}`; + this.$http.get(path).then(res => { + const dms = res.data && res.data; + this.dms = { + reference: dms.reference, + warehouseId: dms.warehouseFk, + companyId: dms.companyFk, + dmsTypeId: dms.dmsTypeFk, + description: dms.description, + hasFile: dms.hasFile, + hasFileAttached: false, + files: [] + }; + }); + } + + onSubmit() { + const query = `dms/${this.$params.dmsId}/updateFile`; + const options = { + method: 'POST', + url: query, + params: this.dms, + headers: { + 'Content-Type': undefined + }, + transformRequest: files => { + const formData = new FormData(); + + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.dms.files + }; + this.$http(options).then(res => { + if (res) { + this.vnApp.showSuccess(this.$translate.instant('Data saved!')); + this.$.watcher.updateOriginalData(); + this.$state.go('worker.card.dms.index'); + } + }); + } + + onFileChange(files) { + let hasFileAttached = false; + if (files.length > 0) + hasFileAttached = true; + + this.$.$applyAsync(() => { + this.dms.hasFileAttached = hasFileAttached; + }); + } +} + +ngModule.component('vnWorkerDmsEdit', { + template: require('./index.html'), + controller: Controller, + bindings: { + worker: '<' + } +}); diff --git a/modules/worker/front/dms/edit/index.spec.js b/modules/worker/front/dms/edit/index.spec.js new file mode 100644 index 000000000..b883d4472 --- /dev/null +++ b/modules/worker/front/dms/edit/index.spec.js @@ -0,0 +1,84 @@ +import './index'; + +describe('Worker', () => { + describe('Component vnClientDmsEdit', () => { + let controller; + let $scope; + let $element; + let $httpBackend; + + beforeEach(ngModule('worker')); + + beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + $scope = $rootScope.$new(); + $httpBackend = _$httpBackend_; + $element = angular.element(` { + it('should set the worker data and then call setDefaultParams() and getAllowedContentTypes()', () => { + spyOn(controller, 'setDefaultParams'); + spyOn(controller, 'getAllowedContentTypes'); + controller._worker = undefined; + controller.worker = { + id: 106 + }; + + expect(controller.setDefaultParams).toHaveBeenCalledWith(); + expect(controller.worker).toBeDefined(); + expect(controller.getAllowedContentTypes).toHaveBeenCalledWith(); + }); + }); + + describe('setDefaultParams()', () => { + it('should perform a GET query and define the dms property on controller', () => { + const dmsId = 4; + const expectedResponse = { + reference: 101, + warehouseFk: 1, + companyFk: 442, + dmsTypeFk: 3, + description: 'Test', + hasFile: false, + hasFileAttached: false + }; + + $httpBackend.when('GET', `Dms/${dmsId}`).respond(expectedResponse); + $httpBackend.expect('GET', `Dms/${dmsId}`).respond(expectedResponse); + controller.setDefaultParams(); + $httpBackend.flush(); + + expect(controller.dms).toBeDefined(); + expect(controller.dms.reference).toEqual(101); + expect(controller.dms.dmsTypeId).toEqual(3); + }); + }); + + describe('onFileChange()', () => { + it('should set dms hasFileAttached property to true if has any files', () => { + const files = [{id: 1, name: 'MyFile'}]; + controller.dms = {hasFileAttached: false}; + controller.onFileChange(files); + $scope.$apply(); + + expect(controller.dms.hasFileAttached).toBeTruthy(); + }); + }); + + describe('getAllowedContentTypes()', () => { + it('should make an HTTP GET request to get the allowed content types', () => { + const expectedResponse = ['image/png', 'image/jpg']; + $httpBackend.when('GET', `WorkerDms/allowedContentTypes`).respond(expectedResponse); + $httpBackend.expect('GET', `WorkerDms/allowedContentTypes`); + controller.getAllowedContentTypes(); + $httpBackend.flush(); + + expect(controller.allowedContentTypes).toBeDefined(); + expect(controller.allowedContentTypes).toEqual('image/png, image/jpg'); + }); + }); + }); +}); diff --git a/modules/worker/front/dms/edit/style.scss b/modules/worker/front/dms/edit/style.scss new file mode 100644 index 000000000..73f136fc1 --- /dev/null +++ b/modules/worker/front/dms/edit/style.scss @@ -0,0 +1,7 @@ +vn-ticket-request { + .vn-textfield { + margin: 0!important; + max-width: 100px; + } +} + diff --git a/modules/worker/front/dms/index/index.html b/modules/worker/front/dms/index/index.html new file mode 100644 index 000000000..7859a50a4 --- /dev/null +++ b/modules/worker/front/dms/index/index.html @@ -0,0 +1,117 @@ + + + + + + + + Id + Type + Order + Reference + Description + Original + File + Employee + Created + + + + + + + + {{::document.dmsFk}} + + + {{::document.dms.dmsType.name}} + + + + + {{::document.dms.hardCopyNumber}} + + + + + {{::document.dms.reference}} + + + + + {{::document.dms.description}} + + + + + + + + {{::document.dms.file}} + + + + + {{::document.dms.worker.user.nickname | dashIfEmpty}} + + + {{::document.dms.created | date:'dd/MM/yyyy HH:mm'}} + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/worker/front/dms/index/index.js b/modules/worker/front/dms/index/index.js new file mode 100644 index 000000000..907047f97 --- /dev/null +++ b/modules/worker/front/dms/index/index.js @@ -0,0 +1,76 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +class Controller extends Component { + constructor($element, $, vnToken) { + super($element, $); + this.accessToken = vnToken.token; + this.filter = { + include: { + relation: 'dms', + scope: { + fields: [ + 'dmsTypeFk', + 'reference', + 'hardCopyNumber', + 'workerFk', + 'description', + 'hasFile', + 'file', + 'created', + ], + include: [{ + relation: 'dmsType', + scope: { + fields: ['name'] + } + }, + { + relation: 'worker', + scope: { + fields: ['userFk'], + include: { + relation: 'user', + scope: { + fields: ['nickname'] + } + }, + } + }] + }, + } + }; + } + + showWorkerDescriptor(event, workerFk) { + event.preventDefault(); + event.stopImmediatePropagation(); + this.$.workerDescriptor.parent = event.target; + this.$.workerDescriptor.workerFk = workerFk; + this.$.workerDescriptor.show(); + } + + showDeleteConfirm(index) { + this.dmsIndex = index; + this.$.confirm.show(); + } + + deleteDms(response) { + if (response === 'accept') { + const dmsFk = this.workerDms[this.dmsIndex].dmsFk; + const query = `WorkerDms/${dmsFk}/removeFile`; + this.$http.post(query).then(() => { + this.$.model.remove(this.dmsIndex); + this.vnApp.showSuccess(this.$translate.instant('Data saved!')); + }); + } + } +} + +Controller.$inject = ['$element', '$scope', 'vnToken']; + +ngModule.component('vnWorkerDmsIndex', { + template: require('./index.html'), + controller: Controller, +}); diff --git a/modules/worker/front/dms/index/index.spec.js b/modules/worker/front/dms/index/index.spec.js new file mode 100644 index 000000000..6220d051c --- /dev/null +++ b/modules/worker/front/dms/index/index.spec.js @@ -0,0 +1,42 @@ +import './index'; +import crudModel from 'core/mocks/crud-model'; + +describe('Worker', () => { + describe('Component vnWorkerDmsIndex', () => { + let $componentController; + let $scope; + let $element; + let $httpBackend; + let controller; + + beforeEach(ngModule('worker')); + + beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => { + $componentController = _$componentController_; + $httpBackend = _$httpBackend_; + $scope = $rootScope.$new(); + $element = angular.element(` { + it('should make an HTTP Post query', () => { + const dmsId = 4; + const dmsIndex = 0; + spyOn(controller.vnApp, 'showSuccess'); + spyOn(controller.$.model, 'remove'); + controller.workerDms = [{dmsFk: 4}]; + controller.dmsIndex = dmsIndex; + + $httpBackend.when('POST', `WorkerDms/${dmsId}/removeFile`).respond({}); + $httpBackend.expect('POST', `WorkerDms/${dmsId}/removeFile`); + controller.deleteDms('accept'); + $httpBackend.flush(); + + expect(controller.$.model.remove).toHaveBeenCalledWith(dmsIndex); + expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + }); + }); + }); +}); diff --git a/modules/worker/front/dms/index/locale/es.yml b/modules/worker/front/dms/index/locale/es.yml new file mode 100644 index 000000000..0994c7d86 --- /dev/null +++ b/modules/worker/front/dms/index/locale/es.yml @@ -0,0 +1,9 @@ +Type: Tipo +File management: Gestión documental +File: Fichero +Hard copy: Copia +This file will be deleted: Este fichero va a ser borrado +Are you sure?: Estas seguro? +File deleted: Fichero eliminado +Remove file: Eliminar fichero +Download file: Descargar fichero \ No newline at end of file diff --git a/modules/worker/front/dms/index/style.scss b/modules/worker/front/dms/index/style.scss new file mode 100644 index 000000000..a6758e2e6 --- /dev/null +++ b/modules/worker/front/dms/index/style.scss @@ -0,0 +1,6 @@ +vn-client-risk-index { + .totalBox { + display: table; + float: right; + } +} \ No newline at end of file diff --git a/modules/worker/front/dms/locale/en.yml b/modules/worker/front/dms/locale/en.yml new file mode 100644 index 000000000..766853fca --- /dev/null +++ b/modules/worker/front/dms/locale/en.yml @@ -0,0 +1,2 @@ +ClientFileDescription: "{{dmsTypeName}} from client {{clientName}} id {{clientId}}" +ContentTypesInfo: Allowed file types {{allowedContentTypes}} \ No newline at end of file diff --git a/modules/worker/front/dms/locale/es.yml b/modules/worker/front/dms/locale/es.yml new file mode 100644 index 000000000..fa4178d35 --- /dev/null +++ b/modules/worker/front/dms/locale/es.yml @@ -0,0 +1,20 @@ +Reference: Referencia +Description: Descripción +Company: Empresa +Upload file: Subir fichero +Edit file: Editar fichero +Upload: Subir +File: Fichero +WorkerFileDescription: "{{dmsTypeName}} del empleado {{workerName}} id {{workerId}}" +ContentTypesInfo: "Tipos de archivo permitidos: {{allowedContentTypes}}" +Generate identifier for original file: Generar identificador para archivo original +Are you sure you want to continue?: ¿Seguro que quieres continuar? +File management: Gestión documental +Hard copy: Copia +This file will be deleted: Este fichero va a ser borrado +Are you sure?: ¿Seguro? +File deleted: Fichero eliminado +Remove file: Eliminar fichero +Download file: Descargar fichero +Created: Creado +Employee: Empleado \ No newline at end of file diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js index 3ef1c9217..775524a3d 100644 --- a/modules/worker/front/index.js +++ b/modules/worker/front/index.js @@ -14,3 +14,6 @@ import './calendar'; import './time-control'; import './log'; import './phones'; +import './dms/index'; +import './dms/create'; +import './dms/edit'; diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json index 7cd5fda20..d117cf18c 100644 --- a/modules/worker/front/routes.json +++ b/modules/worker/front/routes.json @@ -13,7 +13,8 @@ {"state": "worker.card.pbx", "icon": "icon-pbx"}, {"state": "worker.card.calendar", "icon": "icon-calendar"}, {"state": "worker.card.timeControl", "icon": "access_time"}, - {"state": "worker.card.phones", "icon": "contact_phone"} + {"state": "worker.card.phones", "icon": "contact_phone"}, + {"state": "worker.card.dms.index", "icon": "cloud_upload"} ] }, "routes": [ @@ -89,6 +90,36 @@ "params": { "worker": "$ctrl.worker" } + }, + { + "url": "/dms", + "state": "worker.card.dms", + "abstract": true, + "component": "ui-view" + }, + { + "url": "/index", + "state": "worker.card.dms.index", + "component": "vn-worker-dms-index", + "description": "File management" + }, + { + "url": "/create", + "state": "worker.card.dms.create", + "component": "vn-worker-dms-create", + "description": "Upload file", + "params": { + "worker": "$ctrl.worker" + } + }, + { + "url": "/:dmsId/edit", + "state": "worker.card.dms.edit", + "component": "vn-worker-dms-edit", + "description": "Edit file", + "params": { + "worker": "$ctrl.worker" + } } ] } \ No newline at end of file