diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 4af445bfa..bec4d964d 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -2400,7 +2400,8 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`) (18, 'dua', NULL, NULL, 'dua'), (19, 'inmovilizado', NULL, NULL, 'fixedAssets'), (20, 'ReclamaciĆ³n', 1, 1, 'claim'), - (21, 'Entrada', 1, 1, 'entry'); + (21, 'Entrada', 1, 1, 'entry'), + (22, 'Proveedor', 1, 1, 'supplier'); INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`) VALUES @@ -2412,7 +2413,8 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa (6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', util.VN_CURDATE()), (7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()), (8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()), - (9, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA ID 1', util.VN_CURDATE()); + (9, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA ID 1', util.VN_CURDATE()), + (10, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA DE PRUEBA', util.VN_CURDATE()); INSERT INTO `vn`.`claimDms`(`claimFk`, `dmsFk`) VALUES @@ -3737,3 +3739,7 @@ INSERT INTO vn.parkingLog(originFk, userFk, `action`, creationDate, description, INSERT INTO vn.ticketLog (originFk,userFk,`action`,creationDate,changedModel,newInstance,changedModelId,changedModelValue) VALUES (18,9,'insert','2001-01-01 11:01:00.000','Ticket','{"isDeleted":true}',45,'Super Man'); + +INSERT INTO `vn`.`supplierDms`(`supplierFk`, `dmsFk`, `editorFk`) + VALUES + (1, 10, 9); \ No newline at end of file diff --git a/db/versions/10969-silverCataractarum/00-aclSupplierDms.sql b/db/versions/10969-silverCataractarum/00-aclSupplierDms.sql new file mode 100644 index 000000000..48c7438f1 --- /dev/null +++ b/db/versions/10969-silverCataractarum/00-aclSupplierDms.sql @@ -0,0 +1,3 @@ +-- Place your SQL code here +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES('SupplierDms', '*', '*', 'ALLOW', 'ROLE', 'employee'); \ No newline at end of file diff --git a/loopback/server/datasources.json b/loopback/server/datasources.json index 608479b4b..a8fc13152 100644 --- a/loopback/server/datasources.json +++ b/loopback/server/datasources.json @@ -126,5 +126,20 @@ "allowedContentTypes": [ "application/x-7z-compressed" ] + }, + "supplierStorage": { + "name": "supplierStorage", + "connector": "loopback-component-storage", + "provider": "filesystem", + "root": "./storage/dms", + "maxFileSize": "31457280", + "allowedContentTypes": [ + "image/png", + "image/jpeg", + "image/jpg", + "image/webp", + "video/mp4", + "application/pdf" + ] } } diff --git a/modules/supplier/back/methods/supplier/supplier-dms/downloadFile.js b/modules/supplier/back/methods/supplier/supplier-dms/downloadFile.js new file mode 100644 index 000000000..fa70dc554 --- /dev/null +++ b/modules/supplier/back/methods/supplier/supplier-dms/downloadFile.js @@ -0,0 +1,59 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethodCtx('downloadFile', { + description: 'Get the supplier file', + accessType: 'READ', + accepts: [ + { + arg: 'id', + type: 'Number', + description: 'The file 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/downloadFile`, + verb: 'GET' + } + }); + + Self.downloadFile = async function(ctx, id) { + const models = Self.app.models; + const SupplierContainer = models.SupplierContainer; + const dms = await models.Dms.findById(id); + const pathHash = SupplierContainer.getHash(dms.id); + try { + await SupplierContainer.getFile(pathHash, dms.file); + } catch (e) { + if (e.code != 'ENOENT') + throw e; + + const error = new UserError(`File doesn't exists`); + error.statusCode = 404; + + throw error; + } + + const stream = SupplierContainer.downloadStream(pathHash, dms.file); + + return [stream, dms.contentType, `filename="${dms.file}"`]; + }; +}; diff --git a/modules/supplier/back/methods/supplier/supplier-dms/removeFile.js b/modules/supplier/back/methods/supplier/supplier-dms/removeFile.js new file mode 100644 index 000000000..04a9011f1 --- /dev/null +++ b/modules/supplier/back/methods/supplier/supplier-dms/removeFile.js @@ -0,0 +1,53 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethodCtx('removeFile', { + description: 'Removes a entry document', + accessType: 'WRITE', + accepts: { + arg: 'id', + type: 'number', + description: 'The file id', + http: {source: 'path'} + }, + returns: { + type: 'object', + root: true + }, + http: { + path: `/:id/removeFile`, + verb: 'POST' + } + }); + + Self.removeFile = async(ctx, id, options) => { + const myOptions = {}; + let tx; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const targetSupplierDms = await Self.findById(id, null, myOptions); + const targetDms = await Self.app.models.Dms.removeFile(ctx, targetSupplierDms.dmsFk, myOptions); + + if (!targetDms) + throw new UserError('Try again'); + + const supplierDmsDestroyed = await targetSupplierDms.destroy(myOptions); + + if (tx) await tx.commit(); + + return supplierDmsDestroyed; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; + diff --git a/modules/supplier/back/methods/supplier/supplier-dms/uploadFile.js b/modules/supplier/back/methods/supplier/supplier-dms/uploadFile.js new file mode 100644 index 000000000..a9ff2d125 --- /dev/null +++ b/modules/supplier/back/methods/supplier/supplier-dms/uploadFile.js @@ -0,0 +1,86 @@ + +module.exports = Self => { + Self.remoteMethodCtx('uploadFile', { + description: 'Upload and attach a file', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'number', + description: 'The supplier id', + http: {source: 'path'} + }, + { + arg: 'warehouseId', + type: 'number', + description: 'The warehouse id', + required: true + }, + { + arg: 'companyId', + type: 'number', + description: 'The company id', + required: true + }, + { + arg: 'dmsTypeId', + type: 'number', + description: 'The dms type id', + required: true + }, + { + arg: 'reference', + type: 'string', + required: true + }, + { + arg: 'description', + type: 'string', + required: true + }, + { + arg: 'hasFile', + type: 'boolean', + description: 'True if has an attached file', + required: true + }], + returns: { + type: 'object', + root: true + }, + http: { + path: `/:id/uploadFile`, + verb: 'POST' + } + }); + + Self.uploadFile = async(ctx, id, options) => { + const {Dms, SupplierDms} = Self.app.models; + const myOptions = {}; + let tx; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const uploadedFiles = await Dms.uploadFile(ctx, myOptions); + + const promises = uploadedFiles.map(dms => SupplierDms.create({ + supplierFk: id, + dmsFk: dms.id + }, myOptions)); + await Promise.all(promises); + + if (tx) await tx.commit(); + + return uploadedFiles; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/supplier/back/model-config.json b/modules/supplier/back/model-config.json index dbc387ed2..efbb4ceed 100644 --- a/modules/supplier/back/model-config.json +++ b/modules/supplier/back/model-config.json @@ -5,6 +5,12 @@ "Supplier": { "dataSource": "vn" }, + "SupplierDms": { + "dataSource": "vn" + }, + "SupplierContainer": { + "dataSource": "supplierStorage" + }, "SupplierAddress": { "dataSource": "vn" }, diff --git a/modules/supplier/back/models/supplier-container.json b/modules/supplier/back/models/supplier-container.json new file mode 100644 index 000000000..91b931869 --- /dev/null +++ b/modules/supplier/back/models/supplier-container.json @@ -0,0 +1,10 @@ +{ + "name": "SupplierContainer", + "base": "Container", + "acls": [{ + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + }] +} \ No newline at end of file diff --git a/modules/supplier/back/models/supplier-dms.js b/modules/supplier/back/models/supplier-dms.js new file mode 100644 index 000000000..13e492bfb --- /dev/null +++ b/modules/supplier/back/models/supplier-dms.js @@ -0,0 +1,13 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + require('../methods/supplier/supplier-dms/removeFile')(Self); + require('../methods/supplier/supplier-dms/downloadFile')(Self); + require('../methods/supplier/supplier-dms/uploadFile')(Self); + + Self.rewriteDbError(function(err) { + if (err.code === 'ER_DUP_ENTRY') + return new UserError(`The file is already attached to another entry`); + return err; + }); +}; diff --git a/modules/supplier/back/models/supplier-dms.json b/modules/supplier/back/models/supplier-dms.json new file mode 100644 index 000000000..8d32cc4e8 --- /dev/null +++ b/modules/supplier/back/models/supplier-dms.json @@ -0,0 +1,37 @@ +{ + "name": "SupplierDms", + "base": "VnModel", + "mixins": { + "Loggable": true + }, + "options": { + "mysql": { + "table": "supplierDms" + } + }, + "allowedContentTypes": [ + "image/png", + "image/jpeg", + "image/jpg", + "application/pdf" + ], + "properties": { + "dmsFk": { + "type": "number", + "id": true, + "required": true + } + }, + "relations": { + "supplier": { + "type": "belongsTo", + "model": "Supplier", + "foreignKey": "supplierFk" + }, + "dms": { + "type": "belongsTo", + "model": "Dms", + "foreignKey": "dmsFk" + } + } +} \ No newline at end of file