diff --git a/Dockerfile b/Dockerfile index 5a65b9b18..a574e61fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apt-get update \ ca-certificates \ gnupg2 \ libfontconfig \ - && curl -sL https://deb.nodesource.com/setup_10.x | bash - \ + && curl -sL https://deb.nodesource.com/setup_12.x | bash - \ && apt-get install -y --no-install-recommends \ nodejs \ && apt-get purge -y --auto-remove \ diff --git a/README.md b/README.md index 8960c8d00..52f854b6e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Salix is also the scientific name of a beautifull tree! :) Required applications. * Visual Studio Code -* Node.js = 10.15.3 LTS +* Node.js = 12.17.0 LTS * Docker In Visual Studio Code we use the ESLint extension. Open Visual Studio Code, press Ctrl+P and paste the following command. diff --git a/db/changes/10180-holyWeek/00-ACL.sql b/db/changes/10180-holyWeek/00-ACL.sql new file mode 100644 index 000000000..b0ab68a97 --- /dev/null +++ b/db/changes/10180-holyWeek/00-ACL.sql @@ -0,0 +1 @@ +INSERT IGNORE INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('WorkerLog', '*', 'READ', 'ALLOW', 'ROLE', 'hr'); \ No newline at end of file diff --git a/db/changes/10190-PostErte/00-ACL.sql b/db/changes/10190-PostErte/00-ACL.sql new file mode 100644 index 000000000..2a2673fcc --- /dev/null +++ b/db/changes/10190-PostErte/00-ACL.sql @@ -0,0 +1,4 @@ +UPDATE `salix`.`ACL` SET `accessType`='WRITE' WHERE `id`='213'; + +INSERT IGNORE INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('CustomsAgent', '*', '*', 'ALLOW', 'ROLE', 'employee'); + diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index ecf0d37e3..b12c470f5 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -122,6 +122,12 @@ export default { mobileInput: 'vn-textfield[ng-model="$ctrl.address.mobile"]', defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]', incoterms: 'vn-autocomplete[ng-model="$ctrl.address.incotermsId"]', + addNewCustomsAgent: 'vn-client-address-create vn-autocomplete[ng-model="$ctrl.address.customsAgentId"] vn-icon-button[icon="add_circle"]', + newCustomsAgentFiscalID: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.nif"]', + newCustomsAgentFiscalName: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.fiscalName"]', + newCustomsAgentStreet: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.street"]', + newCustomsAgentPhone: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.phone"]', + saveNewCustomsAgentButton: 'button[response="accept"]', customsAgent: 'vn-autocomplete[ng-model="$ctrl.address.customsAgentId"]', secondMakeDefaultStar: 'vn-client-address-index vn-card div:nth-child(2) vn-icon-button[icon="star_border"]', firstEditAddress: 'vn-client-address-index div:nth-child(1) > a', @@ -828,6 +834,12 @@ export default { }, travelThermograph: { add: 'vn-travel-thermograph-index vn-float-button[icon="add"]', + addThermographIcon: 'vn-travel-thermograph-create vn-autocomplete vn-icon[icon="add_circle"]', + newThermographId: 'vn-textfield[ng-model="$ctrl.newThermograph.thermographId"]', + newThermographModel: 'vn-autocomplete[ng-model="$ctrl.newThermograph.model"]', + newThermographWarehouse: 'vn-autocomplete[ng-model="$ctrl.newThermograph.warehouseId"]', + newThermographTemperature: 'vn-autocomplete[ng-model="$ctrl.newThermograph.temperature"]', + createThermographButton: 'form button[response="accept"]', thermographID: 'vn-travel-thermograph-create vn-autocomplete[ng-model="$ctrl.dms.thermographId"]', uploadIcon: 'vn-travel-thermograph-create vn-icon[icon="icon-attach"]', createdThermograph: 'vn-travel-thermograph-index vn-tbody > vn-tr', diff --git a/e2e/paths/02-client/05_add_address.spec.js b/e2e/paths/02-client/05_add_address.spec.js index 2fe238fea..f16408b34 100644 --- a/e2e/paths/02-client/05_add_address.spec.js +++ b/e2e/paths/02-client/05_add_address.spec.js @@ -61,12 +61,18 @@ describe('Client Add address path', () => { expect(message.text).toBe('Customs agent is required for a non UEE member'); }); - it(`should create a new address with all it's data`, async() => { - await page.autocompleteSearch(selectors.clientAddresses.customsAgent, 'Agent one'); + it(`should create a new custom agent and then save the address`, async() => { + await page.waitToClick(selectors.clientAddresses.addNewCustomsAgent); + await page.write(selectors.clientAddresses.newCustomsAgentFiscalID, 'ID'); + await page.write(selectors.clientAddresses.newCustomsAgentFiscalName, 'name'); + await page.write(selectors.clientAddresses.newCustomsAgentStreet, 'street'); + await page.write(selectors.clientAddresses.newCustomsAgentPhone, '555555555'); + await page.waitToClick(selectors.clientAddresses.saveNewCustomsAgentButton); + await page.waitToClick(selectors.clientAddresses.saveButton); const message = await page.waitForSnackbar(); - expect(message.type).toBe('success'); + expect(message.text).toBe('Data saved!'); }); it(`should navigate back to the addresses index`, async() => { diff --git a/e2e/paths/10-travel/01_thermograph.spec.js b/e2e/paths/10-travel/01_thermograph.spec.js index 67a62381a..e7f1e234d 100644 --- a/e2e/paths/10-travel/01_thermograph.spec.js +++ b/e2e/paths/10-travel/01_thermograph.spec.js @@ -2,6 +2,7 @@ import selectors from '../../helpers/selectors.js'; import getBrowser from '../../helpers/puppeteer'; describe('Travel thermograph path', () => { + const thermographName = '7H3-37H3RN4L-FL4M3'; let browser; let page; @@ -26,10 +27,18 @@ describe('Travel thermograph path', () => { await page.waitForState('travel.card.thermograph.create'); }); - it('should select the thermograph and then the file to upload', async() => { + it('should click on the add thermograph icon of the thermograph autocomplete', async() => { + await page.waitToClick(selectors.travelThermograph.addThermographIcon); + await page.write(selectors.travelThermograph.newThermographId, thermographName); + await page.autocompleteSearch(selectors.travelThermograph.newThermographModel, 'TEMPMATE'); + await page.autocompleteSearch(selectors.travelThermograph.newThermographWarehouse, 'Warehouse Two'); + await page.autocompleteSearch(selectors.travelThermograph.newThermographTemperature, 'WARM'); + await page.waitToClick(selectors.travelThermograph.createThermographButton); + }); + + it('should select the file to upload', async() => { let currentDir = process.cwd(); let filePath = `${currentDir}/e2e/dms/ecc/3.jpeg`; - await page.autocompleteSearch(selectors.travelThermograph.thermographID, '138350-0'); const [fileChooser] = await Promise.all([ page.waitForFileChooser(), @@ -38,11 +47,17 @@ describe('Travel thermograph path', () => { await fileChooser.accept([filePath]); await page.waitToClick(selectors.travelThermograph.upload); + + const message = await page.waitForSnackbar(); + const state = await page.getState(); + + expect(message.type).toBe('success'); + expect(state).toBe('travel.card.thermograph.index'); }); - it('should reload the section and check everything was saved', async() => { - let createdThermograph = await page.waitToGetProperty(selectors.travelThermograph.createdThermograph, 'innerText'); + it('should check everything was saved correctly', async() => { + const result = await page.waitToGetProperty(selectors.travelThermograph.createdThermograph, 'innerText'); - expect(createdThermograph).toContain('138350-0'); + expect(result).toContain(thermographName); }); }); diff --git a/loopback/common/methods/vn-model/getEnumValues.js b/loopback/common/methods/vn-model/getEnumValues.js new file mode 100644 index 000000000..0bc8f8eb6 --- /dev/null +++ b/loopback/common/methods/vn-model/getEnumValues.js @@ -0,0 +1,55 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + /** + * Returns a set of allowed values defined on table scheme + * @param {String} column - Model or Table column name + * @return {Array} - Array of set values + */ + Self.getEnumValues = async function(column) { + let model = this.app.models[this.modelName].definition; + let properties = model.properties; + let tableName = this.modelName; + let schema = null; + + if (model.settings && model.settings.mysql) { + let tableSplit = model.settings.mysql.table.split('.'); + tableName = tableSplit.pop(); + schema = tableSplit.pop() || null; + } + + let property = properties[column]; + + if (!property) + throw new UserError(`Column does not exist`); + + let columnName = property.mysql + ? property.mysql.columnName + : column; + + let columnInfo = await this.rawSql( + `SELECT column_type columnType + FROM information_schema.columns + WHERE table_name = ? + AND table_schema = IFNULL(?, DATABASE()) + AND column_name = ?`, + [tableName, schema, columnName] + ); + + if (!columnInfo || !columnInfo[0]) + throw new UserError(`Cannot fetch column values`); + + let setValues; + setValues = columnInfo[0].columnType + .replace(/^enum\((.*)\)$/i, '$1') + .replace(/'/g, '') + .match(new RegExp(/(\w+)+/, 'ig')); + + let values = []; + setValues.forEach(setValue => { + values.push({value: setValue}); + }); + + return values; + }; +}; diff --git a/loopback/common/methods/vn-model/specs/getEnumValues.spec.js b/loopback/common/methods/vn-model/specs/getEnumValues.spec.js new file mode 100644 index 000000000..b49a952e4 --- /dev/null +++ b/loopback/common/methods/vn-model/specs/getEnumValues.spec.js @@ -0,0 +1,18 @@ +const app = require('vn-loopback/server/server'); + +describe('Model getEnumValues()', () => { + it('should extend getEnumValues properties to any model passed', () => { + let exampleModel = app.models.TravelThermograph; + + expect(exampleModel.getEnumValues).toBeDefined(); + }); + + it('should return an array of enum values from a given column', async() => { + let result = await app.models.TravelThermograph.getSetValues('temperature'); + + expect(result.length).toEqual(3); + expect(result[0].value).toEqual('enum'); + expect(result[1].value).toEqual('COOL'); + expect(result[2].value).toEqual('WARM'); + }); +}); diff --git a/loopback/common/models/loggable.js b/loopback/common/models/loggable.js index d9116a0de..ec6989717 100644 --- a/loopback/common/models/loggable.js +++ b/loopback/common/models/loggable.js @@ -12,23 +12,34 @@ module.exports = function(Self) { }); Self.observe('before save', async function(ctx) { - let options = {}; + const appModels = ctx.Model.app.models; + const definition = ctx.Model.definition; + const options = {}; + + // Check for transactions if (ctx.options && ctx.options.transaction) options.transaction = ctx.options.transaction; let oldInstance; - let oldInstanceFk; let newInstance; if (ctx.data) { - oldInstanceFk = pick(ctx.currentInstance, Object.keys(ctx.data)); + const changes = pick(ctx.currentInstance, Object.keys(ctx.data)); newInstance = await fkToValue(ctx.data, ctx); - oldInstance = await fkToValue(oldInstanceFk, ctx); + oldInstance = await fkToValue(changes, ctx); + if (ctx.where && !ctx.currentInstance) { - let fields = Object.keys(ctx.data); - ctx.oldInstances = await ctx.Model.app.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields}, options); + const fields = Object.keys(ctx.data); + const modelName = definition.name; + + ctx.oldInstances = await appModels[modelName].find({ + where: ctx.where, + fields: fields + }, options); } } + + // Get changes from created instance if (ctx.isNewInstance) newInstance = await fkToValue(ctx.instance.__data, ctx); @@ -37,18 +48,24 @@ module.exports = function(Self) { }); Self.observe('before delete', async function(ctx) { + const appModels = ctx.Model.app.models; + const definition = ctx.Model.definition; + const relations = ctx.Model.relations; + let options = {}; if (ctx.options && ctx.options.transaction) options.transaction = ctx.options.transaction; if (ctx.where) { - let affectedModel = ctx.Model.definition.name; - let definition = ctx.Model.definition; - let deletedInstances = await ctx.Model.app.models[affectedModel].find({where: ctx.where}, options); + let affectedModel = definition.name; + let deletedInstances = await appModels[affectedModel].find({ + where: ctx.where + }, options); + let relation = definition.settings.log.relation; if (relation) { - let primaryKey = ctx.Model.relations[relation].keyFrom; + let primaryKey = relations[relation].keyFrom; let arrangedDeletedInstances = []; for (let i = 0; i < deletedInstances.length; i++) { @@ -69,6 +86,8 @@ module.exports = function(Self) { }); async function logDeletedInstances(ctx, loopBackContext) { + const appModels = ctx.Model.app.models; + const definition = ctx.Model.definition; let options = {}; if (ctx.options && ctx.options.transaction) options.transaction = ctx.options.transaction; @@ -78,14 +97,12 @@ module.exports = function(Self) { if (loopBackContext) userFk = loopBackContext.active.accessToken.userId; - let definition = ctx.Model.definition; - let changedModelValue = definition.settings.log.changedModelValue; let logRecord = { originFk: instance.originFk, userFk: userFk, action: 'delete', - changedModel: ctx.Model.definition.name, + changedModel: definition.name, changedModelId: instance.id, changedModelValue: instance[changedModelValue], oldInstance: instance, @@ -95,26 +112,44 @@ module.exports = function(Self) { delete instance.originFk; let logModel = definition.settings.log.model; - await ctx.Model.app.models[logModel].create(logRecord, options); + await appModels[logModel].create(logRecord, options); }); } + // Get log values from a foreign key async function fkToValue(instance, ctx) { + const appModels = ctx.Model.app.models; + const relations = ctx.Model.relations; let options = {}; + + // Check for transactions if (ctx.options && ctx.options.transaction) options.transaction = ctx.options.transaction; - let cleanInstance = JSON.parse(JSON.stringify(instance)); - let result = {}; - for (let key in cleanInstance) { - let val = cleanInstance[key]; - if (val === undefined || val === null) continue; - for (let key1 in ctx.Model.relations) { - let val1 = ctx.Model.relations[key1]; - if (val1.keyFrom == key && key != 'id') { - let recordSet = await ctx.Model.app.models[val1.modelTo.modelName].findById(val, null, options); + const instanceCopy = JSON.parse(JSON.stringify(instance)); + const result = {}; + for (const key in instanceCopy) { + let value = instanceCopy[key]; + + if (value instanceof Object) + continue; + + if (value === undefined || value === null) continue; + + for (let relationName in relations) { + const relation = relations[relationName]; + if (relation.keyFrom == key && key != 'id') { + const model = relation.modelTo; + const modelName = relation.modelTo.modelName; + const properties = model && model.definition.properties; + const settings = model && model.definition.settings; + + const recordSet = await appModels[modelName].findById(value, null, options); + + const hasShowField = settings.log && settings.log.showField; + let showField = hasShowField && recordSet + && recordSet[settings.log.showField]; - let showField = val1.modelTo && val1.modelTo.definition.settings.log && val1.modelTo.definition.settings.log.showField && recordSet && recordSet[val1.modelTo.definition.settings.log.showField]; if (!showField) { const showFieldNames = [ 'name', @@ -122,7 +157,10 @@ module.exports = function(Self) { 'code' ]; for (field of showFieldNames) { - if (val1.modelTo.definition.properties && val1.modelTo.definition.properties[field] && recordSet && recordSet[field]) { + const propField = properties && properties[field]; + const recordField = recordSet && recordSet[field]; + + if (propField && recordField) { showField = field; break; } @@ -130,25 +168,29 @@ module.exports = function(Self) { } if (showField && recordSet && recordSet[showField]) { - val = recordSet[showField]; + value = recordSet[showField]; break; } - val = recordSet && recordSet.id || val; + value = recordSet && recordSet.id || value; break; } } - result[key] = val; + result[key] = value; } return result; } async function logInModel(ctx, loopBackContext) { - let options = {}; + const appModels = ctx.Model.app.models; + const definition = ctx.Model.definition; + const defSettings = ctx.Model.definition.settings; + const relations = ctx.Model.relations; + + const options = {}; if (ctx.options && ctx.options.transaction) options.transaction = ctx.options.transaction; - let definition = ctx.Model.definition; let primaryKey; for (let property in definition.properties) { if (definition.properties[property].id) { @@ -163,11 +205,11 @@ module.exports = function(Self) { // RELATIONS LOG let changedModelId; - if (ctx.instance && !definition.settings.log.relation) { + if (ctx.instance && !defSettings.log.relation) { originId = ctx.instance.id; changedModelId = ctx.instance.id; - } else if (definition.settings.log.relation) { - primaryKey = ctx.Model.relations[definition.settings.log.relation].keyFrom; + } else if (defSettings.log.relation) { + primaryKey = relations[defSettings.log.relation].keyFrom; if (ctx.where && ctx.where[primaryKey]) originId = ctx.where[primaryKey]; @@ -181,12 +223,16 @@ module.exports = function(Self) { } // Sets the changedModelValue to save and the instances changed in case its an updateAll - let showField = definition.settings.log.showField; + let showField = defSettings.log.showField; let where; if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) { changedModelId = []; where = []; - let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', showField, primaryKey]}, options); + let changedInstances = await appModels[definition.name].find({ + where: ctx.where, + fields: ['id', showField, primaryKey] + }, options); + changedInstances.forEach(element => { where.push(element[showField]); changedModelId.push(element.id); @@ -195,7 +241,6 @@ module.exports = function(Self) { } else if (ctx.hookState.oldInstance) where = ctx.instance[showField]; - // Set oldInstance, newInstance, userFk and action let oldInstance = {}; if (ctx.hookState.oldInstance) @@ -211,14 +256,14 @@ module.exports = function(Self) { let action = setActionType(ctx); - removeUnloggableProperties(definition, oldInstance); - removeUnloggableProperties(definition, newInstance); + removeUnloggable(definition, oldInstance); + removeUnloggable(definition, newInstance); let logRecord = { originFk: originId, userFk: userFk, action: action, - changedModel: ctx.Model.definition.name, + changedModel: definition.name, changedModelId: changedModelId, // Model property with an different data type will throw a NaN error changedModelValue: where, oldInstance: oldInstance, @@ -226,9 +271,9 @@ module.exports = function(Self) { }; let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx); - let logModel = definition.settings.log.model; + let logModel = defSettings.log.model; - await ctx.Model.app.models[logModel].create(logsToSave, options); + await appModels[logModel].create(logsToSave, options); } /** @@ -236,7 +281,7 @@ module.exports = function(Self) { * @param {*} definition Model definition * @param {*} properties Modified object properties */ - function removeUnloggableProperties(definition, properties) { + function removeUnloggable(definition, properties) { const propList = Object.keys(properties); const propDefs = new Map(); diff --git a/loopback/common/models/vn-model.js b/loopback/common/models/vn-model.js index d65ca71df..f56183df2 100644 --- a/loopback/common/models/vn-model.js +++ b/loopback/common/models/vn-model.js @@ -6,6 +6,7 @@ module.exports = function(Self) { Self.ParameterizedSQL = ParameterizedSQL; require('../methods/vn-model/getSetValues')(Self); + require('../methods/vn-model/getEnumValues')(Self); Object.assign(Self, { setup() { diff --git a/loopback/locale/es.json b/loopback/locale/es.json index eeb18fb73..d77b6d290 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -132,5 +132,5 @@ "Distance must be lesser than 1000": "La distancia debe ser inferior a 1000", "This ticket is deleted": "Este ticket está eliminado", "A travel with this data already exists": "Ya existe un travel con estos datos", - "AMOUNT_NOT_MATCH_GROUPING": "AMOUNT_NOT_MATCH_GROUPING" + "This thermograph id already exists": "La id del termógrafo ya existe" } \ No newline at end of file diff --git a/modules/client/front/address/create/index.js b/modules/client/front/address/create/index.js index b1629073d..79774cf93 100644 --- a/modules/client/front/address/create/index.js +++ b/modules/client/front/address/create/index.js @@ -29,7 +29,7 @@ export default class Controller extends Section { onCustomAgentAccept() { return this.$http.post(`CustomsAgents`, this.newCustomsAgent) - .then(res => this.address.customsAgentFk = res.data.id); + .then(res => this.address.customsAgentId = res.data.id); } get town() { diff --git a/modules/client/front/address/create/index.spec.js b/modules/client/front/address/create/index.spec.js index fb6567dce..4f332e75e 100644 --- a/modules/client/front/address/create/index.spec.js +++ b/modules/client/front/address/create/index.spec.js @@ -123,7 +123,7 @@ describe('Client', () => { controller.onCustomAgentAccept(); $httpBackend.flush(); - expect(controller.address.customsAgentFk).toEqual(1); + expect(controller.address.customsAgentId).toEqual(1); }); }); }); diff --git a/modules/client/front/address/edit/index.spec.js b/modules/client/front/address/edit/index.spec.js index b67138b6d..c4c1a78b5 100644 --- a/modules/client/front/address/edit/index.spec.js +++ b/modules/client/front/address/edit/index.spec.js @@ -64,7 +64,7 @@ describe('Client', () => { }); describe('onCustomAgentAccept()', () => { - it(`should create a new customs agent and then set the customsAgentFk property on the address`, () => { + it(`should now create a new customs agent and then set the customsAgentFk property on the address`, () => { const expectedResult = {id: 1, fiscalName: 'Customs agent one'}; $httpBackend.when('POST', 'CustomsAgents').respond(200, expectedResult); controller.onCustomAgentAccept(); diff --git a/modules/order/back/methods/order/getSourceValues.js b/modules/order/back/methods/order/getSourceValues.js index da3685c62..5e9f0e6dc 100644 --- a/modules/order/back/methods/order/getSourceValues.js +++ b/modules/order/back/methods/order/getSourceValues.js @@ -12,7 +12,7 @@ module.exports = Self => { } }); - Self.getSourceValues = async () => { + Self.getSourceValues = async() => { return Self.getSetValues('sourceApp'); }; }; diff --git a/modules/travel/back/methods/thermograph/createThermograph.js b/modules/travel/back/methods/thermograph/createThermograph.js new file mode 100644 index 000000000..bfca208fe --- /dev/null +++ b/modules/travel/back/methods/thermograph/createThermograph.js @@ -0,0 +1,60 @@ +module.exports = Self => { + Self.remoteMethod('createThermograph', { + description: 'Creates a new thermograph', + accessType: 'WRITE', + accepts: [{ + arg: 'thermographId', + type: 'String', + description: 'The thermograph id', + required: true + }, { + arg: 'model', + type: 'String', + description: 'The thermograph model', + required: true + }, { + arg: 'temperature', + type: 'String', + description: 'The thermograph temperature', + required: true + }, { + arg: 'warehouseId', + type: 'Number', + description: 'The warehouse id', + required: true + }], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/createThermograph`, + verb: 'POST' + } + }); + + Self.createThermograph = async(thermographId, model, temperature, warehouseId) => { + const models = Self.app.models; + const tx = await Self.beginTransaction({}); + + try { + const options = {transaction: tx}; + const thermograph = await models.Thermograph.create({ + id: thermographId, + model: model + }, options); + + await Self.rawSql(` + INSERT INTO travelThermograph(thermographFk, warehouseFk, temperature, created) + VALUES (?, ?,?, NOW()) + `, [thermograph.id, warehouseId, temperature], options); + + await tx.commit(); + + return thermograph; + } catch (err) { + await tx.rollback(); + throw err; + } + }; +}; diff --git a/modules/travel/back/methods/thermograph/getThermographModels.js b/modules/travel/back/methods/thermograph/getThermographModels.js new file mode 100644 index 000000000..188c3a530 --- /dev/null +++ b/modules/travel/back/methods/thermograph/getThermographModels.js @@ -0,0 +1,18 @@ +module.exports = Self => { + Self.remoteMethod('getThermographModels', { + description: 'Gets the thermograph models', + accessType: 'READ', + returns: { + type: ['String'], + root: true + }, + http: { + path: `/getThermographModels`, + verb: 'GET' + } + }); + + Self.getThermographModels = async() => { + return Self.getEnumValues('model'); + }; +}; diff --git a/modules/travel/back/methods/thermograph/specs/createThermograph.spec.js b/modules/travel/back/methods/thermograph/specs/createThermograph.spec.js new file mode 100644 index 000000000..733b713f0 --- /dev/null +++ b/modules/travel/back/methods/thermograph/specs/createThermograph.spec.js @@ -0,0 +1,49 @@ +const app = require('vn-loopback/server/server'); + +describe('Termograph createThermograph()', () => { + const models = app.models; + const thermographId = '99999-1'; + const model = 'DISPOSABLE'; + const temperature = 'COOL'; + const warehouseId = 1; + let createdThermograph; + + afterAll(async done => { + let travelThermograpToDelete = await models.TravelThermograph.findOne({where: {thermographFk: createdThermograph.id}}); + let thermograpToDelete = await models.Thermograph.findById(createdThermograph.id); + + await travelThermograpToDelete.destroy(); + await thermograpToDelete.destroy(); + + done(); + }); + + it(`should create a thermograph which is saved in both thermograph and travelThermograph`, async() => { + let createdTravelThermograpth = await models.TravelThermograph.findOne({where: {thermographFk: thermographId}}); + + expect(createdTravelThermograpth).toBeNull(); + + createdThermograph = await models.Thermograph.createThermograph(thermographId, model, temperature, warehouseId); + + expect(createdThermograph.id).toEqual(thermographId); + expect(createdThermograph.model).toEqual(model); + + createdTravelThermograpth = await models.TravelThermograph.findOne({where: {thermographFk: thermographId}}); + + expect(createdTravelThermograpth.warehouseFk).toEqual(warehouseId); + expect(createdTravelThermograpth.temperature).toEqual(temperature); + }); + + it(`should not be able to created duplicated entries`, async() => { + let error; + + try { + await models.Thermograph.createThermograph(thermographId, model, temperature, warehouseId); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.message).toBe('This thermograph id already exists'); + }); +}); diff --git a/modules/travel/back/methods/travel-thermograph/getThermographTemperatures.js b/modules/travel/back/methods/travel-thermograph/getThermographTemperatures.js new file mode 100644 index 000000000..1d510b513 --- /dev/null +++ b/modules/travel/back/methods/travel-thermograph/getThermographTemperatures.js @@ -0,0 +1,18 @@ +module.exports = Self => { + Self.remoteMethod('getThermographTemperatures', { + description: 'Gets the thermograph temperatures', + accessType: 'READ', + returns: { + type: ['String'], + root: true + }, + http: { + path: `/getThermographTemperatures`, + verb: 'GET' + } + }); + + Self.getThermographTemperatures = async() => { + return Self.getEnumValues('temperature'); + }; +}; diff --git a/modules/travel/back/models/thermograph.js b/modules/travel/back/models/thermograph.js new file mode 100644 index 000000000..3f95a0670 --- /dev/null +++ b/modules/travel/back/models/thermograph.js @@ -0,0 +1,12 @@ +let UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + require('../methods/thermograph/createThermograph')(Self); + require('../methods/thermograph/getThermographModels')(Self); + + Self.rewriteDbError(function(err) { + if (err.code === 'ER_DUP_ENTRY') + return new UserError(`This thermograph id already exists`); + return err; + }); +}; diff --git a/modules/travel/back/models/thermograph.json b/modules/travel/back/models/thermograph.json index 421ae4341..2519fffc4 100644 --- a/modules/travel/back/models/thermograph.json +++ b/modules/travel/back/models/thermograph.json @@ -10,10 +10,12 @@ "id": { "type": "String", "id": true, - "description": "Identifier" + "description": "Identifier", + "required": true }, "model": { - "type": "String" + "type": "String", + "required": true } } } diff --git a/modules/travel/back/models/travel-thermograph.js b/modules/travel/back/models/travel-thermograph.js index 0d70edd7e..a16e68b98 100644 --- a/modules/travel/back/models/travel-thermograph.js +++ b/modules/travel/back/models/travel-thermograph.js @@ -1,4 +1,5 @@ module.exports = Self => { require('../methods/travel-thermograph/allowedContentTypes')(Self); + require('../methods/travel-thermograph/getThermographTemperatures')(Self); }; diff --git a/modules/travel/back/models/travel-thermograph.json b/modules/travel/back/models/travel-thermograph.json index b8f7fa41a..70ee0de07 100644 --- a/modules/travel/back/models/travel-thermograph.json +++ b/modules/travel/back/models/travel-thermograph.json @@ -21,10 +21,15 @@ "type": "Date" }, "temperature": { - "type": "String" + "type": "String", + "required": true }, "result": { "type": "String" + }, + "warehouseFk": { + "type": "Number", + "required": true } }, "relations": { diff --git a/modules/travel/front/thermograph/create/index.html b/modules/travel/front/thermograph/create/index.html index 4b1fc8cf4..0232c1b12 100644 --- a/modules/travel/front/thermograph/create/index.html +++ b/modules/travel/front/thermograph/create/index.html @@ -17,6 +17,18 @@ where="{travelFk: null}" show-field="thermographFk" value-field="thermographFk"> + + {{thermographFk}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/travel/front/thermograph/create/index.js b/modules/travel/front/thermograph/create/index.js index 6c0464991..d398febf1 100644 --- a/modules/travel/front/thermograph/create/index.js +++ b/modules/travel/front/thermograph/create/index.js @@ -55,6 +55,28 @@ class Controller extends Section { }); } + onAddThermographClick(event) { + const defaultTemperature = 'COOL'; + const defaultModel = 'DISPOSABLE'; + + event.preventDefault(); + this.newThermograph = { + thermographId: this.thermographId, + warehouseId: this.warehouseId, + temperature: defaultTemperature, + model: defaultModel + }; + + this.$.modelsModel.refresh(); + this.$.temperaturesModel.refresh(); + this.$.newThermographDialog.show(); + } + + onNewThermographAccept() { + return this.$http.post(`Thermographs/createThermograph`, this.newThermograph) + .then(res => this.dms.thermographId = res.data.id); + } + onSubmit() { const query = `Travels/${this.travel.id}/createThermograph`; const options = { diff --git a/modules/travel/front/thermograph/create/index.spec.js b/modules/travel/front/thermograph/create/index.spec.js index 58fbe3991..23976fc96 100644 --- a/modules/travel/front/thermograph/create/index.spec.js +++ b/modules/travel/front/thermograph/create/index.spec.js @@ -63,5 +63,32 @@ describe('Ticket', () => { expect(controller.allowedContentTypes).toEqual('application/pdf, image/png, image/jpg'); }); }); + + describe('onAddThermographClick()', () => { + it('should call the show() function of the create thermograph dialog', () => { + controller.$.newThermographDialog = {show: jest.fn()}; + controller.$.modelsModel = {refresh: jest.fn()}; + controller.$.temperaturesModel = {refresh: jest.fn()}; + + const event = new Event('click'); + jest.spyOn(event, 'preventDefault'); + + controller.onAddThermographClick(event); + + expect(event.preventDefault).toHaveBeenCalledTimes(1); + expect(controller.$.newThermographDialog.show).toHaveBeenCalledTimes(1); + }); + }); + + describe('onNewThermographAccept()', () => { + it('should set the created thermograph id on to the controller for the autocomplete to use it', () => { + const response = {id: 'the created id'}; + $httpBackend.when('POST', `Thermographs/createThermograph`).respond(response); + controller.onNewThermographAccept(); + $httpBackend.flush(); + + expect(controller.dms.thermographId).toEqual(response.id); + }); + }); }); }); diff --git a/modules/travel/front/thermograph/locale/es.yml b/modules/travel/front/thermograph/locale/es.yml index 9f9be564b..0e3bc99fc 100644 --- a/modules/travel/front/thermograph/locale/es.yml +++ b/modules/travel/front/thermograph/locale/es.yml @@ -15,4 +15,6 @@ Add thermograph: Añadir termógrafo Edit thermograph: Editar termógrafo Thermograph deleted: Termógrafo eliminado Thermograph: Termógrafo -Are you sure you want to remove the thermograph?: ¿Seguro que quieres quitar el termógrafo? \ No newline at end of file +New thermograph: Nuevo termógrafo +Are you sure you want to remove the thermograph?: ¿Seguro que quieres quitar el termógrafo? +Identifier: Identificador \ No newline at end of file diff --git a/modules/worker/front/log/locale/es.yml b/modules/worker/front/log/locale/es.yml index d9c204e55..c48c571ac 100644 --- a/modules/worker/front/log/locale/es.yml +++ b/modules/worker/front/log/locale/es.yml @@ -1,3 +1,4 @@ +Date: Fecha Model: Modelo Action: Acción Author: Autor diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json index 8447ef9c4..7825e9735 100644 --- a/modules/worker/front/routes.json +++ b/modules/worker/front/routes.json @@ -62,7 +62,8 @@ "url" : "/log", "state": "worker.card.workerLog", "component": "vn-worker-log", - "description": "Log" + "description": "Log", + "acl": ["hr"] }, { "url": "/pbx", "state": "worker.card.pbx",