Merge branch 'dev' into 5691-route.index_optimizar
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Vicent Llopis 2023-06-14 10:57:58 +00:00
commit 9e89167d27
9 changed files with 175 additions and 176 deletions

View File

@ -2,8 +2,9 @@ const models = require('vn-loopback/server/server').models;
describe('docuware upload()', () => { describe('docuware upload()', () => {
const userId = 9; const userId = 9;
const ticketId = 10; const ticketIds = [10];
const ctx = { const ctx = {
args: {ticketIds},
req: { req: {
getLocale: () => { getLocale: () => {
return 'en'; return 'en';
@ -27,7 +28,7 @@ describe('docuware upload()', () => {
let error; let error;
try { try {
await models.Docuware.upload(ctx, ticketId, fileCabinetName); await models.Docuware.upload(ctx, ticketIds, fileCabinetName);
} catch (e) { } catch (e) {
error = e.message; error = e.message;
} }

View File

@ -3,34 +3,34 @@ const axios = require('axios');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('upload', { Self.remoteMethodCtx('upload', {
description: 'Upload an docuware PDF', description: 'Upload docuware PDFs',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [ accepts: [
{ {
arg: 'id', arg: 'ticketIds',
type: 'number', type: ['number'],
description: 'The ticket id', description: 'The ticket ids',
http: {source: 'path'} required: true
}, },
{ {
arg: 'fileCabinet', arg: 'fileCabinet',
type: 'string', type: 'string',
description: 'The file cabinet' description: 'The file cabinet',
}, required: true
{
arg: 'dialog',
type: 'string',
description: 'The dialog'
} }
], ],
returns: [], returns: {
type: 'object',
root: true
},
http: { http: {
path: `/:id/upload`, path: `/upload`,
verb: 'POST' verb: 'POST'
} }
}); });
Self.upload = async function(ctx, id, fileCabinet) { Self.upload = async function(ctx, ticketIds, fileCabinet) {
delete ctx.args.ticketIds;
const models = Self.app.models; const models = Self.app.models;
const action = 'store'; const action = 'store';
@ -38,104 +38,114 @@ module.exports = Self => {
const fileCabinetId = await Self.getFileCabinet(fileCabinet); const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
// get delivery note const uploaded = [];
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { for (id of ticketIds) {
id, // get delivery note
type: 'deliveryNote' ctx.args.id = id;
}); const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
id,
// get ticket data type: 'deliveryNote'
const ticket = await models.Ticket.findById(id, {
include: [{
relation: 'client',
scope: {
fields: ['id', 'socialName', 'fi']
}
}]
});
// upload file
const templateJson = {
'Fields': [
{
'FieldName': 'N__ALBAR_N',
'ItemElementName': 'string',
'Item': id,
},
{
'FieldName': 'CIF_PROVEEDOR',
'ItemElementName': 'string',
'Item': ticket.client().fi,
},
{
'FieldName': 'CODIGO_PROVEEDOR',
'ItemElementName': 'string',
'Item': ticket.client().id,
},
{
'FieldName': 'NOMBRE_PROVEEDOR',
'ItemElementName': 'string',
'Item': ticket.client().socialName,
},
{
'FieldName': 'FECHA_FACTURA',
'ItemElementName': 'date',
'Item': ticket.shipped,
},
{
'FieldName': 'TOTAL_FACTURA',
'ItemElementName': 'Decimal',
'Item': ticket.totalWithVat,
},
{
'FieldName': 'ESTADO',
'ItemElementName': 'string',
'Item': 'Pendiente procesar',
},
{
'FieldName': 'FIRMA_',
'ItemElementName': 'string',
'Item': 'Si',
},
{
'FieldName': 'FILTRO_TABLET',
'ItemElementName': 'string',
'Item': 'Tablet1',
}
]
};
if (process.env.NODE_ENV != 'production')
throw new UserError('Action not allowed on the test environment');
// delete old
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
if (docuwareFile) {
const deleteJson = {
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
};
const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
await axios.put(deleteUri, deleteJson, options.headers);
}
const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
const FormData = require('form-data');
const data = new FormData();
data.append('document', JSON.stringify(templateJson), 'schema.json');
data.append('file[]', deliveryNote[0], 'file.pdf');
const uploadOptions = {
headers: {
'Content-Type': 'multipart/form-data',
'X-File-ModifiedDate': Date.vnNew(),
'Cookie': options.headers.headers.Cookie,
...data.getHeaders()
},
};
return await axios.post(uploadUri, data, uploadOptions)
.catch(() => {
throw new UserError('Failed to upload file');
}); });
// get ticket data
const ticket = await models.Ticket.findById(id, {
include: [{
relation: 'client',
scope: {
fields: ['id', 'name', 'fi']
}
}]
});
// upload file
const templateJson = {
'Fields': [
{
'FieldName': 'N__ALBAR_N',
'ItemElementName': 'string',
'Item': id,
},
{
'FieldName': 'CIF_PROVEEDOR',
'ItemElementName': 'string',
'Item': ticket.client().fi,
},
{
'FieldName': 'CODIGO_PROVEEDOR',
'ItemElementName': 'string',
'Item': ticket.client().id,
},
{
'FieldName': 'NOMBRE_PROVEEDOR',
'ItemElementName': 'string',
'Item': ticket.client().name + ' - ' + id,
},
{
'FieldName': 'FECHA_FACTURA',
'ItemElementName': 'date',
'Item': ticket.shipped,
},
{
'FieldName': 'TOTAL_FACTURA',
'ItemElementName': 'Decimal',
'Item': ticket.totalWithVat,
},
{
'FieldName': 'ESTADO',
'ItemElementName': 'string',
'Item': 'Pendiente procesar',
},
{
'FieldName': 'FIRMA_',
'ItemElementName': 'string',
'Item': 'Si',
},
{
'FieldName': 'FILTRO_TABLET',
'ItemElementName': 'string',
'Item': 'Tablet1',
}
]
};
if (process.env.NODE_ENV != 'production')
throw new UserError('Action not allowed on the test environment');
// delete old
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
if (docuwareFile) {
const deleteJson = {
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
};
const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
await axios.put(deleteUri, deleteJson, options.headers);
}
const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
const FormData = require('form-data');
const data = new FormData();
data.append('document', JSON.stringify(templateJson), 'schema.json');
data.append('file[]', deliveryNote[0], 'file.pdf');
const uploadOptions = {
headers: {
'Content-Type': 'multipart/form-data',
'X-File-ModifiedDate': Date.vnNew(),
'Cookie': options.headers.headers.Cookie,
...data.getHeaders()
},
};
try {
await axios.post(uploadUri, data, uploadOptions);
} catch (err) {
const $t = ctx.req.__;
const message = $t('Failed to upload delivery note', {id});
if (uploaded.length)
await models.TicketTracking.setDelivered(ctx, uploaded);
throw new UserError(message);
}
uploaded.push(id);
}
return models.TicketTracking.setDelivered(ctx, ticketIds);
}; };
}; };

View File

@ -174,5 +174,6 @@
"A claim with that sale already exists": "A claim with that sale already exists", "A claim with that sale already exists": "A claim with that sale already exists",
"Pass expired": "The password has expired, change it from Salix", "Pass expired": "The password has expired, change it from Salix",
"Can't transfer claimed sales": "Can't transfer claimed sales", "Can't transfer claimed sales": "Can't transfer claimed sales",
"Invalid quantity": "Invalid quantity" "Invalid quantity": "Invalid quantity",
"Failed to upload delivery note": "Error to upload delivery note {{id}}"
} }

View File

@ -258,7 +258,7 @@
"App name does not exist": "El nombre de aplicación no es válido", "App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo", "Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9", "Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload file": "Error al subir archivo", "Failed to upload delivery note": "Error al subir albarán {{id}}",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe", "The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar", "It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo", "It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",

View File

@ -326,14 +326,8 @@ class Controller extends Section {
if (!force) if (!force)
return this.$.pdfToTablet.show(); return this.$.pdfToTablet.show();
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'}) return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds: [this.id]})
.then(() => { .then(() => {
this.$.balanceCreate.amountPaid = this.ticket.totalWithVat;
this.$.balanceCreate.clientFk = this.ticket.clientFk;
this.$.balanceCreate.description = 'Albaran: ';
this.$.balanceCreate.description += this.ticket.id;
this.$.balanceCreate.show();
this.vnApp.showSuccess(this.$t('PDF sent!')); this.vnApp.showSuccess(this.$t('PDF sent!'));
}); });
} }

View File

@ -304,17 +304,15 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
expect(controller.$.pdfToTablet.show).toHaveBeenCalled(); expect(controller.$.pdfToTablet.show).toHaveBeenCalled();
}); });
it('should make a query and show balance create', () => { it('should make a query', () => {
controller.$.balanceCreate = {show: () => {}}; controller.$.balanceCreate = {show: () => {}};
jest.spyOn(controller.$.balanceCreate, 'show');
jest.spyOn(controller.vnApp, 'showSuccess'); jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.whenPOST(`Docuwares/${ticket.id}/upload`).respond(true); $httpBackend.whenPOST(`Docuwares/upload`).respond(true);
controller.uploadDocuware(true); controller.uploadDocuware(true);
$httpBackend.flush(); $httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled(); expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.$.balanceCreate.show).toHaveBeenCalled();
}); });
}); });

View File

@ -9,7 +9,7 @@
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th shrink> <vn-th shrink>
<vn-multi-check <vn-multi-check
model="model"> model="model">
</vn-multi-check> </vn-multi-check>
</vn-th> </vn-th>
@ -33,7 +33,7 @@
class="clickable vn-tr search-result" class="clickable vn-tr search-result"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})"> ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<vn-td> <vn-td>
<vn-check <vn-check
ng-model="ticket.checked" ng-model="ticket.checked"
vn-click-stop> vn-click-stop>
</vn-check> </vn-check>
@ -109,7 +109,7 @@
class="link"> class="link">
{{::ticket.refFk}} {{::ticket.refFk}}
</span> </span>
<span <span
ng-show="!ticket.refFk" ng-show="!ticket.refFk"
class="chip {{$ctrl.stateColor(ticket)}}"> class="chip {{$ctrl.stateColor(ticket)}}">
{{ticket.state}} {{ticket.state}}
@ -132,8 +132,8 @@
<vn-td actions> <vn-td actions>
<vn-icon-button <vn-icon-button
vn-anchor="::{ vn-anchor="::{
state: 'ticket.card.sale', state: 'ticket.card.sale',
params: {id: ticket.id}, params: {id: ticket.id},
target: '_blank' target: '_blank'
}" }"
vn-tooltip="Go to lines" vn-tooltip="Go to lines"
@ -152,21 +152,21 @@
</vn-data-viewer> </vn-data-viewer>
<div fixed-bottom-right> <div fixed-bottom-right>
<vn-vertical style="align-items: center;"> <vn-vertical style="align-items: center;">
<vn-button class="round sm vn-mb-sm" <vn-button class="round vn-mb-sm"
icon="print" icon="install_mobile"
ng-show="$ctrl.totalChecked > 0" ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.setDelivered()" ng-click="$ctrl.sendDocuware()"
vn-tooltip="Set as delivered and open delivery note(s)" vn-tooltip="Set as delivered and open delivery note(s)"
tooltip-position="left"> tooltip-position="left">
</vn-button> </vn-button>
<vn-button class="round sm vn-mb-sm" <vn-button class="round vn-mb-sm"
icon="icon-recovery" icon="icon-recovery"
ng-show="$ctrl.totalChecked > 0" ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.openBalanceDialog()" ng-click="$ctrl.openBalanceDialog()"
vn-tooltip="Payment on account..." vn-tooltip="Payment on account..."
tooltip-position="left"> tooltip-position="left">
</vn-button> </vn-button>
<vn-button class="round sm vn-mb-sm" <vn-button class="round vn-mb-sm"
icon="icon-invoice" icon="icon-invoice"
ng-click="makeInvoiceConfirmation.show()" ng-click="makeInvoiceConfirmation.show()"
ng-show="$ctrl.totalChecked > 0" ng-show="$ctrl.totalChecked > 0"
@ -193,16 +193,16 @@
<vn-client-descriptor-popover <vn-client-descriptor-popover
vn-id="clientDescriptor"> vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor"> vn-id="workerDescriptor">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<vn-zone-descriptor-popover <vn-zone-descriptor-popover
vn-id="zoneDescriptor"> vn-id="zoneDescriptor">
</vn-zone-descriptor-popover> </vn-zone-descriptor-popover>
<vn-client-balance-create <vn-client-balance-create
vn-id="balanceCreateDialog"> vn-id="balanceCreateDialog">
</vn-client-balance-create> </vn-client-balance-create>
<vn-invoice-out-descriptor-popover <vn-invoice-out-descriptor-popover
vn-id="invoiceOutDescriptor"> vn-id="invoiceOutDescriptor">
</vn-invoice-out-descriptor-popover> </vn-invoice-out-descriptor-popover>
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model" <vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
@ -213,22 +213,22 @@
ng-click="contextmenu.filterBySelection()"> ng-click="contextmenu.filterBySelection()">
Filter by selection Filter by selection
</vn-item> </vn-item>
<vn-item translate <vn-item translate
ng-if="contextmenu.isFilterAllowed()" ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()"> ng-click="contextmenu.excludeSelection()">
Exclude selection Exclude selection
</vn-item> </vn-item>
<vn-item translate <vn-item translate
ng-if="contextmenu.isFilterAllowed()" ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()"> ng-click="contextmenu.removeFilter()">
Remove filter Remove filter
</vn-item> </vn-item>
<vn-item translate <vn-item translate
ng-click="contextmenu.removeAllFilters()"> ng-click="contextmenu.removeAllFilters()">
Remove all filters Remove all filters
</vn-item> </vn-item>
<vn-item translate <vn-item translate
ng-if="contextmenu.isActionAllowed()" ng-if="contextmenu.isActionAllowed()"
ng-click="contextmenu.copyValue()"> ng-click="contextmenu.copyValue()">
Copy value Copy value
</vn-item> </vn-item>
@ -241,4 +241,4 @@
on-accept="$ctrl.makeInvoice()" on-accept="$ctrl.makeInvoice()"
question="{{$ctrl.confirmationMessage}}" question="{{$ctrl.confirmationMessage}}"
message="Invoice selected tickets"> message="Invoice selected tickets">
</vn-confirm> </vn-confirm>

View File

@ -9,28 +9,23 @@ export default class Controller extends Section {
this.vnReport = vnReport; this.vnReport = vnReport;
} }
setDelivered() { sendDocuware() {
const checkedTickets = this.checked; const checkedTickets = this.checked;
let ids = []; let ticketIds = [];
for (let ticket of checkedTickets) for (let ticket of checkedTickets)
ids.push(ticket.id); ticketIds.push(ticket.id);
this.$http.post('TicketTrackings/setDelivered', ids).then(res => { return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds})
let state = res.data; .then(res => {
for (let ticket of checkedTickets) { let state = res.data;
ticket.stateFk = state.id; for (let ticket of checkedTickets) {
ticket.state = state.name; ticket.stateFk = state.id;
ticket.alertLevel = state.alertLevel; ticket.state = state.name;
ticket.alertLevelCode = state.code; ticket.alertLevel = state.alertLevel;
} ticket.alertLevelCode = state.code;
this.openDeliveryNotes(ids); }
}); });
}
openDeliveryNotes(ids) {
for (let id of ids)
this.vnReport.show(`Tickets/${id}/delivery-note-pdf`);
} }
openBalanceDialog() { openBalanceDialog() {

View File

@ -12,12 +12,14 @@ describe('Component vnTicketIndex', () => {
id: 2, id: 2,
clientFk: 1, clientFk: 1,
checked: true, checked: true,
totalWithVat: 20.5 totalWithVat: 20.5,
stateFk: 1
}, { }, {
id: 3, id: 3,
clientFk: 1, clientFk: 1,
checked: true, checked: true,
totalWithVat: 30 totalWithVat: 30,
stateFk: 1
}]; }];
beforeEach(ngModule('ticket')); beforeEach(ngModule('ticket'));
@ -86,18 +88,16 @@ describe('Component vnTicketIndex', () => {
}); });
}); });
describe('setDelivered()/openDeliveryNotes()', () => { describe('sendDocuware()', () => {
it('should perform a post to setDelivered and open tabs with the delivery notes', () => { it('should perform a post to sendDocuware and change tickets state', () => {
controller.$.model = {data: tickets, refresh: () => {}}; controller.$.model = {data: tickets, refresh: () => {}};
const newState = {id: 2};
$window.open = jest.fn(); $httpBackend.expect('POST', 'Docuwares/upload').respond({id: newState.id});
controller.sendDocuware();
$httpBackend.expect('POST', 'TicketTrackings/setDelivered').respond('ok');
controller.setDelivered();
$httpBackend.flush(); $httpBackend.flush();
expect($window.open).toHaveBeenCalledWith(`api/Tickets/${tickets[1].id}/delivery-note-pdf`); expect(controller.$.model.data[1].stateFk).toEqual(newState.id);
expect($window.open).toHaveBeenCalledWith(`api/Tickets/${tickets[2].id}/delivery-note-pdf`);
}); });
}); });