Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into dev
This commit is contained in:
commit
a1539516b7
|
@ -1,5 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
FORCE=FALSE
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [-f] [environment]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
while getopts ":f" option
|
||||
do
|
||||
case $option in
|
||||
f)
|
||||
FORCE=TRUE
|
||||
;;
|
||||
\?|:)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
ENV=$1
|
||||
|
||||
if [ "$ENV" == "production" ]; then
|
||||
|
@ -14,11 +35,13 @@ if [ "$ENV" == "production" ]; then
|
|||
echo "|_| |_|_\ \___/|___/ \___/ \___| |_| |___| \___/|_|\_|"
|
||||
echo ""
|
||||
|
||||
read -p "Are you sure? (Default: no) [yes|no]: " ANSWER
|
||||
if [ "$FORCE" != "TRUE" ]; then
|
||||
read -p "Are you sure? (Default: no) [yes|no]: " ANSWER
|
||||
|
||||
if [ "$ANSWER" != "yes" ]; then
|
||||
echo "Aborting"
|
||||
exit;
|
||||
if [ "$ANSWER" != "yes" ]; then
|
||||
echo "Aborting"
|
||||
exit;
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ -z "$ENV" ]; then
|
||||
|
@ -28,15 +51,16 @@ fi
|
|||
INI_FILE="config.$ENV.ini"
|
||||
|
||||
if [ ! -f "$INI_FILE" ]; then
|
||||
echo "File $INI_FILE doesn't exists"
|
||||
exit 1
|
||||
echo "Config file doesn't exists: $INI_FILE"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "[INFO] Config file: $INI_FILE"
|
||||
echo "[INFO] Importing changes."
|
||||
echo "[INFO] Applying changes"
|
||||
|
||||
# Import changes
|
||||
for file in install/changes/*.sql; do
|
||||
echo "[INFO] -> Applying $file"
|
||||
echo "[INFO] -> $file"
|
||||
mysql --defaults-file="$INI_FILE" < $file
|
||||
done
|
||||
|
||||
|
|
|
@ -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');
|
||||
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,23 @@
|
|||
/* Ejecutar en prod * /
|
||||
|
||||
/* 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,86 @@
|
|||
/* Ejecutar en prod * /
|
||||
|
||||
/* 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,31 @@
|
|||
/* Ejecutar en prod * /
|
||||
|
||||
/* 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 @@
|
|||
ALTER TABLE vn.itemLog MODIFY COLUMN userFk int(10) unsigned NULL;
|
|
@ -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"
|
||||
ng-class="{
|
||||
'expanded': item.active,
|
||||
'collapsed': !item.active,
|
||||
'included': item.isIncluded == 1,
|
||||
'excluded': item.isIncluded == 0
|
||||
'included': item.selected == 1,
|
||||
'excluded': item.selected == 0
|
||||
}">
|
||||
<vn-horizontal>
|
||||
<vn-auto class="actions">
|
||||
<vn-icon icon="keyboard_arrow_down"
|
||||
ng-show="item.sons > 0"
|
||||
ng-click="$ctrl.toggle(item, $event)" >
|
||||
<vn-icon icon="keyboard_arrow_down" title="{{'Toggle' | translate}}"
|
||||
ng-click="$ctrl.toggle(item, $event)">
|
||||
</vn-icon>
|
||||
</vn-auto>
|
||||
<div class="description">
|
||||
<vn-check vn-auto field="item.isIncluded"
|
||||
on-change="$ctrl.select(item, value)" triple-state="true">
|
||||
<vn-one class="description">
|
||||
<vn-check vn-auto vn-acl="{{$ctrl.aclRole}}"
|
||||
ng-if="$ctrl.selectable"
|
||||
field="item.selected"
|
||||
disabled="$ctrl.disabled"
|
||||
on-change="$ctrl.select(item, value)"
|
||||
triple-state="true">
|
||||
</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>
|
||||
</vn-horizontal>
|
||||
<vn-treeview-child items="item.childs"></vn-treeview-child>
|
||||
</li>
|
||||
</ul>
|
|
@ -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: '<?',
|
||||
disabled: '<?',
|
||||
selectable: '<?',
|
||||
editable: '<?',
|
||||
aclRole: '<?',
|
||||
},
|
||||
require: {
|
||||
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) {
|
||||
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: '<?',
|
||||
disabled: '<?',
|
||||
selectable: '<?',
|
||||
editable: '<?',
|
||||
aclRole: '@?'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ vn-treeview {
|
|||
}
|
||||
|
||||
li ul {
|
||||
padding: 0 1.8em;
|
||||
padding-left: 1.8em;
|
||||
}
|
||||
|
||||
li > vn-horizontal {
|
||||
|
@ -62,4 +62,8 @@ vn-treeview {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
vn-icon-button {
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
});
|
||||
|
|
|
@ -39,4 +39,6 @@ November: Noviembre
|
|||
December: Diciembre
|
||||
Has delivery: Hay reparto
|
||||
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);
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
on-search="$ctrl.onSearch()"
|
||||
vn-focus>
|
||||
</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)">
|
||||
</vn-treeview>
|
||||
</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 './basic-data';
|
||||
import './pbx';
|
||||
import './department';
|
||||
|
|
|
@ -8,11 +8,23 @@
|
|||
<div class="content-block">
|
||||
<div class="vn-list">
|
||||
<vn-card pad-medium-h>
|
||||
<vn-searchbar
|
||||
panel="vn-worker-search-panel"
|
||||
on-search="$ctrl.onSearch($params)"
|
||||
vn-focus>
|
||||
</vn-searchbar>
|
||||
<vn-horizontal>
|
||||
<vn-searchbar
|
||||
style="width: 100%"
|
||||
panel="vn-worker-search-panel"
|
||||
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 margin-medium-v>
|
||||
<a
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import ngModule from '../module';
|
||||
import './style.scss';
|
||||
|
||||
export default class Controller {
|
||||
constructor($) {
|
||||
constructor($, $state) {
|
||||
this.$state = $state;
|
||||
Object.assign(this, {
|
||||
$,
|
||||
selectedWorker: null,
|
||||
});
|
||||
this.moreOptions = [
|
||||
{callback: () => 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'),
|
||||
|
|
|
@ -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
|
||||
Fiscal Identifier: NIF
|
||||
User name: Usuario
|
||||
Departments: Departamentos
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue