1762-worker_dms #31
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
@ -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();
|
||||
};
|
||||
};
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -20,6 +20,9 @@
|
|||
"WorkCenterHoliday": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"WorkerDms": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Worker": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/worker-dms/removeFile')(Self);
|
||||
require('../methods/worker-dms/allowedContentTypes')(Self);
|
||||
};
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<mg-ajax path="dms/upload" options="vnPost"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.dms">
|
||||
</vn-watcher>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="$ctrl.onSubmit()"
|
||||
class="vn-ma-md"
|
||||
enctype="multipart/form-data">
|
||||
<div class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
vn-focus
|
||||
label="Reference"
|
||||
ng-model="$ctrl.dms.reference"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete vn-one
|
||||
label="Company"
|
||||
ng-model="$ctrl.dms.companyId"
|
||||
url="Companies"
|
||||
show-field="code"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
label="Warehouse"
|
||||
ng-model="$ctrl.dms.warehouseId"
|
||||
url="Warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
label="Type"
|
||||
ng-model="$ctrl.dms.dmsTypeId"
|
||||
url="DmsTypes"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
vn-one
|
||||
label="Description"
|
||||
ng-model="$ctrl.dms.description"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-file
|
||||
vn-one
|
||||
label="File"
|
||||
ng-model="$ctrl.dms.files"
|
||||
on-change="$ctrl.onFileChange($files)"
|
||||
accept="{{$ctrl.allowedContentTypes}}"
|
||||
required="true"
|
||||
multiple="true">
|
||||
<append>
|
||||
<vn-icon vn-none
|
||||
color-marginal
|
||||
title="{{$ctrl.contentTypesInfo}}"
|
||||
icon="info">
|
||||
</vn-icon>
|
||||
</append>
|
||||
</vn-input-file>
|
||||
</vn-horizontal>
|
||||
<vn-vertical>
|
||||
<vn-check
|
||||
label="Generate identifier for original file"
|
||||
ng-model="$ctrl.dms.hasFile">
|
||||
</vn-check>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Upload"></vn-submit>
|
||||
<vn-button ui-sref="worker.card.dms.index" label="Cancel"></vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
|
@ -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: '<'
|
||||
}
|
||||
});
|
|
@ -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(`<vn-worker-dms-create></vn-worker-dms-create>`)($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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
vn-ticket-request {
|
||||
.vn-textfield {
|
||||
margin: 0!important;
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.dms">
|
||||
</vn-watcher>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="$ctrl.onSubmit()"
|
||||
class="vn-ma-md"
|
||||
enctype="multipart/form-data">
|
||||
<div class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
vn-focus
|
||||
label="Reference"
|
||||
ng-model="$ctrl.dms.reference"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete vn-one required="true"
|
||||
label="Company"
|
||||
ng-model="$ctrl.dms.companyId"
|
||||
url="Companies"
|
||||
show-field="code"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one required="true"
|
||||
label="Warehouse"
|
||||
ng-model="$ctrl.dms.warehouseId"
|
||||
url="Warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one required="true"
|
||||
label="Type"
|
||||
ng-model="$ctrl.dms.dmsTypeId"
|
||||
url="DmsTypes"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
vn-one
|
||||
required="true"
|
||||
label="Description"
|
||||
ng-model="$ctrl.dms.description"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-file
|
||||
vn-one
|
||||
label="File"
|
||||
ng-model="$ctrl.dms.files"
|
||||
on-change="$ctrl.onFileChange($files)"
|
||||
accept="{{$ctrl.allowedContentTypes}}"
|
||||
multiple="true">
|
||||
<append>
|
||||
<vn-icon vn-none
|
||||
color-marginal
|
||||
title="{{$ctrl.contentTypesInfo}}"
|
||||
icon="info">
|
||||
</vn-icon>
|
||||
</append>
|
||||
</vn-input-file>
|
||||
</vn-horizontal>
|
||||
<vn-vertical>
|
||||
<vn-check disabled="true"
|
||||
label="Generate identifier for original file"
|
||||
ng-model="$ctrl.dms.hasFile">
|
||||
</vn-check>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Save"></vn-submit>
|
||||
<vn-button ui-sref="worker.card.dms.index" label="Cancel"></vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
|
@ -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: '<'
|
||||
}
|
||||
});
|
|
@ -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(`<vn-worker-dms-edit></vn-worker-dms-edit`);
|
||||
controller = $componentController('vnWorkerDmsEdit', {$element, $scope});
|
||||
controller._worker = {id: 106};
|
||||
controller.$params = {dmsId: 4};
|
||||
}));
|
||||
|
||||
describe('worker() setter', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
vn-ticket-request {
|
||||
.vn-textfield {
|
||||
margin: 0!important;
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="WorkerDms"
|
||||
link="{workerFk: $ctrl.$params.id}"
|
||||
filter="::$ctrl.filter"
|
||||
limit="20"
|
||||
data="$ctrl.workerDms"
|
||||
order="dmsFk DESC"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-w-lg">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th field="dmsFk" shrink>Id</vn-th>
|
||||
<vn-th field="dmsTypeFk" shrink>Type</vn-th>
|
||||
<vn-th field="hardCopyNumber" shrink number>Order</vn-th>
|
||||
<vn-th field="reference" shrink>Reference</vn-th>
|
||||
<vn-th expand>Description</vn-th>
|
||||
<vn-th field="hasFile" shrink>Original</vn-th>
|
||||
<vn-th shrink>File</vn-th>
|
||||
<vn-th shrink>Employee</vn-th>
|
||||
<vn-th field="created">Created</vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="document in $ctrl.workerDms">
|
||||
<vn-td number shrink>{{::document.dmsFk}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<span title="{{::document.dms.dmsType.name}}">
|
||||
{{::document.dms.dmsType.name}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink number>
|
||||
<span class="chip" title="{{::document.dms.hardCopyNumber}}"
|
||||
ng-class="{'message': document.dms.hardCopyNumber}">
|
||||
{{::document.dms.hardCopyNumber}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<span title="{{::document.dms.reference}}">
|
||||
{{::document.dms.reference}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
<span title="{{::document.dms.description}}">
|
||||
{{::document.dms.description}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-check disabled="true"
|
||||
ng-model="document.dms.hasFile">
|
||||
</vn-check>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<a target="_blank"
|
||||
title="{{'Download file' | translate}}"
|
||||
href="api/dms/{{::document.dmsFk}}/downloadFile?access_token={{::$ctrl.accessToken}}">{{::document.dms.file}}
|
||||
</a>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<span class="link"
|
||||
ng-click="$ctrl.showWorkerDescriptor($event, document.dms.workerFk)">
|
||||
{{::document.dms.worker.user.nickname | dashIfEmpty}}
|
||||
</span></vn-td>
|
||||
<vn-td>
|
||||
{{::document.dms.created | date:'dd/MM/yyyy HH:mm'}}
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<a target="_blank"
|
||||
href="api/dms/{{::document.dmsFk}}/downloadFile?access_token={{::$ctrl.accessToken}}">
|
||||
<vn-icon-button
|
||||
icon="cloud_download"
|
||||
title="{{'Download file' | translate}}">
|
||||
</vn-icon-button>
|
||||
</a>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon-button ui-sref="worker.card.dms.edit({dmsId: {{::document.dmsFk}}})"
|
||||
icon="edit"
|
||||
title="{{'Edit file' | translate}}">
|
||||
</vn-icon-button>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon-button
|
||||
icon="delete"
|
||||
ng-click="$ctrl.showDeleteConfirm($index)"
|
||||
title="{{'Remove file' | translate}}"
|
||||
tabindex="-1">
|
||||
</vn-icon-button>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
<a ui-sref="worker.card.dms.create"
|
||||
vn-tooltip="Upload file"
|
||||
vn-bind="+"
|
||||
fixed-bottom-right>
|
||||
<vn-float-button icon="add"></vn-float-button>
|
||||
</a>
|
||||
<vn-confirm
|
||||
vn-id="confirm"
|
||||
message="This file will be deleted"
|
||||
question="Are you sure you want to continue?"
|
||||
on-response="$ctrl.deleteDms($response)">
|
||||
</vn-confirm>
|
|
@ -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,
|
||||
});
|
|
@ -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(`<vn-worker-dms-index></vn-worker-dms-index`);
|
||||
controller = $componentController('vnWorkerDmsIndex', {$element, $scope});
|
||||
controller.$.model = crudModel;
|
||||
}));
|
||||
|
||||
describe('deleteDms()', () => {
|
||||
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!');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
vn-client-risk-index {
|
||||
.totalBox {
|
||||
display: table;
|
||||
float: right;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ClientFileDescription: "{{dmsTypeName}} from client {{clientName}} id {{clientId}}"
|
||||
ContentTypesInfo: Allowed file types {{allowedContentTypes}}
|
|
@ -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
|
|
@ -14,3 +14,6 @@ import './calendar';
|
|||
import './time-control';
|
||||
import './log';
|
||||
import './phones';
|
||||
import './dms/index';
|
||||
import './dms/create';
|
||||
import './dms/edit';
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue