From d2de312a27c910ca839e0156b9a60270ed914836 Mon Sep 17 00:00:00 2001 From: Joan Sanchez Date: Mon, 15 Jul 2019 11:40:11 +0200 Subject: [PATCH] dms update files --- back/methods/dms/specs/updateFile.spec.js | 22 +++ back/methods/dms/updateFile.js | 158 ++++++++++++++++++++ back/methods/dms/uploadFile.js | 14 +- back/models/dms.js | 1 + front/core/components/table/style.scss | 6 + loopback/server/boot/storage.js | 2 +- modules/client/front/dms/edit/index.html | 63 ++++++++ modules/client/front/dms/edit/index.js | 86 +++++++++++ modules/client/front/dms/edit/index.spec.js | 60 ++++++++ modules/client/front/dms/edit/locale/es.yml | 3 + modules/client/front/dms/edit/style.scss | 7 + modules/client/front/dms/index/index.html | 19 ++- modules/client/front/index.js | 1 + modules/client/front/routes.json | 9 ++ modules/ticket/front/dms/edit/index.html | 63 ++++++++ modules/ticket/front/dms/edit/index.js | 86 +++++++++++ modules/ticket/front/dms/edit/index.spec.js | 60 ++++++++ modules/ticket/front/dms/edit/locale/es.yml | 3 + modules/ticket/front/dms/edit/style.scss | 7 + modules/ticket/front/dms/index/index.html | 7 +- modules/ticket/front/index.js | 1 + modules/ticket/front/routes.json | 9 ++ 22 files changed, 670 insertions(+), 17 deletions(-) create mode 100644 back/methods/dms/specs/updateFile.spec.js create mode 100644 back/methods/dms/updateFile.js create mode 100644 modules/client/front/dms/edit/index.html create mode 100644 modules/client/front/dms/edit/index.js create mode 100644 modules/client/front/dms/edit/index.spec.js create mode 100644 modules/client/front/dms/edit/locale/es.yml create mode 100644 modules/client/front/dms/edit/style.scss create mode 100644 modules/ticket/front/dms/edit/index.html create mode 100644 modules/ticket/front/dms/edit/index.js create mode 100644 modules/ticket/front/dms/edit/index.spec.js create mode 100644 modules/ticket/front/dms/edit/locale/es.yml create mode 100644 modules/ticket/front/dms/edit/style.scss diff --git a/back/methods/dms/specs/updateFile.spec.js b/back/methods/dms/specs/updateFile.spec.js new file mode 100644 index 000000000..2945b1ac9 --- /dev/null +++ b/back/methods/dms/specs/updateFile.spec.js @@ -0,0 +1,22 @@ +const app = require('vn-loopback/server/server'); + +describe('dms updateFile()', () => { + it(`should return an error for a user without enough privileges`, async() => { + let clientId = 101; + let companyId = 442; + let warehouseId = 1; + let dmsTypeId = 14; + + let dmsId = 1; + let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: dmsTypeId}}; + + let error; + await app.models.Dms.updateFile(ctx, dmsId, warehouseId, companyId, dmsTypeId).catch(e => { + error = e; + }).finally(() => { + expect(error.message).toEqual(`You don't have enough privileges`); + }); + + expect(error).toBeDefined(); + }); +}); diff --git a/back/methods/dms/updateFile.js b/back/methods/dms/updateFile.js new file mode 100644 index 000000000..e502c7b94 --- /dev/null +++ b/back/methods/dms/updateFile.js @@ -0,0 +1,158 @@ +const UserError = require('vn-loopback/util/user-error'); +const fs = require('fs-extra'); + +module.exports = Self => { + Self.remoteMethodCtx('updateFile', { + description: 'updates a file properties or file', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'Number', + description: 'The document id', + http: {source: 'path'} + }, + { + arg: 'warehouseId', + type: 'Number', + description: 'The warehouse id' + }, { + arg: 'companyId', + type: 'Number', + description: 'The company id' + }, { + arg: 'dmsTypeId', + type: 'Number', + description: 'The dms type id' + }, { + arg: 'reference', + type: 'String' + }, { + arg: 'description', + type: 'String' + }, { + arg: 'hasFile', + type: 'Boolean', + description: 'True if has an attached file' + }], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/:id/updateFile`, + verb: 'POST' + } + }); + + Self.updateFile = async(ctx, id, warehouseId, companyId, + dmsTypeId, reference, description, hasFile, options) => { + const storageConnector = Self.app.dataSources.storage.connector; + const models = Self.app.models; + const fileOptions = {}; + + let tx; + let myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const hasWriteRole = await models.DmsType.hasWriteRole(ctx, dmsTypeId); + if (!hasWriteRole) + throw new UserError(`You don't have enough privileges`); + + // Upload file to temporary path + const tempContainer = await getContainer('temp'); + let files = []; + try { + const uploaded = await models.Container.upload(tempContainer.name, ctx.req, ctx.result, fileOptions); + files = Object.values(uploaded.files).map(file => { + return file[0]; + }); + } catch (err) { + if (err.message != 'No file content uploaded') + throw e; + } + + const updatedDmsList = []; + for (const file of files) { + const updatedDms = await updateDms(id, dmsTypeId, companyId, warehouseId, + reference, description, hasFile, file.name, myOptions); + + const pathHash = storageConnector.getPathHash(updatedDms.id); + const container = await getContainer(pathHash); + + const originPath = `${tempContainer.client.root}/${tempContainer.name}/${file.name}`; + const destinationPath = `${container.client.root}/${pathHash}/${updatedDms.file}`; + + fs.rename(originPath, destinationPath); + + updatedDmsList.push(updatedDms); + } + + if (tx) await tx.commit(); + return updatedDmsList; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; + + async function updateDms(id, dmsTypeId, companyId, warehouseId, + reference, description, hasFile, fileName, myOptions) { + const storageConnector = Self.app.dataSources.storage.connector; + const dms = await Self.findById(id, null, myOptions); + const updatedDms = await dms.updateAttributes({ + dmsTypeFk: dmsTypeId, + companyFk: companyId, + warehouseFk: warehouseId, + reference: reference, + description: description, + hasFile: hasFile + }, myOptions); + + const oldExtension = storageConnector.getFileExtension(dms.file); + const newExtension = storageConnector.getFileExtension(fileName); + + try { + if (oldExtension != newExtension) { + const pathHash = storageConnector.getPathHash(updatedDms.id); + + await Self.app.models.Container.removeFile(pathHash, dms.file); + } + } catch (err) {} + + fileName = `${updatedDms.id}.${newExtension}`; + + return updatedDms.updateAttribute('file', fileName, myOptions); + } + + + /** + * Returns a container instance + * If doesn't exists creates a new one + * + * @param {String} name Container name + * @return {Object} Container instance + */ + async function getContainer(name) { + const models = Self.app.models; + let container; + try { + container = await models.Container.getContainer(name); + } catch (err) { + if (err.code === 'ENOENT') { + container = await models.Container.createContainer({ + name: name + }); + } else throw err; + } + + return container; + } +}; diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js index 9ef60c005..5d4634aae 100644 --- a/back/methods/dms/uploadFile.js +++ b/back/methods/dms/uploadFile.js @@ -9,27 +9,25 @@ module.exports = Self => { { arg: 'warehouseId', type: 'Number', - description: '' + description: 'The warehouse id' }, { arg: 'companyId', type: 'Number', - description: '' + description: 'The company id' }, { arg: 'dmsTypeId', type: 'Number', - description: '' + description: 'The dms type id' }, { arg: 'reference', - type: 'String', - description: '' + type: 'String' }, { arg: 'description', - type: 'String', - description: '' + type: 'String' }, { arg: 'hasFile', type: 'Boolean', - description: '' + description: 'True if has an attached file' }], returns: { type: 'Object', diff --git a/back/models/dms.js b/back/models/dms.js index d3471178b..9a06928db 100644 --- a/back/models/dms.js +++ b/back/models/dms.js @@ -2,4 +2,5 @@ module.exports = Self => { require('../methods/dms/downloadFile')(Self); require('../methods/dms/uploadFile')(Self); require('../methods/dms/removeFile')(Self); + require('../methods/dms/updateFile')(Self); }; diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss index 87af10834..7d985c437 100644 --- a/front/core/components/table/style.scss +++ b/front/core/components/table/style.scss @@ -141,6 +141,12 @@ vn-table { background-color: $color-alert-medium; } + & > vn-td vn-icon-menu { + display: inline-block; + color: $color-main; + padding: .25em + } + & > [actions] { width: 1px; diff --git a/loopback/server/boot/storage.js b/loopback/server/boot/storage.js index d34804059..12662ab73 100644 --- a/loopback/server/boot/storage.js +++ b/loopback/server/boot/storage.js @@ -9,7 +9,7 @@ module.exports = app => { }; storageConnector.getFileExtension = function(fileName) { - return fileName.split('.').pop(); + return fileName.split('.').pop().toLowerCase(); }; storageConnector.getPathHash = function(id) { diff --git a/modules/client/front/dms/edit/index.html b/modules/client/front/dms/edit/index.html new file mode 100644 index 000000000..65c280c5d --- /dev/null +++ b/modules/client/front/dms/edit/index.html @@ -0,0 +1,63 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/modules/client/front/dms/edit/index.js b/modules/client/front/dms/edit/index.js new file mode 100644 index 000000000..ba0108e6b --- /dev/null +++ b/modules/client/front/dms/edit/index.js @@ -0,0 +1,86 @@ +import ngModule from '../../module'; +import './style.scss'; + +class Controller { + constructor($scope, $http, $state, $translate, vnApp) { + this.$ = $scope; + this.$http = $http; + this.$state = $state; + this.$stateParams = $state.params; + this.$translate = $translate; + this.vnApp = vnApp; + } + + get client() { + return this._client; + } + + set client(value) { + this._client = value; + + if (value) + this.setDefaultParams(); + } + + setDefaultParams() { + const path = `/api/Dms/${this.$stateParams.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, + files: [] + }; + }); + } + + onSubmit() { + const query = `/api/dms/${this.$stateParams.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('client.card.dms.index'); + } + }); + } + + onFileChange(files) { + if (files.length > 0) { + this.$.$applyAsync(() => { + this.dms.hasFile = true; + }); + } + } +} + +Controller.$inject = ['$scope', '$http', '$state', '$translate', 'vnApp']; + +ngModule.component('vnClientDmsEdit', { + template: require('./index.html'), + controller: Controller, + bindings: { + client: '<' + } +}); diff --git a/modules/client/front/dms/edit/index.spec.js b/modules/client/front/dms/edit/index.spec.js new file mode 100644 index 000000000..2825d4d25 --- /dev/null +++ b/modules/client/front/dms/edit/index.spec.js @@ -0,0 +1,60 @@ +import './index'; + +describe('Client', () => { + describe('Component vnClientDmsCreate', () => { + let controller; + let $scope; + let $httpBackend; + let $httpParamSerializer; + + beforeEach(ngModule('client')); + + beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => { + $scope = $rootScope.$new(); + $httpBackend = _$httpBackend_; + $httpParamSerializer = _$httpParamSerializer_; + controller = $componentController('vnClientDmsCreate', {$scope}); + controller._client = {id: 101, name: 'Bruce wayne'}; + })); + + describe('client() setter', () => { + it('should set the client data and then call setDefaultParams()', () => { + spyOn(controller, 'setDefaultParams'); + controller.client = { + id: 15, + name: 'Bruce wayne' + }; + + expect(controller.client).toBeDefined(); + expect(controller.setDefaultParams).toHaveBeenCalledWith(); + }); + }); + + describe('setDefaultParams()', () => { + it('should perform a GET query and define the dms property on controller', () => { + const params = {filter: { + where: {code: 'paymentsLaw'} + }}; + let serializedParams = $httpParamSerializer(params); + $httpBackend.when('GET', `/api/DmsTypes/findOne?${serializedParams}`).respond({id: 12, code: 'paymentsLaw'}); + $httpBackend.expect('GET', `/api/DmsTypes/findOne?${serializedParams}`); + 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 hasFile property to true if has any files', () => { + const files = [{id: 1, name: 'MyFile'}]; + controller.onFileChange(files); + $scope.$apply(); + + expect(controller.dms.hasFile).toBeTruthy(); + }); + }); + }); +}); diff --git a/modules/client/front/dms/edit/locale/es.yml b/modules/client/front/dms/edit/locale/es.yml new file mode 100644 index 000000000..7625d5195 --- /dev/null +++ b/modules/client/front/dms/edit/locale/es.yml @@ -0,0 +1,3 @@ +Edit file: Editar fichero +File: Fichero +Attached file: Fichero adjunto \ No newline at end of file diff --git a/modules/client/front/dms/edit/style.scss b/modules/client/front/dms/edit/style.scss new file mode 100644 index 000000000..b47544b12 --- /dev/null +++ b/modules/client/front/dms/edit/style.scss @@ -0,0 +1,7 @@ +vn-ticket-request { + vn-textfield { + margin: 0!important; + max-width: 100px; + } +} + diff --git a/modules/client/front/dms/index/index.html b/modules/client/front/dms/index/index.html index 2cf6cdd4f..b55bc137f 100644 --- a/modules/client/front/dms/index/index.html +++ b/modules/client/front/dms/index/index.html @@ -16,11 +16,11 @@ Type Reference Description - Attached file + Original File - Employee + Employee Created - + @@ -41,13 +41,13 @@ {{::document.dms.description}} - + {{::document.dms.file}} - + {{::document.dms.worker.user.nickname | dashIfEmpty}} @@ -55,16 +55,21 @@ {{::document.dms.created | dateTime:'dd/MM/yyyy HH:mm'}} - + + ng-show="document.dms.hasFile"> + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/modules/ticket/front/dms/edit/index.js b/modules/ticket/front/dms/edit/index.js new file mode 100644 index 000000000..c35c4140d --- /dev/null +++ b/modules/ticket/front/dms/edit/index.js @@ -0,0 +1,86 @@ +import ngModule from '../../module'; +import './style.scss'; + +class Controller { + constructor($scope, $http, $state, $translate, vnApp) { + this.$ = $scope; + this.$http = $http; + this.$state = $state; + this.$stateParams = $state.params; + this.$translate = $translate; + this.vnApp = vnApp; + } + + get ticket() { + return this._ticket; + } + + set ticket(value) { + this._ticket = value; + + if (value) + this.setDefaultParams(); + } + + setDefaultParams() { + const path = `/api/Dms/${this.$stateParams.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, + files: [] + }; + }); + } + + onSubmit() { + const query = `/api/dms/${this.$stateParams.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('ticket.card.dms.index'); + } + }); + } + + onFileChange(files) { + if (files.length > 0) { + this.$.$applyAsync(() => { + this.dms.hasFile = true; + }); + } + } +} + +Controller.$inject = ['$scope', '$http', '$state', '$translate', 'vnApp']; + +ngModule.component('vnTicketDmsEdit', { + template: require('./index.html'), + controller: Controller, + bindings: { + ticket: '<' + } +}); diff --git a/modules/ticket/front/dms/edit/index.spec.js b/modules/ticket/front/dms/edit/index.spec.js new file mode 100644 index 000000000..2825d4d25 --- /dev/null +++ b/modules/ticket/front/dms/edit/index.spec.js @@ -0,0 +1,60 @@ +import './index'; + +describe('Client', () => { + describe('Component vnClientDmsCreate', () => { + let controller; + let $scope; + let $httpBackend; + let $httpParamSerializer; + + beforeEach(ngModule('client')); + + beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => { + $scope = $rootScope.$new(); + $httpBackend = _$httpBackend_; + $httpParamSerializer = _$httpParamSerializer_; + controller = $componentController('vnClientDmsCreate', {$scope}); + controller._client = {id: 101, name: 'Bruce wayne'}; + })); + + describe('client() setter', () => { + it('should set the client data and then call setDefaultParams()', () => { + spyOn(controller, 'setDefaultParams'); + controller.client = { + id: 15, + name: 'Bruce wayne' + }; + + expect(controller.client).toBeDefined(); + expect(controller.setDefaultParams).toHaveBeenCalledWith(); + }); + }); + + describe('setDefaultParams()', () => { + it('should perform a GET query and define the dms property on controller', () => { + const params = {filter: { + where: {code: 'paymentsLaw'} + }}; + let serializedParams = $httpParamSerializer(params); + $httpBackend.when('GET', `/api/DmsTypes/findOne?${serializedParams}`).respond({id: 12, code: 'paymentsLaw'}); + $httpBackend.expect('GET', `/api/DmsTypes/findOne?${serializedParams}`); + 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 hasFile property to true if has any files', () => { + const files = [{id: 1, name: 'MyFile'}]; + controller.onFileChange(files); + $scope.$apply(); + + expect(controller.dms.hasFile).toBeTruthy(); + }); + }); + }); +}); diff --git a/modules/ticket/front/dms/edit/locale/es.yml b/modules/ticket/front/dms/edit/locale/es.yml new file mode 100644 index 000000000..7625d5195 --- /dev/null +++ b/modules/ticket/front/dms/edit/locale/es.yml @@ -0,0 +1,3 @@ +Edit file: Editar fichero +File: Fichero +Attached file: Fichero adjunto \ No newline at end of file diff --git a/modules/ticket/front/dms/edit/style.scss b/modules/ticket/front/dms/edit/style.scss new file mode 100644 index 000000000..b47544b12 --- /dev/null +++ b/modules/ticket/front/dms/edit/style.scss @@ -0,0 +1,7 @@ +vn-ticket-request { + vn-textfield { + margin: 0!important; + max-width: 100px; + } +} + diff --git a/modules/ticket/front/dms/index/index.html b/modules/ticket/front/dms/index/index.html index eb51710b6..920035060 100644 --- a/modules/ticket/front/dms/index/index.html +++ b/modules/ticket/front/dms/index/index.html @@ -59,12 +59,17 @@ + ng-show="document.dms.hasFile"> + +