Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2001_e2e_basicData_log
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2020-03-02 09:58:55 +01:00
commit bfdc7924df
25 changed files with 492 additions and 73 deletions

View File

@ -10,8 +10,7 @@ module.exports = Self => {
type: 'Number',
description: 'The document id',
http: {source: 'path'}
},
{
}, {
arg: 'warehouseId',
type: 'Number',
description: 'The warehouse id'
@ -44,9 +43,9 @@ module.exports = Self => {
}
});
Self.updateFile = async(ctx, id, warehouseId, companyId,
dmsTypeId, reference, description, hasFileAttached, options) => {
Self.updateFile = async(ctx, id, options) => {
const models = Self.app.models;
const args = ctx.args;
let tx;
let myOptions = {};
@ -60,20 +59,20 @@ module.exports = Self => {
}
try {
const hasWriteRole = await models.DmsType.hasWriteRole(ctx, dmsTypeId);
const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId);
if (!hasWriteRole)
throw new UserError(`You don't have enough privileges`);
const dms = await Self.findById(id, null, myOptions);
await dms.updateAttributes({
dmsTypeFk: dmsTypeId,
companyFk: companyId,
warehouseFk: warehouseId,
reference: reference,
description: description
dmsTypeFk: args.dmsTypeId,
companyFk: args.companyId,
warehouseFk: args.warehouseId,
reference: args.reference,
description: args.description
}, myOptions);
if (hasFileAttached)
if (args.hasFileAttached)
await uploadNewFile(ctx, dms, myOptions);
if (tx) await tx.commit();

View File

@ -1901,11 +1901,11 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `c
VALUES
(1, 'Facturas Recibidas', 'recibidas', NULL, NULL, 'invoiceIn'),
(2, 'Doc oficial', 'oficial', NULL, NULL, 'officialDoc'),
(3, 'Laboral', 'laboral', NULL, NULL, 'hhrrData'),
(3, 'Laboral', 'laboral', 37, 37, 'hhrrData'),
(4, 'Albaranes recibidos', 'entradas', NULL, NULL, 'deliveryNote'),
(5, 'Otros', 'otros', 1, 1, 'miscellaneous'),
(6, 'Pruebas', 'pruebas', NULL, NULL, 'tests'),
(7, 'IAE Clientes', 'IAE_Clientes', NULL, NULL, 'economicActivitiesTax'),
(7, 'IAE Clientes', 'IAE_Clientes', 1, 1, 'economicActivitiesTax'),
(8, 'Fiscal', 'fiscal', NULL, NULL, 'fiscal'),
(9, 'Vehiculos', 'vehiculos', NULL, NULL, 'vehicles'),
(10, 'Plantillas', 'plantillas', NULL, NULL, 'templates'),

View File

@ -192,7 +192,7 @@ export default {
},
dms: {
deleteFileButton: 'vn-client-dms-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
firstDocWorker: 'vn-client-dms-index vn-td:nth-child(8) > span',
firstDocWorker: 'vn-client-dms-index vn-td:nth-child(7) > span',
firstDocWorkerDescriptor: '.vn-popover.shown vn-worker-descriptor',
acceptDeleteButton: '.vn-confirm.shown button[response="accept"]'
},
@ -801,7 +801,7 @@ export default {
travelThermograph: {
add: 'vn-travel-thermograph-index vn-float-button[icon="add"]',
thermographID: 'vn-travel-thermograph-create vn-autocomplete[ng-model="$ctrl.dms.thermographId"]',
uploadIcon: 'vn-travel-thermograph-create vn-icon[icon="cloud_upload"]',
uploadIcon: 'vn-travel-thermograph-create vn-icon[icon="attach_file"]',
createdThermograph: 'vn-travel-thermograph-index vn-tbody > vn-tr',
upload: 'vn-travel-thermograph-create button[type=submit]'
},

View File

@ -30,7 +30,7 @@
ng-click="$ctrl.onClear($event)">
</vn-icon>
<vn-icon
icon="cloud_upload"
icon="attach_file"
vn-tooltip="Select a file"
ng-click="$ctrl.openFileSelector()">
</vn-icon>

View File

@ -56,7 +56,16 @@
label="File"
ng-model="$ctrl.dms.files"
on-change="$ctrl.onFileChange($files)"
accept=".pdf, .png, .jpg, .jpeg, application/zip, application/rar, application/x-7z-compressed">
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>

View File

@ -53,11 +53,6 @@
{{::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}}"

View File

@ -51,11 +51,6 @@
{{::document.dms.description}}
</span>
</vn-td>
<vn-td shrink>
<vn-check disabled="true"
field="document.dms.hasFile">
</vn-check>
</vn-td>
<vn-td shrink>
<a target="_blank"
title="{{'Download file' | translate}}"

View File

@ -14,6 +14,10 @@ module.exports = Self => {
type: 'String',
description: 'The thermograph id',
required: true
}, {
arg: 'state',
type: 'String',
required: true
}, {
arg: 'warehouseId',
type: 'Number',
@ -48,13 +52,12 @@ module.exports = Self => {
}
});
Self.createThermograph = async(ctx, id, thermographId) => {
Self.createThermograph = async(ctx, id, thermographId, state) => {
const models = Self.app.models;
const tx = await Self.beginTransaction({});
try {
const options = {transaction: tx};
const travelThermograph = await models.TravelThermograph.findOne({
where: {
thermographFk: thermographId,
@ -70,7 +73,8 @@ module.exports = Self => {
await travelThermograph.updateAttributes({
dmsFk: firstDms.id,
travelFk: id
travelFk: id,
result: state
}, options);
await tx.commit();

View File

@ -0,0 +1,83 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('updateThermograph', {
description: 'updates a file properties or file',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'Number',
description: 'The travel id',
http: {source: 'path'}
}, {
arg: 'thermographId',
type: 'String',
description: 'The thermograph id',
required: true
}, {
arg: 'state',
type: 'String',
required: true
}, {
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: 'hasFileAttached',
type: 'Boolean',
description: 'True if has an attached file'
}],
returns: {
type: 'Object',
root: true
},
http: {
path: `/:id/updateThermograph`,
verb: 'POST'
}
});
Self.updateThermograph = async(ctx, id, thermographId, state) => {
const models = Self.app.models;
const tx = await Self.beginTransaction({});
try {
const options = {transaction: tx};
const travelThermograph = await models.TravelThermograph.findOne({
where: {
thermographFk: thermographId,
travelFk: id
}
}, options);
if (!travelThermograph)
throw new UserError('No valid travel thermograph found');
const dmsFk = travelThermograph.dmsFk;
await models.Dms.updateFile(ctx, dmsFk, options);
await travelThermograph.updateAttributes({
result: state
}, options);
await tx.commit();
return travelThermograph;
} catch (e) {
await tx.rollback();
throw e;
}
};
};

View File

@ -4,4 +4,5 @@ module.exports = Self => {
require('../methods/travel/filter')(Self);
require('../methods/travel/createThermograph')(Self);
require('../methods/travel/deleteThermograph')(Self);
require('../methods/travel/updateThermograph')(Self);
};

View File

@ -11,4 +11,5 @@ import './log';
import './create';
import './thermograph/index/';
import './thermograph/create/';
import './thermograph/edit/';
import './descriptor-popover';

View File

@ -81,6 +81,15 @@
"travel": "$ctrl.travel"
},
"acl": ["buyer"]
}, {
"url" : "/:thermographId/edit",
"state": "travel.card.thermograph.edit",
"component": "vn-travel-thermograph-edit",
"description": "Edit thermograph",
"params": {
"travel": "$ctrl.travel"
},
"acl": ["buyer"]
}
]
}

View File

@ -9,6 +9,35 @@
enctype="multipart/form-data">
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete vn-one
label="Thermograph"
ng-model="$ctrl.dms.thermographId"
url="TravelThermographs"
where="{travelFk: null}"
show-field="thermographFk"
value-field="thermographFk">
</vn-autocomplete>
<vn-textfield vn-one
label="State"
ng-model="$ctrl.dms.state"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<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-autocomplete vn-one
label="Company"
@ -25,28 +54,6 @@
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
url="DmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-textfield vn-one
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one
label="Thermograph"
ng-model="$ctrl.dms.thermographId"
url="TravelThermographs"
where="{travelFk: null}"
show-field="thermographFk"
value-field="thermographFk">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea vn-one vn-focus
label="Description"

View File

@ -8,7 +8,7 @@ class Controller {
this.$translate = $translate;
this.vnApp = vnApp;
this.vnConfig = vnConfig;
this.dms = {files: []};
this.dms = {files: [], state: 'Ok'};
}
get travel() {

View File

@ -0,0 +1,87 @@
<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-autocomplete vn-one
label="Thermograph"
ng-model="$ctrl.thermograph.thermographId"
url="TravelThermographs"
show-field="thermographFk"
value-field="thermographFk"
disabled="true">
</vn-autocomplete>
<vn-textfield vn-one
label="State"
ng-model="$ctrl.thermograph.state"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Reference"
ng-model="$ctrl.thermograph.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one
label="Type"
ng-model="$ctrl.thermograph.dmsTypeId"
url="DmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Company"
ng-model="$ctrl.thermograph.companyId"
url="Companies"
show-field="code"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse"
ng-model="$ctrl.thermograph.warehouseId"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea vn-one vn-focus
label="Description"
ng-model="$ctrl.thermograph.description"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
label="File"
ng-model="$ctrl.thermograph.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-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button ui-sref="travel.card.thermograph.index" label="Cancel"></vn-button>
</vn-button-bar>
</div>
</form>

View File

@ -0,0 +1,98 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
get travel() {
return this._travel;
}
set travel(value) {
this._travel = value;
if (value) {
this.setDefaultParams();
this.getAllowedContentTypes();
}
}
getAllowedContentTypes() {
this.$http.get('TravelThermographs/allowedContentTypes').then(res => {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
});
}
get contentTypesInfo() {
return this.$translate.instant('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
setDefaultParams() {
const filterObj = {include: {relation: 'dms'}};
const filter = encodeURIComponent(JSON.stringify(filterObj));
const path = `TravelThermographs/${this.$params.thermographId}?filter=${filter}`;
this.$http.get(path).then(res => {
const thermograph = res.data && res.data;
this.thermograph = {
thermographId: thermograph.thermographFk,
state: thermograph.result,
reference: thermograph.dms.reference,
warehouseId: thermograph.dms.warehouseFk,
companyId: thermograph.dms.companyFk,
dmsTypeId: thermograph.dms.dmsTypeFk,
description: thermograph.dms.description,
hasFile: thermograph.dms.hasFile,
hasFileAttached: false,
files: []
};
});
}
onSubmit() {
const query = `travels/${this.$params.id}/updateThermograph`;
const options = {
method: 'POST',
url: query,
params: this.thermograph,
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.thermograph.files
};
this.$http(options).then(res => {
if (res) {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.$.watcher.updateOriginalData();
this.$state.go('travel.card.thermograph.index');
}
});
}
onFileChange(files) {
let hasFileAttached = false;
if (files.length > 0)
hasFileAttached = true;
this.$.$applyAsync(() => {
this.thermograph.hasFileAttached = hasFileAttached;
});
}
}
ngModule.component('vnTravelThermographEdit', {
template: require('./index.html'),
controller: Controller,
bindings: {
travel: '<'
}
});

View File

@ -0,0 +1,122 @@
import './index';
import watcher from 'core/mocks/watcher.js';
describe('Worker', () => {
describe('Component vnTravelThermographEdit', () => {
let controller;
let $scope;
let $element;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('travel'));
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$element = angular.element(`<vn-travel-thermograph-edit></vn-travel-thermograph-edit`);
controller = $componentController('vnTravelThermographEdit', {$element, $scope});
controller._travel = {id: 3};
controller.$params = {id: 3, thermographId: 6};
controller.$.watcher = watcher;
}));
describe('travel() setter', () => {
it('should set the travel data and then call setDefaultParams() and getAllowedContentTypes()', () => {
jest.spyOn(controller, 'setDefaultParams');
jest.spyOn(controller, 'getAllowedContentTypes');
controller._travel = undefined;
controller.travel = {
id: 3
};
expect(controller.setDefaultParams).toHaveBeenCalledWith();
expect(controller.travel).toBeDefined();
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
});
});
describe('setDefaultParams()', () => {
it('should perform a GET query and define the dms property on controller', () => {
const thermographId = 6;
const expectedResponse = {
thermographFk: 6,
result: 'Ok',
dms: {
reference: '123456-01',
warehouseFk: 1,
companyFk: 442,
dmsTypeFk: 3,
description: 'Test'
}
};
const filterObj = {include: {relation: 'dms'}};
const filter = encodeURIComponent(JSON.stringify(filterObj));
const query = `TravelThermographs/${thermographId}?filter=${filter}`;
$httpBackend.expect('GET', query).respond(expectedResponse);
controller.setDefaultParams();
$httpBackend.flush();
expect(controller.thermograph).toBeDefined();
expect(controller.thermograph.reference).toEqual('123456-01');
expect(controller.thermograph.dmsTypeId).toEqual(3);
expect(controller.thermograph.state).toEqual('Ok');
});
});
describe('onFileChange()', () => {
it('should set dms hasFileAttached property to true if has any files', () => {
const files = [{id: 1, name: 'MyFile'}];
controller.thermograph = {hasFileAttached: false};
controller.onFileChange(files);
$scope.$apply();
expect(controller.thermograph.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', `TravelThermographs/allowedContentTypes`).respond(expectedResponse);
$httpBackend.expect('GET', `TravelThermographs/allowedContentTypes`);
controller.getAllowedContentTypes();
$httpBackend.flush();
expect(controller.allowedContentTypes).toBeDefined();
expect(controller.allowedContentTypes).toEqual('image/png, image/jpg');
});
});
describe('contentTypesInfo()', () => {
it('should return a description with a list of allowed content types', () => {
controller.allowedContentTypes = ['image/png', 'image/jpg'];
const expectedTypes = controller.allowedContentTypes.join(', ');
const expectedResult = `Allowed content types: ${expectedTypes}`;
jest.spyOn(controller.$translate, 'instant').mockReturnValue(expectedResult);
const result = controller.contentTypesInfo;
expect(result).toEqual(expectedResult);
});
});
describe('onSubmit()', () => {
it('should make an HTTP POST request to save the form data', () => {
jest.spyOn(controller.$.watcher, 'updateOriginalData');
const files = [{id: 1, name: 'MyFile'}];
controller.thermograph = {files};
const serializedParams = $httpParamSerializer(controller.thermograph);
const query = `travels/${controller.$params.id}/updateThermograph?${serializedParams}`;
$httpBackend.expect('POST', query).respond({});
controller.onSubmit();
$httpBackend.flush();
});
});
});
});

View File

@ -0,0 +1,7 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -23,11 +23,11 @@
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="thermograph in $ctrl.travelThermographs">
<vn-td>{{thermograph.thermographFk}} </vn-td>
<vn-td>{{thermograph.temperature}} </vn-td>
<vn-td expand>{{thermograph.result}}</vn-td>
<vn-td>{{thermograph.warehouse.name}}</vn-td>
<vn-td>{{thermograph.created | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td>{{::thermograph.thermographFk}} </vn-td>
<vn-td>{{::thermograph.temperature}} </vn-td>
<vn-td expand>{{::thermograph.result}}</vn-td>
<vn-td>{{::thermograph.warehouse.name}}</vn-td>
<vn-td>{{::thermograph.created | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td shrink>
<a target="_blank"
href="api/dms/{{::thermograph.dmsFk}}/downloadFile?access_token={{::$ctrl.accessToken}}">
@ -37,6 +37,12 @@
</vn-icon-button>
</a>
</vn-td>
<vn-td shrink>
<vn-icon-button ui-sref="travel.card.thermograph.edit({thermographId: {{::thermograph.id}}})"
icon="edit"
title="{{'Edit file' | translate}}">
</vn-icon-button>
</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"

View File

@ -12,6 +12,7 @@ FileDescription: Travel id {{travelId}}
ContentTypesInfo: 'Tipos de archivo permitidos: {{allowedContentTypes}}'
Are you sure you want to continue?: ¿Seguro que quieres continuar?
Add thermograph: Añadir termógrafo
Edit thermograph: Editar termógrafo
Thermograph deleted: Termógrafo eliminado
Thermograph: Termógrafo
Are you sure you want to remove the thermograph?: ¿Seguro que quieres quitar el termógrafo?

View File

@ -5,7 +5,7 @@ module.exports = Self => {
accepts: {
arg: 'id',
type: 'Number',
description: 'The document id',
description: 'The worker document id',
http: {source: 'path'}
},
returns: {

View File

@ -13,10 +13,10 @@
},
"properties": {
"id": {
"type": "Number"
"type": "Number",
"id": true
},
"dmsFk": {
"id": true,
"type": "Number",
"required": true,
"mysql": {

View File

@ -19,7 +19,6 @@
<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 field="created">Created</vn-th>
<vn-th shrink></vn-th>
<vn-th shrink></vn-th>
@ -39,17 +38,13 @@
{{::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>
{{::document.dms.created | date:'dd/MM/yyyy HH:mm'}}
</vn-td>
<vn-td shrink>

View File

@ -58,8 +58,8 @@ class Controller extends Component {
deleteDms(response) {
if (response === 'accept') {
const dmsFk = this.workerDms[this.dmsIndex].dmsFk;
const query = `WorkerDms/${dmsFk}/removeFile`;
const workerDmsId = this.workerDms[this.dmsIndex].id;
const query = `WorkerDms/${workerDmsId}/removeFile`;
this.$http.post(query).then(() => {
this.$.model.remove(this.dmsIndex);
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));

View File

@ -22,15 +22,15 @@ describe('Worker', () => {
describe('deleteDms()', () => {
it('should make an HTTP Post query', () => {
const dmsId = 4;
const workerDmsId = 1;
const dmsIndex = 0;
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$.model, 'remove');
controller.workerDms = [{dmsFk: 4}];
controller.workerDms = [{id: 1, dmsFk: 4}];
controller.dmsIndex = dmsIndex;
$httpBackend.when('POST', `WorkerDms/${dmsId}/removeFile`).respond({});
$httpBackend.expect('POST', `WorkerDms/${dmsId}/removeFile`);
$httpBackend.when('POST', `WorkerDms/${workerDmsId}/removeFile`).respond({});
$httpBackend.expect('POST', `WorkerDms/${workerDmsId}/removeFile`);
controller.deleteDms('accept');
$httpBackend.flush();