parent
3615fe36a8
commit
2b7287dabb
|
@ -0,0 +1,76 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('download', {
|
||||
description: 'Download a document',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'String',
|
||||
description: 'The document id',
|
||||
http: {source: 'path'}
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'body',
|
||||
type: 'file',
|
||||
root: true
|
||||
}, {
|
||||
arg: 'Content-Type',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}, {
|
||||
arg: 'Content-Disposition',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/:id/download`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.download = async function(ctx, id) {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const env = process.env.NODE_ENV;
|
||||
const document = await Self.findById(id, {
|
||||
include: {
|
||||
relation: 'dmsType',
|
||||
scope: {
|
||||
fields: ['path', 'readRoleFk'],
|
||||
include: {
|
||||
relation: 'readRole'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const readRole = document.dmsType().readRole().name;
|
||||
const hasRequiredRole = await Self.app.models.Account.hasRole(userId, readRole);
|
||||
|
||||
if (!hasRequiredRole)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
if (env && env != 'development') {
|
||||
const path = `/${document.companyFk}/${document.dmsType().path}/${document.file}`;
|
||||
file = {
|
||||
path: `/var/lib/salix/dms/${path}`,
|
||||
contentType: 'application/octet-stream',
|
||||
name: document.file
|
||||
};
|
||||
} else {
|
||||
file = {
|
||||
path: `${process.cwd()}/README.md`,
|
||||
contentType: 'text/plain',
|
||||
name: `README.md`
|
||||
};
|
||||
}
|
||||
|
||||
await fs.access(file.path);
|
||||
let stream = fs.createReadStream(file.path);
|
||||
return [stream, file.contentType, `filename="${file.name}"`];
|
||||
};
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
/**
|
||||
* Pendiente de fixtures dms, dmsType, ticketDms
|
||||
* CAU: 10728
|
||||
*/
|
||||
xdescribe('dms download()', () => {
|
||||
let dmsId = 1;
|
||||
it('should return a response for an employee with text content-type', async() => {
|
||||
let workerFk = 107;
|
||||
let ctx = {req: {accessToken: {userId: workerFk}}};
|
||||
const result = await app.models.Dms.download(ctx, dmsId);
|
||||
|
||||
expect(result[1]).toEqual('text/plain');
|
||||
});
|
||||
|
||||
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.Dms.download(ctx, dmsId).catch(e => {
|
||||
error = e;
|
||||
}).finally(() => {
|
||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||
});
|
||||
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/dms/download')(Self);
|
||||
};
|
|
@ -22,12 +22,22 @@
|
|||
"required": true
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"relations": {
|
||||
"readRole": {
|
||||
"type": "belongsTo",
|
||||
"model": "Role",
|
||||
"foreignKey": "readRoleFk"
|
||||
},
|
||||
"writeRole": {
|
||||
"type": "belongsTo",
|
||||
"model": "Role",
|
||||
"foreignKey": "writeRoleFk"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (162, 'InvoiceOut', 'delete', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');
|
||||
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (163, 'InvoiceOut', 'book', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');
|
||||
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (164, 'InvoiceOut', 'regenerate', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');
|
||||
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (165, 'TicketDms', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (166, 'Dms', 'download', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
|
|
|
@ -6,6 +6,7 @@ CREATE
|
|||
SQL SECURITY DEFINER
|
||||
VIEW `vn`.`ticketDms` AS
|
||||
SELECT
|
||||
`g`.`Id_Ticket` AS `ticketFk`, `g`.`gestdoc_id` AS `dmsFk`
|
||||
`g`.`Id_Ticket` AS `ticketFk`,
|
||||
`g`.`gestdoc_id` AS `dmsFk`
|
||||
FROM
|
||||
`vn2008`.`tickets_gestdoc` `g`;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
ALTER TABLE `vn2008`.`gesttip`
|
||||
ADD COLUMN `writeRoleFk` INT(10) UNSIGNED NULL AFTER `path`,
|
||||
ADD COLUMN `ReadRoleFk` INT(10) UNSIGNED NULL AFTER `writeRoleFk`;
|
||||
ADD COLUMN `readRoleFk` INT(10) UNSIGNED NULL AFTER `writeRoleFk`,
|
||||
ADD CONSTRAINT `readRoleFk` FOREIGN KEY (`readRoleFk`) REFERENCES `account`.`role` (`id`),
|
||||
ADD CONSTRAINT `writeRoleFk` FOREIGN KEY (`writeRoleFk`) REFERENCES `account`.`role` (`id`);
|
||||
|
||||
UPDATE `vn2008`.`gesttip` SET `readRoleFk`='1' WHERE `id`='14';
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
CREATE
|
||||
OR REPLACE ALGORITHM = UNDEFINED
|
||||
DEFINER = `root`@`%`
|
||||
SQL SECURITY DEFINER
|
||||
VIEW `vn`.`dmsType` AS
|
||||
SELECT
|
||||
`g`.`id` AS `id`,
|
||||
`g`.`tipo` AS `name`,
|
||||
`g`.`path` AS `path`,
|
||||
`g`.`readRoleFk` AS `readRoleFk`,
|
||||
`g`.`writeRoleFk` AS `writeRoleFk`
|
||||
FROM
|
||||
`vn2008`.`gesttip` `g`;
|
|
@ -19,3 +19,4 @@ services:
|
|||
volumes:
|
||||
- /containers/salix:/etc/salix
|
||||
- /mnt/storage/pdfs:/var/lib/salix/pdfs
|
||||
- /mnt/storage/dms:/var/lib/salix/dms
|
|
@ -356,7 +356,7 @@ export default {
|
|||
expeditionRow: 'vn-ticket-expedition vn-table vn-tbody > vn-tr'
|
||||
},
|
||||
ticketPackages: {
|
||||
packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package.index"]',
|
||||
packagesButton: 'vn-left-menu a[ui-sref="ticket.card.package"]',
|
||||
firstPackageAutocomplete: 'vn-autocomplete[label="Package"]',
|
||||
firstQuantityInput: 'vn-input-number[label="Quantity"] input',
|
||||
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('Ticket Create packages path', () => {
|
|||
return nightmare
|
||||
.loginAndModule('employee', 'ticket')
|
||||
.accessToSearchResult('id:1')
|
||||
.accessToSection('ticket.card.package.index');
|
||||
.accessToSection('ticket.card.package');
|
||||
});
|
||||
|
||||
it(`should attempt create a new package but receive an error if package is blank`, async() => {
|
||||
|
@ -53,7 +53,7 @@ describe('Ticket Create packages path', () => {
|
|||
|
||||
it(`should confirm the first select is the expected one`, async() => {
|
||||
const result = await nightmare
|
||||
.reloadSection('ticket.card.package.index')
|
||||
.reloadSection('ticket.card.package')
|
||||
.waitForTextInInput(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'Container medical box 1m')
|
||||
.waitToGetProperty(`${selectors.ticketPackages.firstPackageAutocomplete} input`, 'value');
|
||||
|
||||
|
|
|
@ -14,10 +14,9 @@
|
|||
<vn-vertical>
|
||||
<vn-card pad-large>
|
||||
<vn-horizontal>
|
||||
<vn-one></vn-one>
|
||||
<vn-one>
|
||||
</vn-one>
|
||||
<vn-one>
|
||||
<vn-autocomplete vn-one
|
||||
<vn-autocomplete
|
||||
vn-id="company"
|
||||
field="$ctrl.companyFk"
|
||||
on-change="$ctrl.setOrder(value)"
|
||||
|
@ -89,7 +88,7 @@
|
|||
href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.accessToken}}">
|
||||
<vn-icon-button
|
||||
icon="cloud_download"
|
||||
title="Download PDF">
|
||||
title="{{'Download PDF' | translate}}">
|
||||
</vn-icon-button>
|
||||
</a>
|
||||
</vn-td>
|
||||
|
|
|
@ -87,10 +87,6 @@ class Controller {
|
|||
this.$.balanceCreateDialog.show();
|
||||
}
|
||||
|
||||
onDownload() {
|
||||
alert('Not implemented yet');
|
||||
}
|
||||
|
||||
showWorkerDescriptor(event, workerFk) {
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
vn-client-risk-index {
|
||||
|
||||
@import "./variables";
|
||||
|
||||
vn-client-balance-index {
|
||||
.totalBox {
|
||||
display: table;
|
||||
float: right;
|
||||
border: $border-thin-light;
|
||||
text-align: left;
|
||||
float: right
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="/ticket/api/TicketDms"
|
||||
link="{ticketFk: $ctrl.$stateParams.id}"
|
||||
filter="::$ctrl.filter"
|
||||
limit="20"
|
||||
data="$ctrl.ticketDms">
|
||||
</vn-crud-model>
|
||||
<vn-vertical>
|
||||
<vn-card pad-large>
|
||||
<vn-vertical>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th field="dmsFk" default-order="DESC" number>Id</vn-th>
|
||||
<vn-th>Type</vn-th>
|
||||
<vn-th>Employee</vn-th>
|
||||
<vn-th>Created</vn-th>
|
||||
<vn-th></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="document in $ctrl.ticketDms">
|
||||
<vn-td number>{{::document.dmsFk}}</vn-td>
|
||||
<vn-td>{{::document.dms.dmsType.name}}</vn-td>
|
||||
<vn-td>
|
||||
<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 | dateTime:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||
<vn-td center>
|
||||
<a
|
||||
target="_blank"
|
||||
href="api/dms/{{::document.dmsFk}}/download?access_token={{::$ctrl.accessToken}}">
|
||||
<vn-icon-button
|
||||
icon="cloud_download"
|
||||
title="{{'Download PDF' | translate}}">
|
||||
</vn-icon-button>
|
||||
</a>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-vertical>
|
||||
<vn-pagination model="model"></vn-pagination>
|
||||
</vn-card>
|
||||
</vn-vertical>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
|
@ -0,0 +1,51 @@
|
|||
import ngModule from '../module';
|
||||
import './style.scss';
|
||||
|
||||
class Controller {
|
||||
constructor($stateParams, $scope, vnToken) {
|
||||
this.$stateParams = $stateParams;
|
||||
this.$ = $scope;
|
||||
this.accessToken = vnToken.token;
|
||||
this.filter = {
|
||||
include: {
|
||||
relation: 'dms',
|
||||
scope: {
|
||||
fields: ['dmsTypeFk', 'workerFk', '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();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$stateParams', '$scope', 'vnToken'];
|
||||
|
||||
ngModule.component('vnTicketDms', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
import './index';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientBalanceIndex', () => {
|
||||
let $componentController;
|
||||
let $scope;
|
||||
let $httpBackend;
|
||||
let $httpParamSerializer;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('client'));
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
|
||||
$componentController = _$componentController_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpParamSerializer = _$httpParamSerializer_;
|
||||
$scope = $rootScope.$new();
|
||||
controller = $componentController('vnClientBalanceIndex', {$scope});
|
||||
}));
|
||||
|
||||
describe('balances() setter', () => {
|
||||
it('should calculate the balance for each line from the oldest date to the newest', () => {
|
||||
controller.getCurrentBalance = jasmine.createSpy(controller, 'getCurrentBalance').and.returnValue(1000);
|
||||
let balances = [
|
||||
{credit: -100, debit: 0},
|
||||
{credit: 0, debit: 300},
|
||||
{credit: 100, debit: 0},
|
||||
{credit: 0, debit: -300}
|
||||
];
|
||||
const params = {filter: controller.filter};
|
||||
let serializedParams = $httpParamSerializer(params);
|
||||
$httpBackend.when('GET', `/client/api/ClientRisks?${serializedParams}`).respond(balances);
|
||||
$httpBackend.expect('GET', `/client/api/ClientRisks?${serializedParams}`);
|
||||
controller.balances = balances;
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.balances[0].balance).toEqual(1000);
|
||||
expect(controller.balances[1].balance).toEqual(900);
|
||||
expect(controller.balances[2].balance).toEqual(600);
|
||||
expect(controller.balances[3].balance).toEqual(700);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
Type: Tipo
|
||||
File management: Gestión documental
|
|
@ -0,0 +1,6 @@
|
|||
vn-client-risk-index {
|
||||
.totalBox {
|
||||
display: table;
|
||||
float: right;
|
||||
}
|
||||
}
|
|
@ -31,3 +31,5 @@ import './request/index';
|
|||
import './request/create';
|
||||
import './log';
|
||||
import './weekly';
|
||||
import './dms';
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class Controller {
|
|||
|
||||
Controller.$inject = ['$scope', '$stateParams'];
|
||||
|
||||
ngModule.component('vnTicketPackageIndex', {
|
||||
ngModule.component('vnTicketPackage', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
{"state": "ticket.card.volume", "icon": "icon-volume"},
|
||||
{"state": "ticket.card.expedition", "icon": "icon-package"},
|
||||
{"state": "ticket.card.service", "icon": "icon-services"},
|
||||
{"state": "ticket.card.package.index", "icon": "icon-bucket"},
|
||||
{"state": "ticket.card.package", "icon": "icon-bucket"},
|
||||
{"state": "ticket.card.tracking.index", "icon": "remove_red_eye"},
|
||||
{"state": "ticket.card.saleChecked", "icon": "assignment"},
|
||||
{"state": "ticket.card.components", "icon": "icon-components"},
|
||||
{"state": "ticket.card.saleTracking", "icon": "assignment"},
|
||||
{"state": "ticket.card.picture", "icon": "image"},
|
||||
{"state": "ticket.card.log", "icon": "history"},
|
||||
{"state": "ticket.card.request.index", "icon": "icon-100"}
|
||||
{"state": "ticket.card.request.index", "icon": "icon-100"},
|
||||
{"state": "ticket.card.dms", "icon": "cloud_download"}
|
||||
],
|
||||
"keybindings": [
|
||||
{"key": "t", "state": "ticket.index"}
|
||||
|
@ -114,13 +115,8 @@
|
|||
}
|
||||
}, {
|
||||
"url" : "/package",
|
||||
"abstract": true,
|
||||
"state": "ticket.card.package",
|
||||
"component": "ui-view"
|
||||
}, {
|
||||
"url" : "/index",
|
||||
"state": "ticket.card.package.index",
|
||||
"component": "vn-ticket-package-index",
|
||||
"component": "vn-ticket-package",
|
||||
"description": "Packages",
|
||||
"params": {
|
||||
"ticket": "$ctrl.ticket"
|
||||
|
@ -216,6 +212,12 @@
|
|||
"state": "ticket.create",
|
||||
"component": "vn-ticket-create",
|
||||
"description": "New ticket"
|
||||
},
|
||||
{
|
||||
"url": "/dms",
|
||||
"state": "ticket.card.dms",
|
||||
"component": "vn-ticket-dms",
|
||||
"description": "File management"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -31,3 +31,4 @@ Reserved: Reservado
|
|||
SMSAvailability: >-
|
||||
Verdnatura le comunica: Pedido {{ticketFk}} día {{created | date: "dd/MM/yyyy"}}.
|
||||
{{notAvailables}} no disponible/s. Disculpe las molestias.
|
||||
Continue anyway?: ¿Continuar de todas formas?
|
Loading…
Reference in New Issue