Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5325-defaultSearch-SmartTable

This commit is contained in:
Carlos Satorres 2023-06-14 17:03:09 +02:00
commit 9cdc914a25
10 changed files with 210 additions and 196 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

@ -1,6 +1,21 @@
<vn-auto-search <vn-auto-search
model="model"> model="model">
</vn-auto-search> </vn-auto-search>
<vn-crud-model
auto-load="true"
url="AgencyModes"
data="agencyModes">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Vehicles"
data="vehicles">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Workers/activeWithInheritedRole"
data="activeWithInheritedRole">
</vn-crud-model>
<div class="vn-w-xl"> <div class="vn-w-xl">
<vn-card> <vn-card>
<smart-table <smart-table
@ -83,7 +98,7 @@
<td> <td>
<vn-autocomplete <vn-autocomplete
ng-model="route.workerFk" ng-model="route.workerFk"
url="Workers/activeWithInheritedRole" data="activeWithInheritedRole"
show-field="nickname" show-field="nickname"
search-function="{firstName: $search}" search-function="{firstName: $search}"
value-field="id" value-field="id"
@ -98,7 +113,7 @@
<td expand> <td expand>
<vn-autocomplete <vn-autocomplete
ng-model="route.agencyModeFk" ng-model="route.agencyModeFk"
url="AgencyModes" data="agencyModes"
show-field="name" show-field="name"
value-field="id" value-field="id"
on-change="$ctrl.updateAttributes(route)" on-change="$ctrl.updateAttributes(route)"
@ -108,7 +123,7 @@
<td expand> <td expand>
<vn-autocomplete <vn-autocomplete
ng-model="route.vehicleFk" ng-model="route.vehicleFk"
url="Vehicles" data="vehicles"
show-field="numberPlate" show-field="numberPlate"
value-field="id" value-field="id"
on-change="$ctrl.updateAttributes(route)" on-change="$ctrl.updateAttributes(route)"

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

@ -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"

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`);
}); });
}); });