This commit is contained in:
parent
4c111b31ef
commit
92170c34d5
|
@ -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 (149, 'Sip', '*', 'WRITE', 'ALLOW', 'ROLE', 'hr');
|
||||||
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 (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');
|
||||||
|
|
|
@ -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`;
|
|
@ -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 ;
|
|
@ -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 ;
|
|
@ -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);
|
|
@ -1,25 +1,54 @@
|
||||||
<ul ng-if="$ctrl.items">
|
<ul ng-if="::$ctrl.items">
|
||||||
<li ng-repeat="item in $ctrl.items"
|
<li ng-repeat="item in $ctrl.items"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
'expanded': item.active,
|
'expanded': item.active,
|
||||||
'collapsed': !item.active,
|
'collapsed': !item.active,
|
||||||
'included': item.isIncluded == 1,
|
'included': item.selected == 1,
|
||||||
'excluded': item.isIncluded == 0
|
'excluded': item.selected == 0
|
||||||
}">
|
}">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-auto class="actions">
|
<vn-auto class="actions">
|
||||||
<vn-icon icon="keyboard_arrow_down"
|
<vn-icon icon="keyboard_arrow_down" title="{{'Toggle' | translate}}"
|
||||||
ng-show="item.sons > 0"
|
ng-click="$ctrl.toggle(item, $event)">
|
||||||
ng-click="$ctrl.toggle(item, $event)" >
|
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
</vn-auto>
|
</vn-auto>
|
||||||
<div class="description">
|
<vn-one class="description">
|
||||||
<vn-check vn-auto field="item.isIncluded"
|
<vn-check vn-auto vn-acl="{{$ctrl.aclRole}}"
|
||||||
on-change="$ctrl.select(item, value)" triple-state="true">
|
ng-if="$ctrl.selectable"
|
||||||
|
field="item.selected"
|
||||||
|
disabled="$ctrl.disabled"
|
||||||
|
on-change="$ctrl.select(item, value)"
|
||||||
|
triple-state="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
{{::item.name}}
|
{{::item.name}}
|
||||||
|
</vn-one>
|
||||||
|
<vn-auto>
|
||||||
|
<vn-icon-button icon="{{icon.icon}}"
|
||||||
|
ng-repeat="icon in $ctrl.icons"
|
||||||
|
ng-click="$ctrl.onClick(icon, item, $ctrl.parent, $parent.$index)"
|
||||||
|
vn-acl="{{$ctrl.aclRole}}" vn-acl-action="remove">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-auto>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-treeview-child items="item.childs" parent="item"
|
||||||
|
selectable="$ctrl.selectable"
|
||||||
|
disabled="$ctrl.disabled"
|
||||||
|
editable="$ctrl.editable"
|
||||||
|
icons="$ctrl.icons"
|
||||||
|
acl-role="$ctrl.aclRole">
|
||||||
|
</vn-treeview-child>
|
||||||
|
</li>
|
||||||
|
<li ng-if="$ctrl.isInsertable && $ctrl.editable"
|
||||||
|
ng-click="$ctrl.onCreate($ctrl.parent)"
|
||||||
|
vn-acl="{{$ctrl.aclRole}}"
|
||||||
|
vn-acl-action="remove">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-auto>
|
||||||
|
<vn-icon-button icon="add_circle"></vn-icon-button>
|
||||||
|
</vn-auto>
|
||||||
|
<div class="description" translate>
|
||||||
|
Create new one
|
||||||
</div>
|
</div>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-treeview-child items="item.childs"></vn-treeview-child>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
|
@ -4,6 +4,7 @@ import Component from '../../lib/component';
|
||||||
class Controller extends Component {
|
class Controller extends Component {
|
||||||
constructor($element, $scope) {
|
constructor($element, $scope) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
|
this.$scope = $scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle(item) {
|
toggle(item) {
|
||||||
|
@ -13,13 +14,33 @@ class Controller extends Component {
|
||||||
select(item, value) {
|
select(item, value) {
|
||||||
this.treeview.onSelection(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', {
|
ngModule.component('vnTreeviewChild', {
|
||||||
template: require('./child.html'),
|
template: require('./child.html'),
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
items: '<'
|
items: '<',
|
||||||
|
parent: '<',
|
||||||
|
icons: '<?',
|
||||||
|
disabled: '<?',
|
||||||
|
selectable: '<?',
|
||||||
|
editable: '<?',
|
||||||
|
aclRole: '<?',
|
||||||
},
|
},
|
||||||
require: {
|
require: {
|
||||||
treeview: '^vnTreeview'
|
treeview: '^vnTreeview'
|
||||||
|
|
|
@ -1 +1,9 @@
|
||||||
<vn-treeview-child items="$ctrl.data"></vn-treeview-child>
|
<vn-treeview-child
|
||||||
|
items="$ctrl.data"
|
||||||
|
parent="$ctrl.data"
|
||||||
|
selectable="$ctrl.selectable"
|
||||||
|
editable="$ctrl.editable"
|
||||||
|
disabled="$ctrl.disabled"
|
||||||
|
icons="$ctrl.icons"
|
||||||
|
acl-role="$ctrl.aclRole">
|
||||||
|
</vn-treeview-child>
|
||||||
|
|
|
@ -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) {
|
onSelection(item, value) {
|
||||||
this.emit('selection', {item, value});
|
this.emit('selection', {item, value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCreate(parent) {
|
||||||
|
this.emit('create', {parent});
|
||||||
|
}
|
||||||
|
|
||||||
onToggle(item) {
|
onToggle(item) {
|
||||||
if (item.active)
|
if (item.active)
|
||||||
item.childs = undefined;
|
item.childs = undefined;
|
||||||
|
@ -45,12 +54,12 @@ export default class Treeview extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
item.childs = newData.sort((a, b) => {
|
item.childs = newData.sort((a, b) => {
|
||||||
if (b.isIncluded !== a.isIncluded) {
|
if (b.selected !== a.selected) {
|
||||||
if (a.isIncluded == null)
|
if (a.selected == null)
|
||||||
return 1;
|
return 1;
|
||||||
if (b.isIncluded == null)
|
if (b.selected == null)
|
||||||
return -1;
|
return -1;
|
||||||
return b.isIncluded - a.isIncluded;
|
return b.selected - a.selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
|
@ -68,6 +77,11 @@ ngModule.component('vnTreeview', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Treeview,
|
controller: Treeview,
|
||||||
bindings: {
|
bindings: {
|
||||||
model: '<'
|
model: '<',
|
||||||
|
icons: '<?',
|
||||||
|
disabled: '<?',
|
||||||
|
selectable: '<?',
|
||||||
|
editable: '<?',
|
||||||
|
aclRole: '@?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,7 +21,7 @@ vn-treeview {
|
||||||
}
|
}
|
||||||
|
|
||||||
li ul {
|
li ul {
|
||||||
padding: 0 1.8em;
|
padding-left: 1.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
li > vn-horizontal {
|
li > vn-horizontal {
|
||||||
|
@ -62,4 +62,8 @@ vn-treeview {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vn-icon-button {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ function vnAcl(aclService, $timeout) {
|
||||||
return conditions;
|
return conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function permissionElement($element, action) {
|
function permissionElement($scope, $element, action) {
|
||||||
if (!aclService.hasAny(acls)) {
|
if (!aclService.hasAny(acls)) {
|
||||||
if (action === 'disabled') {
|
if (action === 'disabled') {
|
||||||
let input = $element[0];
|
let input = $element[0];
|
||||||
|
@ -72,9 +72,13 @@ function vnAcl(aclService, $timeout) {
|
||||||
priority: -1,
|
priority: -1,
|
||||||
link: function($scope, $element, $attrs) {
|
link: function($scope, $element, $attrs) {
|
||||||
acls = $attrs.vnAcl.split(',').map(i => i.trim());
|
acls = $attrs.vnAcl.split(',').map(i => i.trim());
|
||||||
|
|
||||||
|
if (acls[0] == '') return;
|
||||||
|
|
||||||
let action = $attrs.vnAclAction || 'disabled';
|
let action = $attrs.vnAclAction || 'disabled';
|
||||||
let conditions = getDynamicConditions($attrs);
|
let conditions = getDynamicConditions($attrs);
|
||||||
permissionElement($element, action);
|
|
||||||
|
permissionElement($scope, $element, action);
|
||||||
|
|
||||||
if (Object.keys(conditions).length) {
|
if (Object.keys(conditions).length) {
|
||||||
let watchConditions = $scope.$watch(() => {
|
let watchConditions = $scope.$watch(() => {
|
||||||
|
@ -82,7 +86,7 @@ function vnAcl(aclService, $timeout) {
|
||||||
let hasPermission = $scope.$eval($attrs[attrName]);
|
let hasPermission = $scope.$eval($attrs[attrName]);
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
updateAcls(conditions[attrName].role, hasPermission);
|
updateAcls(conditions[attrName].role, hasPermission);
|
||||||
permissionElement($element, action);
|
permissionElement($scope, $element, action);
|
||||||
delete conditions[attrName];
|
delete conditions[attrName];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,4 +39,6 @@ November: Noviembre
|
||||||
December: Diciembre
|
December: Diciembre
|
||||||
Has delivery: Hay reparto
|
Has delivery: Hay reparto
|
||||||
Loading: Cargando
|
Loading: Cargando
|
||||||
Fields to show: Campos a mostrar
|
Fields to show: Campos a mostrar
|
||||||
|
Create new one: Crear nuevo
|
||||||
|
Toggle: Desplegar/Plegar
|
|
@ -128,44 +128,33 @@ module.exports = function(Self) {
|
||||||
return replaceErrFunc(err);
|
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', () => {
|
this.once('attached', () => {
|
||||||
let realUpsert = this.upsert;
|
this.remove =
|
||||||
this.upsert = async(data, options, cb) => {
|
this.deleteAll =
|
||||||
if (options instanceof Function) {
|
this.destroyAll = rewriteMethod.call(this, 'remove');
|
||||||
cb = options;
|
this.upsert = rewriteMethod.call(this, 'upsert');
|
||||||
options = null;
|
this.create = rewriteMethod.call(this, 'create');
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -38,5 +38,6 @@
|
||||||
"You can't create a ticket for a frozen client": "You can't create a ticket for a frozen client",
|
"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",
|
"can't be blank": "can't be blank",
|
||||||
"Street cannot be empty": "Street cannot be empty",
|
"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"
|
||||||
}
|
}
|
|
@ -72,5 +72,6 @@
|
||||||
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
|
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
|
||||||
"Street cannot be empty": "Dirección no puede estar en blanco",
|
"Street cannot be empty": "Dirección no puede estar en blanco",
|
||||||
"City cannot be empty": "Cuidad 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"
|
||||||
}
|
}
|
|
@ -73,7 +73,7 @@ module.exports = Self => {
|
||||||
zg.rgt,
|
zg.rgt,
|
||||||
zg.depth,
|
zg.depth,
|
||||||
zg.sons,
|
zg.sons,
|
||||||
IF(ch.id = zg.id, isIncluded, null) isIncluded
|
IF(ch.id = zg.id, isIncluded, null) selected
|
||||||
FROM zoneGeo zg
|
FROM zoneGeo zg
|
||||||
JOIN tmp.checkedChilds ch
|
JOIN tmp.checkedChilds ch
|
||||||
ON zg.lft <= ch.lft AND zg.rgt >= ch.rgt
|
ON zg.lft <= ch.lft AND zg.rgt >= ch.rgt
|
||||||
|
@ -86,7 +86,7 @@ module.exports = Self => {
|
||||||
child.rgt,
|
child.rgt,
|
||||||
child.depth,
|
child.depth,
|
||||||
child.sons,
|
child.sons,
|
||||||
zi.isIncluded
|
zi.isIncluded AS selected
|
||||||
FROM zoneGeo parent
|
FROM zoneGeo parent
|
||||||
JOIN zoneGeo child ON child.lft > parent.lft
|
JOIN zoneGeo child ON child.lft > parent.lft
|
||||||
AND child.rgt < parent.rgt
|
AND child.rgt < parent.rgt
|
||||||
|
@ -122,9 +122,11 @@ module.exports = Self => {
|
||||||
|
|
||||||
function nestLeaves(elements) {
|
function nestLeaves(elements) {
|
||||||
elements.forEach(element => {
|
elements.forEach(element => {
|
||||||
element.childs = Object.assign([], getLeaves(element));
|
let childs = Object.assign([], getLeaves(element));
|
||||||
|
if (childs.length > 0) {
|
||||||
nestLeaves(element.childs);
|
element.childs = childs;
|
||||||
|
nestLeaves(element.childs);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,12 +144,12 @@ module.exports = Self => {
|
||||||
|
|
||||||
function sortNodes(nodes) {
|
function sortNodes(nodes) {
|
||||||
return nodes.sort((a, b) => {
|
return nodes.sort((a, b) => {
|
||||||
if (b.isIncluded !== a.isIncluded) {
|
if (b.selected !== a.selected) {
|
||||||
if (a.isIncluded == null)
|
if (a.selected == null)
|
||||||
return 1;
|
return 1;
|
||||||
if (b.isIncluded == null)
|
if (b.selected == null)
|
||||||
return -1;
|
return -1;
|
||||||
return b.isIncluded - a.isIncluded;
|
return b.selected - a.selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
on-search="$ctrl.onSearch()"
|
on-search="$ctrl.onSearch()"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
<vn-treeview vn-id="treeview" model="model"
|
<vn-treeview vn-id="treeview" model="model" selectable="true" acl-role="deliveryBoss"
|
||||||
on-selection="$ctrl.onSelection(item, value)">
|
on-selection="$ctrl.onSelection(item, value)">
|
||||||
</vn-treeview>
|
</vn-treeview>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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);
|
||||||
|
};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,45 @@
|
||||||
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="/worker/api/departments/getLeaves"
|
||||||
|
params="::$ctrl.params"
|
||||||
|
auto-load="false">
|
||||||
|
</vn-crud-model>
|
||||||
|
|
||||||
|
<form name="form">
|
||||||
|
<div margin-medium>
|
||||||
|
<vn-card margin-medium-v pad-medium>
|
||||||
|
<vn-treeview vn-id="treeview" model="model"
|
||||||
|
on-selection="$ctrl.onSelection(item, value)"
|
||||||
|
on-create="$ctrl.onCreate(parent)"
|
||||||
|
icons="$ctrl.icons" editable="true" acl-role="hr">
|
||||||
|
</vn-treeview>
|
||||||
|
</vn-card>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="deleteNode"
|
||||||
|
on-response="$ctrl.onRemoveResponse(response)"
|
||||||
|
question="Delete department"
|
||||||
|
message="Are you sure you want to delete it?">
|
||||||
|
</vn-confirm>
|
||||||
|
|
||||||
|
<!-- Create department dialog -->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="createNode"
|
||||||
|
on-open="$ctrl.onCreateDialogOpen()"
|
||||||
|
on-response="$ctrl.onCreateResponse(response)">
|
||||||
|
<tpl-body>
|
||||||
|
<h5 pad-small-v translate>New department</h5>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
label="Name"
|
||||||
|
model="$ctrl.newNode.name">
|
||||||
|
</vn-textfield>
|
||||||
|
</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>
|
|
@ -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
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
New department: Nuevo departamento
|
||||||
|
Delete department: Eliminar departamento
|
||||||
|
Are you sure you want to delete it?: ¿Seguro que quieres eliminarlo?
|
|
@ -8,3 +8,4 @@ import './descriptor-popover';
|
||||||
import './search-panel';
|
import './search-panel';
|
||||||
import './basic-data';
|
import './basic-data';
|
||||||
import './pbx';
|
import './pbx';
|
||||||
|
import './department';
|
||||||
|
|
|
@ -8,11 +8,23 @@
|
||||||
<div class="content-block">
|
<div class="content-block">
|
||||||
<div class="vn-list">
|
<div class="vn-list">
|
||||||
<vn-card pad-medium-h>
|
<vn-card pad-medium-h>
|
||||||
<vn-searchbar
|
<vn-horizontal>
|
||||||
panel="vn-worker-search-panel"
|
<vn-searchbar
|
||||||
on-search="$ctrl.onSearch($params)"
|
style="width: 100%"
|
||||||
vn-focus>
|
panel="vn-worker-search-panel"
|
||||||
</vn-searchbar>
|
on-search="$ctrl.onSearch($params)"
|
||||||
|
vn-focus>
|
||||||
|
</vn-searchbar>
|
||||||
|
<vn-icon-menu
|
||||||
|
vn-id="more-button"
|
||||||
|
icon="more_vert"
|
||||||
|
show-filter="false"
|
||||||
|
value-field="callback"
|
||||||
|
translate-fields="['name']"
|
||||||
|
data="$ctrl.moreOptions"
|
||||||
|
on-change="$ctrl.onMoreChange(value)">
|
||||||
|
</vn-icon-menu>
|
||||||
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-card margin-medium-v>
|
<vn-card margin-medium-v>
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller {
|
||||||
constructor($) {
|
constructor($, $state) {
|
||||||
|
this.$state = $state;
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
$,
|
$,
|
||||||
selectedWorker: null,
|
selectedWorker: null,
|
||||||
});
|
});
|
||||||
|
this.moreOptions = [
|
||||||
|
{callback: () => this.$state.go('worker.department'), name: 'Departments'}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearch(params) {
|
onSearch(params) {
|
||||||
|
@ -22,9 +27,13 @@ export default class Controller {
|
||||||
this.$.preview.show();
|
this.$.preview.show();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMoreChange(callback) {
|
||||||
|
callback.call(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope'];
|
Controller.$inject = ['$scope', '$state'];
|
||||||
|
|
||||||
ngModule.component('vnWorkerIndex', {
|
ngModule.component('vnWorkerIndex', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-worker-index vn-icon-menu {
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: $color-main;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: initial;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,3 +13,4 @@ View worker: Ver trabajador
|
||||||
Worker id: Id trabajador
|
Worker id: Id trabajador
|
||||||
Fiscal Identifier: NIF
|
Fiscal Identifier: NIF
|
||||||
User name: Usuario
|
User name: Usuario
|
||||||
|
Departments: Departamentos
|
|
@ -33,7 +33,8 @@
|
||||||
"component": "vn-worker-card",
|
"component": "vn-worker-card",
|
||||||
"abstract": true,
|
"abstract": true,
|
||||||
"description": "Detail"
|
"description": "Detail"
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"url": "/basic-data",
|
"url": "/basic-data",
|
||||||
"state": "worker.card.basicData",
|
"state": "worker.card.basicData",
|
||||||
"component": "vn-worker-basic-data",
|
"component": "vn-worker-basic-data",
|
||||||
|
@ -51,6 +52,12 @@
|
||||||
"worker": "$ctrl.worker"
|
"worker": "$ctrl.worker"
|
||||||
},
|
},
|
||||||
"acl": ["hr"]
|
"acl": ["hr"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url" : "/department",
|
||||||
|
"state": "worker.department",
|
||||||
|
"component": "vn-worker-department",
|
||||||
|
"description": "Departments"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue