diff --git a/Jenkinsfile b/Jenkinsfile
index d87695cc4..06eb85561 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -69,13 +69,13 @@ pipeline {
}
}
}
- /* stage('Backend') {
- steps {
- nodejs('node-lts') {
- sh 'gulp launchBackTest --ci'
- }
- }
- } */
+ // stage('Backend') {
+ // steps {
+ // nodejs('node-lts') {
+ // sh 'gulp launchBackTest --ci'
+ // }
+ // }
+ // }
}
}
stage('Build') {
diff --git a/db/changes/10260-holidays/00-ACL.sql b/db/changes/10260-holidays/00-ACL.sql
index 1f8045bc7..e72e6fa73 100644
--- a/db/changes/10260-holidays/00-ACL.sql
+++ b/db/changes/10260-holidays/00-ACL.sql
@@ -1 +1,2 @@
-INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) VALUES ('Image', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee')
\ No newline at end of file
+INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) VALUES ('Image', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee');
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('PayDem', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
\ No newline at end of file
diff --git a/db/changes/10260-holidays/00-entryObservation.sql b/db/changes/10260-holidays/00-entryObservation.sql
new file mode 100644
index 000000000..6184cf3bd
--- /dev/null
+++ b/db/changes/10260-holidays/00-entryObservation.sql
@@ -0,0 +1,14 @@
+CREATE TABLE `vn`.`entryObservation` (
+ id int NOT NULL AUTO_INCREMENT,
+ entryFk int NOT NULL,
+ observationTypeFk TINYINT(3) UNSIGNED,
+ description TEXT,
+ PRIMARY KEY (id),
+ CONSTRAINT entry_id_entryFk
+ FOREIGN KEY (entryFk) REFERENCES entry(id),
+ CONSTRAINT observationType_id_observationTypeFk
+ FOREIGN KEY (observationTypeFk) REFERENCES observationType(id)
+);
+
+ALTER TABLE `vn`.`entryObservation`
+ADD UNIQUE INDEX `entryFk_observationTypeFk_UNIQUE` (`entryFk` ASC,`observationTypeFk` ASC);
\ No newline at end of file
diff --git a/db/changes/10260-holidays/00-zoneLog.sql b/db/changes/10260-holidays/00-zoneLog.sql
new file mode 100644
index 000000000..13d81bc92
--- /dev/null
+++ b/db/changes/10260-holidays/00-zoneLog.sql
@@ -0,0 +1,18 @@
+CREATE TABLE `vn`.`zoneLog` (
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+ `originFk` int(10) NOT NULL,
+ `userFk` int(10) unsigned DEFAULT NULL,
+ `action` set('insert','update','delete') COLLATE utf8_unicode_ci NOT NULL,
+ `creationDate` timestamp NULL DEFAULT current_timestamp(),
+ `description` text CHARACTER SET utf8 DEFAULT NULL,
+ `changedModel` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
+ `oldInstance` text COLLATE utf8_unicode_ci DEFAULT NULL,
+ `newInstance` text COLLATE utf8_unicode_ci DEFAULT NULL,
+ `changedModelId` int(11) DEFAULT NULL,
+ `changedModelValue` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `originFk` (`originFk`),
+ KEY `userFk` (`userFk`),
+ CONSTRAINT `zoneLog_ibfk_1` FOREIGN KEY (`originFk`) REFERENCES `vn`.`zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `zoneLog_ibfk_2` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
\ No newline at end of file
diff --git a/db/changes/10260-navidad/00-acl.sql b/db/changes/10260-navidad/00-acl.sql
deleted file mode 100644
index 9fba4786b..000000000
--- a/db/changes/10260-navidad/00-acl.sql
+++ /dev/null
@@ -1,2 +0,0 @@
- INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
- VALUES ('PayDem', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index b4fce628e..b8e9ef06e 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -1639,7 +1639,7 @@ INSERT INTO `vn`.`orderTicket`(`orderFk`, `ticketFk`)
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`)
VALUES
- (1, 2, 69),
+ (1, 1, 69),
(5, 1, 442),
(9, 1, 442),
(18, 3, 567);
diff --git a/e2e/helpers/extensions.js b/e2e/helpers/extensions.js
index b38b3f718..71f834476 100644
--- a/e2e/helpers/extensions.js
+++ b/e2e/helpers/extensions.js
@@ -300,9 +300,14 @@ let actions = {
},
waitForNumberOfElements: async function(selector, count) {
- return await this.waitForFunction((selector, count) => {
- return document.querySelectorAll(selector).length == count;
- }, {}, selector, count);
+ try {
+ await this.waitForFunction((selector, count) => {
+ return document.querySelectorAll(selector).length == count;
+ }, {}, selector, count);
+ } catch (error) {
+ const amount = await this.countElement(selector);
+ throw new Error(`actual amount of elements was: ${amount} instead of ${count}, ${error}`);
+ }
},
waitForClassNotPresent: async function(selector, className) {
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 11087d2d4..0ed3607ad 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -674,7 +674,14 @@ export default {
confirmButton: '.vn-confirm.shown button[response="accept"]',
},
routeIndex: {
- addNewRouteButton: 'vn-route-index a[ui-sref="route.create"]'
+ anyResult: 'vn-table a',
+ firstRouteCheckbox: 'a:nth-child(1) vn-td:nth-child(1) > vn-check',
+ addNewRouteButton: 'vn-route-index a[ui-sref="route.create"]',
+ cloneButton: 'vn-route-index button > vn-icon[icon="icon-clone"]',
+ submitClonationButton: 'tpl-buttons > button[response="accept"]',
+ openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
+ searchAgencyAutocomlete: 'vn-route-search-panel vn-autocomplete[ng-model="filter.agencyModeFk"]',
+ advancedSearchButton: 'vn-route-search-panel button[type=submit]',
},
createRouteView: {
worker: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
diff --git a/e2e/paths/02-client/15_user_config.spec.js b/e2e/paths/02-client/15_user_config.spec.js
index 0e18c07ad..2954ff522 100644
--- a/e2e/paths/02-client/15_user_config.spec.js
+++ b/e2e/paths/02-client/15_user_config.spec.js
@@ -71,7 +71,7 @@ describe('User config', () => {
expect(expectedLocalWarehouse).toBeTruthy();
expect(expectedLocalBank).toBeTruthy();
expect(expectedLocalCompany).toBeTruthy();
- expect(userWarehouse).toEqual('Warehouse Two');
+ expect(userWarehouse).toEqual('Warehouse One');
expect(userCompany).toEqual('CCs');
});
diff --git a/e2e/paths/08-route/03_create.spec.js b/e2e/paths/08-route/03_create_and_clone.spec.js
similarity index 63%
rename from e2e/paths/08-route/03_create.spec.js
rename to e2e/paths/08-route/03_create_and_clone.spec.js
index 670f7e17b..be758f788 100644
--- a/e2e/paths/08-route/03_create.spec.js
+++ b/e2e/paths/08-route/03_create_and_clone.spec.js
@@ -57,5 +57,36 @@ describe('Route create path', () => {
it(`should confirm the redirection to the created route summary`, async() => {
await page.waitForState('route.card.summary');
});
+
+ it(`should navigate back to the route index`, async() => {
+ await page.waitToClick(selectors.globalItems.returnToModuleIndexButton);
+ await page.waitForState('route.index');
+ });
+
+ let count;
+ it(`should count the amount of routes before clonation`, async() => {
+ await page.waitForFunction(selector => {
+ return document.querySelectorAll(selector).length > 6;
+ }, {}, selectors.routeIndex.anyResult);
+ count = await page.countElement(selectors.routeIndex.anyResult);
+
+ expect(count).toBeGreaterThanOrEqual(7);
+ });
+
+ it(`should clone the first route`, async() => {
+ await page.waitToClick(selectors.routeIndex.firstRouteCheckbox);
+ await page.waitToClick(selectors.routeIndex.cloneButton);
+ await page.waitToClick(selectors.routeIndex.submitClonationButton);
+ const message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('Data saved!');
+ });
+
+ it(`should reload the section and count the amount of routes after clonation`, async() => {
+ await page.waitForNumberOfElements(selectors.routeIndex.anyResult, count + 1);
+ const result = await page.countElement(selectors.routeIndex.anyResult);
+
+ expect(result).toEqual(count + 1);
+ });
});
});
diff --git a/front/salix/components/descriptor-popover/index.js b/front/salix/components/descriptor-popover/index.js
index b746f5c81..3ec5be1ae 100644
--- a/front/salix/components/descriptor-popover/index.js
+++ b/front/salix/components/descriptor-popover/index.js
@@ -4,6 +4,8 @@ import './style.scss';
export default class DescriptorPopover extends Popover {
show(parent, id) {
+ if (!id) return;
+
super.show(parent);
this.id = id;
diff --git a/loopback/common/models/vn-model.js b/loopback/common/models/vn-model.js
index f56183df2..cc3eede8e 100644
--- a/loopback/common/models/vn-model.js
+++ b/loopback/common/models/vn-model.js
@@ -145,9 +145,15 @@ module.exports = function(Self) {
rewriteDbError(replaceErrFunc) {
function replaceErr(err, replaceErrFunc) {
if (Array.isArray(err)) {
+ const errors = err.filter(error => {
+ return error != undefined && error != null;
+ });
let errs = [];
- for (let e of err)
- errs.push(replaceErrFunc(e));
+ for (let e of errors) {
+ if (!(e instanceof UserError))
+ errs.push(replaceErrFunc(e));
+ else errs.push(e);
+ }
return errs;
}
return replaceErrFunc(err);
diff --git a/loopback/server/connectors/vn-mysql.js b/loopback/server/connectors/vn-mysql.js
index 4e1345cd6..fde0ddcf6 100644
--- a/loopback/server/connectors/vn-mysql.js
+++ b/loopback/server/connectors/vn-mysql.js
@@ -246,3 +246,98 @@ exports.initialize = function initialize(dataSource, callback) {
dataSource.connector.connect(callback);
}
};
+
+MySQL.prototype.connect = function(callback) {
+ const self = this;
+ const options = generateOptions(this.settings);
+
+ if (this.client) {
+ if (callback) {
+ process.nextTick(function() {
+ callback(null, self.client);
+ });
+ }
+ } else
+ this.client = connectionHandler(options, callback);
+
+ function connectionHandler(options, callback) {
+ const client = mysql.createPool(options);
+ client.getConnection(function(err, connection) {
+ const conn = connection;
+ if (!err) {
+ if (self.debug)
+ debug('MySQL connection is established: ' + self.settings || {});
+
+ connection.release();
+ } else {
+ if (err.code == 'ECONNREFUSED' || err.code == 'PROTOCOL_CONNECTION_LOST') { // PROTOCOL_CONNECTION_LOST
+ console.error(`MySQL connection lost (${err.code}). Retrying..`);
+
+ return setTimeout(() =>
+ connectionHandler(options, callback), 5000);
+ }
+ if (self.debug || !callback)
+ console.error('MySQL connection is failed: ' + self.settings || {}, err);
+ }
+ callback && callback(err, conn);
+ });
+
+ return client;
+ }
+};
+
+function generateOptions(settings) {
+ const s = settings || {};
+ if (s.collation) {
+ // Charset should be first 'chunk' of collation.
+ s.charset = s.collation.substr(0, s.collation.indexOf('_'));
+ } else {
+ s.collation = 'utf8_general_ci';
+ s.charset = 'utf8';
+ }
+
+ s.supportBigNumbers = (s.supportBigNumbers || false);
+ s.timezone = (s.timezone || 'local');
+
+ if (isNaN(s.connectionLimit))
+ s.connectionLimit = 10;
+
+ let options;
+ if (s.url) {
+ // use url to override other settings if url provided
+ options = s.url;
+ } else {
+ options = {
+ host: s.host || s.hostname || 'localhost',
+ port: s.port || 3306,
+ user: s.username || s.user,
+ password: s.password,
+ timezone: s.timezone,
+ socketPath: s.socketPath,
+ charset: s.collation.toUpperCase(), // Correct by docs despite seeming odd.
+ supportBigNumbers: s.supportBigNumbers,
+ connectionLimit: s.connectionLimit,
+ };
+
+ // Don't configure the DB if the pool can be used for multiple DBs
+ if (!s.createDatabase)
+ options.database = s.database;
+
+ // Take other options for mysql driver
+ // See https://github.com/strongloop/loopback-connector-mysql/issues/46
+ for (const p in s) {
+ if (p === 'database' && s.createDatabase)
+ continue;
+
+ if (options[p] === undefined)
+ options[p] = s[p];
+ }
+ // Legacy UTC Date Processing fallback - SHOULD BE TRANSITIONAL
+ if (s.legacyUtcDateProcessing === undefined)
+ s.legacyUtcDateProcessing = true;
+
+ if (s.legacyUtcDateProcessing)
+ options.timezone = 'Z';
+ }
+ return options;
+}
diff --git a/modules/entry/back/model-config.json b/modules/entry/back/model-config.json
index 0e37b947f..eddef9c41 100644
--- a/modules/entry/back/model-config.json
+++ b/modules/entry/back/model-config.json
@@ -7,5 +7,8 @@
},
"EntryLog": {
"dataSource": "vn"
+ },
+ "EntryObservation": {
+ "dataSource": "vn"
}
}
diff --git a/modules/entry/back/models/entry-observation.js b/modules/entry/back/models/entry-observation.js
new file mode 100644
index 000000000..77d15d85c
--- /dev/null
+++ b/modules/entry/back/models/entry-observation.js
@@ -0,0 +1,9 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.rewriteDbError(function(err) {
+ if (err.code === 'ER_DUP_ENTRY')
+ return new UserError(`The observation type can't be repeated`);
+ return err;
+ });
+};
diff --git a/modules/entry/back/models/entry-observation.json b/modules/entry/back/models/entry-observation.json
new file mode 100644
index 000000000..535735d83
--- /dev/null
+++ b/modules/entry/back/models/entry-observation.json
@@ -0,0 +1,37 @@
+{
+ "name": "EntryObservation",
+ "base": "Loggable",
+ "log": {
+ "model": "EntryLog",
+ "relation": "entry"
+ },
+ "options": {
+ "mysql": {
+ "table": "entryObservation"
+ }
+ },
+ "properties": {
+ "id": {
+ "id": true,
+ "type": "Number",
+ "description": "Identifier"
+ },
+ "description": {
+ "type": "String",
+ "required": true
+ }
+ },
+ "relations": {
+ "entry": {
+ "type": "belongsTo",
+ "model": "Entry",
+ "foreignKey": "entryFk"
+ },
+ "observationType": {
+ "type": "belongsTo",
+ "model": "ObservationType",
+ "foreignKey": "observationTypeFk",
+ "required": true
+ }
+ }
+}
diff --git a/modules/entry/front/index.js b/modules/entry/front/index.js
index e92f23fc1..14aecc8db 100644
--- a/modules/entry/front/index.js
+++ b/modules/entry/front/index.js
@@ -10,6 +10,7 @@ import './latest-buys-search-panel';
import './descriptor';
import './descriptor-popover';
import './card';
+import './note';
import './summary';
import './log';
import './buy';
diff --git a/modules/entry/front/note/index.html b/modules/entry/front/note/index.html
new file mode 100644
index 000000000..5f3c7f77e
--- /dev/null
+++ b/modules/entry/front/note/index.html
@@ -0,0 +1,72 @@
+