#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)
VALUES
INSERT INTO vn.accountDetail (id, value, accountDetailTypeFk, supplierAccountFk)
VALUES
(21, 'ES12345B12345678', 3, 241),
(35, 'ES12346B12345679', 3, 241);
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');
INSERT INTO vn.accountDetailType (id, description, code)
VALUES
(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)
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 = ?