Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2278-ticket_summary_invoiceOut
This commit is contained in:
commit
c4be76977d
|
@ -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 \
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
INSERT IGNORE INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('WorkerLog', '*', 'READ', 'ALLOW', 'ROLE', 'hr');
|
|
@ -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');
|
||||
|
|
@ -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',
|
||||
|
|
|
@ -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() => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -123,7 +123,7 @@ describe('Client', () => {
|
|||
controller.onCustomAgentAccept();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.address.customsAgentFk).toEqual(1);
|
||||
expect(controller.address.customsAgentId).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.getSourceValues = async () => {
|
||||
Self.getSourceValues = async() => {
|
||||
return Self.getSetValues('sourceApp');
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -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');
|
||||
};
|
||||
};
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
});
|
||||
};
|
|
@ -10,10 +10,12 @@
|
|||
"id": {
|
||||
"type": "String",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
"description": "Identifier",
|
||||
"required": true
|
||||
},
|
||||
"model": {
|
||||
"type": "String"
|
||||
"type": "String",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/travel-thermograph/allowedContentTypes')(Self);
|
||||
require('../methods/travel-thermograph/getThermographTemperatures')(Self);
|
||||
};
|
||||
|
||||
|
|
|
@ -21,10 +21,15 @@
|
|||
"type": "Date"
|
||||
},
|
||||
"temperature": {
|
||||
"type": "String"
|
||||
"type": "String",
|
||||
"required": true
|
||||
},
|
||||
"result": {
|
||||
"type": "String"
|
||||
},
|
||||
"warehouseFk": {
|
||||
"type": "Number",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
where="{travelFk: null}"
|
||||
show-field="thermographFk"
|
||||
value-field="thermographFk">
|
||||
<tpl-item>
|
||||
{{thermographFk}}
|
||||
</tpl-item>
|
||||
<append>
|
||||
<vn-icon-button
|
||||
icon="add_circle"
|
||||
vn-tooltip="New thermograph"
|
||||
ng-click="$ctrl.onAddThermographClick($event)"
|
||||
vn-acl="buyer"
|
||||
vn-acl-action="remove">
|
||||
</vn-icon-button>
|
||||
</append>
|
||||
</vn-autocomplete>
|
||||
<vn-textfield vn-one
|
||||
label="State"
|
||||
|
@ -84,3 +96,63 @@
|
|||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Create thermograph dialog -->
|
||||
<vn-crud-model
|
||||
vn-id="modelsModel"
|
||||
url="Thermographs/getThermographModels"
|
||||
data="thermographModels">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
vn-id="temperaturesModel"
|
||||
url="TravelThermographs/getThermographTemperatures"
|
||||
data="thermographTemperatures">
|
||||
</vn-crud-model>
|
||||
<vn-dialog class="edit"
|
||||
vn-id="newThermographDialog"
|
||||
on-accept="$ctrl.onNewThermographAccept()"
|
||||
message="New thermograph">
|
||||
<tpl-body>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
required="true"
|
||||
label="Identifier"
|
||||
ng-model="$ctrl.newThermograph.thermographId"
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
required="true"
|
||||
label="Model"
|
||||
ng-model="$ctrl.newThermograph.model"
|
||||
data="thermographModels"
|
||||
show-field="value"
|
||||
value-field="value">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
required="true"
|
||||
label="Warehouse"
|
||||
ng-model="$ctrl.newThermograph.warehouseId"
|
||||
url="Warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
required="true"
|
||||
label="Temperature"
|
||||
ng-model="$ctrl.newThermograph.temperature"
|
||||
data="thermographTemperatures"
|
||||
show-field="value"
|
||||
value-field="value">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||
<button response="accept" translate>Create</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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?
|
||||
New thermograph: Nuevo termógrafo
|
||||
Are you sure you want to remove the thermograph?: ¿Seguro que quieres quitar el termógrafo?
|
||||
Identifier: Identificador
|
|
@ -1,3 +1,4 @@
|
|||
Date: Fecha
|
||||
Model: Modelo
|
||||
Action: Acción
|
||||
Author: Autor
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue