Merge branch 'dev' into 3002-backend_transactions
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2021-06-29 10:23:47 +00:00
commit 01a9154015
18 changed files with 166 additions and 34 deletions

View File

@ -0,0 +1,4 @@
DELETE FROM `salix`.`ACL` WHERE id = 189;
DELETE FROM `salix`.`ACL` WHERE id = 188;
UPDATE `salix`.`ACL` tdms SET tdms.accessType = '*'
WHERE tdms.id = 165;

View File

@ -1 +0,0 @@
Delete me

View File

@ -646,6 +646,12 @@ export default {
saveServiceButton: 'button[type=submit]',
saveServiceTypeButton: '.vn-dialog.shown tpl-buttons > button'
},
ticketDms: {
import: 'vn-ticket-dms-index vn-button[icon="file_copy"]',
document: 'vn-autocomplete[ng-model="$ctrl.dmsId"]',
saveImport: 'button[response="accept"]',
anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr'
},
createStateView: {
state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
worker: 'vn-autocomplete[ng-model="$ctrl.workerFk"]',

View File

@ -0,0 +1,49 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket DMS path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.dms.index');
});
afterAll(async() => {
await browser.close();
});
it('should import a document', async() => {
await page.waitToClick(selectors.ticketDms.import);
await page.autocompleteSearch(selectors.ticketDms.document, '1');
await page.waitToClick(selectors.ticketDms.saveImport);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should check there's a listed document now`, async() => {
const result = await page.countElement(selectors.ticketDms.anyDocument);
expect(result).toEqual(1);
});
it('should attempt to import an existing document on this ticket', async() => {
await page.waitToClick(selectors.ticketDms.import);
await page.autocompleteSearch(selectors.ticketDms.document, '1');
await page.waitToClick(selectors.ticketDms.saveImport);
const message = await page.waitForSnackbar();
expect(message.text).toContain('This document already exists on this ticket');
});
it(`should check there's still one document`, async() => {
const result = await page.countElement(selectors.ticketDms.anyDocument);
expect(result).toEqual(1);
});
});

View File

@ -98,5 +98,6 @@
"Client assignment has changed": "I did change the salesperson ~*\"<{{previousWorkerName}}>\"*~ by *\"<{{currentWorkerName}}>\"* from the client [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "None",
"error densidad = 0": "error densidad = 0",
"nickname": "nickname"
"nickname": "nickname",
"This document already exists on this ticket": "This document already exists on this ticket"
}

View File

@ -181,5 +181,6 @@
"This specie already exist": "Esta especie ya existe",
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "Ninguno",
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada"
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada",
"This document already exists on this ticket": "Este documento ya existe en el ticket"
}

View File

@ -33,14 +33,14 @@
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th class="icon-field" shrink></vn-th>
<vn-th class="icon-field"></vn-th>
<vn-th field="nickname">Client</vn-th>
<vn-th field="salesPersonFk" class="expendable" shrink>Salesperson</vn-th>
<vn-th field="shipped" shrink-date>Date</vn-th>
<vn-th>Hour</vn-th>
<vn-th field="zoneHour" shrink>Closure</vn-th>
<vn-th field="provinceFk" class="expendable">Province</vn-th>
<vn-th field="stateFk" shrink>State</vn-th>
<vn-th field="stateFk">State</vn-th>
<vn-th field="zoneFk">Zone</vn-th>
<vn-th shrink>Total</vn-th>
<vn-th></vn-th>
@ -50,7 +50,7 @@
<a ng-repeat="ticket in model.data"
class="clickable vn-tr search-result"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})" target="_blank">
<vn-td class="icon-field" shrink>
<vn-td class="icon-field">
<vn-icon
ng-show="::ticket.isTaxDataChecked === 0"
translate-attr="{title: 'No verified data'}"
@ -88,7 +88,7 @@
icon="icon-components">
</vn-icon>
</vn-td>
<vn-td>
<vn-td name="nickname">
<span
title="{{::ticket.nickname}}"
vn-click-stop="clientDescriptor.show($event, ticket.clientFk)"
@ -112,7 +112,7 @@
<vn-td shrink>{{::ticket.shipped | date: 'HH:mm'}}</vn-td>
<vn-td shrink>{{::ticket.zoneLanding | date: 'HH:mm'}}</vn-td>
<vn-td class="expendable">{{::ticket.province}}</vn-td>
<vn-td class="expendable" shrink>
<vn-td class="expendable">
<span
ng-show="::ticket.refFk"
title="{{::ticket.refFk}}"

View File

@ -11,11 +11,13 @@ vn-monitor-sales-tickets {
vn-th.icon-field *,
vn-td.icon-field,
vn-td.icon-field * {
padding: 0
padding: 0;
max-width: 50px
}
vn-th[field="nickname"] {
width: 250px
vn-th[field="nickname"],
vn-td[name="nickname"] {
min-width: 250px
}
vn-td.icon-field > vn-icon {

View File

@ -48,12 +48,12 @@ module.exports = Self => {
scope: {
fields: ['id', 'm3', 'numberPlate']
}
}
],
},
]
};
summary.route = await Self.app.models.Route.findOne(filter);
summary.tickets = await Self.app.models.Route.getTickets({id: id});
summary.tickets = await Self.app.models.Route.getTickets({id: id, order: 'priority ASC'});
return summary;
};

View File

@ -133,7 +133,7 @@ describe('Component vnRouteIndex', () => {
});
describe('insert()', () => {
it('should make a HTTP patch query and then call both refresh and showSuccess methods', () => {
it('should perform a HTTP patch query and then call both refresh and showSuccess methods', () => {
jest.spyOn(controller.$.model, 'refresh').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');

View File

@ -131,6 +131,7 @@ class Controller extends Section {
.then(() => {
this.$.model.data = this.$.model.data.concat(tickets);
this.vnApp.showSuccess(this.$t('Data saved!'));
this.updateVolume();
});
}
@ -157,8 +158,7 @@ class Controller extends Section {
const query = `Routes/${this.route.id}/insertTicket`;
return this.$http.patch(query, {ticketId}).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.model.refresh();
this.card.reload();
this.updateVolume();
}).catch(error => {
if (error.status == 404)
return this.vnApp.showError(this.$t('Ticket not found'));

View File

@ -129,19 +129,20 @@ describe('Route', () => {
describe('removeTicketFromRoute()', () => {
it('should perform a patch query then call showSuccess and updateVolume methods', () => {
jest.spyOn(controller, 'updateVolume').mockReturnThis();
controller.$params = {id: 1101};
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$.model, 'remove');
let ticketId = 1;
controller.selectedTicket = ticketId;
$httpBackend.whenPOST(`Routes/${controller.$params.id}/updateVolume`).respond(200);
$httpBackend.expectPATCH(`Tickets/${ticketId}/`).respond('ok');
controller.removeTicketFromRoute();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Ticket removed from route');
expect(controller.updateVolume).toHaveBeenCalledWith();
});
});
@ -196,9 +197,8 @@ describe('Route', () => {
describe('setTicketsRoute()', () => {
it('should perform a POST query to add tickets to the route', () => {
controller.$.model = {data: [
{id: 1, checked: false}
]};
controller.$params = {id: 1101};
controller.$.model.data = [{id: 1, checked: false}];
const existingTicket = controller.$.model.data[0];
@ -217,7 +217,8 @@ describe('Route', () => {
{id: 5}
];
$httpBackend.expectPOST(`Tickets/crud`).respond();
$httpBackend.whenPOST(`Routes/${controller.$params.id}/updateVolume`).respond(200);
$httpBackend.expectPOST('Tickets/crud').respond();
controller.setTicketsRoute();
$httpBackend.flush();
@ -273,11 +274,15 @@ describe('Route', () => {
describe('insert()', () => {
it('should make a HTTP patch query and then call both refresh and showSuccess methods', () => {
controller.$params = {id: 1101};
jest.spyOn(controller.$.model, 'refresh').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');
const ticketId = 11;
const data = {ticketId};
$httpBackend.whenPOST(`Routes/${controller.$params.id}/updateVolume`).respond(200);
$httpBackend.expect('PATCH', `Routes/1/insertTicket`, data).respond();
controller.insert(ticketId);
$httpBackend.flush();

View File

@ -32,7 +32,9 @@ module.exports = Self => {
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant', myOptions);
const isProductionBoss = await Self.app.models.Account.hasRole(userId, 'productionBoss', myOptions);
const isValidRole = isSalesAssistant || isProductionBoss;
const isBuyer = await Self.app.models.Account.hasRole(userId, 'buyer', myOptions);
const isValidRole = isSalesAssistant || isProductionBoss || isBuyer;
let alertLevel = state ? state.alertLevel : null;
let ticket = await Self.app.models.Ticket.findById(id, {

View File

@ -1,3 +1,11 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
require('../methods/ticket-dms/removeFile')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError('This document already exists on this ticket');
return err;
});
};

View File

@ -12,7 +12,7 @@
},
"properties": {
"dmsFk": {
"type": "Number",
"type": "number",
"id": true,
"required": true
}

View File

@ -96,18 +96,48 @@
</vn-table>
</vn-card>
</vn-data-viewer>
<div fixed-bottom-right>
<vn-vertical style="align-items: center;">
<vn-button class="round sm vn-mb-sm"
icon="file_copy"
ng-click="$ctrl.$.dmsImportDialog.show()"
vn-tooltip="Import from existing"
tooltip-position="left">
</vn-button>
<a ui-sref="ticket.card.dms.create" vn-bind="+">
<vn-button class="round md vn-mb-sm"
icon="publish"
vn-tooltip="Upload file"
tooltip-position="left">
</vn-button>
</a>
</vn-vertical>
</div>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<a ui-sref="ticket.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-accept="$ctrl.deleteDms($data)">
</vn-confirm>
<vn-dialog
message="Select document id"
vn-id="dmsImportDialog"
on-accept="$ctrl.importDms()">
<tpl-body>
<vn-autocomplete
label="Document"
ng-model="$ctrl.dmsId"
url="Dms"
show-field="id"
vn-focus>
</vn-autocomplete>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -55,6 +55,28 @@ class Controller extends Section {
downloadFile(dmsId) {
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
}
importDms() {
try {
if (!this.dmsId)
throw new Error(`The document indentifier can't be empty`);
const data = {
ticketFk: this.$params.id,
dmsFk: this.dmsId
};
this.$http.post('ticketDms', data).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.dmsId = null;
this.$.model.refresh();
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
}
Controller.$inject = ['$element', '$scope', 'vnFile'];

View File

@ -7,3 +7,6 @@ Generate identifier for original file: Generar identificador para archivo origin
ContentTypesInfo: 'Tipos de archivo permitidos: {{allowedContentTypes}}'
Are you sure you want to continue?: ¿Seguro que quieres continuar?
File management: Gestión documental
Select document id: Introduzca id de gestion documental
Import from existing: Importar desde existente
The document indentifier can't be empty: El número de documento no puede estar vacío