#5926 - Worker/PDA docuware #2482

Open
jsegarra wants to merge 40 commits from 5926_pda_worker_docuware into dev
26 changed files with 722 additions and 161 deletions

View File

@ -1,6 +1,67 @@
const axios = require('axios');
const env = process.env.NODE_ENV;
const {existsSync} = require('fs');
module.exports = Self => {
/**
* Returns templateJSON
*
* @param {object} config - The path for real config file
* @return {boolean} - The template parse
*/
Self.hasDeviceReady = config => {
let isDeviceConfigured = false;
if (!config)
config = existsSync(`../docuware.${env}.json`) ?? {};
Review

Esto que es?

Esto que es?
Review

Es una funcionalidad que he hecho muy similar a print.development.json para que si existe algún archivo de configuración de tablet.
Esto es útil para hacer pruebas reales con la tablet 4 o con datos falsos

Es una funcionalidad que he hecho muy similar a print.development.json para que si existe algún archivo de configuración de tablet. Esto es útil para hacer pruebas reales con la tablet 4 o con datos falsos
isDeviceConfigured = !!config?.device;
return isDeviceConfigured;
};
/**
* Returns templateJSON
*
* @param {string} fields - The config as template upload
* @return {object} - The template parse
*/
Self.buildTemplateJSON = fields => {
const templateJson = {
'Fields': []
};
templateJson.Fields = Object.keys(fields).map(fieldName => ({
'FieldName': fieldName,
'ItemElementName': fields[fieldName].type,
'Item': fields[fieldName].value
}));
return templateJson;
};
/**
* Returns upload options
*
* @param {string} value - The document value
* @param {Object} template - The config as template upload
* @param {string} fileName - The document fileName with extension
* @param {object} options - The options
* @return {object} - The options with headers
*/
Self.uploadOptions = async(value, template, fileName = '', options) => {
const FormData = require('form-data');
const data = new FormData();
const docuwareOptions = options ?? await Self.getOptions();
const templateJson = Self.buildTemplateJSON(template);
data.append('document', JSON.stringify(templateJson), 'schema.json');
data.append('file[]', value, fileName);
const uploadOptions = {
headers: {
'Content-Type': 'multipart/form-data',
'X-File-ModifiedDate': Date.vnNew(),
'Cookie': docuwareOptions.headers.headers.Cookie,
...data.getHeaders()
},
};
return {data, uploadOptions};
};
/**
* Returns basic headers
*
@ -23,6 +84,27 @@ module.exports = Self => {
};
};
/**
* Returns the docuware id
*
* @param {object} filter - The filter to use in findOne method
* @return {object} - The doware record
*/
Self.getDocuware = async filter => {
return await Self.app.models.Docuware.findOne(filter);
};
/**
* Returns the base url
*
* @param {object} options - The docuware options
* @param {string} fileCabinetId - The fileCabinetId to use in findOne method
* @param {string} model - The model to use in findOne method
* @return {string} - The doware record
*/
Self.baseURL = (options, fileCabinetId, model) => {
return `${options.url}/FileCabinets/${fileCabinetId}/${model}`;
};
/**
* Returns the dialog id
*
@ -32,10 +114,10 @@ module.exports = Self => {
* @return {number} - The fileCabinet id
*/
Self.getDialog = async(code, action, fileCabinetId) => {
if (!process.env.NODE_ENV)
if (!env && !Self.hasDeviceReady())
return Math.floor(Math.random() + 100);
const docuwareInfo = await Self.app.models.Docuware.findOne({
const docuwareInfo = await Self.getDocuware({
where: {
code,
action
@ -45,7 +127,7 @@ module.exports = Self => {
const options = await Self.getOptions();
const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers);
const response = await axios.get(Self.baseURL(options, fileCabinetId, 'dialog'), options.headers);
const dialogs = response.data.Dialog;
const dialogId = dialogs.find(dialogs => dialogs.DisplayName === docuwareInfo.dialogName).Id;
@ -59,7 +141,7 @@ module.exports = Self => {
* @return {number} - The fileCabinet id
*/
Self.getFileCabinet = async code => {
if (!process.env.NODE_ENV)
if (!env && !Self.hasDeviceReady())
return Math.floor(Math.random() + 100);
const options = await Self.getOptions();
@ -85,14 +167,14 @@ module.exports = Self => {
* @return {object} - The data
*/
Self.get = async(code, filter, parse) => {
if (!process.env.NODE_ENV) return;
if (!env && !Self.hasDeviceReady()) return;
const options = await Self.getOptions();
const fileCabinetId = await Self.getFileCabinet(code);
const dialogId = await Self.getDialog(code, 'find', fileCabinetId);
const data = await axios.post(
`${options.url}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`,
`${Self.baseURL(options, fileCabinetId)}/Query/DialogExpression?dialogId=${dialogId}`,
filter,
options.headers
);
@ -108,7 +190,7 @@ module.exports = Self => {
* @return {object} - The data
*/
Self.getById = async(code, id, parse) => {
if (!process.env.NODE_ENV) return;
if (!env && !Self.hasDeviceReady()) return;
const docuwareInfo = await Self.app.models.Docuware.findOne({
fields: ['findById'],
@ -129,6 +211,26 @@ module.exports = Self => {
return Self.get(code, filter, parse);
};
/**
* Execute detete old docuware
*
* @param {string} id - The id
* @param {string} fileCabinet - The fieldCabinet
* @param {Object} template - The config
* @param {string} uri - The uri
* @param {Object} options - The options
*/
Self.deleteOld = async(id, fileCabinet, template, uri, options) => {
const docuwareOptions = options ?? await Self.getOptions();
template = template ?? {'ESTADO': {type: 'String', value: 'Pendiente eliminar'}};
const docuwareFile = await Self.checkFile(id, fileCabinet, false);
if (docuwareFile) {
const deleteJson = Self.buildTemplateJSON(template);
const deleteUri = `${uri}/${docuwareFile.id}/Fields`;
await axios.put(deleteUri, deleteJson, docuwareOptions.headers);
}
};
/**
* Returns docuware data filtered
*

View File

@ -4,7 +4,7 @@ const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('download', {
description: 'Download an docuware PDF',
description: 'Download a docuware PDF',
accessType: 'READ',
accepts: [
{
@ -58,7 +58,7 @@ module.exports = Self => {
const fileName = `filename="${id}.pdf"`;
const contentType = 'application/pdf';
const downloadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/FileDownload?targetFileType=Auto&keepAnnotations=false`;
const downloadUri = `${Self.baseURL(options, fileCabinetId)}Documents/${docuwareFile.id}/FileDownload?targetFileType=Auto&keepAnnotations=false`;
const stream = await axios.get(downloadUri, options.headers);

View File

@ -1,9 +1,29 @@
const axios = require('axios');
const models = require('vn-loopback/server/server').models;
const {Docuware} = require('vn-loopback/server/server').models;
describe('hasDeviceReady()', () => {
it('should return true', async() => {
const result = await Docuware.hasDeviceReady({device: 'Tablet 1'});
expect(result).toBeTrue();
});
it('should return false', async() => {
const result = await Docuware.hasDeviceReady({device: null});
expect(result).toBeFalse();
});
it('should not exists return false ', async() => {
const result = await Docuware.hasDeviceReady();
expect(result).toBeFalse();
});
});
describe('Docuware core', () => {
beforeAll(() => {
process.env.NODE_ENV = 'testing';
spyOn(Docuware, 'hasDeviceReady').and.returnValue(true);
});
afterAll(() => {
@ -12,7 +32,7 @@ describe('Docuware core', () => {
describe('getOptions()', () => {
it('should return url and headers', async() => {
const result = await models.Docuware.getOptions();
const result = await Docuware.getOptions();
expect(result.url).toBeDefined();
expect(result.headers).toBeDefined();
@ -32,16 +52,90 @@ describe('Docuware core', () => {
}
};
spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs)));
const result = await models.Docuware.getDialog('deliveryNote', 'find', 'randomFileCabinetId');
const result = await Docuware.getDialog('deliveryNote', 'find', 'randomFileCabinetId');
expect(result).toEqual('getDialogTest');
});
});
describe('buildTemplateJSON()', () => {
it('should return buildTemplateJSON', async() => {
const config = {
'N__DOCUMENTO': {
type: 'string',
value: '12345'
},
'ESTADO': {
type: 'string',
value: 'Pendiente procesar'
},
'FIRMA_': {
type: 'string',
value: 'Si'
},
'FILTRO_TABLET': {
type: 'string',
value: 'Tablet123'
}
};
const result = await Docuware.buildTemplateJSON(config);
expect(result).toEqual({
'Fields': [
{
'FieldName': 'N__DOCUMENTO',
'ItemElementName': 'string',
'Item': '12345',
},
{
'FieldName': 'ESTADO',
'ItemElementName': 'string',
'Item': 'Pendiente procesar',
},
{
'FieldName': 'FIRMA_',
'ItemElementName': 'string',
'Item': 'Si',
},
{
'FieldName': 'FILTRO_TABLET',
'ItemElementName': 'string',
'Item': 'Tablet123',
}
]
});
});
});
describe('uploadOptions()', () => {
it('should return uploadOptions', async() => {
spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
const {uploadOptions: result} = await Docuware.uploadOptions(1, {
'N__DOCUMENTO': {
type: 'string',
jsegarra marked this conversation as resolved
Review

Quitar?

Quitar?
value: '12345'
}}, 'test.pdf');
expect(result.headers.Cookie).toEqual(null);
expect(result.headers['Content-Type']).toEqual('multipart/form-data');
expect(result.headers['content-type']).toMatch(/^multipart\/form-data; boundary=/);
});
});
describe('deleteOld()', () => {
it('should return deleteOld', async() => {
await Docuware.deleteOld(1, 'deliveryNote', {
'N__DOCUMENTO': {
type: 'string',
value: '12345'
}});
});
});
describe('getFileCabinet()', () => {
it('should return fileCabinetId', async() => {
const code = 'deliveryNote';
const docuwareInfo = await models.Docuware.findOne({
const docuwareInfo = await Docuware.findOne({
where: {
code
}
@ -57,7 +151,7 @@ describe('Docuware core', () => {
}
};
spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs)));
const result = await models.Docuware.getFileCabinet(code);
const result = await Docuware.getFileCabinet(code);
expect(result).toEqual('getFileCabinetTest');
});
@ -65,22 +159,22 @@ describe('Docuware core', () => {
describe('get()', () => {
it('should return data without parse', async() => {
spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
const data = {
data: {
id: 1
}
};
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data)));
const result = await models.Docuware.get('deliveryNote');
const result = await Docuware.get('deliveryNote');
expect(result.id).toEqual(1);
});
it('should return data with parse', async() => {
spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
const data = {
data: {
Items: [{
@ -109,7 +203,7 @@ describe('Docuware core', () => {
'secondRequiredField': 'name',
};
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data)));
const [result] = await models.Docuware.get('deliveryNote', null, parse);
const [result] = await Docuware.get('deliveryNote', null, parse);
expect(result.id).toEqual(1);
expect(result.name).toEqual('myName');
@ -119,15 +213,15 @@ describe('Docuware core', () => {
describe('getById()', () => {
it('should return data', async() => {
spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
const data = {
data: {
id: 1
}
};
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data)));
const result = await models.Docuware.getById('deliveryNote', 1);
const result = await Docuware.getById('deliveryNote', 1);
expect(result.id).toEqual(1);
});

View File

@ -1,10 +1,10 @@
const models = require('vn-loopback/server/server').models;
describe('docuware upload()', () => {
const userId = 9;
const ticketIds = [10];
const userId = 18;
const ids = [10];
const ctx = {
args: {ticketIds},
args: {ids},
req: {
getLocale: () => {
return 'en';
@ -32,7 +32,7 @@ describe('docuware upload()', () => {
const options = {transaction: tx};
const user = await models.UserConfig.findById(userId, null, options);
await user.updateAttribute('tabletFk', 'Tablet1', options);
await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options);
await models.Docuware.upload(ctx, ids, fileCabinetName, options);
await tx.rollback();
} catch (e) {
@ -50,7 +50,7 @@ describe('docuware upload()', () => {
let error;
try {
const options = {transaction: tx};
await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options);
await models.Docuware.upload(ctx, ids, fileCabinetName, options);
await tx.rollback();
} catch (e) {

View File

@ -1,5 +1,4 @@
const UserError = require('vn-loopback/util/user-error');
const axios = require('axios');
const isProduction = require('vn-loopback/server/boot/isProduction');
module.exports = Self => {
@ -8,9 +7,9 @@ module.exports = Self => {
accessType: 'WRITE',
accepts: [
{
arg: 'ticketIds',
arg: 'ids',
type: ['number'],
description: 'The ticket ids',
description: 'The ids',
required: true
},
{
@ -30,8 +29,7 @@ module.exports = Self => {
}
});
Self.upload = async function(ctx, ticketIds, fileCabinet, options) {
delete ctx.args.ticketIds;
Self.upload = async function(ctx, ids, fileCabinet, options) {
const models = Self.app.models;
const action = 'store';
@ -40,125 +38,33 @@ module.exports = Self => {
if (typeof options == 'object')
Object.assign(myOptions, options);
const userConfig = await models.UserConfig.findById(ctx.req.accessToken.userId, {
const {tabletFk} = await models.UserConfig.findById(ctx.req.accessToken.userId, {
fields: ['tabletFk']
}, myOptions);
if (!userConfig?.tabletFk)
if (!tabletFk)
throw new UserError('This user does not have an assigned tablet');
const docuwareOptions = await Self.getOptions();
const docuware = await Self.getDocuware({where: {code: fileCabinet, action}});
const {modelFk} = docuware;
const modelIsValid = await Self.app.models.Module.findOne({where: {code: modelFk}});
if (!modelIsValid)
throw new UserError('This fileCabinet does not have an assigned module');
const model = modelFk.replace(/^.{1}/g, modelFk[0].toUpperCase());
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
const uploaded = [];
for (id of ticketIds) {
// get delivery note
ctx.args.id = id;
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
id,
type: 'deliveryNote'
}, myOptions);
// get ticket data
const ticket = await models.Ticket.findById(id, {
include: [{
relation: 'client',
scope: {
fields: ['id', 'name', 'fi']
}
}]
}, myOptions);
const uri = Self.baseURL(docuwareOptions, fileCabinetId, 'Documents');
// 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': userConfig.tabletFk,
}
]
};
if (!isProduction(false))
if (!isProduction(false) && !Self.hasDeviceReady())
throw new UserError('Action not allowed on the test environment');
const upload = {ctx, tabletFk, ids, myOptions, uri, fileCabinet, fileCabinetId, dialogId};
await Self.app.models[model].docuwareUpload(upload);
// delete old
const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false);
if (docuwareFile) {
const deleteJson = {
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
};
const deleteUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
await axios.put(deleteUri, deleteJson, docuwareOptions.headers);
}
const uploadUri = `${docuwareOptions.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': docuwareOptions.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, myOptions);
throw new UserError(message);
}
uploaded.push(id);
}
return models.TicketTracking.setDelivered(ctx, ticketIds, myOptions);
// return models.TicketTracking.setDelivered(ctx, ids, myOptions);
};
};

View File

@ -27,6 +27,9 @@
},
"findById": {
"type": "string"
},
"modelFk": {
"type": "string"
}
},
"relations": {

View File

@ -2786,7 +2786,7 @@ INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`)
INSERT INTO `vn`.`docuwareConfig` (`id`, `url`)
VALUES
(1, 'http://docuware.url/');
(1, 'http://docuware.url');
INSERT INTO `vn`.`calendarHolidaysName` (`id`, `name`)
VALUES
@ -3141,6 +3141,11 @@ INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`)
('Tablet1','Jarvis tablet'),
('Tablet2','Avengers tablet');
-- Auto-generated SQL script #202406060955
INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`,`fileCabinet`)
VALUES ('Tablet4','Docuware Tablet','RRHH');
INSERT INTO `vn`.`sms` (`id`, `senderFk`, `sender`, `destination`, `message`, `statusCode`, `status`, `created`)
VALUES (1, 66, '111111111', '0001111111111', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'OK', util.VN_CURDATE()),
(2, 66, '222222222', '0002222222222', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'PENDING', util.VN_CURDATE()),
@ -3884,6 +3889,41 @@ INSERT INTO vn.trainingCourse (workerFk,trainingCourseTypeFk,centerFk,started,en
(9,1,2,'2018-06-20 00:00:00.000','2020-06-24 00:00:00.000',1,0),
(9,2,2,'2018-06-20 00:00:00.000','2020-06-24 00:00:00.000',1,1);
-- Auto-generated SQL script #202405201318
UPDATE vn.userConfig
SET tabletFk='Tablet4'
WHERE userFk=5;
-- Auto-generated SQL script #202405201207
UPDATE vn.docuware
SET modelFk='ticket'
WHERE id=1;
UPDATE vn.docuware
SET modelFk='ticket'
WHERE id=2;
UPDATE vn.docuware
SET modelFk='worker'
WHERE id=3;
-- Auto-generated SQL script #202405201318
UPDATE vn.userConfig
SET tabletFk='Tablet1'
WHERE userFk=9;
-- Auto-generated SQL script #202405201207
UPDATE vn.docuware
SET modelFk='ticket'
WHERE id=1;
UPDATE vn.docuware
SET modelFk='ticket'
WHERE id=2;
UPDATE vn.docuware
SET modelFk='worker'
WHERE id=3;
INSERT INTO vn.sectorCollection
SET id = 2,
userFk = 18,
@ -3953,21 +3993,22 @@ INSERT INTO dipole.expedition_PrintOut (expeditionFk, ticketFk, addressFk, stree
truckName, clientFk, phone, province, agency, m3, workerCode, itemFk, quantity, longName, shelvingFk, comments)
VALUES(1, 1, 0, ' ', ' ', ' ', ' ', 0, '2001-01-01 00:00:00', 1, 0, ' ', ' ', 0, NULL, '', NULL, 0.000, NULL, 10, NULL, NULL, 'NCC', NULL);
INSERT INTO vn.accountDetail
(id, value, accountDetailTypeFk, supplierAccountFk)
INSERT INTO vn.accountDetail (id, value, accountDetailTypeFk, supplierAccountFk)
VALUES
(21, 'ES12345B12345678', 3, 241),
(35, 'ES12346B12345679', 3, 241);
INSERT INTO vn.accountDetailType
(id, description, code)
INSERT INTO vn.accountDetailType (id, description, code)
VALUES
(1, 'IBAN', 'iban'),
(2, 'SWIFT', 'swift'),
(3, 'Referencia Remesas', 'remRef'),
(4, 'Referencia Transferencias', 'trnRef'),
(5, 'Referencia Nominas', 'payRef'),
(6, 'ABA', 'aba');
(1, 'IBAN'),
(2, 'SWIFT'),
(3, 'Referencia Remesas'),
(4, 'Referencia Transferencias'),
(5, 'Referencia Nominas'),
(6, 'ABA');
INSERT INTO account.userConfig (userFk,warehouseFk,companyFk,darkMode,tabletFk)
VALUES (37,1,442,1,'Tablet4');
INSERT IGNORE INTO ormConfig
SET id =1,

View File

@ -0,0 +1,13 @@
ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL;
ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code);
ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL;
INSERT IGNORE INTO vn.docuware (code,fileCabinetName,`action`,dialogName,findById,dmsTypeFk,modelFk)
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Quitar

Quitar
VALUES ('hr','RRHH','store','Archivar','N__DOCUMENTO',3,'worker');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Worker','signPdaPdf','READ','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
jsegarra marked this conversation as resolved
Review

quitar

quitar
VALUES ('Docuware','upload','WRITE','ALLOW','ROLE','hr');

View File

@ -0,0 +1,89 @@
const axios = require('axios');
const {models} = require('vn-loopback/server/server');
const UserError = require('vn-loopback/util/user-error');
const isProduction = require('vn-loopback/server/boot/isProduction');
module.exports = Self => {
Self.docuwareUpload = async({ctx, tabletFk, ids: ticketIds, myOptions, uri, dialogId}) => {
const type = 'deliveryNote';
for (id of ticketIds) {
// get delivery note
ctx.args.id = id;
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
id,
type
}, myOptions);
// get ticket data
const ticket = await models.Ticket.findById(id, {
include: [{
relation: 'client',
scope: {
fields: ['id', 'name', 'fi']
}
}]
}, myOptions);
// upload file
const configTemplate = {
'N__ALBAR_N': {
type: 'string',
value: id,
},
'CIF_PROVEEDOR': {
type: 'string',
value: ticket.client().fi,
},
'CODIGO_PROVEEDOR': {
type: 'string',
value: ticket.client().id,
},
'NOMBRE_PROVEEDOR': {
type: 'string',
value: ticket.client().name + ' - ' + id,
},
'FECHA_FACTURA': {
type: 'date',
value: ticket.shipped,
},
'TOTAL_FACTURA': {
type: 'Decimal',
value: ticket.totalWithVat,
},
'ESTADO': {
type: 'string',
value: 'Pendiente procesar',
},
'FIRMA_': {
type: 'string',
value: 'Si',
},
'FILTRO_TABLET': {
type: 'string',
value: tabletFk,
}
};
if (!isProduction(false) && !Self.hasDeviceReady())
throw new UserError('Action not allowed on the test environment');
// delete old
await models.Docuware.deleteOld(id, fileCabinet, uri);
const uploadUri = `${uri}?StoreDialogId=${dialogId}`;
const {data, uploadOptions} = models.Docuware.uploadOptions(deliveryNote[0], configTemplate, `${type}.pdf`);
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, myOptions);
throw new UserError(message);
}
uploaded.push(id);
}
};
};

View File

@ -45,5 +45,6 @@ module.exports = function(Self) {
require('../methods/ticket/invoiceTickets')(Self);
require('../methods/ticket/invoiceTicketsAndPdf')(Self);
require('../methods/ticket/docuwareDownload')(Self);
require('../methods/ticket/docuwareUpload')(Self);
require('../methods/ticket/myLastModified')(Self);
};

View File

@ -324,7 +324,7 @@ class Controller extends Section {
if (!force)
return this.$.pdfToTablet.show();
return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds: [this.id]})
return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ids: [this.id]})
.then(() => {
this.vnApp.showSuccess(this.$t('PDF sent!'));
});

View File

@ -11,12 +11,12 @@ export default class Controller extends Section {
sendDocuware() {
const checkedTickets = this.checked;
let ticketIds = [];
let ids = [];
for (let ticket of checkedTickets)
ticketIds.push(ticket.id);
ids.push(ticket.id);
return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds})
return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ids})
.then(res => {
let state = res.data;
for (let ticket of checkedTickets) {

View File

@ -0,0 +1,63 @@
const axios = require('axios');
const {models} = require('vn-loopback/server/server');
const UserError = require('vn-loopback/util/user-error');
const isProduction = require('vn-loopback/server/boot/isProduction');
module.exports = Self => {
Self.docuwareUpload = async({ctx, tabletFk, ids: id, myOptions, uri, fileCabinet, dialogId}) => {
const pdaId = id[0];
ctx.args.id = pdaId;
delete ctx.args.ids;
// upload file
const workerFk = await models.DeviceProductionUser.findOne(
{
fields: ['userFk'],
where: {deviceProductionFk: pdaId}
}
, myOptions);
const signPda = await models.Worker.signPdaPdf(ctx,
{pdaId, workerFk}
, myOptions);
const configTemplate = {
'N__DOCUMENTO': {
type: 'String',
value: pdaId
},
'ESTADO': {
type: 'String',
value: 'Pendiente procesar'
},
'FIRMA_': {
type: 'String',
value: 'Si'
},
'FILTRO_TABLET': {
type: 'String',
value: tabletFk
}
};
if (!isProduction(false) && !Self.hasDeviceReady())
throw new UserError('Action not allowed on the test environment');
// delete old
await models.Docuware.deleteOld(id, fileCabinet, uri);
const uploadUri = `${uri}?StoreDialogId=${dialogId}`;
const fileName = `assign${pdaId}Pda${workerFk}.pdf`;
const {data, uploadOptions} = await models.Docuware.uploadOptions(signPda[0], configTemplate, fileName);
try {
await axios.post(uploadUri, data, uploadOptions);
} catch (err) {
const $t = ctx.req.__;
const message = $t('Failed to upload delivery note', {id});
throw new UserError(message);
}
uploaded.push(id);
};
};

View File

@ -0,0 +1,44 @@
module.exports = Self => {
Self.remoteMethodCtx('signPdaPdf', {
description: 'Print pdf to sign PDA',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'The pda id',
http: {source: 'path'}
},
{
arg: 'workerFk',
type: 'number',
required: true,
description: 'The worker 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/sign-pda-pdf',
verb: 'GET'
},
accessScopes: ['DEFAULT', 'read:multimedia']
});
Self.signPdaPdf = (ctx, id) => Self.printReport(ctx, id, 'sign-pda');
};

View File

@ -8,6 +8,7 @@ module.exports = Self => {
require('../methods/worker/createAbsence')(Self);
require('../methods/worker/deleteAbsence')(Self);
require('../methods/worker/updateAbsence')(Self);
require('../methods/worker/docuwareUpload')(Self);
require('../methods/worker/active')(Self);
require('../methods/worker/activeWithRole')(Self);
require('../methods/worker/activeWithInheritedRole')(Self);
@ -17,6 +18,7 @@ module.exports = Self => {
require('../methods/worker/new')(Self);
require('../methods/worker/deallocatePDA')(Self);
require('../methods/worker/allocatePDA')(Self);
require('../methods/worker/signPdaPdf')(Self);
require('../methods/worker/search')(Self);
require('../methods/worker/isAuthorized')(Self);
require('../methods/worker/setPassword')(Self);

View File

@ -0,0 +1,12 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/report.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -0,0 +1,25 @@
.description strong {
text-transform: uppercase;
}
h2 {
font-weight: 100;
color: #555
}
.column-oriented {
margin-bottom: 5px;
}
.report-info {
font-size: 20px
}
.row-oriented > tbody > tr > th {
padding-left: 30px;
width: 20%
}
.grid-block {
font-size: 1.2em
}

View File

@ -0,0 +1,18 @@
reportName: pda
signNote: Recepción PDA
date: Fecha
deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional.
pdaModel: PDA Modelo
pdaSerie: Serie PDA
sim: sim vodafone
pin: pin
puk: puk
label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa.
label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas.
label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla.
label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales.
label5: La empresa prohibe el uso de accesorios no originales.
label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal.
sign1: Recibe, D/Dña
sign2: DNI
sign: FIRMA

View File

@ -0,0 +1,18 @@
reportName: pda
signNote: Recepción PDA
date: Fecha
deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional.
pdaModel: PDA Modelo
pdaSerie: Serie PDA
sim: sim vodafone
pin: pin
puk: puk
label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa.
label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas.
label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla.
label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales.
label5: La empresa prohibe el uso de accesorios no originales.
label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal.
sign1: Recibe, D/Dña
sign2: DNI
sign: FIRMA

View File

@ -0,0 +1,18 @@
reportName: pda
signNote: Recepción PDA
date: Fecha
deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional.
pdaModel: PDA Modelo
pdaSerie: Serie PDA
sim: sim vodafone
pin: pin
puk: puk
label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa.
label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas.
label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla.
label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales.
label5: La empresa prohibe el uso de accesorios no originales.
label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal.
sign1: Recibe, D/Dña
sign2: DNI
sign: FIRMA

View File

@ -0,0 +1,18 @@
reportName: pda
signNote: Recepción PDA
date: Fecha
deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional.
pdaModel: PDA Modelo
pdaSerie: Serie PDA
sim: sim vodafone
pin: pin
puk: puk
label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa.
label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas.
label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla.
label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales.
label5: La empresa prohibe el uso de accesorios no originales.
label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal.
sign1: Recibe, D/Dña
sign2: DNI
sign: FIRMA

View File

@ -0,0 +1,18 @@
reportName: pda
signNote: Recepción PDA
date: Fecha
deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional.
pdaModel: PDA Modelo
pdaSerie: Serie PDA
sim: sim vodafone
pin: pin
puk: puk
label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa.
label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas.
label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla.
label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales.
label5: La empresa prohibe el uso de accesorios no originales.
label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal.
sign1: Recibe, D/Dña
sign2: DNI
sign: FIRMA

View File

@ -0,0 +1,48 @@
<report-body v-bind="$props">
<template v-slot:header>
<report-header v-bind="$props"> </report-header>
</template>
<div class="grid-row">
<div class="grid-block">
<div class="columns">
<div class="body">
<span>{{$t('date')}}:{{formatDate(new Date(), '%d-%m-%Y')}}</span>
<p>
{{$t('deviceRecieved',{modelFk: device.modelFk, serialNumber: device.serialNumber})}}:
</p>
<p>{{$t('label1')}}
</p>
<p>
{{$t('label2')}}
</p>
<p>
{{$t('label3')}}
</p>
<p>
{{$t('label4')}}
</p>
<p>
{{$t('label5')}}
</p>
<p>
{{$t('label6')}}
</p>
<p>
{{$t('sign1')}} {{worker.firstName}} {{ worker.lastName}}
</p>
<p>
{{$t('sign2')}} {{worker.fi}}
</p>
<p>
{{$t('sign')}}
</p>
</div>
</div>
</div>
</div>
<template v-slot:footer>
<report-footer id="pageFooter" v-bind="$props"> </report-footer>
</template>
</report-body>

View File

@ -0,0 +1,26 @@
const vnReport = require('../../../core/mixins/vn-report.js');
module.exports = {
name: 'sign-pda',
mixins: [vnReport],
async serverPrefetch() {
this.device = await this.findOneFromDef('device', [this.id]);
this.worker = await this.findOneFromDef('worker', [this.workerFk]);
},
props: {
id: {
type: Number,
required: true,
description: 'The device id'
},
workerFk: {
type: Number,
required: true,
description: 'The worker id'
},
type: {
type: String,
required: false
}
}
};

View File

@ -0,0 +1 @@
SELECT * FROM vn.deviceProduction WHERE id = ?

View File

@ -0,0 +1 @@
SELECT * FROM vn.worker WHERE id = ?