Merge branch 'dev' into 2534-sage_taxDataChecked
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
c5421ee543
|
@ -9,7 +9,7 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"required": true
|
||||
"id": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
|
@ -56,7 +56,8 @@
|
|||
"roles": {
|
||||
"type": "hasMany",
|
||||
"model": "RoleRole",
|
||||
"foreignKey": "role"
|
||||
"foreignKey": "role",
|
||||
"primaryKey": "roleFk"
|
||||
},
|
||||
"emailUser": {
|
||||
"type": "hasOne",
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
ALTER TABLE `vn`.`observationType`
|
||||
ADD COLUMN `code` VARCHAR(45) NOT NULL AFTER `description`;
|
||||
|
||||
UPDATE `vn`.`observationType` SET `code` = 'itemPicker' WHERE (`id` = '1');
|
||||
UPDATE `vn`.`observationType` SET `code` = 'packager' WHERE (`id` = '2');
|
||||
UPDATE `vn`.`observationType` SET `code` = 'salesPerson' WHERE (`id` = '4');
|
||||
UPDATE `vn`.`observationType` SET `code` = 'administrative' WHERE (`id` = '5');
|
||||
UPDATE `vn`.`observationType` SET `code` = 'weight' WHERE (`id` = '6');
|
||||
UPDATE `vn`.`observationType` SET `code` = 'delivery' WHERE (`id` = '3');
|
|
@ -0,0 +1,7 @@
|
|||
ALTER TABLE `account`.`roleRole`
|
||||
ADD `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
UPDATE `account`.`role` SET id = 100 WHERE `name` = 'root';
|
||||
|
||||
CALL account.role_sync;
|
|
@ -0,0 +1,503 @@
|
|||
DROP PROCEDURE IF EXISTS account.role_syncPrivileges;
|
||||
DELIMITER $$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `account`.`role_syncPrivileges`()
|
||||
BEGIN
|
||||
/**
|
||||
* Synchronizes permissions of MySQL role users based on role hierarchy.
|
||||
* The computed role users of permission mix will be named according to
|
||||
* pattern z-[role_name].
|
||||
*
|
||||
* If any@localhost user exists, it will be taken as a template for basic
|
||||
* attributes.
|
||||
*
|
||||
* Warning! This procedure should only be called when MySQL privileges
|
||||
* are modified. If role hierarchy is modified, you must call the role_sync()
|
||||
* procedure wich calls this internally.
|
||||
*/
|
||||
DECLARE vIsMysql BOOL DEFAULT VERSION() NOT LIKE '%MariaDB%';
|
||||
DECLARE vVersion INT DEFAULT SUBSTRING_INDEX(VERSION(), '.', 1);
|
||||
DECLARE vTplUser VARCHAR(255) DEFAULT 'any';
|
||||
DECLARE vTplHost VARCHAR(255) DEFAULT '%';
|
||||
DECLARE vRoleHost VARCHAR(255) DEFAULT 'localhost';
|
||||
DECLARE vAllHost VARCHAR(255) DEFAULT '%';
|
||||
DECLARE vPrefix VARCHAR(2) DEFAULT 'z-';
|
||||
DECLARE vPrefixedLike VARCHAR(255);
|
||||
DECLARE vPassword VARCHAR(255) DEFAULT '';
|
||||
|
||||
-- Deletes computed role users
|
||||
|
||||
SET vPrefixedLike = CONCAT(vPrefix, '%');
|
||||
|
||||
IF vIsMysql THEN
|
||||
DELETE FROM mysql.user
|
||||
WHERE `User` LIKE vPrefixedLike;
|
||||
ELSE
|
||||
DELETE FROM mysql.global_priv
|
||||
WHERE `User` LIKE vPrefixedLike;
|
||||
END IF;
|
||||
|
||||
DELETE FROM mysql.db
|
||||
WHERE `User` LIKE vPrefixedLike;
|
||||
|
||||
DELETE FROM mysql.tables_priv
|
||||
WHERE `User` LIKE vPrefixedLike;
|
||||
|
||||
DELETE FROM mysql.columns_priv
|
||||
WHERE `User` LIKE vPrefixedLike;
|
||||
|
||||
DELETE FROM mysql.procs_priv
|
||||
WHERE `User` LIKE vPrefixedLike;
|
||||
|
||||
DELETE FROM mysql.proxies_priv
|
||||
WHERE `Proxied_user` LIKE vPrefixedLike;
|
||||
|
||||
-- Temporary tables
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tRole;
|
||||
CREATE TEMPORARY TABLE tRole
|
||||
(INDEX (id))
|
||||
ENGINE = MEMORY
|
||||
SELECT
|
||||
id,
|
||||
`name` role,
|
||||
CONCAT(vPrefix, `name`) prefixedRole
|
||||
FROM role
|
||||
WHERE hasLogin;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tRoleInherit;
|
||||
CREATE TEMPORARY TABLE tRoleInherit
|
||||
(INDEX (inheritsFrom))
|
||||
ENGINE = MEMORY
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
ri.`name` inheritsFrom
|
||||
FROM tRole r
|
||||
JOIN roleRole rr ON rr.role = r.id
|
||||
JOIN role ri ON ri.id = rr.inheritsFrom;
|
||||
|
||||
-- Recreate role users
|
||||
|
||||
IF vIsMysql THEN
|
||||
DROP TEMPORARY TABLE IF EXISTS tUser;
|
||||
CREATE TEMPORARY TABLE tUser
|
||||
SELECT
|
||||
r.prefixedRole `User`,
|
||||
vTplHost `Host`,
|
||||
IFNULL(t.`authentication_string`,
|
||||
'') `authentication_string`,
|
||||
IFNULL(t.`plugin`,
|
||||
'mysql_native_password') `plugin`,
|
||||
IFNULL(IF('' != u.`ssl_type`,
|
||||
u.`ssl_type`, t.`ssl_type`),
|
||||
'') `ssl_type`,
|
||||
IFNULL(IF('' != u.`ssl_cipher`,
|
||||
u.`ssl_cipher`, t.`ssl_cipher`),
|
||||
'') `ssl_cipher`,
|
||||
IFNULL(IF('' != u.`x509_issuer`,
|
||||
u.`x509_issuer`, t.`x509_issuer`),
|
||||
'') `x509_issuer`,
|
||||
IFNULL(IF('' != u.`x509_subject`,
|
||||
u.`x509_subject`, t.`x509_subject`),
|
||||
'') `x509_subject`,
|
||||
IFNULL(IF(0 != u.`max_questions`,
|
||||
u.`max_questions`, t.`max_questions`),
|
||||
0) `max_questions`,
|
||||
IFNULL(IF(0 != u.`max_updates`,
|
||||
u.`max_updates`, t.`max_updates`),
|
||||
0) `max_updates`,
|
||||
IFNULL(IF(0 != u.`max_connections`,
|
||||
u.`max_connections`, t.`max_connections`),
|
||||
0) `max_connections`,
|
||||
IFNULL(IF(0 != u.`max_user_connections`,
|
||||
u.`max_user_connections`, t.`max_user_connections`),
|
||||
0) `max_user_connections`
|
||||
FROM tRole r
|
||||
LEFT JOIN mysql.user t
|
||||
ON t.`User` = vTplUser
|
||||
AND t.`Host` = vRoleHost
|
||||
LEFT JOIN mysql.user u
|
||||
ON u.`User` = r.role
|
||||
AND u.`Host` = vRoleHost;
|
||||
|
||||
IF vVersion <= 5 THEN
|
||||
SELECT `Password` INTO vPassword
|
||||
FROM mysql.user
|
||||
WHERE `User` = vTplUser
|
||||
AND `Host` = vRoleHost;
|
||||
|
||||
INSERT INTO mysql.user (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Password`,
|
||||
`authentication_string`,
|
||||
`plugin`,
|
||||
`ssl_type`,
|
||||
`ssl_cipher`,
|
||||
`x509_issuer`,
|
||||
`x509_subject`,
|
||||
`max_questions`,
|
||||
`max_updates`,
|
||||
`max_connections`,
|
||||
`max_user_connections`
|
||||
)
|
||||
SELECT
|
||||
`User`,
|
||||
`Host`,
|
||||
vPassword,
|
||||
`authentication_string`,
|
||||
`plugin`,
|
||||
`ssl_type`,
|
||||
`ssl_cipher`,
|
||||
`x509_issuer`,
|
||||
`x509_subject`,
|
||||
`max_questions`,
|
||||
`max_updates`,
|
||||
`max_connections`,
|
||||
`max_user_connections`
|
||||
FROM tUser;
|
||||
ELSE
|
||||
INSERT INTO mysql.user (
|
||||
`User`,
|
||||
`Host`,
|
||||
`authentication_string`,
|
||||
`plugin`,
|
||||
`ssl_type`,
|
||||
`ssl_cipher`,
|
||||
`x509_issuer`,
|
||||
`x509_subject`,
|
||||
`max_questions`,
|
||||
`max_updates`,
|
||||
`max_connections`,
|
||||
`max_user_connections`
|
||||
)
|
||||
SELECT
|
||||
`User`,
|
||||
`Host`,
|
||||
`authentication_string`,
|
||||
`plugin`,
|
||||
`ssl_type`,
|
||||
`ssl_cipher`,
|
||||
`x509_issuer`,
|
||||
`x509_subject`,
|
||||
`max_questions`,
|
||||
`max_updates`,
|
||||
`max_connections`,
|
||||
`max_user_connections`
|
||||
FROM tUser;
|
||||
END IF;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tUser;
|
||||
ELSE
|
||||
INSERT INTO mysql.global_priv (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Priv`
|
||||
)
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
vTplHost,
|
||||
JSON_MERGE_PATCH(
|
||||
IFNULL(t.`Priv`, '{}'),
|
||||
IFNULL(u.`Priv`, '{}'),
|
||||
JSON_OBJECT(
|
||||
'mysql_old_password', JSON_VALUE(t.`Priv`, '$.mysql_old_password'),
|
||||
'mysql_native_password', JSON_VALUE(t.`Priv`, '$.mysql_native_password'),
|
||||
'authentication_string', JSON_VALUE(t.`Priv`, '$.authentication_string')
|
||||
)
|
||||
)
|
||||
FROM tRole r
|
||||
LEFT JOIN mysql.global_priv t
|
||||
ON t.`User` = vTplUser
|
||||
AND t.`Host` = vRoleHost
|
||||
LEFT JOIN mysql.global_priv u
|
||||
ON u.`User` = r.role
|
||||
AND u.`Host` = vRoleHost;
|
||||
END IF;
|
||||
|
||||
INSERT INTO mysql.proxies_priv (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Proxied_user`,
|
||||
`Proxied_host`,
|
||||
`Grantor`
|
||||
)
|
||||
SELECT
|
||||
'',
|
||||
vAllHost,
|
||||
prefixedRole,
|
||||
vTplHost,
|
||||
CONCAT(prefixedRole, '@', vTplHost)
|
||||
FROM tRole;
|
||||
|
||||
-- Copies global privileges
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tUserPriv;
|
||||
|
||||
IF vIsMysql THEN
|
||||
CREATE TEMPORARY TABLE tUserPriv
|
||||
(INDEX (prefixedRole))
|
||||
ENGINE = MEMORY
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
MAX(u.`Select_priv`) `Select_priv`,
|
||||
MAX(u.`Insert_priv`) `Insert_priv`,
|
||||
MAX(u.`Update_priv`) `Update_priv`,
|
||||
MAX(u.`Delete_priv`) `Delete_priv`,
|
||||
MAX(u.`Create_priv`) `Create_priv`,
|
||||
MAX(u.`Drop_priv`) `Drop_priv`,
|
||||
MAX(u.`Reload_priv`) `Reload_priv`,
|
||||
MAX(u.`Shutdown_priv`) `Shutdown_priv`,
|
||||
MAX(u.`Process_priv`) `Process_priv`,
|
||||
MAX(u.`File_priv`) `File_priv`,
|
||||
MAX(u.`Grant_priv`) `Grant_priv`,
|
||||
MAX(u.`References_priv`) `References_priv`,
|
||||
MAX(u.`Index_priv`) `Index_priv`,
|
||||
MAX(u.`Alter_priv`) `Alter_priv`,
|
||||
MAX(u.`Show_db_priv`) `Show_db_priv`,
|
||||
MAX(u.`Super_priv`) `Super_priv`,
|
||||
MAX(u.`Create_tmp_table_priv`) `Create_tmp_table_priv`,
|
||||
MAX(u.`Lock_tables_priv`) `Lock_tables_priv`,
|
||||
MAX(u.`Execute_priv`) `Execute_priv`,
|
||||
MAX(u.`Repl_slave_priv`) `Repl_slave_priv`,
|
||||
MAX(u.`Repl_client_priv`) `Repl_client_priv`,
|
||||
MAX(u.`Create_view_priv`) `Create_view_priv`,
|
||||
MAX(u.`Show_view_priv`) `Show_view_priv`,
|
||||
MAX(u.`Create_routine_priv`) `Create_routine_priv`,
|
||||
MAX(u.`Alter_routine_priv`) `Alter_routine_priv`,
|
||||
MAX(u.`Create_user_priv`) `Create_user_priv`,
|
||||
MAX(u.`Event_priv`) `Event_priv`,
|
||||
MAX(u.`Trigger_priv`) `Trigger_priv`,
|
||||
MAX(u.`Create_tablespace_priv`) `Create_tablespace_priv`
|
||||
FROM tRoleInherit r
|
||||
JOIN mysql.user u
|
||||
ON u.`User` = r.inheritsFrom
|
||||
AND u.`Host`= vRoleHost
|
||||
GROUP BY r.prefixedRole;
|
||||
|
||||
UPDATE mysql.user u
|
||||
JOIN tUserPriv t
|
||||
ON u.`User` = t.prefixedRole
|
||||
AND u.`Host` = vTplHost
|
||||
SET
|
||||
u.`Select_priv`
|
||||
= t.`Select_priv`,
|
||||
u.`Insert_priv`
|
||||
= t.`Insert_priv`,
|
||||
u.`Update_priv`
|
||||
= t.`Update_priv`,
|
||||
u.`Delete_priv`
|
||||
= t.`Delete_priv`,
|
||||
u.`Create_priv`
|
||||
= t.`Create_priv`,
|
||||
u.`Drop_priv`
|
||||
= t.`Drop_priv`,
|
||||
u.`Reload_priv`
|
||||
= t.`Reload_priv`,
|
||||
u.`Shutdown_priv`
|
||||
= t.`Shutdown_priv`,
|
||||
u.`Process_priv`
|
||||
= t.`Process_priv`,
|
||||
u.`File_priv`
|
||||
= t.`File_priv`,
|
||||
u.`Grant_priv`
|
||||
= t.`Grant_priv`,
|
||||
u.`References_priv`
|
||||
= t.`References_priv`,
|
||||
u.`Index_priv`
|
||||
= t.`Index_priv`,
|
||||
u.`Alter_priv`
|
||||
= t.`Alter_priv`,
|
||||
u.`Show_db_priv`
|
||||
= t.`Show_db_priv`,
|
||||
u.`Super_priv`
|
||||
= t.`Super_priv`,
|
||||
u.`Create_tmp_table_priv`
|
||||
= t.`Create_tmp_table_priv`,
|
||||
u.`Lock_tables_priv`
|
||||
= t.`Lock_tables_priv`,
|
||||
u.`Execute_priv`
|
||||
= t.`Execute_priv`,
|
||||
u.`Repl_slave_priv`
|
||||
= t.`Repl_slave_priv`,
|
||||
u.`Repl_client_priv`
|
||||
= t.`Repl_client_priv`,
|
||||
u.`Create_view_priv`
|
||||
= t.`Create_view_priv`,
|
||||
u.`Show_view_priv`
|
||||
= t.`Show_view_priv`,
|
||||
u.`Create_routine_priv`
|
||||
= t.`Create_routine_priv`,
|
||||
u.`Alter_routine_priv`
|
||||
= t.`Alter_routine_priv`,
|
||||
u.`Create_user_priv`
|
||||
= t.`Create_user_priv`,
|
||||
u.`Event_priv`
|
||||
= t.`Event_priv`,
|
||||
u.`Trigger_priv`
|
||||
= t.`Trigger_priv`,
|
||||
u.`Create_tablespace_priv`
|
||||
= t.`Create_tablespace_priv`;
|
||||
ELSE
|
||||
CREATE TEMPORARY TABLE tUserPriv
|
||||
(INDEX (prefixedRole))
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
BIT_OR(JSON_VALUE(p.`Priv`, '$.access')) access
|
||||
FROM tRoleInherit r
|
||||
JOIN mysql.global_priv p
|
||||
ON p.`User` = r.inheritsFrom
|
||||
AND p.`Host`= vRoleHost
|
||||
GROUP BY r.prefixedRole;
|
||||
|
||||
UPDATE mysql.global_priv p
|
||||
JOIN tUserPriv t
|
||||
ON p.`User` = t.prefixedRole
|
||||
AND p.`Host` = vTplHost
|
||||
SET
|
||||
p.`Priv` = JSON_SET(p.`Priv`, '$.access', t.access);
|
||||
END IF;
|
||||
|
||||
DROP TEMPORARY TABLE tUserPriv;
|
||||
|
||||
-- Copy schema level privileges
|
||||
|
||||
INSERT INTO mysql.db (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Db`,
|
||||
`Select_priv`,
|
||||
`Insert_priv`,
|
||||
`Update_priv`,
|
||||
`Delete_priv`,
|
||||
`Create_priv`,
|
||||
`Drop_priv`,
|
||||
`Grant_priv`,
|
||||
`References_priv`,
|
||||
`Index_priv`,
|
||||
`Alter_priv`,
|
||||
`Create_tmp_table_priv`,
|
||||
`Lock_tables_priv`,
|
||||
`Create_view_priv`,
|
||||
`Show_view_priv`,
|
||||
`Create_routine_priv`,
|
||||
`Alter_routine_priv`,
|
||||
`Execute_priv`,
|
||||
`Event_priv`,
|
||||
`Trigger_priv`
|
||||
)
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
vTplHost,
|
||||
t.`Db`,
|
||||
MAX(t.`Select_priv`),
|
||||
MAX(t.`Insert_priv`),
|
||||
MAX(t.`Update_priv`),
|
||||
MAX(t.`Delete_priv`),
|
||||
MAX(t.`Create_priv`),
|
||||
MAX(t.`Drop_priv`),
|
||||
MAX(t.`Grant_priv`),
|
||||
MAX(t.`References_priv`),
|
||||
MAX(t.`Index_priv`),
|
||||
MAX(t.`Alter_priv`),
|
||||
MAX(t.`Create_tmp_table_priv`),
|
||||
MAX(t.`Lock_tables_priv`),
|
||||
MAX(t.`Create_view_priv`),
|
||||
MAX(t.`Show_view_priv`),
|
||||
MAX(t.`Create_routine_priv`),
|
||||
MAX(t.`Alter_routine_priv`),
|
||||
MAX(t.`Execute_priv`),
|
||||
MAX(t.`Event_priv`),
|
||||
MAX(t.`Trigger_priv`)
|
||||
FROM tRoleInherit r
|
||||
JOIN mysql.db t
|
||||
ON t.`User` = r.inheritsFrom
|
||||
AND t.`Host`= vRoleHost
|
||||
GROUP BY r.prefixedRole, t.`Db`;
|
||||
|
||||
-- Copy table level privileges
|
||||
|
||||
INSERT INTO mysql.tables_priv (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Db`,
|
||||
`Table_name`,
|
||||
`Grantor`,
|
||||
`Timestamp`,
|
||||
`Table_priv`,
|
||||
`Column_priv`
|
||||
)
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
vTplHost,
|
||||
t.`Db`,
|
||||
t.`Table_name`,
|
||||
t.`Grantor`,
|
||||
MAX(t.`Timestamp`),
|
||||
IFNULL(GROUP_CONCAT(NULLIF(t.`Table_priv`, '')), ''),
|
||||
IFNULL(GROUP_CONCAT(NULLIF(t.`Column_priv`, '')), '')
|
||||
FROM tRoleInherit r
|
||||
JOIN mysql.tables_priv t
|
||||
ON t.`User` = r.inheritsFrom
|
||||
AND t.`Host`= vRoleHost
|
||||
GROUP BY r.prefixedRole, t.`Db`, t.`Table_name`;
|
||||
|
||||
-- Copy column level privileges
|
||||
|
||||
INSERT INTO mysql.columns_priv (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Db`,
|
||||
`Table_name`,
|
||||
`Column_name`,
|
||||
`Timestamp`,
|
||||
`Column_priv`
|
||||
)
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
vTplHost,
|
||||
t.`Db`,
|
||||
t.`Table_name`,
|
||||
t.`Column_name`,
|
||||
MAX(t.`Timestamp`),
|
||||
IFNULL(GROUP_CONCAT(NULLIF(t.`Column_priv`, '')), '')
|
||||
FROM tRoleInherit r
|
||||
JOIN mysql.columns_priv t
|
||||
ON t.`User` = r.inheritsFrom
|
||||
AND t.`Host`= vRoleHost
|
||||
GROUP BY r.prefixedRole, t.`Db`, t.`Table_name`, t.`Column_name`;
|
||||
|
||||
-- Copy routine privileges
|
||||
|
||||
INSERT IGNORE INTO mysql.procs_priv (
|
||||
`User`,
|
||||
`Host`,
|
||||
`Db`,
|
||||
`Routine_name`,
|
||||
`Routine_type`,
|
||||
`Grantor`,
|
||||
`Timestamp`,
|
||||
`Proc_priv`
|
||||
)
|
||||
SELECT
|
||||
r.prefixedRole,
|
||||
vTplHost,
|
||||
t.`Db`,
|
||||
t.`Routine_name`,
|
||||
t.`Routine_type`,
|
||||
t.`Grantor`,
|
||||
t.`Timestamp`,
|
||||
t.`Proc_priv`
|
||||
FROM tRoleInherit r
|
||||
JOIN mysql.procs_priv t
|
||||
ON t.`User` = r.inheritsFrom
|
||||
AND t.`Host`= vRoleHost;
|
||||
|
||||
-- Free memory
|
||||
|
||||
DROP TEMPORARY TABLE
|
||||
tRole,
|
||||
tRoleInherit;
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,10 @@
|
|||
ALTER TABLE account.sambaConfig ADD adUser VARCHAR(255) DEFAULT NULL NULL COMMENT 'Active directory user';
|
||||
ALTER TABLE account.sambaConfig ADD adPassword varchar(255) DEFAULT NULL NULL COMMENT 'Active directory password';
|
||||
ALTER TABLE account.sambaConfig ADD userDn varchar(255) DEFAULT NULL NULL COMMENT 'The base DN for users';
|
||||
ALTER TABLE account.sambaConfig DROP COLUMN uidBase;
|
||||
ALTER TABLE account.sambaConfig CHANGE sshPass sshPassword varchar(255) DEFAULT NULL NULL COMMENT 'The SSH password';
|
||||
|
||||
ALTER TABLE account.ldapConfig DROP COLUMN `filter`;
|
||||
ALTER TABLE account.ldapConfig CHANGE baseDn userDn varchar(255) DEFAULT NULL NULL COMMENT 'The base DN to do the query';
|
||||
ALTER TABLE account.ldapConfig CHANGE host server varchar(255) NOT NULL COMMENT 'The hostname of LDAP server';
|
||||
ALTER TABLE account.ldapConfig MODIFY COLUMN password varchar(255) NOT NULL COMMENT 'The LDAP password';
|
|
@ -0,0 +1,20 @@
|
|||
CREATE TABLE `vn`.`supplierLog` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`originFk` int(11) NOT NULL,
|
||||
`userFk` int(10) unsigned NOT NULL,
|
||||
`action` set('insert','update','delete') COLLATE utf8_unicode_ci NOT NULL,
|
||||
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
||||
`description` text CHARACTER SET utf8 DEFAULT NULL,
|
||||
`changedModel` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
`oldInstance` text COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
`newInstance` text COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
`changedModelId` int(11) DEFAULT NULL,
|
||||
`changedModelValue` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `logSupplier_ibfk_1` (`originFk`),
|
||||
KEY `supplierLog_ibfk_2` (`userFk`),
|
||||
CONSTRAINT `supplierLog_ibfk_1` FOREIGN KEY (`originFk`) REFERENCES `supplier` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `supplierLog_ibfk_2` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
CREATE TABLE `vn`.`supplierContact` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`supplierFk` INT(11) NULL DEFAULT NULL,
|
||||
`phone` VARCHAR(16) NULL DEFAULT NULL,
|
||||
`mobile` VARCHAR(16) NULL DEFAULT NULL,
|
||||
`email` VARCHAR(255) NULL DEFAULT NULL,
|
||||
`observation` TEXT NULL DEFAULT NULL,
|
||||
`name` VARCHAR(255) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`))
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
ALTER TABLE `vn`.`supplierContact`
|
||||
ADD CONSTRAINT `supplier_id`
|
||||
FOREIGN KEY (`supplierFk`)
|
||||
REFERENCES `vn`.`supplier` (`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
INSERT INTO vn.supplierContact(supplierFk,phone,mobile,email,observation,`name`)
|
||||
SELECT r.Id_Proveedor,c.Telefono,c.Movil,c.email,c.Notas,concat(c.Nombre," ", IFNULL(c.Apellidos,""))
|
||||
FROM vn2008.Contactos c
|
||||
JOIN vn2008.Relaciones r ON r.Id_Contacto = c.Id_Contacto
|
||||
JOIN vn.supplier s ON s.id = r.Id_Proveedor;
|
|
@ -0,0 +1,25 @@
|
|||
DROP TRIGGER IF EXISTS `vn`.`ticket_afterUpdate`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `vn`$$
|
||||
CREATE DEFINER=`root`@`%` TRIGGER `ticket_afterUpdate`
|
||||
AFTER UPDATE ON `ticket`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF !(NEW.id <=> OLD.id)
|
||||
OR !(NEW.warehouseFk <=> OLD.warehouseFk)
|
||||
OR !(NEW.shipped <=> OLD.shipped) THEN
|
||||
CALL stock.log_add('ticket', NEW.id, OLD.id);
|
||||
END IF;
|
||||
|
||||
IF NEW.clientFk = 2067 AND !(NEW.clientFk <=> OLD.clientFk) THEN
|
||||
-- Fallo que se insertan no se sabe como tickets en este cliente
|
||||
INSERT INTO vn.mail SET
|
||||
`sender` = 'jgallego@verdnatura.es',
|
||||
`replyTo` = 'jgallego@verdnatura.es',
|
||||
`subject` = 'Modificado ticket al cliente 2067',
|
||||
`body` = CONCAT(account.myUserGetName(), ' ha modificado el ticket ',
|
||||
NEW.id);
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,2 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('supplier', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierContact', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -304,36 +304,36 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
|
|||
(10, 'Jessica Jones', 'NYCC 2015 Poster', 'Silla', 46460, 1, 1111111111, 222222222, 1, 110, 2, NULL, NULL, 0, 1),
|
||||
(11, 'Missing', 'The space', 'Silla', 46460, 1, 1111111111, 222222222, 1, 111, 10, NULL, NULL, 0, 1),
|
||||
(12, 'Trash', 'New York city', 'Silla', 46460, 1, 1111111111, 222222222, 1, 112, 10, NULL, NULL, 0, 1),
|
||||
(101, 'address 01', 'Somewhere in Thailand', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(102, 'address 02', 'Somewhere in Poland', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(103, 'address 03', 'Somewhere in Japan', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(104, 'address 04', 'Somewhere in Spain', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(105, 'address 05', 'Somewhere in Potugal', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(106, 'address 06', 'Somewhere in UK', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(107, 'address 07', 'Somewhere in Valencia', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(108, 'address 08', 'Somewhere in Silla', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(109, 'address 09', 'Somewhere in London', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(110, 'address 10', 'Somewhere in Algemesi', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(111, 'address 11', 'Somewhere in Carlet', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(112, 'address 12', 'Somewhere in Campanar', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(113, 'address 13', 'Somewhere in Malilla', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(114, 'address 14', 'Somewhere in France', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(115, 'address 15', 'Somewhere in Birmingham', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(116, 'address 16', 'Somewhere in Scotland', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(117, 'address 17', 'Somewhere in nowhere', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(118, 'address 18', 'Somewhere over the rainbow', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(119, 'address 19', 'Somewhere in Alberic', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(120, 'address 20', 'Somewhere in Montortal', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(121, 'address 21', 'the bat cave', 'Silla', 46460, 1, 1111111111, 222222222, 1, 101, 2, NULL, NULL, 0, 0),
|
||||
(122, 'address 22', 'NY roofs', 'Silla', 46460, 1, 1111111111, 222222222, 1, 102, 2, NULL, NULL, 0, 0),
|
||||
(123, 'address 23', 'The phone box', 'Silla', 46460, 1, 1111111111, 222222222, 1, 103, 2, NULL, NULL, 0, 0),
|
||||
(124, 'address 24', 'Stark tower Silla', 'Silla', 46460, 1, 1111111111, 222222222, 1, 104, 2, NULL, NULL, 0, 0),
|
||||
(125, 'address 25', 'The plastic cell', 'Silla', 46460, 1, 1111111111, 222222222, 1, 105, 2, NULL, NULL, 0, 0),
|
||||
(126, 'address 26', 'Many places', 'Silla', 46460, 1, 1111111111, 222222222, 1, 106, 2, NULL, NULL, 0, 0),
|
||||
(127, 'address 27', 'Your pocket', 'Silla', 46460, 1, 1111111111, 222222222, 1, 107, 2, NULL, NULL, 0, 0),
|
||||
(128, 'address 28', 'Cerebro', 'Silla', 46460, 1, 1111111111, 222222222, 1, 108, 2, NULL, NULL, 0, 0),
|
||||
(129, 'address 29', 'Luke Cages Bar', 'Silla', 46460, 1, 1111111111, 222222222, 1, 110, 2, NULL, NULL, 0, 0),
|
||||
(130, 'address 30', 'Non valid address', 'Silla', 46460, 1, 1111111111, 222222222, 0, 101, 2, NULL, NULL, 0, 0);
|
||||
(101, 'Somewhere in Thailand', 'address 01', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(102, 'Somewhere in Poland', 'address 02', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(103, 'Somewhere in Japan', 'address 03', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(104, 'Somewhere in Spain', 'address 04', 'Silla', 46460, 1, 3333333333, 444444444, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(105, 'Somewhere in Potugal', 'address 05', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(106, 'Somewhere in UK', 'address 06', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(107, 'Somewhere in Valencia', 'address 07', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(108, 'Somewhere in Silla', 'address 08', 'Silla', 46460, 1, 5555555555, 666666666, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(109, 'Somewhere in London', 'address 09', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(110, 'Somewhere in Algemesi', 'address 10', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(111, 'Somewhere in Carlet', 'address 11', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(112, 'Somewhere in Campanar', 'address 12', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(113, 'Somewhere in Malilla', 'address 13', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(114, 'Somewhere in France', 'address 14', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(115, 'Somewhere in Birmingham', 'address 15', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(116, 'Somewhere in Scotland', 'address 16', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(117, 'Somewhere in nowhere', 'address 17', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(118, 'Somewhere over the rainbow', 'address 18', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(119, 'Somewhere in Alberic', 'address 19', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(120, 'Somewhere in Montortal', 'address 20', 'Silla', 46460, 1, 1111111111, 222222222, 1, 109, 2, NULL, NULL, 0, 0),
|
||||
(121, 'the bat cave', 'address 21', 'Silla', 46460, 1, 1111111111, 222222222, 1, 101, 2, NULL, NULL, 0, 0),
|
||||
(122, 'NY roofs', 'address 22', 'Silla', 46460, 1, 1111111111, 222222222, 1, 102, 2, NULL, NULL, 0, 0),
|
||||
(123, 'The phone box', 'address 23', 'Silla', 46460, 1, 1111111111, 222222222, 1, 103, 2, NULL, NULL, 0, 0),
|
||||
(124, 'Stark tower Silla', 'address 24', 'Silla', 46460, 1, 1111111111, 222222222, 1, 104, 2, NULL, NULL, 0, 0),
|
||||
(125, 'The plastic cell', 'address 25', 'Silla', 46460, 1, 1111111111, 222222222, 1, 105, 2, NULL, NULL, 0, 0),
|
||||
(126, 'Many places', 'address 26', 'Silla', 46460, 1, 1111111111, 222222222, 1, 106, 2, NULL, NULL, 0, 0),
|
||||
(127, 'Your pocket', 'address 27', 'Silla', 46460, 1, 1111111111, 222222222, 1, 107, 2, NULL, NULL, 0, 0),
|
||||
(128, 'Cerebro', 'address 28', 'Silla', 46460, 1, 1111111111, 222222222, 1, 108, 2, NULL, NULL, 0, 0),
|
||||
(129, 'Luke Cages Bar', 'address 29', 'Silla', 46460, 1, 1111111111, 222222222, 1, 110, 2, NULL, NULL, 0, 0),
|
||||
(130, 'Non valid address', 'address 30', 'Silla', 46460, 1, 1111111111, 222222222, 0, 101, 2, NULL, NULL, 0, 0);
|
||||
|
||||
INSERT INTO `vn`.`address`( `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `isActive`, `clientFk`, `agencyModeFk`, `isDefaultAddress`)
|
||||
SELECT name, CONCAT(name, 'Street'), 'SILLA', 46460, 1, 1, id, 2, 1
|
||||
|
@ -400,18 +400,20 @@ INSERT INTO `vn`.`clientObservation`(`id`, `clientFk`, `workerFk`, `text`, `crea
|
|||
(9, 109, 18, 'HULK SMASH! ...', CURDATE()),
|
||||
(10, 110, 18, 'They say everyone is born a hero. But if you let it, life will push you over the line until you are the villain.', CURDATE());
|
||||
|
||||
INSERT INTO `vn`.`observationType`(`id`,`description`)
|
||||
INSERT INTO `vn`.`observationType`(`id`,`description`, `code`)
|
||||
VALUES
|
||||
(1,'observation one'),
|
||||
(2,'observation two'),
|
||||
(3,'observation three'),
|
||||
(4,'comercial');
|
||||
(1, 'observation one', 'observation one'),
|
||||
(2, 'observation two', 'observation two'),
|
||||
(3, 'observation three', 'observation three'),
|
||||
(4, 'comercial', 'salesPerson'),
|
||||
(5, 'delivery', 'delivery');
|
||||
|
||||
INSERT INTO `vn`.`addressObservation`(`id`,`addressFk`,`observationTypeFk`,`description`)
|
||||
VALUES
|
||||
(1, 121, 1, 'under the floor'),
|
||||
(2, 121, 2, 'wears leather and goes out at night'),
|
||||
(3, 121, 3, 'care with the dog');
|
||||
(3, 121, 3, 'care with the dog'),
|
||||
(5, 122, 5, 'Delivery after 10am');
|
||||
|
||||
INSERT INTO `vn`.`creditClassification`(`id`, `client`, `dateStart`, `dateEnd`)
|
||||
VALUES
|
||||
|
@ -605,7 +607,8 @@ INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `des
|
|||
(8, 23, 2, 'wears leather and goes out at night'),
|
||||
(9, 23, 3, 'care with the dog'),
|
||||
(10, 23, 4, 'Reclama ticket: 8'),
|
||||
(11, 24, 4, 'Reclama ticket: 7');
|
||||
(11, 24, 4, 'Reclama ticket: 7'),
|
||||
(12, 11, 5, 'Delivery after 10am');
|
||||
|
||||
-- FIX for state hours on local, inter_afterInsert
|
||||
UPDATE vncontrol.inter SET odbc_date = DATE_ADD(CURDATE(), INTERVAL -10 SECOND);
|
||||
|
@ -1208,11 +1211,18 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
|
|||
(104, 500),
|
||||
(105, 5000);
|
||||
|
||||
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `phone`, `payDay`)
|
||||
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `transactionTypeSageFk`)
|
||||
VALUES
|
||||
(1, 'Plants SL', 'Plants nick', 4000000001, 1, 'A11111111', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 123456789, 15),
|
||||
(2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 987654321, 10),
|
||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 987123654, 15);
|
||||
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, NULL, NULL),
|
||||
(2, 'Farmer King', 'The farmer', 4000020002, 1, 'B22222222', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 8),
|
||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, 'C33333333', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, NULL, NULL);
|
||||
|
||||
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
||||
VALUES
|
||||
(1, 1, 123121212, 654789123, 'supplier1@email.es', 'observation1', 'the boss'),
|
||||
(2, 1, 987456132, NULL, NULL, NULL, 'the salesperson'),
|
||||
(3, 2, 321654987, NULL, 'supplier2@email.es', NULL, NULL),
|
||||
(4, 442, 321654987, NULL, NULL, 'observation442', NULL);
|
||||
|
||||
INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`)
|
||||
VALUES
|
||||
|
|
|
@ -316,10 +316,23 @@ let actions = {
|
|||
},
|
||||
|
||||
waitForTextInElement: async function(selector, text) {
|
||||
await this.waitForSelector(selector);
|
||||
return await this.waitForFunction((selector, text) => {
|
||||
return document.querySelector(selector).innerText.toLowerCase().includes(text.toLowerCase());
|
||||
}, {}, selector, text);
|
||||
const expectedText = text.toLowerCase();
|
||||
return new Promise((resolve, reject) => {
|
||||
let attempts = 0;
|
||||
const interval = setInterval(async() => {
|
||||
const currentText = await this.evaluate(selector => {
|
||||
return document.querySelector(selector).innerText.toLowerCase();
|
||||
}, selector);
|
||||
|
||||
if (currentText === expectedText || attempts === 40) {
|
||||
clearInterval(interval);
|
||||
resolve(currentText);
|
||||
}
|
||||
attempts += 1;
|
||||
}, 100);
|
||||
}).then(result => {
|
||||
return expect(result).toContain(expectedText);
|
||||
});
|
||||
},
|
||||
|
||||
selectorFormater: function(selector) {
|
||||
|
|
|
@ -123,19 +123,19 @@ export default {
|
|||
streetAddress: 'vn-textfield[ng-model="$ctrl.address.street"]',
|
||||
postcode: 'vn-datalist[ng-model="$ctrl.address.postalCode"]',
|
||||
city: 'vn-datalist[ng-model="$ctrl.address.city"]',
|
||||
province: 'vn-autocomplete[ng-model="$ctrl.address.provinceId"]',
|
||||
agency: 'vn-autocomplete[ng-model="$ctrl.address.agencyModeId"]',
|
||||
province: 'vn-autocomplete[ng-model="$ctrl.address.provinceFk"]',
|
||||
agency: 'vn-autocomplete[ng-model="$ctrl.address.agencyModeFk"]',
|
||||
phone: 'vn-textfield[ng-model="$ctrl.address.phone"]',
|
||||
mobileInput: 'vn-textfield[ng-model="$ctrl.address.mobile"]',
|
||||
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
|
||||
incoterms: 'vn-autocomplete[ng-model="$ctrl.address.incotermsId"]',
|
||||
addNewCustomsAgent: 'vn-client-address-create vn-autocomplete[ng-model="$ctrl.address.customsAgentId"] vn-icon-button[icon="add_circle"]',
|
||||
incoterms: 'vn-autocomplete[ng-model="$ctrl.address.incotermsFk"]',
|
||||
addNewCustomsAgent: 'vn-client-address-create vn-autocomplete[ng-model="$ctrl.address.customsAgentFk"] vn-icon-button[icon="add_circle"]',
|
||||
newCustomsAgentFiscalID: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.nif"]',
|
||||
newCustomsAgentFiscalName: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.fiscalName"]',
|
||||
newCustomsAgentStreet: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.street"]',
|
||||
newCustomsAgentPhone: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.phone"]',
|
||||
saveNewCustomsAgentButton: 'button[response="accept"]',
|
||||
customsAgent: 'vn-autocomplete[ng-model="$ctrl.address.customsAgentId"]',
|
||||
customsAgent: 'vn-autocomplete[ng-model="$ctrl.address.customsAgentFk"]',
|
||||
secondMakeDefaultStar: 'vn-client-address-index vn-card div:nth-child(2) vn-icon-button[icon="star_border"]',
|
||||
firstEditAddress: 'vn-client-address-index div:nth-child(1) > a',
|
||||
secondEditAddress: 'vn-client-address-index div:nth-child(2) > a',
|
||||
|
@ -901,5 +901,27 @@ export default {
|
|||
newEntryTravel: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.travelFk"]',
|
||||
newEntryCompany: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.companyFk"]',
|
||||
saveNewEntry: 'vn-entry-create button[type="submit"]'
|
||||
},
|
||||
supplierSummary: {
|
||||
header: 'vn-supplier-summary > vn-card > h5',
|
||||
basicDataId: 'vn-supplier-summary vn-label-value[label="Id"]',
|
||||
fiscalAddressTaxNumber: 'vn-supplier-summary vn-label-value[label="Tax number"]',
|
||||
billingDataPayMethod: 'vn-supplier-summary vn-label-value[label="Pay method"]'
|
||||
},
|
||||
supplierDescriptor: {
|
||||
alias: 'vn-supplier-descriptor vn-label-value[label="Alias"]',
|
||||
clientButton: 'vn-supplier-descriptor vn-icon[icon="person"]',
|
||||
entriesButton: 'vn-supplier-descriptor vn-icon[icon="icon-entry"]',
|
||||
},
|
||||
supplierContact: {
|
||||
anyContact: 'vn-supplier-contact > form > vn-card > div',
|
||||
addNewContact: 'vn-supplier-contact vn-icon[icon="add_circle"]',
|
||||
thirdContactName: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.name"]',
|
||||
thirdContactPhone: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.phone"]',
|
||||
thirdContactMobile: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.mobile"]',
|
||||
thirdContactEmail: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.email"]',
|
||||
thirdContactNotes: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.observation"]',
|
||||
saveButton: 'vn-supplier-contact button[type="submit"]',
|
||||
thirdContactDeleteButton: 'vn-supplier-contact div:nth-child(3) vn-icon-button[icon="delete"]'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('Login path', async() => {
|
|||
const message = await page.waitForSnackbar();
|
||||
const state = await page.getState();
|
||||
|
||||
expect(message.type).toBe('error');
|
||||
expect(message.text).toBe('Invalid login, remember that distinction is made between uppercase and lowercase');
|
||||
expect(state).toBe('login');
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@ describe('Login path', async() => {
|
|||
const message = await page.waitForSnackbar();
|
||||
const state = await page.getState();
|
||||
|
||||
expect(message.type).toBe('error');
|
||||
expect(message.text).toBe('Invalid login, remember that distinction is made between uppercase and lowercase');
|
||||
expect(state).toBe('login');
|
||||
});
|
||||
|
||||
|
@ -37,7 +37,7 @@ describe('Login path', async() => {
|
|||
const message = await page.waitForSnackbar();
|
||||
const state = await page.getState();
|
||||
|
||||
expect(message.type).toBe('error');
|
||||
expect(message.text).toBe('Please enter your username');
|
||||
expect(state).toBe('login');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -119,7 +119,7 @@ describe('Client create path', () => {
|
|||
await page.waitToClick(selectors.createClientView.createButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should click on the Clients button of the top bar menu', async() => {
|
||||
|
|
|
@ -37,7 +37,7 @@ describe('Client Edit basicData path', () => {
|
|||
await page.waitToClick(selectors.clientBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the name have been edited', async() => {
|
||||
|
@ -101,7 +101,7 @@ describe('Client Edit basicData path', () => {
|
|||
await page.waitToClick(selectors.clientBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should now confirm the name have been edited', async() => {
|
||||
|
|
|
@ -93,7 +93,7 @@ describe('Client Edit fiscalData path', () => {
|
|||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should propagate the Equalization tax', async() => {
|
||||
|
@ -119,7 +119,7 @@ describe('Client Edit fiscalData path', () => {
|
|||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
// confirm all addresses have now EQtax checked step 1
|
||||
|
@ -155,7 +155,7 @@ describe('Client Edit fiscalData path', () => {
|
|||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should propagate the Equalization tax changes', async() => {
|
||||
|
@ -289,7 +289,7 @@ describe('Client Edit fiscalData path', () => {
|
|||
await page.waitToClick(selectors.clientAddresses.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
// confirm invoice by address checkbox gets checked if the EQtax differs between addresses step 3
|
||||
|
|
|
@ -91,18 +91,18 @@ describe('Client Add address path', () => {
|
|||
await page.waitToClick(selectors.clientAddresses.secondMakeDefaultStar);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the default address is the expected one`, async() => {
|
||||
await page.waitForTextInElement(selectors.clientAddresses.defaultAddress, 'Somewhere in Thailand');
|
||||
await page.waitForTextInElement(selectors.clientAddresses.defaultAddress, 'somewhere in new york');
|
||||
const result = await page.waitToGetProperty(selectors.clientAddresses.defaultAddress, 'innerText');
|
||||
|
||||
expect(result).toContain('Somewhere in Thailand');
|
||||
expect(result).toContain('Somewhere in New York');
|
||||
});
|
||||
|
||||
it(`should click on the edit icon of the default address`, async() => {
|
||||
await page.waitForTextInElement(selectors.clientAddresses.defaultAddress, 'Somewhere in Thailand');
|
||||
await page.waitForTextInElement(selectors.clientAddresses.defaultAddress, 'somewhere in new york');
|
||||
await page.waitToClick(selectors.clientAddresses.firstEditAddress);
|
||||
await page.waitForState('client.card.address.edit');
|
||||
});
|
||||
|
|
|
@ -48,6 +48,6 @@ describe('Client add address notes path', () => {
|
|||
await page.waitToClick(selectors.clientAddresses.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Client Edit web access path', () => {
|
|||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm web access is now unchecked', async() => {
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('Client Add notes path', () => {
|
|||
await page.waitToClick(selectors.clientNotes.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the note was created', async() => {
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('Client Add credit path', () => {
|
|||
await page.waitToClick(selectors.clientCredit.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the credit was updated', async() => {
|
||||
|
|
|
@ -36,7 +36,7 @@ describe('Client Add greuge path', () => {
|
|||
await page.waitToClick(selectors.clientGreuge.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the greuge was added to the list', async() => {
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('Client lock verified data path', () => {
|
|||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the social name have been edited', async() => {
|
||||
|
@ -64,7 +64,7 @@ describe('Client lock verified data path', () => {
|
|||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm Verified data checkbox is checked', async() => {
|
||||
|
@ -81,7 +81,7 @@ describe('Client lock verified data path', () => {
|
|||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should again confirm the social name have been edited', async() => {
|
||||
|
@ -135,7 +135,7 @@ describe('Client lock verified data path', () => {
|
|||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should now confirm the social name have been edited once and for all', async() => {
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('Client log path', () => {
|
|||
await page.waitToClick(selectors.clientBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should navigate to the log section', async() => {
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('Client balance path', () => {
|
|||
await page.autocompleteSearch(selectors.globalItems.userLocalCompany, 'CCs');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should access to the balance section to check the data shown matches the local settings', async() => {
|
||||
|
@ -35,7 +35,7 @@ describe('Client balance path', () => {
|
|||
await page.clearInput(selectors.globalItems.userConfigThirdAutocomplete);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should click the new payment button', async() => {
|
||||
|
@ -51,7 +51,7 @@ describe('Client balance path', () => {
|
|||
await page.waitToClick(selectors.clientBalance.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should edit the 1st line reference', async() => {
|
||||
|
@ -60,7 +60,7 @@ describe('Client balance path', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check balance is now 0, the reference was saved and the company is now VNL becouse the user local settings were removed', async() => {
|
||||
|
@ -85,7 +85,7 @@ describe('Client balance path', () => {
|
|||
await page.waitToClick(selectors.clientBalance.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check balance is now -100', async() => {
|
||||
|
@ -101,7 +101,7 @@ describe('Client balance path', () => {
|
|||
await page.waitToClick(selectors.clientBalance.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check balance is now 50', async() => {
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('Client DMS', () => {
|
|||
await page.respondToDialog('accept');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should click on the first document line worker name making the descriptor visible`, async() => {
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('Client contacts', () => {
|
|||
await page.waitToClick(selectors.clientContacts.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should delete de contact', async() => {
|
||||
|
@ -33,6 +33,6 @@ describe('Client contacts', () => {
|
|||
await page.waitToClick(selectors.clientContacts.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ describe('Worker basic data path', () => {
|
|||
await page.waitToClick(selectors.workerBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section then check the name was edited', async() => {
|
||||
|
|
|
@ -93,12 +93,8 @@ describe('Worker time control path', () => {
|
|||
expect(result).toEqual(scanTime);
|
||||
});
|
||||
|
||||
it(`should check Hank Pym worked 8 hours`, async() => {
|
||||
it(`should check Hank Pym worked 7 hours`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '07:00 h.');
|
||||
const result = await page
|
||||
.waitToGetProperty(selectors.workerTimeControl.mondayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('07:00 h.');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -149,9 +145,6 @@ describe('Worker time control path', () => {
|
|||
|
||||
it(`should check Hank Pym worked 8 happy hours`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.tuesdayWorkedHours, '08:00 h.');
|
||||
const result = await page.waitToGetProperty(selectors.workerTimeControl.tuesdayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('08:00 h.');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -202,9 +195,6 @@ describe('Worker time control path', () => {
|
|||
|
||||
it(`should check Hank Pym worked 8 cheerfull hours`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.wednesdayWorkedHours, '08:00 h.');
|
||||
const result = await page.waitToGetProperty(selectors.workerTimeControl.wednesdayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('08:00 h.');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -252,9 +242,6 @@ describe('Worker time control path', () => {
|
|||
|
||||
it(`should check Hank Pym worked 8 joyfull hours`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.thursdayWorkedHours, '08:00 h.');
|
||||
const result = await page.waitToGetProperty(selectors.workerTimeControl.thursdayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('08:00 h.');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -301,9 +288,6 @@ describe('Worker time control path', () => {
|
|||
|
||||
it(`should check Hank Pym worked 8 hours with a smile on his face`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.fridayWorkedHours, '08:00 h.');
|
||||
const result = await page.waitToGetProperty(selectors.workerTimeControl.fridayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('08:00 h.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -346,9 +330,6 @@ describe('Worker time control path', () => {
|
|||
|
||||
it(`should check Hank Pym worked 8 hours with all his will`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.saturdayWorkedHours, '08:00 h.');
|
||||
const result = await page.waitToGetProperty(selectors.workerTimeControl.saturdayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('08:00 h.');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -375,19 +356,12 @@ describe('Worker time control path', () => {
|
|||
|
||||
it(`should check Hank Pym worked 8 glad hours`, async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.sundayWorkedHours, '08:00 h.');
|
||||
const result = await page.waitToGetProperty(selectors.workerTimeControl.sundayWorkedHours, 'innerText');
|
||||
|
||||
expect(result).toEqual('08:00 h.');
|
||||
});
|
||||
|
||||
it(`should check Hank Pym doesn't have hours set on the next months first week`, async() => {
|
||||
await page.waitToClick(selectors.workerTimeControl.nextMonthButton);
|
||||
await page.waitToClick(selectors.workerTimeControl.secondWeekDay);
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '00:00 h.');
|
||||
const wholeWeekHours = await page
|
||||
.waitToGetProperty(selectors.workerTimeControl.weekWorkedHours, 'innerText');
|
||||
|
||||
expect(wholeWeekHours).toEqual('00:00 h.');
|
||||
});
|
||||
|
||||
it(`should check he didn't scan in this week yet`, async() => {
|
||||
|
@ -410,11 +384,8 @@ describe('Worker time control path', () => {
|
|||
await page.accessToSection('worker.card.timeControl');
|
||||
});
|
||||
|
||||
it('should check his hours are alright', async() => {
|
||||
it('should check his weekly hours are alright', async() => {
|
||||
await page.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '55:00 h.');
|
||||
const wholeWeekHours = await page.waitToGetProperty(selectors.workerTimeControl.weekWorkedHours, 'innerText');
|
||||
|
||||
expect(wholeWeekHours).toEqual('55:00 h.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('Worker calendar path', () => {
|
|||
expect(result).toContain(' 5 ');
|
||||
});
|
||||
|
||||
it('should set two days as holidays on the calendar', async() => {
|
||||
it('should set two days as holidays on the calendar and check the total holidays increased by 1.5', async() => {
|
||||
await page.waitToClick(selectors.workerCalendar.holidays);
|
||||
await page.waitFor(reasonableTimeBetweenClicks);
|
||||
await page.waitToClick(selectors.workerCalendar.januaryThirtyFirst);
|
||||
|
@ -50,9 +50,8 @@ describe('Worker calendar path', () => {
|
|||
await page.waitToClick(selectors.workerCalendar.halfFurlough);
|
||||
await page.waitFor(reasonableTimeBetweenClicks);
|
||||
await page.waitToClick(selectors.workerCalendar.mayEighth);
|
||||
});
|
||||
await page.waitFor(reasonableTimeBetweenClicks);
|
||||
|
||||
it('should check the total holidays increased by 1.5', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
|
||||
|
||||
expect(result).toContain(' 6.5 ');
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('Item Edit basic data path', () => {
|
|||
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should create a new intrastat`, async() => {
|
||||
|
@ -57,7 +57,7 @@ describe('Item Edit basic data path', () => {
|
|||
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the item name was edited`, async() => {
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Item edit tax path', () => {
|
|||
await page.waitToClick(selectors.itemTax.submitTaxButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the first item tax class was edited`, async() => {
|
||||
|
|
|
@ -26,7 +26,7 @@ describe('Item create tags path', () => {
|
|||
await page.waitToClick(selectors.itemTags.submitItemTagsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the fourth row data is the expected one`, async() => {
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('Item create niche path', () => {
|
|||
await page.waitToClick(selectors.itemNiches.submitNichesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the first niche is the expected one`, async() => {
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Item Create botanical path', () => {
|
|||
await page.waitToClick(selectors.itemBotanical.submitBotanicalButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the botanical for the item was created`, async() => {
|
||||
|
@ -58,7 +58,7 @@ describe('Item Create botanical path', () => {
|
|||
await page.waitToClick(selectors.itemBotanical.submitBotanicalButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the botanical for the item was edited`, async() => {
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Item Create barcodes path', () => {
|
|||
await page.waitToClick(selectors.itemBarcodes.submitBarcodesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the barcode 5 is created and it is now the third barcode as the first was deleted`, async() => {
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('Item Create/Clone path', () => {
|
|||
await page.waitToClick(selectors.itemCreateView.createButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm Infinity Gauntlet item was created', async() => {
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('Item regularize path', () => {
|
|||
await page.autocompleteSearch(selectors.globalItems.userLocalWarehouse, 'Warehouse Four');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check the local settings were saved', async() => {
|
||||
|
@ -51,7 +51,7 @@ describe('Item regularize path', () => {
|
|||
await page.waitToClick(selectors.itemDescriptor.regularizeSaveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should click on the Tickets button of the top bar menu', async() => {
|
||||
|
@ -70,7 +70,7 @@ describe('Item regularize path', () => {
|
|||
await page.clearInput(selectors.globalItems.userConfigFirstAutocomplete);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should search for the ticket with alias missing', async() => {
|
||||
|
@ -114,7 +114,7 @@ describe('Item regularize path', () => {
|
|||
await page.waitToClick(selectors.itemDescriptor.regularizeSaveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should again click on the Tickets button of the top bar menu', async() => {
|
||||
|
|
|
@ -36,7 +36,7 @@ describe('Item index path', () => {
|
|||
await page.waitToClick(selectors.itemsIndex.saveFieldsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should navigate forth and back to see the images column is still visible', async() => {
|
||||
|
@ -70,7 +70,7 @@ describe('Item index path', () => {
|
|||
await page.waitToClick(selectors.itemsIndex.saveFieldsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should now navigate forth and back to see the ids column is now visible', async() => {
|
||||
|
|
|
@ -34,7 +34,7 @@ describe('Item log path', () => {
|
|||
await page.waitToClick(selectors.itemCreateView.createButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should return to the items index by clicking the return to items button', async() => {
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('Item descriptor path', () => {
|
|||
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section and check the inactive icon is bright', async() => {
|
||||
|
@ -45,6 +45,6 @@ describe('Item descriptor path', () => {
|
|||
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -77,7 +77,7 @@ describe('Ticket List sale path', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should update the description of the new sale', async() => {
|
||||
|
@ -86,7 +86,7 @@ describe('Ticket List sale path', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should add a third empty item to the sale list', async() => {
|
||||
|
@ -104,7 +104,7 @@ describe('Ticket List sale path', () => {
|
|||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should verify there's only 1 single line remaining`, async() => {
|
||||
|
|
|
@ -41,7 +41,7 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should check it's state is libre now`, async() => {
|
||||
|
@ -55,7 +55,7 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.waitToClick(selectors.ticketSales.setOk);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should check it's state is OK now`, async() => {
|
||||
|
@ -135,7 +135,7 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.type(selectors.ticketSales.firstSaleQuantity, '9\u000d');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should update the price', async() => {
|
||||
|
@ -144,7 +144,7 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.type(selectors.ticketSales.firstSalePriceInput, '5\u000d');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the price have been updated', async() => {
|
||||
|
@ -165,7 +165,7 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.type(selectors.ticketSales.firstSaleDiscountInput, '50\u000d');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the discount have been updated', async() => {
|
||||
|
@ -224,7 +224,7 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.waitForSpinnerLoad();
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the third sale was deleted`, async() => {
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('Ticket Create notes path', () => {
|
|||
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the note is the expected one', async() => {
|
||||
|
@ -45,6 +45,6 @@ describe('Ticket Create notes path', () => {
|
|||
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('Ticket Create packages path', () => {
|
|||
await page.waitToClick(selectors.ticketPackages.savePackagesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the first select is the expected one`, async() => {
|
||||
|
|
|
@ -36,7 +36,7 @@ describe('Ticket Create new tracking state path', () => {
|
|||
await page.waitToClick(selectors.createStateView.saveStateButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -74,7 +74,7 @@ describe('Ticket Create new tracking state path', () => {
|
|||
await page.waitToClick(selectors.createStateView.saveStateButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('Ticket descriptor path', () => {
|
|||
await page.waitToClick(selectors.ticketDescriptor.thursdayButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should again click on the Tickets button of the top bar menu', async() => {
|
||||
|
@ -68,7 +68,7 @@ describe('Ticket descriptor path', () => {
|
|||
await page.waitToClick(selectors.ticketDescriptor.saturdayButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should click on the Tickets button of the top bar menu once again', async() => {
|
||||
|
@ -97,7 +97,7 @@ describe('Ticket descriptor path', () => {
|
|||
await page.waitToClick(selectors.ticketsIndex.acceptDeleteTurn);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the sixth weekly ticket was deleted', async() => {
|
||||
|
@ -111,11 +111,11 @@ describe('Ticket descriptor path', () => {
|
|||
await page.autocompleteSearch(selectors.ticketsIndex.firstWeeklyTicketAgency, 'Silla247');
|
||||
let message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
|
||||
await page.clearInput(selectors.ticketsIndex.firstWeeklyTicketAgency);
|
||||
message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('Ticket purchase request path', () => {
|
|||
await page.waitToClick(selectors.ticketRequests.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should have been redirected to the request index', async() => {
|
||||
|
@ -39,24 +39,24 @@ describe('Ticket purchase request path', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the new request was added', async() => {
|
||||
it('should check the new request was added', async() => {
|
||||
await page.reloadSection('ticket.card.request.index');
|
||||
const result = await page.waitToGetProperty(selectors.ticketRequests.thirdRequestQuantity, 'value');
|
||||
|
||||
expect(result).toEqual('99');
|
||||
});
|
||||
|
||||
it(`should confirm first request can't be edited as its state is different to new`, async() => {
|
||||
it(`should check the first request can't be edited as its state is different to new`, async() => {
|
||||
await page.waitForClassPresent(selectors.ticketRequests.firstRequestQuantity, 'disabled');
|
||||
const result = await page.isDisabled(selectors.ticketRequests.firstRequestQuantity);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it(`should confirm second request can't be edited as its state is different to new`, async() => {
|
||||
it(`should check the second request can't be edited as its state is different to new`, async() => {
|
||||
await page.waitForClassPresent(selectors.ticketRequests.secondRequestQuantity, 'disabled');
|
||||
const result = await page.isDisabled(selectors.ticketRequests.secondRequestQuantity);
|
||||
|
||||
|
@ -67,10 +67,10 @@ describe('Ticket purchase request path', () => {
|
|||
await page.waitToClick(selectors.ticketRequests.thirdRemoveRequestButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the request was deleted', async() => {
|
||||
it('should check the request was deleted', async() => {
|
||||
await page.reloadSection('ticket.card.request.index');
|
||||
await page.wait(selectors.ticketRequests.addRequestButton);
|
||||
await page.waitForSelector(selectors.ticketRequests.thirdDescription, {hidden: true});
|
||||
|
|
|
@ -100,7 +100,7 @@ describe('Ticket descriptor path', () => {
|
|||
await page.waitToClick(selectors.ticketDescriptor.addStowawayDialogFirstTicket);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
xit(`should check the state of the stowaway ticket is embarked`, async() => {
|
||||
|
@ -122,7 +122,7 @@ describe('Ticket descriptor path', () => {
|
|||
await page.waitToClick(selectors.ticketDescriptor.acceptDeleteStowawayButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the ship buton doesn't exisist any more`, async() => {
|
||||
|
|
|
@ -83,7 +83,7 @@ describe('Ticket services path', () => {
|
|||
await page.waitToClick(selectors.ticketService.saveServiceButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the service description was created correctly', async() => {
|
||||
|
@ -114,7 +114,7 @@ describe('Ticket services path', () => {
|
|||
await page.waitToClick(selectors.ticketService.saveServiceButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the service was removed`, async() => {
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('Ticket create path', () => {
|
|||
await page.waitToClick(selectors.createTicketView.createButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check the url is now the summary of the ticket', async() => {
|
||||
|
@ -55,7 +55,7 @@ describe('Ticket create path', () => {
|
|||
await page.waitToClick(selectors.createTicketView.createButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check the url is now the summary of the created ticket', async() => {
|
||||
|
@ -68,7 +68,7 @@ describe('Ticket create path', () => {
|
|||
await page.waitToClick(selectors.ticketDescriptor.addStowawayDialogFirstTicket);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should delete the current ticket', async() => {
|
||||
|
@ -77,7 +77,7 @@ describe('Ticket create path', () => {
|
|||
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Ticket deleted. You can undo this action within the first hour');
|
||||
});
|
||||
|
||||
it('should search for the stowaway ticket of the previously deleted ticket', async() => {
|
||||
|
|
|
@ -80,7 +80,7 @@ describe('Ticket Summary path', () => {
|
|||
await page.waitToClick(selectors.ticketSummary.setOk);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the ticket state was updated', async() => {
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('Ticket log path', () => {
|
|||
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should navigate to the log section', async() => {
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('Ticket index payout path', () => {
|
|||
await page.waitToClick(selectors.ticketsIndex.submitPayout);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should navigate to the client balance section and check a new balance line was entered', async() => {
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('Claim edit basic data path', () => {
|
|||
await page.waitToClick(selectors.claimBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should have been redirected to the next section of claims as the role is claimManager`, async() => {
|
||||
|
@ -40,7 +40,7 @@ describe('Claim edit basic data path', () => {
|
|||
await page.waitToClick(selectors.claimBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the claim state was edited', async() => {
|
||||
|
@ -71,6 +71,6 @@ describe('Claim edit basic data path', () => {
|
|||
await page.waitToClick(selectors.claimBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,7 +28,7 @@ describe('Claim development', () => {
|
|||
await page.waitToClick(selectors.claimDevelopment.saveDevelopmentButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should redirect to the next section of claims as the role is claimManager`, async() => {
|
||||
|
@ -45,7 +45,7 @@ describe('Claim development', () => {
|
|||
await page.waitToClick(selectors.claimDevelopment.saveDevelopmentButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the first development is the expected one', async() => {
|
||||
|
|
|
@ -23,7 +23,7 @@ xdescribe('Claim detail', () => {
|
|||
await page.waitToClick(selectors.claimDetail.firstClaimableSaleFromTicket);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the claim contains now two items', async() => {
|
||||
|
@ -38,7 +38,7 @@ xdescribe('Claim detail', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the first item quantity, and the claimed total were correctly edited', async() => {
|
||||
|
@ -67,7 +67,7 @@ xdescribe('Claim detail', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check the mana is the expected one', async() => {
|
||||
|
@ -81,7 +81,7 @@ xdescribe('Claim detail', () => {
|
|||
await page.waitToClick(selectors.claimDetail.secondItemDeleteButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the claim contains now one item', async() => {
|
||||
|
@ -95,7 +95,7 @@ xdescribe('Claim detail', () => {
|
|||
await page.waitToClick(selectors.claimDetail.firstClaimableSaleFromTicket);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should have been redirected to the next section in claims`, async() => {
|
||||
|
|
|
@ -21,7 +21,7 @@ describe('Claim action path', () => {
|
|||
await page.waitToClick(selectors.claimAction.importClaimButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should import the second importable ticket', async() => {
|
||||
|
@ -33,7 +33,7 @@ describe('Claim action path', () => {
|
|||
await page.waitToClick(selectors.claimAction.secondImportableTicket);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should edit the second line destination field', async() => {
|
||||
|
@ -41,14 +41,14 @@ describe('Claim action path', () => {
|
|||
await page.autocompleteSearch(selectors.claimAction.secondLineDestination, 'Bueno');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should delete the first line', async() => {
|
||||
await page.waitToClick(selectors.claimAction.firstDeleteLine);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should refresh the view to check the remaining line is the expected one', async() => {
|
||||
|
@ -62,14 +62,14 @@ describe('Claim action path', () => {
|
|||
await page.waitToClick(selectors.claimAction.firstDeleteLine);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should check the "is paid with mana" checkbox', async() => {
|
||||
await page.waitToClick(selectors.claimAction.isPaidWithManaCheckbox);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the "is paid with mana" is checked', async() => {
|
||||
|
|
|
@ -29,13 +29,13 @@ describe('Order summary path', () => {
|
|||
it('should check the summary contains the order alias', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.orderSummary.alias, 'innerText');
|
||||
|
||||
expect(result).toEqual('address 26');
|
||||
expect(result).toEqual('Many places');
|
||||
});
|
||||
|
||||
it('should check the summary contains the order consignee', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.orderSummary.consignee, 'innerText');
|
||||
|
||||
expect(result).toEqual('Many places - Silla (Province one)');
|
||||
expect(result).toEqual('address 26 - Silla (Province one)');
|
||||
});
|
||||
|
||||
it('should check the summary contains the order subtotal', async() => {
|
||||
|
|
|
@ -81,7 +81,7 @@ describe('Order edit basic data path', () => {
|
|||
await page.waitToClick(selectors.orderBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should now confirm the client have been edited', async() => {
|
||||
|
|
|
@ -28,7 +28,7 @@ describe('Order lines', () => {
|
|||
await page.waitToClick(selectors.orderLine.confirmButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the order subtotal has changed', async() => {
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('Route basic Data path', () => {
|
|||
await page.waitToClick(selectors.routeBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the worker was edited', async() => {
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('Route create path', () => {
|
|||
await page.waitToClick(selectors.createRouteView.submitButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should confirm the redirection to the created route summary`, async() => {
|
||||
|
|
|
@ -23,7 +23,7 @@ xdescribe('Route basic Data path', () => {
|
|||
await page.keyboard.press('Enter');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the buscamanButton is disabled', async() => {
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('Travel thermograph path', () => {
|
|||
const message = await page.waitForSnackbar();
|
||||
const state = await page.getState();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
expect(state).toBe('travel.card.thermograph.index');
|
||||
});
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ describe('Travel basic data path', () => {
|
|||
await page.waitToClick(selectors.travelBasicDada.save);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section and check the reference was saved', async() => {
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('Zone basic data path', () => {
|
|||
await page.waitToClick(selectors.zoneBasicData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.type).toBe('success');
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it('should now reload the section', async() => {
|
||||
|
|
|
@ -21,10 +21,10 @@ describe('Entry summary path', () => {
|
|||
});
|
||||
|
||||
it(`should display details from the entry on the header`, async() => {
|
||||
await page.waitForTextInElement(selectors.entrySummary.header, 'The king');
|
||||
await page.waitForTextInElement(selectors.entrySummary.header, 'The farmer');
|
||||
const result = await page.waitToGetProperty(selectors.entrySummary.header, 'innerText');
|
||||
|
||||
expect(result).toContain('The king');
|
||||
expect(result).toContain('The farmer');
|
||||
});
|
||||
|
||||
it('should display some entry details like the reference', async() => {
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Supplier summary & descriptor path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('administrative', 'supplier');
|
||||
await page.accessToSearchResult('1');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
// summary
|
||||
it('should reach the second entry summary section', async() => {
|
||||
await page.waitForState('supplier.card.summary');
|
||||
});
|
||||
|
||||
it(`should confirm there's data on the summary header`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierSummary.header, 'innerText');
|
||||
|
||||
expect(result).toContain('Plants SL - 1');
|
||||
});
|
||||
|
||||
it(`should confirm there's data on the summary basic data`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierSummary.basicDataId, 'innerText');
|
||||
|
||||
expect(result).toContain('Id 1');
|
||||
});
|
||||
|
||||
it(`should confirm there's data on the summary fiscal address`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierSummary.fiscalAddressTaxNumber, 'innerText');
|
||||
|
||||
expect(result).toContain('Tax number 06089160W');
|
||||
});
|
||||
|
||||
it(`should confirm there's data on the summary fiscal pay method`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierSummary.billingDataPayMethod, 'innerText');
|
||||
|
||||
expect(result).toContain('Pay method PayMethod one');
|
||||
});
|
||||
|
||||
// descriptor
|
||||
it(`should confirm there's data on the descriptor`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierDescriptor.alias, 'innerText');
|
||||
|
||||
expect(result).toContain('Plants nick');
|
||||
});
|
||||
|
||||
it(`should navigate to the supplier's client summary using the icon client button`, async() => {
|
||||
await page.waitToClick(selectors.supplierDescriptor.clientButton);
|
||||
await page.waitForState('client.card.summary');
|
||||
});
|
||||
|
||||
it(`should navigate back to the supplier`, async() => {
|
||||
await page.waitToClick(selectors.globalItems.homeButton);
|
||||
await page.waitForState('home');
|
||||
await page.selectModule('supplier');
|
||||
await page.accessToSearchResult('1');
|
||||
await page.waitForState('supplier.card.summary');
|
||||
});
|
||||
|
||||
it(`should navigate to the supplier's entries`, async() => {
|
||||
await page.waitToClick(selectors.supplierDescriptor.entriesButton);
|
||||
await page.waitForState('entry.index');
|
||||
});
|
||||
|
||||
it(`should navigate back to suppliers but a different one this time`, async() => {
|
||||
await page.waitToClick(selectors.globalItems.homeButton);
|
||||
await page.waitForState('home');
|
||||
await page.selectModule('supplier');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.waitForState('supplier.card.summary');
|
||||
});
|
||||
|
||||
it(`should check the client button isn't present since this supplier should not be a client`, async() => {
|
||||
await page.waitForSelector(selectors.supplierDescriptor.clientButton, {hidden: true});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Supplier contact path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('administrative', 'supplier');
|
||||
await page.accessToSearchResult('1');
|
||||
await page.accessToSection('supplier.card.contact');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should create a new contact', async() => {
|
||||
await page.waitToClick(selectors.supplierContact.addNewContact);
|
||||
await page.write(selectors.supplierContact.thirdContactName, 'The tester');
|
||||
await page.write(selectors.supplierContact.thirdContactPhone, '99 999 99 99');
|
||||
await page.write(selectors.supplierContact.thirdContactMobile, '555 55 55 55');
|
||||
await page.write(selectors.supplierContact.thirdContactEmail, 'testing@puppeteer.com');
|
||||
await page.write(selectors.supplierContact.thirdContactNotes, 'the end to end integration tester');
|
||||
await page.waitToClick(selectors.supplierContact.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should reload the section and count the contacts`, async() => {
|
||||
await page.reloadSection('supplier.card.contact');
|
||||
const result = await page.countElement(selectors.supplierContact.anyContact);
|
||||
|
||||
expect(result).toEqual(3);
|
||||
});
|
||||
|
||||
it(`should check the new contact name was saved correctly`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierContact.thirdContactName, 'value');
|
||||
|
||||
expect(result).toContain('The tester');
|
||||
});
|
||||
|
||||
it(`should check the new contact phone was saved correctly`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierContact.thirdContactPhone, 'value');
|
||||
|
||||
expect(result).toContain('99 999 99 99');
|
||||
});
|
||||
|
||||
it(`should check the new contact mobile was saved correctly`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierContact.thirdContactMobile, 'value');
|
||||
|
||||
expect(result).toContain('555 55 55 55');
|
||||
});
|
||||
|
||||
it(`should check the new contact email was saved correctly`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierContact.thirdContactEmail, 'value');
|
||||
|
||||
expect(result).toContain('testing@puppeteer.com');
|
||||
});
|
||||
|
||||
it(`should check the new contact note was saved correctly`, async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierContact.thirdContactNotes, 'value');
|
||||
|
||||
expect(result).toContain('the end to end integration tester');
|
||||
});
|
||||
|
||||
it(`should remove the created contact`, async() => {
|
||||
await page.waitToClick(selectors.supplierContact.thirdContactDeleteButton, 'value');
|
||||
await page.waitToClick(selectors.supplierContact.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toBe('Data saved!');
|
||||
});
|
||||
|
||||
it(`should reload the section and count the amount of contacts went back to 2`, async() => {
|
||||
await page.reloadSection('supplier.card.contact');
|
||||
const result = await page.countElement(selectors.supplierContact.anyContact);
|
||||
|
||||
expect(result).toEqual(2);
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -163,6 +163,9 @@ function e2eSingleRun() {
|
|||
`${__dirname}/e2e/paths/08*/*[sS]pec.js`,
|
||||
`${__dirname}/e2e/paths/09*/*[sS]pec.js`,
|
||||
`${__dirname}/e2e/paths/10*/*[sS]pec.js`,
|
||||
`${__dirname}/e2e/paths/11*/*[sS]pec.js`,
|
||||
`${__dirname}/e2e/paths/12*/*[sS]pec.js`,
|
||||
`${__dirname}/e2e/paths/13*/*[sS]pec.js`,
|
||||
`${__dirname}/e2e/paths/**/*[sS]pec.js`
|
||||
];
|
||||
|
||||
|
|
|
@ -154,7 +154,8 @@ module.exports = function(Self) {
|
|||
const showFieldNames = [
|
||||
'name',
|
||||
'description',
|
||||
'code'
|
||||
'code',
|
||||
'nickname'
|
||||
];
|
||||
for (field of showFieldNames) {
|
||||
const propField = properties && properties[field];
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const ldap = require('../../util/ldapjs-extra');
|
||||
|
||||
const SyncEngine = require('../../util/sync-engine');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('sync', {
|
||||
|
@ -10,105 +11,17 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.sync = async function() {
|
||||
let $ = Self.app.models;
|
||||
|
||||
let ldapConfig = await $.LdapConfig.findOne({
|
||||
fields: ['host', 'rdn', 'password', 'groupDn']
|
||||
});
|
||||
let accountConfig = await $.AccountConfig.findOne({
|
||||
fields: ['idBase']
|
||||
});
|
||||
|
||||
if (!ldapConfig) return;
|
||||
|
||||
// Connect
|
||||
|
||||
let client = ldap.createClient({
|
||||
url: `ldap://${ldapConfig.host}:389`
|
||||
});
|
||||
|
||||
let ldapPassword = Buffer
|
||||
.from(ldapConfig.password, 'base64')
|
||||
.toString('ascii');
|
||||
await client.bind(ldapConfig.rdn, ldapPassword);
|
||||
let engine = new SyncEngine();
|
||||
await engine.init(Self.app.models);
|
||||
|
||||
let err;
|
||||
try {
|
||||
// Delete roles
|
||||
|
||||
let opts = {
|
||||
scope: 'sub',
|
||||
attributes: ['dn'],
|
||||
filter: 'objectClass=posixGroup'
|
||||
};
|
||||
res = await client.search(ldapConfig.groupDn, opts);
|
||||
|
||||
let reqs = [];
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', err => {
|
||||
if (err.name === 'NoSuchObjectError')
|
||||
err = new Error(`Object '${ldapConfig.groupDn}' does not exist`);
|
||||
reject(err);
|
||||
});
|
||||
res.on('searchEntry', e => {
|
||||
reqs.push(client.del(e.object.dn));
|
||||
});
|
||||
res.on('end', resolve);
|
||||
});
|
||||
await Promise.all(reqs);
|
||||
|
||||
// Recreate roles
|
||||
|
||||
let roles = await $.Role.find({
|
||||
fields: ['id', 'name']
|
||||
});
|
||||
let accounts = await $.UserAccount.find({
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
include: {
|
||||
relation: 'roles',
|
||||
scope: {
|
||||
fields: ['inheritsFrom']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let map = new Map();
|
||||
for (let account of accounts) {
|
||||
let user = account.user();
|
||||
for (let inherit of user.roles()) {
|
||||
let roleId = inherit.inheritsFrom;
|
||||
if (!map.has(roleId)) map.set(roleId, []);
|
||||
map.get(roleId).push(user.name);
|
||||
}
|
||||
}
|
||||
|
||||
reqs = [];
|
||||
for (let role of roles) {
|
||||
let newEntry = {
|
||||
objectClass: ['top', 'posixGroup'],
|
||||
cn: role.name,
|
||||
gidNumber: accountConfig.idBase + role.id
|
||||
};
|
||||
|
||||
let memberUid = map.get(role.id);
|
||||
if (memberUid) newEntry.memberUid = memberUid;
|
||||
|
||||
let dn = `cn=${role.name},${ldapConfig.groupDn}`;
|
||||
reqs.push(client.add(dn, newEntry));
|
||||
}
|
||||
await Promise.all(reqs);
|
||||
await engine.syncRoles();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
// FIXME: Cannot disconnect, hangs on undind() call
|
||||
// await client.unbind();
|
||||
await engine.deinit();
|
||||
if (err) throw err;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
const SyncEngine = require('../../util/sync-engine');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('syncAll', {
|
||||
description: 'Synchronizes user database with LDAP and Samba',
|
||||
http: {
|
||||
path: `/syncAll`,
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
||||
Self.syncAll = async function() {
|
||||
let $ = Self.app.models;
|
||||
|
||||
let engine = new SyncEngine();
|
||||
await engine.init($);
|
||||
|
||||
let usersToSync = await engine.getUsers();
|
||||
usersToSync = Array.from(usersToSync.values())
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
for (let user of usersToSync) {
|
||||
try {
|
||||
console.log(`Synchronizing user '${user}'`);
|
||||
await engine.sync(user);
|
||||
console.log(` -> '${user}' sinchronized`);
|
||||
} catch (err) {
|
||||
console.error(` -> '${user}' synchronization error:`, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
await engine.deinit();
|
||||
await $.RoleInherit.sync();
|
||||
};
|
||||
};
|
|
@ -1,7 +1,5 @@
|
|||
const ldap = require('../../util/ldapjs-extra');
|
||||
const nthash = require('smbhash').nthash;
|
||||
const ssh = require('node-ssh');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const SyncEngine = require('../../util/sync-engine');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('sync', {
|
||||
|
@ -19,7 +17,7 @@ module.exports = Self => {
|
|||
}
|
||||
],
|
||||
http: {
|
||||
path: `/sync`,
|
||||
path: `/:userName/sync`,
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
@ -35,233 +33,19 @@ module.exports = Self => {
|
|||
|
||||
if (user && isSync) return;
|
||||
|
||||
let accountConfig;
|
||||
let mailConfig;
|
||||
let extraParams;
|
||||
let hasAccount = false;
|
||||
|
||||
if (user) {
|
||||
accountConfig = await $.AccountConfig.findOne({
|
||||
fields: ['homedir', 'shell', 'idBase']
|
||||
});
|
||||
mailConfig = await $.MailConfig.findOne({
|
||||
fields: ['domain']
|
||||
});
|
||||
|
||||
user = await $.Account.findById(user.id, {
|
||||
fields: [
|
||||
'id',
|
||||
'nickname',
|
||||
'email',
|
||||
'lang',
|
||||
'roleFk',
|
||||
'sync',
|
||||
'active',
|
||||
'created',
|
||||
'updated'
|
||||
],
|
||||
where: {name: userName},
|
||||
include: {
|
||||
relation: 'roles',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'inherits',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
extraParams = {
|
||||
corporateMail: `${userName}@${mailConfig.domain}`,
|
||||
uidNumber: accountConfig.idBase + user.id
|
||||
};
|
||||
|
||||
hasAccount = user.active
|
||||
&& await $.UserAccount.exists(user.id);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
let bcryptPassword = $.User.hashPassword(password);
|
||||
|
||||
await $.Account.upsertWithWhere({id: user.id},
|
||||
{bcryptPassword}
|
||||
);
|
||||
await $.user.upsert({
|
||||
id: user.id,
|
||||
username: userName,
|
||||
password: bcryptPassword,
|
||||
email: user.email,
|
||||
created: user.created,
|
||||
updated: user.updated
|
||||
});
|
||||
}
|
||||
|
||||
// SIP
|
||||
|
||||
if (hasAccount) {
|
||||
await Self.rawSql('CALL pbx.sip_setPassword(?, ?)',
|
||||
[user.id, password]
|
||||
);
|
||||
}
|
||||
|
||||
// LDAP
|
||||
|
||||
let ldapConfig = await $.LdapConfig.findOne({
|
||||
fields: ['host', 'rdn', 'password', 'baseDn', 'groupDn']
|
||||
});
|
||||
|
||||
if (ldapConfig) {
|
||||
let ldapClient = ldap.createClient({
|
||||
url: `ldap://${ldapConfig.host}:389`
|
||||
});
|
||||
|
||||
let ldapPassword = Buffer
|
||||
.from(ldapConfig.password, 'base64')
|
||||
.toString('ascii');
|
||||
await ldapClient.bind(ldapConfig.rdn, ldapPassword);
|
||||
|
||||
let err;
|
||||
try {
|
||||
// Deletes user
|
||||
let engine = new SyncEngine();
|
||||
await engine.init($);
|
||||
|
||||
try {
|
||||
let dn = `uid=${userName},${ldapConfig.baseDn}`;
|
||||
await ldapClient.del(dn);
|
||||
} catch (e) {
|
||||
if (e.name !== 'NoSuchObjectError') throw e;
|
||||
}
|
||||
|
||||
// Removes user from groups
|
||||
|
||||
let opts = {
|
||||
scope: 'sub',
|
||||
attributes: ['dn'],
|
||||
filter: `&(memberUid=${userName})(objectClass=posixGroup)`
|
||||
};
|
||||
res = await ldapClient.search(ldapConfig.groupDn, opts);
|
||||
|
||||
let oldGroups = [];
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', reject);
|
||||
res.on('searchEntry', e => oldGroups.push(e.object));
|
||||
res.on('end', resolve);
|
||||
});
|
||||
|
||||
let reqs = [];
|
||||
for (oldGroup of oldGroups) {
|
||||
let change = new ldap.Change({
|
||||
operation: 'delete',
|
||||
modification: {memberUid: userName}
|
||||
});
|
||||
reqs.push(ldapClient.modify(oldGroup.dn, change));
|
||||
}
|
||||
await Promise.all(reqs);
|
||||
|
||||
if (hasAccount) {
|
||||
// Recreates user
|
||||
|
||||
let nameArgs = user.nickname.split(' ');
|
||||
let sshaPassword = crypto
|
||||
.createHash('sha1')
|
||||
.update(password)
|
||||
.digest('base64');
|
||||
|
||||
let dn = `uid=${userName},${ldapConfig.baseDn}`;
|
||||
let newEntry = {
|
||||
uid: userName,
|
||||
objectClass: [
|
||||
'inetOrgPerson',
|
||||
'posixAccount',
|
||||
'sambaSamAccount'
|
||||
],
|
||||
cn: user.nickname || userName,
|
||||
displayName: user.nickname,
|
||||
givenName: nameArgs[0],
|
||||
sn: nameArgs[1] || 'Empty',
|
||||
mail: extraParams.corporateMail,
|
||||
userPassword: `{SSHA}${sshaPassword}`,
|
||||
preferredLanguage: user.lang,
|
||||
homeDirectory: `${accountConfig.homedir}/${userName}`,
|
||||
loginShell: accountConfig.shell,
|
||||
uidNumber: extraParams.uidNumber,
|
||||
gidNumber: accountConfig.idBase + user.roleFk,
|
||||
sambaSID: '-',
|
||||
sambaNTPassword: nthash(password)
|
||||
};
|
||||
await ldapClient.add(dn, newEntry);
|
||||
|
||||
// Adds user to groups
|
||||
|
||||
let reqs = [];
|
||||
for (let role of user.roles()) {
|
||||
let change = new ldap.Change({
|
||||
operation: 'add',
|
||||
modification: {memberUid: userName}
|
||||
});
|
||||
let roleName = role.inherits().name;
|
||||
let dn = `cn=${roleName},${ldapConfig.groupDn}`;
|
||||
reqs.push(ldapClient.modify(dn, change));
|
||||
}
|
||||
await Promise.all(reqs);
|
||||
}
|
||||
await engine.sync(userName, password, true);
|
||||
await $.UserSync.destroyById(userName);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
// FIXME: Cannot disconnect, hangs on undind() call
|
||||
// await ldapClient.unbind();
|
||||
await engine.deinit();
|
||||
if (err) throw err;
|
||||
}
|
||||
|
||||
// Samba
|
||||
|
||||
let sambaConfig = await $.SambaConfig.findOne({
|
||||
fields: ['host', 'sshUser', 'sshPass']
|
||||
});
|
||||
|
||||
if (sambaConfig) {
|
||||
let sshPassword = Buffer
|
||||
.from(sambaConfig.sshPass, 'base64')
|
||||
.toString('ascii');
|
||||
|
||||
let sshClient = new ssh.NodeSSH();
|
||||
await sshClient.connect({
|
||||
host: sambaConfig.host,
|
||||
username: sambaConfig.sshUser,
|
||||
password: sshPassword
|
||||
});
|
||||
|
||||
let commands;
|
||||
|
||||
if (hasAccount) {
|
||||
commands = [
|
||||
`samba-tool user create "${userName}" `
|
||||
+ `--uid-number=${extraParams.uidNumber} `
|
||||
+ `--mail-address="${extraParams.corporateMail}" `
|
||||
+ `--random-password`,
|
||||
`samba-tool user setexpiry "${userName}" `
|
||||
+ `--noexpiry`,
|
||||
`samba-tool user setpassword "${userName}" `
|
||||
+ `--newpassword="${password}"`,
|
||||
`mkhomedir_helper "${userName}" 0027`
|
||||
];
|
||||
} else {
|
||||
commands = [
|
||||
`samba-tool user delete "${userName}"`
|
||||
];
|
||||
}
|
||||
|
||||
for (let command of commands)
|
||||
await sshClient.execCommand(command);
|
||||
|
||||
await sshClient.dispose();
|
||||
}
|
||||
|
||||
// Mark as synchronized
|
||||
|
||||
await $.UserSync.destroyById(userName);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
"LdapConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Mail": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"MailAlias": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
@ -34,8 +37,5 @@
|
|||
},
|
||||
"UserSync": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Mail": {
|
||||
"dataSource": "vn"
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"host": {
|
||||
"server": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
|
@ -23,10 +23,7 @@
|
|||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"baseDn": {
|
||||
"type": "string"
|
||||
},
|
||||
"filter": {
|
||||
"userDn": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupDn": {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
}
|
||||
},
|
||||
"properties": {
|
||||
"role": {
|
||||
"id": {
|
||||
"id": true
|
||||
}
|
||||
},
|
||||
|
|
|
@ -18,7 +18,16 @@
|
|||
"sshUser": {
|
||||
"type": "string"
|
||||
},
|
||||
"sshPass": {
|
||||
"sshPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"adUser": {
|
||||
"type": "string"
|
||||
},
|
||||
"adPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"userDn": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/user-account/sync')(Self);
|
||||
require('../methods/user-account/sync-by-id')(Self);
|
||||
require('../methods/user-account/sync-all')(Self);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Base class for user synchronizators.
|
||||
*
|
||||
* @property {Array<Model>} $
|
||||
* @property {Object} accountConfig
|
||||
* @property {Object} mailConfig
|
||||
*/
|
||||
class SyncConnector {
|
||||
/**
|
||||
* Initalizes the connector.
|
||||
*/
|
||||
async init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users to synchronize.
|
||||
*
|
||||
* @param {Set} usersToSync Set where users are added
|
||||
*/
|
||||
async getUsers(usersToSync) {}
|
||||
|
||||
/**
|
||||
* Synchronizes a user.
|
||||
*
|
||||
* @param {Object} info User information
|
||||
* @param {String} userName The user name
|
||||
* @param {String} password Thepassword
|
||||
*/
|
||||
async sync(info, userName, password) {}
|
||||
|
||||
/**
|
||||
* Synchronizes user groups.
|
||||
*
|
||||
* @param {User} user Instace of user
|
||||
* @param {String} userName The user name
|
||||
*/
|
||||
async syncGroups(user, userName) {}
|
||||
|
||||
/**
|
||||
* Synchronizes roles.
|
||||
*/
|
||||
async syncRoles() {}
|
||||
|
||||
/**
|
||||
* Deinitalizes the connector.
|
||||
*/
|
||||
async deinit() {}
|
||||
}
|
||||
|
||||
SyncConnector.connectors = [];
|
||||
module.exports = SyncConnector;
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
const SyncConnector = require('./sync-connector');
|
||||
|
||||
class SyncDb extends SyncConnector {
|
||||
async sync(info, userName, password) {
|
||||
let {$} = this;
|
||||
let {user} = info;
|
||||
|
||||
if (user && user.active) {
|
||||
let bcryptPassword = password
|
||||
? $.User.hashPassword(password)
|
||||
: user.bcryptPassword;
|
||||
|
||||
await $.Account.upsertWithWhere({id: user.id},
|
||||
{bcryptPassword}
|
||||
);
|
||||
|
||||
let dbUser = {
|
||||
id: user.id,
|
||||
username: userName,
|
||||
email: user.email,
|
||||
created: user.created,
|
||||
updated: user.updated
|
||||
};
|
||||
if (bcryptPassword)
|
||||
dbUser.password = bcryptPassword;
|
||||
|
||||
if (await $.user.exists(user.id))
|
||||
await $.user.replaceById(user.id, dbUser);
|
||||
else
|
||||
await $.user.create(dbUser);
|
||||
} else
|
||||
await $.user.destroyAll({username: userName});
|
||||
}
|
||||
|
||||
async getUsers(usersToSync) {
|
||||
let accounts = await this.$.UserAccount.find({
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
where: {active: true}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (let account of accounts) {
|
||||
let user = account.user();
|
||||
if (!user) continue;
|
||||
usersToSync.add(user.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SyncConnector.connectors.push(SyncDb);
|
||||
module.exports = SyncDb;
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
const SyncConnector = require('./sync-connector');
|
||||
require('./sync-db');
|
||||
require('./sync-sip');
|
||||
require('./sync-ldap');
|
||||
require('./sync-samba');
|
||||
|
||||
module.exports = class SyncEngine {
|
||||
async init($) {
|
||||
let accountConfig = await $.AccountConfig.findOne({
|
||||
fields: ['homedir', 'shell', 'idBase']
|
||||
});
|
||||
let mailConfig = await $.MailConfig.findOne({
|
||||
fields: ['domain']
|
||||
});
|
||||
|
||||
let connectors = [];
|
||||
|
||||
for (let ConnectorClass of SyncConnector.connectors) {
|
||||
let connector = new ConnectorClass();
|
||||
Object.assign(connector, {
|
||||
engine: this,
|
||||
$,
|
||||
accountConfig,
|
||||
mailConfig
|
||||
});
|
||||
if (!await connector.init()) continue;
|
||||
connectors.push(connector);
|
||||
}
|
||||
|
||||
Object.assign(this, {
|
||||
connectors,
|
||||
$,
|
||||
accountConfig,
|
||||
mailConfig
|
||||
});
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
for (let connector of this.connectors)
|
||||
await connector.deinit();
|
||||
}
|
||||
|
||||
async sync(userName, password, syncGroups) {
|
||||
let {
|
||||
$,
|
||||
accountConfig,
|
||||
mailConfig
|
||||
} = this;
|
||||
|
||||
if (!userName) return;
|
||||
userName = userName.toLowerCase();
|
||||
|
||||
// Skip conflicting users
|
||||
if (['administrator', 'root'].indexOf(userName) >= 0)
|
||||
return;
|
||||
|
||||
let user = await $.Account.findOne({
|
||||
where: {name: userName},
|
||||
fields: [
|
||||
'id',
|
||||
'nickname',
|
||||
'email',
|
||||
'lang',
|
||||
'roleFk',
|
||||
'sync',
|
||||
'active',
|
||||
'created',
|
||||
'bcryptPassword',
|
||||
'updated'
|
||||
],
|
||||
include: {
|
||||
relation: 'roles',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'inherits',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let info = {
|
||||
user,
|
||||
hasAccount: false
|
||||
};
|
||||
|
||||
if (user) {
|
||||
let exists = await $.UserAccount.exists(user.id);
|
||||
Object.assign(info, {
|
||||
hasAccount: user.active && exists,
|
||||
corporateMail: `${userName}@${mailConfig.domain}`,
|
||||
uidNumber: accountConfig.idBase + user.id
|
||||
});
|
||||
}
|
||||
|
||||
let errs = [];
|
||||
|
||||
for (let connector of this.connectors) {
|
||||
try {
|
||||
await connector.sync(info, userName, password);
|
||||
if (syncGroups)
|
||||
await connector.syncGroups(info, userName);
|
||||
} catch (err) {
|
||||
errs.push(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (errs.length) throw errs[0];
|
||||
}
|
||||
|
||||
async syncRoles() {
|
||||
for (let connector of this.connectors)
|
||||
await connector.syncRoles();
|
||||
}
|
||||
|
||||
async getUsers() {
|
||||
let usersToSync = new Set();
|
||||
|
||||
for (let connector of this.connectors)
|
||||
await connector.getUsers(usersToSync);
|
||||
|
||||
return usersToSync;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,293 @@
|
|||
|
||||
const SyncConnector = require('./sync-connector');
|
||||
const nthash = require('smbhash').nthash;
|
||||
const ldap = require('./ldapjs-extra');
|
||||
const crypto = require('crypto');
|
||||
|
||||
class SyncLdap extends SyncConnector {
|
||||
async init() {
|
||||
let ldapConfig = await this.$.LdapConfig.findOne({
|
||||
fields: [
|
||||
'server',
|
||||
'rdn',
|
||||
'password',
|
||||
'userDn',
|
||||
'groupDn'
|
||||
]
|
||||
});
|
||||
if (!ldapConfig) return false;
|
||||
|
||||
let client = ldap.createClient({
|
||||
url: ldapConfig.server
|
||||
});
|
||||
await client.bind(ldapConfig.rdn, ldapConfig.password);
|
||||
|
||||
Object.assign(this, {
|
||||
ldapConfig,
|
||||
client
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
if (this.client)
|
||||
await this.client.unbind();
|
||||
}
|
||||
|
||||
async sync(info, userName, password) {
|
||||
let {
|
||||
ldapConfig,
|
||||
client,
|
||||
accountConfig
|
||||
} = this;
|
||||
|
||||
let {user} = info;
|
||||
|
||||
let res = await client.search(ldapConfig.userDn, {
|
||||
scope: 'sub',
|
||||
attributes: ['userPassword', 'sambaNTPassword'],
|
||||
filter: `&(uid=${userName})`
|
||||
});
|
||||
|
||||
let oldUser;
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', reject);
|
||||
res.on('searchEntry', e => oldUser = e.object);
|
||||
res.on('end', resolve);
|
||||
});
|
||||
|
||||
try {
|
||||
let dn = `uid=${userName},${ldapConfig.userDn}`;
|
||||
await client.del(dn);
|
||||
} catch (e) {
|
||||
if (e.name !== 'NoSuchObjectError') throw e;
|
||||
}
|
||||
|
||||
if (!info.hasAccount) {
|
||||
if (oldUser)
|
||||
console.log(` -> '${userName}' removed from LDAP`);
|
||||
return;
|
||||
}
|
||||
|
||||
let nickname = user.nickname || userName;
|
||||
let nameArgs = nickname.trim().split(' ');
|
||||
let sn = nameArgs.length > 1
|
||||
? nameArgs.splice(1).join(' ')
|
||||
: '-';
|
||||
|
||||
let dn = `uid=${userName},${ldapConfig.userDn}`;
|
||||
let newEntry = {
|
||||
uid: userName,
|
||||
objectClass: [
|
||||
'inetOrgPerson',
|
||||
'posixAccount',
|
||||
'sambaSamAccount'
|
||||
],
|
||||
cn: nickname,
|
||||
displayName: nickname,
|
||||
givenName: nameArgs[0],
|
||||
sn,
|
||||
mail: info.corporateMail,
|
||||
preferredLanguage: user.lang,
|
||||
homeDirectory: `${accountConfig.homedir}/${userName}`,
|
||||
loginShell: accountConfig.shell,
|
||||
uidNumber: info.uidNumber,
|
||||
gidNumber: accountConfig.idBase + user.roleFk,
|
||||
sambaSID: '-'
|
||||
};
|
||||
|
||||
if (password) {
|
||||
let salt = crypto
|
||||
.randomBytes(8)
|
||||
.toString('base64');
|
||||
|
||||
let hash = crypto.createHash('sha1');
|
||||
hash.update(password);
|
||||
hash.update(salt, 'binary');
|
||||
let digest = hash.digest('binary');
|
||||
|
||||
let ssha = Buffer
|
||||
.from(digest + salt, 'binary')
|
||||
.toString('base64');
|
||||
|
||||
Object.assign(newEntry, {
|
||||
userPassword: `{SSHA}${ssha}`,
|
||||
sambaNTPassword: nthash(password)
|
||||
});
|
||||
} else if (oldUser) {
|
||||
Object.assign(newEntry, {
|
||||
userPassword: oldUser.userPassword,
|
||||
sambaNTPassword: oldUser.sambaNTPassword
|
||||
});
|
||||
}
|
||||
|
||||
for (let prop in newEntry) {
|
||||
if (newEntry[prop] == null)
|
||||
delete newEntry[prop];
|
||||
}
|
||||
|
||||
await client.add(dn, newEntry);
|
||||
}
|
||||
|
||||
async syncGroups(info, userName) {
|
||||
let {
|
||||
ldapConfig,
|
||||
client
|
||||
} = this;
|
||||
|
||||
let res = await client.search(ldapConfig.groupDn, {
|
||||
scope: 'sub',
|
||||
attributes: ['dn'],
|
||||
filter: `&(memberUid=${userName})(objectClass=posixGroup)`
|
||||
});
|
||||
|
||||
let oldGroups = [];
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', reject);
|
||||
res.on('searchEntry', e => oldGroups.push(e.object));
|
||||
res.on('end', resolve);
|
||||
});
|
||||
|
||||
let reqs = [];
|
||||
for (let oldGroup of oldGroups) {
|
||||
let change = new ldap.Change({
|
||||
operation: 'delete',
|
||||
modification: {memberUid: userName}
|
||||
});
|
||||
reqs.push(client.modify(oldGroup.dn, change));
|
||||
}
|
||||
await Promise.all(reqs);
|
||||
|
||||
if (!info.hasAccount) return;
|
||||
|
||||
reqs = [];
|
||||
for (let role of info.user.roles()) {
|
||||
let change = new ldap.Change({
|
||||
operation: 'add',
|
||||
modification: {memberUid: userName}
|
||||
});
|
||||
let roleName = role.inherits().name;
|
||||
let dn = `cn=${roleName},${ldapConfig.groupDn}`;
|
||||
reqs.push(client.modify(dn, change));
|
||||
}
|
||||
await Promise.all(reqs);
|
||||
}
|
||||
|
||||
async getUsers(usersToSync) {
|
||||
let {
|
||||
ldapConfig,
|
||||
client
|
||||
} = this;
|
||||
|
||||
let res = await client.search(ldapConfig.userDn, {
|
||||
scope: 'sub',
|
||||
attributes: ['uid'],
|
||||
filter: `uid=*`
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', reject);
|
||||
res.on('searchEntry', e => usersToSync.add(e.object.uid));
|
||||
res.on('end', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
async syncRoles() {
|
||||
let {
|
||||
$,
|
||||
ldapConfig,
|
||||
client,
|
||||
accountConfig
|
||||
} = this;
|
||||
|
||||
// Delete roles
|
||||
|
||||
let opts = {
|
||||
scope: 'sub',
|
||||
attributes: ['dn'],
|
||||
filter: 'objectClass=posixGroup'
|
||||
};
|
||||
let res = await client.search(ldapConfig.groupDn, opts);
|
||||
|
||||
let reqs = [];
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', err => {
|
||||
if (err.name === 'NoSuchObjectError')
|
||||
err = new Error(`Object '${ldapConfig.groupDn}' does not exist`);
|
||||
reject(err);
|
||||
});
|
||||
res.on('searchEntry', e => {
|
||||
reqs.push(client.del(e.object.dn));
|
||||
});
|
||||
res.on('end', resolve);
|
||||
});
|
||||
await Promise.all(reqs);
|
||||
|
||||
// Recreate roles
|
||||
|
||||
let roles = await $.Role.find({
|
||||
fields: ['id', 'name']
|
||||
});
|
||||
let roleRoles = await $.RoleRole.find({
|
||||
fields: ['role', 'inheritsFrom']
|
||||
});
|
||||
let roleMap = toMap(roleRoles, e => {
|
||||
return {key: e.inheritsFrom, val: e.role};
|
||||
});
|
||||
|
||||
let accounts = await $.UserAccount.find({
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['name', 'roleFk'],
|
||||
where: {active: true}
|
||||
}
|
||||
}
|
||||
});
|
||||
let accountMap = toMap(accounts, e => {
|
||||
let user = e.user();
|
||||
if (!user) return;
|
||||
return {key: user.roleFk, val: user.name};
|
||||
});
|
||||
|
||||
console.log;
|
||||
|
||||
reqs = [];
|
||||
for (let role of roles) {
|
||||
let newEntry = {
|
||||
objectClass: ['top', 'posixGroup'],
|
||||
cn: role.name,
|
||||
gidNumber: accountConfig.idBase + role.id
|
||||
};
|
||||
|
||||
let memberUid = [];
|
||||
for (let subrole of roleMap.get(role.id) || [])
|
||||
memberUid = memberUid.concat(accountMap.get(subrole) || []);
|
||||
|
||||
if (memberUid.length) {
|
||||
memberUid.sort((a, b) => a.localeCompare(b));
|
||||
newEntry.memberUid = memberUid;
|
||||
}
|
||||
|
||||
let dn = `cn=${role.name},${ldapConfig.groupDn}`;
|
||||
reqs.push(client.add(dn, newEntry));
|
||||
}
|
||||
await Promise.all(reqs);
|
||||
}
|
||||
}
|
||||
|
||||
SyncConnector.connectors.push(SyncLdap);
|
||||
module.exports = SyncLdap;
|
||||
|
||||
function toMap(array, fn) {
|
||||
let map = new Map();
|
||||
for (let item of array) {
|
||||
let keyVal = fn(item);
|
||||
if (!keyVal) continue;
|
||||
let key = keyVal.key;
|
||||
if (!map.has(key)) map.set(key, []);
|
||||
map.get(key).push(keyVal.val);
|
||||
}
|
||||
return map;
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
|
||||
const SyncConnector = require('./sync-connector');
|
||||
const ssh = require('node-ssh');
|
||||
const ldap = require('./ldapjs-extra');
|
||||
|
||||
class SyncSamba extends SyncConnector {
|
||||
async init() {
|
||||
let sambaConfig = await this.$.SambaConfig.findOne({
|
||||
fields: [
|
||||
'host',
|
||||
'sshUser',
|
||||
'sshPassword',
|
||||
'adUser',
|
||||
'adPassword',
|
||||
'userDn'
|
||||
]
|
||||
});
|
||||
if (!sambaConfig) return false;
|
||||
|
||||
let client = new ssh.NodeSSH();
|
||||
await client.connect({
|
||||
host: sambaConfig.host,
|
||||
username: sambaConfig.sshUser,
|
||||
password: sambaConfig.sshPassword
|
||||
});
|
||||
|
||||
let adClient = ldap.createClient({
|
||||
url: `ldaps://${sambaConfig.host}:636`,
|
||||
tlsOptions: {rejectUnauthorized: false}
|
||||
});
|
||||
|
||||
Object.assign(this, {
|
||||
sambaConfig,
|
||||
client,
|
||||
adClient
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
if (!this.client) return;
|
||||
await this.client.dispose();
|
||||
await this.adClient.unbind();
|
||||
}
|
||||
|
||||
async sync(info, userName, password) {
|
||||
let {client} = this;
|
||||
|
||||
if (info.hasAccount) {
|
||||
try {
|
||||
await client.exec('samba-tool user create', [
|
||||
userName,
|
||||
'--uid-number', `${info.uidNumber}`,
|
||||
'--mail-address', info.corporateMail,
|
||||
'--random-password'
|
||||
]);
|
||||
await client.exec('samba-tool user setexpiry', [
|
||||
userName,
|
||||
'--noexpiry'
|
||||
]);
|
||||
await client.exec('mkhomedir_helper', [
|
||||
userName,
|
||||
'0027'
|
||||
]);
|
||||
} catch (e) {}
|
||||
|
||||
await client.exec('samba-tool user enable', [
|
||||
userName
|
||||
]);
|
||||
|
||||
if (password) {
|
||||
await client.exec('samba-tool user setpassword', [
|
||||
userName,
|
||||
'--newpassword', password
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await client.exec('samba-tool user disable', [
|
||||
userName
|
||||
]);
|
||||
console.log(` -> '${userName}' disabled on Samba`);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets enabled users from Samba.
|
||||
*
|
||||
* Summary of userAccountControl flags:
|
||||
* https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
|
||||
*
|
||||
* @param {Set} usersToSync
|
||||
*/
|
||||
async getUsers(usersToSync) {
|
||||
let {
|
||||
sambaConfig,
|
||||
adClient
|
||||
} = this;
|
||||
|
||||
await adClient.bind(sambaConfig.adUser, sambaConfig.adPassword);
|
||||
|
||||
let opts = {
|
||||
scope: 'sub',
|
||||
attributes: ['sAMAccountName'],
|
||||
filter: '(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'
|
||||
};
|
||||
let res = await adClient.search(sambaConfig.userDn, opts);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
res.on('error', err => {
|
||||
if (err.name === 'NoSuchObjectError')
|
||||
err = new Error(`Object '${sambaConfig.userDn}' does not exist`);
|
||||
reject(err);
|
||||
});
|
||||
res.on('searchEntry', e => {
|
||||
usersToSync.add(e.object.sAMAccountName);
|
||||
});
|
||||
res.on('end', resolve);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SyncConnector.connectors.push(SyncSamba);
|
||||
module.exports = SyncSamba;
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
const SyncConnector = require('./sync-connector');
|
||||
|
||||
class SyncSip extends SyncConnector {
|
||||
async sync(info, userName, password) {
|
||||
if (!info.hasAccount || !password) return;
|
||||
|
||||
await this.$.Account.rawSql('CALL pbx.sip_setPassword(?, ?)',
|
||||
[info.user.id, password]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SyncConnector.connectors.push(SyncSip);
|
||||
module.exports = SyncSip;
|
|
@ -15,3 +15,6 @@ import './basic-data';
|
|||
import './mail-forwarding';
|
||||
import './aliases';
|
||||
import './roles';
|
||||
import './ldap';
|
||||
import './samba';
|
||||
import './posix';
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="LdapConfigs"
|
||||
data="$ctrl.config"
|
||||
id-value="1"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="watcher.submit()"
|
||||
class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-vertical>
|
||||
<vn-check
|
||||
label="Enable synchronization"
|
||||
ng-model="watcher.hasData">
|
||||
</vn-check>
|
||||
</vn-vertical>
|
||||
<vn-vertical
|
||||
ng-if="watcher.hasData"
|
||||
class="vn-mt-md">
|
||||
<vn-textfield
|
||||
label="Server"
|
||||
ng-model="$ctrl.config.server"
|
||||
rule="LdapConfig"
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="RDN"
|
||||
ng-model="$ctrl.config.rdn"
|
||||
rule="LdapConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="Password"
|
||||
ng-model="$ctrl.config.password"
|
||||
type="password"
|
||||
rule="LdapConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="User DN"
|
||||
ng-model="$ctrl.config.userDn"
|
||||
rule="LdapConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="Group DN"
|
||||
ng-model="$ctrl.config.groupDn"
|
||||
rule="LdapConfig">
|
||||
</vn-textfield>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-button
|
||||
label="Undo changes"
|
||||
ng-if="watcher.dataChanged()"
|
||||
ng-click="watcher.loadOriginalData()">
|
||||
</vn-button>
|
||||
<vn-button
|
||||
label="Synchronize now"
|
||||
ng-if="watcher.hasData"
|
||||
ng-click="$ctrl.onSynchronizeAll()">
|
||||
</vn-button>
|
||||
<vn-button
|
||||
label="Synchronize user"
|
||||
ng-if="watcher.hasData"
|
||||
ng-click="syncUser.show()">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
<vn-submit
|
||||
icon="save"
|
||||
vn-tooltip="Save"
|
||||
class="round"
|
||||
fixed-bottom-right>
|
||||
</vn-submit>
|
||||
</form>
|
||||
<vn-dialog
|
||||
vn-id="syncUser"
|
||||
on-accept="$ctrl.onUserSync()"
|
||||
on-close="$ctrl.onPassClose()">
|
||||
<tpl-body>
|
||||
<vn-textfield
|
||||
label="Username"
|
||||
ng-model="$ctrl.syncUser">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="Password"
|
||||
ng-model="$ctrl.syncPassword"
|
||||
type="password"
|
||||
info="If password is not specified, just user attributes are synchronized">
|
||||
</vn-textfield>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||
<button response="accept" translate>Synchronize</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
|
@ -0,0 +1,30 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import UserError from 'core/lib/user-error';
|
||||
|
||||
export default class Controller extends Section {
|
||||
onSynchronizeAll() {
|
||||
this.vnApp.showSuccess(this.$t('Synchronizing in the background'));
|
||||
this.$http.patch(`UserAccounts/syncAll`)
|
||||
.then(() => this.vnApp.showSuccess(this.$t('Users synchronized!')));
|
||||
}
|
||||
|
||||
onUserSync() {
|
||||
if (!this.syncUser)
|
||||
throw new UserError('Please enter the username');
|
||||
|
||||
let params = {password: this.syncPassword};
|
||||
return this.$http.patch(`UserAccounts/${this.syncUser}/sync`, params)
|
||||
.then(() => this.vnApp.showSuccess(this.$t('User synchronized!')));
|
||||
}
|
||||
|
||||
onSyncClose() {
|
||||
this.syncUser = '';
|
||||
this.syncPassword = '';
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnAccountLdap', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
Enable synchronization: Habilitar sincronización
|
||||
Server: Servidor
|
||||
RDN: RDN
|
||||
User DN: DN usuarios
|
||||
Filter: Filtro
|
||||
Group DN: DN grupos
|
||||
Synchronize now: Sincronizar ahora
|
||||
Synchronize user: Sincronizar usuario
|
||||
If password is not specified, just user attributes are synchronized: >-
|
||||
Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
|
||||
Synchronizing in the background: Sincronizando en segundo plano
|
||||
Users synchronized!: ¡Usuarios sincronizados!
|
||||
Username: Nombre de usuario
|
||||
Synchronize: Sincronizar
|
||||
Please enter the username: Por favor introduce el nombre de usuario
|
||||
User synchronized!: ¡Usuario sincronizado!
|
|
@ -0,0 +1,69 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="AccountConfigs"
|
||||
data="$ctrl.config"
|
||||
id-value="1"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="watcher.submit()"
|
||||
class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-vertical>
|
||||
<vn-textfield
|
||||
label="Homedir"
|
||||
ng-model="$ctrl.config.homedir"
|
||||
rule="AccountConfig"
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="Shell"
|
||||
ng-model="$ctrl.config.shell"
|
||||
rule="AccountConfig">
|
||||
</vn-textfield>
|
||||
<vn-input-number
|
||||
label="Id base"
|
||||
ng-model="$ctrl.config.idBase"
|
||||
rule="AccountConfig">
|
||||
</vn-input-number>
|
||||
<vn-horizontal>
|
||||
<vn-input-number
|
||||
label="Min"
|
||||
ng-model="$ctrl.config.min"
|
||||
rule="AccountConfig">
|
||||
</vn-input-number>
|
||||
<vn-input-number
|
||||
label="Max"
|
||||
ng-model="$ctrl.config.max"
|
||||
rule="AccountConfig">
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-number
|
||||
label="Warn"
|
||||
ng-model="$ctrl.config.warn"
|
||||
rule="AccountConfig">
|
||||
</vn-input-number>
|
||||
<vn-input-number
|
||||
label="Inact"
|
||||
ng-model="$ctrl.config.inact"
|
||||
rule="AccountConfig">
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-button
|
||||
label="Undo changes"
|
||||
ng-if="watcher.dataChanged()"
|
||||
ng-click="watcher.loadOriginalData()">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
<vn-submit
|
||||
icon="save"
|
||||
vn-tooltip="Save"
|
||||
class="round"
|
||||
fixed-bottom-right>
|
||||
</vn-submit>
|
||||
</form>
|
|
@ -0,0 +1,9 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {}
|
||||
|
||||
ngModule.component('vnAccountPosix', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
Host: Host
|
||||
RDN: RDN
|
||||
Base DN: DN base
|
||||
Password should be base64 encoded: La contraseña debe estar codificada en base64
|
||||
Filter: Filtro
|
||||
Group DN: DN grupos
|
||||
Synchronize now: Sincronizar ahora
|
||||
Synchronize user: Sincronizar usuario
|
||||
If password is not specified, just user attributes are synchronized: >-
|
||||
Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
|
||||
Synchronizing in the background: Sincronizando en segundo plano
|
||||
LDAP users synchronized: Usuarios LDAP sincronizados
|
||||
Username: Nombre de usuario
|
||||
Synchronize: Sincronizar
|
||||
Please enter the username: Por favor introduce el nombre de usuario
|
||||
User synchronized: Usuario sincronizado
|
|
@ -9,6 +9,9 @@
|
|||
{"state": "account.index", "icon": "face"},
|
||||
{"state": "account.role", "icon": "group"},
|
||||
{"state": "account.alias", "icon": "email"},
|
||||
{"state": "account.posix", "icon": "accessibility"},
|
||||
{"state": "account.ldap", "icon": "account_tree"},
|
||||
{"state": "account.samba", "icon": "desktop_windows"},
|
||||
{"state": "account.acl", "icon": "check"},
|
||||
{"state": "account.connections", "icon": "share"}
|
||||
],
|
||||
|
@ -174,6 +177,24 @@
|
|||
"state": "account.alias.card.users",
|
||||
"component": "vn-alias-users",
|
||||
"description": "Users"
|
||||
}, {
|
||||
"url": "/posix",
|
||||
"state": "account.posix",
|
||||
"component": "vn-account-posix",
|
||||
"description": "Posix",
|
||||
"acl": ["developer"]
|
||||
}, {
|
||||
"url": "/ldap",
|
||||
"state": "account.ldap",
|
||||
"component": "vn-account-ldap",
|
||||
"description": "LDAP",
|
||||
"acl": ["developer"]
|
||||
}, {
|
||||
"url": "/samba",
|
||||
"state": "account.samba",
|
||||
"component": "vn-account-samba",
|
||||
"description": "Samba",
|
||||
"acl": ["developer"]
|
||||
}, {
|
||||
"url": "/acl?q",
|
||||
"state": "account.acl",
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="SambaConfigs"
|
||||
data="$ctrl.config"
|
||||
id-value="1"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="watcher.submit()"
|
||||
class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-vertical>
|
||||
<vn-check
|
||||
label="Enable synchronization"
|
||||
ng-model="watcher.hasData">
|
||||
</vn-check>
|
||||
</vn-vertical>
|
||||
<vn-vertical
|
||||
ng-if="watcher.hasData"
|
||||
class="vn-mt-md">
|
||||
<vn-textfield
|
||||
label="Host"
|
||||
ng-model="$ctrl.config.host"
|
||||
rule="SambaConfig"
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="SSH user"
|
||||
ng-model="$ctrl.config.sshUser"
|
||||
rule="SambaConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="SSH password"
|
||||
ng-model="$ctrl.config.sshPassword"
|
||||
type="password"
|
||||
rule="SambaConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="AD user"
|
||||
ng-model="$ctrl.config.adUser"
|
||||
rule="SambaConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="AD password"
|
||||
ng-model="$ctrl.config.adPassword"
|
||||
type="password"
|
||||
rule="SambaConfig">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="User DN"
|
||||
ng-model="$ctrl.config.userDn"
|
||||
rule="SambaConfig">
|
||||
</vn-textfield>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-button
|
||||
label="Undo changes"
|
||||
ng-if="watcher.dataChanged()"
|
||||
ng-click="watcher.loadOriginalData()">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
<vn-submit
|
||||
icon="save"
|
||||
vn-tooltip="Save"
|
||||
class="round"
|
||||
fixed-bottom-right>
|
||||
</vn-submit>
|
||||
</form>
|
|
@ -0,0 +1,9 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {}
|
||||
|
||||
ngModule.component('vnAccountSamba', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
Enable synchronization: Habilitar sincronización
|
||||
Host: Host
|
||||
SSH user: Usuario SSH
|
||||
SSH password: Contraseña SSH
|
||||
AD user: Usuario AD
|
||||
AD password: Contraseña AD
|
||||
User DN: DN usuarios
|
|
@ -1,12 +1,12 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getSummary', {
|
||||
description: 'Updates the item taxes',
|
||||
description: 'Return the claim summary',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The item id',
|
||||
description: 'The claim id',
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
returns: {
|
||||
|
|
|
@ -56,7 +56,7 @@ describe('Update Claim', () => {
|
|||
hasToPickUp: false
|
||||
}
|
||||
};
|
||||
await app.models.Claim.updateClaim(ctx, newClaim.id,);
|
||||
await app.models.Claim.updateClaim(ctx, newClaim.id);
|
||||
|
||||
let updatedClaim = await app.models.Claim.findById(newClaim.id);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue