From 3b5b5112e0b617d84e6611181f3fba75b04dbed6 Mon Sep 17 00:00:00 2001 From: Joan Sanchez Date: Tue, 12 Mar 2019 07:52:12 +0100 Subject: [PATCH 1/3] fixed sepa-core get mandate by supplier id --- print/report/rpt-sepa-core/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/print/report/rpt-sepa-core/index.js b/print/report/rpt-sepa-core/index.js index 9c4ee6ac6..c98ddbcb4 100755 --- a/print/report/rpt-sepa-core/index.js +++ b/print/report/rpt-sepa-core/index.js @@ -11,7 +11,7 @@ module.exports = { if (!params.companyFk) throw new UserException('No company id specified'); - return this.methods.fetchClient(params.clientFk) + return this.methods.fetchClient(params.clientFk, params.companyFk) .then(([[client]]) => { if (!client) throw new UserException('No client data found'); @@ -24,7 +24,7 @@ module.exports = { this.$i18n.locale = this.locale; }, methods: { - fetchClient(clientFk) { + fetchClient(clientFk, companyFk) { return database.pool.query( `SELECT c.id clientId, @@ -46,12 +46,12 @@ module.exports = { FROM client c JOIN account.user u ON u.id = c.id JOIN country ct ON ct.id = c.countryFk + JOIN mandate m ON m.clientFk = c.id AND m.finished IS NULL + JOIN supplier s ON s.id = m.companyFk + JOIN country sc ON sc.id = s.countryFk + JOIN province sp ON sp.id = s.provinceFk LEFT JOIN province p ON p.id = c.provinceFk - LEFT JOIN mandate m ON m.clientFk = c.id AND m.finished IS NULL - LEFT JOIN supplier s ON s.id = m.companyFk - LEFT JOIN country sc ON sc.id = s.countryFk - LEFT JOIN province sp ON sp.id = s.provinceFk - WHERE c.id = ?`, [clientFk]); + WHERE m.companyFk = ? AND c.id = ?`, [companyFk, clientFk]); }, dated: () => { return strftime('%d-%m-%Y', new Date()); From 51cf09da2d7d558aeb5912d160f4d0c85a2af55b Mon Sep 17 00:00:00 2001 From: Joan Sanchez Date: Tue, 12 Mar 2019 14:21:22 +0100 Subject: [PATCH 2/3] fixed sepa core mandate by supplier id --- print/report/rpt-sepa-core/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/print/report/rpt-sepa-core/index.js b/print/report/rpt-sepa-core/index.js index 9c4ee6ac6..c98ddbcb4 100755 --- a/print/report/rpt-sepa-core/index.js +++ b/print/report/rpt-sepa-core/index.js @@ -11,7 +11,7 @@ module.exports = { if (!params.companyFk) throw new UserException('No company id specified'); - return this.methods.fetchClient(params.clientFk) + return this.methods.fetchClient(params.clientFk, params.companyFk) .then(([[client]]) => { if (!client) throw new UserException('No client data found'); @@ -24,7 +24,7 @@ module.exports = { this.$i18n.locale = this.locale; }, methods: { - fetchClient(clientFk) { + fetchClient(clientFk, companyFk) { return database.pool.query( `SELECT c.id clientId, @@ -46,12 +46,12 @@ module.exports = { FROM client c JOIN account.user u ON u.id = c.id JOIN country ct ON ct.id = c.countryFk + JOIN mandate m ON m.clientFk = c.id AND m.finished IS NULL + JOIN supplier s ON s.id = m.companyFk + JOIN country sc ON sc.id = s.countryFk + JOIN province sp ON sp.id = s.provinceFk LEFT JOIN province p ON p.id = c.provinceFk - LEFT JOIN mandate m ON m.clientFk = c.id AND m.finished IS NULL - LEFT JOIN supplier s ON s.id = m.companyFk - LEFT JOIN country sc ON sc.id = s.countryFk - LEFT JOIN province sp ON sp.id = s.provinceFk - WHERE c.id = ?`, [clientFk]); + WHERE m.companyFk = ? AND c.id = ?`, [companyFk, clientFk]); }, dated: () => { return strftime('%d-%m-%Y', new Date()); From 92170c34d506a8d9e2b236eafacd225783c11080 Mon Sep 17 00:00:00 2001 From: Joan Sanchez Date: Tue, 12 Mar 2019 15:04:09 +0100 Subject: [PATCH 3/3] worker department #1133 --- db/install/changes/0-ACL.sql | 4 +- db/install/changes/12-department.sql | 21 +++++ db/install/changes/13-nodeAdd.sql | 83 ++++++++++++++++++ db/install/changes/14-nodeRecalc.sql | 28 ++++++ db/install/changes/15-zoneGeo.sql | 4 + front/core/components/treeview/child.html | 51 ++++++++--- front/core/components/treeview/child.js | 23 ++++- front/core/components/treeview/index.html | 10 ++- front/core/components/treeview/index.js | 24 +++-- front/core/components/treeview/style.scss | 6 +- front/core/directives/acl.js | 10 ++- front/core/locale/es.yml | 4 +- loopback/common/models/vn-model.js | 63 ++++++-------- loopback/locale/en.json | 3 +- loopback/locale/es.json | 3 +- .../agency/back/methods/zone-geo/getLeaves.js | 20 +++-- modules/agency/front/location/index.html | 2 +- .../back/methods/department/getLeaves.js | 87 +++++++++++++++++++ .../worker/back/methods/department/nodeAdd.js | 43 +++++++++ .../back/methods/department/nodeDelete.js | 29 +++++++ modules/worker/back/models/department.js | 19 ++++ modules/worker/front/department/index.html | 45 ++++++++++ modules/worker/front/department/index.js | 78 +++++++++++++++++ modules/worker/front/department/locale/es.yml | 3 + modules/worker/front/index.js | 1 + modules/worker/front/index/index.html | 22 +++-- modules/worker/front/index/index.js | 13 ++- modules/worker/front/index/style.scss | 11 +++ modules/worker/front/locale/es.yml | 1 + modules/worker/front/routes.json | 9 +- 30 files changed, 639 insertions(+), 81 deletions(-) create mode 100644 db/install/changes/12-department.sql create mode 100644 db/install/changes/13-nodeAdd.sql create mode 100644 db/install/changes/14-nodeRecalc.sql create mode 100644 db/install/changes/15-zoneGeo.sql create mode 100644 modules/worker/back/methods/department/getLeaves.js create mode 100644 modules/worker/back/methods/department/nodeAdd.js create mode 100644 modules/worker/back/methods/department/nodeDelete.js create mode 100644 modules/worker/back/models/department.js create mode 100644 modules/worker/front/department/index.html create mode 100644 modules/worker/front/department/index.js create mode 100644 modules/worker/front/department/locale/es.yml create mode 100644 modules/worker/front/index/style.scss diff --git a/db/install/changes/0-ACL.sql b/db/install/changes/0-ACL.sql index 099e3c5a9..a2f3991f1 100644 --- a/db/install/changes/0-ACL.sql +++ b/db/install/changes/0-ACL.sql @@ -1,2 +1,4 @@ INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (149, 'Sip', '*', 'WRITE', 'ALLOW', 'ROLE', 'hr'); -INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (150, 'Sip', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); \ No newline at end of file +INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (150, 'Sip', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); +INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (151, 'Department','*','READ','ALLOW','ROLE','employee'); +INSERT INTO `salix`.`ACL` (`id`,`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES (152, 'Department','*','WRITE','ALLOW','ROLE','hr'); diff --git a/db/install/changes/12-department.sql b/db/install/changes/12-department.sql new file mode 100644 index 000000000..38c68a45c --- /dev/null +++ b/db/install/changes/12-department.sql @@ -0,0 +1,21 @@ +USE `vn2008`; + +ALTER TABLE vn2008.department ADD `depth` int DEFAULT 0 NOT NULL; +ALTER TABLE vn2008.department ADD sons int DEFAULT 0 NOT NULL; + +USE `vn`; + +CREATE +OR REPLACE +VIEW `vn`.`department` AS select + `b`.`department_id` AS `id`, + `b`.`name` AS `name`, + `b`.`father_id` AS `fatherFk`, + `b`.`production` AS `isProduction`, + `b`.`lft` AS `lft`, + `b`.`rgt` AS `rgt`, + `b`.`isSelected` AS `isSelected`, + `b`.`depth` AS `depth`, + `b`.`sons` AS `sons` +from + `vn2008`.`department` `b`; \ No newline at end of file diff --git a/db/install/changes/13-nodeAdd.sql b/db/install/changes/13-nodeAdd.sql new file mode 100644 index 000000000..b3c537548 --- /dev/null +++ b/db/install/changes/13-nodeAdd.sql @@ -0,0 +1,83 @@ +DROP PROCEDURE IF EXISTS nst.NodeAdd; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`%` PROCEDURE `nst`.`nodeAdd`(IN `vScheme` VARCHAR(45), IN `vTable` VARCHAR(45), IN `vParentFk` INT, IN `vChild` VARCHAR(100)) +BEGIN + DECLARE vSql TEXT; + DECLARE vTableClone VARCHAR(45); + + SET vTableClone = CONCAT(vTable, 'Clone'); + + CALL util.exec(CONCAT('DROP TEMPORARY TABLE IF EXISTS tmp.', vTableClone)); + CALL util.exec(CONCAT( + 'CREATE TEMPORARY TABLE tmp.', vTableClone, + ' ENGINE = MEMORY' + ' SELECT * FROM ', vScheme, '.', vTable + )); + + -- Check parent childs + SET vSql = sql_printf(' + SELECT COUNT(c.id) INTO @childs + FROM %t.%t p + LEFT JOIN %t.%t c ON c.lft BETWEEN p.lft AND p.rgt AND c.id != %v + WHERE p.id = %v', + vScheme, vTable, 'tmp', vTableClone, vParentFk, vParentFk); + SET @qrySql := vSql; + + PREPARE stmt FROM @qrySql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + -- Select left from last child + IF @childs = 0 THEN + SET vSql = sql_printf('SELECT lft INTO @vLeft FROM %t.%t WHERE id = %v', vScheme, vTable, vParentFk); + SET @qrySql := vSql; + ELSE + SET vSql = sql_printf(' + SELECT c.rgt INTO @vLeft + FROM %t.%t p + JOIN %t.%t c ON c.lft BETWEEN p.lft AND p.rgt + WHERE p.id = %v + ORDER BY c.lft + DESC LIMIT 1', + vScheme, vTable, 'tmp', vTableClone, vParentFk); + SET @qrySql := vSql; + END IF; + + PREPARE stmt FROM @qrySql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + -- Update right + SET vSql = sql_printf('UPDATE %t.%t SET rgt = rgt + 2 WHERE rgt > %v ORDER BY rgt DESC', vScheme, vTable, @vLeft); + SET @qrySql := vSql; + + PREPARE stmt FROM @qrySql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + SET vSql = sql_printf('UPDATE %t.%t SET lft = lft + 2 WHERE lft > %v ORDER BY lft DESC', vScheme, vTable, @vLeft); + SET @qrySql := vSql; + + PREPARE stmt FROM @qrySql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + -- Escape character + SET vChild = REPLACE(vChild, "'", "\\'"); + + -- Add child + SET vSql = sql_printf('INSERT INTO %t.%t (name, lft, rgt) VALUES (%v, %v, %v)', vScheme, vTable, vChild, @vLeft + 1, @vLeft + 2); + SET @qrySql := vSql; + + PREPARE stmt FROM @qrySql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + + SELECT id, name, lft, rgt, depth, sons FROM vn.department + WHERE id = LAST_INSERT_ID(); + + CALL util.exec(CONCAT('DROP TEMPORARY TABLE tmp.', vTableClone)); +END$$ +DELIMITER ; diff --git a/db/install/changes/14-nodeRecalc.sql b/db/install/changes/14-nodeRecalc.sql new file mode 100644 index 000000000..a8f4d932c --- /dev/null +++ b/db/install/changes/14-nodeRecalc.sql @@ -0,0 +1,28 @@ +DROP PROCEDURE IF EXISTS nst.nodeRecalc; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`%` PROCEDURE `nst`.`nodeRecalc`(IN `vScheme` VARCHAR(45), IN `vTable` VARCHAR(45)) +BEGIN + CALL util.exec (sql_printf ( + 'UPDATE %t.%t d + JOIN (SELECT + node.id, + COUNT(parent.id) - 1 as depth, + cast((node.rgt - node.lft - 1) / 2 as DECIMAL) as sons + FROM + %t.%t AS node, + %t.%t AS parent + WHERE node.lft BETWEEN parent.lft AND parent.rgt + GROUP BY node.id + ORDER BY node.lft) n ON n.id = d.id + SET d.`depth` = n.depth, d.sons = n.sons', + vScheme, + vTable, + vScheme, + vTable, + vScheme, + vTable + )); +END$$ +DELIMITER ; diff --git a/db/install/changes/15-zoneGeo.sql b/db/install/changes/15-zoneGeo.sql new file mode 100644 index 000000000..1037fe089 --- /dev/null +++ b/db/install/changes/15-zoneGeo.sql @@ -0,0 +1,4 @@ +USE `vn`; + +CREATE UNIQUE INDEX zoneGeo_lft_IDX USING BTREE ON vn.zoneGeo (lft); +CREATE UNIQUE INDEX zoneGeo_rgt_IDX USING BTREE ON vn.zoneGeo (rgt); \ No newline at end of file diff --git a/front/core/components/treeview/child.html b/front/core/components/treeview/child.html index 8589790ec..a079d8f40 100644 --- a/front/core/components/treeview/child.html +++ b/front/core/components/treeview/child.html @@ -1,25 +1,54 @@ -
    +
    • - + -
      - + + - {{::item.name}} + {{::item.name}} + + + + + + + + +
    • +
    • + + + + +
      + Create new one
      -
    \ No newline at end of file diff --git a/front/core/components/treeview/child.js b/front/core/components/treeview/child.js index b5bdb67e5..ca5b59efb 100644 --- a/front/core/components/treeview/child.js +++ b/front/core/components/treeview/child.js @@ -4,6 +4,7 @@ import Component from '../../lib/component'; class Controller extends Component { constructor($element, $scope) { super($element, $scope); + this.$scope = $scope; } toggle(item) { @@ -13,13 +14,33 @@ class Controller extends Component { select(item, value) { this.treeview.onSelection(item, value); } + + onClick(icon, item, parent, index) { + let parentScope = this.$scope.$parent.$parent; + let parentController = parentScope.$ctrl; + icon.callback.call(parentController, item, parent, index); + } + + onCreate(parent) { + this.treeview.onCreate(parent); + } + + get isInsertable() { + return Array.isArray(this.parent) || this.parent.childs; + } } ngModule.component('vnTreeviewChild', { template: require('./child.html'), controller: Controller, bindings: { - items: '<' + items: '<', + parent: '<', + icons: ' + + diff --git a/front/core/components/treeview/index.js b/front/core/components/treeview/index.js index 0737bf8cb..9f2a0a3fa 100644 --- a/front/core/components/treeview/index.js +++ b/front/core/components/treeview/index.js @@ -23,10 +23,19 @@ export default class Treeview extends Component { }); } + /** + * Emits selection event + * @param {Object} item - Selected item + * @param {Boolean} value - Changed value + */ onSelection(item, value) { this.emit('selection', {item, value}); } + onCreate(parent) { + this.emit('create', {parent}); + } + onToggle(item) { if (item.active) item.childs = undefined; @@ -45,12 +54,12 @@ export default class Treeview extends Component { } item.childs = newData.sort((a, b) => { - if (b.isIncluded !== a.isIncluded) { - if (a.isIncluded == null) + if (b.selected !== a.selected) { + if (a.selected == null) return 1; - if (b.isIncluded == null) + if (b.selected == null) return -1; - return b.isIncluded - a.isIncluded; + return b.selected - a.selected; } return a.name.localeCompare(b.name); @@ -68,6 +77,11 @@ ngModule.component('vnTreeview', { template: require('./index.html'), controller: Treeview, bindings: { - model: '<' + model: '<', + icons: ' vn-horizontal { @@ -62,4 +62,8 @@ vn-treeview { } } } + + vn-icon-button { + padding: 0 + } } diff --git a/front/core/directives/acl.js b/front/core/directives/acl.js index 2ff35a87f..46e8c582e 100644 --- a/front/core/directives/acl.js +++ b/front/core/directives/acl.js @@ -36,7 +36,7 @@ function vnAcl(aclService, $timeout) { return conditions; } - function permissionElement($element, action) { + function permissionElement($scope, $element, action) { if (!aclService.hasAny(acls)) { if (action === 'disabled') { let input = $element[0]; @@ -72,9 +72,13 @@ function vnAcl(aclService, $timeout) { priority: -1, link: function($scope, $element, $attrs) { acls = $attrs.vnAcl.split(',').map(i => i.trim()); + + if (acls[0] == '') return; + let action = $attrs.vnAclAction || 'disabled'; let conditions = getDynamicConditions($attrs); - permissionElement($element, action); + + permissionElement($scope, $element, action); if (Object.keys(conditions).length) { let watchConditions = $scope.$watch(() => { @@ -82,7 +86,7 @@ function vnAcl(aclService, $timeout) { let hasPermission = $scope.$eval($attrs[attrName]); if (!hasPermission) { updateAcls(conditions[attrName].role, hasPermission); - permissionElement($element, action); + permissionElement($scope, $element, action); delete conditions[attrName]; } }); diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml index 2a5af89ce..7c413dd22 100644 --- a/front/core/locale/es.yml +++ b/front/core/locale/es.yml @@ -39,4 +39,6 @@ November: Noviembre December: Diciembre Has delivery: Hay reparto Loading: Cargando -Fields to show: Campos a mostrar \ No newline at end of file +Fields to show: Campos a mostrar +Create new one: Crear nuevo +Toggle: Desplegar/Plegar \ No newline at end of file diff --git a/loopback/common/models/vn-model.js b/loopback/common/models/vn-model.js index 539b4aa98..41287c2a7 100644 --- a/loopback/common/models/vn-model.js +++ b/loopback/common/models/vn-model.js @@ -128,44 +128,33 @@ module.exports = function(Self) { return replaceErrFunc(err); } + function rewriteMethod(methodName) { + const realMethod = this[methodName]; + return async(data, options, cb) => { + if (options instanceof Function) { + cb = options; + options = null; + } + + try { + await realMethod.call(this, data, options); + if (cb) cb(); + } catch (err) { + let myErr = replaceErr(err, replaceErrFunc); + if (cb) + cb(myErr); + else + throw myErr; + } + }; + } + this.once('attached', () => { - let realUpsert = this.upsert; - this.upsert = async(data, options, cb) => { - if (options instanceof Function) { - cb = options; - options = null; - } - - try { - await realUpsert.call(this, data, options); - if (cb) cb(); - } catch (err) { - let myErr = replaceErr(err, replaceErrFunc); - if (cb) - cb(myErr); - else - throw myErr; - } - }; - - let realCreate = this.create; - this.create = async(data, options, cb) => { - if (options instanceof Function) { - cb = options; - options = null; - } - - try { - await realCreate.call(this, data, options); - if (cb) cb(); - } catch (err) { - let myErr = replaceErr(err, replaceErrFunc); - if (cb) - cb(myErr); - else - throw myErr; - } - }; + this.remove = + this.deleteAll = + this.destroyAll = rewriteMethod.call(this, 'remove'); + this.upsert = rewriteMethod.call(this, 'upsert'); + this.create = rewriteMethod.call(this, 'create'); }); }, diff --git a/loopback/locale/en.json b/loopback/locale/en.json index ff07a26e8..f18137966 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -38,5 +38,6 @@ "You can't create a ticket for a frozen client": "You can't create a ticket for a frozen client", "can't be blank": "can't be blank", "Street cannot be empty": "Street cannot be empty", - "City cannot be empty": "City cannot be empty" + "City cannot be empty": "City cannot be empty", + "EXTENSION_INVALID_FORMAT": "EXTENSION_INVALID_FORMAT" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 4cf5f65c1..d4f3733b0 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -72,5 +72,6 @@ "Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido", "Street cannot be empty": "Dirección no puede estar en blanco", "City cannot be empty": "Cuidad no puede estar en blanco", - "Code cannot be blank": "Código no puede estar en blanco" + "Code cannot be blank": "Código no puede estar en blanco", + "You cannot remove this department": "No puedes eliminar este departamento" } \ No newline at end of file diff --git a/modules/agency/back/methods/zone-geo/getLeaves.js b/modules/agency/back/methods/zone-geo/getLeaves.js index 64bb68318..7209f5268 100644 --- a/modules/agency/back/methods/zone-geo/getLeaves.js +++ b/modules/agency/back/methods/zone-geo/getLeaves.js @@ -73,7 +73,7 @@ module.exports = Self => { zg.rgt, zg.depth, zg.sons, - IF(ch.id = zg.id, isIncluded, null) isIncluded + IF(ch.id = zg.id, isIncluded, null) selected FROM zoneGeo zg JOIN tmp.checkedChilds ch ON zg.lft <= ch.lft AND zg.rgt >= ch.rgt @@ -86,7 +86,7 @@ module.exports = Self => { child.rgt, child.depth, child.sons, - zi.isIncluded + zi.isIncluded AS selected FROM zoneGeo parent JOIN zoneGeo child ON child.lft > parent.lft AND child.rgt < parent.rgt @@ -122,9 +122,11 @@ module.exports = Self => { function nestLeaves(elements) { elements.forEach(element => { - element.childs = Object.assign([], getLeaves(element)); - - nestLeaves(element.childs); + let childs = Object.assign([], getLeaves(element)); + if (childs.length > 0) { + element.childs = childs; + nestLeaves(element.childs); + } }); } @@ -142,12 +144,12 @@ module.exports = Self => { function sortNodes(nodes) { return nodes.sort((a, b) => { - if (b.isIncluded !== a.isIncluded) { - if (a.isIncluded == null) + if (b.selected !== a.selected) { + if (a.selected == null) return 1; - if (b.isIncluded == null) + if (b.selected == null) return -1; - return b.isIncluded - a.isIncluded; + return b.selected - a.selected; } return a.name.localeCompare(b.name); diff --git a/modules/agency/front/location/index.html b/modules/agency/front/location/index.html index 4482ff52e..c33a71e3c 100644 --- a/modules/agency/front/location/index.html +++ b/modules/agency/front/location/index.html @@ -12,7 +12,7 @@ on-search="$ctrl.onSearch()" vn-focus> - diff --git a/modules/worker/back/methods/department/getLeaves.js b/modules/worker/back/methods/department/getLeaves.js new file mode 100644 index 000000000..a97dc4f1d --- /dev/null +++ b/modules/worker/back/methods/department/getLeaves.js @@ -0,0 +1,87 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('getLeaves', { + description: 'Returns the first shipped and landed possible for params', + accessType: '', + accepts: [{ + arg: 'parentFk', + type: 'Number', + default: 1, + required: false, + }, + { + arg: 'filter', + type: 'Object', + description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string', + http: {source: 'query'} + }], + returns: { + type: ['object'], + root: true + }, + http: { + path: `/getLeaves`, + verb: 'GET' + } + }); + + Self.getLeaves = async(parentFk = 1, filter) => { + let conn = Self.dataSource.connector; + let stmt = new ParameterizedSQL( + `SELECT + child.id, + child.name, + child.lft, + child.rgt, + child.depth, + child.sons + FROM department parent + JOIN department child ON child.lft > parent.lft + AND child.rgt < parent.rgt + AND child.depth = parent.depth + 1 + WHERE parent.id = ?`, [parentFk]); + + // Get nodes from depth greather than Origin + stmt.merge(conn.makeSuffix(filter)); + + const nodes = await Self.rawStmt(stmt); + + if (nodes.length == 0) + return nodes; + + // Get parent nodes + const minorDepth = nodes.reduce((a, b) => { + return b < a ? b : a; + }).depth; + + const parentNodes = nodes.filter(element => { + return element.depth === minorDepth; + }); + const leaves = Object.assign([], parentNodes); + + nestLeaves(leaves); + + function nestLeaves(elements) { + elements.forEach(element => { + let childs = Object.assign([], getLeaves(element)); + if (childs.length > 0) { + element.childs = childs; + nestLeaves(element.childs); + } + }); + } + + function getLeaves(parent) { + let elements = nodes.filter(element => { + return element.lft > parent.lft && element.rgt < parent.rgt + && element.depth === parent.depth + 1; + }); + + return elements; + } + + return leaves; + }; +}; diff --git a/modules/worker/back/methods/department/nodeAdd.js b/modules/worker/back/methods/department/nodeAdd.js new file mode 100644 index 000000000..bbbcd82ef --- /dev/null +++ b/modules/worker/back/methods/department/nodeAdd.js @@ -0,0 +1,43 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('nodeAdd', { + description: 'Returns the first shipped and landed possible for params', + accessType: '', + accepts: [{ + arg: 'parentFk', + type: 'Number', + required: false, + }, + { + arg: 'name', + type: 'String', + required: true, + }], + returns: { + type: 'object', + root: true + }, + http: { + path: `/nodeAdd`, + verb: 'POST' + } + }); + + Self.nodeAdd = async(parentFk = 1, name) => { + let stmts = []; + let conn = Self.dataSource.connector; + let nodeIndex = stmts.push(new ParameterizedSQL( + `CALL nst.NodeAdd('vn', 'department', ?, ?)`, [parentFk, name])) - 1; + + stmts.push(`CALL nst.nodeRecalc('vn', 'department')`); + + + let sql = ParameterizedSQL.join(stmts, ';'); + let result = await conn.executeStmt(sql); + let [node] = result[nodeIndex]; + + return node; + }; +}; diff --git a/modules/worker/back/methods/department/nodeDelete.js b/modules/worker/back/methods/department/nodeDelete.js new file mode 100644 index 000000000..22cf5312c --- /dev/null +++ b/modules/worker/back/methods/department/nodeDelete.js @@ -0,0 +1,29 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('nodeDelete', { + description: 'Returns the first shipped and landed possible for params', + accessType: '', + accepts: [{ + arg: 'parentFk', + type: 'Number', + required: false, + }], + returns: { + type: ['object'], + root: true + }, + http: { + path: `/nodeDelete`, + verb: 'POST' + } + }); + + Self.nodeDelete = async parentFk => { + let stmt = new ParameterizedSQL( + `CALL nst.nodeDelete('vn', 'department', ?)`, [parentFk]); + + return await Self.rawStmt(stmt); + }; +}; diff --git a/modules/worker/back/models/department.js b/modules/worker/back/models/department.js new file mode 100644 index 000000000..99b470dbb --- /dev/null +++ b/modules/worker/back/models/department.js @@ -0,0 +1,19 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + require('../methods/department/getLeaves')(Self); + require('../methods/department/nodeAdd')(Self); + require('../methods/department/nodeDelete')(Self); + + Self.rewriteDbError(function(err) { + if (err.code === 'ER_ROW_IS_REFERENCED_2') + return new UserError(`You cannot remove this department`); + return err; + }); + + Self.rewriteDbError(function(err) { + if (err.code === 'ER_DUP_ENTRY') + return new UserError(`The department name can't be repeated`); + return err; + }); +}; diff --git a/modules/worker/front/department/index.html b/modules/worker/front/department/index.html new file mode 100644 index 000000000..7cf4bade7 --- /dev/null +++ b/modules/worker/front/department/index.html @@ -0,0 +1,45 @@ + + + +
    +
    + + + + +
    +
    + + + + + + + +
    New department
    + + + + +
    + + + + +
    \ No newline at end of file diff --git a/modules/worker/front/department/index.js b/modules/worker/front/department/index.js new file mode 100644 index 000000000..99d65a478 --- /dev/null +++ b/modules/worker/front/department/index.js @@ -0,0 +1,78 @@ +import ngModule from '../module'; + +class Controller { + constructor($scope, $http, vnApp, $translate) { + this.$scope = $scope; + this.$http = $http; + this.vnApp = vnApp; + this.$translate = $translate; + this.params = {parentFk: 1}; + this.icons = [{icon: 'delete', tooltip: 'Delete', callback: this.onDelete}]; + this.newNode = { + name: '' + }; + } + + onCreate(parent) { + if (parent instanceof Object) + this.newNode.parentFk = parent.id; + + this.selectedNode = {parent}; + this.$scope.createNode.show(); + } + + onDelete(item, parent, index) { + this.selectedNode = {id: item.id, parent, index}; + this.$scope.deleteNode.show(); + } + + onCreateDialogOpen() { + this.newNode.name = ''; + } + + onCreateResponse(response) { + if (response == 'ACCEPT') { + try { + if (!this.newNode.name) + throw new Error(`Name can't be empty`); + + this.$http.post(`/worker/api/Departments/nodeAdd`, this.newNode).then(response => { + if (response.data) { + let parent = this.selectedNode.parent; + if ((parent instanceof Object) && !(parent instanceof Array)) { + const childs = parent.childs; + childs.push(response.data); + } else if ((parent instanceof Object) && (parent instanceof Array)) + parent.push(response.data); + } + }); + } catch (e) { + this.vnApp.showError(this.$translate.instant(e.message)); + return false; + } + } + return true; + } + + onRemoveResponse(response) { + if (response === 'ACCEPT') { + const path = `/worker/api/Departments/nodeDelete`; + this.$http.post(path, {parentFk: this.selectedNode.id}).then(() => { + let parent = this.selectedNode.parent; + + if ((parent instanceof Object) && !(parent instanceof Array)) { + const childs = parent.childs; + childs.splice(this.selectedNode.index, 1); + } else if ((parent instanceof Object) && (parent instanceof Array)) + parent.splice(this.selectedNode.index, 1); + }); + } + } +} + +Controller.$inject = ['$scope', '$http', 'vnApp', '$translate']; + +ngModule.component('vnWorkerDepartment', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/worker/front/department/locale/es.yml b/modules/worker/front/department/locale/es.yml new file mode 100644 index 000000000..c481bf4a9 --- /dev/null +++ b/modules/worker/front/department/locale/es.yml @@ -0,0 +1,3 @@ +New department: Nuevo departamento +Delete department: Eliminar departamento +Are you sure you want to delete it?: ¿Seguro que quieres eliminarlo? \ No newline at end of file diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js index e9aa1424c..512e712c2 100644 --- a/modules/worker/front/index.js +++ b/modules/worker/front/index.js @@ -8,3 +8,4 @@ import './descriptor-popover'; import './search-panel'; import './basic-data'; import './pbx'; +import './department'; diff --git a/modules/worker/front/index/index.html b/modules/worker/front/index/index.html index 6f679b3e9..703c82edd 100644 --- a/modules/worker/front/index/index.html +++ b/modules/worker/front/index/index.html @@ -8,11 +8,23 @@
    - - + + + + + + this.$state.go('worker.department'), name: 'Departments'} + ]; } onSearch(params) { @@ -22,9 +27,13 @@ export default class Controller { this.$.preview.show(); event.stopImmediatePropagation(); } + + onMoreChange(callback) { + callback.call(this); + } } -Controller.$inject = ['$scope']; +Controller.$inject = ['$scope', '$state']; ngModule.component('vnWorkerIndex', { template: require('./index.html'), diff --git a/modules/worker/front/index/style.scss b/modules/worker/front/index/style.scss new file mode 100644 index 000000000..f6ebf3828 --- /dev/null +++ b/modules/worker/front/index/style.scss @@ -0,0 +1,11 @@ +@import "variables"; + +vn-worker-index vn-icon-menu { + padding-top: 30px; + padding-left: 10px; + color: $color-main; + + li { + color: initial; + } +} \ No newline at end of file diff --git a/modules/worker/front/locale/es.yml b/modules/worker/front/locale/es.yml index d5d84a833..a7a47d654 100644 --- a/modules/worker/front/locale/es.yml +++ b/modules/worker/front/locale/es.yml @@ -13,3 +13,4 @@ View worker: Ver trabajador Worker id: Id trabajador Fiscal Identifier: NIF User name: Usuario +Departments: Departamentos \ No newline at end of file diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json index 7ef1237e0..73b503058 100644 --- a/modules/worker/front/routes.json +++ b/modules/worker/front/routes.json @@ -33,7 +33,8 @@ "component": "vn-worker-card", "abstract": true, "description": "Detail" - }, { + }, + { "url": "/basic-data", "state": "worker.card.basicData", "component": "vn-worker-basic-data", @@ -51,6 +52,12 @@ "worker": "$ctrl.worker" }, "acl": ["hr"] + }, + { + "url" : "/department", + "state": "worker.department", + "component": "vn-worker-department", + "description": "Departments" } ] } \ No newline at end of file