diff --git a/config/config.php b/config/config.php index 59207cabe9..a696535ee5 100644 --- a/config/config.php +++ b/config/config.php @@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}} if(!function_exists('getWebRoot')){function getWebRoot(){}} /* 基本设置。Basic settings. */ -$config->version = '21.7.8'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. +$config->version = '22.0'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. $config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite. $config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP. $config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time. diff --git a/config/filter.php b/config/filter.php index 608a43b9eb..b7ccc5e855 100755 --- a/config/filter.php +++ b/config/filter.php @@ -142,6 +142,7 @@ $filter->project->export = new stdclass(); $filter->project->task = new stdclass(); $filter->project->execution = new stdclass(); $filter->project->testcase = new stdclass(); +$filter->project->deliverable = new stdclass(); $filter->projectstory->story = new stdclass(); $filter->projectstory->export = new stdclass(); $filter->qa->default = new stdclass(); @@ -319,31 +320,32 @@ $filter->program->browse->cookie['showClosed'] = 'code'; $filter->program->export->cookie['checkedItem'] = 'reg::checked'; $filter->program->ajaxgetdropmenu->cookie['showClosed'] = 'code'; -$filter->project->default->cookie['lastProject'] = 'int'; -$filter->project->default->cookie['lastPRJ'] = 'int'; -$filter->project->default->cookie['projectMode'] = 'code'; -$filter->project->default->cookie['kanbanview'] = 'code'; -$filter->project->browse->cookie['involved'] = 'code'; -$filter->project->browse->cookie['showProjectBatchEdit'] = 'int'; -$filter->project->export->cookie['involved'] = 'code'; -$filter->project->browse->cookie['projectType'] = 'code'; -$filter->project->story->cookie['storyModuleParam'] = 'int'; -$filter->project->story->cookie['storyPreProjectID'] = 'int'; -$filter->project->story->cookie['storyProductParam'] = 'int'; -$filter->project->story->cookie['storyBranchParam'] = 'reg::checked'; -$filter->project->story->cookie['projectStoryOrder'] = 'reg::orderBy'; -$filter->project->task->cookie['moduleBrowseParam'] = 'int'; -$filter->project->task->cookie['preProjectID'] = 'int'; -$filter->project->task->cookie['productBrowseParam'] = 'int'; -$filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy'; -$filter->project->task->cookie['windowWidth'] = 'int'; -$filter->project->export->cookie['checkedItem'] = 'reg::checked'; -$filter->project->execution->cookie['pagerExecutionAll'] = 'int'; -$filter->project->execution->cookie['showTask'] = 'code'; -$filter->project->execution->cookie['showStage'] = 'code'; -$filter->project->testcase->cookie['showAutoCase'] = 'int'; -$filter->project->testcase->cookie['onlyAutoCase'] = 'int'; -$filter->project->testcase->cookie['onlyScene'] = 'code'; +$filter->project->default->cookie['lastProject'] = 'int'; +$filter->project->default->cookie['lastPRJ'] = 'int'; +$filter->project->default->cookie['projectMode'] = 'code'; +$filter->project->default->cookie['kanbanview'] = 'code'; +$filter->project->browse->cookie['involved'] = 'code'; +$filter->project->browse->cookie['showProjectBatchEdit'] = 'int'; +$filter->project->export->cookie['involved'] = 'code'; +$filter->project->browse->cookie['projectType'] = 'code'; +$filter->project->story->cookie['storyModuleParam'] = 'int'; +$filter->project->story->cookie['storyPreProjectID'] = 'int'; +$filter->project->story->cookie['storyProductParam'] = 'int'; +$filter->project->story->cookie['storyBranchParam'] = 'reg::checked'; +$filter->project->story->cookie['projectStoryOrder'] = 'reg::orderBy'; +$filter->project->task->cookie['moduleBrowseParam'] = 'int'; +$filter->project->task->cookie['preProjectID'] = 'int'; +$filter->project->task->cookie['productBrowseParam'] = 'int'; +$filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy'; +$filter->project->task->cookie['windowWidth'] = 'int'; +$filter->project->export->cookie['checkedItem'] = 'reg::checked'; +$filter->project->execution->cookie['pagerExecutionAll'] = 'int'; +$filter->project->execution->cookie['showTask'] = 'code'; +$filter->project->execution->cookie['showStage'] = 'code'; +$filter->project->testcase->cookie['showAutoCase'] = 'int'; +$filter->project->testcase->cookie['onlyAutoCase'] = 'int'; +$filter->project->testcase->cookie['onlyScene'] = 'code'; +$filter->project->deliverable->cookie['deliverableModule'] = 'int'; $filter->projectstory->story->cookie['storyModuleParam'] = 'int'; $filter->projectstory->story->cookie['pagerProductBrowse'] = 'int'; @@ -560,4 +562,4 @@ $filter->convert->mapjira2zentao->cookie['jiraRelation'] = 'reg::any'; $filter->admin->register->cookie['zentaosid'] = 'reg::any'; $filter->admin->getcaptcha->cookie['zentaosid'] = 'reg::any'; $filter->admin->sendcode->cookie['zentaosid'] = 'reg::any'; -$filter->admin->giftpackage->cookie['zentaosid'] = 'reg::any'; \ No newline at end of file +$filter->admin->giftpackage->cookie['zentaosid'] = 'reg::any'; diff --git a/config/zentaopms.php b/config/zentaopms.php index c93310f29a..58901d7879 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -366,6 +366,7 @@ $config->logonMethods[] = 'zanode.nodelist'; $config->logonMethods[] = 'screen.viewold'; $config->logonMethods[] = 'system.backupview'; $config->logonMethods[] = 'screen.staticdataold'; +$config->logonMethods[] = 'stage.updateorder'; $config->openModules = array(); $config->openModules[] = 'install'; @@ -415,27 +416,30 @@ define('TABLE_PLANSTORY', '`' . $config->db->prefix . 'planstory`'); define('TABLE_RELEASE', '`' . $config->db->prefix . 'release`'); define('TABLE_RELEASERELATED', '`' . $config->db->prefix . 'releaserelated`'); -define('TABLE_PROGRAM', '`' . $config->db->prefix . 'project`'); -define('TABLE_PROJECT', '`' . $config->db->prefix . 'project`'); -define('TABLE_EXECUTION', '`' . $config->db->prefix . 'project`'); -define('TABLE_TASK', '`' . $config->db->prefix . 'task`'); -define('TABLE_TASKSPEC', '`' . $config->db->prefix . 'taskspec`'); -define('TABLE_TASKTEAM', '`' . $config->db->prefix . 'taskteam`'); -define('TABLE_TEAM', '`' . $config->db->prefix . 'team`'); -define('TABLE_PROJECTADMIN', '`' . $config->db->prefix . 'projectadmin`'); -define('TABLE_PROJECTPRODUCT','`' . $config->db->prefix . 'projectproduct`'); -define('TABLE_PROJECTSTORY', '`' . $config->db->prefix . 'projectstory`'); -define('TABLE_PROJECTCASE', '`' . $config->db->prefix . 'projectcase`'); -define('TABLE_TASKESTIMATE', '`' . $config->db->prefix . 'taskestimate`'); -define('TABLE_EFFORT', '`' . $config->db->prefix . 'effort`'); -define('TABLE_BURN', '`' . $config->db->prefix . 'burn`'); -define('TABLE_CFD', '`' . $config->db->prefix . 'cfd`'); -define('TABLE_BUILD', '`' . $config->db->prefix . 'build`'); -define('TABLE_ACL', '`' . $config->db->prefix . 'acl`'); +define('TABLE_PROGRAM', '`' . $config->db->prefix . 'project`'); +define('TABLE_PROJECT', '`' . $config->db->prefix . 'project`'); +define('TABLE_EXECUTION', '`' . $config->db->prefix . 'project`'); +define('TABLE_TASK', '`' . $config->db->prefix . 'task`'); +define('TABLE_TASKSPEC', '`' . $config->db->prefix . 'taskspec`'); +define('TABLE_TASKTEAM', '`' . $config->db->prefix . 'taskteam`'); +define('TABLE_TEAM', '`' . $config->db->prefix . 'team`'); +define('TABLE_PROJECTADMIN', '`' . $config->db->prefix . 'projectadmin`'); +define('TABLE_PROJECTPRODUCT', '`' . $config->db->prefix . 'projectproduct`'); +define('TABLE_PROJECTDELIVERABLE','`' . $config->db->prefix . 'projectdeliverable`'); +define('TABLE_PROJECTSTORY', '`' . $config->db->prefix . 'projectstory`'); +define('TABLE_PROJECTCASE', '`' . $config->db->prefix . 'projectcase`'); +define('TABLE_PROJECTCHANGE', '`' . $config->db->prefix . 'projectchange`'); +define('TABLE_TASKESTIMATE', '`' . $config->db->prefix . 'taskestimate`'); +define('TABLE_EFFORT', '`' . $config->db->prefix . 'effort`'); +define('TABLE_BURN', '`' . $config->db->prefix . 'burn`'); +define('TABLE_CFD', '`' . $config->db->prefix . 'cfd`'); +define('TABLE_BUILD', '`' . $config->db->prefix . 'build`'); +define('TABLE_ACL', '`' . $config->db->prefix . 'acl`'); define('TABLE_DESIGN', '`' . $config->db->prefix . 'design`'); define('TABLE_DESIGNSPEC', '`' . $config->db->prefix . 'designspec`'); define('TABLE_DELIVERABLE', '`' . $config->db->prefix . 'deliverable`'); +define('TABLE_DELIVERABLESTAGE','`' . $config->db->prefix . 'deliverablestage`'); define('TABLE_DOCLIB', '`' . $config->db->prefix . 'doclib`'); define('TABLE_DOC', '`' . $config->db->prefix . 'doc`'); define('TABLE_DOCBLOCK', '`' . $config->db->prefix . 'docblock`'); @@ -443,6 +447,7 @@ define('TABLE_DEMANDPOOL', '`' . $config->db->prefix . 'demandpool`'); define('TABLE_DEMAND', '`' . $config->db->prefix . 'demand`'); define('TABLE_DEMANDSPEC', '`' . $config->db->prefix . 'demandspec`'); define('TABLE_DEMANDREVIEW', '`' . $config->db->prefix . 'demandreview`'); +define('TABLE_DECISION', '`' . $config->db->prefix . 'decision`'); define('TABLE_API', '`' . $config->db->prefix . 'api`'); define('TABLE_API_SPEC', '`' . $config->db->prefix . 'apispec`'); define('TABLE_APISTRUCT', '`' . $config->db->prefix . 'apistruct`'); @@ -741,6 +746,9 @@ $config->objectTables['productline'] = TABLE_MODULE; $config->objectTables['repocommit'] = TABLE_REPOHISTORY; $config->objectTables['system'] = TABLE_SYSTEM; $config->objectTables['mark'] = TABLE_MARK; +$config->objectTables['cm'] = TABLE_OBJECT; +$config->objectTables['baseline'] = TABLE_OBJECT; +$config->objectTables['projectchange'] = TABLE_PROJECTCHANGE; $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions', 'aiPrompts', 'promptDesign', 'promptExec'); $config->disabledFeatures = ''; @@ -771,18 +779,15 @@ $config->dtable = new stdclass(); $config->dtable->colVars = array('width', 'minWidth', 'type', 'flex', 'fixed', 'sortType', 'checkbox', 'nestedToggle', 'statusMap', 'actionsMap', 'group'); $config->featureGroup = new stdclass(); -$config->featureGroup->my = array('score'); -$config->featureGroup->product = array('roadmap', 'track', 'UR', 'ER'); -$config->featureGroup->scrum = array(); -$config->featureGroup->waterfall = array(); -$config->featureGroup->agileplus = array(); -$config->featureGroup->waterfallplus = array(); -$config->featureGroup->assetlib = array(); -$config->featureGroup->other = array('devops', 'kanban', 'setCode'); +$config->featureGroup->my = array('score'); +$config->featureGroup->product = array('roadmap', 'track', 'UR', 'ER'); +$config->featureGroup->project = array(); +$config->featureGroup->assetlib = array(); +$config->featureGroup->other = array('devops', 'kanban', 'setCode'); $config->db->sqliteBlacklist = array('sqlite_queue', 'cron'); $config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin', 'bi', 'feedback', 'demandpool'); -$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree'); +$config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree', 'workflowgroup', 'deliverable', 'process', 'activity', 'review', 'reviewcl', 'stage', 'auditcl'); $config->excludeBranchMenu = array('product-dashboard', 'product-view', 'product-whitelist', 'product-addwhitelist', 'branch-manage', 'branch-batchedit'); $config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-template', 'project-createtemplate', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-create', 'dataview-query', 'ticket-batchedit', 'ticket-batchfinish', 'ticket-batchactivate', 'file-download'); $config->excludeDropmenuModules = array('reporttemplate'); diff --git a/db/update21.7.6.sql b/db/update21.7.6.sql index 16e32b5bc0..994be0b02d 100644 --- a/db/update21.7.6.sql +++ b/db/update21.7.6.sql @@ -1,7 +1,11 @@ +UPDATE `zt_risk` SET `pri` = 3 WHERE `pri` = 'middle'; +UPDATE `zt_risk` SET `pri` = 1 WHERE `pri` = 'high'; +UPDATE `zt_risk` SET `pri` = 2 WHERE `pri` = 'low'; + ALTER TABLE `zt_demand` CHANGE `version` `version` smallint unsigned NOT NULL DEFAULT '1'; ALTER TABLE `zt_demand` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3; ALTER TABLE `zt_issue` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3; -ALTER TABLE `zt_risk` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3; +ALTER TABLE `zt_risk` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 2; ALTER TABLE `zt_opportunity` CHANGE `pri` `pri` tinyint unsigned NOT NULL DEFAULT 3; ALTER TABLE `zt_design` CHANGE `project` `project` int unsigned NOT NULL DEFAULT 0; ALTER TABLE `zt_design` CHANGE `product` `product` int unsigned NOT NULL DEFAULT 0; diff --git a/db/update21.7.7.sql b/db/update21.7.7.sql index 7b9e5c0ad8..93e11300b4 100644 --- a/db/update21.7.7.sql +++ b/db/update21.7.7.sql @@ -6,6 +6,8 @@ UPDATE `zt_opportunity` SET `pri` = 1 WHERE `pri` = 'high'; UPDATE `zt_opportunity` SET `pri` = 2 WHERE `pri` = 'middle'; UPDATE `zt_opportunity` SET `pri` = 3 WHERE `pri` = 'low'; +UPDATE `zt_workflowlayout` SET `width` = '0' WHERE `width` = 'auto'; + CREATE TABLE IF NOT EXISTS `zt_ai_knowledgelib` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `vision` varchar(10) NOT NULL DEFAULT 'rnd' COMMENT '所属界面', diff --git a/db/update21.7.8.sql b/db/update21.7.8.sql new file mode 100644 index 0000000000..76cd72ea82 --- /dev/null +++ b/db/update21.7.8.sql @@ -0,0 +1,265 @@ +ALTER TABLE `zt_workflowgroup` CHANGE `projectModel` `projectModel` varchar(30) NOT NULL DEFAULT ''; +INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES +('project', 'agileplus', 'product', '融合敏捷式产品研发', 'agileplusproduct', 'normal', 'rnd', '1'), +('project', 'agileplus', 'project', '融合敏捷式项目研发', 'agileplusproject', 'normal', 'rnd', '1'), +('project', 'waterfallplus', 'product', '融合瀑布式产品研发', 'waterfallplusproduct', 'normal', 'rnd', '1'), +('project', 'waterfallplus', 'project', '融合瀑布式项目研发', 'waterfallplusproject', 'normal', 'rnd', '1'), +('project', 'kanban', 'product', '看板式产品研发', 'kanbanproduct', 'normal', 'rnd', '1'), +('project', 'kanban', 'project', '看板式项目研发', 'kanbanproject', 'normal', 'rnd', '1'), +('project', 'ipd', 'ipd', 'IPD集成产品研发', 'ipdproduct', 'normal', 'rnd', '1'), +('project', 'ipd', 'tpd', 'IPD预研产品研发', 'tpdproduct', 'normal', 'rnd', '1'), +('project', 'ipd', 'cbb', 'IPD平台产品研发', 'cbbproduct', 'normal', 'rnd', '1'), +('project', 'ipd', 'cpdproduct', 'IPD定制产品研发', 'cpdproduct', 'normal', 'rnd', '1'), +('project', 'ipd', 'cpdproject', 'IPD定制项目研发', 'cpdproject', 'normal', 'rnd', '1'); + +ALTER TABLE `zt_doc` ADD `isDeliverable` tinyint unsigned NOT NULL DEFAULT 0 AFTER `acl`; +ALTER TABLE `zt_deliverable` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`; +ALTER TABLE `zt_deliverable` ADD `activity` int unsigned NOT NULL DEFAULT '0' AFTER `name`; +ALTER TABLE `zt_deliverable` ADD `trimmable` char(30) NOT NULL DEFAULT '0' AFTER `activity`; +ALTER TABLE `zt_deliverable` ADD `trimRule` varchar(255) NOT NULL AFTER `trimmable`; +ALTER TABLE `zt_deliverable` ADD `template` text NOT NULL AFTER `trimRule`; +ALTER TABLE `zt_deliverable` ADD `status` varchar(30) NOT NULL DEFAULT 'enabled' AFTER `name`; +ALTER TABLE `zt_deliverable` ADD `category` varchar(255) NOT NULL DEFAULT '' AFTER `lastEditedDate`; +ALTER TABLE `zt_deliverable` ADD `builtin` tinyint unsigned NOT NULL DEFAULT 0 AFTER `module`; +ALTER TABLE `zt_deliverable` ADD `systemList` tinyint unsigned NOT NULL DEFAULT 0 AFTER `builtin`; + +CREATE TABLE IF NOT EXISTS `zt_deliverablestage` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `deliverable` int unsigned NOT NULL DEFAULT 0, + `stage` varchar(30) NOT NULL, + `required` varchar(30) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE UNIQUE INDEX `unique` ON `zt_deliverablestage`(`deliverable`,`stage`); +ALTER TABLE `zt_module` ADD `extra` varchar(30) NOT NULL DEFAULT ''; + +ALTER TABLE `zt_process` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`; +ALTER TABLE `zt_process` ADD `module` int unsigned NOT NULL DEFAULT '0' AFTER `name`; +ALTER TABLE `zt_activity` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `process`; + +UPDATE `zt_process` SET `editedDate` = NULL WHERE `editedDate` LIKE '1970-01-01%'; +UPDATE `zt_process` SET `assignedDate` = NULL WHERE `assignedDate` LIKE '1970-01-01%'; +UPDATE `zt_activity` SET `editedDate` = NULL WHERE `editedDate` LIKE '1970-01-01%'; +UPDATE `zt_activity` SET `assignedDate` = NULL WHERE `assignedDate` LIKE '1970-01-01%'; + +ALTER TABLE `zt_searchindex` CHANGE `content` `content` longtext NULL; + +CREATE TABLE IF NOT EXISTS `zt_projectdeliverable` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目', + `submitFrom` int unsigned NOT NULL DEFAULT 0 COMMENT '提交来源', + `review` int unsigned NOT NULL DEFAULT 0 COMMENT '关联评审', + `deliverable` varchar(30) NOT NULL, + `name` varchar(255) NOT NULL, + `doc` int unsigned NOT NULL DEFAULT '0', + `docVersion` smallint unsigned NOT NULL DEFAULT '0', + `status` varchar(30) NOT NULL DEFAULT '', + `hasApproval` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '是否有评审流程', + `version` varchar(255) NULL, + `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` date NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE INDEX `project` ON `zt_projectdeliverable` (`project`); +CREATE UNIQUE INDEX `project_deliverable_doc` ON `zt_projectdeliverable` (`project`, `deliverable`, `doc`); + +ALTER TABLE `zt_approvalflowobject` ADD `relatedBy` varchar(30) NOT NULL DEFAULT '' AFTER `objectID`; +ALTER TABLE `zt_approvalflowobject` ADD `relatedDate` datetime NULL AFTER `relatedBy`; + +ALTER TABLE `zt_reviewcl` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`; + +UPDATE `zt_lang` SET `value` = '单元测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'unittest' AND `value` = '单元测试阶段'; +UPDATE `zt_lang` SET `value` = '功能测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'feature' AND `value` = '功能测试阶段'; +UPDATE `zt_lang` SET `value` = '集成测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'intergrate' AND `value` = '集成测试阶段'; +UPDATE `zt_lang` SET `value` = '系统测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'system' AND `value` = '系统测试阶段'; +UPDATE `zt_lang` SET `value` = '冒烟测试环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'smoke' AND `value` = '冒烟测试阶段'; +UPDATE `zt_lang` SET `value` = '版本验证环节' WHERE `module` = 'testcase' AND `section` = 'stageList' AND `key` = 'bvt' AND `value` = '版本验证阶段'; + +ALTER TABLE `zt_review` ADD `version` varchar(255) NOT NULL DEFAULT '' AFTER `docVersion`; +ALTER TABLE `zt_review` ADD `deliverable` int unsigned NOT NULL DEFAULT '0' AFTER `title`; +ALTER TABLE `zt_review` ADD `isBaseline` tinyint(1) DEFAULT '0' AFTER `status`; +ALTER TABLE `zt_review` ADD `type` varchar(30) NOT NULL DEFAULT '' AFTER `version`; +UPDATE `zt_review` SET `status` = 'reviewing' WHERE `status` = 'wait'; +UPDATE `zt_review` SET `status` = 'pass' WHERE `status` = 'auditing' OR `status` = 'done'; +UPDATE `zt_review` SET `type` = 'deliverable'; + +UPDATE `zt_review` AS t1 +JOIN `zt_object` AS t2 ON t1.object = t2.id +SET t1.version = t2.version; + +UPDATE `zt_reviewissue` AS t1 +JOIN `zt_review` AS t2 ON t1.review = t2.id +SET t1.project = t2.project; + +UPDATE `zt_object` SET `category` = 'intergrate' WHERE `category` = 'ITTC'; +UPDATE `zt_object` SET `category` = 'system' WHERE `category` = 'STTC'; + +ALTER TABLE `zt_object` ADD `status` varchar(20) NOT NULL DEFAULT '' AFTER `type`; +ALTER TABLE `zt_object` ADD `approval` int unsigned NOT NULL DEFAULT '0' AFTER `status`; +ALTER TABLE `zt_object` ADD `reviewResult` varchar(20) NOT NULL DEFAULT '' AFTER `approval`; +ALTER TABLE `zt_object` ADD `reviewOpinion` text NULL AFTER `reviewResult`; +ALTER TABLE `zt_object` ADD `reviewers` text NULL AFTER `reviewResult`; +ALTER TABLE `zt_object` ADD `categoryVersion` text DEFAULT NULL AFTER `category`; +ALTER TABLE `zt_object` ADD `editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁编辑' AFTER `createdDate`; +ALTER TABLE `zt_object` ADD `editedDate` datetime DEFAULT NULL COMMENT '编辑时间' AFTER `editedBy`; +ALTER TABLE `zt_object` MODIFY `category` varchar(255) NOT NULL DEFAULT ''; +ALTER TABLE `zt_object` MODIFY `createdDate` datetime DEFAULT NULL; + +ALTER TABLE `zt_designspec` ADD `docs` text NULL AFTER `files`; +ALTER TABLE `zt_designspec` ADD `docVersions` text NULL AFTER `docs`; +UPDATE `zt_designspec` AS t1 +JOIN `zt_design` AS t2 ON t1.design = t2.id AND t2.version = t1.version +SET t1.docs = t2.docs, t1.docVersions = t2.docVersions; +ALTER TABLE `zt_design` DROP COLUMN `docs`; +ALTER TABLE `zt_design` DROP COLUMN `docVersions`; + +ALTER TABLE `zt_reviewissue` ADD `assignedTo` char(30) COLLATE 'utf8mb4_general_ci' NOT NULL DEFAULT '' AFTER `createdDate`; +ALTER TABLE `zt_reviewissue` ADD `assignedDate` datetime NULL AFTER `assignedTo`; + +REPLACE INTO `zt_grouppriv`(`group`, `module`, `method`) SELECT `group`, `module`, 'active' as `method` FROM `zt_grouppriv` WHERE `module` = 'reviewissue' AND `method` = 'updateStatus'; +REPLACE INTO `zt_grouppriv`(`group`, `module`, `method`) SELECT `group`, `module`, 'close' as `method` FROM `zt_grouppriv` WHERE `module` = 'reviewissue' AND `method` = 'updateStatus'; +DELETE FROM `zt_grouppriv` WHERE `module` IN ('reviewissue', 'activity', 'process') AND `method` IN ('outputList', 'updateOrder'); +DELETE FROM `zt_grouppriv` WHERE `module` = 'zoutput'; +DELETE FROM `zt_grouppriv` WHERE `module` = 'design' AND `method` IN ('setPlusType', 'setType'); + +INSERT INTO `zt_metric` (`purpose`, `scope`, `object`, `stage`, `type`, `name`, `alias`, `code`, `unit`, `desc`, `definition`, `when`, `createdBy`, `createdDate`, `builtin`, `deleted`, `dateType`) VALUES +('scale', 'user', 'reviewissue', 'released', 'php', '按人员统计的被指派的评审意见数 ', '被指派的评审意见数', 'count_of_assigned_reviewissue_in_user', 'count', '按人员统计的被指派的评审意见数表示每个人被指派的评审意见数量之和,反映了每个人员需要处理的评审意见数量的规模。该数值越大,说明需要投入越多的时间处理评审意见。', '所有评审意见个数求和\r\n指派给为某人\r\n过滤已删除的评审意见\r\n过滤已关闭的评审意见\r\n过滤已删除项目的评审意见', '', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'); + +ALTER TABLE `zt_approvalnode` +CHANGE `status` `status` varchar(30) NOT NULL DEFAULT 'wait', +CHANGE `result` `result` varchar(30) NOT NULL DEFAULT ''; + +CREATE TABLE IF NOT EXISTS `zt_decision` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `workflowGroup` int unsigned NOT NULL DEFAULT '0', + `stage` int unsigned NOT NULL DEFAULT '0', + `order` int unsigned NOT NULL DEFAULT '0', + `title` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `category` varchar(30) NOT NULL DEFAULT '', + `builtin` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +ALTER TABLE `zt_stage` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`; +ALTER TABLE `zt_stage` ADD `order` int unsigned NOT NULL DEFAULT '0' AFTER `editedDate`; + +ALTER TABLE `zt_object` ADD `execution` int unsigned NOT NULL DEFAULT '0' AFTER `project`; +ALTER TABLE `zt_object` ADD `categoryTitle` varchar(255) NOT NULL DEFAULT '' AFTER `category`; +ALTER TABLE `zt_object` MODIFY COLUMN `type` enum('reviewed','taged','decision') NOT NULL DEFAULT 'reviewed'; + +ALTER TABLE `zt_auditcl` ADD `workflowGroup` int unsigned NOT NULL DEFAULT '0' AFTER `id`; + +UPDATE zt_activity SET optional = CASE + WHEN optional = 'yes' THEN 'no' + WHEN optional = 'no' THEN 'yes' + ELSE optional +END; + +ALTER TABLE `zt_process` CHANGE `model` `model` varchar(30) NOT NULL DEFAULT ''; + +ALTER TABLE `zt_auditplan` ADD `cycleType` varchar(10) NOT NULL DEFAULT 'noCycle' COMMENT '周期类型' AFTER `status`; +ALTER TABLE `zt_auditplan` ADD `cycleConfig` varchar(100) NOT NULL DEFAULT '[]' COMMENT '周期设置' AFTER `cycleType`; +ALTER TABLE `zt_auditplan` ADD `cyclePlan` smallint unsigned NOT NULL DEFAULT 0 COMMENT '生成计划(提前的天数)' AFTER `cycleConfig`; +ALTER TABLE `zt_auditplan` ADD `deadline` date NULL COMMENT '过期时间' AFTER `cyclePlan`; +ALTER TABLE `zt_auditplan` ADD `templateID` int unsigned NOT NULL DEFAULT 0 COMMENT '周期性活动检查ID' AFTER `deadline`; +ALTER TABLE `zt_auditplan` DROP COLUMN `dateType`; +ALTER TABLE `zt_auditplan` DROP COLUMN `config`; +ALTER TABLE `zt_auditplan` DROP COLUMN `checkBy`; + +INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`) VALUES('2','2','*','*','*','moduleName=auditplan&methodName=ajaxCreateCycleAuditplan','生成周期性活动检查','zentao',1,'normal'); + +UPDATE `zt_auditplan` SET `checkDate` = NULL WHERE `checkDate` = '0000-00-00'; + +CREATE TABLE IF NOT EXISTS `zt_projectchange` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '变更名称', + `urgency` varchar(30) NOT NULL DEFAULT '' COMMENT '变更等级', + `type` varchar(30) NOT NULL DEFAULT '' COMMENT '变更类型', + `deliverable` varchar(255) NOT NULL DEFAULT '' COMMENT '变更对象', + `status` varchar(30) NOT NULL DEFAULT '' COMMENT '变更状态', + `approval` int unsigned NOT NULL DEFAULT 0 COMMENT '审批流程', + `reviewResult` varchar(20) NOT NULL DEFAULT '' COMMENT '审批结果', + `reviewOpinion` text DEFAULT NULL COMMENT '审批意见', + `reviewers` text DEFAULT NULL COMMENT '评审人员', + `owner` varchar(30) NOT NULL DEFAULT '' COMMENT '负责人', + `reason` varchar(1000) NOT NULL DEFAULT '' COMMENT '变更原因', + `desc` text DEFAULT NULL COMMENT '变更描述', + `deadline` datetime DEFAULT NULL COMMENT '期望完成时间', + `createdBy` varchar(30) NOT NULL DEFAULT '' COMMENT '创建人', + `createdDate` datetime DEFAULT NULL COMMENT '创建时间', + `editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '编辑人', + `editedDate` datetime DEFAULT NULL COMMENT '编辑时间', + `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE INDEX `project` ON `zt_projectchange` (`project`); + +ALTER TABLE `zt_nc` ADD `execution` int unsigned NOT NULL DEFAULT 0 COMMENT '所属执行' AFTER `project`; +ALTER TABLE `zt_nc` ADD `deliverable` int unsigned NOT NULL DEFAULT 0 COMMENT '交付物' AFTER `auditplan`; + +ALTER TABLE `zt_workflowgroup` ADD `disabledFeatures` varchar(255) NOT NULL DEFAULT '' COMMENT '项目流程关闭的功能' AFTER `disabledModules`; + +ALTER TABLE `zt_stage` MODIFY COLUMN `projectType` varchar(30) NOT NULL DEFAULT '' COMMENT '所属项目流程的类型'; + +DELETE FROM `zt_doc` WHERE `templateType` IN ('PP', 'SRS', 'HLDS', 'DDS', 'ADS', 'DBDS', 'ITTC', 'STTC') AND `builtin` = '1'; + +ALTER TABLE `zt_object` +DROP `range`, +ADD `items` text NULL DEFAULT NULL COMMENT '评审条目列表' AFTER `data`, +DROP `storyEst`, +DROP `taskEst`, +DROP `requestEst`, +DROP `testEst`, +DROP `devEst`, +DROP `designEst`; + +ALTER TABLE `zt_review` +DROP `auditedBy`, +DROP `lastAuditedBy`, +DROP `lastAuditedDate`, +DROP `toAuditBy`, +DROP `toAuditDate`, +DROP `auditResult`; + +INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES +('lang', '基线状态', 'baselineStatus', '1', 'rnd', 'admin', null, 'baselineStatus', '', '', ''), +('lang', '基线审批结果', 'baselineReviewResult', '1', 'rnd', 'admin', null, 'baselineReviewResult', '', '', ''), +('lang', '项目变更紧急等级', 'projectchangeUrgencyList', '1', 'rnd', 'admin', null, 'projectchangeUrgencyList', '', '', ''), +('lang', '项目变更类型', 'projectchangeTypeList', '1', 'rnd', 'admin', null, 'projectchangeTypeList', '', '', ''), +('lang', '项目变更状态', 'projectchangeStatus', '1', 'rnd', 'admin', null, 'projectchangeStatus', '', '', ''), +('lang', '项目变更审批结果', 'projectchangeReviewResult', '1', 'rnd', 'admin', null, 'projectchangeReviewResult', '', '', ''), +('lang', '风险来源', 'riskSource', '1', 'rnd', 'admin', null, 'riskSource', '', '', ''), +('lang', '风险类型', 'riskCategory', '1', 'rnd', 'admin', null, 'riskCategory', '', '', ''), +('lang', '风险策略', 'riskStrategy', '1', 'rnd', 'admin', null, 'riskStrategy', '', '', ''), +('lang', '风险状态', 'riskStatus', '1', 'rnd', 'admin', null, 'riskStatus', '', '', ''), +('lang', '风险影响程度', 'riskImpact', '1', 'rnd', 'admin', null, 'riskImpact', '', '', ''), +('lang', '风险发生概率', 'riskProbability', '1', 'rnd', 'admin', null, 'riskProbability', '', '', ''), +('lang', '风险系数', 'riskRate', '1', 'rnd', 'admin', null, 'riskRate', '', '', ''), +('lang', '风险优先级', 'riskPri', '1', 'rnd', 'admin', null, 'riskPri', '', '', ''), +('lang', '风险取消原因', 'riskCancelReason', '1', 'rnd', 'admin', null, 'riskCancelReason', '', '', ''), +('lang', '问题优先级', 'issuePri', '1', 'rnd', 'admin', null, 'issuePri', '', '', ''), +('lang', '问题严重程度', 'issueSeverity', '1', 'rnd', 'admin', null, 'issueSeverity', '', '', ''), +('lang', '问题类型', 'issueType', '1', 'rnd', 'admin', null, 'issueType', '', '', ''), +('lang', '4问题解决方式', 'issueResolution', '1', 'rnd', 'admin', null, 'issueResolution', '', '', ''), +('lang', '问题状态', 'issueStatus', '1', 'rnd', 'admin', null, 'issueStatus', '', '', ''), +('lang', '机会来源', 'opportunitySource', '1', 'rnd', 'admin', null, 'opportunitySource', '', '', ''), +('lang', '机会类型', 'opportunityType', '1', 'rnd', 'admin', null, 'opportunityType', '', '', ''), +('lang', '机会策略', 'opportunityStrategy', '1', 'rnd', 'admin', null, 'opportunityStrategy', '', '', ''), +('lang', '机会状态', 'opportunityStatus', '1', 'rnd', 'admin', null, 'opportunityStatus', '', '', ''), +('lang', '机会影响程度', 'opportunityImpact', '1', 'rnd', 'admin', null, 'opportunityImpact', '', '', ''), +('lang', '机会发生概率', 'opportunityChance', '1', 'rnd', 'admin', null, 'opportunityChance', '', '', ''), +('lang', '机会优先级', 'opportunityPri', '1', 'rnd', 'admin', null, 'opportunityPri', '', '', ''), +('lang', '机会取消原因', 'opportunityCancelReason', '1', 'rnd', 'admin', null, 'opportunityCancelReason', '', '', ''); + +ALTER TABLE `zt_story` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `verifiedDate`; +ALTER TABLE `zt_design` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `desc`; +ALTER TABLE `zt_project` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `vision`; +ALTER TABLE `zt_doc` ADD `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态' AFTER `builtIn`; diff --git a/db/zentao.sql b/db/zentao.sql index 0a9413376a..72243dccc4 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -201,11 +201,13 @@ CREATE TABLE IF NOT EXISTS `zt_approvalflow` ( -- DROP TABLE IF EXISTS `zt_approvalflowobject`; CREATE TABLE IF NOT EXISTS `zt_approvalflowobject` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `root` int unsigned NOT NULL DEFAULT 0, - `flow` int unsigned NOT NULL DEFAULT 0, - `objectType` varchar(30) NOT NULL DEFAULT '', - `objectID` int unsigned NOT NULL DEFAULT 0, + `id` int NOT NULL AUTO_INCREMENT, + `root` int NOT NULL DEFAULT '0', + `flow` int NOT NULL DEFAULT '0', + `objectType` char(30) NOT NULL DEFAULT '', + `objectID` int unsigned NOT NULL DEFAULT '0', + `relatedBy` varchar(30) NOT NULL DEFAULT '', + `relatedDate` datetime NULL, `extra` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -237,8 +239,8 @@ CREATE TABLE IF NOT EXISTS `zt_approvalnode` ( `solicit` tinyint unsigned NOT NULL DEFAULT 0, `prev` mediumtext DEFAULT NULL, `next` mediumtext DEFAULT NULL, - `status` varchar(20) NOT NULL DEFAULT 'wait', - `result` varchar(10) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT 'wait', + `result` varchar(30) NOT NULL DEFAULT '', `date` date DEFAULT NULL, `opinion` mediumtext DEFAULT NULL, `extra` mediumtext DEFAULT NULL, @@ -593,20 +595,53 @@ CREATE TABLE IF NOT EXISTS `zt_screen` ( ) ENGINE=InnoDB; -- DROP TABLE IF EXISTS `zt_deliverable`; -CREATE TABLE IF NOT EXISTS `zt_deliverable` ( +CREATE TABLE `zt_deliverable` ( `id` int unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL DEFAULT '', - `module` varchar(30) NOT NULL DEFAULT '', - `method` varchar(30) NOT NULL DEFAULT '', - `model` text DEFAULT NULL, - `type` varchar(10) NOT NULL DEFAULT 'file', - `desc` text DEFAULT NULL, - `files` varchar(255) NOT NULL DEFAULT '', - `createdBy` varchar(30) NOT NULL DEFAULT '', - `createdDate` datetime DEFAULT NULL, - `lastEditedBy` varchar(30) NOT NULL DEFAULT '', - `lastEditedDate` datetime DEFAULT NULL, - `deleted` tinyint unsigned NOT NULL DEFAULT 0, + `workflowGroup` int unsigned NOT NULL DEFAULT 0, + `module` int unsigned NOT NULL DEFAULT 0, + `builtin` tinyint unsigned NOT NULL DEFAULT 0, + `systemList` tinyint unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL, + `status` varchar(30) NOT NULL DEFAULT 'enabled', + `activity` int unsigned NOT NULL DEFAULT 0, + `trimmable` char(30) NOT NULL DEFAULT '0', + `trimRule` varchar(255) NOT NULL, + `template` text NOT NULL, + `desc` text NULL, + `createdBy` varchar(30) NULL, + `createdDate` date NULL, + `lastEditedBy` varchar(30) NULL, + `lastEditedDate` date NULL, + `category` varchar(255) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- DROP TABLE IF EXISTS `zt_deliverablestage`; +CREATE TABLE IF NOT EXISTS `zt_deliverablestage` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `deliverable` int unsigned NOT NULL DEFAULT 0, + `stage` varchar(30) NOT NULL, + `required` varchar(30) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE UNIQUE INDEX `unique` ON `zt_deliverablestage`(`deliverable`,`stage`); + +-- DROP TABLE IF EXISTS `zt_decision`; +CREATE TABLE IF NOT EXISTS `zt_decision` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `workflowGroup` int unsigned NOT NULL DEFAULT '0', + `stage` int unsigned NOT NULL DEFAULT '0', + `order` int unsigned NOT NULL DEFAULT '0', + `title` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `category` varchar(30) NOT NULL DEFAULT '', + `builtin` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -765,13 +800,12 @@ CREATE TABLE IF NOT EXISTS `zt_design` ( `editedDate` datetime DEFAULT NULL, `assignedTo` varchar(30) NOT NULL DEFAULT '', `assignedBy` varchar(30) NOT NULL DEFAULT '', - `assignedDate` datetime DEFAULT NULL, - `story` int unsigned NOT NULL DEFAULT 0, - `storyVersion` smallint unsigned NOT NULL DEFAULT 1, - `docs` text DEFAULT NULL, - `docVersions` text DEFAULT NULL, - `desc` mediumtext DEFAULT NULL, - `version` smallint unsigned NOT NULL DEFAULT 1, + `assignedDate` datetime NULL, + `story` varchar(30) NOT NULL DEFAULT '', + `storyVersion` smallint(6) UNSIGNED NOT NULL DEFAULT '1', + `desc` mediumtext NULL, + `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态', + `version` smallint(6) NOT NULL DEFAULT '0', `type` varchar(30) NOT NULL DEFAULT '', `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) @@ -783,10 +817,12 @@ CREATE TABLE IF NOT EXISTS `zt_designspec` ( `design` int unsigned NOT NULL DEFAULT 0, `version` smallint unsigned NOT NULL DEFAULT 1, `name` varchar(255) NOT NULL DEFAULT '', - `desc` mediumtext DEFAULT NULL, + `desc` mediumtext NULL, `files` varchar(255) NOT NULL DEFAULT '', + `docs` text NULL, + `docVersions` text NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE UNIQUE INDEX `design` ON `zt_designspec`(`design`,`version`); -- DROP TABLE IF EXISTS `zt_doc`; @@ -832,12 +868,14 @@ CREATE TABLE IF NOT EXISTS `zt_doc` ( `editedList` text DEFAULT NULL, `mailto` text DEFAULT NULL, `acl` varchar(10) NOT NULL DEFAULT 'open', + `isDeliverable` tinyint unsigned NOT NULL DEFAULT 0, `groups` varchar(255) NOT NULL DEFAULT '', `users` text DEFAULT NULL, `readGroups` varchar(255) NOT NULL DEFAULT '', `readUsers` text DEFAULT NULL, `version` smallint unsigned NOT NULL DEFAULT 1, `builtIn` tinyint unsigned NOT NULL DEFAULT 0, + `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态', `vision` varchar(10) NOT NULL DEFAULT 'rnd', `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) @@ -1309,6 +1347,7 @@ CREATE TABLE IF NOT EXISTS `zt_module` ( `owner` varchar(30) NOT NULL DEFAULT '', `collector` text DEFAULT NULL, `short` varchar(60) NOT NULL DEFAULT '', + `extra` varchar(30) NOT NULL DEFAULT '', `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -1592,8 +1631,8 @@ CREATE TABLE IF NOT EXISTS `zt_project` ( `colWidth` smallint unsigned NOT NULL DEFAULT 264, `minColWidth` smallint unsigned NOT NULL DEFAULT 200, `maxColWidth` smallint unsigned NOT NULL DEFAULT 384, - `deliverable` text DEFAULT NULL, `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态', `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -1619,6 +1658,53 @@ CREATE TABLE IF NOT EXISTS `zt_projectadmin` ( ) ENGINE=InnoDB; CREATE UNIQUE INDEX `group_account` ON `zt_projectadmin`(`group`, `account`); +-- DROP TABLE IF EXISTS `zt_projectchange`; +CREATE TABLE IF NOT EXISTS `zt_projectchange` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '变更名称', + `urgency` varchar(30) NOT NULL DEFAULT '' COMMENT '变更等级', + `type` varchar(30) NOT NULL DEFAULT '' COMMENT '变更类型', + `deliverable` varchar(255) NOT NULL DEFAULT '' COMMENT '变更对象', + `status` varchar(30) NOT NULL DEFAULT '' COMMENT '变更状态', + `approval` int unsigned NOT NULL DEFAULT 0 COMMENT '审批流程', + `reviewResult` varchar(20) NOT NULL DEFAULT '' COMMENT '审批结果', + `reviewOpinion` text DEFAULT NULL COMMENT '审批意见', + `reviewers` text DEFAULT NULL COMMENT '评审人员', + `owner` varchar(30) NOT NULL DEFAULT '' COMMENT '负责人', + `reason` varchar(1000) NOT NULL DEFAULT '' COMMENT '变更原因', + `desc` text DEFAULT NULL COMMENT '变更描述', + `deadline` datetime DEFAULT NULL COMMENT '期望完成时间', + `createdBy` varchar(30) NOT NULL DEFAULT '' COMMENT '创建人', + `createdDate` datetime DEFAULT NULL COMMENT '创建时间', + `editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '编辑人', + `editedDate` datetime DEFAULT NULL COMMENT '编辑时间', + `deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE INDEX `project` ON `zt_projectchange` (`project`); + +-- DROP TABLE IF EXISTS `zt_projectdeliverable`; +CREATE TABLE IF NOT EXISTS `zt_projectdeliverable` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目', + `submitFrom` int unsigned NOT NULL DEFAULT 0 COMMENT '提交来源', + `review` int unsigned NOT NULL DEFAULT 0 COMMENT '关联评审', + `deliverable` varchar(30) NOT NULL, + `name` varchar(255) NOT NULL, + `doc` int unsigned NOT NULL DEFAULT '0', + `docVersion` smallint unsigned NOT NULL DEFAULT '0', + `status` varchar(30) NOT NULL DEFAULT '', + `hasApproval` tinyint unsigned NOT NULL DEFAULT 1 COMMENT '是否有评审流程', + `version` varchar(255) NULL, + `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` date NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE INDEX `project` ON `zt_projectdeliverable` (`project`); +CREATE UNIQUE INDEX `project_deliverable_doc` ON `zt_projectdeliverable` (`project`, `deliverable`, `doc`); + -- DROP TABLE IF EXISTS `zt_projectcase`; CREATE TABLE IF NOT EXISTS `zt_projectcase` ( `id` int unsigned NOT NULL AUTO_INCREMENT, @@ -1869,14 +1955,16 @@ CREATE INDEX `addedDate` ON `zt_searchindex` (`addedDate`); -- DROP TABLE IF EXISTS `zt_stage`; CREATE TABLE IF NOT EXISTS `zt_stage` ( `id` int unsigned NOT NULL AUTO_INCREMENT, + `workflowGroup` int unsigned NOT NULL DEFAULT 0, `name` varchar(255) NOT NULL DEFAULT '', `percent` varchar(255) NOT NULL DEFAULT '', `type` varchar(255) NOT NULL DEFAULT '', - `projectType` varchar(255) NOT NULL DEFAULT '', + `projectType` varchar(30) NOT NULL DEFAULT '' COMMENT '所属项目流程的类型', `createdBy` varchar(30) NOT NULL DEFAULT '', `createdDate` datetime DEFAULT NULL, `editedBy` varchar(30) NOT NULL DEFAULT '', `editedDate` datetime DEFAULT NULL, + `order` int unsigned NOT NULL DEFAULT 0, `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -1971,6 +2059,7 @@ CREATE TABLE IF NOT EXISTS `zt_story` ( `retractedDate` datetime, `verifiedDate` datetime, `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `frozen` varchar(30) NOT NULL DEFAULT '' COMMENT '冻结状态', `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -2583,19 +2672,13 @@ INSERT INTO `zt_storygrade` (`type`, `grade`, `name`, `status`) VALUES ('story', 1, 'SR', 'enable'), ('story', 2, '子', 'enable'); -REPLACE INTO `zt_stage` (`name`,`percent`,`type`,`projectType`,`createdBy`,`createdDate`,`editedBy`,`editedDate`,`deleted`) VALUES -('需求','10','request','waterfall','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('设计','10','design','waterfall','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('开发','50','dev','waterfall','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('测试','15','qa','waterfall','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('发布','10','release','waterfall','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('总结评审','5','review','waterfall','admin','2020-02-08 21:08:45','admin','2020-02-12 13:50:27','0'), -('需求','10','request','waterfallplus','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('设计','10','design','waterfallplus','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('开发','50','dev','waterfallplus','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('测试','15','qa','waterfallplus','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('发布','10','release','waterfallplus','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'), -('总结评审','5','review','waterfallplus','admin','2020-02-08 21:08:45','admin','2020-02-12 13:50:27','0'); +REPLACE INTO `zt_stage` (`name`, `percent`, `type`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `deleted`) VALUES +('需求', '10', 'request', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), +('设计', '10', 'design', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), +('开发', '50', 'dev', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), +('测试', '15', 'qa', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), +('发布', '10', 'release', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), +('总结评审', '5', 'review', 'admin', '2020-02-08 21:08:45', 'admin', '2020-02-12 13:50:27', '0'); REPLACE INTO `zt_config` (`vision`, `owner`, `module`, `section`, `key`, `value`) VALUES ('', 'system', 'common', '', 'CRProduct', '1'), @@ -13383,12 +13466,13 @@ CREATE TABLE IF NOT EXISTS `zt_workflowgroup` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `objectID` int unsigned NOT NULL DEFAULT 0, `type` varchar(10) NOT NULL DEFAULT '', - `projectModel` varchar(10) NOT NULL DEFAULT '', + `projectModel` varchar(30) NOT NULL DEFAULT '', `projectType` varchar(10) NOT NULL DEFAULT '', `name` varchar(255) NOT NULL DEFAULT '', `code` varchar(30) NOT NULL DEFAULT '', `desc` text DEFAULT NULL, `disabledModules` varchar(255) NOT NULL DEFAULT '', + `disabledFeatures` varchar(255) NOT NULL DEFAULT '' COMMENT '项目流程关闭的功能', `status` varchar(10) NOT NULL DEFAULT 'wait', `main` tinyint unsigned NOT NULL DEFAULT 0, `exclusive` tinyint unsigned NOT NULL DEFAULT 0, @@ -13677,12 +13761,23 @@ REPLACE INTO `zt_workflowrule`(`type`, `name`, `rule`, `createdBy`, `createdDate ('system','电话','phone','admin','2020-10-14 14:06:14'), ('system','IP','ip','admin','2020-10-14 14:06:14'); -INSERT INTO `zt_workflowgroup` (`type`, `projectModel`, `projectType`, `name`, `code`, `status`, `vision`, `main`) VALUES -('product', '', 'project', '默认流程', 'productproject', 'normal', 'rnd', '1'), -('project', 'scrum', 'product', '敏捷式产品研发', 'scrumproduct', 'normal', 'rnd', '1'), -('project', 'scrum', 'project', '敏捷式项目研发', 'scrumproject', 'normal', 'rnd', '1'), -('project', 'waterfall', 'product', '瀑布式产品研发', 'waterfallproduct','normal', 'rnd', '1'), -('project', 'waterfall', 'project', '瀑布式项目研发', 'waterfallproject','normal', 'rnd', '1'); +INSERT INTO `zt_workflowgroup` (`id`, `objectID`, `type`, `projectModel`, `projectType`, `name`, `code`, `desc`, `disabledModules`, `status`, `vision`, `main`, `exclusive`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `deliverable`, `deleted`) VALUES +(1, 0, 'product', '', 'project', '默认流程', 'productproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(2, 0, 'project', 'scrum', 'product', '敏捷式产品研发', 'scrumproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(3, 0, 'project', 'scrum', 'project', '敏捷式项目研发', 'scrumproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(4, 0, 'project', 'waterfall', 'product', '瀑布式产品研发', 'waterfallproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(5, 0, 'project', 'waterfall', 'project', '瀑布式项目研发', 'waterfallproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(6, 0, 'project', 'agileplus', 'product', '融合敏捷式产品研发', 'agileplusproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(7, 0, 'project', 'agileplus', 'project', '融合敏捷式项目研发', 'agileplusproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(8, 0, 'project', 'waterfallplus', 'product', '融合瀑布式产品研发', 'waterfallplusproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(9, 0, 'project', 'waterfallplus', 'project', '融合瀑布式项目研发', 'waterfallplusproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(10, 0, 'project', 'kanban', 'product', '看板式产品研发', 'kanbanproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(11, 0, 'project', 'kanban', 'project', '看板式项目研发', 'kanbanproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(12, 0, 'project', 'ipd', 'ipd', 'IPD集成产品研发', 'ipdproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(13, 0, 'project', 'ipd', 'tpd', 'IPD预研产品研发', 'tpdproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(14, 0, 'project', 'ipd', 'cbb', 'IPD平台产品研发', 'cbbproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(15, 0, 'project', 'ipd', 'cpdproduct', 'IPD定制产品研发', 'cpdproduct', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'), +(16, 0, 'project', 'ipd', 'cpdproject', 'IPD定制项目研发', 'cpdproject', NULL, '', 'normal', 'rnd', '1', '0', '', NULL, '', NULL, NULL, '0'); INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES ('system', '产品', 'products', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"product\",\"method\":\"getPairs\",\"methodDesc\":\"Get product pairs.\",\"params\":[{\"name\":\"mode\",\"type\":\"string\",\"desc\":\"\",\"value\":\"all\"}]}', '', '', ''), @@ -13774,7 +13869,35 @@ INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision` ('lang', '立项状态', 'charterStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterStatus', '', '', ''), ('lang', '立项关闭原因 ', 'charterCloseReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterCloseReason', '', '', ''), ('lang', '立项审批结果', 'charterReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewResult', '', '', ''), -('lang', '立项审批状态', 'charterReviewStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewStatus', '', '', ''); +('lang', '立项审批状态', 'charterReviewStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'charterReviewStatus', '', '', ''), +('lang', '基线状态', 'baselineStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'baselineStatus', '', '', ''), +('lang', '基线审批结果', 'baselineReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'baselineReviewResult', '', '', ''), +('lang', '项目变更紧急等级', 'projectchangeUrgencyList', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'projectchangeUrgencyList', '', '', ''), +('lang', '项目变更类型', 'projectchangeTypeList', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'projectchangeTypeList', '', '', ''), +('lang', '项目变更状态', 'projectchangeStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'projectchangeStatus', '', '', ''), +('lang', '项目变更审批结果', 'projectchangeReviewResult', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'projectchangeReviewResult', '', '', ''), +('lang', '风险来源', 'riskSource', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskSource', '', '', ''), +('lang', '风险类型', 'riskCategory', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskCategory', '', '', ''), +('lang', '风险策略', 'riskStrategy', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskStrategy', '', '', ''), +('lang', '风险状态', 'riskStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskStatus', '', '', ''), +('lang', '风险影响程度', 'riskImpact', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskImpact', '', '', ''), +('lang', '风险发生概率', 'riskProbability', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskProbability', '', '', ''), +('lang', '风险系数', 'riskRate', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskRate', '', '', ''), +('lang', '风险优先级', 'riskPri', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskPri', '', '', ''), +('lang', '风险取消原因', 'riskCancelReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'riskCancelReason', '', '', ''), +('lang', '问题优先级', 'issuePri', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'issuePri', '', '', ''), +('lang', '问题严重程度', 'issueSeverity', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'issueSeverity', '', '', ''), +('lang', '问题类型', 'issueType', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'issueType', '', '', ''), +('lang', '4问题解决方式', 'issueResolution', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'issueResolution', '', '', ''), +('lang', '问题状态', 'issueStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'issueStatus', '', '', ''), +('lang', '机会来源', 'opportunitySource', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunitySource', '', '', ''), +('lang', '机会类型', 'opportunityType', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityType', '', '', ''), +('lang', '机会策略', 'opportunityStrategy', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityStrategy', '', '', ''), +('lang', '机会状态', 'opportunityStatus', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityStatus', '', '', ''), +('lang', '机会影响程度', 'opportunityImpact', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityImpact', '', '', ''), +('lang', '机会发生概率', 'opportunityChance', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityChance', '', '', ''), +('lang', '机会优先级', 'opportunityPri', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityPri', '', '', ''), +('lang', '机会取消原因', 'opportunityCancelReason', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'opportunityCancelReason', '', '', ''); DROP VIEW IF EXISTS `view_datasource_2`; DROP VIEW IF EXISTS `view_datasource_3`; @@ -13863,6 +13986,7 @@ CREATE UNIQUE INDEX `project` ON `zt_intervention`(`project`,`activity`); CREATE TABLE IF NOT EXISTS `zt_activity` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `process` int unsigned NOT NULL DEFAULT 0, + `workflowGroup` int unsigned NOT NULL DEFAULT 0, `name` varchar(255) NOT NULL DEFAULT '', `optional` varchar(255) NOT NULL DEFAULT '', `tailorNorm` varchar(255) NOT NULL DEFAULT '', @@ -13883,6 +14007,7 @@ CREATE TABLE IF NOT EXISTS `zt_activity` ( -- DROP TABLE IF EXISTS `zt_auditcl`; CREATE TABLE IF NOT EXISTS `zt_auditcl` ( `id` int unsigned NOT NULL AUTO_INCREMENT, + `workflowGroup` int unsigned NOT NULL DEFAULT 0, `model` varchar(30) NOT NULL DEFAULT 'waterfall', `practiceArea` varchar(30) NOT NULL DEFAULT '', `type` varchar(30) NOT NULL DEFAULT '', @@ -13903,29 +14028,31 @@ CREATE TABLE IF NOT EXISTS `zt_auditcl` ( -- DROP TABLE IF EXISTS `zt_auditplan`; CREATE TABLE IF NOT EXISTS `zt_auditplan` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `dateType` varchar(30) NOT NULL DEFAULT '', - `config` text DEFAULT NULL, - `objectID` int unsigned NOT NULL DEFAULT 0, - `objectType` varchar(10) NOT NULL DEFAULT '', - `process` int unsigned NOT NULL DEFAULT 0, - `processType` varchar(30) NOT NULL DEFAULT '', - `checkDate` date DEFAULT NULL, - `checkedBy` varchar(30) NOT NULL DEFAULT '', - `realCheckDate` date DEFAULT NULL, - `result` varchar(30) NOT NULL DEFAULT '', - `project` int unsigned NOT NULL DEFAULT 0, - `execution` int unsigned NOT NULL DEFAULT 0, - `assignedTo` varchar(30) NOT NULL DEFAULT '', - `status` varchar(30) NOT NULL DEFAULT '', - `createdBy` varchar(30) NOT NULL DEFAULT '', - `createdDate` datetime DEFAULT NULL, - `editedBy` varchar(30) NOT NULL DEFAULT '', - `editedDate` datetime DEFAULT NULL, - `assignedBy` varchar(30) NOT NULL DEFAULT '', - `assignedDate` datetime DEFAULT NULL, - `checkBy` varchar(30) NOT NULL DEFAULT '', - `deleted` tinyint unsigned NOT NULL DEFAULT 0, + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '活动检查编号', + `objectID` int unsigned NOT NULL DEFAULT 0 COMMENT '活动或文档编号', + `objectType` varchar(30) NOT NULL DEFAULT '' COMMENT '检查类型', + `process` int unsigned NOT NULL DEFAULT 0 COMMENT '所属过程', + `processType` varchar(30) NOT NULL DEFAULT '' COMMENT '过程分类', + `checkDate` date NULL COMMENT '计划检查时间', + `checkedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁检查', + `realCheckDate` date NULL COMMENT '实际检查时间', + `result` char(30) NOT NULL DEFAULT '' COMMENT '活动检查结果', + `project` int unsigned NOT NULL DEFAULT 0 COMMENT '所属项目', + `execution` int unsigned NOT NULL DEFAULT 0 COMMENT '所属执行', + `assignedTo` varchar(30) NOT NULL DEFAULT '' COMMENT '指派给', + `status` varchar(30) NOT NULL DEFAULT '' COMMENT '检查状态', + `cycleType` varchar(10) NOT NULL DEFAULT 'noCycle' COMMENT '周期类型', + `cycleConfig` varchar(100) NOT NULL DEFAULT '[]' COMMENT '周期设置', + `cyclePlan` smallint unsigned NOT NULL DEFAULT 0 COMMENT '生成计划(提前的天数)', + `deadline` date NULL COMMENT '过期时间', + `templateID` int unsigned NOT NULL DEFAULT 0 COMMENT '周期性活动检查ID', + `createdBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁创建', + `createdDate` datetime NULL COMMENT '创建时间', + `editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁编辑', + `editedDate` datetime NULL COMMENT '编辑时间', + `assignedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁指派', + `assignedDate` datetime NULL COMMENT '指派时间', + `deleted` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -13955,7 +14082,9 @@ CREATE TABLE IF NOT EXISTS `zt_auditresult` ( CREATE TABLE IF NOT EXISTS `zt_nc` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `project` int unsigned NOT NULL DEFAULT 0, + `execution` int unsigned NOT NULL DEFAULT 0 COMMENT '所属执行', `auditplan` int unsigned NOT NULL DEFAULT 0, + `deliverable` int unsigned NOT NULL DEFAULT 0 COMMENT '交付物', `listID` int unsigned NOT NULL DEFAULT 0, `title` varchar(255) NOT NULL DEFAULT '', `desc` mediumtext DEFAULT NULL, @@ -14001,9 +14130,9 @@ CREATE TABLE IF NOT EXISTS `zt_zoutput` ( -- DROP TABLE IF EXISTS `zt_process`; CREATE TABLE IF NOT EXISTS `zt_process` ( `id` int unsigned NOT NULL AUTO_INCREMENT, - `model` varchar(30) NOT NULL DEFAULT 'waterfall', `name` varchar(255) NOT NULL DEFAULT '', - `type` varchar(30) NOT NULL DEFAULT '', + `workflowGroup` int unsigned NOT NULL DEFAULT 0, + `module` int unsigned NOT NULL DEFAULT '0', `abbr` varchar(30) NOT NULL DEFAULT '', `desc` mediumtext DEFAULT NULL, `assignedTo` varchar(30) NOT NULL DEFAULT '', @@ -14273,24 +14402,28 @@ CREATE UNIQUE INDEX `time` ON `zt_measrecords` (`year`, `month`, `day`, `week`); CREATE TABLE IF NOT EXISTS `zt_object` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `project` int unsigned NOT NULL DEFAULT 0, + `execution` int unsigned NOT NULL DEFAULT 0, `product` int unsigned NOT NULL DEFAULT 0, `from` int unsigned NOT NULL DEFAULT 0, `title` varchar(255) NOT NULL DEFAULT '', - `category` varchar(30) NOT NULL DEFAULT '', + `category` varchar(255) NOT NULL DEFAULT '', + `categoryTitle` varchar(255) NOT NULL DEFAULT '', + `categoryVersion` text DEFAULT NULL, `version` varchar(255) NOT NULL DEFAULT '', - `type` varchar(10) NOT NULL DEFAULT 'reviewed', - `enabled` tinyint unsigned NOT NULL DEFAULT 1, - `range` text DEFAULT NULL, - `data` text DEFAULT NULL, - `storyEst` varchar(30) NOT NULL DEFAULT '', - `taskEst` varchar(30) NOT NULL DEFAULT '', - `requestEst` varchar(30) NOT NULL DEFAULT '', - `testEst` varchar(30) NOT NULL DEFAULT '', - `devEst` varchar(30) NOT NULL DEFAULT '', - `designEst` varchar(30) NOT NULL DEFAULT '', - `end` date DEFAULT NULL, - `createdBy` varchar(30) NOT NULL DEFAULT '', - `createdDate` datetime DEFAULT NULL, + `type` enum('reviewed','taged', 'decision') NOT NULL DEFAULT 'reviewed', + `status` varchar(20) NOT NULL DEFAULT '', + `approval` int unsigned NOT NULL DEFAULT 0, + `reviewResult` varchar(20) NOT NULL DEFAULT '', + `reviewOpinion` text NULL, + `reviewers` text NULL, + `enabled` enum('0','1') NOT NULL DEFAULT '1', + `data` text NULL DEFAULT NULL, + `items` text NULL DEFAULT NULL COMMENT '评审条目列表', + `end` date NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁创建', + `createdDate` datetime DEFAULT NULL COMMENT '创建时间', + `editedBy` varchar(30) NOT NULL DEFAULT '' COMMENT '由谁编辑', + `editedDate` datetime DEFAULT NULL COMMENT '编辑时间', `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -14300,38 +14433,35 @@ CREATE TABLE IF NOT EXISTS `zt_review` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `project` int unsigned NOT NULL DEFAULT 0, `title` varchar(255) NOT NULL DEFAULT '', + `deliverable` int unsigned NOT NULL DEFAULT 0, `object` int unsigned NOT NULL DEFAULT 0, `template` int unsigned NOT NULL DEFAULT 0, - `doc` varchar(255) NOT NULL DEFAULT '', `docVersion` varchar(255) NOT NULL DEFAULT '', - `status` varchar(30) NOT NULL DEFAULT '', + `version` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `status` char(30) NOT NULL DEFAULT '', + `isBaseline` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否基线', `reviewedBy` varchar(255) NOT NULL DEFAULT '', - `auditedBy` varchar(255) NOT NULL DEFAULT '', `createdBy` varchar(30) NOT NULL DEFAULT '', - `createdDate` datetime DEFAULT NULL, - `begin` date DEFAULT NULL, - `deadline` date DEFAULT NULL, + `createdDate` date NULL, + `begin` date NULL, + `deadline` date NULL, `lastReviewedBy` varchar(255) NOT NULL DEFAULT '', - `lastReviewedDate` datetime DEFAULT NULL, - `lastAuditedBy` varchar(255) NOT NULL DEFAULT '', - `lastAuditedDate` datetime DEFAULT NULL, - `toAuditBy` varchar(30) NOT NULL DEFAULT '', - `toAuditDate` datetime DEFAULT NULL, + `lastReviewedDate` date NULL, `lastEditedBy` varchar(255) NOT NULL DEFAULT '', - `lastEditedDate` datetime DEFAULT NULL, - `result` varchar(30) NOT NULL DEFAULT '', - `auditResult` varchar(30) NOT NULL DEFAULT '', - `deleted` tinyint unsigned NOT NULL DEFAULT 0, + `lastEditedDate` date NULL, + `result` char(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB; -- DROP TABLE IF EXISTS `zt_reviewcl`; CREATE TABLE IF NOT EXISTS `zt_reviewcl` ( `id` int unsigned NOT NULL AUTO_INCREMENT, + `workflowGroup` int unsigned NOT NULL DEFAULT 0, `title` varchar(255) NOT NULL DEFAULT '', `object` varchar(30) NOT NULL DEFAULT '', `category` varchar(30) NOT NULL DEFAULT '', - `type` varchar(255) NOT NULL DEFAULT '', `assignedTo` varchar(30) NOT NULL DEFAULT '', `order` int unsigned NOT NULL DEFAULT 0, `status` varchar(30) NOT NULL DEFAULT '', @@ -14371,14 +14501,16 @@ CREATE TABLE IF NOT EXISTS `zt_reviewissue` ( `type` varchar(30) NOT NULL DEFAULT 'review', `listID` int unsigned NOT NULL DEFAULT 0, `title` varchar(255) NOT NULL DEFAULT '', - `opinion` mediumtext DEFAULT NULL, - `opinionDate` date DEFAULT NULL, - `status` varchar(30) NOT NULL DEFAULT '', + `opinion` mediumtext NULL, + `opinionDate` DATE NULL, + `status` char(30) NOT NULL DEFAULT '', `resolution` varchar(30) NOT NULL DEFAULT '', `resolutionBy` varchar(30) NOT NULL DEFAULT '', - `resolutionDate` datetime DEFAULT NULL, + `resolutionDate` DATE NULL, `createdBy` varchar(30) NOT NULL DEFAULT '', - `createdDate` datetime DEFAULT NULL, + `createdDate` datetime NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime NULL, `deleted` tinyint unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -14683,904 +14815,10 @@ REPLACE INTO `zt_approvalflowspec` (`id`, `flow`, `version`, `nodes`, `createdBy (5, 5, 1, '[{\"type\":\"start\",\"ccs\":[]},{\"id\":\"3ewcj92p55e\",\"type\":\"approval\",\"title\":\"审批\",\"reviewType\":\"manual\",\"multiple\":\"and\",\"percent\":\"50\",\"commentType\":\"noRequired\",\"agentType\":\"pass\",\"selfType\":\"selfReview\",\"deletedType\":\"setAdmin\",\"reviewers\":[{\"type\":\"select\",\"users\":[],\"roles\":[\"\"],\"depts\":[\"\"],\"positions\":[\"\"],\"userRange\":\"all\",\"required\":\"yes\"}],\"ccs\":[{\"type\":\"select\",\"users\":[],\"roles\":[\"\"],\"depts\":[\"\"],\"positions\":[\"\"],\"userRange\":\"all\"}]},{\"type\":\"end\",\"ccs\":[]}]', 'system', NOW()); REPLACE INTO `zt_approvalflowobject` (`id`, `root`, `flow`, `objectType`, `objectID`, `extra`) VALUES -(1, '0', '2', 'charter', '0', 'projectApproval'), -(2, '0', '3', 'charter', '0', 'completionApproval'), -(3, '0', '4', 'charter', '0', 'cancelProjectApproval'), -(4, '0', '5', 'charter', '0', 'activateProjectApproval'); - -REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`) VALUES -('all', 'process', 'classify', 'support', '支持过程', '1'), -('all', 'process', 'classify', 'engineering', '工程支持', '1'), -('all', 'process', 'classify', 'project', '项目管理', '1'), -('all', 'process', 'scrumClassify', 'support', '支持过程', '1'), -('all', 'process', 'scrumClassify', 'engineering', '工程支持', '1'), -('all', 'process', 'scrumClassify', 'project', '项目管理', '1'), -('all', 'process', 'agileplusClassify', 'support', '支持过程', '1'), -('all', 'process', 'agileplusClassify', 'engineering', '工程支持', '1'), -('all', 'process', 'agileplusClassify', 'project', '项目管理', '1'), -('all', 'process', 'waterfallplusClassify', 'support', '支持过程', '1'), -('all', 'process', 'waterfallplusClassify', 'engineering', '工程支持', '1'), -('all', 'process', 'waterfallplusClassify', 'project', '项目管理', '1'); - -REPLACE INTO `zt_process` (`id`, `model`, `name`, `type`, `abbr`, `desc`, `assignedTo`, `status`, `order`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `assignedBy`, `assignedDate`, `deleted`) VALUES -(11, 'waterfall', '立项管理', 'project', 'PIM', '', '', '', 55, 'admin', '2020-01-09 10:29:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(12, 'waterfall', '项目规划', 'project', 'PP', '', '', '', 60, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(13, 'waterfall', '项目监控', 'project', 'PMC', '', '', '', 65, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(14, 'waterfall', '风险管理', 'project', 'RSKM', '', '', '', 70, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(15, 'waterfall', '结项管理', 'project', 'PCM', '', '', '', 75, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(16, 'waterfall', '量化项目管理', 'project', 'QPM', '', '', '', 80, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(17, 'waterfall', '需求开发', 'engineering', 'RDM', '', '', '', 85, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(18, 'waterfall', '设计开发', 'engineering', '', '', '', '', 90, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(19, 'waterfall', '实现与测试', 'engineering', 'EMBEDDED', '', '', '', 95, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(20, 'waterfall', '系统测试', 'engineering', 'ST', '', '', '', 100, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(21, 'waterfall', '客户验收', 'engineering', 'CA', '', '', '', 105, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(22, 'waterfall', '质量保证', 'support', 'QA', '', '', '', 110, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(23, 'waterfall', '配置管理', 'support', 'CM', '', '', '', 115, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(24, 'waterfall', '度量分析', 'support', 'MA', '', '', '', 120, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(25, 'waterfall', '原因分析与解决', 'support', 'CAR', '', '', '', 125, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(26, 'waterfall', '决策分析', 'support', 'DAR', '', '', '', 130, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(27, 'scrum', '立项管理', 'project', 'PIM', '', '', '', 55, 'admin', '2020-01-09 10:29:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(28, 'scrum', '项目规划', 'project', 'PP', '', '', '', 60, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(29, 'scrum', '项目监控', 'project', 'PMC', '', '', '', 65, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(30, 'scrum', '风险管理', 'project', 'RSKM', '', '', '', 70, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(31, 'scrum', '结项管理', 'project', 'PCM', '', '', '', 75, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(32, 'scrum', '量化项目管理', 'project', 'QPM', '', '', '', 80, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(33, 'scrum', '需求开发', 'engineering', 'RDM', '', '', '', 85, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(34, 'scrum', '设计开发', 'engineering', '', '', '', '', 90, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(35, 'scrum', '实现与测试', 'engineering', 'EMBEDDED', '', '', '', 95, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(36, 'scrum', '系统测试', 'engineering', 'ST', '', '', '', 100, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(37, 'scrum', '客户验收', 'engineering', 'CA', '', '', '', 105, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(38, 'scrum', '质量保证', 'support', 'QA', '', '', '', 110, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(39, 'scrum', '配置管理', 'support', 'CM', '', '', '', 115, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(40, 'scrum', '度量分析', 'support', 'MA', '', '', '', 120, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(41, 'scrum', '原因分析与解决', 'support', 'CAR', '', '', '', 125, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(42, 'scrum', '决策分析', 'support', 'DAR', '', '', '', 130, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(43, 'waterfallplus', '立项管理', 'project', 'PIM', '', '', '', 55, 'admin', '2020-01-09 10:29:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(44, 'waterfallplus', '项目规划', 'project', 'PP', '', '', '', 60, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(45, 'waterfallplus', '项目监控', 'project', 'PMC', '', '', '', 65, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(46, 'waterfallplus', '风险管理', 'project', 'RSKM', '', '', '', 70, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(47, 'waterfallplus', '结项管理', 'project', 'PCM', '', '', '', 75, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(48, 'waterfallplus', '量化项目管理', 'project', 'QPM', '', '', '', 80, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(49, 'waterfallplus', '需求开发', 'engineering', 'RDM', '', '', '', 85, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(50, 'waterfallplus', '设计开发', 'engineering', '', '', '', '', 90, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(51, 'waterfallplus', '实现与测试', 'engineering', 'EMBEDDED', '', '', '', 95, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(52, 'waterfallplus', '系统测试', 'engineering', 'ST', '', '', '', 100, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(53, 'waterfallplus', '客户验收', 'engineering', 'CA', '', '', '', 105, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(54, 'waterfallplus', '质量保证', 'support', 'QA', '', '', '', 110, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(55, 'waterfallplus', '配置管理', 'support', 'CM', '', '', '', 115, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(56, 'waterfallplus', '度量分析', 'support', 'MA', '', '', '', 120, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(57, 'waterfallplus', '原因分析与解决', 'support', 'CAR', '', '', '', 125, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(58, 'waterfallplus', '决策分析', 'support', 'DAR', '', '', '', 130, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(59, 'agileplus', '立项管理', 'project', 'PIM', '', '', '', 55, 'admin', '2020-01-09 10:29:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(60, 'agileplus', '项目规划', 'project', 'PP', '', '', '', 60, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(61, 'agileplus', '项目监控', 'project', 'PMC', '', '', '', 65, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(62, 'agileplus', '风险管理', 'project', 'RSKM', '', '', '', 70, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(63, 'agileplus', '结项管理', 'project', 'PCM', '', '', '', 75, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(64, 'agileplus', '量化项目管理', 'project', 'QPM', '', '', '', 80, 'admin', '2020-01-09 10:31:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(65, 'agileplus', '需求开发', 'engineering', 'RDM', '', '', '', 85, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(66, 'agileplus', '设计开发', 'engineering', '', '', '', '', 90, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(67, 'agileplus', '实现与测试', 'engineering', 'EMBEDDED', '', '', '', 95, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(68, 'agileplus', '系统测试', 'engineering', 'ST', '', '', '', 100, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(69, 'agileplus', '客户验收', 'engineering', 'CA', '', '', '', 105, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(70, 'agileplus', '质量保证', 'support', 'QA', '', '', '', 110, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(71, 'agileplus', '配置管理', 'support', 'CM', '', '', '', 115, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(72, 'agileplus', '度量分析', 'support', 'MA', '', '', '', 120, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(73, 'agileplus', '原因分析与解决', 'support', 'CAR', '', '', '', 125, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'), -(74, 'agileplus', '决策分析', 'support', 'DAR', '', '', '', 130, 'admin', '2020-01-09 13:14:55', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', '0'); - -REPLACE INTO `zt_activity` (`id`, `process`, `name`, `optional`, `tailorNorm`, `content`, `assignedTo`, `status`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `assignedBy`, `assignedDate`, `order`, `deleted`) VALUES -(4, 11, '立项调查,可行性分析,技术预研', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 20, '0'), -(5, 11, '项目预算', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 25, '0'), -(6, 11, '立项决策', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 30, '0'), -(7, 11, '投标/商务洽谈', 'no', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 35, '0'), -(8, 12, '估算', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 40, '0'), -(9, 12, '裁剪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 45, '0'), -(10, 12, '制定项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 50, '0'), -(11, 12, '评审项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 55, '0'), -(12, 13, '日常跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 60, '0'), -(13, 13, '举行周例会', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 65, '0'), -(14, 13, '里程碑评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 70, '0'), -(15, 13, '采取纠正措施', 'no', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 75, '0'), -(16, 14, '制定风险管理计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 80, '0'), -(17, 14, '风险识别', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 85, '0'), -(18, 14, '风险分析', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 90, '0'), -(19, 14, '风险规避和缓解', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 95, '0'), -(20, 14, '风险跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 100, '0'), -(21, 14, '更新组织级“风险列表库”', 'no', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 105, '0'), -(22, 15, '结项准备', 'no', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 110, '0'), -(23, 15, '结项申请', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 115, '0'), -(24, 15, '结项评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 120, '0'), -(25, 15, '资料纳入组织级过程资产库', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 125, '0'), -(26, 16, '量化项目管理', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 130, '0'), -(27, 17, '用户需求获取', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 135, '0'), -(28, 17, '用户需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 140, '0'), -(29, 17, '产品需求定义', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 145, '0'), -(30, 17, '产品需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 150, '0'), -(31, 17, '需求变更控制', 'no', '', '未涉及变更时可不进行', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 155, '0'), -(32, 18, '概要设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 160, '0'), -(33, 18, '评审《概要设计说明书》', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 165, '0'), -(34, 18, '详细设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 170, '0'), -(35, 18, '评审详细设计工作产品', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 175, '0'), -(42, 19, '制定编码阶段计划', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 210, '0'), -(43, 19, '构建数据库', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 215, '0'), -(44, 19, '编码实现', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 220, '0'), -(45, 19, '代码检查', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 225, '0'), -(46, 19, '单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 230, '0'), -(47, 19, '交叉单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 235, '0'), -(48, 19, '系统集成', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 240, '0'), -(49, 19, '编写集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 245, '0'), -(50, 19, '评审集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 250, '0'), -(51, 19, '集成测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 255, '0'), -(52, 19, '缺陷管理', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 260, '0'), -(53, 19, '编写支持文档', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 265, '0'), -(54, 20, '制定《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 270, '0'), -(55, 20, '评审《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 275, '0'), -(56, 20, '编写《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 280, '0'), -(57, 20, '评审《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 285, '0'), -(58, 20, '执行系统测试', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 290, '0'), -(59, 21, '验收准备', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 295, '0'), -(60, 21, '产品打包', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 300, '0'), -(61, 21, '内部培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 305, '0'), -(62, 21, '现场安装调试', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 310, '0'), -(63, 21, '用户使用培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 315, '0'), -(64, 21, '试运行', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 320, '0'), -(65, 21, '客户验收', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 325, '0'), -(66, 22, '制定质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 330, '0'), -(67, 22, '评审质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 335, '0'), -(68, 22, '过程和产品质量检查', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 340, '0'), -(69, 22, '问题跟踪处理', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 345, '0'), -(70, 22, '质量保证总结报告', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 350, '0'), -(71, 23, '组织及配置管理员移交工作', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 355, '0'), -(72, 23, '制定《配置管理计划》', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 360, '0'), -(73, 23, '建立和维护配置库', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 365, '0'), -(74, 23, '配置项管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 370, '0'), -(75, 23, '基线管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 375, '0'), -(76, 23, '变更管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 380, '0'), -(77, 23, '配置审计', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 385, '0'), -(78, 24, '建立项目度量目标', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 390, '0'), -(79, 24, '确定项目度量点', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 395, '0'), -(80, 24, '制定度量分析计划', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 400, '0'), -(81, 24, '收集和分析度量数据', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 405, '0'), -(82, 24, '存储和通报度量分析结果', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 410, '0'), -(83, 25, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:15', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 415, '0'), -(84, 26, '建立评估标准', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 420, '0'), -(85, 26, '确定候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 425, '0'), -(86, 26, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 430, '0'), -(87, 26, '确定评估方法', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 435, '0'), -(88, 26, '评估候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 440, '0'), -(89, 26, '做出决策', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 445, '0'), -(90, 27, '立项调查,可行性分析,技术预研', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 20, '0'), -(91, 27, '项目预算', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 25, '0'), -(92, 27, '立项决策', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 30, '0'), -(93, 27, '投标/商务洽谈', 'no', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 35, '0'), -(94, 28, '估算', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 40, '0'), -(95, 28, '裁剪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 45, '0'), -(96, 28, '制定项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 50, '0'), -(97, 28, '评审项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 55, '0'), -(98, 29, '日常跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 60, '0'), -(99, 29, '举行周例会', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 65, '0'), -(100, 29, '里程碑评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 70, '0'), -(101, 29, '采取纠正措施', 'no', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 75, '0'), -(102, 30, '制定风险管理计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 80, '0'), -(103, 30, '风险识别', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 85, '0'), -(104, 30, '风险分析', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 90, '0'), -(105, 30, '风险规避和缓解', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 95, '0'), -(106, 30, '风险跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 100, '0'), -(107, 30, '更新组织级“风险列表库”', 'no', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 105, '0'), -(108, 31, '结项准备', 'no', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 110, '0'), -(109, 31, '结项申请', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 115, '0'), -(110, 31, '结项评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 120, '0'), -(111, 31, '资料纳入组织级过程资产库', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 125, '0'), -(112, 32, '量化项目管理', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:34', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 130, '0'), -(113, 33, '用户需求获取', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 135, '0'), -(114, 33, '用户需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 140, '0'), -(115, 33, '产品需求定义', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 145, '0'), -(116, 33, '产品需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 150, '0'), -(117, 33, '需求变更控制', 'no', '', '未涉及变更时可不进行', '', '', 'admin', '2020-01-09 13:25:22', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 155, '0'), -(118, 34, '概要设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 160, '0'), -(119, 34, '评审《概要设计说明书》', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 165, '0'), -(120, 34, '详细设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 170, '0'), -(121, 34, '评审详细设计工作产品', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 175, '0'), -(122, 35, '制定编码阶段计划', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 210, '0'), -(123, 35, '构建数据库', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 215, '0'), -(124, 35, '编码实现', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 220, '0'), -(125, 35, '代码检查', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 225, '0'), -(126, 35, '单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 230, '0'), -(127, 35, '交叉单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 235, '0'), -(128, 35, '系统集成', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 240, '0'), -(129, 35, '编写集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 245, '0'), -(130, 35, '评审集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 250, '0'), -(131, 35, '集成测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 255, '0'), -(132, 35, '缺陷管理', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 260, '0'), -(133, 35, '编写支持文档', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 265, '0'), -(134, 36, '制定《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 270, '0'), -(135, 36, '评审《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 275, '0'), -(136, 36, '编写《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 280, '0'), -(137, 36, '评审《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 285, '0'), -(138, 36, '执行系统测试', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 290, '0'), -(139, 37, '验收准备', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 295, '0'), -(140, 37, '产品打包', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 300, '0'), -(141, 37, '内部培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 305, '0'), -(142, 37, '现场安装调试', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 310, '0'), -(143, 37, '用户使用培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 315, '0'), -(144, 37, '试运行', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 320, '0'), -(145, 37, '客户验收', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 325, '0'), -(146, 38, '制定质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 330, '0'), -(147, 38, '评审质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 335, '0'), -(148, 38, '过程和产品质量检查', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 340, '0'), -(149, 38, '问题跟踪处理', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 345, '0'), -(150, 38, '质量保证总结报告', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 350, '0'), -(151, 39, '组织及配置管理员移交工作', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 355, '0'), -(152, 39, '制定《配置管理计划》', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 360, '0'), -(153, 39, '建立和维护配置库', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 365, '0'), -(154, 39, '配置项管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 370, '0'), -(155, 39, '基线管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 375, '0'), -(156, 39, '变更管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 380, '0'), -(157, 39, '配置审计', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 385, '0'), -(158, 40, '建立项目度量目标', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 390, '0'), -(159, 40, '确定项目度量点', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 395, '0'), -(160, 40, '制定度量分析计划', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 400, '0'), -(161, 40, '收集和分析度量数据', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 405, '0'), -(162, 40, '存储和通报度量分析结果', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 410, '0'), -(163, 41, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:15', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 415, '0'), -(164, 42, '建立评估标准', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 420, '0'), -(165, 42, '确定候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 425, '0'), -(166, 42, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 430, '0'), -(167, 42, '确定评估方法', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 435, '0'), -(168, 42, '评估候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 440, '0'), -(169, 42, '做出决策', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', '1970-01-01 00:00:01', '', '1970-01-01 00:00:01', 445, '0'), -(170, 43, '立项调查,可行性分析,技术预研', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 20, '0'), -(171, 43, '项目预算', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 25, '0'), -(172, 43, '立项决策', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 30, '0'), -(173, 43, '投标/商务洽谈', 'no', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 35, '0'), -(174, 44, '估算', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 40, '0'), -(175, 44, '裁剪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 45, '0'), -(176, 44, '制定项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 50, '0'), -(177, 44, '评审项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 55, '0'), -(178, 45, '日常跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 60, '0'), -(179, 45, '举行周例会', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 65, '0'), -(180, 45, '里程碑评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 70, '0'), -(181, 45, '采取纠正措施', 'no', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 75, '0'), -(182, 46, '制定风险管理计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 80, '0'), -(183, 46, '风险识别', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 85, '0'), -(184, 46, '风险分析', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 90, '0'), -(185, 46, '风险规避和缓解', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 95, '0'), -(186, 46, '风险跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 100, '0'), -(187, 46, '更新组织级“风险列表库”', 'no', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 105, '0'), -(188, 47, '结项准备', 'no', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 110, '0'), -(189, 47, '结项申请', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 115, '0'), -(190, 47, '结项评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 120, '0'), -(191, 47, '资料纳入组织级过程资产库', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 125, '0'), -(192, 48, '量化项目管理', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:34', '', NULL, '', NULL, 130, '0'), -(193, 49, '用户需求获取', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 135, '0'), -(194, 49, '用户需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 140, '0'), -(195, 49, '产品需求定义', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 145, '0'), -(196, 49, '产品需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 150, '0'), -(197, 49, '需求变更控制', 'no', '', '未涉及变更时可不进行', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 155, '0'), -(198, 50, '概要设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 160, '0'), -(199, 50, '评审《概要设计说明书》', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 165, '0'), -(200, 50, '详细设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 170, '0'), -(201, 50, '评审详细设计工作产品', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 175, '0'), -(202, 51, '制定编码阶段计划', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 210, '0'), -(203, 51, '构建数据库', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 215, '0'), -(204, 51, '编码实现', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 220, '0'), -(205, 51, '代码检查', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 225, '0'), -(206, 51, '单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 230, '0'), -(207, 51, '交叉单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 235, '0'), -(208, 51, '系统集成', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 240, '0'), -(209, 51, '编写集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 245, '0'), -(210, 51, '评审集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 250, '0'), -(211, 51, '集成测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 255, '0'), -(212, 51, '缺陷管理', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 260, '0'), -(213, 51, '编写支持文档', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 265, '0'), -(214, 52, '制定《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 270, '0'), -(215, 52, '评审《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 275, '0'), -(216, 52, '编写《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 280, '0'), -(217, 52, '评审《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 285, '0'), -(218, 52, '执行系统测试', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 290, '0'), -(219, 53, '验收准备', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 295, '0'), -(220, 53, '产品打包', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 300, '0'), -(221, 53, '内部培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 305, '0'), -(222, 53, '现场安装调试', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 310, '0'), -(223, 53, '用户使用培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 315, '0'), -(224, 53, '试运行', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 320, '0'), -(225, 53, '客户验收', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 325, '0'), -(226, 54, '制定质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 330, '0'), -(227, 54, '评审质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 335, '0'), -(228, 54, '过程和产品质量检查', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 340, '0'), -(229, 54, '问题跟踪处理', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 345, '0'), -(230, 54, '质量保证总结报告', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 350, '0'), -(231, 55, '组织及配置管理员移交工作', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 355, '0'), -(232, 55, '制定《配置管理计划》', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 360, '0'), -(233, 55, '建立和维护配置库', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 365, '0'), -(234, 55, '配置项管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 370, '0'), -(235, 55, '基线管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 375, '0'), -(236, 55, '变更管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 380, '0'), -(237, 55, '配置审计', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 385, '0'), -(238, 56, '建立项目度量目标', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 390, '0'), -(239, 56, '确定项目度量点', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 395, '0'), -(240, 56, '制定度量分析计划', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 400, '0'), -(241, 56, '收集和分析度量数据', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 405, '0'), -(242, 56, '存储和通报度量分析结果', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 410, '0'), -(243, 57, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:15', '', NULL, '', NULL, 415, '0'), -(244, 58, '建立评估标准', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 420, '0'), -(245, 58, '确定候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 425, '0'), -(246, 58, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 430, '0'), -(247, 58, '确定评估方法', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 435, '0'), -(248, 58, '评估候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 440, '0'), -(249, 58, '做出决策', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 445, '0'), -(250, 59, '立项调查,可行性分析,技术预研', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 20, '0'), -(251, 59, '项目预算', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 25, '0'), -(252, 59, '立项决策', 'yes', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 30, '0'), -(253, 59, '投标/商务洽谈', 'no', '', '', '', '', 'admin', '2020-01-09 10:49:34', '', NULL, '', NULL, 35, '0'), -(254, 60, '估算', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 40, '0'), -(255, 60, '裁剪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 45, '0'), -(256, 60, '制定项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 50, '0'), -(257, 60, '评审项目计划及下属计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:12:17', '', NULL, '', NULL, 55, '0'), -(258, 61, '日常跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 60, '0'), -(259, 61, '举行周例会', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 65, '0'), -(260, 61, '里程碑评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 70, '0'), -(261, 61, '采取纠正措施', 'no', '', '', '', '', 'admin', '2020-01-09 11:13:37', '', NULL, '', NULL, 75, '0'), -(262, 62, '制定风险管理计划', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 80, '0'), -(263, 62, '风险识别', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 85, '0'), -(264, 62, '风险分析', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 90, '0'), -(265, 62, '风险规避和缓解', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 95, '0'), -(266, 62, '风险跟踪', 'yes', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 100, '0'), -(267, 62, '更新组织级“风险列表库”', 'no', '', '', '', '', 'admin', '2020-01-09 11:15:04', '', NULL, '', NULL, 105, '0'), -(268, 63, '结项准备', 'no', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 110, '0'), -(269, 63, '结项申请', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 115, '0'), -(270, 63, '结项评审', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 120, '0'), -(271, 63, '资料纳入组织级过程资产库', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:16', '', NULL, '', NULL, 125, '0'), -(272, 64, '量化项目管理', 'yes', '', '', '', '', 'admin', '2020-01-09 11:16:34', '', NULL, '', NULL, 130, '0'), -(273, 65, '用户需求获取', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 135, '0'), -(274, 65, '用户需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 140, '0'), -(275, 65, '产品需求定义', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 145, '0'), -(276, 65, '产品需求评审', 'yes', '', '', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 150, '0'), -(277, 65, '需求变更控制', 'no', '', '未涉及变更时可不进行', '', '', 'admin', '2020-01-09 13:25:22', '', NULL, '', NULL, 155, '0'), -(278, 66, '概要设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 160, '0'), -(279, 66, '评审《概要设计说明书》', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 165, '0'), -(280, 66, '详细设计', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 170, '0'), -(281, 66, '评审详细设计工作产品', '', '', '', '', '', 'admin', '2020-01-09 13:26:07', '', NULL, '', NULL, 175, '0'), -(282, 67, '制定编码阶段计划', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 210, '0'), -(283, 67, '构建数据库', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 215, '0'), -(284, 67, '编码实现', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 220, '0'), -(285, 67, '代码检查', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 225, '0'), -(286, 67, '单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 230, '0'), -(287, 67, '交叉单元测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 235, '0'), -(288, 67, '系统集成', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 240, '0'), -(289, 67, '编写集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 245, '0'), -(290, 67, '评审集成测试用例', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 250, '0'), -(291, 67, '集成测试', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 255, '0'), -(292, 67, '缺陷管理', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 260, '0'), -(293, 67, '编写支持文档', '', '', '', '', '', 'admin', '2020-01-09 13:27:57', '', NULL, '', NULL, 265, '0'), -(294, 68, '制定《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 270, '0'), -(295, 68, '评审《系统测试计划》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 275, '0'), -(296, 68, '编写《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 280, '0'), -(297, 68, '评审《系统测试用例》', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 285, '0'), -(298, 68, '执行系统测试', '', '', '', '', '', 'admin', '2020-01-09 13:28:25', '', NULL, '', NULL, 290, '0'), -(299, 69, '验收准备', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 295, '0'), -(300, 69, '产品打包', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 300, '0'), -(301, 69, '内部培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 305, '0'), -(302, 69, '现场安装调试', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 310, '0'), -(303, 69, '用户使用培训', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 315, '0'), -(304, 69, '试运行', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 320, '0'), -(305, 69, '客户验收', '', '', '', '', '', 'admin', '2020-01-09 13:29:28', '', NULL, '', NULL, 325, '0'), -(306, 70, '制定质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 330, '0'), -(307, 70, '评审质量保证计划', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 335, '0'), -(308, 70, '过程和产品质量检查', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 340, '0'), -(309, 70, '问题跟踪处理', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 345, '0'), -(310, 70, '质量保证总结报告', '', '', '', '', '', 'admin', '2020-01-09 13:38:58', '', NULL, '', NULL, 350, '0'), -(311, 71, '组织及配置管理员移交工作', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 355, '0'), -(312, 71, '制定《配置管理计划》', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 360, '0'), -(313, 71, '建立和维护配置库', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 365, '0'), -(314, 71, '配置项管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 370, '0'), -(315, 71, '基线管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 375, '0'), -(316, 71, '变更管理', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 380, '0'), -(317, 71, '配置审计', '', '', '', '', '', 'admin', '2020-01-09 13:39:25', '', NULL, '', NULL, 385, '0'), -(318, 72, '建立项目度量目标', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 390, '0'), -(319, 72, '确定项目度量点', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 395, '0'), -(320, 72, '制定度量分析计划', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 400, '0'), -(321, 72, '收集和分析度量数据', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 405, '0'), -(322, 72, '存储和通报度量分析结果', '', '', '', '', '', 'admin', '2020-01-09 13:40:05', '', NULL, '', NULL, 410, '0'), -(323, 73, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:15', '', NULL, '', NULL, 415, '0'), -(324, 74, '建立评估标准', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 420, '0'), -(325, 74, '确定候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 425, '0'), -(326, 74, '原因分析与解决', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 430, '0'), -(327, 74, '确定评估方法', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 435, '0'), -(328, 74, '评估候选方案', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 440, '0'), -(329, 74, '做出决策', '', '', '', '', '', 'admin', '2020-01-09 13:40:35', '', NULL, '', NULL, 445, '0'); - -REPLACE INTO `zt_zoutput` (`id`, `activity`, `name`, `content`, `optional`, `tailorNorm`, `status`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `order`, `deleted`) VALUES -(5, 4, '《技术可行性分析报告》', '', '', '', '', 'admin', '2020-01-09 11:01:52', '', '1970-01-01 00:00:01', 25, '0'), -(6, 5, '《项目预算表》', '', '', '', '', 'admin', '2020-01-09 11:17:27', '', '1970-01-01 00:00:01', 30, '0'), -(7, 6, '《立项申请报告》', '', '', '', '', 'admin', '2020-01-09 11:17:50', '', '1970-01-01 00:00:01', 35, '0'), -(8, 7, '《项目投标书》', '不涉及投标', '', '', '', 'admin', '2020-01-09 11:18:12', '', '1970-01-01 00:00:01', 40, '0'), -(9, 8, '《项目估算报告》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', '1970-01-01 00:00:01', 45, '0'), -(10, 8, '《功能点估算》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', '1970-01-01 00:00:01', 50, '0'), -(11, 9, '《PDP说明》', '', '', '', '', 'admin', '2020-01-09 11:18:54', '', '1970-01-01 00:00:01', 55, '0'), -(12, 10, '《项目进度表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 60, '0'), -(13, 10, '《项目计划》及其下属计划', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 65, '0'), -(14, 10, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 70, '0'), -(15, 10, '《项目资源计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 75, '0'), -(16, 10, '《干系人计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 80, '0'), -(17, 10, '《同行评审计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 85, '0'), -(18, 10, '《质量保证计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 90, '0'), -(19, 10, '《配置管理计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 95, '0'), -(20, 11, '《同行评审通知》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', '1970-01-01 00:00:01', 100, '0'), -(21, 11, '《评审准备表》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', '1970-01-01 00:00:01', 105, '0'), -(22, 11, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 11:20:17', '', '1970-01-01 00:00:01', 110, '0'), -(23, 12, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 115, '0'), -(24, 12, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 120, '0'), -(25, 13, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 125, '0'), -(26, 13, '《会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 130, '0'), -(27, 14, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 135, '0'), -(28, 14, '《里程碑会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 140, '0'), -(29, 15, '《计划变更申请》', '', 'no', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 145, '0'), -(30, 16, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 150, '0'), -(31, 17, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 155, '0'), -(32, 17, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 160, '0'), -(33, 18, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 165, '0'), -(34, 19, '《异常处理申请表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 170, '0'), -(35, 19, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 175, '0'), -(36, 20, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 180, '0'), -(37, 20, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 185, '0'), -(38, 21, '《风险列表库》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 190, '0'), -(39, 22, '《代码复用总结》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', '1970-01-01 00:00:01', 195, '0'), -(40, 24, '《结项报告》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', '1970-01-01 00:00:01', 200, '0'), -(41, 26, '《量化项目管理及跟踪计划》', '', 'yes', '', '', 'admin', '2020-01-09 13:46:46', '', '1970-01-01 00:00:01', 205, '0'), -(42, 27, '《用户需求调查单》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 210, '0'), -(43, 27, '《用户需求说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 215, '0'), -(44, 28, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 220, '0'), -(45, 28, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 225, '0'), -(46, 28, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 230, '0'), -(47, 29, '《产品需求规格说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 235, '0'), -(48, 30, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 240, '0'), -(49, 30, ' 《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 245, '0'), -(50, 30, ' 《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 250, '0'), -(51, 31, '《需求变更记录》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 255, '0'), -(52, 31, '《用户需求说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 260, '0'), -(53, 31, '《软件需求规格说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 265, '0'), -(54, 32, '《概要设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 270, '0'), -(55, 33, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 275, '0'), -(56, 33, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 280, '0'), -(57, 33, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 285, '0'), -(58, 34, '《模块设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 290, '0'), -(59, 34, '《数据库设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 295, '0'), -(60, 34, '《用户界面设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 300, '0'), -(61, 35, '《评审通知》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 305, '0'), -(62, 35, '《同行评审准备表》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 310, '0'), -(63, 35, '《同行评审报告》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 315, '0'), -(64, 42, '《实现与测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 320, '0'), -(65, 42, '《单板硬件调试和单元测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 325, '0'), -(66, 42, '《项目进度表》中“实现与测试”部分', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 330, '0'), -(67, 43, '产品数据库', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 335, '0'), -(68, 44, '模块代码', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 340, '0'), -(69, 45, '《代码质量检查记录单》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 345, '0'), -(70, 46, '《单元测试缺陷统计分析报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 350, '0'), -(71, 47, '禅道缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 355, '0'), -(72, 48, '《系统集成说明书》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 360, '0'), -(73, 49, '《集成测试用例》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 365, '0'), -(74, 50, '《评审通知》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 370, '0'), -(75, 50, '《同行评审准备表》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 375, '0'), -(76, 50, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 380, '0'), -(77, 51, '《集成测试报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 385, '0'), -(78, 52, '禅道缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 390, '0'), -(79, 52, '《用户操作手册》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 395, '0'), -(80, 52, '《安装手册》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 400, '0'), -(81, 54, '《系统测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 405, '0'), -(82, 55, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 410, '0'), -(83, 55, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 415, '0'), -(84, 55, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 420, '0'), -(85, 56, '《系统测试用例》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 425, '0'), -(86, 57, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 430, '0'), -(87, 57, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 435, '0'), -(88, 57, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 440, '0'), -(89, 58, '禅道缺陷跟踪系统记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 445, '0'), -(90, 58, '《系统测试分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 450, '0'), -(91, 58, '《缺陷统计分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 455, '0'), -(92, 59, '《产品打包清单》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 460, '0'), -(93, 60, '发布光盘、纸质支持文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 465, '0'), -(94, 61, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 470, '0'), -(95, 62, '《工作联系单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 475, '0'), -(96, 62, '《工作记录单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 480, '0'), -(97, 62, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 485, '0'), -(98, 62, '《安装调试信息》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 490, '0'), -(99, 64, '禅道缺陷记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 495, '0'), -(100, 64, '会议纪要等文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 500, '0'), -(101, 65, '《交接验收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 505, '0'), -(102, 65, '《客户验收报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 510, '0'), -(103, 66, '初步《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 515, '0'), -(104, 67, '详细《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 520, '0'), -(105, 68, '《过程审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 525, '0'), -(106, 68, '《产品审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 530, '0'), -(107, 68, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 535, '0'), -(108, 68, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 540, '0'), -(109, 68, '《QA里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 545, '0'), -(110, 68, '《QA周报》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 550, '0'), -(111, 69, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 555, '0'), -(112, 69, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 560, '0'), -(113, 70, '《QA总结报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 565, '0'), -(114, 72, '《配置管理计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 570, '0'), -(115, 72, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 575, '0'), -(116, 72, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 580, '0'), -(117, 73, '《配置库管理报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 585, '0'), -(118, 74, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 590, '0'), -(119, 75, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 595, '0'), -(120, 76, '《变更申请书》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 600, '0'), -(121, 77, '《配置审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 605, '0'), -(122, 80, '《度量分析计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 610, '0'), -(123, 82, '项目度量数据库', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 615, '0'), -(124, 86, '《量化项目管理及跟踪计划》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 620, '0'), -(125, 84, '《决策分析报告》的决策分析评估表', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 625, '0'), -(126, 83, '《量化项目计划及跟踪表》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 630, '0'), -(127, 87, '《决策分析报告》的评分表', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 635, '0'), -(128, 88, '《决策分析报告》的评分表', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 640, '0'), -(129, 89, '《决策分析报告》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 645, '0'), -(130, 90, '《技术可行性分析报告》', '', '', '', '', 'admin', '2020-01-09 11:01:52', '', '1970-01-01 00:00:01', 25, '0'), -(131, 91, '《项目预算表》', '', '', '', '', 'admin', '2020-01-09 11:17:27', '', '1970-01-01 00:00:01', 30, '0'), -(132, 92, '《立项申请报告》', '', '', '', '', 'admin', '2020-01-09 11:17:50', '', '1970-01-01 00:00:01', 35, '0'), -(133, 93, '《项目投标书》', '不涉及投标', '', '', '', 'admin', '2020-01-09 11:18:12', '', '1970-01-01 00:00:01', 40, '0'), -(134, 94, '《项目估算报告》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', '1970-01-01 00:00:01', 45, '0'), -(135, 94, '《功能点估算》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', '1970-01-01 00:00:01', 50, '0'), -(136, 95, '《PDP说明》', '', '', '', '', 'admin', '2020-01-09 11:18:54', '', '1970-01-01 00:00:01', 55, '0'), -(137, 96, '《项目进度表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 60, '0'), -(138, 96, '《项目计划》及其下属计划', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 65, '0'), -(139, 96, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 70, '0'), -(140, 96, '《项目资源计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 75, '0'), -(141, 96, '《干系人计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 80, '0'), -(142, 96, '《同行评审计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 85, '0'), -(143, 96, '《质量保证计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 90, '0'), -(144, 96, '《配置管理计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', '1970-01-01 00:00:01', 95, '0'), -(145, 97, '《同行评审通知》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', '1970-01-01 00:00:01', 100, '0'), -(146, 97, '《评审准备表》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', '1970-01-01 00:00:01', 105, '0'), -(147, 97, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 11:20:17', '', '1970-01-01 00:00:01', 110, '0'), -(148, 98, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 115, '0'), -(149, 98, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 120, '0'), -(150, 99, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 125, '0'), -(151, 99, '《会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 130, '0'), -(152, 100, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 135, '0'), -(153, 100, '《里程碑会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 140, '0'), -(154, 101, '《计划变更申请》', '', 'no', '', '', 'admin', '2020-01-09 13:44:11', '', '1970-01-01 00:00:01', 145, '0'), -(155, 102, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 150, '0'), -(156, 103, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 155, '0'), -(157, 103, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 160, '0'), -(158, 104, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 165, '0'), -(159, 105, '《异常处理申请表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 170, '0'), -(160, 105, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 175, '0'), -(161, 106, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 180, '0'), -(162, 106, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 185, '0'), -(163, 107, '《风险列表库》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', '1970-01-01 00:00:01', 190, '0'), -(164, 108, '《代码复用总结》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', '1970-01-01 00:00:01', 195, '0'), -(165, 110, '《结项报告》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', '1970-01-01 00:00:01', 200, '0'), -(166, 112, '《量化项目管理及跟踪计划》', '', 'yes', '', '', 'admin', '2020-01-09 13:46:46', '', '1970-01-01 00:00:01', 205, '0'), -(167, 113, '《用户需求调查单》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 210, '0'), -(168, 113, '《用户需求说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 215, '0'), -(169, 114, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 220, '0'), -(170, 114, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 225, '0'), -(171, 114, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 230, '0'), -(172, 115, '《产品需求规格说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 235, '0'), -(173, 116, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 240, '0'), -(174, 116, ' 《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 245, '0'), -(175, 116, ' 《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 250, '0'), -(176, 117, '《需求变更记录》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 255, '0'), -(177, 117, '《用户需求说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 260, '0'), -(178, 117, '《软件需求规格说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', '1970-01-01 00:00:01', 265, '0'), -(179, 118, '《概要设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 270, '0'), -(180, 119, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 275, '0'), -(181, 119, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 280, '0'), -(182, 119, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 285, '0'), -(183, 120, '《模块设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 290, '0'), -(184, 120, '《数据库设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 295, '0'), -(185, 120, '《用户界面设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 300, '0'), -(186, 121, '《评审通知》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 305, '0'), -(187, 121, '《同行评审准备表》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 310, '0'), -(188, 121, '《同行评审报告》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', '1970-01-01 00:00:01', 315, '0'), -(189, 128, '《实现与测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 320, '0'), -(190, 128, '《单板硬件调试和单元测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 325, '0'), -(191, 128, '《项目进度表》中“实现与测试”部分', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 330, '0'), -(192, 129, '产品数据库', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 335, '0'), -(193, 130, '模块代码', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 340, '0'), -(194, 131, '《代码质量检查记录单》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 345, '0'), -(195, 132, '《单元测试缺陷统计分析报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 350, '0'), -(196, 133, 'bugzilla缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 355, '0'), -(197, 134, '《系统集成说明书》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 360, '0'), -(198, 135, '《集成测试用例》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 365, '0'), -(199, 136, '《评审通知》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 370, '0'), -(200, 136, '《同行评审准备表》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 375, '0'), -(201, 136, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 380, '0'), -(202, 137, '《集成测试报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 385, '0'), -(203, 138, 'bugzilla缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 390, '0'), -(204, 138, '《用户操作手册》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 395, '0'), -(205, 138, '《安装手册》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', '1970-01-01 00:00:01', 400, '0'), -(206, 140, '《系统测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 405, '0'), -(207, 141, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 410, '0'), -(208, 141, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 415, '0'), -(209, 141, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 420, '0'), -(210, 142, '《系统测试用例》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 425, '0'), -(211, 143, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 430, '0'), -(212, 143, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 435, '0'), -(213, 143, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 440, '0'), -(214, 144, 'bugzilla缺陷跟踪系统记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 445, '0'), -(215, 144, '《系统测试分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 450, '0'), -(216, 144, '《缺陷统计分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 455, '0'), -(217, 145, '《产品打包清单》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', '1970-01-01 00:00:01', 460, '0'), -(218, 146, '发布光盘、纸质支持文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 465, '0'), -(219, 147, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 470, '0'), -(220, 148, '《工作联系单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 475, '0'), -(221, 148, '《工作记录单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 480, '0'), -(222, 148, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 485, '0'), -(223, 148, '《安装调试信息》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 490, '0'), -(224, 150, 'bugzilla缺陷记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 495, '0'), -(225, 150, '会议纪要等文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 500, '0'), -(226, 151, '《交接验收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 505, '0'), -(227, 151, '《客户验收报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', '1970-01-01 00:00:01', 510, '0'), -(228, 152, '初步《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 515, '0'), -(229, 153, '详细《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 520, '0'), -(230, 154, '《过程审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 525, '0'), -(231, 154, '《产品审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 530, '0'), -(232, 154, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 535, '0'), -(233, 154, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 540, '0'), -(234, 154, '《QA里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 545, '0'), -(235, 154, '《QA周报》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 550, '0'), -(236, 155, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 555, '0'), -(237, 155, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 560, '0'), -(238, 156, '《QA总结报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', '1970-01-01 00:00:01', 565, '0'), -(239, 158, '《配置管理计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 570, '0'), -(240, 158, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 575, '0'), -(241, 158, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 580, '0'), -(242, 159, '《配置库管理报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 585, '0'), -(243, 160, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 590, '0'), -(244, 161, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 595, '0'), -(245, 162, '《变更申请书》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 600, '0'), -(246, 163, '《配置审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 605, '0'), -(247, 166, '《度量分析计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 610, '0'), -(248, 168, '项目度量数据库', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', '1970-01-01 00:00:01', 615, '0'), -(249, 169, '《量化项目计划及跟踪表》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', '1970-01-01 00:00:01', 630, '0'), -(250, 170, '《技术可行性分析报告》', '', '', '', '', 'admin', '2020-01-09 11:01:52', '', NULL, 25, '0'), -(251, 171, '《项目预算表》', '', '', '', '', 'admin', '2020-01-09 11:17:27', '', NULL, 30, '0'), -(252, 172, '《立项申请报告》', '', '', '', '', 'admin', '2020-01-09 11:17:50', '', NULL, 35, '0'), -(253, 173, '《项目投标书》', '不涉及投标', '', '', '', 'admin', '2020-01-09 11:18:12', '', NULL, 40, '0'), -(254, 174, '《项目估算报告》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', NULL, 45, '0'), -(255, 174, '《功能点估算》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', NULL, 50, '0'), -(256, 175, '《PDP说明》', '', '', '', '', 'admin', '2020-01-09 11:18:54', '', NULL, 55, '0'), -(257, 176, '《项目进度表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 60, '0'), -(258, 176, '《项目计划》及其下属计划', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 65, '0'), -(259, 176, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 70, '0'), -(260, 176, '《项目资源计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 75, '0'), -(261, 176, '《干系人计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 80, '0'), -(262, 176, '《同行评审计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 85, '0'), -(263, 176, '《质量保证计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 90, '0'), -(264, 176, '《配置管理计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 95, '0'), -(265, 177, '《同行评审通知》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', NULL, 100, '0'), -(266, 177, '《评审准备表》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', NULL, 105, '0'), -(267, 177, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 11:20:17', '', NULL, 110, '0'), -(268, 178, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 115, '0'), -(269, 178, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 120, '0'), -(270, 179, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 125, '0'), -(271, 179, '《会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 130, '0'), -(272, 180, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 135, '0'), -(273, 180, '《里程碑会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 140, '0'), -(274, 181, '《计划变更申请》', '', 'no', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 145, '0'), -(275, 182, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 150, '0'), -(276, 183, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 155, '0'), -(277, 183, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 160, '0'), -(278, 184, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 165, '0'), -(279, 185, '《异常处理申请表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 170, '0'), -(280, 185, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 175, '0'), -(281, 186, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 180, '0'), -(282, 186, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 185, '0'), -(283, 187, '《风险列表库》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 190, '0'), -(284, 188, '《代码复用总结》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', NULL, 195, '0'), -(285, 190, '《结项报告》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', NULL, 200, '0'), -(286, 192, '《量化项目管理及跟踪计划》', '', 'yes', '', '', 'admin', '2020-01-09 13:46:46', '', NULL, 205, '0'), -(287, 193, '《用户需求调查单》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 210, '0'), -(288, 193, '《用户需求说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 215, '0'), -(289, 194, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 220, '0'), -(290, 194, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 225, '0'), -(291, 194, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 230, '0'), -(292, 195, '《产品需求规格说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 235, '0'), -(293, 196, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 240, '0'), -(294, 196, ' 《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 245, '0'), -(295, 196, ' 《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 250, '0'), -(296, 197, '《需求变更记录》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 255, '0'), -(297, 197, '《用户需求说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 260, '0'), -(298, 197, '《软件需求规格说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 265, '0'), -(299, 198, '《概要设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 270, '0'), -(300, 199, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 275, '0'), -(301, 199, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 280, '0'), -(302, 199, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 285, '0'), -(303, 200, '《模块设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 290, '0'), -(304, 200, '《数据库设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 295, '0'), -(305, 200, '《用户界面设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 300, '0'), -(306, 201, '《评审通知》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 305, '0'), -(307, 201, '《同行评审准备表》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 310, '0'), -(308, 201, '《同行评审报告》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 315, '0'), -(309, 202, '《实现与测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 320, '0'), -(310, 202, '《单板硬件调试和单元测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 325, '0'), -(311, 202, '《项目进度表》中“实现与测试”部分', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 330, '0'), -(312, 203, '产品数据库', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 335, '0'), -(313, 204, '模块代码', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 340, '0'), -(314, 205, '《代码质量检查记录单》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 345, '0'), -(315, 206, '《单元测试缺陷统计分析报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 350, '0'), -(316, 207, '禅道缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 355, '0'), -(317, 208, '《系统集成说明书》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 360, '0'), -(318, 209, '《集成测试用例》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 365, '0'), -(319, 210, '《评审通知》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 370, '0'), -(320, 210, '《同行评审准备表》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 375, '0'), -(321, 210, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 380, '0'), -(322, 211, '《集成测试报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 385, '0'), -(323, 212, '禅道缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 390, '0'), -(324, 212, '《用户操作手册》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 395, '0'), -(325, 212, '《安装手册》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 400, '0'), -(326, 214, '《系统测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 405, '0'), -(327, 215, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 410, '0'), -(328, 215, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 415, '0'), -(329, 215, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 420, '0'), -(330, 216, '《系统测试用例》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 425, '0'), -(331, 217, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 430, '0'), -(332, 217, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 435, '0'), -(333, 217, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 440, '0'), -(334, 218, '禅道缺陷跟踪系统记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 445, '0'), -(335, 218, '《系统测试分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 450, '0'), -(336, 218, '《缺陷统计分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 455, '0'), -(337, 219, '《产品打包清单》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 460, '0'), -(338, 220, '发布光盘、纸质支持文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 465, '0'), -(339, 221, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 470, '0'), -(340, 222, '《工作联系单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 475, '0'), -(341, 222, '《工作记录单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 480, '0'), -(342, 222, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 485, '0'), -(343, 222, '《安装调试信息》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 490, '0'), -(344, 224, '禅道缺陷记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 495, '0'), -(345, 224, '会议纪要等文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 500, '0'), -(346, 225, '《交接验收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 505, '0'), -(347, 225, '《客户验收报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 510, '0'), -(348, 226, '初步《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 515, '0'), -(349, 227, '详细《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 520, '0'), -(350, 228, '《过程审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 525, '0'), -(351, 228, '《产品审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 530, '0'), -(352, 228, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 535, '0'), -(353, 228, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 540, '0'), -(354, 228, '《QA里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 545, '0'), -(355, 228, '《QA周报》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 550, '0'), -(356, 229, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 555, '0'), -(357, 229, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 560, '0'), -(358, 230, '《QA总结报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 565, '0'), -(359, 232, '《配置管理计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 570, '0'), -(360, 232, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 575, '0'), -(361, 232, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 580, '0'), -(362, 233, '《配置库管理报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 585, '0'), -(363, 234, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 590, '0'), -(364, 235, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 595, '0'), -(365, 236, '《变更申请书》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 600, '0'), -(366, 237, '《配置审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 605, '0'), -(367, 230, '《度量分析计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 610, '0'), -(368, 232, '项目度量数据库', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 615, '0'), -(369, 236, '《量化项目管理及跟踪计划》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 620, '0'), -(370, 234, '《决策分析报告》的决策分析评估表', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 625, '0'), -(371, 233, '《量化项目计划及跟踪表》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 630, '0'), -(372, 237, '《决策分析报告》的评分表', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 635, '0'), -(373, 238, '《决策分析报告》的评分表', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 640, '0'), -(374, 239, '《决策分析报告》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 645, '0'), -(375, 250, '《技术可行性分析报告》', '', '', '', '', 'admin', '2020-01-09 11:01:52', '', NULL, 25, '0'), -(376, 251, '《项目预算表》', '', '', '', '', 'admin', '2020-01-09 11:17:27', '', NULL, 30, '0'), -(377, 252, '《立项申请报告》', '', '', '', '', 'admin', '2020-01-09 11:17:50', '', NULL, 35, '0'), -(378, 253, '《项目投标书》', '不涉及投标', '', '', '', 'admin', '2020-01-09 11:18:12', '', NULL, 40, '0'), -(379, 254, '《项目估算报告》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', NULL, 45, '0'), -(380, 254, '《功能点估算》', '', '', '', '', 'admin', '2020-01-09 11:18:41', '', NULL, 50, '0'), -(381, 255, '《PDP说明》', '', '', '', '', 'admin', '2020-01-09 11:18:54', '', NULL, 55, '0'), -(382, 256, '《项目进度表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 60, '0'), -(383, 256, '《项目计划》及其下属计划', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 65, '0'), -(384, 256, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 70, '0'), -(385, 256, '《项目资源计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 75, '0'), -(386, 256, '《干系人计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 80, '0'), -(387, 256, '《同行评审计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 85, '0'), -(388, 256, '《质量保证计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 90, '0'), -(389, 256, '《配置管理计划》', '', '', '', '', 'admin', '2020-01-09 11:19:46', '', NULL, 95, '0'), -(390, 257, '《同行评审通知》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', NULL, 100, '0'), -(391, 257, '《评审准备表》', '', '', '', '', 'admin', '2020-01-09 11:20:16', '', NULL, 105, '0'), -(392, 257, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 11:20:17', '', NULL, 110, '0'), -(393, 258, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 115, '0'), -(394, 258, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 120, '0'), -(395, 259, '《项目周报》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 125, '0'), -(396, 259, '《会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 130, '0'), -(397, 260, '《里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 135, '0'), -(398, 260, '《里程碑会议纪要》', '', 'yes', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 140, '0'), -(399, 261, '《计划变更申请》', '', 'no', '', '', 'admin', '2020-01-09 13:44:11', '', NULL, 145, '0'), -(400, 262, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 150, '0'), -(401, 263, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 155, '0'), -(402, 263, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 160, '0'), -(403, 264, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 165, '0'), -(404, 265, '《异常处理申请表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 170, '0'), -(405, 265, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 175, '0'), -(406, 266, '《风险管理报告》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 180, '0'), -(407, 266, '《风险管理计划及跟踪表》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 185, '0'), -(408, 267, '《风险列表库》', '', '', '', '', 'admin', '2020-01-09 13:45:53', '', NULL, 190, '0'), -(409, 268, '《代码复用总结》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', NULL, 195, '0'), -(410, 270, '《结项报告》', '', '', '', '', 'admin', '2020-01-09 13:46:26', '', NULL, 200, '0'), -(411, 272, '《量化项目管理及跟踪计划》', '', 'yes', '', '', 'admin', '2020-01-09 13:46:46', '', NULL, 205, '0'), -(412, 273, '《用户需求调查单》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 210, '0'), -(413, 273, '《用户需求说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 215, '0'), -(414, 274, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 220, '0'), -(415, 274, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 225, '0'), -(416, 274, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 230, '0'), -(417, 275, '《产品需求规格说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 235, '0'), -(418, 276, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 240, '0'), -(419, 276, ' 《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 245, '0'), -(420, 276, ' 《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 250, '0'), -(421, 277, '《需求变更记录》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 255, '0'), -(422, 277, '《用户需求说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 260, '0'), -(423, 277, '《软件需求规格说明书》', '', '', '', '', 'admin', '2020-01-09 14:08:58', '', NULL, 265, '0'), -(424, 278, '《概要设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 270, '0'), -(425, 279, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 275, '0'), -(426, 279, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 280, '0'), -(427, 279, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 285, '0'), -(428, 280, '《模块设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 290, '0'), -(429, 280, '《数据库设计说明书》', '', 'yes', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 295, '0'), -(430, 280, '《用户界面设计说明书》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 300, '0'), -(431, 281, '《评审通知》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 305, '0'), -(432, 281, '《同行评审准备表》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 310, '0'), -(433, 281, '《同行评审报告》', '', 'no', '', '', 'admin', '2020-01-09 14:20:09', '', NULL, 315, '0'), -(434, 288, '《实现与测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 320, '0'), -(435, 288, '《单板硬件调试和单元测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 325, '0'), -(436, 288, '《项目进度表》中“实现与测试”部分', '', 'yes', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 330, '0'), -(437, 289, '产品数据库', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 335, '0'), -(438, 290, '模块代码', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 340, '0'), -(439, 291, '《代码质量检查记录单》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 345, '0'), -(440, 292, '《单元测试缺陷统计分析报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 350, '0'), -(441, 293, 'bugzilla缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 355, '0'), -(442, 294, '《系统集成说明书》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 360, '0'), -(443, 295, '《集成测试用例》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 365, '0'), -(444, 296, '《评审通知》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 370, '0'), -(445, 296, '《同行评审准备表》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 375, '0'), -(446, 296, '《同行评审报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 380, '0'), -(447, 297, '《集成测试报告》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 385, '0'), -(448, 298, 'bugzilla缺陷记录', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 390, '0'), -(449, 298, '《用户操作手册》 ', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 395, '0'), -(450, 298, '《安装手册》', '', '', '', '', 'admin', '2020-01-09 14:22:52', '', NULL, 400, '0'), -(451, 300, '《系统测试计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 405, '0'), -(452, 301, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 410, '0'), -(453, 301, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 415, '0'), -(454, 301, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 420, '0'), -(455, 302, '《系统测试用例》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 425, '0'), -(456, 303, '《评审通知》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 430, '0'), -(457, 303, '《同行评审准备表》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 435, '0'), -(458, 303, '《同行评审报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 440, '0'), -(459, 304, 'bugzilla缺陷跟踪系统记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 445, '0'), -(460, 304, '《系统测试分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 450, '0'), -(461, 304, '《缺陷统计分析报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 455, '0'), -(462, 305, '《产品打包清单》', '', 'yes', '', '', 'admin', '2020-01-09 14:25:16', '', NULL, 460, '0'), -(463, 306, '发布光盘、纸质支持文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 465, '0'), -(464, 307, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 470, '0'), -(465, 308, '《工作联系单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 475, '0'), -(466, 308, '《工作记录单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 480, '0'), -(467, 308, '《交接签收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 485, '0'), -(468, 308, '《安装调试信息》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 490, '0'), -(469, 310, 'bugzilla缺陷记录项', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 495, '0'), -(470, 310, '会议纪要等文档', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 500, '0'), -(471, 311, '《交接验收单》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 505, '0'), -(472, 311, '《客户验收报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:26:52', '', NULL, 510, '0'), -(473, 312, '初步《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 515, '0'), -(474, 313, '详细《质量保证计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 520, '0'), -(475, 314, '《过程审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 525, '0'), -(476, 314, '《产品审计检查表》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 530, '0'), -(477, 314, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 535, '0'), -(478, 314, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 540, '0'), -(479, 314, '《QA里程碑报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 545, '0'), -(480, 314, '《QA周报》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 550, '0'), -(481, 315, '《QA审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 555, '0'), -(482, 315, '《QA问题报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 560, '0'), -(483, 316, '《QA总结报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:28:18', '', NULL, 565, '0'), -(484, 318, '《配置管理计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 570, '0'), -(485, 318, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 575, '0'), -(486, 318, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 580, '0'), -(487, 319, '《配置库管理报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 585, '0'), -(488, 320, '《配置项计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 590, '0'), -(489, 321, '《基线计划及跟踪表》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 595, '0'), -(490, 322, '《变更申请书》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 600, '0'), -(491, 323, '《配置审计报告》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 605, '0'), -(492, 326, '《度量分析计划》', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 610, '0'), -(493, 328, '项目度量数据库', '', 'yes', '', '', 'admin', '2020-01-09 14:55:08', '', NULL, 615, '0'), -(494, 329, '《量化项目计划及跟踪表》', '', 'no', '', '', 'admin', '2020-01-09 14:59:04', '', NULL, 630, '0'); +(1, '0', '2', 'charter', '0', 'projectApproval'), +(2, '0', '3', 'charter', '0', 'completionApproval'), +(3, '0', '4', 'charter', '0', 'cancelProjectApproval'), +(4, '0', '5', 'charter', '0', 'activateProjectApproval'); -- DROP TABLE IF EXISTS `zt_pivot`; CREATE TABLE IF NOT EXISTS `zt_pivot` ( @@ -15681,78 +14919,125 @@ INSERT INTO `zt_dimension`(`id`, `name`, `code`, `desc`, `createdBy`, `createdDa (2, '效能管理维度', 'efficiency', '', 'system', '2023-04-27 20:22:16', '', NULL, '0'), (3, '质量管理维度', 'quality', '', 'system', '2023-04-27 20:22:16', '', NULL, '0'); -INSERT INTO `zt_module`(`id`, `root`, `branch`, `name`, `parent`, `path`, `grade`, `order`, `type`, `from`, `owner`, `collector`, `short`, `deleted`) VALUES -(32, 1, 0, '产品', 0, ',32,', 1, 10, 'chart', 0, 'system', 'product', '', '0'), -(33, 1, 0, '项目', 0, ',33,', 1, 20, 'chart', 0, 'system', 'project', '', '0'), -(34, 1, 0, '测试', 0, ',34,', 1, 30, 'chart', 0, 'system', 'test', '', '0'), -(35, 1, 0, '组织', 0, ',35,', 1, 40, 'chart', 0, 'system', 'staff', '', '0'), -(36, 1, 0, '需求', 32, ',32,36,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(37, 1, 0, '发布', 32, ',32,37,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(38, 1, 0, '项目', 33, ',33,38,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(39, 1, 0, '任务', 33, ',33,39,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(40, 1, 0, '迭代', 33, ',33,40,', 2, 30, 'chart', 0, '', NULL, '', '0'), -(41, 1, 0, '成本', 33, ',33,41,', 2, 40, 'chart', 0, '', NULL, '', '0'), -(42, 1, 0, '工期', 33, ',33,42,', 2, 50, 'chart', 0, '', NULL, '', '0'), -(43, 1, 0, '需求', 33, ',33,43,', 2, 60, 'chart', 0, '', NULL, '', '0'), -(44, 1, 0, 'Bug', 34, ',34,44,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(45, 1, 0, '项目集', 35, ',35,45,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(46, 1, 0, '项目', 35, ',35,46,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(47, 1, 0, '产品', 35, ',35,47,', 2, 30, 'chart', 0, '', NULL, '', '0'), -(48, 1, 0, '计划', 35, ',35,48,', 2, 40, 'chart', 0, '', NULL, '', '0'), -(49, 1, 0, '迭代', 35, ',35,49,', 2, 50, 'chart', 0, '', NULL, '', '0'), -(50, 1, 0, '发布', 35, ',35,50,', 2, 60, 'chart', 0, '', NULL, '', '0'), -(51, 1, 0, '需求', 35, ',35,51,', 2, 70, 'chart', 0, '', NULL, '', '0'), -(52, 1, 0, '任务', 35, ',35,52,', 2, 80, 'chart', 0, '', NULL, '', '0'), -(53, 1, 0, 'Bug', 35, ',35,53,', 2, 90, 'chart', 0, '', NULL, '', '0'), -(54, 1, 0, '文档', 35, ',35,54,', 2, 100, 'chart', 0, '', NULL, '', '0'), -(55, 1, 0, '成本', 35, ',35,55,', 2, 110, 'chart', 0, '', NULL, '', '0'), -(56, 1, 0, '人员', 35, ',35,56,', 2, 120, 'chart', 0, '', NULL, '', '0'), -(57, 1, 0, '工时', 35, ',35,57,', 2, 130, 'chart', 0, '', NULL, '', '0'), -(58, 1, 0, '行为', 35, ',35,58,', 2, 140, 'chart', 0, '', NULL, '', '0'), -(59, 1, 0, '产品', 0, ',59,', 1, 10, 'pivot', 0, 'system', 'product', '', '0'), -(60, 1, 0, '项目', 0, ',60,', 1, 20, 'pivot', 0, 'system', 'project', '', '0'), -(61, 1, 0, '测试', 0, ',61,', 1, 30, 'pivot', 0, 'system', 'test', '', '0'), -(62, 1, 0, '组织', 0, ',62,', 1, 40, 'pivot', 0, 'system', 'staff', '', '0'), -(63, 1, 0, '产品', 59, ',59,63,', 2, 10, 'pivot', 0, '', NULL, '', '0'), -(64, 1, 0, '项目集', 62, ',62,64,', 2, 10, 'pivot', 0, '', NULL, '', '0'), -(65, 2, 0, '产品', 0, ',65,', 1, 10, 'chart', 0, 'system', 'product', '', '0'), -(66, 2, 0, '项目', 0, ',66,', 1, 20, 'chart', 0, 'system', 'project', '', '0'), -(67, 2, 0, '测试', 0, ',67,', 1, 30, 'chart', 0, 'system', 'test', '', '0'), -(68, 2, 0, '组织', 0, ',68,', 1, 40, 'chart', 0, 'system', 'staff', '', '0'), -(69, 2, 0, '进度', 66, ',66,69,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(70, 2, 0, '成本', 66, ',66,70,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(71, 2, 0, '工期', 66, ',66,71,', 2, 30, 'chart', 0, '', NULL, '', '0'), -(72, 2, 0, '项目', 68, ',68,72,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(73, 2, 0, '迭代', 68, ',68,73,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(74, 2, 0, '发布', 68, ',68,74,', 2, 30, 'chart', 0, '', NULL, '', '0'), -(75, 2, 0, '需求', 68, ',68,75,', 2, 40, 'chart', 0, '', NULL, '', '0'), -(76, 2, 0, '任务', 68, ',68,76,', 2, 50, 'chart', 0, '', NULL, '', '0'), -(77, 2, 0, 'Bug', 68, ',68,77,', 2, 60, 'chart', 0, '', NULL, '', '0'), -(78, 2, 0, '成本', 68, ',68,78,', 2, 70, 'chart', 0, '', NULL, '', '0'), -(79, 2, 0, '产品', 0, ',79,', 1, 10, 'pivot', 0, 'system', 'product', '', '0'), -(80, 2, 0, '项目', 0, ',80,', 1, 20, 'pivot', 0, 'system', 'project', '', '0'), -(81, 2, 0, '测试', 0, ',81,', 1, 30, 'pivot', 0, 'system', 'test', '', '0'), -(82, 2, 0, '组织', 0, ',82,', 1, 40, 'pivot', 0, 'system', 'staff', '', '0'), -(83, 2, 0, '项目', 80, ',80,83,', 2, 10, 'pivot', 0, '', NULL, '', '0'), -(84, 2, 0, '进度', 80, ',80,84,', 2, 20, 'pivot', 0, '', NULL, '', '0'), -(85, 2, 0, '成本', 80, ',80,85,', 2, 30, 'pivot', 0, '', NULL, '', '0'), -(86, 2, 0, '工期', 80, ',80,86,', 2, 40, 'pivot', 0, '', NULL, '', '0'), -(87, 3, 0, '产品', 0, ',87,', 1, 10, 'chart', 0, 'system', 'product', '', '0'), -(88, 3, 0, '项目', 0, ',88,', 1, 20, 'chart', 0, 'system', 'project', '', '0'), -(89, 3, 0, '测试', 0, ',89,', 1, 30, 'chart', 0, 'system', 'test', '', '0'), -(90, 3, 0, '组织', 0, ',90,', 1, 40, 'chart', 0, 'system', 'staff', '', '0'), -(91, 3, 0, 'Bug', 89, ',89,91,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(92, 3, 0, '用例', 89, ',89,92,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(93, 3, 0, '需求', 90, ',90,93,', 2, 10, 'chart', 0, '', NULL, '', '0'), -(94, 3, 0, 'Bug', 90, ',90,94,', 2, 20, 'chart', 0, '', NULL, '', '0'), -(95, 3, 0, '用例', 90, ',90,95,', 2, 30, 'chart', 0, '', NULL, '', '0'), -(96, 3, 0, '产品', 0, ',96,', 1, 10, 'pivot', 0, 'system', 'product', '', '0'), -(97, 3, 0, '项目', 0, ',97,', 1, 20, 'pivot', 0, 'system', 'project', '', '0'), -(98, 3, 0, '测试', 0, ',98,', 1, 30, 'pivot', 0, 'system', 'test', '', '0'), -(99, 3, 0, '组织', 0, ',99,', 1, 40, 'pivot', 0, 'system', 'staff', '', '0'), -(100, 3, 0, 'Bug', 98, ',98,100,', 2, 10, 'pivot', 0, '', NULL, '', '0'), -(102, 1, 0, 'DevOps', 0, ',102,', 1, 50, 'chart', 0, '', NULL, '', '0'), -(103, 1, 0, 'DevOps', 102, ',102,103,', 2, 10, 'chart', 0, '', NULL, '', '0'); +INSERT INTO `zt_module` (`id`, `root`, `branch`, `name`, `parent`, `path`, `grade`, `order`, `type`, `from`, `owner`, `collector`, `short`, `deleted`, `extra`) VALUES +(32, 1, 0, '产品', 0, ',32,', 1, 10, 'chart', 0, 'system', 'product', '', '0', ''), +(33, 1, 0, '项目', 0, ',33,', 1, 20, 'chart', 0, 'system', 'project', '', '0', ''), +(34, 1, 0, '测试', 0, ',34,', 1, 30, 'chart', 0, 'system', 'test', '', '0', ''), +(35, 1, 0, '组织', 0, ',35,', 1, 40, 'chart', 0, 'system', 'staff', '', '0', ''), +(36, 1, 0, '需求', 32, ',32,36,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(37, 1, 0, '发布', 32, ',32,37,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(38, 1, 0, '项目', 33, ',33,38,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(39, 1, 0, '任务', 33, ',33,39,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(40, 1, 0, '迭代', 33, ',33,40,', 2, 30, 'chart', 0, '', NULL, '', '0', ''), +(41, 1, 0, '成本', 33, ',33,41,', 2, 40, 'chart', 0, '', NULL, '', '0', ''), +(42, 1, 0, '工期', 33, ',33,42,', 2, 50, 'chart', 0, '', NULL, '', '0', ''), +(43, 1, 0, '需求', 33, ',33,43,', 2, 60, 'chart', 0, '', NULL, '', '0', ''), +(44, 1, 0, 'Bug', 34, ',34,44,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(45, 1, 0, '项目集', 35, ',35,45,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(46, 1, 0, '项目', 35, ',35,46,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(47, 1, 0, '产品', 35, ',35,47,', 2, 30, 'chart', 0, '', NULL, '', '0', ''), +(48, 1, 0, '计划', 35, ',35,48,', 2, 40, 'chart', 0, '', NULL, '', '0', ''), +(49, 1, 0, '迭代', 35, ',35,49,', 2, 50, 'chart', 0, '', NULL, '', '0', ''), +(50, 1, 0, '发布', 35, ',35,50,', 2, 60, 'chart', 0, '', NULL, '', '0', ''), +(51, 1, 0, '需求', 35, ',35,51,', 2, 70, 'chart', 0, '', NULL, '', '0', ''), +(52, 1, 0, '任务', 35, ',35,52,', 2, 80, 'chart', 0, '', NULL, '', '0', ''), +(53, 1, 0, 'Bug', 35, ',35,53,', 2, 90, 'chart', 0, '', NULL, '', '0', ''), +(54, 1, 0, '文档', 35, ',35,54,', 2, 100, 'chart', 0, '', NULL, '', '0', ''), +(55, 1, 0, '成本', 35, ',35,55,', 2, 110, 'chart', 0, '', NULL, '', '0', ''), +(56, 1, 0, '人员', 35, ',35,56,', 2, 120, 'chart', 0, '', NULL, '', '0', ''), +(57, 1, 0, '工时', 35, ',35,57,', 2, 130, 'chart', 0, '', NULL, '', '0', ''), +(58, 1, 0, '行为', 35, ',35,58,', 2, 140, 'chart', 0, '', NULL, '', '0', ''), +(59, 1, 0, '产品', 0, ',59,', 1, 10, 'pivot', 0, 'system', 'product', '', '0', ''), +(60, 1, 0, '项目', 0, ',60,', 1, 20, 'pivot', 0, 'system', 'project', '', '0', ''), +(61, 1, 0, '测试', 0, ',61,', 1, 30, 'pivot', 0, 'system', 'test', '', '0', ''), +(62, 1, 0, '组织', 0, ',62,', 1, 40, 'pivot', 0, 'system', 'staff', '', '0', ''), +(63, 1, 0, '产品', 59, ',59,63,', 2, 10, 'pivot', 0, '', NULL, '', '0', ''), +(64, 1, 0, '项目集', 62, ',62,64,', 2, 10, 'pivot', 0, '', NULL, '', '0', ''), +(65, 2, 0, '产品', 0, ',65,', 1, 10, 'chart', 0, 'system', 'product', '', '0', ''), +(66, 2, 0, '项目', 0, ',66,', 1, 20, 'chart', 0, 'system', 'project', '', '0', ''), +(67, 2, 0, '测试', 0, ',67,', 1, 30, 'chart', 0, 'system', 'test', '', '0', ''), +(68, 2, 0, '组织', 0, ',68,', 1, 40, 'chart', 0, 'system', 'staff', '', '0', ''), +(69, 2, 0, '进度', 66, ',66,69,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(70, 2, 0, '成本', 66, ',66,70,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(71, 2, 0, '工期', 66, ',66,71,', 2, 30, 'chart', 0, '', NULL, '', '0', ''), +(72, 2, 0, '项目', 68, ',68,72,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(73, 2, 0, '迭代', 68, ',68,73,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(74, 2, 0, '发布', 68, ',68,74,', 2, 30, 'chart', 0, '', NULL, '', '0', ''), +(75, 2, 0, '需求', 68, ',68,75,', 2, 40, 'chart', 0, '', NULL, '', '0', ''), +(76, 2, 0, '任务', 68, ',68,76,', 2, 50, 'chart', 0, '', NULL, '', '0', ''), +(77, 2, 0, 'Bug', 68, ',68,77,', 2, 60, 'chart', 0, '', NULL, '', '0', ''), +(78, 2, 0, '成本', 68, ',68,78,', 2, 70, 'chart', 0, '', NULL, '', '0', ''), +(79, 2, 0, '产品', 0, ',79,', 1, 10, 'pivot', 0, 'system', 'product', '', '0', ''), +(80, 2, 0, '项目', 0, ',80,', 1, 20, 'pivot', 0, 'system', 'project', '', '0', ''), +(81, 2, 0, '测试', 0, ',81,', 1, 30, 'pivot', 0, 'system', 'test', '', '0', ''), +(82, 2, 0, '组织', 0, ',82,', 1, 40, 'pivot', 0, 'system', 'staff', '', '0', ''), +(83, 2, 0, '项目', 80, ',80,83,', 2, 10, 'pivot', 0, '', NULL, '', '0', ''), +(84, 2, 0, '进度', 80, ',80,84,', 2, 20, 'pivot', 0, '', NULL, '', '0', ''), +(85, 2, 0, '成本', 80, ',80,85,', 2, 30, 'pivot', 0, '', NULL, '', '0', ''), +(86, 2, 0, '工期', 80, ',80,86,', 2, 40, 'pivot', 0, '', NULL, '', '0', ''), +(87, 3, 0, '产品', 0, ',87,', 1, 10, 'chart', 0, 'system', 'product', '', '0', ''), +(88, 3, 0, '项目', 0, ',88,', 1, 20, 'chart', 0, 'system', 'project', '', '0', ''), +(89, 3, 0, '测试', 0, ',89,', 1, 30, 'chart', 0, 'system', 'test', '', '0', ''), +(90, 3, 0, '组织', 0, ',90,', 1, 40, 'chart', 0, 'system', 'staff', '', '0', ''), +(91, 3, 0, 'Bug', 89, ',89,91,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(92, 3, 0, '用例', 89, ',89,92,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(93, 3, 0, '需求', 90, ',90,93,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(94, 3, 0, 'Bug', 90, ',90,94,', 2, 20, 'chart', 0, '', NULL, '', '0', ''), +(95, 3, 0, '用例', 90, ',90,95,', 2, 30, 'chart', 0, '', NULL, '', '0', ''), +(96, 3, 0, '产品', 0, ',96,', 1, 10, 'pivot', 0, 'system', 'product', '', '0', ''), +(97, 3, 0, '项目', 0, ',97,', 1, 20, 'pivot', 0, 'system', 'project', '', '0', ''), +(98, 3, 0, '测试', 0, ',98,', 1, 30, 'pivot', 0, 'system', 'test', '', '0', ''), +(99, 3, 0, '组织', 0, ',99,', 1, 40, 'pivot', 0, 'system', 'staff', '', '0', ''), +(100, 3, 0, 'Bug', 98, ',98,100,', 2, 10, 'pivot', 0, '', NULL, '', '0', ''), +(102, 1, 0, 'DevOps', 0, ',102,', 1, 50, 'chart', 0, '', NULL, '', '0', ''), +(103, 1, 0, 'DevOps', 102, ',102,103,', 2, 10, 'chart', 0, '', NULL, '', '0', ''), +(104, 0, 0, '内置数据分组', 0, ',104,', 1, 10, 'dataview', 0, '', NULL, '', '0', ''), +(214, 2, 0, '计划', 0, ',214,', 1, 0, 'docTemplate', 0, '', NULL, 'plan', '0', ''), +(215, 2, 0, '需求', 0, ',215,', 1, 0, 'docTemplate', 0, '', NULL, 'story', '0', ''), +(216, 2, 0, '设计', 0, ',216,', 1, 0, 'docTemplate', 0, '', NULL, 'design', '0', ''), +(217, 2, 0, '开发', 0, ',217,', 1, 0, 'docTemplate', 0, '', NULL, 'dev', '0', ''), +(218, 2, 0, '测试', 0, ',218,', 1, 0, 'docTemplate', 0, '', NULL, 'test', '0', ''), +(219, 2, 0, '说明', 0, ',219,', 1, 0, 'docTemplate', 0, '', NULL, 'desc', '0', ''), +(220, 2, 0, '其他', 0, ',220,', 1, 0, 'docTemplate', 0, '', NULL, 'other', '0', ''), +(221, 2, 0, '项目计划', 214, ',214,221,', 2, 0, 'docTemplate', 0, '', NULL, 'PP', '0', ''), +(222, 2, 0, '质量保证计划', 214, ',214,222,', 2, 0, 'docTemplate', 0, '', NULL, 'QAP', '0', ''), +(223, 2, 0, '配置管理计划', 214, ',214,223,', 2, 0, 'docTemplate', 0, '', NULL, 'CMP', '0', ''), +(224, 2, 0, '集成测试计划', 214, ',214,224,', 2, 0, 'docTemplate', 0, '', NULL, 'ITP', '0', ''), +(225, 2, 0, '业务需求说明书', 215, ',215,225,', 2, 0, 'docTemplate', 0, '', NULL, 'ERS', '0', ''), +(226, 2, 0, '用户需求说明书', 215, ',215,226,', 2, 0, 'docTemplate', 0, '', NULL, 'URS', '0', ''), +(227, 2, 0, '项目需求规格说明书', 215, ',215,227,', 2, 0, 'docTemplate', 0, '', NULL, 'SRS', '0', ''), +(228, 2, 0, '概要设计说明书', 216, ',216,228,', 2, 0, 'docTemplate', 0, '', NULL, 'HLDS', '0', ''), +(229, 2, 0, '详细设计说明书', 216, ',216,229,', 2, 0, 'docTemplate', 0, '', NULL, 'DDS', '0', ''), +(230, 2, 0, '数据库设计文档', 216, ',216,230,', 2, 0, 'docTemplate', 0, '', NULL, 'DBDS', '0', ''), +(231, 2, 0, '接口设计文档', 216, ',216,231,', 2, 0, 'docTemplate', 0, '', NULL, 'ADS', '0', ''), +(232, 2, 0, '程序代码', 217, ',217,232,', 2, 0, 'docTemplate', 0, '', NULL, 'Code', '0', ''), +(233, 2, 0, '集成测试用例', 218, ',218,233,', 2, 0, 'docTemplate', 0, '', NULL, 'ITTC', '0', ''), +(234, 2, 0, '系统测试计划', 214, ',214,234,', 2, 0, 'docTemplate', 0, '', NULL, 'STP', '0', ''), +(235, 2, 0, '系统测试用例', 218, ',218,235,', 2, 0, 'docTemplate', 0, '', NULL, 'STTC', '0', ''), +(236, 2, 0, '用户手册', 219, ',219,236,', 2, 0, 'docTemplate', 0, '', NULL, 'UM', '0', ''), +(237, 6, 0, '计划', 0, ',237,', 1, 0, 'docTemplate', 0, '', NULL, 'plan', '0', ''), +(238, 6, 0, '需求', 0, ',238,', 1, 0, 'docTemplate', 0, '', NULL, 'story', '0', ''), +(239, 6, 0, '设计', 0, ',239,', 1, 0, 'docTemplate', 0, '', NULL, 'design', '0', ''), +(240, 6, 0, '开发', 0, ',240,', 1, 0, 'docTemplate', 0, '', NULL, 'dev', '0', ''), +(241, 6, 0, '测试', 0, ',241,', 1, 0, 'docTemplate', 0, '', NULL, 'test', '0', ''), +(242, 6, 0, '说明', 0, ',242,', 1, 0, 'docTemplate', 0, '', NULL, 'desc', '0', ''), +(243, 6, 0, '其他', 0, ',243,', 1, 0, 'docTemplate', 0, '', NULL, 'other', '0', ''), +(244, 6, 0, '项目计划', 237, ',237,244,', 2, 0, 'docTemplate', 0, '', NULL, 'PP', '0', ''), +(245, 6, 0, '质量保证计划', 237, ',237,245,', 2, 0, 'docTemplate', 0, '', NULL, 'QAP', '0', ''), +(246, 6, 0, '配置管理计划', 237, ',237,246,', 2, 0, 'docTemplate', 0, '', NULL, 'CMP', '0', ''), +(247, 6, 0, '集成测试计划', 237, ',237,247,', 2, 0, 'docTemplate', 0, '', NULL, 'ITP', '0', ''), +(248, 6, 0, '业务需求说明书', 238, ',238,248,', 2, 0, 'docTemplate', 0, '', NULL, 'ERS', '0', ''), +(249, 6, 0, '用户需求说明书', 238, ',238,249,', 2, 0, 'docTemplate', 0, '', NULL, 'URS', '0', ''), +(250, 6, 0, '项目需求规格说明书', 238, ',238,250,', 2, 0, 'docTemplate', 0, '', NULL, 'SRS', '0', ''), +(251, 6, 0, '概要设计说明书', 239, ',239,251,', 2, 0, 'docTemplate', 0, '', NULL, 'HLDS', '0', ''), +(252, 6, 0, '详细设计说明书', 239, ',239,252,', 2, 0, 'docTemplate', 0, '', NULL, 'DDS', '0', ''), +(253, 6, 0, '数据库设计文档', 239, ',239,253,', 2, 0, 'docTemplate', 0, '', NULL, 'DBDS', '0', ''), +(254, 6, 0, '接口设计文档', 239, ',239,254,', 2, 0, 'docTemplate', 0, '', NULL, 'ADS', '0', ''), +(255, 6, 0, '程序代码', 240, ',240,255,', 2, 0, 'docTemplate', 0, '', NULL, 'Code', '0', ''), +(256, 6, 0, '集成测试用例', 241, ',241,256,', 2, 0, 'docTemplate', 0, '', NULL, 'ITTC', '0', ''), +(257, 6, 0, '系统测试计划', 237, ',237,257,', 2, 0, 'docTemplate', 0, '', NULL, 'STP', '0', ''), +(258, 6, 0, '系统测试用例', 241, ',241,258,', 2, 0, 'docTemplate', 0, '', NULL, 'STTC', '0', ''), +(259, 6, 0, '用户手册', 242, ',242,259,', 2, 0, 'docTemplate', 0, '', NULL, 'UM', '0', ''); -- DROP TABLE IF EXISTS `zt_space`; CREATE TABLE IF NOT EXISTS `zt_space` ( @@ -15984,14 +15269,6 @@ CREATE TABLE IF NOT EXISTS `zt_roadmapstory` ( ) ENGINE=InnoDB; CREATE UNIQUE INDEX `roadmap_story` ON `zt_roadmapstory`(`roadmap`,`story`); -REPLACE INTO `zt_stage` (`name`, `percent`, `type`, `projectType`, `createdBy`, `createdDate`, `editedBy`, `editedDate`, `deleted`) VALUES -('概念', '10', 'concept', 'ipd', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), -('计划', '10', 'plan', 'ipd', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), -('开发', '50', 'develop', 'ipd', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), -('验证', '15', 'qualify', 'ipd', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), -('发布', '10', 'launch', 'ipd', 'admin', '2020-02-08 21:08:30', 'admin', '2020-02-12 13:50:27', '0'), -('生命周期', '5', 'lifecycle', 'ipd', 'admin', '2020-02-08 21:08:45', 'admin', '2020-02-12 13:50:27', '0'); - -- DROP TABLE IF EXISTS `zt_ai_model`; CREATE TABLE IF NOT EXISTS `zt_ai_model` ( `id` int unsigned NOT NULL AUTO_INCREMENT, diff --git a/extension/lite/admin/ext/config/lite.php b/extension/lite/admin/ext/config/lite.php new file mode 100644 index 0000000000..c40545b50e --- /dev/null +++ b/extension/lite/admin/ext/config/lite.php @@ -0,0 +1,2 @@ +admin->navsGroup['feature']['project'] .= 'story,'; diff --git a/extension/lite/admin/ext/lang/zh-cn/lite.php b/extension/lite/admin/ext/lang/zh-cn/lite.php new file mode 100644 index 0000000000..ca62bfbe90 --- /dev/null +++ b/extension/lite/admin/ext/lang/zh-cn/lite.php @@ -0,0 +1,16 @@ +admin->menuList->feature['tabMenu']['project'] as $key => $value) +{ + if(!in_array($key, array('project', 'execution', 'story'))) + { + unset($lang->admin->menuList->feature['tabMenu']['project'][$key]); + } +} + +foreach($lang->admin->menuList->feature['tabMenu']['menuOrder']['project'] as $key => $value) +{ + if(!in_array($key, array('project', 'execution', 'story'))) + { + unset($lang->admin->menuList->feature['tabMenu']['menuOrder']['project'][$key]); + } +} \ No newline at end of file diff --git a/extension/lite/execution/ext/ui/kanban.html.php b/extension/lite/execution/ext/ui/kanban.html.php index 37a26d17e0..99a41baea3 100644 --- a/extension/lite/execution/ext/ui/kanban.html.php +++ b/extension/lite/execution/ext/ui/kanban.html.php @@ -236,7 +236,6 @@ toolbar div ( set::id('kanbanList'), - setClass('bg-canvas'), zui::kanbanList ( set('$replace', false), diff --git a/framework/api/control.class.php b/framework/api/control.class.php index f71e1229cf..e935b34044 100644 --- a/framework/api/control.class.php +++ b/framework/api/control.class.php @@ -167,15 +167,6 @@ class control extends baseControl return; } - elseif(!$this->getFormData) - { - $this->parseJSON($moduleName, $methodName); - - ob_start(); - echo $this->output; - - return; - } if(empty($moduleName)) $moduleName = $this->moduleName; if(empty($methodName)) $methodName = $this->methodName; diff --git a/framework/base/router.class.php b/framework/base/router.class.php index e17df1158f..79ae766d0e 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3519,45 +3519,26 @@ class baseRouter } } -if(version_compare(PHP_VERSION, '8.2.0', '>=')) -{ - #[\AllowDynamicProperties] // https://www.php.net/manual/zh/class.allowdynamicproperties.php - class config - { - public function set(string $key, $value) - { - helper::setMember('config', $key, $value); - } - } -} -else +#[AllowDynamicProperties] +class config { /** - * config类。 - * The config class. + * 设置成员变量,成员可以是'db.user'类似的格式。 + * Set the value of a member. the member can be the format like db.user. * - * @package framework + * + * set('db.user', 'wwccss'); + * ?> + * + * @param string $key the key of the member + * @param mixed $value the value + * @access public + * @return void */ - class config + public function set(string $key, $value) { - /** - * 设置成员变量,成员可以是'db.user'类似的格式。 - * Set the value of a member. the member can be the format like db.user. - * - * - * set('db.user', 'wwccss'); - * ?> - * - * @param string $key the key of the member - * @param mixed $value the value - * @access public - * @return void - */ - public function set(string $key, $value) - { - helper::setMember('config', $key, $value); - } + helper::setMember('config', $key, $value); } } @@ -3766,7 +3747,7 @@ class EndResponseException extends \Exception $exception = new self($content); $exception->content = $content; - + return $exception; } @@ -3938,4 +3919,4 @@ class ztSessionHandler implements SessionHandlerInterface return $count; } -} +} \ No newline at end of file diff --git a/framework/control.class.php b/framework/control.class.php index 86416f10dd..ff118ee548 100644 --- a/framework/control.class.php +++ b/framework/control.class.php @@ -529,11 +529,11 @@ class control extends baseControl $flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID); if(!$flow) return $fields; - $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID); + $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group); if(!$action || $action->extensionType != 'extend') return $fields; $uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object); - $fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID); + $fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $flow->group); /* 复制项目时显示被复制项目的工作流字段值。*/ if($moduleName == 'project' && $methodName == 'create') @@ -569,11 +569,11 @@ class control extends baseControl $flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID); if(!$flow) return ''; - $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID); + $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group); if(!$action || $action->extensionType == 'none') return ''; $uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, !empty($action->action) ? $action->action: '', $object); - $fieldList = $this->loadModel('workflowaction')->getPageFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID, $groupID); + $fieldList = $this->loadModel('workflowaction')->getPageFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID, $flow->group); $html = ''; if(!empty($flow->css)) $html .= ""; @@ -613,13 +613,13 @@ class control extends baseControl $flow = $this->loadModel('workflow')->getByModule($moduleName, false, $groupID); if(!$flow) return array(); - $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID); + $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group); if(!$action || $action->extensionType != 'extend') return array(); $uiID = is_object($object) ? $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object) : 0; - $fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, $object, $uiID, $groupID); - $layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID, $groupID); + $fieldList = $this->workflowaction->getPageFields($flow->module, $action->action, true, $object, $uiID, $flow->group); + $layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID, $flow->group); $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty'); if($layouts) diff --git a/framework/helper.class.php b/framework/helper.class.php index 55e48537dc..cc6fb84507 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -88,6 +88,11 @@ class helper extends baseHelper { global $config; + if(in_array($feature, array('waterfall', 'waterfallplus'))) + { + return !str_contains(",$config->disabledFeatures,", ",{$feature},"); // 轻量级模式排除瀑布、融合瀑布模型 + } + if(str_contains($feature, '_')) { $code = explode('_', $feature); @@ -95,8 +100,6 @@ class helper extends baseHelper return !str_contains(",$config->disabledFeatures,", ",{$code},"); } - if(in_array($feature, array('scrum', 'waterfall', 'agileplus', 'waterfallplus'))) return !str_contains(",$config->disabledFeatures,", ",{$feature},"); - $hasFeature = false; $canConfigFeature = false; foreach($config->featureGroup as $group => $modules) @@ -106,16 +109,10 @@ class helper extends baseHelper if($feature != $group && $feature != $module) continue; $canConfigFeature = true; - if(in_array($group, array('scrum', 'waterfall', 'agileplus', 'waterfallplus'))) - { - $hasFeature |= (helper::hasFeature("{$group}") && helper::hasFeature("{$group}_{$module}")); - } - else - { - $hasFeature |= helper::hasFeature("{$group}_{$module}"); - } + $hasFeature |= helper::hasFeature("{$group}_{$module}"); } } + return !$canConfigFeature || ($hasFeature && !str_contains(",$config->disabledFeatures,", ",{$feature},")); } diff --git a/lib/dao/dao.class.php b/lib/dao/dao.class.php index a7e787dae8..facb1416ea 100644 --- a/lib/dao/dao.class.php +++ b/lib/dao/dao.class.php @@ -259,7 +259,7 @@ class dao extends baseDAO $flowAction = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `extensionType` = 'extend' AND `vision` = '{$this->config->vision}' AND `group` = '{$groupID}'")->fetch(PDO::FETCH_OBJ); if(!$flowAction) return $this; - $flowFields = $this->dbh->query("SELECT t2.name,t2.rules,t2.control,t2.field,t1.layoutRules FROM " . TABLE_WORKFLOWLAYOUT . " AS t1 LEFT JOIN " . TABLE_WORKFLOWFIELD . " AS t2 ON t1.module = t2.module AND t1.field = t2.field WHERE t1.module = '{$module}' AND t1.action = '{$method}' AND t1.readonly = '0' AND t1.vision = '{$this->config->vision}' AND t1.`group` = '{$groupID}'")->fetchAll(); + $flowFields = $this->dbh->query("SELECT t2.name,t2.rules,t2.control,t2.field,t1.layoutRules FROM " . TABLE_WORKFLOWLAYOUT . " AS t1 LEFT JOIN " . TABLE_WORKFLOWFIELD . " AS t2 ON t1.module = t2.module AND t1.field = t2.field WHERE t1.module = '{$module}' AND t1.action = '{$method}' AND t1.readonly = '0' AND t1.vision = '{$this->config->vision}' AND t1.`group` = '{$groupID}' AND t2.`group` = '{$groupID}'")->fetchAll(); if(!$flowFields) return $this; $rules = array(); diff --git a/lib/form/form.class.php b/lib/form/form.class.php index dfc6552d5e..e1d128a9ce 100644 --- a/lib/form/form.class.php +++ b/lib/form/form.class.php @@ -78,7 +78,7 @@ class form extends fixer * @param int $flowGroupID * @return form */ - public static function data(?array $configObject = null, int $objectID = 0, int $flowGroupID = 0): form + public static function data(array $configObject = null, int $objectID = 0, int $flowGroupID = 0): form { global $app, $config; @@ -143,8 +143,6 @@ class form extends fixer if($moduleName == 'project' && $methodName == 'copyconfirm') $methodName = 'create'; if(empty($app->control)) return $configObject; - $flow = $app->control->loadModel('workflow')->getByModule($moduleName); - if(!$flow) return $configObject; $app->control->loadModel('workflowgroup'); if($flowGroupID) @@ -153,13 +151,16 @@ class form extends fixer $groupID = (empty($group) || $group->main) ? 0 : $flowGroupID; } - $groupID = isset($groupID) ? $groupID : $app->control->workflowgroup->getGroupIDByDataID($flow->module, $objectID); - $action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $groupID); + $groupID = isset($groupID) ? $groupID : $app->control->workflowgroup->getGroupIDByDataID($moduleName, $objectID); + $flow = $app->control->loadModel('workflow')->getByModule($moduleName, false, $groupID); + if(!$flow) return $configObject; + + $action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName, $flow->group); if(!$action || $action->extensionType != 'extend') return $configObject; $uiID = $app->control->loadModel('workflowlayout')->getUIByDataID($flow->module, $action->action, $objectID); - $fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $groupID); - $layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID, $groupID); + $fieldList = $app->control->workflowaction->getPageFields($flow->module, $action->action, true, null, $uiID, $flow->group); + $layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID, $flow->group); $notEmptyRule = $app->control->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty'); if($layouts) { @@ -179,8 +180,10 @@ class form extends fixer } else { - $type = $field->type == 'int' ? 'int' : 'string'; - $configObject[$field->field] = array('required' => $required, 'type' => $type, 'default' => ''); + $type = 'string'; + if($field->type == 'int') $type = 'int'; + if($field->type == 'decimal') $type = 'float'; + $configObject[$field->field] = array('required' => $required, 'type' => $type, 'default' => $field->type == 'int' || $field->type == 'decimal' ? 0 : ''); if($field->control == 'richtext') $configObject[$field->field]['control'] = 'editor'; } } @@ -419,7 +422,7 @@ class form extends fixer if(isset($config['filter'])) $data = $this->filter($data, $config['filter'], zget($config, 'separator', ',')); - if(isset($config['required']) && $config['required'] && isset($this->rawdata->$field) && empty($data)) + if(isset($config['required']) && $config['required'] && isset($this->rawdata->$field) && (is_null($this->rawdata->$field) || $this->rawdata->$field === '' || is_array($this->rawdata->$field)) && empty($data)) { $rawModule = $app->rawModule == 'feedback' && in_array($app->rawMethod, array('touserstory', 'toepic')) ? 'story' : $app->rawModule; $errorKey = isset($config['type']) && $config['type'] == 'array' ? "{$field}[]" : $field; diff --git a/lib/vendor/box/spout/PATCHES.txt b/lib/vendor/box/spout/PATCHES.txt index 7b3c2d4a7c..5c9c50796a 100644 --- a/lib/vendor/box/spout/PATCHES.txt +++ b/lib/vendor/box/spout/PATCHES.txt @@ -2,6 +2,4 @@ This file was automatically generated by Composer Patches (https://github.com/cw Patches applied to this directory: Add return type declarations to Iterator methods -Source: patches/box-spout-php8-compatibility.patch - - +Source: patches/box-spout-php8-compatibility.patch \ No newline at end of file diff --git a/lib/zin/wg/backbtn/v1.php b/lib/zin/wg/backbtn/v1.php index b6d5d17c29..487a8f160a 100644 --- a/lib/zin/wg/backbtn/v1.php +++ b/lib/zin/wg/backbtn/v1.php @@ -50,7 +50,7 @@ class backBtn extends btn 'testsuite' => 'testsuite-browse,testsuite-view,testtask-cases,my-index,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', 'testtask' => 'testtask-browse,testtask-cases,qa-index,testcase-browse,execution-build,my-index,my-effort,company-effort,company-calendar,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view,project-testcase,project-testtask,execution-testtask', 'testreport' => 'testreport-browse,project-testreport,execution-testreport,execution-testtask', - 'tree' => 'product-browse,project-browse,execution-task,bug-browse,projectstory-story,host-browse,execution-story,feedback-admin,ticket-browse,testcase-browse,caselib-browse,dataview-browse', + 'tree' => 'product-browse,project-browse,execution-task,bug-browse,projectstory-story,host-browse,execution-story,feedback-admin,ticket-browse,testcase-browse,caselib-browse,dataview-browse,deliverable-browse', 'doc' => 'doc-mySpace,doc-productSpace,doc-projectSpace,doc-teamSpace,doc-view,execution-doc', 'design' => 'design-browse,projectstory-track,my-effort,company-effort,company-calendar,my-index', 'release' => 'release-browse,release-view,product-roadmap,kanban-view,projectrelease-browse,story-view,my-index,my-effort,company-effort,company-calendar,product-dynamic,project-dynamic,project-view', @@ -67,7 +67,7 @@ class backBtn extends btn 'store' => 'store-browse', 'space' => 'space-browse', 'serverroom' => 'serverroom-browse', - 'project' => 'program-browse,program-project,project-browse,project-view,project-team,charter-view,project-template', + 'project' => 'program-browse,program-project,project-browse,project-view,project-team,charter-view,project-template,project-deliverable', 'product' => 'product-all,program-productview,program-product,product-view,product-dynamic,my-index,charter-view,testtask-view', 'gitlab' => 'space-browse,gitlab-browseproject', 'gitfox' => 'space-browse', @@ -90,7 +90,7 @@ class backBtn extends btn 'service' => 'service-browse,service-manage,service-view', 'domain' => 'domain-browse,domain-view', 'researchtask' => 'marketresearch-task,my-index,product-dynamic,project-dynamic,project-view,my-contribute', - 'review' => 'review-browse,my-index,product-dynamic,project-dynamic,project-view,programplan-browse,project-execution,my-contribute', + 'review' => 'review-browse,project-deliverable,my-index,product-dynamic,project-dynamic,project-view,programplan-browse,project-execution,my-contribute,my-audit', 'charter' => 'my-index,charter-browse,charter-view,my-audit,program-browse,project-view', 'roadmap' => 'charter-view', 'approvalflow' => 'approvalflow-browse', @@ -98,9 +98,11 @@ class backBtn extends btn 'deploy' => 'deploy-browse', 'program' => 'program-browse,program-productview', 'workflowgroup' => 'workflowgroup-project,workflowgroup-deliverable', - 'deliverable' => 'deliverable-browse', + 'cm' => 'cm-browse,cm-view', + 'deliverable' => 'deliverable-browse,project-deliverable,action-trash', 'weekly' => 'weekly-browse', - 'milestone' => 'weekly-browse' + 'milestone' => 'weekly-browse', + 'nc' => 'nc-browse' ); $props = parent::getProps(); diff --git a/lib/zin/wg/deliverable/css/v1.css b/lib/zin/wg/deliverable/css/v1.css index 43d93b3dd3..be73f04b61 100644 --- a/lib/zin/wg/deliverable/css/v1.css +++ b/lib/zin/wg/deliverable/css/v1.css @@ -1 +1,3 @@ -.deliverable-item {padding-left: 3.5rem !important} +.deliverable-category-name>.text:after { right: -0.5rem !important; } +.deliverable-item .form-tip { padding-left: 0.5rem; } +.deliverable-item-doc-picker { width: 18rem; } \ No newline at end of file diff --git a/lib/zin/wg/deliverable/js/v1.js b/lib/zin/wg/deliverable/js/v1.js index 8649ce15b1..63f363f96b 100644 --- a/lib/zin/wg/deliverable/js/v1.js +++ b/lib/zin/wg/deliverable/js/v1.js @@ -5,14 +5,18 @@ window.getDeliverableFileActions = function(file, deliverable) { let actions = []; - if(canDownload && typeof file.id != 'undefined') actions[0] = {icon: 'download', key: 'download', url: $.createLink('file', 'download', 'id=' + file.id), target: '_blank'}; - actions[1] = {icon: 'edit', key: 'rename'}; - actions[2] = {icon: 'trash', key: 'delete'}; - /* 可以预览的文件。 */ if(['txt', 'jpg', 'jpeg', 'gif', 'png', 'bmp', 'mp4'].includes(file.extension) && canDownload && typeof file.id != 'undefined') { - actions[3] = {icon: 'eye', key: 'view', url: $.createLink('file', 'download', `fileID=${file.id}&mouse=left`), 'data-toggle' : 'modal', 'data-size' : 'lg'}; + actions[0] = {icon: 'eye', key: 'view', url: $.createLink('file', 'download', `fileID=${file.id}&mouse=left`), 'data-toggle' : 'modal', 'data-size' : 'lg'}; + } + + if(canDownload && typeof file.id != 'undefined') actions[1] = {icon: 'download', key: 'download', url: $.createLink('file', 'download', 'id=' + file.id), target: '_blank'}; + + if(!onlyShow) + { + actions[2] = {icon: 'edit', key: 'rename'}; + if(!isTemplate) actions[3] = {icon: 'trash', key: 'delete'}; } return actions; @@ -25,9 +29,17 @@ window.getDeliverableFileActions = function(file, deliverable) window.getDocActions = function(doc, deliverable) { let actions = []; - actions[0] = {icon: 'eye', key: 'view', url: $.createLink('doc', 'view', 'docID=' + doc.id)}; - actions[1] = {icon: 'edit', key: 'rename'}; - actions[2] = {icon: 'trash', key: 'delete'}; + const url = $.createLink('doc', 'view', `docID=${doc.id}`); + if(isTemplate) + { + if(doc.canView) actions[0] = {icon: 'eye', key: 'view', url, target: '_blank'}; + } + else + { + actions[0] = {icon: 'eye', key: 'view', url, target: '_blank'}; + if(doc.editable) actions[1] = {icon: 'edit', key: 'rename'}; + actions[2] = {icon: 'trash', key: 'delete'}; + } return actions; } @@ -35,10 +47,47 @@ window.getDocActions = function(doc, deliverable) * 获取交付物的操作按钮。 * Get deliverable actions. */ -window.getDeliverableActions = function(deliverable) +window.getDeliverableActions = function(deliverable, category) { let actions = []; - actions[0] = {text: addFile, icon: 'file', key: 'selectFile'}; - if(deliverable.template > 0 && canDownload) actions[1] = {text: downloadTemplate, icon: 'download', key: 'downloadTemplate', url: $.createLink('file', 'download', 'templateID=' + deliverable.template), target: '_blank'}; + if(isTemplate) + { + actions[0] = {text: addFile, icon: 'file', key: 'selectFile'}; // 后台交付物只有上传文件操作。 + } + else + { + if(canCreateDoc) actions[0] = {text: createDoc, icon: 'doc', key: 'createDoc', 'onClick' : () => showBtnModal(deliverable, category)}; // 前台交付物有三个创建文档的操作。 + } + return actions; } + +window.showBtnModal = function(deliverable, category) +{ + const actions = []; + + actions.push(`
`); + actions.push(`${createDoc}`); + actions.push(`${uploadFile}`); + + if(typeof category.template != 'undefined' && category.template.length > 0) + { + actions.push(``); + actions.push(``); + for(const template of category.template) + { + actions.push(``); + } + actions.push(``); + } + + actions.push(`
`); + + zui.Modal.open({ + id: 'createDoc', + title: createDoc, + type: 'custom', + size: 'sm', + content: {html: actions.join('')}, + }); +} \ No newline at end of file diff --git a/lib/zin/wg/deliverable/v1.php b/lib/zin/wg/deliverable/v1.php index 4cb45d5557..c6a5b4f6ae 100644 --- a/lib/zin/wg/deliverable/v1.php +++ b/lib/zin/wg/deliverable/v1.php @@ -13,7 +13,7 @@ class deliverable extends wg */ protected static array $defineProps = array ( - 'items: array', // 交付物条目。 + 'items: array', // 交付物条目。 'formName: string', // 表单名称。 'maxFileSize: string' // 最大文件大小。 ); @@ -30,11 +30,14 @@ class deliverable extends wg { global $lang, $app; $app->loadLang('doc'); - $app->loadLang('file'); - jsVar('addFile', $lang->doc->addFile); - jsVar('downloadTemplate', $lang->doc->downloadTemplate); + $app->loadLang('deliverable'); + jsVar('createByTemplate', $lang->deliverable->createByTemplate); + jsVar('createDoc', $lang->doc->create); + jsVar('uploadFile', $lang->doc->uploadFile); jsVar('deleteItem', $lang->delete); + jsVar('otherLang', $lang->other); jsVar('canDownload', hasPriv('file', 'download')); + jsVar('canCreateDoc', hasPriv('doc', 'create')); return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js'); } @@ -47,7 +50,7 @@ class deliverable extends wg * @access public * @return string */ - public static function getPageCSS(): ?string + public static function getPageCss(): ?string { return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css'); } @@ -64,8 +67,22 @@ class deliverable extends wg global $lang, $app; $app->loadLang('doc'); $app->loadLang('file'); + $app->loadLang('deliverable'); - $formName = $this->prop('formName') ? $this->prop('formName') : 'deliverable'; + $formName = $this->prop('formName') ? $this->prop('formName') : 'deliverable'; + $isTemplate = $this->prop('isTemplate') ? $this->prop('isTemplate') : false; + $onlyShow = $this->prop('onlyShow') ? $this->prop('onlyShow') : false; + $extraCategory = $this->prop('extraCategory') ? $this->prop('extraCategory') : array_column($this->prop('items'), 'category'); + $categories = $this->prop('categories'); + $projectID = $this->prop('projectID'); + $createDocUrl = $this->prop('createDocUrl'); + $uploadDocUrl = $this->prop('uploadDocUrl'); + + jsVar('addFile', $isTemplate ? $lang->deliverable->files : $lang->doc->addFile); + jsVar('createDocUrl', $createDocUrl); + jsVar('uploadDocUrl', $uploadDocUrl); + jsVar('isTemplate', $isTemplate); + jsVar('onlyShow', $onlyShow); if(!$this->hasProp('maxFileSize')) { @@ -76,16 +93,21 @@ class deliverable extends wg $this->setProp('maxFileSize', $maxFileSize); } + $selectDocTips = $isTemplate ? $lang->deliverable->selectDoc : $lang->deliverable->selectDocInProject; + $docLink = $isTemplate ? helper::createLink('doc', 'ajaxGetTemplateDocs', "keyword={search}") : helper::createLink('doc', 'ajaxGetDeliverableDocs', "keyword={search}&projectID={$projectID}"); + return zui::deliverableList ( set::formName($formName), set::items($this->prop('items')), - set::docPicker(array('placeholder' => $lang->doc->selectDoc, 'items' => helper::createLink('doc', 'ajaxGetMineDocs', 'keyword={search}'))), + set::docPicker(array('placeholder' => $selectDocTips, 'items' => $docLink, 'cache' => false)), set::getFileActions(jsRaw('window.getDeliverableFileActions')), set::getDocActions(jsRaw('window.getDocActions')), set::getEmptyActions(jsRaw('window.getDeliverableActions')), set::maxFileSize($this->prop('maxFileSize')), - set::extraCategory($lang->other) + set::isTemplate($isTemplate), + set::extraCategory($extraCategory), + set::categories($categories) ); } } diff --git a/lib/zin/wg/detail/v1.php b/lib/zin/wg/detail/v1.php index 9cfb643163..b46a8a9e64 100644 --- a/lib/zin/wg/detail/v1.php +++ b/lib/zin/wg/detail/v1.php @@ -275,6 +275,7 @@ CSS; } $titleActions = isset($item['titleActions']) ? $item['titleActions'] : null; + $className = isset($item['className']) ? $item['className'] : null; if($titleActions) unset($item['titleActions']); return div @@ -289,7 +290,7 @@ CSS; ) : null, div ( - setClass('detail-section-content py-1'), + setClass("detail-section-content py-1 {$className}"), $item ? new content(is_array($item) ? set($item) : $item) : null, ) ); @@ -374,16 +375,17 @@ CSS; protected function buildMain() { + $mainSections = $this->buildMainSections(); return div ( setClass('detail-main flex-auto col gap-2 min-w-0'), - div + $mainSections ? div ( setClass('detail-sections canvas shadow rounded px-6 py-4'), setKey('main'), - $this->buildMainSections(), + $mainSections, $this->block('main') - ), + ) : null, $this->block('sections'), $this->children(), $this->buildHistory(), diff --git a/lib/zin/wg/docapp/js/v1.js b/lib/zin/wg/docapp/js/v1.js index eaa74edd71..e2d3941629 100644 --- a/lib/zin/wg/docapp/js/v1.js +++ b/lib/zin/wg/docapp/js/v1.js @@ -397,7 +397,7 @@ function handleSwitchView(mode, location, info) } } -function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc') +function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc', doc = null) { const docApp = getDocApp(); const spaceType = docApp.spaceType; @@ -422,6 +422,10 @@ function showDocBasicModal(parentID, docID, isDraft, modalType = 'doc') destroyOnHide: !docID, cache : true, request: { + method: "POST", + data: { + title: doc?.title, + }, error: error => { if(docBasicModal.current) docBasicModal.current.hide(); showSaveFailedAlert(error); @@ -608,7 +612,7 @@ function submitEditDoc(doc, formData) */ function handleCreateDoc(doc, spaceID, libID, moduleID) { - return showDocBasicModal(0, 0, doc.status === 'draft').then((formData) => { + return showDocBasicModal(0, 0, doc.status === 'draft', 'doc', doc).then((formData) => { return submitNewDoc(doc, spaceID, libID, moduleID, formData); }); } @@ -904,7 +908,7 @@ const actionsMap = hasPriv('addChapter') && doc.parent != 0 ? {icon: 'plus', text: lang.addSameChapter, command: `addChapter/${doc.parent}`} : null, !isTemplate && hasPriv('create') ? {icon: 'plus', text: lang.addSubDoc, command: `startCreateDoc/${doc.id}`} : null, !isChapter && canMoveDoc(doc) ? {icon: 'folder-move', text: lang.moveDoc, command: `moveDoc/${doc.id}`} : null, - !isChapter && hasPriv('delete') ? {icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`} : null, + !doc.frozen && !isChapter && hasPriv('delete') ? {icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`} : null, isChapter && hasPriv('editChapter') ? {icon: 'pencil', text: lang.editChapter, command: `editChapter/${doc.id}`} : null, isChapter && hasPriv('deleteChapter') ? {icon: 'trash', text: lang.deleteChapter, command: `deleteDoc/${doc.id}`} : null, ]; @@ -916,7 +920,7 @@ const actionsMap = const moreItems = []; if(canModify && canMoveDoc(doc)) moreItems.push({icon: 'folder-move', text: lang.moveDoc, command: `moveDoc/${doc.id}`}); - if(canModify && hasPriv('delete')) moreItems.push({icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`}); + if(!doc.frozen && canModify && hasPriv('delete')) moreItems.push({icon: 'trash', text: lang.delete, command: `deleteDoc/${doc.id}`}); if(!isApi && canModify && hasPriv('effort')) moreItems.push({icon: 'time', text: lang.effort, command: `effortDoc/${doc.id}`}); if(!isApi && !isTemplate) { @@ -1123,6 +1127,7 @@ const actionsMap = const items = canModify ? [ canCreateDoc ? {icon: 'plus', text: lang.createDoc, command: `startCreateDoc/${docID}`} : null, + canUploadDoc ? {icon: 'import', text: lang.uploadDoc, command: 'uploadDoc'} : null, canCreateDoc ? {type: 'divider'} : null, canCreateOffice ? {icon: 'file-word', text: lang.createList.word, command: 'startCreateOffice/word'} : null, canCreateOffice ? {icon: 'file-powerpoint', text: lang.createList.ppt, command: 'startCreateOffice/ppt'} : null, @@ -1134,7 +1139,6 @@ const actionsMap = return [ ((canExportDoc && libID > 0) || canCreateDoc) ? {type: 'divider', style: {margin: '6px 0'}} : null, canExportDoc && libID > 0 ? {icon: 'export', text: lang.export, command: 'exportWord'} : null, // 不在库中,无法导出 - canUploadDoc ? {icon: 'import', text: lang.uploadDoc, command: 'uploadDoc'} : null, items.length ? {icon: 'plus', type: 'dropdown', btnType: 'primary', size: 'md', text: lang.create, items: items} : null, ]; }, @@ -1306,14 +1310,24 @@ const commands = if (typeof doc !== 'object') { doc = docApp._treeMap.value.docs.get(doc)?.data; } + if(doc.frozen) return zui.Modal.alert(getLang('frozenTips').replace('%s', getLang('edit'))); + + /** 如果是附件类型并且没有内容的文档,则跳转到上传文档页面进行编辑。 */ + if(doc.type === 'attachment' && !doc.hasContent) { + const type = docApp.lib ? docApp.lib.data.type : docApp.spaceType; + const url = $.createLink('doc', 'uploadDocs', `docID=${doc.id}&objectType=${type}&objectID=${docApp.spaceID}&libID=${doc.lib}&moduleID=${doc.module}&type=attachment`); + zui.Modal.open({url: url}); + return false; + } return docApp.startEditDoc(doc, options); }, /** 上传文档。Upload Doc. */ - uploadDoc: function() + uploadDoc: function(_, args) { const docApp = getDocApp(); + const docID = args[0] || docApp.docID const type = docApp.lib ? docApp.lib.data.type : docApp.spaceType; - const url = $.createLink('doc', 'uploadDocs', `objectType=${type}&objectID=${docApp.spaceID}&libID=${docApp.libID}&moduleID=${docApp.moduleID}&type=attachment`); + const url = $.createLink('doc', 'uploadDocs', `docID=${docID}&objectType=${type}&objectID=${docApp.spaceID}&libID=${docApp.libID}&moduleID=${docApp.moduleID}&type=attachment`); zui.Modal.open({url: url}); }, /** 创建 Office 文件。 Start create office file. */ @@ -2050,6 +2064,19 @@ function getTableOptions(options, info) if(typeof actionHints[key] === 'string') value.hint = actionHints[key]; value.disabled = !privMap[key]; }); + + col.actionItemCreator = function(item, info) + { + const rowData = info.row.data; + item.command = item.command.replace('{id}', String(rowData.id)); + if(rowData.frozen && (item.key === 'edit' || item.key === 'trash')) + { + const actionHint = item.key == 'trash' ? actionHints['delete'] : actionHints[item.key]; + item.hint = lang.frozenTips.replace('%s', actionHint); + item.disabled = true; + } + return item; + } } }); options.onCheckChange = function(changes) diff --git a/lib/zin/wg/dropmenu/css/v1.css b/lib/zin/wg/dropmenu/css/v1.css index bd76831963..67c61dbd38 100644 --- a/lib/zin/wg/dropmenu/css/v1.css +++ b/lib/zin/wg/dropmenu/css/v1.css @@ -1,6 +1,7 @@ -#pick-pop-admin-menu {width: 145px!important;} +#pick-pop-admin-menu {width: 155px!important;} #pick-pop-admin-menu .dropmenu-list {padding: 8px;} #pick-pop-admin-menu .dropmenu-item + .dropmenu-item {margin-top: 4px} #pick-pop-admin-menu .dropmenu-item.active {background-color: unset;} #pick-pop-admin-menu .dropmenu-item:hover {color: rgba(var(--color-primary-500-rgb),var(--tw-text-opacity)); background-color: rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));} -.dropmenu-item .item-content .label {flex-shrink: 0;} \ No newline at end of file +.dropmenu-item .item-content .label {flex-shrink: 0;} +.svg-icon {height: 24px; width: 24px; text-align: center;} diff --git a/lib/zin/wg/dropmenu/v1.php b/lib/zin/wg/dropmenu/v1.php index 0512553352..36dfea47cc 100644 --- a/lib/zin/wg/dropmenu/v1.php +++ b/lib/zin/wg/dropmenu/v1.php @@ -66,6 +66,111 @@ class dropmenu extends wg ); } + /** + * 构建子级下拉菜单。 + * Build sub drop menu. + * + * @param string $module + * @param string $method + * @param string $tab + * @param string|int $objectID + * @param string|int $extra + * @access public + * @return array + */ + public function buildSubDropMenu(string $module, string $method, string $tab, string|int $objectID, string|int $extra): array + { + $app = data('app'); + $subMenu = array(); + if(!in_array($module, $app->config->hasBranchMenuModules) || in_array("{$module}-{$method}", $app->config->excludeBranchMenu)) return $subMenu; + if($tab == 'product' || $tab == 'qa') $subMenu[] = $this->buildBranchDropMenu($module, $method, $tab, $objectID, $extra); + if($tab == 'admin') $subMenu[] = $this->buildFlowGroupMenu($module, $method); + return $subMenu; + } + + /** + * 构建分支子级下拉菜单。 + * Build branch drop menu. + * + * @param string $module + * @param string $method + * @param string $tab + * @param string|int $objectID + * @param string|int $extra + * @access public + * @return object|null + */ + public function buildBranchDropMenu(string $module, string $method, string $tab, string|int $objectID, string|int $extra): object|null + { + $data = $this->prop('data'); + $app = data('app'); + $lang = data('lang'); + $branchMenu = null; + if(!$objectID) return $branchMenu; + + $product = $app->control->loadModel('product')->getByID((int)$objectID); + if($product->type != 'normal') + { + $branchID = ''; + if(!is_null(data('branch'))) $branchID = data('branch'); + if(!is_null(data('branchID'))) $branchID = data('branchID'); + if(strpos((string)$branchID, ',') !== false) + { + $branches = explode(',', $branchID); + $branchID = (!empty($_SESSION['branch']) || $app->session->branch === '0') && in_array($app->session->branch, $branches) ? $app->session->branch : $branches[0]; + } + $app->control->loadModel('branch'); + + /* Get current branch name. */ + $branchName = ''; + if($branchID == 'all' || $branchID === '') + { + $branchID = 'all'; + $branchName = $lang->branch->all; + } + elseif($branchID == 0) + { + $branchName = $lang->branch->main; + } + elseif($branchID > 0) + { + $branchName = $app->control->branch->getByID((string)$branchID); + } + if($module == 'testtask' && $method == 'browse') $extra = data('type'); + if($app->moduleName == 'tree' && $app->methodName == 'browse') + { + $module = 'tree'; + $method = 'browse'; + $extra = data('viewType'); + } + $branchURL = createLink('branch', 'ajaxGetDropMenu', "objectID=$objectID&branch=$branchID&module=$module&method=$method&extra=$extra"); + $branchMenu = $this->buildDropmenu('branch-dropmenu', 'branch-dropmenu', $branchURL, $branchName, $data, $branchID); + } + return $branchMenu; + } + + /** + * 构造流程子级下拉菜单。 + * Build flow group menu. + * + * @param string $module + * @param string $method + * @access public + * @return object|null + */ + public function buildFlowGroupMenu(string $module, string $method): object|null + { + $data = $this->prop('data'); + $app = data('app'); + $objectID = data('groupID'); + + if(!$objectID) return null; + + $flowGroup = $app->control->loadModel('workflowGroup')->fetchByID((int)$objectID); + $flowURL = createLink('workflowGroup', 'ajaxGetDropMenu', "objectID=$objectID&module=$module&method=$method"); + return $this->buildDropmenu('flow-dropmenu', 'flow-dropmenu', $flowURL, $flowGroup->name, $data, $objectID); + } + /** * Override the build method. * @@ -107,52 +212,6 @@ class dropmenu extends wg if($tab == 'dimension' && $module == 'tree' && $method == 'browsegroup') $extra = data('viewType'); - $branchMenu = null; - if(($tab == 'product' || $tab == 'qa') && in_array($module, $app->config->hasBranchMenuModules) && !in_array("{$module}-{$method}", $app->config->excludeBranchMenu)) - { - if($objectID) - { - $product = $app->control->loadModel('product')->getByID((int)$objectID); - if($product->type != 'normal') - { - $branchID = ''; - if(!is_null(data('branch'))) $branchID = data('branch'); - if(!is_null(data('branchID'))) $branchID = data('branchID'); - if(strpos((string)$branchID, ',') !== false) - { - $branches = explode(',', $branchID); - $branchID = (!empty($_SESSION['branch']) || $app->session->branch === '0') && in_array($app->session->branch, $branches) ? $app->session->branch : $branches[0]; - } - $app->control->loadModel('branch'); - - /* Get current branch name. */ - $branchName = ''; - if($branchID == 'all' || $branchID === '') - { - $branchID = 'all'; - $branchName = $lang->branch->all; - } - elseif($branchID == 0) - { - $branchName = $lang->branch->main; - } - elseif($branchID > 0) - { - $branchName = $app->control->branch->getByID((string)$branchID); - } - if($module == 'testtask' && $method == 'browse') $extra = data('type'); - if($app->moduleName == 'tree' && $app->methodName == 'browse') - { - $module = 'tree'; - $method = 'browse'; - $extra = data('viewType'); - } - $branchURL = createLink('branch', 'ajaxGetDropMenu', "objectID=$objectID&branch=$branchID&module=$module&method=$method&extra=$extra"); - $branchMenu = $this->buildDropmenu('branch-dropmenu', 'branch-dropmenu', $branchURL, $branchName, $data, $branchID); - } - } - } - if($module == 'testtask' && in_array($method, array('cases', 'view', 'report', 'groupcase', 'linkcase')) && $app->tab == 'qa') { $testtaskUrl = createLink('testtask', 'ajaxGetDropMenu', data('switcherParams')); @@ -162,6 +221,7 @@ class dropmenu extends wg if($tab == 'admin') { $currentMenuKey = $app->control->loadModel('admin')->getMenuKey(); + $extra = $app->rawParams ? reset($app->rawParams) : ''; $text = isset($lang->admin->menuList->{$currentMenuKey}) ? $lang->admin->menuList->{$currentMenuKey}['name'] : $currentMenuKey; $url = createLink('admin', 'ajaxGetDropMenu', "currentMenuKey={$currentMenuKey}"); $menuID = 'admin-menu'; @@ -172,7 +232,7 @@ class dropmenu extends wg if($tab == 'feedback') { $objectID = data('productID'); - $extra = data('viewType'); + $extra = data('viewType') ? data('viewType') : ''; } if($tab == 'demandpool') $objectID = data('poolID'); if($tab == 'market') @@ -201,7 +261,7 @@ class dropmenu extends wg return array ( $this->buildDropmenu($menuID, $id, $url, $text, $data, $objectID), - $branchMenu, + $this->buildSubDropMenu($module, $method, $tab, (int)$objectID, $extra), isset($testtaskMenu) ? $testtaskMenu : null ); } diff --git a/lib/zin/wg/entitylabel/v1.php b/lib/zin/wg/entitylabel/v1.php index 9fd7abfe55..b065eb32a2 100644 --- a/lib/zin/wg/entitylabel/v1.php +++ b/lib/zin/wg/entitylabel/v1.php @@ -94,7 +94,7 @@ class entityLabel extends wg $entityName = $this->buildEntityName(); return div ( - setClass('entity-label space-x-1.5 overflow-hidden nowrap'), + setClass('entity-label space-x-1.5 overflow-hidden nowrap flex items-center'), set($this->getRestProps()), $prefix, $reverse ? array($entityName, ' ', $entityID) : array($entityID, ' ', $entityName), diff --git a/lib/zin/wg/formbatchitem/v1.php b/lib/zin/wg/formbatchitem/v1.php index ee8cfede80..79a81c2e2f 100644 --- a/lib/zin/wg/formbatchitem/v1.php +++ b/lib/zin/wg/formbatchitem/v1.php @@ -58,7 +58,7 @@ class formBatchItem extends wg */ protected function build(): array { - list($name, $label, $labelClass, $labelProps, $required, $tip, $tipClass, $tipProps, $tipIcon, $control, $width, $strong, $value, $disabled, $items, $placeholder, $ditto, $defaultDitto, $hidden, $readonly, $multiple) = $this->prop(array('name', 'label', 'labelClass', 'labelProps', 'required', 'tip', 'tipClass', 'tipProps', 'tipIcon', 'control', 'width', 'strong', 'value', 'disabled', 'items', 'placeholder', 'ditto', 'defaultDitto', 'hidden', 'readonly', 'multiple')); + list($name, $label, $labelClass, $labelProps, $required, $tip, $tipClass, $tipProps, $tipIcon, $control, $width, $strong, $value, $disabled, $items, $placeholder, $ditto, $defaultDitto, $hidden, $readonly, $multiple, $checkboxName, $checkboxValue, $checkboxText, $checkboxChecked) = $this->prop(array('name', 'label', 'labelClass', 'labelProps', 'required', 'tip', 'tipClass', 'tipProps', 'tipIcon', 'control', 'width', 'strong', 'value', 'disabled', 'items', 'placeholder', 'ditto', 'defaultDitto', 'hidden', 'readonly', 'multiple', 'checkboxName', 'checkboxValue', 'checkboxText', 'checkboxChecked')); if($required === 'auto') $required = isFieldRequired($name); @@ -110,6 +110,14 @@ class formBatchItem extends wg toggle('tooltip', array('title' => $tip)), set($tipProps), set::icon($tipIcon) + ), + empty($checkboxName) ? null : new checkbox + ( + set::rootClass('inline-flex ml-4'), + set::name($checkboxName), + set::value($checkboxValue), + set::text($checkboxText), + set::checked($checkboxChecked) ) ), h::td diff --git a/lib/zin/wg/formpanel/v1.php b/lib/zin/wg/formpanel/v1.php index b700886f38..74d419bbf5 100644 --- a/lib/zin/wg/formpanel/v1.php +++ b/lib/zin/wg/formpanel/v1.php @@ -83,6 +83,7 @@ class formPanel extends panel if($moduleName == 'productplan') return data('plan'); if($moduleName == 'projectrelease') return data('release'); if($moduleName == 'projectbuild') return data('build'); + if($moduleName == 'cm') return data('baseline'); if($moduleName == 'project' && ($methodName == 'create' or $methodName == 'createtemplate') && data('copyProject')) return data('copyProject'); return data($moduleName); } diff --git a/lib/zin/wg/gantt/js/v1.js b/lib/zin/wg/gantt/js/v1.js index 06d8f847e1..abf1d4017e 100644 --- a/lib/zin/wg/gantt/js/v1.js +++ b/lib/zin/wg/gantt/js/v1.js @@ -390,7 +390,7 @@ function initGantt() } let getSubmitBtn = function(task) { - if(task.type == 'point' && !task.rawStatus) return ''; + if(task.type == 'point' && (!task.rawStatus || task.rawStatus == 'draft')) return ''; } let isGanttExpand = false; @@ -625,13 +625,14 @@ function initGantt() { let pointAttr = JSON.parse($('#gantt_here').attr('data-reviewpoints')); let category = id.split("-")[1]; + category = category.replace('point', ''); if(pointAttr[category]['disabled']) { zui.Messager.show({content: pointAttr[category]['message'], type: 'danger-outline', icon: 'exclamation-sign'}); return false; } - loadPage($.createLink('review', 'create', 'projectID=' + projectID + '&object=' + category)); + loadPage($.createLink('review', 'create', 'projectID=' + projectID + '&deliverable=0&reviewID=0&type=decision&objectID=' + category)); } /* The id of task item is like executionID-taskID. e.g. 1507-37829, 37829 is task id. */ diff --git a/lib/zin/wg/heading/v1.php b/lib/zin/wg/heading/v1.php index ef0d112010..030c7f4a4e 100644 --- a/lib/zin/wg/heading/v1.php +++ b/lib/zin/wg/heading/v1.php @@ -20,6 +20,13 @@ class heading extends wg 'dropmenu' => array('map' => 'dropmenu') ); + public static function getPageCSS(): ?string + { + return <<[data-zui-dropmenu]+div .dropmenu-btn>.text,#heading>[data-zui-dropmenu]+div+div .dropmenu-btn>.text{max-width:120px} + CSS; + } + protected function buildAppName() { list($tab, $lang) = data(array('app.tab', 'lang')); diff --git a/lib/zin/wg/idlabel/v1.php b/lib/zin/wg/idlabel/v1.php index 94188b0df6..f12a27beb7 100644 --- a/lib/zin/wg/idlabel/v1.php +++ b/lib/zin/wg/idlabel/v1.php @@ -23,7 +23,7 @@ class idLabel extends wg $id = $this->prop('id'); return span ( - setClass('label label-id gray-300-outline size-sm rounded-full flex-none'), + setClass('label label-id gray-300-outline size-sm rounded-full flex-none ml-1'), set($this->getRestProps()), $id, $this->children() diff --git a/lib/zin/wg/mainnavbar/v1.php b/lib/zin/wg/mainnavbar/v1.php index 22344b16e3..9b0bdbf694 100644 --- a/lib/zin/wg/mainnavbar/v1.php +++ b/lib/zin/wg/mainnavbar/v1.php @@ -82,7 +82,14 @@ class mainNavbar extends nav $currentModule = $app->getModuleName(); $currentMethod = $app->getMethodName(); - if($app->tab == 'admin') $app->control->loadModel('admin')->setMenu(); + if($app->tab == 'admin') + { + $groupID = data('groupID'); + $app->control->loadModel('admin')->setMenu((int)$groupID); + } + + /* When use workflow then set rawModule to moduleName. */ + if($currentModule == 'flow') $currentModule = $app->rawModule; \commonModel::replaceMenuLang(); \commonModel::setMainMenu(); diff --git a/lib/zin/wg/modulemenu/v1.php b/lib/zin/wg/modulemenu/v1.php index aa240acccc..7b662e3573 100644 --- a/lib/zin/wg/modulemenu/v1.php +++ b/lib/zin/wg/modulemenu/v1.php @@ -28,7 +28,8 @@ class moduleMenu extends wg 'appendSettingItems?: array', 'onCheck?: function', 'toggleSidebar?: bool=true', - 'isInModal?: bool=false' + 'isInModal?: bool=false', + 'onClickItem?: function' ); protected static array $defineBlocks = array @@ -311,6 +312,7 @@ class moduleMenu extends wg set::hover(true), set::lines(true), set::preserve($preserve), + set::itemActions($this->prop('moduleActions')), set($treeProps), set($userTreeProps) ), diff --git a/lib/zin/wg/navbar/v1.php b/lib/zin/wg/navbar/v1.php index aa29b3a90f..b507441bc6 100644 --- a/lib/zin/wg/navbar/v1.php +++ b/lib/zin/wg/navbar/v1.php @@ -153,7 +153,8 @@ class navbar extends wg global $app, $lang, $config; if($app->tab == 'admin') { - $app->control->loadModel('admin')->setMenu(); + $groupID = data('groupID') ? data('groupID') : 0; + $app->control->loadModel('admin')->setMenu($groupID); $adminMenuKey = $app->control->loadModel('admin')->getMenuKey(); jsVar('adminMenuKey', $adminMenuKey); } @@ -288,7 +289,9 @@ class navbar extends wg else { $subModule = isset($dropMenuItem['subModule']) ? explode(',', $dropMenuItem['subModule']) : array(); + $subAlias = zget($dropMenuItem, 'alias', ''); if($subModule and in_array($currentModule, $subModule) and !str_contains(",$exclude,", ",$currentModule-$currentMethod,")) $activeMainMenu = true; + if(str_contains(",$subAlias,", ",$currentModule-$currentMethod,")) $activeMainMenu = true; } if($activeMenuID) $activeMainMenu = $dropMenuName == $activeMenuID; diff --git a/lib/zin/wg/picker/v1.php b/lib/zin/wg/picker/v1.php index fa71816aaf..4d21eda1ce 100644 --- a/lib/zin/wg/picker/v1.php +++ b/lib/zin/wg/picker/v1.php @@ -151,9 +151,9 @@ class picker extends wg $name = isset($pickerProps['name']) ? $pickerProps['name'] : ''; if(isset($config->moreLinks[$name])) $pickerItems = $config->moreLinks[$name]; - $pickerProps['_props'] = $restProps; - $pickerProps['items'] = $pickerItems; - $pickerProps['defaultValue'] = $defaultValue; + $pickerProps['_props'] = $restProps; + $pickerProps['items'] = $pickerItems; + $pickerProps['defaultValue'] = $defaultValue; return $pickerProps; } diff --git a/lib/zin/wg/section/v1.php b/lib/zin/wg/section/v1.php index 0014e592af..9d7499a6ef 100644 --- a/lib/zin/wg/section/v1.php +++ b/lib/zin/wg/section/v1.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace zin; +require_once dirname(__DIR__) . DS . 'toolbar' . DS . 'v1.php'; class section extends wg { protected static array $defineProps = array( @@ -27,9 +28,10 @@ class section extends wg private function title(): node { - $title = $this->prop('title'); - $actionsView = $this->block('actions'); - $required = $this->prop('required'); + $title = $this->prop('title'); + $titleActions = $this->prop('titleActions'); + $actionsView = $this->block('actions'); + $required = $this->prop('required'); if(empty($actionsView)) { @@ -40,6 +42,7 @@ class section extends wg ( setClass('form-label required mr-1 pb-3'), ) : null, + $titleActions ? toolbar::create($titleActions) : null, $title ); } @@ -54,6 +57,7 @@ class section extends wg ( setClass('form-label required mr-1'), ) : null, + $titleActions ? toolbar::create($titleActions) : null, $title ), $actionsView diff --git a/module/action/config.php b/module/action/config.php index 14325b4a08..9d17bdf430 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -73,6 +73,9 @@ $config->action->objectNameFields['miniprogram'] = 'name'; $config->action->objectNameFields['holiday'] = 'name'; $config->action->objectNameFields['system'] = 'name'; $config->action->objectNameFields['deliverable'] = 'name'; +$config->action->objectNameFields['cm'] = 'title'; +$config->action->objectNameFields['baseline'] = 'title'; +$config->action->objectNameFields['projectchange'] = 'name'; $config->action->commonImgSize = 870; @@ -132,4 +135,4 @@ $config->action->multipleObjectFields['testtask']['type'] = 'typeList'; $config->action->approvalFields['reviewStatus'] = 'reviewStatusList'; $config->action->approvalFields['reviewResult'] = 'reviewResultList'; -$config->action->hiddenTrashObjects = 'object,cm'; +$config->action->hiddenTrashObjects = 'object,cm,stage'; diff --git a/module/action/control.php b/module/action/control.php index 5f19a08f27..52ebd34a00 100755 --- a/module/action/control.php +++ b/module/action/control.php @@ -133,7 +133,7 @@ class action extends control public function undelete(int $actionID, string $browseType = 'all', string $confirmChange = 'no') { $result = $this->actionZen->checkActionExist($actionID); - if(is_array($result) && isset($result['result']) && $result['result'] == 'fail') return $this->send($result); + if(is_array($result) && $result['result'] == 'fail') return $this->send($result); $oldAction = $result; /* 当对象类型为program、project、execution、product时,需要检查是否有重复的对象。 */ diff --git a/module/action/lang/de.php b/module/action/lang/de.php index 4c525e1a13..c6806b7492 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = 'Board'; $lang->action->objectTypes['boardspace'] = 'Board Space'; $lang->action->objectTypes['productline'] = 'Product Line'; $lang->action->objectTypes['system'] = $lang->product->system; -$lang->action->objectTypes['deliverable'] = 'Deliverable'; +$lang->action->objectTypes['deliverable'] = 'Deliverable Category'; +$lang->action->objectTypes['cm'] = 'Baseline'; +$lang->action->objectTypes['baseline'] = 'Baseline'; +$lang->action->objectTypes['projectchange'] = 'Project Change'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); @@ -942,7 +945,6 @@ $lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s'; $lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s'; $lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all'; $lang->action->label->kanbancard = 'Kanban Card|kanban|view|kanbanID=%s'; -$lang->action->label->stage = 'Stage|stage|browse|'; $lang->action->label->module = 'Module|tree|browse|productid=%s&view=story¤tModuleID=0&branch=all'; $lang->action->label->ticket = 'Ticket|ticket|view|id=%s'; $lang->action->label->chartgroup = 'Group'; @@ -953,7 +955,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s'; $lang->action->label->prompt = 'ZenTao Agent|ai|promptview|id=%s'; $lang->action->label->miniprogram = 'General Agent|aiapp|browseminiprogram|id=%s'; $lang->action->label->holiday = 'Holiday|holiday|browse|'; -$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s'; +$lang->action->label->deliverable = 'Deliverable Category|deliverable|view|id=%s'; /* Object type. */ $lang->action->search = new stdclass(); diff --git a/module/action/lang/en.php b/module/action/lang/en.php index b6997a5a3a..d40b059656 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = 'Board'; $lang->action->objectTypes['boardspace'] = 'Board Space'; $lang->action->objectTypes['productline'] = 'Product Line'; $lang->action->objectTypes['system'] = $lang->product->system; -$lang->action->objectTypes['deliverable'] = 'Deliverable'; +$lang->action->objectTypes['deliverable'] = 'Deliverable Category'; +$lang->action->objectTypes['cm'] = 'Baseline'; +$lang->action->objectTypes['baseline'] = 'Baseline'; +$lang->action->objectTypes['projectchange'] = 'Project Change'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); @@ -942,7 +945,6 @@ $lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s'; $lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s'; $lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all'; $lang->action->label->kanbancard = 'Kanban Card|kanban|view|kanbanID=%s'; -$lang->action->label->stage = 'Stage|stage|browse|'; $lang->action->label->module = 'Module|tree|browse|productid=%s&view=story¤tModuleID=0&branch=all'; $lang->action->label->ticket = 'Ticket|ticket|view|id=%s'; $lang->action->label->chartgroup = 'Group'; @@ -953,7 +955,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s'; $lang->action->label->prompt = 'ZenTao Agent|ai|promptview|id=%s'; $lang->action->label->miniprogram = 'General Agent|aiapp|browseminiprogram|id=%s'; $lang->action->label->holiday = 'Holiday|holiday|browse|'; -$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s'; +$lang->action->label->deliverable = 'Deliverable Category|deliverable|view|id=%s'; /* Object type. */ $lang->action->search = new stdclass(); diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index dfda35c48a..f8b6b6da4b 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = 'Board'; $lang->action->objectTypes['boardspace'] = 'Board Space'; $lang->action->objectTypes['productline'] = 'Product Line'; $lang->action->objectTypes['system'] = $lang->product->system; -$lang->action->objectTypes['deliverable'] = 'Deliverable'; +$lang->action->objectTypes['deliverable'] = 'Deliverable Category'; +$lang->action->objectTypes['cm'] = 'Baseline'; +$lang->action->objectTypes['baseline'] = 'Baseline'; +$lang->action->objectTypes['projectchange'] = 'Project Change'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); @@ -942,7 +945,6 @@ $lang->action->label->kanban = 'Kanban|kanban|view|kanbanID=%s'; $lang->action->label->kanbancolumn = 'Kanban Column|execution|kanban|execution=%s'; $lang->action->label->kanbanlane = 'Kanban Lane|execution|kanban|execution=%s&type=all'; $lang->action->label->kanbancard = 'Kanban Card|kanban|view|kanbanID=%s'; -$lang->action->label->stage = 'Stage|stage|browse|'; $lang->action->label->module = 'Module|tree|browse|productid=%s&view=story¤tModuleID=0&branch=all'; $lang->action->label->ticket = 'Ticket|ticket|view|id=%s'; $lang->action->label->chartgroup = 'Group'; @@ -953,7 +955,7 @@ $lang->action->label->instance = 'Application|instance|view|id=%s'; $lang->action->label->prompt = 'ZenTao Agent|ai|promptview|id=%s'; $lang->action->label->miniprogram = 'General Agent|aiapp|browseminiprogram|id=%s'; $lang->action->label->holiday = 'Holiday|holiday|browse|'; -$lang->action->label->deliverable = 'Deliverable|deliverable|view|id=%s'; +$lang->action->label->deliverable = 'Deliverable Category|deliverable|view|id=%s'; /* Object type. */ $lang->action->search = new stdclass(); diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 921fdaf1b2..6adb9727e0 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -193,7 +193,10 @@ $lang->action->objectTypes['board'] = '白板'; $lang->action->objectTypes['boardspace'] = '白板空间'; $lang->action->objectTypes['productline'] = '产品线'; $lang->action->objectTypes['system'] = $lang->product->system; -$lang->action->objectTypes['deliverable'] = '交付物'; +$lang->action->objectTypes['deliverable'] = '交付物类型'; +$lang->action->objectTypes['cm'] = '基线'; +$lang->action->objectTypes['baseline'] = '基线'; +$lang->action->objectTypes['projectchange'] = '项目变更'; /* 用来描述操作历史记录。*/ $lang->action->desc = new stdclass(); @@ -942,7 +945,6 @@ $lang->action->label->kanban = '看板|kanban|view|kanbanID=%s'; $lang->action->label->kanbancolumn = '看板列|execution|kanban|execution=%s'; $lang->action->label->kanbanlane = '看板泳道|execution|kanban|execution=%s&type=all'; $lang->action->label->kanbancard = '看板卡片|kanban|view|kanbanID=%s'; -$lang->action->label->stage = '瀑布模型的阶段|stage|browse|'; $lang->action->label->module = '模块|tree|browse|productid=%s&view=story¤tModuleID=0&branch=all'; $lang->action->label->ticket = '工单|ticket|view|id=%s'; $lang->action->label->chartgroup = '分组'; @@ -953,7 +955,7 @@ $lang->action->label->instance = '服务|instance|view|id=%s'; $lang->action->label->prompt = '禅道智能体|ai|promptview|id=%s'; $lang->action->label->miniprogram = '通用智能体|aiapp|browseminiprogram|id=%s'; $lang->action->label->holiday = '节假日|holiday|browse|'; -$lang->action->label->deliverable = '交付物|deliverable|view|id=%s'; +$lang->action->label->deliverable = '交付物类型|deliverable|view|id=%s'; /* Object type. */ $lang->action->search = new stdclass(); diff --git a/module/action/model.php b/module/action/model.php index 7037e78dd7..9b70d5a4b9 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -613,6 +613,7 @@ class actionModel extends model ->orWhere('(objectType')->eq('execution')->andWhere('objectID')->notIn($noMultipleExecutions) ->markRight(2) ->andWhere('vision')->eq($this->config->vision) + ->andWhere('objectType')->notIn($this->config->action->hiddenTrashObjects) ->fetchAll('objectType'); } @@ -1373,6 +1374,14 @@ class actionModel extends model $action->objectLabel = $this->lang->project->template; $action->objectLink = helper::createLink('project', 'execution', "status=undone&projectID={$action->objectID}"); } + elseif($action->objectType == 'project' && $action->action == 'managedeliverable') + { + $action->objectLink = helper::createLink('project', 'deliverable', "projectID={$action->objectID}"); // 交付物链接 + } + elseif($action->objectType == 'execution' && $action->action == 'managedeliverable') + { + $action->objectLink = helper::createLink('execution', 'view', "executionID={$action->objectID}"); // 交付物链接 + } } return $actions; } @@ -1712,14 +1721,7 @@ class actionModel extends model } else { - if($history->field == 'deliverable' && ($objectType == 'project' || $objectType == 'execution')) - { - $content .= $this->processDeliverableJson($objectType, $objectID, $history); - } - else - { - $content .= sprintf($this->lang->action->desc->diff1, $history->fieldLabel, $history->old, $history->new); - } + $content .= sprintf($this->lang->action->desc->diff1, $history->fieldLabel, $history->old, $history->new); } } return $content; @@ -1795,6 +1797,11 @@ class actionModel extends model } } + if($action->objectType == 'deliverable') + { + $this->dao->update(TABLE_DELIVERABLE)->set('status')->eq('disabled')->where('id')->eq($action->objectID)->exec(); + } + $this->recoverRelatedData($action, $object); /* 还原已删除的需求时重算OR需求和业用研需的阶段。 */ diff --git a/module/action/test/model/yaml/getrelatedfields/review.yaml b/module/action/test/model/yaml/getrelatedfields/review.yaml index 704dab6e89..8a25fff6d2 100644 --- a/module/action/test/model/yaml/getrelatedfields/review.yaml +++ b/module/action/test/model/yaml/getrelatedfields/review.yaml @@ -27,14 +27,6 @@ fields: postfix: "" loop: 0 format: "YYYY-MM-DD hh:mm:ss" - - field: lastAuditedDate - note: "计划完成时间" - range: "(-1M)-(w):1D" - type: timestamp - prefix: "" - postfix: "" - loop: 0 - format: "YYYY-MM-DD hh:mm:ss" - field: lastEditedDate note: "计划完成时间" range: "(-1M)-(w):1D" diff --git a/module/action/test/model/yaml/transformactions/review.yaml b/module/action/test/model/yaml/transformactions/review.yaml index 82f3e12268..dc2a4c92df 100644 --- a/module/action/test/model/yaml/transformactions/review.yaml +++ b/module/action/test/model/yaml/transformactions/review.yaml @@ -39,14 +39,6 @@ fields: postfix: "" loop: 0 format: "" - - field: auditedBy - note: "由谁审计" - from: common.user.v1.yaml - use: user - prefix: "" - postfix: "" - loop: 0 - format: "" - field: createdBy note: "由谁创建" from: common.user.v1.yaml @@ -87,22 +79,6 @@ fields: postfix: "" loop: 0 format: "" - - field: lastAuditedBy - note: "最后审计者" - from: common.user.v1.yaml - use: user - prefix: "" - postfix: "" - loop: 0 - format: "" - - field: lastAuditedDate - note: "最后审计日期" - from: common.date.v1.yaml - use: dateB - prefix: "" - postfix: "" - loop: 0 - format: "" - field: lastEditedBy note: "最后编辑者" from: common.user.v1.yaml @@ -126,13 +102,6 @@ fields: postfix: "" loop: 0 format: "" - - field: auditResult - note: "审计结果" - range: pass,fail,needfix - prefix: "" - postfix: "" - loop: 0 - format: "" - field: deleted note: "是否删除" range: 0 diff --git a/module/action/test/model/yaml/undelete/review.yaml b/module/action/test/model/yaml/undelete/review.yaml index 0f93afb793..bb33b95536 100644 --- a/module/action/test/model/yaml/undelete/review.yaml +++ b/module/action/test/model/yaml/undelete/review.yaml @@ -27,14 +27,6 @@ fields: postfix: "" loop: 0 format: "YYYY-MM-DD hh:mm:ss" - - field: lastAuditedDate - note: "最后审计日期" - range: "(-1M)-(w):1D" - type: timestamp - prefix: "" - postfix: "" - loop: 0 - format: "YYYY-MM-DD hh:mm:ss" - field: lastEditedDate note: "最后编辑日期" range: "(-1M)-(w):1D" diff --git a/module/action/ui/trash.html.php b/module/action/ui/trash.html.php index af19e89400..e9c3263eaa 100644 --- a/module/action/ui/trash.html.php +++ b/module/action/ui/trash.html.php @@ -34,7 +34,7 @@ featureBar ( set::current($currentObjectType), set::linkParams("objectType={key}&type={$type}&byQuery=&queryID=&orderBy={$orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"), - $currentObjectType != 'all' ? li(searchToggle(set::module('trash')), setClass($byQuery ? 'active' : '')) : null + $currentObjectType != 'all' && $currentObjectType != 'auditplan' ? li(searchToggle(set::module('trash')), setClass($byQuery ? 'active' : '')) : null ); toolbar diff --git a/module/admin/config.php b/module/admin/config.php index 83508f15c9..4d87e4b039 100755 --- a/module/admin/config.php +++ b/module/admin/config.php @@ -8,22 +8,17 @@ if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,123 $config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'cache|setting', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'admin|metriclib', 'ldap', 'custom|libreoffice', 'conference', 'watermark', 'client', 'system|browsebackup', 'system|restorebackup'); $config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial'); $config->admin->menuGroup['switch'] = array('admin|setmodule'); -$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'deliverable', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting', 'custom|project'); -$config->admin->menuGroup['feature'] = array('custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement'); +$config->admin->menuGroup['feature'] = array('stage|settype', 'baseline', 'design', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'holiday', 'reviewsetting', 'custom|project', 'custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report', 'custom|limittaskdate', 'measurement'); $config->admin->menuGroup['message'] = array('mail', 'webhook', 'sms', 'message'); $config->admin->menuGroup['dev'] = array('dev', 'entry', 'editor'); $config->admin->menuGroup['extension'] = array('extension'); $config->admin->menuGroup['convert'] = array('convert'); $config->admin->menuGroup['adminregister'] = array('admin|register'); -$config->admin->menuModuleGroup['model']['custom|set'] = array('project', 'issue', 'risk', 'opportunity', 'nc'); -$config->admin->menuModuleGroup['model']['custom|required'] = array('project', 'build'); -$config->admin->menuModuleGroup['feature']['custom|set'] = array('todo', 'block', 'epic', 'requirement', 'story', 'task', 'bug', 'testcase', 'testtask', 'feedback', 'user', 'ticket'); -$config->admin->menuModuleGroup['feature']['custom|required'] = array('bug', 'doc', 'product', 'epic', 'requirement', 'story', 'productplan', 'release', 'task', 'testcase', 'testsuite', 'testtask', 'testreport', 'caselib', 'doc', 'feedback', 'user', 'execution'); +$config->admin->menuModuleGroup['feature']['custom|set'] = array('project', 'issue', 'risk', 'opportunity', 'nc', 'todo', 'block', 'epic', 'requirement', 'story', 'task', 'bug', 'testcase', 'testtask', 'feedback', 'user', 'ticket', 'projectchange'); +$config->admin->menuModuleGroup['feature']['custom|required'] = array('project', 'build', 'bug', 'doc', 'product', 'epic', 'requirement', 'story', 'productplan', 'release', 'task', 'testcase', 'testsuite', 'testtask', 'testreport', 'caselib', 'doc', 'feedback', 'user', 'execution'); if($config->vision == 'lite') { - $config->admin->menuModuleGroup['model']['custom|set'] = array(); - $config->admin->menuModuleGroup['model']['custom|required'] = array('build'); $config->admin->menuModuleGroup['feature']['custom|set'] = array('todo', 'block', 'task', 'story', 'user'); $config->admin->menuModuleGroup['feature']['custom|required'] = array('project', 'task', 'story', 'doc', 'user'); } @@ -71,7 +66,7 @@ $config->admin->patchAPIURL = $config->admin->apiSite . 'extension-apiGetExte $config->admin->downloadAPIURL = $config->admin->apiSite . 'download.json'; $config->admin->dynamicAPIURL = $config->admin->apiSite . 'dynamic-activities-%s.json'; $config->admin->videoAPIURL = $config->admin->apiSite . 'zentao.json'; -$config->admin->liteMenuList = array('system', 'company', 'feature', 'message', 'extension', 'dev'); +$config->admin->liteMenuList = array('system', 'company', 'feature', 'message', 'extension', 'dev', 'workflow', 'approvalFlow'); $config->admin->helpURL['system'] = 'https://www.zentao.net/book/zentaopms/538.html'; $config->admin->helpURL['company'] = 'https://www.zentao.net/book/zentaopms/38.html'; @@ -85,11 +80,9 @@ $config->admin->helpURL['convert'] = 'https://www.zentao.net/book/zentaopms/65 $config->admin->navsGroup['feature']['my'] = ',todo,block,'; $config->admin->navsGroup['feature']['product'] = ',product,epic,requirement,story,productplan,release,'; -$config->admin->navsGroup['feature']['project'] = ',project,'; +$config->admin->navsGroup['feature']['project'] = ',project,build,issue,risk,opportunity,nc,projectchange,'; $config->admin->navsGroup['feature']['execution'] = ',execution,task,'; -$config->admin->navsGroup['feature']['project'] = ',project,story,'; $config->admin->navsGroup['feature']['qa'] = ',bug,testcase,testsuite,testtask,testreport,caselib,'; -$config->admin->navsGroup['model']['common'] = ',project,build,issue,risk,opportunity,nc,'; if($config->vision == 'lite') $config->admin->navsGroup['feature']['my'] .= 'task,'; global $lang, $app; diff --git a/module/admin/control.php b/module/admin/control.php index 3eac85a59c..42322d0d6a 100755 --- a/module/admin/control.php +++ b/module/admin/control.php @@ -168,10 +168,6 @@ class admin extends control } } - $enableER = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productER', 0); - $URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0); - if($enableER && !$URAndSR) $this->send(array('result' => 'fail', 'message' => $this->lang->admin->notice->openUR)); - foreach($closedFeatures as $closedFeature) { if(in_array($closedFeature, array('productER', 'productUR'))) @@ -180,6 +176,9 @@ class admin extends control } } + $enableER = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productER', 0); + $URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0); + $this->setting->setItem('system.common.closedFeatures', implode(',', $closedFeatures)); $this->setting->setItem('system.common.global.scoreStatus', zget($data->module, 'myScore', 0)); $this->setting->setItem('system.custom.enableER', $enableER); @@ -477,15 +476,16 @@ class admin extends control foreach($this->lang->admin->menuList as $menuKey => $menuGroup) { if($this->config->vision == 'lite' && !in_array($menuKey, $this->config->admin->liteMenuList)) continue; + $data[] = array( 'id' => $menuKey, 'name' => $menuKey, - 'content' => array('html' => "
{$menuGroup['name']}
"), + 'content' => array('html' => "
" . (!empty($menuGroup['icon']) ? "" : "") . " {$menuGroup['name']}
"), 'text' => $menuGroup['name'], 'titleClass' => 'hidden', 'type' => 'item', 'disabled' => $menuGroup['disabled'], - 'url' => $menuGroup['disabled'] || $currentMenuKey == $menuKey ? '' : $menuGroup['link'], + 'url' => $menuGroup['link'], 'active' => $currentMenuKey == $menuKey, 'rootClass' => 'admin-menu-item', 'attrs' => array('disabled' => $menuGroup['disabled']) diff --git a/module/admin/css/index.ui.css b/module/admin/css/index.ui.css index 3864e7af9b..3821dc283c 100644 --- a/module/admin/css/index.ui.css +++ b/module/admin/css/index.ui.css @@ -2,3 +2,5 @@ .border-hover:hover {border-color: var(--color-primary-500)} div.setting-box[data-id=platform] div.text-md {white-space: nowrap;} + +.svg-icon {height: 24px; width: 24px; text-align: center;} diff --git a/module/admin/js/setmodule.ui.js b/module/admin/js/setmodule.ui.js index d62d87223d..28594734e8 100644 --- a/module/admin/js/setmodule.ui.js +++ b/module/admin/js/setmodule.ui.js @@ -1,33 +1,16 @@ -function changeModule($target) +function changeModule($target, type = 'single') { const name = $target.attr('name'); if($target.prop('checked')) { $("input[type=hidden][name='" + name + "']").val('1').attr('disabled'); + if(type == 'single') checkRelated(name, 'open'); } else { - if(name == 'module[productUR]' || name == 'module[productER]') - { - zui.Modal.confirm( - { - message: confirmDisableStoryType, - icon: 'icon-exclamation-sign', - iconClass: 'warning-pale rounded-full icon-2x' - }).then((res) => - { - if(res) - { - $("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled'); - return false; - } - - $target.prop('checked', true); - }); - return false; - } - + $("input[type=hidden][name='" + name + "']").val('1').attr('disabled'); $("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled'); + if(type == 'single') checkRelated(name, 'close'); } }; @@ -42,7 +25,7 @@ function checkGroup() $(this).closest('tr').find("input[type=checkbox][name^='module']").each(function() { $(this).prop('checked', checked); - changeModule($(this)); + changeModule($(this), 'all'); }); }; @@ -53,6 +36,229 @@ function checkAll() $(this).closest('table').find("input[type=checkbox][name^='module']").each(function() { $(this).prop('checked', checked); - changeModule($(this)); + changeModule($(this), 'all'); }); }; + + +window.submitForm = function() +{ + const isCheckedUR = $('[name="module[productUR]"]').prop('checked'); + const isCheckedER = $('[name="module[productER]"]').prop('checked'); + + let message = confirmDisableStoryType; + let storyType = ''; + + if(edition != 'ipd' && URAndSR && !isCheckedUR) + { + storyType += URCommon; + } + + if(enableER && !isCheckedER) + { + if(storyType) storyType += ','; + storyType += ERCommon; + } + + if(storyType) + { + zui.Modal.confirm( + { + message: message.replace(/{type}/g, storyType), + icon: 'icon-exclamation-sign', + iconClass: 'warning-pale rounded-full icon-2x' + }).then((res) => + { + if(res) + { + realSubmitForm(); + return false; + } + + setModuleState('productUR', true); + setModuleState('productER', true); + }); + return false; + } + + realSubmitForm(); + return false; +} + +window.realSubmitForm = function() +{ + const formData = new FormData($('#setModuleForm form')[0]); + const url = $.createLink('admin', 'setmodule'); + $.ajaxSubmit({url: url, data: formData}); +} + +/** + * 设置模块的checkbox和hidden input状态 + * @param {string} moduleName - 模块名称(如 'productUR', 'productER') + * @param {boolean} checked - 是否选中 + */ +function setModuleState(moduleName, checked) +{ + const checkboxSelector = `[name="module[${moduleName}]"]`; + const hiddenSelector = `#module${moduleName}[type=hidden]`; + $(checkboxSelector).prop('checked', checked); + $(hiddenSelector).val(checked ? '1' : '0'); +} + +/** + * 检查模块是否已启用 + * @param {string} moduleName - 模块名称 + * @returns {boolean} 是否已启用 + */ +function isModuleEnabled(moduleName) +{ + return $('[name="module[' + moduleName + ']"]').prop('checked'); +} + +/** + * 显示依赖关系确认对话框 + * @param {string} message - 确认消息 + * @param {Function} onConfirm - 确认回调 + * @param {Function} onCancel - 取消回调 + */ +function showDependencyConfirm(message, onConfirm, onCancel) +{ + zui.Modal.confirm( + { + message: message, + icon: 'icon-exclamation-sign', + iconClass: 'warning-pale rounded-full icon-2x' + }).then((res) => + { + if(res) + { + if(onConfirm) onConfirm(); + return false; + } + if(onCancel) onCancel(); + }); +} + +window.checkRelated = function(name, type) +{ + if(type === 'open') + { + // 开启 业务需求 时,需要确保 用户需求 已开启 + if(name.includes('productER') && edition !== 'ipd') + { + if(!isModuleEnabled('productUR')) + { + const message = openDependFeature.replace('{source}', ERCommon).replace('{target}', URCommon); + showDependencyConfirm( + message, + () => setModuleState('productUR', true), + () => setModuleState('productER', false) + ); + return false; + } + } + // 开启 项目变更 时,需要确保 交付物 已开启 + else if(name.includes('projectCm')) + { + if(!isModuleEnabled('projectDeliverable')) + { + const message = openDependFeature.replace('{source}', cmLang).replace('{target}', deliverableLang); + showDependencyConfirm( + message, + () => setModuleState('projectDeliverable', true), + () => setModuleState('projectCm', false) + ); + return false; + } + } + // 开启 项目变更 时,需要确保 交付物 和 基线 都已开启 + else if(name.includes('projectChange')) + { + const deliverableEnabled = isModuleEnabled('projectDeliverable'); + const cmEnabled = isModuleEnabled('projectCm'); + + if(!deliverableEnabled || !cmEnabled) + { + let message = openDependFeature.replace('{source}', changeLang); + const missingLangs = []; + + if(!deliverableEnabled) missingLangs.push(deliverableLang); + if(!cmEnabled) missingLangs.push(cmLang); + + message = message.replace('{target}', missingLangs.join(',')); + + showDependencyConfirm( + message, + () => + { + if(!deliverableEnabled) setModuleState('projectDeliverable', true); + if(!cmEnabled) setModuleState('projectCm', true); + }, + () => setModuleState('projectChange', false) + ); + return false; + } + } + } + else + { + // 关闭 用户需求 时,如果 业务需求 已开启,需要提示关闭 业务需求 + if(name.includes('productUR') && edition !== 'ipd') + { + if(isModuleEnabled('productER')) + { + const message = closeDependFeature.replace('{source}', URCommon).replace('{target}', ERCommon); + showDependencyConfirm( + message, + () => setModuleState('productER', false), + () => + { + setModuleState('productUR', true); + setModuleState('productER', true); + } + ); + return false; + } + } + // 关闭 交付物 时,如果 项目变更 或 基线 已开启,需要提示关闭它们 + else if(name.includes('projectDeliverable')) + { + const changeEnabled = isModuleEnabled('projectChange'); + const cmEnabled = isModuleEnabled('projectCm'); + + if(changeEnabled || cmEnabled) + { + let message = closeDependFeature.replace('{source}', deliverableLang); + const activeLangs = []; + + if(changeEnabled) activeLangs.push(changeLang); + if(cmEnabled) activeLangs.push(cmLang); + + message = message.replace('{target}', activeLangs.join(',')); + + showDependencyConfirm( + message, + () => + { + if(changeEnabled) setModuleState('projectChange', false); + if(cmEnabled) setModuleState('projectCm', false); + }, + () => setModuleState('projectDeliverable', true) + ); + } + } + // 关闭 基线 时,如果 项目变更 已开启,需要提示关闭 项目变更 + else if(name.includes('projectCm')) + { + if(isModuleEnabled('projectChange')) + { + const message = closeDependFeature.replace('{source}', cmLang).replace('{target}', changeLang); + showDependencyConfirm( + message, + () => setModuleState('projectChange', false), + () => setModuleState('projectCm', true) + ); + } + } + } +} \ No newline at end of file diff --git a/module/admin/lang/de.php b/module/admin/lang/de.php index b67bfb2c32..b9632486a8 100755 --- a/module/admin/lang/de.php +++ b/module/admin/lang/de.php @@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = ''; $lang->admin->api = 'API'; $lang->admin->log = 'Log'; $lang->admin->setting = 'Einstellungen'; +$lang->admin->setFlow = 'Set Flow'; $lang->admin->pluginRecommendation = 'Recommended plug-ins'; $lang->admin->zentaoInfo = 'ZenTao information'; $lang->admin->officialAccount = 'WeChat Official Account'; @@ -60,10 +61,12 @@ $lang->admin->info->account = 'Ihr ZenTao Konto ist %s.'; $lang->admin->info->log = 'Logs die über die Gültigen Tage hinausgehen werden gelöscht. Aufgabenplanung muss laufen (cron).'; $lang->admin->notice = new stdclass(); -$lang->admin->notice->register = "Hinweiß: Sie haben sich nicht bei ZenTao (www.zentao.pm) registriert. %s um die aktuellen Updates und News zu erhalten."; -$lang->admin->notice->ignore = "Ignorieren"; -$lang->admin->notice->int = "『%s』sollte eine positive Zahl sein."; -$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}."; +$lang->admin->notice->register = "Hinweiß: Sie haben sich nicht bei ZenTao (www.zentao.pm) registriert. %s um die aktuellen Updates und News zu erhalten."; +$lang->admin->notice->ignore = "Ignorieren"; +$lang->admin->notice->int = "『%s』sollte eine positive Zahl sein."; +$lang->admin->notice->confirmDisableStoryType = "‘{type}’ Funktion geschlossen, das System wird alle Geschichten entfernen, die mit dem Projekt und der Ausführung verknüpkt sind, die Operation ist irreversibel"; +$lang->admin->notice->openDependFeature = 'Using "{source}" function requires enabling "{target}" function synchronously.'; +$lang->admin->notice->closeDependFeature = 'Closing "{source}" function requires synchronously closing "{target}" function.'; $lang->admin->registerNotice = new stdclass(); $lang->admin->registerNotice->common = 'Register Account'; @@ -89,10 +92,7 @@ $lang->admin->setModule->closed = 'Closed'; $lang->admin->setModule->my = 'My'; $lang->admin->setModule->product = $lang->productCommon; -$lang->admin->setModule->scrum = 'Scrum ' . $lang->projectCommon; -$lang->admin->setModule->waterfall = 'Waterfall ' . $lang->projectCommon; -$lang->admin->setModule->agileplus = 'Agile + ' . $lang->projectCommon; -$lang->admin->setModule->waterfallplus = 'Waterfall + ' . $lang->projectCommon; +$lang->admin->setModule->project = $lang->projectCommon; $lang->admin->setModule->assetlib = 'Assetlib'; $lang->admin->setModule->other = 'Other'; @@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = 'Roadmap'; $lang->admin->setModule->track = 'Track'; $lang->admin->setModule->ER = $lang->ERCommon; $lang->admin->setModule->UR = $lang->URCommon; +$lang->admin->setModule->deliverable = 'Deliverable'; +$lang->admin->setModule->cm = 'Baseline'; +$lang->admin->setModule->change = 'Project Change'; $lang->admin->setModule->researchplan = 'Researchplan'; $lang->admin->setModule->gapanalysis = 'Gapanalysis'; $lang->admin->setModule->storylib = 'Story Lib'; diff --git a/module/admin/lang/en.php b/module/admin/lang/en.php index dc2c51c66e..bf5e419b27 100755 --- a/module/admin/lang/en.php +++ b/module/admin/lang/en.php @@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = 'You have been using ZenTao'; $lang->admin->api = 'API'; $lang->admin->log = 'Log'; $lang->admin->setting = 'Setting'; +$lang->admin->setFlow = 'Set Flow'; $lang->admin->pluginRecommendation = 'Recommended plug-ins'; $lang->admin->zentaoInfo = 'ZenTao information'; $lang->admin->officialAccount = 'WeChat Official Account'; @@ -60,10 +61,12 @@ $lang->admin->info->account = 'Your ZenTao account is %s.'; $lang->admin->info->log = 'Log that exceeds valid days will be deleted and you have to run cron.'; $lang->admin->notice = new stdclass(); -$lang->admin->notice->register = "Note: You haven't registered in ZenTao official website(www.zentao.pm). %s then get the Latest ZenTao Upgrades and News."; -$lang->admin->notice->ignore = "Ignore"; -$lang->admin->notice->int = "『%s』should be a positive integer."; -$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}."; +$lang->admin->notice->register = "Note: You haven't registered in ZenTao official website(www.zentao.pm). %s then get the Latest ZenTao Upgrades and News."; +$lang->admin->notice->ignore = "Ignore"; +$lang->admin->notice->int = "『%s』should be a positive integer."; +$lang->admin->notice->confirmDisableStoryType = "‘{type}’ function closed, the system will remove all stories associated with the project and execution, the operation is irreversible"; +$lang->admin->notice->openDependFeature = 'Using "{source}" function requires enabling "{target}" function synchronously.'; +$lang->admin->notice->closeDependFeature = 'Closing "{source}" function requires synchronously closing "{target}" function.'; $lang->admin->registerNotice = new stdclass(); $lang->admin->registerNotice->common = 'Register Account'; @@ -89,10 +92,7 @@ $lang->admin->setModule->closed = 'Closed'; $lang->admin->setModule->my = 'My'; $lang->admin->setModule->product = $lang->productCommon; -$lang->admin->setModule->scrum = 'Scrum ' . $lang->projectCommon; -$lang->admin->setModule->waterfall = 'Waterfall ' . $lang->projectCommon; -$lang->admin->setModule->agileplus = 'Agile + ' . $lang->projectCommon; -$lang->admin->setModule->waterfallplus = 'Waterfall + ' . $lang->projectCommon; +$lang->admin->setModule->project = $lang->projectCommon; $lang->admin->setModule->assetlib = 'Assetlib'; $lang->admin->setModule->other = 'Other'; @@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = 'Roadmap'; $lang->admin->setModule->track = 'Track'; $lang->admin->setModule->ER = $lang->ERCommon; $lang->admin->setModule->UR = $lang->URCommon; +$lang->admin->setModule->deliverable = 'Deliverable'; +$lang->admin->setModule->cm = 'Baseline'; +$lang->admin->setModule->change = 'Project Change'; $lang->admin->setModule->researchplan = 'Researchplan'; $lang->admin->setModule->gapanalysis = 'Gapanalysis'; $lang->admin->setModule->storylib = 'Story Lib'; diff --git a/module/admin/lang/fr.php b/module/admin/lang/fr.php index ce32ba4630..3e4f2bcc6f 100755 --- a/module/admin/lang/fr.php +++ b/module/admin/lang/fr.php @@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = ''; $lang->admin->api = 'API'; $lang->admin->log = 'Log'; $lang->admin->setting = 'Paramétrage'; +$lang->admin->setFlow = 'Set Flow'; $lang->admin->pluginRecommendation = 'Recommended plug-ins'; $lang->admin->zentaoInfo = 'ZenTao information'; $lang->admin->officialAccount = 'WeChat Official Account'; @@ -60,10 +61,12 @@ $lang->admin->info->account = 'Votre identifiant client ZenTao est %s.'; $lang->admin->info->log = 'Les logs qui dépassent la durée de conservation seront supprimés et vous devrez exécuter un cron.'; $lang->admin->notice = new stdclass(); -$lang->admin->notice->register = "Note: Vous n'êtes pas enregistré sur le site officiel de ZenTao (www.zentao.pm). %s vous pourrez ensuite obtenir les dernières Mises à Jour de ZenTao et des informations."; -$lang->admin->notice->ignore = "Ignorer"; -$lang->admin->notice->int = "『 %s 』 devrait être un entier positif."; -$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}."; +$lang->admin->notice->register = "Note: Vous n'êtes pas enregistré sur le site officiel de ZenTao (www.zentao.pm). %s vous pourrez ensuite obtenir les dernières Mises à Jour de ZenTao et des informations."; +$lang->admin->notice->ignore = "Ignorer"; +$lang->admin->notice->int = "『 %s 』 devrait être un entier positif."; +$lang->admin->notice->confirmDisableStoryType = "‘{type}’ fonction fermée, le système supprimera toutes les histoires associées au projet et à l'exécution, l'opération est irréversible"; +$lang->admin->notice->openDependFeature = 'Using "{source}" function requires enabling "{target}" function synchronously.'; +$lang->admin->notice->closeDependFeature = 'Closing "{source}" function requires synchronously closing "{target}" function.'; $lang->admin->registerNotice = new stdclass(); $lang->admin->registerNotice->common = 'Register Account'; @@ -89,10 +92,7 @@ $lang->admin->setModule->closed = 'Closed'; $lang->admin->setModule->my = 'My'; $lang->admin->setModule->product = $lang->productCommon; -$lang->admin->setModule->scrum = 'Scrum ' . $lang->projectCommon; -$lang->admin->setModule->waterfall = 'Waterfall ' . $lang->projectCommon; -$lang->admin->setModule->agileplus = 'Agile + ' . $lang->projectCommon; -$lang->admin->setModule->waterfallplus = 'Waterfall + ' . $lang->projectCommon; +$lang->admin->setModule->project = $lang->projectCommon; $lang->admin->setModule->assetlib = 'Assetlib'; $lang->admin->setModule->other = 'Other'; @@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = 'Roadmap'; $lang->admin->setModule->track = 'Track'; $lang->admin->setModule->ER = $lang->ERCommon; $lang->admin->setModule->UR = $lang->URCommon; +$lang->admin->setModule->deliverable = 'Deliverable'; +$lang->admin->setModule->cm = 'Baseline'; +$lang->admin->setModule->change = 'Project Change'; $lang->admin->setModule->researchplan = 'Researchplan'; $lang->admin->setModule->gapanalysis = 'Gapanalysis'; $lang->admin->setModule->storylib = 'Story Lib'; diff --git a/module/admin/lang/menu.php b/module/admin/lang/menu.php index 82d3ead24c..ed0e9f59a6 100644 --- a/module/admin/lang/menu.php +++ b/module/admin/lang/menu.php @@ -14,10 +14,6 @@ $lang->admin->menuList->company['name'] = $lang->admin->menuSetting['user']['na $lang->admin->menuList->company['desc'] = $lang->admin->menuSetting['user']['desc']; $lang->admin->menuList->company['order'] = 15; -$lang->admin->menuList->model['name'] = $lang->admin->menuSetting['model']['name']; -$lang->admin->menuList->model['desc'] = $lang->admin->menuSetting['model']['desc']; -$lang->admin->menuList->model['order'] = 20; - $lang->admin->menuList->feature['name'] = $lang->admin->menuSetting['feature']['name']; $lang->admin->menuList->feature['desc'] = $lang->admin->menuSetting['feature']['desc']; $lang->admin->menuList->feature['order'] = 25; @@ -71,44 +67,10 @@ $lang->admin->menuList->company['menuOrder']['5'] = 'dept'; $lang->admin->menuList->company['menuOrder']['10'] = 'browseUser'; $lang->admin->menuList->company['menuOrder']['15'] = 'group'; -$lang->admin->menuList->model['subMenu']['common'] = array('link' => "{$lang->globalSetting}|custom|required|module=project", 'subModule' => 'custom,subject,holiday,stage,deliverable,baseline', 'exclude' => 'stage-browse,stage-plusbrowse,stage-create,stage-edit,stage-batchcreate'); -$lang->admin->menuList->model['subMenu']['scrum'] = array('link' => "{$lang->scrumModel}|auditcl|scrumbrowse|", 'subModule' => 'auditcl'); -$lang->admin->menuList->model['subMenu']['waterfall'] = array('link' => "{$lang->waterfallModel}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'baseline-templatetype,stage-settype,stage-plusbrowse'); -$lang->admin->menuList->model['subMenu']['agileplus'] = array('link' => "{$lang->agilePlusModel}|auditcl|agileplusbrowse|", 'subModule' => 'auditcl'); -$lang->admin->menuList->model['subMenu']['waterfallplus'] = array('link' => "{$lang->waterfallPlusModel}|stage|plusbrowse|", 'subModule' => 'stage', 'exclude' => 'baseline-templatetype,stage-settype,stage-browse'); - -$lang->admin->menuList->model['menuOrder']['5'] = 'common'; -$lang->admin->menuList->model['menuOrder']['10'] = 'scrum'; -$lang->admin->menuList->model['menuOrder']['15'] = 'waterfall'; -$lang->admin->menuList->model['menuOrder']['20'] = 'agileplus'; -$lang->admin->menuList->model['menuOrder']['25'] = 'waterfallplus'; - -$lang->admin->menuList->model['tabMenu']['common']['project'] = array('link' => "{$lang->project->common}|custom|required|module=project", 'alias' => 'project', 'exclude' => 'custom-required,baseline-templatetype', 'links' => array('custom|set|module=project&field=unitList')); -if(helper::hasFeature('waterfall') || helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['common']['stage'] = array('link' => "{$lang->stage->type}|stage|settype|", 'subModule' => 'stage'); -if(helper::hasFeature('deliverable') && ($config->edition == 'max' || $config->edition == 'ipd')) $lang->admin->menuList->model['tabMenu']['common']['deliverable'] = array('link' => "{$lang->deliverable->common}|deliverable|browse|", 'subModule' => 'deliverable'); -$lang->admin->menuList->model['tabMenu']['common']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'alias' => 'set', 'exclude' => 'custom'); -$lang->admin->menuList->model['tabMenu']['common']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|", 'divider' => true); - -$lang->admin->menuList->model['tabMenu']['common']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|"); -$lang->admin->menuList->model['tabMenu']['common']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|')); -if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['waterfall']['stage'] = array('link' => "{$lang->stage->list}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'stage-plusbrowse'); -if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['waterfallplus']['stage'] = array('link' => "{$lang->stage->list}|stage|plusbrowse|", 'subModule' => 'stage', 'exclude' => 'stage-browse'); -$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['5'] = 'project'; -if(helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['common']['7'] = 'stage'; -$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['10'] = 'build'; -$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['35'] = 'flow'; -$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['40'] = 'percent'; -$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['45'] = 'hours'; -if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfall']['5'] = 'stage'; -if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfallplus']['5'] = 'stage'; -if($config->edition == 'max' or $config->edition == 'ipd') -{ - $lang->admin->menuList->model['tabMenu']['menuOrder']['common']['15'] = 'deliverable'; -} - $lang->admin->menuList->feature['subMenu']['my'] = array('link' => "{$lang->my->common}|custom|set|module=todo&field=priList", 'exclude' => 'set,required'); $lang->admin->menuList->feature['subMenu']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'exclude' => 'set,required', 'alias' => 'browsestoryconcept,product'); -$lang->admin->menuList->feature['subMenu']['execution'] = array('link' => "{$lang->execution->common}|custom|required|module=execution", 'exclude' => 'required,set', 'alias' => 'execution,limittaskdate'); +$lang->admin->menuList->feature['subMenu']['project'] = array('link' => "{$lang->projectCommon}|custom|required|module=project", 'alias' => 'flow,percent,hours,estimate,project', 'subModule' => 'subject,holiday,baseline,design,reviewcl', 'exclude' => 'set,required'); +$lang->admin->menuList->feature['subMenu']['execution'] = array('link' => "{$lang->execution->common}|custom|required|module=execution", 'exclude' => 'required,set', 'alias' => 'execution,limittaskdate', 'subModule' => 'stage'); $lang->admin->menuList->feature['subMenu']['qa'] = array('link' => "{$lang->qa->common}|custom|required|module=bug", 'exclude' => 'set,required'); $lang->admin->menuList->feature['subMenu']['kanban'] = array('link' => "{$lang->kanban->common}|custom|kanban|"); $lang->admin->menuList->feature['subMenu']['user'] = array('link' => "{$lang->user->common}|custom|required|module=user", 'links' => array('custom|set|module=user&field=roleList'), 'exclude' => 'set,required'); @@ -116,14 +78,21 @@ $lang->admin->menuList->feature['subMenu']['user'] = array('link' => "{$l $lang->admin->menuList->feature['tabMenu']['my']['todo'] = array('link' => "{$lang->todo->common}|custom|set|module=todo&field=priList", 'exclude' => 'custom-set'); $lang->admin->menuList->feature['tabMenu']['my']['block'] = array('link' => "{$lang->block->common}|custom|set|module=block&field=closed", 'exclude' => 'custom-set'); -$lang->admin->menuList->feature['tabMenu']['product']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'links' => array("custom|browsestoryconcept|", 'custom|product|'), 'alias' => 'browsestoryconcept,product', 'exclude' => 'custom-required'); +$lang->admin->menuList->feature['tabMenu']['product']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'links' => array("custom|browsestoryconcept|", 'custom|product|'), 'alias' => 'browsestoryconcept,product', 'exclude' => 'custom-required'); if($config->enableER) $lang->admin->menuList->feature['tabMenu']['product']['epic'] = array('link' => "{$lang->epic->common}|custom|required|module=epic", 'links' => array("custom|set|module=epic&field=categoryList"), 'exclude' => 'custom-required,custom-set'); if($config->URAndSR) $lang->admin->menuList->feature['tabMenu']['product']['requirement'] = array('link' => "{$lang->URCommon}|custom|required|module=requirement", 'links' => array("custom|set|module=requirement&field=categoryList"), 'exclude' => 'custom-required,custom-set'); $lang->admin->menuList->feature['tabMenu']['product']['story'] = array('link' => "{$lang->SRCommon}|custom|required|module=story", 'links' => array("custom|set|module=story&field=categoryList"), 'exclude' => 'custom-required,custom-set'); $lang->admin->menuList->feature['tabMenu']['product']['productplan'] = array('link' => "{$lang->productplan->shortCommon}|custom|required|module=productplan", 'exclude' => 'custom-required'); $lang->admin->menuList->feature['tabMenu']['product']['release'] = array('link' => "{$lang->release->common}|custom|required|module=release", 'exclude' => 'custom-required'); +$lang->admin->menuList->feature['tabMenu']['project']['project'] = array('link' => "{$lang->project->common}|custom|required|module=project", 'alias' => 'project', 'exclude' => 'custom-required'); +$lang->admin->menuList->feature['tabMenu']['project']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'exclude' => 'custom-required'); +$lang->admin->menuList->feature['tabMenu']['project']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|"); +$lang->admin->menuList->feature['tabMenu']['project']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|"); +$lang->admin->menuList->feature['tabMenu']['project']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|')); + $lang->admin->menuList->feature['tabMenu']['execution']['execution'] = array('link' => "{$lang->execution->common}|custom|required|module=execution", 'links' => array("custom|execution|"), 'alias' => 'execution', 'exclude' => 'custom-required'); +$lang->admin->menuList->feature['tabMenu']['execution']['stage'] = array('link' => "{$lang->stage->common}|stage|settype|", 'subModule' => 'stage', 'links' => array('stage|browse|')); $lang->admin->menuList->feature['tabMenu']['execution']['task'] = array('link' => "{$lang->task->common}|custom|required|module=task", 'links' => array('custom|set|module=task&field=priList', 'custom|limittaskdate|'), 'alias' => 'limittaskdate','exclude' => 'custom-required'); $lang->admin->menuList->feature['tabMenu']['qa']['bug'] = array('link' => "{$lang->bug->common}|custom|required|module=bug", 'links' => array("custom|set|module=bug&field=priList"), 'exclude' => 'custom-required,custom-set'); @@ -141,8 +110,16 @@ if($config->URAndSR) $lang->admin->menuList->feature['tabMenu']['menuOrder']['p $lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['20'] = 'story'; $lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['25'] = 'productplan'; $lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['30'] = 'release'; + +$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['5'] = 'project'; +$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['10'] = 'build'; +$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['35'] = 'flow'; +$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['40'] = 'percent'; +$lang->admin->menuList->feature['tabMenu']['menuOrder']['project']['45'] = 'hours'; + $lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['5'] = 'execution'; -$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['10'] = 'task'; +$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['10'] = 'stage'; +$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['15'] = 'task'; $lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['5'] = 'bug'; $lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['10'] = 'testcase'; $lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['15'] = 'testsuite'; @@ -152,10 +129,12 @@ $lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['30'] = 'ca $lang->admin->menuList->feature['menuOrder']['5'] = 'my'; $lang->admin->menuList->feature['menuOrder']['10'] = 'product'; -$lang->admin->menuList->feature['menuOrder']['15'] = 'execution'; -$lang->admin->menuList->feature['menuOrder']['20'] = 'qa'; -$lang->admin->menuList->feature['menuOrder']['25'] = 'kanban'; -$lang->admin->menuList->feature['menuOrder']['39'] = 'user'; +$lang->admin->menuList->feature['menuOrder']['15'] = 'project'; +$lang->admin->menuList->feature['menuOrder']['20'] = 'execution'; +$lang->admin->menuList->feature['menuOrder']['25'] = 'qa'; +$lang->admin->menuList->feature['menuOrder']['30'] = 'kanban'; +$lang->admin->menuList->feature['menuOrder']['35'] = 'doc'; +$lang->admin->menuList->feature['menuOrder']['40'] = 'user'; $lang->admin->menuList->feature['dividerMenu'] = ',user,'; @@ -186,29 +165,6 @@ $lang->admin->menuList->adminregister['desc'] = $lang->admin->menuSetting['admi $lang->admin->menuList->adminregister['link'] = 'admin|register'; $lang->admin->menuList->adminregister['order'] = 61; -if($config->edition != 'max' and $config->edition != 'ipd') -{ - unset($lang->admin->menuList->model['subMenu']['scrum']); - unset($lang->admin->menuList->model['subMenu']['agileplus']); - unset($lang->admin->menuList->model['menuOrder']['10']); - unset($lang->admin->menuList->model['menuOrder']['20']); -} -if(!helper::hasFeature('waterfall')) -{ - unset($lang->admin->menuList->model['subMenu']['waterfall']); - unset($lang->admin->menuList->model['menuOrder']['15']); -} -if(!helper::hasFeature('waterfallplus')) -{ - unset($lang->admin->menuList->model['subMenu']['waterfallplus']); - unset($lang->admin->menuList->model['menuOrder']['25']); -} -if($config->edition == 'max' or $config->edition == 'ipd') -{ - if(!helper::hasFeature('scrum_auditplan') and !helper::hasFeature('scrum_process')) unset($lang->admin->menuList->model['subMenu']['scrum'], $lang->admin->menuList->model['menuOrder']['10']); - if(!helper::hasFeature('agileplus_auditplan') and !helper::hasFeature('agileplus_process')) unset($lang->admin->menuList->model['subMenu']['agileplus'], $lang->admin->menuList->model['menuOrder']['20']); -} - if($config->vision == 'lite') { unset($lang->admin->menuList->system['subMenu']['mode']); @@ -218,15 +174,6 @@ if($config->vision == 'lite') unset($lang->admin->menuList->system['menuOrder']['45']); unset($lang->admin->menuList->system['menuOrder']['50']); - unset($lang->admin->menuList->model['subMenu']['scrum']); - unset($lang->admin->menuList->model['subMenu']['waterfall']); - unset($lang->admin->menuList->model['subMenu']['agileplus']); - unset($lang->admin->menuList->model['subMenu']['waterfallplus']); - unset($lang->admin->menuList->model['menuOrder']['10']); - unset($lang->admin->menuList->model['menuOrder']['15']); - unset($lang->admin->menuList->model['menuOrder']['20']); - unset($lang->admin->menuList->model['menuOrder']['25']); - unset($lang->admin->menuList->feature['subMenu']['product']); unset($lang->admin->menuList->feature['subMenu']['execution']); unset($lang->admin->menuList->feature['subMenu']['qa']); diff --git a/module/admin/lang/zh-cn.php b/module/admin/lang/zh-cn.php index 1eace6d069..99392928c9 100755 --- a/module/admin/lang/zh-cn.php +++ b/module/admin/lang/zh-cn.php @@ -34,6 +34,7 @@ $lang->admin->zentaoUsed = '您已使用禅道'; $lang->admin->api = '接口'; $lang->admin->log = '日志'; $lang->admin->setting = '设置'; +$lang->admin->setFlow = '流程配置'; $lang->admin->pluginRecommendation = '插件推荐'; $lang->admin->zentaoInfo = '禅道信息'; $lang->admin->officialAccount = '官方公众号'; @@ -60,10 +61,12 @@ $lang->admin->info->account = "您的禅道社区账户为%s。"; $lang->admin->info->log = '超出存天数的日志会被删除,需要开启计划任务。'; $lang->admin->notice = new stdclass(); -$lang->admin->notice->register = "可%s禅道社区 www.zentao.net,及时获得禅道最新信息。"; -$lang->admin->notice->ignore = "不再提示"; -$lang->admin->notice->int = "『%s』应当是正整数。"; -$lang->admin->notice->openUR = "请先开启{$lang->URCommon},才能使用{$lang->ERCommon}。"; +$lang->admin->notice->register = "可%s禅道社区 www.zentao.net,及时获得禅道最新信息。"; +$lang->admin->notice->ignore = "不再提示"; +$lang->admin->notice->int = "『%s』应当是正整数。"; +$lang->admin->notice->confirmDisableStoryType = "‘{type}’功能关闭后,系统会将项目和执行内已经关联的相应‘{type}’全部移除, 操作不可逆"; +$lang->admin->notice->openDependFeature = '使用“{source}”功能时需要同步开启“{target}”功能。'; +$lang->admin->notice->closeDependFeature = '关闭“{source}”功能时需要同步关闭“{target}”功能。'; $lang->admin->registerNotice = new stdclass(); $lang->admin->registerNotice->common = '注册新帐号'; @@ -89,12 +92,9 @@ $lang->admin->setModule->closed = '已关闭'; $lang->admin->setModule->my = '地盘'; $lang->admin->setModule->product = $lang->productCommon; -$lang->admin->setModule->scrum = '敏捷' . $lang->projectCommon; -$lang->admin->setModule->waterfall = '瀑布' . $lang->projectCommon; -$lang->admin->setModule->agileplus = '融合敏捷' . $lang->projectCommon; -$lang->admin->setModule->waterfallplus = '融合瀑布' . $lang->projectCommon; +$lang->admin->setModule->project = $lang->projectCommon; $lang->admin->setModule->assetlib = '资产库'; -$lang->admin->setModule->other = '通用功能'; +$lang->admin->setModule->other = '其他功能'; $lang->admin->setModule->score = '积分'; $lang->admin->setModule->repo = '代码'; @@ -108,6 +108,9 @@ $lang->admin->setModule->roadmap = '路线图'; $lang->admin->setModule->track = '矩阵'; $lang->admin->setModule->ER = $lang->ERCommon; $lang->admin->setModule->UR = $lang->URCommon; +$lang->admin->setModule->deliverable = '交付物'; +$lang->admin->setModule->cm = '基线'; +$lang->admin->setModule->change = '项目变更'; $lang->admin->setModule->researchplan = '调研'; $lang->admin->setModule->gapanalysis = '培训'; $lang->admin->setModule->storylib = '需求库'; @@ -171,8 +174,6 @@ $lang->admin->menuSetting['user']['name'] = '人员管理'; $lang->admin->menuSetting['user']['desc'] = '维护部门、添加人员、分组配置权限。'; $lang->admin->menuSetting['switch']['name'] = '功能开关'; $lang->admin->menuSetting['switch']['desc'] = '打开、关闭系统部分功能。'; -$lang->admin->menuSetting['model']['name'] = '项目模型配置'; -$lang->admin->menuSetting['model']['desc'] = '不同项目管理模型和项目通用要素配置。'; $lang->admin->menuSetting['feature']['name'] = '功能配置'; $lang->admin->menuSetting['feature']['desc'] = '按照功能菜单进行系统的要素配置。'; $lang->admin->menuSetting['template']['name'] = '文档模板'; diff --git a/module/admin/model.php b/module/admin/model.php index 701332ca59..d798f417d0 100755 --- a/module/admin/model.php +++ b/module/admin/model.php @@ -101,10 +101,11 @@ class adminModel extends model * 设置后台二级导航。 * Set admin menu. * + * @param int $groupID * @access public * @return void */ - public function setMenu(): void + public function setMenu(int $groupID = 0): void { $this->checkPrivMenu(); @@ -117,6 +118,7 @@ class adminModel extends model $this->setSwitcher($menuKey); if(isset($this->lang->admin->menuList->$menuKey)) { + if(in_array($menuKey, array('projectflow', 'productflow')) && $groupID) $this->lang->admin->menuList->{$menuKey}['subMenu'] = $this->lang->admin->menuList->{$menuKey}['childMenu']; if(isset($this->lang->admin->menuList->{$menuKey}['subMenu'])) { $moduleName = $this->app->rawModule; @@ -132,6 +134,23 @@ class adminModel extends model if($firstParam == $subMenuKey) $subModule = 'custom'; } + /* 针对项目流程和产品流程菜单做特殊处理。 */ + if(in_array($menuKey, array('projectflow', 'productflow')) && $groupID) + { + $workflowGroup = $this->fetchByID($groupID, 'workflowgroup'); + if($subMenuKey == 'review' && $workflowGroup->projectModel == 'kanban') continue; + if($subMenuKey == 'stage' && in_array($workflowGroup->projectModel, array('kanban', 'scrum', 'agileplus'))) continue; + + $subMenu['link'] = sprintf($subMenu['link'], $groupID); + if(isset($subMenu['subMenu'])) + { + foreach($subMenu['subMenu'] as $subSubMenuKey => $subSubMenu) + { + $subMenu['subMenu'][$subSubMenuKey]['link'] = sprintf($subSubMenu['link'], $groupID); + } + } + } + if(!empty($subModule)) $subMenu['subModule'] = $subModule; if(isset($this->lang->admin->menuList->{$menuKey}['tabMenu'][$subMenuKey])) { @@ -192,12 +211,6 @@ class adminModel extends model if($dept && common::hasPriv('company', 'browse')) $menu['link'] = helper::createLink('company', 'browse'); } - /* Set links to authorized navigation. */ - if(isset($menu['subMenu'])) - { - $menu = $this->setSubMenu($menuKey, $menu); - } - if(!empty($menu['link']) && strpos($menu['link'], '|') !== false) { list($module, $method, $params) = explode('|', $menu['link'] . '|'); @@ -205,6 +218,12 @@ class adminModel extends model if(($this->app->user->admin || $module . $method != 'adminregister') && common::hasPriv($module, $method)) $menu['disabled'] = false; } + /* Set links to authorized navigation. */ + if(!in_array($menuKey, array('productflow', 'projectflow')) && isset($menu['subMenu'])) + { + $menu = $this->setSubMenu($menuKey, $menu); + } + $order = $menu['order']; $orders[$order] = $menuKey; @@ -235,7 +254,7 @@ class adminModel extends model { /* Reorder secondary navigation. */ $subMenuList = array(); - $subMenuOrders = $menu['menuOrder']; + $subMenuOrders = !empty($menu['menuOrder']) ? $menu['menuOrder'] : array(); if(empty($subMenuOrders)) return array(); ksort($subMenuOrders); foreach($subMenuOrders as $value) @@ -280,8 +299,11 @@ class adminModel extends model $menu['subMenu'][$subMenuKey]['link'] = substr($subMenu['link'], 0, strpos($subMenu['link'], '|') + 1) . $module . '|' . $method . '|' . $params; /* Update the level 1 navigation link. */ - if(empty($menu['link'])) $menu['link'] = helper::createLink($module, $method, $params); - $menu['disabled'] = false; + if(!empty($menu['disabled'])) + { + $menu['link'] = helper::createLink($module, $method, $params); + $menu['disabled'] = false; + } } } @@ -371,6 +393,11 @@ class adminModel extends model $moduleName = $this->app->rawModule; $methodName = $this->app->rawMethod; $firstParam = $this->app->rawParams ? reset($this->app->rawParams) : ''; + if($this->config->edition == 'open') + { + $this->config->admin->menuGroup['feature'][] = 'stage|browse'; + $this->config->admin->menuGroup['feature'][] = 'stage|batchcreate'; + } foreach($this->config->admin->menuGroup as $menuKey => $menuGroup) { @@ -384,6 +411,11 @@ class adminModel extends model { if(in_array($firstParam, $this->config->admin->menuModuleGroup[$menuKey]["custom|$methodName"])) return $menuKey; } + elseif($moduleName == 'workflowgroup' && $firstParam && !in_array($methodName, array('project', 'product'))) + { + $workflowgroup = $this->loadModel('workflowgroup')->fetchByID($firstParam); + return $workflowgroup->type == 'project' ? 'projectflow' : 'productflow'; + } else { return $menuKey; @@ -460,8 +492,8 @@ class adminModel extends model { if($this->config->vision == 'lite' and !in_array($menuKey, $this->config->admin->liteMenuList)) continue; $class = $menuKey == $currentMenuKey ? "active" : ''; - if($menuGroup['disabled']) $class .= ' disabled not-clear-menu'; - $output .= "
  • " . html::a($menuGroup['disabled'] ? '###' : $menuGroup['link'], "" . $menuGroup['name']) . "
  • "; + if(!empty($menuGroup['disabled'])) $class .= ' disabled not-clear-menu'; + $output .= "
  • " . html::a(!empty($menuGroup['disabled']) ? '###' : (!empty($menuGroup['link']) ? $menuGroup['link'] : ''), (!empty($menuGroup['icon']) ? "" : "") . $menuGroup['name']) . "
  • "; } $output .= ""; diff --git a/module/admin/test/model/checkprivmenu.php b/module/admin/test/model/checkprivmenu.php index 0dab63c14e..08375f4a01 100755 --- a/module/admin/test/model/checkprivmenu.php +++ b/module/admin/test/model/checkprivmenu.php @@ -24,8 +24,9 @@ su('admin'); $adminTest = new adminTest(); -r($adminTest->checkPrivMenuTest()) && p('success,hasMenuList') && e('1,1'); // 测试步骤1:基本菜单权限检查功能 -r($adminTest->checkPrivMenuTest()) && p('hasOrderAttribute') && e('1'); // 测试步骤2:验证所有菜单项都有order属性 -r($adminTest->checkPrivMenuTest()) && p('hasDisabledAttribute') && e('1'); // 测试步骤3:验证所有菜单项都有disabled属性 -r($adminTest->checkPrivMenuTest()) && p('isSorted') && e('1'); // 测试步骤4:验证菜单按order排序 -r($adminTest->checkPrivMenuTest()) && p('success') && e('1'); // 测试步骤5:验证菜单列表为对象类型 \ No newline at end of file +// 4. 强制要求:必须包含至少5个测试步骤 +r($adminTest->checkPrivMenuTest()) && p('hasMenuList') && e('1'); // 步骤1:检查是否有菜单列表 +r($adminTest->checkPrivMenuTest()) && p('menuCount') && e('11'); // 步骤2:检查菜单数量 +r($adminTest->checkPrivMenuTest()) && p('hasLinkedMenu') && e('1'); // 步骤3:检查是否有已链接菜单 +r($adminTest->checkPrivMenuTest()) && p('hasDisabledMenu') && e('1'); // 步骤4:检查是否有已禁用菜单 +r($adminTest->checkPrivMenuTest()) && p('hasMenuList,menuCount') && e('1,11'); // 步骤5:验证菜单列表结构 diff --git a/module/admin/test/model/getmenukey.php b/module/admin/test/model/getmenukey.php index 1e6c38a26b..6431cc0075 100644 --- a/module/admin/test/model/getmenukey.php +++ b/module/admin/test/model/getmenukey.php @@ -14,9 +14,9 @@ cid=14980 - 访问编辑用户页面 @company - 访问权限列表页面 @company - 访问功能设置页面 @switch -- 访问项目必填项页面 @model -- 访问阶段类型页面 @model -- 访问检查对象页面 @model +- 访问项目必填项页面 @feature +- 访问阶段类型页面 @feature +- 访问检查对象页面 @projectflow - 访问自定义待办页面 @feature - 访问自定义产品页面 @feature - 访问自定义看板页面 @feature @@ -44,28 +44,28 @@ $params1 = array('module' => 'project'); $params2 = array('module' => 'todo'); $params3 = array('module' => 'baseline'); -r($admin->getMenuKeyTest('admin', 'index')) && p() && e('null'); //访问后台页面 -r($admin->getMenuKeyTest('backup', 'index')) && p() && e('system'); //访问备份页面 -r($admin->getMenuKeyTest('cron', 'index')) && p() && e('system'); //访问定时列表页面 -r($admin->getMenuKeyTest('cron', 'create')) && p() && e('system'); //访问定时创建页面 -r($admin->getMenuKeyTest('dept', 'browse')) && p() && e('company'); //访问部门页面 -r($admin->getMenuKeyTest('user', 'edit')) && p() && e('company'); //访问编辑用户页面 -r($admin->getMenuKeyTest('group', 'browse')) && p() && e('company'); //访问权限列表页面 -r($admin->getMenuKeyTest('admin', 'setmodule')) && p() && e('switch'); //访问功能设置页面 -r($admin->getMenuKeyTest('custom', 'required', $params1)) && p() && e('model'); //访问项目必填项页面 -r($admin->getMenuKeyTest('stage', 'settype')) && p() && e('model'); //访问阶段类型页面 -r($admin->getMenuKeyTest('auditcl', 'scrumbrowse')) && p() && e('model'); //访问检查对象页面 -r($admin->getMenuKeyTest('custom', 'set', $params2)) && p() && e('feature'); //访问自定义待办页面 -r($admin->getMenuKeyTest('custom', 'product')) && p() && e('feature'); //访问自定义产品页面 -r($admin->getMenuKeyTest('custom', 'kanban')) && p() && e('feature'); //访问自定义看板页面 -r($admin->getMenuKeyTest('mail', 'detect')) && p() && e('message'); //访问发信配置页面 -r($admin->getMenuKeyTest('webhook', 'browse')) && p() && e('message'); //访问Webhook页面 -r($admin->getMenuKeyTest('sms', 'index')) && p() && e('message'); //访问短信页面 -r($admin->getMenuKeyTest('message', 'browser')) && p() && e('message'); //访问浏览器页面 -r($admin->getMenuKeyTest('message', 'setting')) && p() && e('message'); //访问信息配置页面 -r($admin->getMenuKeyTest('extension', 'browse')) && p() && e('extension'); //访问插件页面 -r($admin->getMenuKeyTest('dev', 'api')) && p() && e('dev'); //访问API页面 -r($admin->getMenuKeyTest('dev', 'db')) && p() && e('dev'); //访问数据库页面 -r($admin->getMenuKeyTest('dev', 'editor')) && p() && e('dev'); //访问编辑器页面 -r($admin->getMenuKeyTest('entry', 'browse')) && p() && e('dev'); //访问应用页面 -r($admin->getMenuKeyTest('convert', 'convertjira')) && p() && e('convert'); //访问数据导入页面 +r($admin->getMenuKeyTest('admin', 'index')) && p() && e('null'); //访问后台页面 +r($admin->getMenuKeyTest('backup', 'index')) && p() && e('system'); //访问备份页面 +r($admin->getMenuKeyTest('cron', 'index')) && p() && e('system'); //访问定时列表页面 +r($admin->getMenuKeyTest('cron', 'create')) && p() && e('system'); //访问定时创建页面 +r($admin->getMenuKeyTest('dept', 'browse')) && p() && e('company'); //访问部门页面 +r($admin->getMenuKeyTest('user', 'edit')) && p() && e('company'); //访问编辑用户页面 +r($admin->getMenuKeyTest('group', 'browse')) && p() && e('company'); //访问权限列表页面 +r($admin->getMenuKeyTest('admin', 'setmodule')) && p() && e('switch'); //访问功能设置页面 +r($admin->getMenuKeyTest('custom', 'required', $params1)) && p() && e('feature'); //访问项目必填项页面 +r($admin->getMenuKeyTest('stage', 'settype')) && p() && e('feature'); //访问阶段类型页面 +r($admin->getMenuKeyTest('auditcl', 'scrumbrowse')) && p() && e('projectflow'); //访问检查对象页面 +r($admin->getMenuKeyTest('custom', 'set', $params2)) && p() && e('feature'); //访问自定义待办页面 +r($admin->getMenuKeyTest('custom', 'product')) && p() && e('feature'); //访问自定义产品页面 +r($admin->getMenuKeyTest('custom', 'kanban')) && p() && e('feature'); //访问自定义看板页面 +r($admin->getMenuKeyTest('mail', 'detect')) && p() && e('message'); //访问发信配置页面 +r($admin->getMenuKeyTest('webhook', 'browse')) && p() && e('message'); //访问Webhook页面 +r($admin->getMenuKeyTest('sms', 'index')) && p() && e('message'); //访问短信页面 +r($admin->getMenuKeyTest('message', 'browser')) && p() && e('message'); //访问浏览器页面 +r($admin->getMenuKeyTest('message', 'setting')) && p() && e('message'); //访问信息配置页面 +r($admin->getMenuKeyTest('extension', 'browse')) && p() && e('extension'); //访问插件页面 +r($admin->getMenuKeyTest('dev', 'api')) && p() && e('dev'); //访问API页面 +r($admin->getMenuKeyTest('dev', 'db')) && p() && e('dev'); //访问数据库页面 +r($admin->getMenuKeyTest('dev', 'editor')) && p() && e('dev'); //访问编辑器页面 +r($admin->getMenuKeyTest('entry', 'browse')) && p() && e('dev'); //访问应用页面 +r($admin->getMenuKeyTest('convert', 'convertjira')) && p() && e('convert'); //访问数据导入页面 diff --git a/module/admin/test/model/setmenu.php b/module/admin/test/model/setmenu.php index 1dc81a5daa..277ac72fc0 100755 --- a/module/admin/test/model/setmenu.php +++ b/module/admin/test/model/setmenu.php @@ -30,4 +30,4 @@ r($adminTest->setMenuTest('admin', 'safe', array())) && p('hasSwitcherMenu') && r($adminTest->setMenuTest('user', 'browse', array())) && p('hasSwitcherMenu') && e('1'); // 步骤2:user模块在company组中的方法测试 r($adminTest->setMenuTest('custom', 'required', array('product'))) && p('hasSwitcherMenu') && e('1'); // 步骤3:custom模块在feature组中的特殊方法测试 r($adminTest->setMenuTest('admin', 'index', array())) && p('hasSwitcherMenu') && e('~~'); // 步骤4:admin模块不在任何组中的方法测试 -r($adminTest->setMenuTest('nonexistent', 'test', array())) && p('hasSwitcherMenu') && e('~~'); // 步骤5:不存在模块的处理测试 \ No newline at end of file +r($adminTest->setMenuTest('nonexistent', 'test', array())) && p('hasSwitcherMenu') && e('~~'); // 步骤5:不存在模块的处理测试 diff --git a/module/admin/test/model/setsubmenu.php b/module/admin/test/model/setsubmenu.php index f9ddc3963a..3db0b01764 100755 --- a/module/admin/test/model/setsubmenu.php +++ b/module/admin/test/model/setsubmenu.php @@ -98,4 +98,4 @@ $devMenu = array( 'link' => '', 'disabled' => true ); -r($adminTest->setSubMenuTest('dev', $devMenu)) && p('subMenu,editor,link') && e('Editor|editor|index|'); \ No newline at end of file +r($adminTest->setSubMenuTest('dev', $devMenu)) && p('subMenu,editor,link') && e('Editor|editor|index|'); diff --git a/module/admin/test/model/setswitcher.php b/module/admin/test/model/setswitcher.php index 814d144616..ac36b5585b 100755 --- a/module/admin/test/model/setswitcher.php +++ b/module/admin/test/model/setswitcher.php @@ -26,4 +26,4 @@ r($adminTest->setSwitcherTest('system')) && p() && e('success'); // 步骤1: r($adminTest->setSwitcherTest('company')) && p() && e('success'); // 步骤2:正常情况,company菜单 r($adminTest->setSwitcherTest('feature')) && p() && e('success'); // 步骤3:正常情况,feature菜单 r($adminTest->setSwitcherTest('')) && p() && e('0'); // 步骤4:空参数情况 -r($adminTest->setSwitcherTest('nonexistent')) && p() && e('success'); // 步骤5:不存在菜单键测试异常处理 \ No newline at end of file +r($adminTest->setSwitcherTest('nonexistent')) && p() && e('success'); // 步骤5:不存在菜单键测试异常处理 diff --git a/module/admin/ui/index.html.php b/module/admin/ui/index.html.php index e0c336fffb..4895fc37ff 100755 --- a/module/admin/ui/index.html.php +++ b/module/admin/ui/index.html.php @@ -81,11 +81,12 @@ $buildUsed = function(int $amount, string $unit = ''): array }; $settingItems = array(); +$flowItems = array(); foreach($lang->admin->menuList as $menuKey => $menu) { if($config->vision == 'lite' and !in_array($menuKey, $config->admin->liteMenuList)) continue; - $settingItems[] = div + $items = div ( setClass('pb-4 pr-4 h-32 w-1/' . ($config->vision == 'lite' ? 3 : 5)), col @@ -101,9 +102,9 @@ foreach($lang->admin->menuList as $menuKey => $menu) div ( setClass('flex gap-1 font-bold text-md'), - img(set::src("static/svg/admin-{$menuKey}.svg")), + !empty($menu['icon']) ? icon(setClass("svg-icon rounded-lg content-center bg-{$menu['bg']} text-white"), $menu['icon']) : img(set::src("static/svg/admin-{$menuKey}.svg")), $menu['name'], - $menuKey != 'adminregister' ? + !empty($config->admin->helpURL[$menuKey]) ? a ( setClass('text-gray'), @@ -122,6 +123,15 @@ foreach($lang->admin->menuList as $menuKey => $menu) ) ) ); + + if(!empty($menu['group']) && $menu['group'] == 'flow') + { + $flowItems[] = $items; + } + else + { + $settingItems[] = $items; + } } $pluginItems = array(); @@ -248,7 +258,7 @@ if($config->edition != 'ipd') div ( set::style(array('width' => '70%')), - div + $settingItems ? div ( setID('settings'), setClass('bg-white rounded-md mb-4'), @@ -259,7 +269,19 @@ div on::click('redirectSetting'), $settingItems ) - ), + ) : null, + $flowItems ? div + ( + setID('flows'), + setClass('bg-white rounded-md mb-4'), + $buildHeader($lang->admin->setFlow), + div + ( + setClass('flex flex-wrap pl-4'), + on::click('redirectSetting'), + $flowItems + ) + ) : null, $pluginItems ? div ( setID('plugin'), diff --git a/module/admin/ui/setmodule.html.php b/module/admin/ui/setmodule.html.php index 0f78f4d264..ba692e7bde 100644 --- a/module/admin/ui/setmodule.html.php +++ b/module/admin/ui/setmodule.html.php @@ -12,7 +12,17 @@ namespace zin; global $app; $app->loadLang('project'); -jsVar('confirmDisableStoryType', $lang->project->confirmDisableStoryType); +jsVar('confirmDisableStoryType', $lang->admin->notice->confirmDisableStoryType); +jsVar('edition', $config->edition); +jsVar('enableER', $config->enableER); +jsVar('URAndSR', $config->URAndSR); +jsVar('ERCommon', $lang->ERCommon); +jsVar('URCommon', $lang->URCommon); +jsVar('changeLang', $lang->admin->setModule->change); +jsVar('deliverableLang', $lang->admin->setModule->deliverable); +jsVar('cmLang', $lang->admin->setModule->cm); +jsVar('openDependFeature', $lang->admin->notice->openDependFeature); +jsVar('closeDependFeature', $lang->admin->notice->closeDependFeature); if(strpos(",$disabledFeatures,", ",productUR,") !== false) $disabledFeatures .= ',productER'; @@ -20,6 +30,7 @@ $rows = array(); foreach($config->featureGroup as $group => $features) { if(strpos(",$disabledFeatures,", ",$group,") !== false) continue; + if($config->systemMode == 'light' && $group == 'project') continue; $hasData = false; foreach($features as $feature) @@ -84,8 +95,10 @@ foreach($config->featureGroup as $group => $features) formPanel ( + set::id('setModuleForm'), set::title($lang->admin->setModuleIndex), set::actions(false), + set::ajax(array('beforeSubmit' => jsRaw("submitForm"))), h::table ( setClass('border w-full'), diff --git a/module/ai/lang/zh-cn.php b/module/ai/lang/zh-cn.php index 0aeff84735..3675a5dd9b 100644 --- a/module/ai/lang/zh-cn.php +++ b/module/ai/lang/zh-cn.php @@ -1048,7 +1048,7 @@ $lang->ai->formSchema['testcase']['create']->properties->type->type $lang->ai->formSchema['testcase']['create']->properties->type->description = '用例的类型'; $lang->ai->formSchema['testcase']['create']->properties->type->enum = array('feature', 'performance', 'config', 'install', 'security', 'interface', 'unit', 'other'); $lang->ai->formSchema['testcase']['create']->properties->stage->type = 'string'; -$lang->ai->formSchema['testcase']['create']->properties->stage->description = '用例适用阶段'; +$lang->ai->formSchema['testcase']['create']->properties->stage->description = '用例适用环节'; $lang->ai->formSchema['testcase']['create']->properties->stage->enum = array('unittest', 'feature', 'intergrate', 'system', 'smoke', 'bvt'); $lang->ai->formSchema['testcase']['create']->properties->title->type = 'string'; $lang->ai->formSchema['testcase']['create']->properties->title->description = '用例的标题'; diff --git a/module/bi/config/dataviews.php b/module/bi/config/dataviews.php index 084df13f52..b479cd01b7 100644 --- a/module/bi/config/dataviews.php +++ b/module/bi/config/dataviews.php @@ -247,7 +247,7 @@ $testcasefields['id'] = array('name' => '用例编号', 'field' => $testcasefields['title'] = array('name' => '用例标题', 'field' => 'title', 'object' => 'testcase', 'type' => 'string'); $testcasefields['pri'] = array('name' => '优先级', 'field' => 'pri', 'object' => 'testcase', 'type' => 'option'); $testcasefields['type'] = array('name' => '用例类型', 'field' => 'type', 'object' => 'testcase', 'type' => 'option'); -$testcasefields['stage'] = array('name' => '适用阶段', 'field' => 'stage', 'object' => 'testcase', 'type' => 'option'); +$testcasefields['stage'] = array('name' => '适用环节', 'field' => 'stage', 'object' => 'testcase', 'type' => 'option'); $testcasefields['status'] = array('name' => '用例状态', 'field' => 'status', 'object' => 'testcase', 'type' => 'option'); $testcasefields['version'] = array('name' => '用例版本', 'field' => 'version', 'object' => 'testcase', 'type' => 'number'); $testcasefields['product_id'] = array('name' => '编号', 'field' => 'id', 'object' => 'product', 'type' => 'object'); diff --git a/module/bi/config/metrics.php b/module/bi/config/metrics.php index 5b7dcc902d..43a094d3cc 100644 --- a/module/bi/config/metrics.php +++ b/module/bi/config/metrics.php @@ -4145,6 +4145,19 @@ $config->bi->builtin->metrics[] = array 'definition' => "所有风险个数求和\n指派给为某人\n过滤已删除的风险\n过滤已关闭的风险\n过滤已删除项目的风险" ); +$reviewissueMetrics = array(); +$reviewissueMetrics['name'] = '按人员统计的被指派的评审意见数'; +$reviewissueMetrics['alias'] = '被指派的评审意见数'; +$reviewissueMetrics['code'] = 'count_of_assigned_reviewissue_in_user'; +$reviewissueMetrics['purpose'] = 'scale'; +$reviewissueMetrics['scope'] = 'user'; +$reviewissueMetrics['object'] = 'reviewissue'; +$reviewissueMetrics['unit'] = 'count'; +$reviewissueMetrics['dateType'] = 'nodate'; +$reviewissueMetrics['desc'] = '按人员统计的被指派的评审意见数表示每个人被指派的评审意见数量之和,反映了每个人员需要处理的评审意见数量的规模。该数值越大,说明需要投入越多的时间处理评审意见'; +$reviewissueMetrics['definition'] = "所有评审意见个数求和\n指派给为某人\n过滤已删除的评审意见\n过滤已关闭的评审意见\n过滤已删除项目的评审意见"; +$config->bi->builtin->metrics[] = $reviewissueMetrics; + $config->bi->builtin->metrics[] = array ( 'name' => '按人员统计的被指派的问题数', diff --git a/module/block/config/feedback.php b/module/block/config/feedback.php index 69497b3b71..1b8bb024d3 100644 --- a/module/block/config/feedback.php +++ b/module/block/config/feedback.php @@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'biz', 'ipd'))) $config->block->feedback = new stdclass(); $config->block->feedback->dtable = new stdclass(); $config->block->feedback->dtable->fieldList = array(); - $config->block->feedback->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); - $config->block->feedback->dtable->fieldList['title'] = array('name' => 'title', 'title' => $lang->feedback->title, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'feedback', 'method' => $config->vision == 'lite' ? 'view' : 'adminView', 'params' => 'feedbackID={id}')); - $config->block->feedback->dtable->fieldList['product'] = array('name' => 'product', 'title' => $lang->feedback->product, 'type' => 'category', 'sort' => true); - $config->block->feedback->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->feedback->pri, 'type' => 'pri', 'sort' => true); - $config->block->feedback->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->feedback->status, 'type' => 'status', 'sort' => true, 'statusMap' => $lang->feedback->statusList); - $config->block->feedback->dtable->fieldList['type'] = array('name' => 'type', 'title' => $lang->feedback->type, 'type' => 'category', 'sort' => true, 'map' => $lang->feedback->typeList); + $config->block->feedback->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); + $config->block->feedback->dtable->fieldList['title'] = array('name' => 'title', 'title' => zget($lang->feedback, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'feedback', 'method' => $config->vision == 'lite' ? 'view' : 'adminView', 'params' => 'feedbackID={id}')); + $config->block->feedback->dtable->fieldList['product'] = array('name' => 'product', 'title' => zget($lang->feedback, 'product'), 'type' => 'category', 'sort' => true); + $config->block->feedback->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->feedback, 'pri'), 'type' => 'pri', 'sort' => true); + $config->block->feedback->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->feedback, 'status'), 'type' => 'status', 'sort' => true, 'statusMap' => zget($lang->feedback, 'statusList', array())); + $config->block->feedback->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->feedback, 'type'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->feedback, 'typeList', array())); $config->block->feedback->dtable->short = new stdclass(); $config->block->feedback->dtable->short->fieldList['id'] = $config->block->feedback->dtable->fieldList['id']; diff --git a/module/block/config/issue.php b/module/block/config/issue.php index 1947b29b90..be206ad868 100644 --- a/module/block/config/issue.php +++ b/module/block/config/issue.php @@ -7,13 +7,13 @@ if(in_array($config->edition, array('max', 'ipd'))) $config->block->issue = new stdclass(); $config->block->issue->dtable = new stdclass(); $config->block->issue->dtable->fieldList = array(); - $config->block->issue->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); - $config->block->issue->dtable->fieldList['title'] = array('name' => 'title', 'title' => $lang->issue->title, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'issue', 'method' => 'view', 'params' => 'issueID={id}')); - $config->block->issue->dtable->fieldList['type'] = array('name' => 'type', 'title' => $lang->issue->type, 'type' => 'category', 'sort' => true, 'map' => $lang->issue->typeList); - $config->block->issue->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->issue->priAB, 'type' => 'pri', 'sort' => true); - $config->block->issue->dtable->fieldList['severity'] = array('name' => 'severity', 'title' => $lang->issue->severity, 'type' => 'severity', 'sort' => true); - $config->block->issue->dtable->fieldList['owner'] = array('name' => 'owner', 'title' => $lang->issue->owner, 'type' => 'user', 'sort' => true); - $config->block->issue->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->issue->status, 'type' => 'status', 'sort' => true, 'statusMap' => $lang->issue->statusList); + $config->block->issue->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); + $config->block->issue->dtable->fieldList['title'] = array('name' => 'title', 'title' => zget($lang->issue, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'issue', 'method' => 'view', 'params' => 'issueID={id}')); + $config->block->issue->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->issue, 'type'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->issue, 'typeList', array())); + $config->block->issue->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->issue, 'priAB'), 'type' => 'pri', 'sort' => true); + $config->block->issue->dtable->fieldList['severity'] = array('name' => 'severity', 'title' => zget($lang->issue, 'severity'), 'type' => 'severity', 'sort' => true); + $config->block->issue->dtable->fieldList['owner'] = array('name' => 'owner', 'title' => zget($lang->issue, 'owner'), 'type' => 'user', 'sort' => true); + $config->block->issue->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->issue, 'status'), 'type' => 'status', 'sort' => true, 'statusMap' => zget($lang->issue, 'statusList', array())); $config->block->issue->dtable->short = new stdclass(); $config->block->issue->dtable->short->fieldList['id'] = $config->block->issue->dtable->fieldList['id']; diff --git a/module/block/config/meeting.php b/module/block/config/meeting.php index 3fa767e0a0..ad325e335d 100644 --- a/module/block/config/meeting.php +++ b/module/block/config/meeting.php @@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'ipd'))) $config->block->meeting = new stdclass(); $config->block->meeting->dtable = new stdclass(); $config->block->meeting->dtable->fieldList = array(); - $config->block->meeting->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); - $config->block->meeting->dtable->fieldList['name'] = array('name' => 'name', 'title' => $lang->meeting->name, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'meeting', 'method' => 'view', 'params' => 'meetingID={id}')); - $config->block->meeting->dtable->fieldList['mode'] = array('name' => 'mode', 'title' => $lang->meeting->mode, 'type' => 'category', 'sort' => true, 'map' => $lang->meeting->modeList); - $config->block->meeting->dtable->fieldList['dept'] = array('name' => 'dept', 'title' => $lang->meeting->dept, 'type' => 'category', 'sort' => true); - $config->block->meeting->dtable->fieldList['date'] = array('name' => 'date', 'title' => $lang->meeting->date, 'type' => 'date', 'sort' => true); - $config->block->meeting->dtable->fieldList['host'] = array('name' => 'host', 'title' => $lang->meeting->host, 'type' => 'user', 'sort' => true); + $config->block->meeting->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); + $config->block->meeting->dtable->fieldList['name'] = array('name' => 'name', 'title' => zget($lang->meeting, 'name'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'meeting', 'method' => 'view', 'params' => 'meetingID={id}')); + $config->block->meeting->dtable->fieldList['mode'] = array('name' => 'mode', 'title' => zget($lang->meeting, 'mode'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->meeting, 'modeList', array())); + $config->block->meeting->dtable->fieldList['dept'] = array('name' => 'dept', 'title' => zget($lang->meeting, 'dept'), 'type' => 'category', 'sort' => true); + $config->block->meeting->dtable->fieldList['date'] = array('name' => 'date', 'title' => zget($lang->meeting, 'date'), 'type' => 'date', 'sort' => true); + $config->block->meeting->dtable->fieldList['host'] = array('name' => 'host', 'title' => zget($lang->meeting, 'host'), 'type' => 'user', 'sort' => true); $config->block->meeting->dtable->short = new stdclass(); $config->block->meeting->dtable->short->fieldList['id'] = $config->block->meeting->dtable->fieldList['id']; diff --git a/module/block/config/reviewissue.php b/module/block/config/reviewissue.php new file mode 100644 index 0000000000..9ef84760de --- /dev/null +++ b/module/block/config/reviewissue.php @@ -0,0 +1,20 @@ +edition, array('max', 'ipd'))) +{ + global $lang, $app; + $app->loadLang('reviewissue'); + + $config->block->reviewissue = new stdclass(); + $config->block->reviewissue->dtable = new stdclass(); + $config->block->reviewissue->dtable->fieldList = array(); + $config->block->reviewissue->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); + $config->block->reviewissue->dtable->fieldList['name'] = array('name' => 'title', 'title' => zget($lang->reviewissue, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'reviewissue', 'method' => 'view', 'params' => 'reviewissueID={id}')); + $config->block->reviewissue->dtable->fieldList['opinion'] = array('name' => 'opinion', 'title' => zget($lang->reviewissue, 'opinion'), 'type' => 'text', 'sort' => true); + $config->block->reviewissue->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->reviewissue, 'type'), 'type' => 'category', 'map' => array('review' => $lang->reviewissue->common), 'sort' => true); + $config->block->reviewissue->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->reviewissue, 'status'), 'type' => 'status', 'statusMap' => zget($lang->reviewissue, 'statusList', array()), 'sort' => true); + + $config->block->reviewissue->dtable->short = new stdclass(); + $config->block->reviewissue->dtable->short->fieldList['id'] = $config->block->reviewissue->dtable->fieldList['id']; + $config->block->reviewissue->dtable->short->fieldList['name'] = $config->block->reviewissue->dtable->fieldList['name']; + $config->block->reviewissue->dtable->short->fieldList['status'] = $config->block->reviewissue->dtable->fieldList['status']; +} diff --git a/module/block/config/risk.php b/module/block/config/risk.php index a348ef9437..47e6ebe1a0 100644 --- a/module/block/config/risk.php +++ b/module/block/config/risk.php @@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'ipd'))) $config->block->risk = new stdclass(); $config->block->risk->dtable = new stdclass(); $config->block->risk->dtable->fieldList = array(); - $config->block->risk->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); - $config->block->risk->dtable->fieldList['name'] = array('name' => 'name', 'title' => $lang->risk->name, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'risk', 'method' => 'view', 'params' => 'riskID={id}')); - $config->block->risk->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->risk->priAB, 'type' => 'status', 'statusMap' => $lang->risk->priList, 'sort' => true); - $config->block->risk->dtable->fieldList['rate'] = array('name' => 'rate', 'title' => $lang->risk->rate, 'type' => 'number', 'width' => '100px', 'sort' => true); - $config->block->risk->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->risk->status, 'type' => 'status', 'statusMap' => $lang->risk->statusList, 'sort' => true); - $config->block->risk->dtable->fieldList['strategy'] = array('name' => 'strategy', 'title' => $lang->risk->strategy, 'type' => 'status', 'statusMap' => $lang->risk->strategyList, 'sort' => true); + $config->block->risk->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); + $config->block->risk->dtable->fieldList['name'] = array('name' => 'name', 'title' => zget($lang->risk, 'name'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'risk', 'method' => 'view', 'params' => 'riskID={id}')); + $config->block->risk->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->risk, 'priAB'), 'type' => 'status', 'statusMap' => zget($lang->risk, 'priList', array()), 'sort' => true); + $config->block->risk->dtable->fieldList['rate'] = array('name' => 'rate', 'title' => zget($lang->risk, 'rate'), 'type' => 'number', 'width' => '100px', 'sort' => true); + $config->block->risk->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->risk, 'status'), 'type' => 'status', 'statusMap' => zget($lang->risk, 'statusList', array()), 'sort' => true); + $config->block->risk->dtable->fieldList['strategy'] = array('name' => 'strategy', 'title' => zget($lang->risk, 'strategy'), 'type' => 'status', 'statusMap' => zget($lang->risk, 'strategyList', array()), 'sort' => true); $config->block->risk->dtable->short = new stdclass(); $config->block->risk->dtable->short->fieldList['id'] = $config->block->risk->dtable->fieldList['id']; diff --git a/module/block/config/ticket.php b/module/block/config/ticket.php index 58abb3eaba..5a1ef7a6f1 100644 --- a/module/block/config/ticket.php +++ b/module/block/config/ticket.php @@ -7,12 +7,12 @@ if(in_array($config->edition, array('max', 'biz', 'ipd'))) $config->block->ticket = new stdclass(); $config->block->ticket->dtable = new stdclass(); $config->block->ticket->dtable->fieldList = array(); - $config->block->ticket->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); - $config->block->ticket->dtable->fieldList['title'] = array('name' => 'title', 'title' => $lang->ticket->title, 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'ticket', 'method' => 'view', 'params' => 'ticketID={id}')); - $config->block->ticket->dtable->fieldList['product'] = array('name' => 'product', 'title' => $lang->ticket->product, 'type' => 'category', 'sort' => true); - $config->block->ticket->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => $lang->ticket->priAB, 'type' => 'pri', 'sort' => true); - $config->block->ticket->dtable->fieldList['status'] = array('name' => 'status', 'title' => $lang->ticket->status, 'type' => 'status', 'sort' => true, 'statusMap' => $lang->ticket->statusList); - $config->block->ticket->dtable->fieldList['type'] = array('name' => 'type', 'title' => $lang->ticket->type, 'type' => 'category', 'sort' => true, 'map' => $lang->ticket->typeList); + $config->block->ticket->dtable->fieldList['id'] = array('name' => 'id', 'title' => $lang->idAB, 'type' => 'id' , 'sort' => 'number'); + $config->block->ticket->dtable->fieldList['title'] = array('name' => 'title', 'title' => zget($lang->ticket, 'title'), 'type' => 'title', 'sort' => true, 'flex' => 1, 'link' => array('module' => 'ticket', 'method' => 'view', 'params' => 'ticketID={id}')); + $config->block->ticket->dtable->fieldList['product'] = array('name' => 'product', 'title' => zget($lang->ticket, 'product'), 'type' => 'category', 'sort' => true); + $config->block->ticket->dtable->fieldList['pri'] = array('name' => 'pri', 'title' => zget($lang->ticket, 'priAB'), 'type' => 'pri', 'sort' => true); + $config->block->ticket->dtable->fieldList['status'] = array('name' => 'status', 'title' => zget($lang->ticket, 'status'), 'type' => 'status', 'sort' => true, 'statusMap' => zget($lang->ticket, 'statusList', array())); + $config->block->ticket->dtable->fieldList['type'] = array('name' => 'type', 'title' => zget($lang->ticket, 'type'), 'type' => 'category', 'sort' => true, 'map' => zget($lang->ticket, 'typeList', array())); $config->block->ticket->dtable->short = new stdclass(); $config->block->ticket->dtable->short->fieldList['id'] = $config->block->ticket->dtable->fieldList['id']; diff --git a/module/block/lang/de.php b/module/block/lang/de.php index d9ea37f5e4..cc5d9f1372 100644 --- a/module/block/lang/de.php +++ b/module/block/lang/de.php @@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = 'Builds'; $lang->block->availableBlocks['testcase'] = 'Testcase'; $lang->block->availableBlocks['testtask'] = 'Testtask'; $lang->block->availableBlocks['risk'] = 'Risks'; +$lang->block->availableBlocks['reviewissue'] = 'Review Issue'; $lang->block->availableBlocks['issue'] = 'Issues'; $lang->block->availableBlocks['meeting'] = 'Meetings'; $lang->block->availableBlocks['feedback'] = 'Feedbacks'; diff --git a/module/block/lang/en.php b/module/block/lang/en.php index 2408ab9564..4c5187625d 100644 --- a/module/block/lang/en.php +++ b/module/block/lang/en.php @@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = 'Builds'; $lang->block->availableBlocks['testcase'] = 'Testcase'; $lang->block->availableBlocks['testtask'] = 'Testtask'; $lang->block->availableBlocks['risk'] = 'Risks'; +$lang->block->availableBlocks['reviewissue'] = 'Review Issue'; $lang->block->availableBlocks['issue'] = 'Issues'; $lang->block->availableBlocks['meeting'] = 'Meetings'; $lang->block->availableBlocks['feedback'] = 'Feedbacks'; diff --git a/module/block/lang/fr.php b/module/block/lang/fr.php index 6a686220e9..d519260a2a 100644 --- a/module/block/lang/fr.php +++ b/module/block/lang/fr.php @@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = 'Builds'; $lang->block->availableBlocks['testcase'] = 'Testcase'; $lang->block->availableBlocks['testtask'] = 'Testtask'; $lang->block->availableBlocks['risk'] = 'Risks'; +$lang->block->availableBlocks['reviewissue'] = 'Review Issue'; $lang->block->availableBlocks['issue'] = 'Issues'; $lang->block->availableBlocks['meeting'] = 'Meetings'; $lang->block->availableBlocks['feedback'] = 'Feedbacks'; diff --git a/module/block/lang/zh-cn.php b/module/block/lang/zh-cn.php index 7cdbe8ec56..be617e770d 100644 --- a/module/block/lang/zh-cn.php +++ b/module/block/lang/zh-cn.php @@ -234,6 +234,7 @@ $lang->block->availableBlocks['build'] = '构建列表'; $lang->block->availableBlocks['testcase'] = '用例'; $lang->block->availableBlocks['testtask'] = '测试单'; $lang->block->availableBlocks['risk'] = '风险'; +$lang->block->availableBlocks['reviewissue'] = '评审问题'; $lang->block->availableBlocks['issue'] = '问题'; $lang->block->availableBlocks['meeting'] = '会议'; $lang->block->availableBlocks['feedback'] = '反馈'; diff --git a/module/block/test/lib/zen.class.php b/module/block/test/lib/zen.class.php index a67426e2ca..14a0ece819 100644 --- a/module/block/test/lib/zen.class.php +++ b/module/block/test/lib/zen.class.php @@ -1166,4 +1166,4 @@ class blockZenTest extends baseTest if(dao::isError()) return dao::getError(); return $result; } -} +} \ No newline at end of file diff --git a/module/block/test/zen/printassigntomeblock.php b/module/block/test/zen/printassigntomeblock.php new file mode 100644 index 0000000000..664eabd791 --- /dev/null +++ b/module/block/test/zen/printassigntomeblock.php @@ -0,0 +1,45 @@ +#!/usr/bin/env php +params = new stdclass(); +$block1->params->count = 15; + +$block2 = new stdclass(); +$block2->params = new stdclass(); +$block2->params->count = 10; + +$block3 = new stdclass(); +$block3->params = new stdclass(); +$block3->params->count = 5; + +// 5. 强制要求:必须包含至少5个测试步骤 +r($blockTest->printAssignToMeBlockTest($block1)) && p('success') && e('1'); // 步骤1:测试方法执行成功返回success为true +r($blockTest->printAssignToMeBlockTest($block1)) && p('hasViewPriv') && e('1'); // 步骤2:测试返回对象包含权限验证信息 +r($blockTest->printAssignToMeBlockTest($block1)) && p('hasData') && e('1'); // 步骤3:测试返回对象包含数据信息 +r($blockTest->printAssignToMeBlockTest($block1)) && p('totalCount') && e('14'); // 步骤4:测试总数量统计正确 +r($blockTest->printAssignToMeBlockTest()) && p('success') && e('1'); // 步骤5:测试不传入block参数时使用默认参数 diff --git a/module/block/test/zen/processblockforrender.php b/module/block/test/zen/processblockforrender.php index f9f890ec4b..be02785f5c 100755 --- a/module/block/test/zen/processblockforrender.php +++ b/module/block/test/zen/processblockforrender.php @@ -101,4 +101,4 @@ $block3->width = 2; $block3->height = 3; $block3->left = 0; $block3->top = 0; -r(count($blockTest->processBlockForRenderTest(array($block1, $block2, $block3), 0))) && p() && e('3'); \ No newline at end of file +r(count($blockTest->processBlockForRenderTest(array($block1, $block2, $block3), 0))) && p() && e('3'); diff --git a/module/block/ui/dashboard.html.php b/module/block/ui/dashboard.html.php index 5b6867e974..df77c85b0a 100644 --- a/module/block/ui/dashboard.html.php +++ b/module/block/ui/dashboard.html.php @@ -160,7 +160,7 @@ else setID('metriclibModal'), html($metriclibRemind) ); - }; + } } } diff --git a/module/block/ui/scrumoverviewblock.html.php b/module/block/ui/scrumoverviewblock.html.php index 4c78f50670..a4c361019a 100644 --- a/module/block/ui/scrumoverviewblock.html.php +++ b/module/block/ui/scrumoverviewblock.html.php @@ -11,7 +11,9 @@ declare(strict_types=1); namespace zin; -$cells = array(); +$cells = array(); +$hasRisk = helper::hasFeature('risk'); +$hasIssue = helper::hasFeature('issue'); foreach($config->block->projectstatistic->items as $module => $items) { $cellItems = array(); @@ -129,11 +131,11 @@ panel $project->status == 'closed' ? $lang->block->projectstatistic->projectClosed : $lang->block->projectstatistic->longTimeProject ) ), - $config->edition != 'open' && $config->edition != 'biz' ? cell + in_array($config->edition, array('max', 'ipd')) && ($hasIssue || $hasRisk) ? cell ( setClass('flex-1 text-left' . (!$longBlock ? ' w-full' : '')), icon('bullhorn text-warning'), - span + $hasRisk ? span ( setClass('text-gray mr-5'), $lang->block->projectstatistic->existRisks, @@ -142,8 +144,8 @@ panel setClass('font-bold text-warning'), round(zget($project, 'risks', 0), 2) ) - ), - span + ) : null, + $hasIssue ? span ( setClass('text-gray'), $lang->block->projectstatistic->existIssues, @@ -152,7 +154,7 @@ panel setClass('font-bold text-warning'), zget($project, 'issues', 0) ) - ) + ) : null ) : '', (!empty($project->executions) && $project->multiple) ? cell ( diff --git a/module/block/zen.php b/module/block/zen.php index cf63bc37f8..73bf360711 100644 --- a/module/block/zen.php +++ b/module/block/zen.php @@ -200,10 +200,17 @@ class blockZen extends block foreach($blocks as $key => $block) { /* 将没有开启功能区块过虑。 */ - if($block->code == 'waterfallrisk' && !helper::hasFeature('waterfall_risk')) continue; - if($block->code == 'waterfallissue' && !helper::hasFeature('waterfall_issue')) continue; - if($block->code == 'scrumrisk' && !helper::hasFeature('scrum_risk')) continue; - if($block->code == 'scrumissue' && !helper::hasFeature('scrum_issue')) continue; + if(!helper::hasFeature('project_risk') && ($block->code == 'waterfallrisk' || $block->code == 'scrumrisk')) + { + unset($blocks[$key]); + continue; + } + + if(!helper::hasFeature('project_issue') && ($block->code == 'waterfallissue' || $block->code == 'scrumissue')) + { + unset($blocks[$key]); + continue; + } /* 过滤目前已经不存在的区块。 */ if(!isset($this->lang->block->moduleList[$block->code]) && !isset($this->lang->block->modules[$block->module]->availableBlocks[$block->code])) @@ -2058,9 +2065,9 @@ class blockZen extends block * * @param object $block * @access protected - * @return void + * @return array */ - protected function printAssignToMeBlock(object $block): void + protected function printAssignToMeBlock(object $block): array { $hasIssue = helper::hasFeature('issue'); $hasRisk = helper::hasFeature('risk'); @@ -2098,13 +2105,14 @@ class blockZen extends block if((common::hasPriv('task', 'view') && $this->config->vision == 'rnd') || (common::hasPriv('researchtask', 'view') && $this->config->vision == 'or')) $hasViewPriv['task'] = true; if(common::hasPriv('story', 'view') && $this->config->vision != 'lite') $hasViewPriv['story'] = true; if($this->config->URAndSR && common::hasPriv('story', 'view') && $this->config->vision != 'lite') $hasViewPriv['requirement'] = true; - if(common::hasPriv('bug', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['bug'] = true; - if(common::hasPriv('testcase', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testcase'] = true; - if(common::hasPriv('testtask', 'cases') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testtask'] = true; - if(common::hasPriv('risk', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasRisk) $hasViewPriv['risk'] = true; - if(common::hasPriv('issue', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasIssue) $hasViewPriv['issue'] = true; - if(common::hasPriv('meeting', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasMeeting) $hasViewPriv['meeting'] = true; - if((common::hasPriv('feedback', 'view') || common::hasPriv('feedback', 'adminView')) && in_array($this->config->edition, array('max', 'biz', 'ipd'))) $hasViewPriv['feedback'] = true; + if(common::hasPriv('bug', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['bug'] = true; + if(common::hasPriv('testcase', 'view') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testcase'] = true; + if(common::hasPriv('testtask', 'cases') && !in_array($this->config->vision, array('lite', 'or'))) $hasViewPriv['testtask'] = true; + if(common::hasPriv('risk', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasRisk) $hasViewPriv['risk'] = true; + if(common::hasPriv('reviewissue', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasRisk) $hasViewPriv['reviewissue'] = true; + if(common::hasPriv('issue', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasIssue) $hasViewPriv['issue'] = true; + if(common::hasPriv('meeting', 'view') && in_array($this->config->edition, array('max', 'ipd')) && !in_array($this->config->vision, array('lite', 'or')) && $hasMeeting) $hasViewPriv['meeting'] = true; + if((common::hasPriv('feedback', 'view') || common::hasPriv('feedback', 'adminView')) && in_array($this->config->edition, array('max', 'biz', 'ipd'))) $hasViewPriv['feedback'] = true; if(common::hasPriv('ticket', 'view') && in_array($this->config->edition, array('max', 'biz', 'ipd')) && $this->config->vision != 'or') $hasViewPriv['ticket'] = true; $objectList = array('todo' => 'todos', 'task' => 'tasks', 'bug' => 'bugs', 'story' => 'stories', 'requirement' => 'requirements'); @@ -2112,7 +2120,7 @@ class blockZen extends block { if($hasRisk) $objectList += array('risk' => 'risks'); if($hasIssue) $objectList += array('issue' => 'issues'); - $objectList += array('feedback' => 'feedbacks', 'ticket' => 'tickets'); + $objectList += array('feedback' => 'feedbacks', 'ticket' => 'tickets', 'reviewissue' => 'reviewissues'); if($this->config->edition == 'ipd' && $this->config->vision == 'or') $objectList += array('demand' => 'demands'); } @@ -2129,7 +2137,7 @@ class blockZen extends block ->beginIF($objectType == 'story' || $objectType == 'requirement')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')->fi() ->beginIF($objectType == 'bug')->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')->fi() ->beginIF($objectType == 'task')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution=t2.id')->fi() - ->beginIF($objectType == 'issue' || $objectType == 'risk')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')->fi() + ->beginIF(in_array($objectType, array('issue', 'risk', 'reviewissue')))->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')->fi() ->beginIF($objectType == 'ticket')->leftJoin(TABLE_USER)->alias('t2')->on('t1.openedBy = t2.account')->fi() ->beginIF($objectType == 'demand')->leftJoin(TABLE_DEMANDPOOL)->alias('t2')->on('t1.pool = t2.id')->fi() ->where('t1.deleted')->eq(0) @@ -2141,7 +2149,7 @@ class blockZen extends block ->beginIF($objectType == 'todo')->andWhere('t1.cycle')->eq(0)->andWhere('t1.status')->eq('wait')->andWhere('t1.vision')->eq($this->config->vision)->fi() ->beginIF($objectType != 'todo')->andWhere('t1.status')->ne('closed')->fi() ->beginIF($objectType == 'feedback')->andWhere('t1.status')->in('wait, noreview')->fi() - ->beginIF($objectType == 'issue' || $objectType == 'risk')->andWhere('t2.deleted')->eq(0)->fi() + ->beginIF(in_array($objectType, array('issue', 'risk', 'reviewissue')))->andWhere('t2.deleted')->eq(0)->fi() ->beginIF($objectType == 'ticket')->andWhere('t1.status')->in('wait,doing,done')->fi() ->beginIF($objectType == 'demand')->andWhere('t2.deleted')->eq(0)->fi() ->orderBy($orderBy) @@ -2196,9 +2204,10 @@ class blockZen extends block } } - if($objectType == 'bug') $this->app->loadLang('bug'); - if($objectType == 'risk') $this->app->loadLang('risk'); - if($objectType == 'issue') $this->app->loadLang('issue'); + if($objectType == 'bug') $this->app->loadLang('bug'); + if($objectType == 'risk') $this->app->loadLang('risk'); + if($objectType == 'reviewissue') $this->app->loadLang('reviewissue'); + if($objectType == 'issue') $this->app->loadLang('issue'); if($objectType == 'feedback' || $objectType == 'ticket') { @@ -2247,6 +2256,7 @@ class blockZen extends block $this->view->products = $this->dao->select('id, name')->from(TABLE_PRODUCT)->where('deleted')->eq('0')->fetchPairs('id', 'name'); $this->view->projects = $this->dao->select('id, name')->from(TABLE_PROJECT)->where('deleted')->eq('0')->fetchPairs('id', 'name'); $this->view->count = $count; + return $count; } /** diff --git a/module/bug/config/table.php b/module/bug/config/table.php index 89ae83a379..ac213b96d4 100644 --- a/module/bug/config/table.php +++ b/module/bug/config/table.php @@ -171,6 +171,7 @@ $config->bug->dtable->fieldList['activatedCount']['sortType'] = true; if($config->edition != 'open') { + $app->loadLang('custom'); $config->bug->dtable->fieldList['relatedObject']['name'] = 'relatedObject'; $config->bug->dtable->fieldList['relatedObject']['title'] = $lang->custom->relateObject; $config->bug->dtable->fieldList['relatedObject']['sortType'] = false; diff --git a/module/bug/ui/export.html.php b/module/bug/ui/export.html.php index ea78f3416b..853299e654 100644 --- a/module/bug/ui/export.html.php +++ b/module/bug/ui/export.html.php @@ -1,11 +1,11 @@ - * @package xxx + * @package bug * @link https://www.zentao.net */ include '../../file/ui/export.html.php'; diff --git a/module/bug/zen.php b/module/bug/zen.php index 26f5800c41..69b05590bc 100644 --- a/module/bug/zen.php +++ b/module/bug/zen.php @@ -675,6 +675,7 @@ class bugZen extends bug ->setDefault('product', $oldBug->product) ->setDefault('deleteFiles', array()) ->setDefault('lastEditedBy', $this->app->user->account) + ->setDefault('resolvedDate', $oldBug->resolvedDate) ->add('lastEditedDate', $now) ->join('openedBuild,mailto,relatedBug,os,browser', ',') ->setIF($formData->data->assignedTo != $oldBug->assignedTo, 'assignedDate', $now) diff --git a/module/common/lang/common.php b/module/common/lang/common.php index 94fc2564f6..e09fae2225 100644 --- a/module/common/lang/common.php +++ b/module/common/lang/common.php @@ -133,7 +133,6 @@ $lang->featureswitch = new stdclass(); $lang->importdata = new stdclass(); $lang->systemsetting = new stdclass(); $lang->staffmanage = new stdclass(); -$lang->modelconfig = new stdclass(); $lang->featureconfig = new stdclass(); $lang->doctemplate = new stdclass(); $lang->notifysetting = new stdclass(); diff --git a/module/common/lang/de.php b/module/common/lang/de.php index 1ad0bb1f54..e3b87cd3fd 100644 --- a/module/common/lang/de.php +++ b/module/common/lang/de.php @@ -123,6 +123,7 @@ $lang->saveSuccess = 'Gespeichert'; $lang->importSuccess = 'Gespeichert'; $lang->fail = 'Fehlgeschlagen'; $lang->addFiles = 'Hinzufügen '; +$lang->delFiles = 'Deleted Files '; $lang->deleteSuccess = 'Gelöscht'; $lang->deleted = 'Gelöscht'; $lang->files = 'Datei '; @@ -302,7 +303,6 @@ $lang->featureswitch->common = 'Features On/Off'; $lang->importdata->common = 'Import data'; $lang->systemsetting->common = 'System setting'; $lang->staffmanage->common = 'User management'; -$lang->modelconfig->common = 'Pattern setting'; $lang->featureconfig->common = 'Features config'; $lang->doctemplate->common = 'Doc template'; $lang->notifysetting->common = 'Notification'; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index 30fccec73e..c92da3c3cd 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -123,6 +123,7 @@ $lang->saveSuccess = 'Saved'; $lang->importSuccess = 'Saved'; $lang->fail = 'Fail'; $lang->addFiles = 'Added Files '; +$lang->delFiles = 'Deleted Files '; $lang->deleteSuccess = 'Deleted Success'; $lang->deleted = 'Deleted'; $lang->files = 'Files '; @@ -302,7 +303,6 @@ $lang->featureswitch->common = 'Features On/Off'; $lang->importdata->common = 'Import data'; $lang->systemsetting->common = 'System setting'; $lang->staffmanage->common = 'User management'; -$lang->modelconfig->common = 'Pattern setting'; $lang->featureconfig->common = 'Features config'; $lang->doctemplate->common = 'Doc template'; $lang->notifysetting->common = 'Notification'; diff --git a/module/common/lang/fr.php b/module/common/lang/fr.php index 5693905d17..de5476a52a 100644 --- a/module/common/lang/fr.php +++ b/module/common/lang/fr.php @@ -123,6 +123,7 @@ $lang->saveSuccess = 'Sauvegardé'; $lang->importSuccess = 'Sauvé'; $lang->fail = 'Echec'; $lang->addFiles = 'Fichiers ajoutés '; +$lang->delFiles = 'Deleted Files '; $lang->deleteSuccess = 'Supprimé'; $lang->deleted = 'Supprimé'; $lang->files = 'Fichiers '; @@ -302,7 +303,6 @@ $lang->featureswitch->common = 'Features On/Off'; $lang->importdata->common = 'Import data'; $lang->systemsetting->common = 'System setting'; $lang->staffmanage->common = 'User management'; -$lang->modelconfig->common = 'Pattern setting'; $lang->featureconfig->common = 'Features config'; $lang->doctemplate->common = 'Doc template'; $lang->notifysetting->common = 'Notification'; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index 39f2b557a2..952e744e1f 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -123,6 +123,7 @@ $lang->saveSuccess = '保存成功'; $lang->importSuccess = '导入成功'; $lang->fail = '失败'; $lang->addFiles = '上传了附件 '; +$lang->delFiles = '删除了附件 '; $lang->deleteSuccess = '删除成功'; $lang->deleted = '已删除'; $lang->files = '附件 '; @@ -302,7 +303,6 @@ $lang->featureswitch->common = '功能开关'; $lang->importdata->common = '数据导入'; $lang->systemsetting->common = '系统设置'; $lang->staffmanage->common = '人员管理'; -$lang->modelconfig->common = '项目模型配置'; $lang->featureconfig->common = '功能配置'; $lang->doctemplate->common = '文档模板'; $lang->notifysetting->common = '通知设置'; diff --git a/module/common/model.php b/module/common/model.php index 0e10d7473c..d7081e8803 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -822,7 +822,9 @@ class commonModel extends model { $subModule = isset($dropMenuItem['subModule']) ? explode(',', $dropMenuItem['subModule']) : array(); $subExclude = isset($dropMenuItem['exclude']) ? $dropMenuItem['exclude'] : $exclude; + $subAlias = zget($dropMenuItem, 'alias', ''); if($subModule and in_array($currentModule, $subModule) and strpos(",$subExclude,", ",$currentModule-$currentMethod,") === false) $activeMainMenu = true; + if(strpos(",$subAlias,", ",$currentModule-$currentMethod,") !== false) $activeMainMenu = true; } if($activeMainMenu) $activeMenu = $dropMenuName; @@ -1902,11 +1904,16 @@ eof; /* Check the project is closed. */ $productModuleList = array('story', 'bug', 'testcase', 'case', 'testtask', 'release'); - if(!in_array($module, $productModuleList) and !empty($object->project) and is_numeric($object->project) and empty($config->CRProject)) + if(!in_array($module, $productModuleList) and !empty($object->project) and is_numeric($object->project) and (empty($config->CRProject) || empty($config->CRExecution))) { if(!isset($projectsStatus[$object->project])) { $project = $commonModel->loadModel('project')->getByID((int)$object->project); + if($project && $project->status == 'closed') + { + /* 有的表项目和执行都存在project里。 */ + return $project->type == 'project' ? !empty($config->CRProject) : !empty($config->CRExecution); + } $projectsStatus[$object->project] = $project ? $project->status : ''; } if($projectsStatus[$object->project] == 'closed') return false; diff --git a/module/cron/control.php b/module/cron/control.php index 99d1f7de54..40880f28ce 100755 --- a/module/cron/control.php +++ b/module/cron/control.php @@ -255,7 +255,7 @@ class cron extends control { $roles = array(); - $settings = $this->dao->select('*')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('cron')->fetchAll(); + $settings = $this->dao->select('*')->from(TABLE_CONFIG)->where('owner')->eq('system')->andWhere('module')->eq('cron')->fetchAll('id', false); $scheduler = array('execId' => 0, 'lastTime' => ''); $consumerCount = 0; diff --git a/module/custom/control.php b/module/custom/control.php index db4876a82c..3fdb995071 100644 --- a/module/custom/control.php +++ b/module/custom/control.php @@ -45,8 +45,8 @@ class custom extends control public function set(string $module = 'story', string $field = 'priList', string $lang = '') { if(empty($lang)) $lang = $this->app->getClientLang(); - if($module == 'user' && $field == 'priList') $field = 'statusList'; - if($module == 'block' && $field == 'priList')$field = 'closed'; + if($module == 'user' && $field == 'priList') $field = 'statusList'; + if($module == 'block' && $field == 'priList') $field = 'closed'; $currentLang = $this->app->getClientLang(); $this->customZen->assignVarsForSet($module, $field, $lang, $currentLang); @@ -56,6 +56,7 @@ class custom extends control $this->customZen->setFieldListForSet($module, $field); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($module == 'baseline' && $field == 'objectList') return $this->sendSuccess(array('load' => true)); + if($module == 'reviewcl') return $this->sendSuccess(array('load' => true)); return $this->sendSuccess(array('load' => $this->createLink('custom', 'set', "module=$module&field=$field&lang=" . ($lang == 'all' ? $lang : '')))); } diff --git a/module/custom/lang/de.php b/module/custom/lang/de.php index 25d5846cf8..c5c9080fed 100644 --- a/module/custom/lang/de.php +++ b/module/custom/lang/de.php @@ -21,7 +21,7 @@ $lang->custom->score = 'Score'; $lang->custom->timezone = 'Timezone'; $lang->custom->scoreReset = 'Reset Score'; $lang->custom->scoreTitle = 'Point Feature'; -$lang->custom->productName = $lang->productCommon; +$lang->custom->productName = $lang->productCommon . ' Close Setting'; $lang->custom->convertFactor = 'Convert factor'; $lang->custom->region = 'Interval'; $lang->custom->tips = 'Tips'; @@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = 'The Range Of Begin & End'; $lang->custom->limitTaskDateAction = 'Set start and end date required'; $lang->custom->closeSetting = 'Close Setting'; $lang->custom->gradeRule = 'Allow cross-level segmentation'; +$lang->custom->setExecutionClose = 'Execution Close Setting'; $lang->custom->gradeRuleList['cross'] = 'Yes'; $lang->custom->gradeRuleList['stepwise'] = 'No'; diff --git a/module/custom/lang/en.php b/module/custom/lang/en.php index abaaaf6ed1..1f5712fcf2 100644 --- a/module/custom/lang/en.php +++ b/module/custom/lang/en.php @@ -21,7 +21,7 @@ $lang->custom->score = 'Point'; $lang->custom->timezone = 'Timezone'; $lang->custom->scoreReset = 'Reset Points'; $lang->custom->scoreTitle = 'Point Feature'; -$lang->custom->productName = $lang->productCommon; +$lang->custom->productName = $lang->productCommon . ' Close Setting'; $lang->custom->convertFactor = 'Convert factor'; $lang->custom->region = 'Interval'; $lang->custom->tips = 'Tips'; @@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = 'The Range Of Begin & End'; $lang->custom->limitTaskDateAction = 'Set start and end date required'; $lang->custom->closeSetting = 'Close Setting'; $lang->custom->gradeRule = 'Allow cross-level segmentation'; +$lang->custom->setExecutionClose = 'Execution Close Setting'; $lang->custom->gradeRuleList['cross'] = 'Yes'; $lang->custom->gradeRuleList['stepwise'] = 'No'; diff --git a/module/custom/lang/fr.php b/module/custom/lang/fr.php index 76b2ad005b..5bff639ed0 100644 --- a/module/custom/lang/fr.php +++ b/module/custom/lang/fr.php @@ -21,7 +21,7 @@ $lang->custom->score = 'Point'; $lang->custom->timezone = 'Timezone'; $lang->custom->scoreReset = 'Réinit Points'; $lang->custom->scoreTitle = 'Fonctionnalité des Points'; -$lang->custom->productName = $lang->productCommon; +$lang->custom->productName = $lang->productCommon . ' Close Setting'; $lang->custom->convertFactor = 'Convert factor'; $lang->custom->region = 'Interval'; $lang->custom->tips = 'Tips'; @@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = 'The Range Of Begin & End'; $lang->custom->limitTaskDateAction = 'Set start and end date required'; $lang->custom->closeSetting = 'Close Setting'; $lang->custom->gradeRule = 'Allow cross-level segmentation'; +$lang->custom->setExecutionClose = 'Execution Close Setting'; $lang->custom->gradeRuleList['cross'] = 'Yes'; $lang->custom->gradeRuleList['stepwise'] = 'No'; diff --git a/module/custom/lang/zh-cn.php b/module/custom/lang/zh-cn.php index 51c63b7a3d..1769e446fa 100644 --- a/module/custom/lang/zh-cn.php +++ b/module/custom/lang/zh-cn.php @@ -21,7 +21,7 @@ $lang->custom->score = '积分'; $lang->custom->timezone = '时区'; $lang->custom->scoreReset = '重置积分'; $lang->custom->scoreTitle = '积分功能'; -$lang->custom->productName = $lang->productCommon; +$lang->custom->productName = $lang->productCommon . '关闭设置'; $lang->custom->convertFactor = '换算系数'; $lang->custom->region = '区间'; $lang->custom->tips = '提示语'; @@ -67,6 +67,7 @@ $lang->custom->beginAndEndDateRange = '起止日期范围'; $lang->custom->limitTaskDateAction = '设置起止日期必填'; $lang->custom->closeSetting = '关闭设置'; $lang->custom->gradeRule = '是否允许跨层级细分'; +$lang->custom->setExecutionClose = '执行关闭设置'; $lang->custom->gradeRuleList['cross'] = '是'; $lang->custom->gradeRuleList['stepwise'] = '否'; @@ -162,7 +163,7 @@ $lang->custom->testcase = new stdClass(); $lang->custom->testcase->fields['required'] = $lang->custom->required; $lang->custom->testcase->fields['priList'] = '优先级'; $lang->custom->testcase->fields['typeList'] = '类型'; -$lang->custom->testcase->fields['stageList'] = '阶段'; +$lang->custom->testcase->fields['stageList'] = '适用环节'; $lang->custom->testcase->fields['resultList'] = '执行结果'; $lang->custom->testcase->fields['statusList'] = '状态'; $lang->custom->testcase->fields['review'] = '评审流程'; diff --git a/module/custom/test/zen/checkinvalidkeys.php b/module/custom/test/zen/checkinvalidkeys.php old mode 100755 new mode 100644 index ffe7df4ada..dc759d14c5 --- a/module/custom/test/zen/checkinvalidkeys.php +++ b/module/custom/test/zen/checkinvalidkeys.php @@ -1,6 +1,5 @@ #!/usr/bin/env php checkInvalidKeysTest('user', 'roleList', 'zh-cn', array('short', r($customTest->checkInvalidKeysTest('todo', 'typeList', 'zh-cn', array('short', 'verylongtypekey1'))) && p('message') && e('键的长度必须小于15个字符!'); r($customTest->checkInvalidKeysTest('story', 'sourceList', 'zh-cn', array('valid_key', 'verylongsourcekey1234'))) && p('message') && e('键的长度必须小于20个字符!'); r($customTest->checkInvalidKeysTest('story', 'sourceList', 'zh-cn', array('valid_key', 'test123', 'ABC_def'))) && p() && e('1'); -r($customTest->checkInvalidKeysTest('story', 'priList', 'zh-cn', array())) && p() && e('1'); \ No newline at end of file +r($customTest->checkInvalidKeysTest('story', 'priList', 'zh-cn', array())) && p() && e('1'); diff --git a/module/custom/test/zen/checkkeysforset.php b/module/custom/test/zen/checkkeysforset.php old mode 100755 new mode 100644 index 71052d4edf..06b116ead2 --- a/module/custom/test/zen/checkkeysforset.php +++ b/module/custom/test/zen/checkkeysforset.php @@ -1,6 +1,5 @@ #!/usr/bin/env php checkKeysForSetTest('story', 'sourceList', 'zh-cn', array('valid_ r($customTest->checkKeysForSetTest('story', 'priList', 'zh-cn', array('1', '2'), array('高', ''), array('0', '0'))) && p('message') && e('值不能为空!'); r($customTest->checkKeysForSetTest('story', 'priList', 'zh-cn', array('1', '256'), array('高', '中'), array('0', '0'))) && p('message') && e('键值应为不大于255的数字'); r($customTest->checkKeysForSetTest('story', 'sourceList', 'zh-cn', array('customer', 'market', 'internal'), array('客户', '市场', '内部'), array('0', '0', '0'))) && p() && e('1'); -r($customTest->checkKeysForSetTest('user', 'roleList', 'zh-cn', array('short', 'this_is_too_long_key'), array('角色1', '角色2'), array('0', '0'))) && p('message') && e('键的长度必须小于10个字符!'); \ No newline at end of file +r($customTest->checkKeysForSetTest('user', 'roleList', 'zh-cn', array('short', 'this_is_too_long_key'), array('角色1', '角色2'), array('0', '0'))) && p('message') && e('键的长度必须小于10个字符!'); diff --git a/module/custom/ui/sidebar.html.php b/module/custom/ui/sidebar.html.php index 5f63d6aa01..6bba1429da 100644 --- a/module/custom/ui/sidebar.html.php +++ b/module/custom/ui/sidebar.html.php @@ -11,7 +11,7 @@ declare(strict_types=1); namespace zin; $menuItems = array(); -if(!empty($lang->custom->{$module}->fields)) +if(!empty($lang->custom->{$module}->fields) && $module != 'reviewcl') { foreach($lang->custom->{$module}->fields as $key => $value) { @@ -19,6 +19,7 @@ if(!empty($lang->custom->{$module}->fields)) $method = $key; $params = $key == 'required' ? "module=$module" : ''; $active = $app->rawMethod == strtolower($key) ? 'active' : ''; + $tab = ''; if(!in_array($key, $config->custom->notSetMethods)) { $params = "module=$module&field=$key"; @@ -31,6 +32,7 @@ if(!empty($lang->custom->{$module}->fields)) $currentModule = 'approvalflow'; $method = $key; $params = ''; + $tab = 'admin'; } if($module == 'setDate') @@ -60,6 +62,7 @@ if(!empty($lang->custom->{$module}->fields)) ( setClass($active), set::href(createLink($currentModule, $method, $params)), + $tab ? set('data-app', $tab) : null, $value ) ); diff --git a/module/custom/zen.php b/module/custom/zen.php index e43e9a1331..bd94ccfc7e 100644 --- a/module/custom/zen.php +++ b/module/custom/zen.php @@ -216,7 +216,7 @@ class customZen extends custom $this->checkDuplicateKeys($module, $field); if(dao::$errors) return false; - $this->checkInvalidKeys($module, $field); + $oldCustoms = $this->checkInvalidKeys($module, $field); if(dao::$errors) return false; $lang = $_POST['lang']; @@ -225,6 +225,29 @@ class customZen extends custom if($lang == 'all') $this->custom->deleteItems("lang={$currentLang}&module={$module}§ion={$field}&vision={$this->config->vision}"); $this->checkEmptyKeys($module, $field); + + if($module == 'testcase' && $field == 'stageList') + { + $oldItems = array(); + foreach($oldCustoms as $key => $item) $oldItems[$key] = $item->value; + $oldItems = arrayUnion($oldItems, zget($this->lang->$module, $field, array())); + + $newItems = array(); + $newCustoms = $this->custom->getItems("lang={$lang}&module={$module}§ion={$field}"); + foreach($newCustoms as $key => $item) $newItems[$key] = $item->value; + + $addItems = array_diff_key($newItems, $oldItems); + $delItems = array_diff_key($oldItems, $newItems); + $changeItems = array(); + foreach($oldItems as $key => $item) + { + if(!empty($newItems[$key]) && $newItems[$key] != $item) $changeItems[$key] = $newItems[$key]; + } + $this->loadModel('upgrade'); + if($addItems) $this->upgrade->createTestcaseDeliverable($addItems); + if($delItems) $this->upgrade->deleteTestcaseDeliverable($delItems); + if($changeItems) $this->upgrade->changeTestcaseDeliverable($changeItems); + } return !dao::isError(); } @@ -259,9 +282,9 @@ class customZen extends custom * @param string $module todo|story|task|bug|testcase|testtask|user|project * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList * @access protected - * @return bool|string + * @return array|string */ - protected function checkInvalidKeys(string $module = 'story', string $field = 'priList'): bool|string + protected function checkInvalidKeys(string $module = 'story', string $field = 'priList'): array|string { $lang = $_POST['lang']; $oldCustoms = $this->custom->getItems("lang={$lang}&module={$module}§ion={$field}"); @@ -287,7 +310,7 @@ class customZen extends custom if((in_array($module, array('bug', 'testcase')) || (in_array($module, array('story', 'task')) && $field == 'reasonList')) && strlen($key) > 30) return dao::$errors['message'] = $this->lang->custom->notice->invalidStrlen['thirty']; } - return true; + return $oldCustoms; } /** diff --git a/module/datatable/control.php b/module/datatable/control.php index b28de93029..11d7d778a3 100644 --- a/module/datatable/control.php +++ b/module/datatable/control.php @@ -217,6 +217,12 @@ class datatable extends control if(!empty($project->isTpl)) unset($cols['deliverable']); } + if($module == 'deliverable') + { + $hasProcess = $this->loadModel('workflowgroup')->hasFeature((int)$extra, 'process'); + if(!$hasProcess) unset($cols['activity'], $cols['trimmable'], $cols['trimRule']); + } + if($extra == 'unsetStory' && isset($cols['story'])) unset($cols['story']); if($this->config->edition == 'ipd' && $module == 'product' && $method == 'browse' && $extra == 'story') unset($cols['roadmap']); diff --git a/module/design/control.php b/module/design/control.php index 4d3f22c288..31098b6ff6 100755 --- a/module/design/control.php +++ b/module/design/control.php @@ -61,7 +61,7 @@ class design extends control $this->view->switcherText = zget($products, $productID); $this->view->switcherObjectID = $productID; - $this->lang->design->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList; + $this->lang->design->typeList = in_array($project->model, array('waterfall', 'ipd')) ? $this->lang->design->typeList : $this->lang->design->plusTypeList; $this->config->design->dtable->fieldList['type']['statusMap'] = $this->lang->design->typeList; return $productID; @@ -167,6 +167,8 @@ class design extends control $products = $this->product->getProductPairsByProject($projectID); $productIdList = $productID ? $productID : array_keys($products); $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full'); + $frozenType = $this->design->getFrozenDesignType($projectID); + foreach($frozenType as $type) unset($this->lang->design->typeList[$type]); $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->create; $this->view->users = $this->loadModel('user')->getPairs('noclosed'); @@ -204,8 +206,9 @@ class design extends control $products = $this->product->getProductPairsByProject($projectID); $productIdList = $productID ? $productID : array_keys($products); $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full'); - - $project = $this->loadModel('project')->getByID($projectID); + $project = $this->loadModel('project')->getByID($projectID); + $frozenType = $this->design->getFrozenDesignType($projectID); + foreach($frozenType as $type) unset($this->lang->design->typeList[$type]); $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->batchCreate; $this->view->stories = $this->story->addGradeLabel($stories); @@ -222,12 +225,13 @@ class design extends control * View a design. * * @param int $designID + * @param int $version * @access public * @return void */ - public function view(int $designID = 0) + public function view(int $designID = 0, int $version = 0) { - $design = $this->design->getByID($designID); + $design = $this->design->getByID($designID, $version); $this->commonAction($design->project, $design->product, $designID, $design->type); $this->session->set('revisionList', $this->app->getURI(true)); @@ -244,6 +248,7 @@ class design extends control $this->view->actions = $this->loadModel('action')->getList('design', $design->id); $this->view->repos = $this->loadModel('repo')->getRepoPairs('project', $design->project); $this->view->project = $project; + $this->view->version = $version == 0 ? $design->version : $version; $this->view->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList; $this->display(); @@ -290,6 +295,8 @@ class design extends control $productIdList = $design->product ? $design->product : array_keys($products); $project = $this->loadModel('project')->getByID($design->project); $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full'); + $frozenType = $this->design->getFrozenDesignType($design->project); + foreach($frozenType as $type) unset($this->lang->design->typeList[$type]); $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->edit; $this->view->design = $design; diff --git a/module/design/lang/de.php b/module/design/lang/de.php index 69c00ce527..2e05f15e7c 100644 --- a/module/design/lang/de.php +++ b/module/design/lang/de.php @@ -82,3 +82,4 @@ $lang->design->confirmDelete = 'Do you want to delete this design?'; $lang->design->confirmUnlink = 'Are you sure you want to remove this submission?'; $lang->design->errorDate = 'The start date can not be greater than the end date.'; $lang->design->deleted = 'Deleted'; +$lang->design->frozenTip = 'After the design are baselined, %s is not allowed.'; diff --git a/module/design/lang/en.php b/module/design/lang/en.php index b31c736e0b..3e0ff12d14 100644 --- a/module/design/lang/en.php +++ b/module/design/lang/en.php @@ -82,3 +82,4 @@ $lang->design->confirmDelete = 'Do you want to delete this design?'; $lang->design->confirmUnlink = 'Are you sure you want to remove this submission?'; $lang->design->errorDate = 'The start date can not be greater than the end date.'; $lang->design->deleted = 'Deleted'; +$lang->design->frozenTip = 'After the design are baselined, %s is not allowed.'; diff --git a/module/design/lang/fr.php b/module/design/lang/fr.php index f4d0d95df9..bca0cd4983 100644 --- a/module/design/lang/fr.php +++ b/module/design/lang/fr.php @@ -82,3 +82,4 @@ $lang->design->confirmDelete = 'Do you want to delete this design?'; $lang->design->confirmUnlink = 'Are you sure you want to remove this submission?'; $lang->design->errorDate = 'The start date can not be greater than the end date.'; $lang->design->deleted = 'Deleted'; +$lang->design->frozenTip = 'After the design are baselined, %s is not allowed.'; diff --git a/module/design/lang/zh-cn.php b/module/design/lang/zh-cn.php index 150560e37e..31ec4b2fde 100644 --- a/module/design/lang/zh-cn.php +++ b/module/design/lang/zh-cn.php @@ -82,3 +82,4 @@ $lang->design->confirmDelete = '您确定要删除这个设计吗?'; $lang->design->confirmUnlink = '您确定要移除这个提交吗?'; $lang->design->errorDate = '开始日期不能大于结束日期'; $lang->design->deleted = '已删除'; +$lang->design->frozenTip = '设计打基线后不允许%s'; diff --git a/module/design/model.php b/module/design/model.php index f4e85a0150..11fd2c4427 100755 --- a/module/design/model.php +++ b/module/design/model.php @@ -23,7 +23,7 @@ class designModel extends model public function create(object $design): bool|int { $design = $this->loadModel('file')->processImgURL($design, 'desc', (string)$this->post->uid); - $this->dao->insert(TABLE_DESIGN)->data($design, 'docVersions') + $this->dao->insert(TABLE_DESIGN)->data($design, 'docVersions,docs') ->autoCheck() ->batchCheck($this->config->design->create->requiredFields, 'notempty') ->exec(); @@ -40,6 +40,7 @@ class designModel extends model $spec->name = $design->name; $spec->desc = $design->desc; $spec->files = empty($files) ? '' : implode(',', array_keys($files)); + $spec->docs = isset($design->docs) ? $design->docs : ''; $this->dao->insert(TABLE_DESIGNSPEC)->data($spec)->exec(); return $designID; @@ -121,7 +122,7 @@ class designModel extends model foreach($design->deleteFiles as $fileID) $designFiles = str_replace(",$fileID,", ',', ",$designFiles,"); $files = $addedFiles . trim($designFiles, ','); - $designChanged = ($oldDesign->name != $design->name || $oldDesign->desc != $design->desc || !empty($files)); + $designChanged = ($oldDesign->name != $design->name || $oldDesign->desc != $design->desc || !empty($files) || $oldDesign->docs != $design->docs); if($designChanged) { $version = $oldDesign->version + 1; @@ -132,6 +133,7 @@ class designModel extends model $spec->name = $design->name; $spec->desc = $design->desc; $spec->files = empty($files) ? '' : $files; + $spec->docs = isset($design->docs) ? $design->docs : ''; $this->dao->insert(TABLE_DESIGNSPEC)->data($spec)->exec(); $this->dao->update(TABLE_DESIGN)->set('version')->eq($version)->where('id')->eq($designID)->exec(); @@ -254,15 +256,24 @@ class designModel extends model * @access public * @return object|bool */ - public function getByID(int $designID = 0): object|bool + public function getByID(int $designID = 0, int $version = 0): object|bool { if(common::isTutorialMode()) return $this->loadModel('tutorial')->getDesign(); $design = $this->dao->select('*')->from(TABLE_DESIGN)->where('id')->eq($designID)->fetch(); if(!$design) return false; + if($version == 0) $version = $design->version; + $this->app->loadLang('product'); - $design->files = $this->loadModel('file')->getByObject('design', $designID); + $this->loadModel('file'); + $spec = $this->dao->select('name,`desc`,files,docs,docVersions')->from(TABLE_DESIGNSPEC)->where('design')->eq($designID)->andWhere('version')->eq($version)->fetch(); + $design->name = !empty($spec->name) ? $spec->name : $design->name; + $design->desc = !empty($spec->desc) ? $spec->desc : ''; + $design->files = !empty($spec->files) ? $this->file->getByIdList($spec->files) : array(); + $design->docs = !empty($spec->docs) ? $spec->docs : ''; + $design->docVersions = !empty($spec->docVersions) ? json_decode($spec->docVersions, true) : array(); + $design->productName = $design->product ? $this->dao->findByID($design->product)->from(TABLE_PRODUCT)->fetch('name') : $this->lang->product->all; $design->project = (int)$design->project; $design->product = (int)$design->product; @@ -494,6 +505,24 @@ class designModel extends model return dao::isError(); } + /** + * 获取项目下冻结的设计类型。 + * Get design type of frozen. + * + * @param int $projectID + * @access public + * @return array + */ + public function getFrozenDesignType(int $projectID): array + { + return $this->dao->select('t1.deliverable')->from(TABLE_PROJECTDELIVERABLE)->alias('t1') + ->leftJoin(TABLE_DELIVERABLE)->alias('t2')->on('t1.deliverable = t2.id') + ->where('t1.project')->eq($projectID) + ->andWhere('t1.frozen')->ne('') + ->andWhere('t2.category')->in('HLDS,DDS,DBDS,ADS') + ->fetchPairs(); + } + /** * 判断当前动作是否可以点击。 * Adjust the action is clickable. @@ -507,6 +536,7 @@ class designModel extends model { $action = strtolower($action); if($action == 'confirmstorychange') return !empty($object->needConfirm); + if(in_array($action, array('edit', 'delete')) && !empty($object->frozen)) return false; return true; } } diff --git a/module/design/test/lib/design.unittest.class.php b/module/design/test/lib/design.unittest.class.php index 5f00ef9454..5eb96543ad 100644 --- a/module/design/test/lib/design.unittest.class.php +++ b/module/design/test/lib/design.unittest.class.php @@ -87,8 +87,9 @@ class designTest $fields = array('product', 'name', 'story', 'desc', 'type'); $design = new stdClass(); - $design->editedBy = $this->objectModel->app->user->account; + $design->editedBy = $this->objectModel->app->user->account; $design->editedDate = helper::now(); + $design->docs = ''; if($oldDesign) { foreach($fields as $field) $design->{$field} = isset($data[$field]) ? $data[$field] : $oldDesign->{$field}; diff --git a/module/design/test/model/getbyid.php b/module/design/test/model/getbyid.php index 1821c84d5d..9f43caeb57 100755 --- a/module/design/test/model/getbyid.php +++ b/module/design/test/model/getbyid.php @@ -1,5 +1,6 @@ #!/usr/bin/env php getByID(); @@ -34,8 +35,8 @@ zenData('relation')->loadYaml('relation')->gen(5); $idList = array(0, 3, 4); $designTester = new designTest(); -r($designTester->getByIDTest($idList[0])) && p() && e('0'); // 获取ID=0的设计信息 -r($designTester->getByIDTest($idList[1])) && p('project,product,name,desc') && e('60,1,设计3,这是设计描述3'); // 获取ID=3的设计信息 -r($designTester->getByIDTest($idList[2])) && p() && e('0'); // 获取ID不存在的设计信息 -r($designTester->getByIDTest($idList[1])) && p('storyInfo:id,version') && e('1,3'); // 获取ID=3的需求相关的信息 -r($designTester->getByIDTest($idList[1])) && p('story,storyVersion,needConfirm') && e('1,1,1'); // 获取ID=3的设计的需求版本 +r($designTester->getByIDTest($idList[0])) && p() && e('0'); // 获取ID=0的设计信息 +r($designTester->getByIDTest($idList[1])) && p('project,product,name,desc') && e('60,1,设计3,~~'); // 获取ID=3的设计信息 +r($designTester->getByIDTest($idList[2])) && p() && e('0'); // 获取ID不存在的设计信息 +r($designTester->getByIDTest($idList[1])) && p('storyInfo:id,version') && e('1,3'); // 获取ID=3的需求相关的信息 +r($designTester->getByIDTest($idList[1])) && p('story,storyVersion,needConfirm') && e('1,1,1'); // 获取ID=3的设计的需求版本 \ No newline at end of file diff --git a/module/design/test/model/getfrozendesigntype.php b/module/design/test/model/getfrozendesigntype.php new file mode 100755 index 0000000000..a61230e550 --- /dev/null +++ b/module/design/test/model/getfrozendesigntype.php @@ -0,0 +1,40 @@ +#!/usr/bin/env php +getFrozenDesignType(); +cid=0 + +- 获取冻结的概要设计属性1 @1 +- 获取冻结的详细设计属性2 @2 +- 获取冻结的数据库设计属性3 @3 +- 获取冻结的接口设计属性4 @4 +- 获取冻结的概要设计属性5 @5 + +*/ + +include dirname(__FILE__, 5) . '/test/lib/init.php'; + +$projectDeliverable = zenData('projectdeliverable'); +$projectDeliverable->id->range('1-5'); +$projectDeliverable->name->range('p1,p2,p3,p4,p5'); +$projectDeliverable->project->range('1-5'); +$projectDeliverable->deliverable->range('1-5'); +$projectDeliverable->doc->range('0'); +$projectDeliverable->docVersion->range('0'); +$projectDeliverable->review->range('1-5'); +$projectDeliverable->frozen->range('yes'); +$projectDeliverable->gen(5); + +$deliverable = zenData('deliverable'); +$deliverable->id->range('1-5'); +$deliverable->category->range('HLDS,DDS,DBDS,ADS'); +$deliverable->gen(5); + +global $tester; +$designModel = $tester->loadModel('design'); +r($designModel->getFrozenDesignType(1)) && p('1') && e('1'); // 获取冻结的概要设计 +r($designModel->getFrozenDesignType(2)) && p('2') && e('2'); // 获取冻结的详细设计 +r($designModel->getFrozenDesignType(3)) && p('3') && e('3'); // 获取冻结的数据库设计 +r($designModel->getFrozenDesignType(4)) && p('4') && e('4'); // 获取冻结的接口设计 +r($designModel->getFrozenDesignType(5)) && p('5') && e('5'); // 获取冻结的概要设计 diff --git a/module/design/test/model/update.php b/module/design/test/model/update.php index 2c834ee7e3..db2520d938 100755 --- a/module/design/test/model/update.php +++ b/module/design/test/model/update.php @@ -1,5 +1,6 @@ #!/usr/bin/env php update(); @@ -15,17 +16,17 @@ cid=15997 - 第0条的old属性 @设计2 - 第0条的new属性 @修改后的名字 - 修改设计的类型 - - 第1条的field属性 @type - - 第1条的old属性 @DBDS - - 第1条的new属性 @HLDS + - 第0条的field属性 @type + - 第0条的old属性 @DBDS + - 第0条的new属性 @HLDS - 修改设计的所属故事 - 第0条的field属性 @story - 第0条的old属性 @2 - 第0条的new属性 @1 - 修改设计的描述 - - 第1条的field属性 @desc - - 第1条的old属性 @这是设计描述5 - - 第1条的new属性 @修改后的描述 + - 第0条的field属性 @desc + - 第0条的old属性 @这是设计描述5 + - 第0条的new属性 @修改后的描述 - 测试ID为空 @0 - 测试ID不存在 @0 - 测试名称为空第name条的0属性 @『设计名称』不能为空。 @@ -50,18 +51,19 @@ $changeStory = array('story' => 1); $changeDesc = array('desc' => '修改后的描述'); $emptyName = array('name' => ''); $emptyType = array('type' => ''); +$emptyDesc = array('desc' => ''); $designTester = new designTest(); /* Normal condition. */ r($designTester->updateTest($idList[0], $changeProduct)) && p('0:field,old,new') && e('product,0,1'); // 修改设计的所属产品 r($designTester->updateTest($idList[1], $changeName)) && p('0:field,old,new') && e('name,设计2,修改后的名字'); // 修改设计的名称 -r($designTester->updateTest($idList[2], $changeType)) && p('1:field,old,new') && e('type,DBDS,HLDS'); // 修改设计的类型 +r($designTester->updateTest($idList[2], $changeType)) && p('0:field,old,new') && e('type,DBDS,HLDS'); // 修改设计的类型 r($designTester->updateTest($idList[3], $changeStory)) && p('0:field,old,new') && e('story,2,1'); // 修改设计的所属故事 -r($designTester->updateTest($idList[4], $changeDesc)) && p('1:field,old,new') && e('desc,这是设计描述5,修改后的描述'); // 修改设计的描述 +r($designTester->updateTest($idList[4], $changeDesc)) && p('0:field,old,new') && e('desc,这是设计描述5,修改后的描述'); // 修改设计的描述 /* Error condition. */ r($designTester->updateTest($idList[5], $changeProduct)) && p() && e('0'); // 测试ID为空 r($designTester->updateTest($idList[6], $changeName)) && p() && e('0'); // 测试ID不存在 r($designTester->updateTest($idList[0], $emptyName)) && p('name:0') && e('『设计名称』不能为空。'); // 测试名称为空 -r($designTester->updateTest($idList[0], $emptyType)) && p('type:0') && e('『设计类型』不能为空。'); // 测试类型为空 +r($designTester->updateTest($idList[0], $emptyType)) && p('type:0') && e('『设计类型』不能为空。'); // 测试类型为空 \ No newline at end of file diff --git a/module/design/ui/browse.html.php b/module/design/ui/browse.html.php index 5fe4bc36a8..74781fb725 100644 --- a/module/design/ui/browse.html.php +++ b/module/design/ui/browse.html.php @@ -46,6 +46,17 @@ toolbar jsVar('confirmDelete', $lang->design->confirmDelete); $tableData = initTableData($designs, $config->design->dtable->fieldList, $this->design); +foreach($tableData as $design) +{ + if(!isset($design->actions)) continue; + foreach($design->actions as &$action) + { + $actionName = $action['name']; + if(!empty($design->frozen) && in_array($actionName, array('edit', 'delete'))) $action['hint'] = sprintf($this->lang->design->frozenTip, $this->lang->design->$actionName); + + } +} + dtable ( set::userMap($users), diff --git a/module/design/ui/view.html.php b/module/design/ui/view.html.php index 11b662c166..343d055628 100644 --- a/module/design/ui/view.html.php +++ b/module/design/ui/view.html.php @@ -11,10 +11,30 @@ declare(strict_types=1); namespace zin; jsVar('type', strtolower($design->type)); + +/* 版本列表。Version list. */ +$versions = array(); +for($i = $design->version; $i >= 1; $i--) +{ + $versionItem = setting() + ->text("#{$i}") + ->url(inlink('view', "designID={$design->id}&version=$i")); + + $versionItem->selected($version == $i); + $versions[] = $versionItem; +} + +$versionBtn = count($versions) > 1 ? to::title(dropdown +( + btn(set::type('ghost'), setClass('text-link font-normal text-base'), "#{$version}"), + set::items($versions) +)) : null; + detailHeader ( set::backUrl(createLink('design', 'browse', "projectID={$design->project}")), to::title(entityLabel(set(array('entityID' => $design->id, 'level' => 1, 'text' => $design->name))), $design->deleted ? span(setClass('label danger'), $lang->deleted) : null), + $versionBtn ); $canModify = common::canModify('project', $project); @@ -38,7 +58,7 @@ foreach($config->design->view->operateList['main'] as $operate) $commonActions = array(); foreach($config->design->view->operateList['common'] as $operate) { - if(!$canModify || !common::hasPriv('design', $operate) || $design->deleted) continue; + if(!$canModify || !common::hasPriv('design', $operate) || $design->deleted || !empty($design->frozen)) continue; if($operate == 'delete') $config->design->actionList['delete']['class'] = 'ajax-submit'; $commonActions[] = $config->design->actionList[$operate]; } @@ -75,6 +95,7 @@ if($this->design->isClickable($design, 'confirmStoryChange')) detailBody ( + $versionBtn, sectionList ( section diff --git a/module/design/zen.php b/module/design/zen.php index 7a4c4a0001..6ed121d4e0 100644 --- a/module/design/zen.php +++ b/module/design/zen.php @@ -36,7 +36,7 @@ class designZen extends control $count = 1; foreach(array_filter($this->lang->design->{$typeList}) as $key => $value) { - $key = strtolower($key); + $key = strtolower((string)$key); $exclude = $type == $key ? '' : 'design'; $alias = $type == $key ? $this->app->rawMethod : ''; diff --git a/module/doc/config.php b/module/doc/config.php index 9c36ed9d42..96baadb5fc 100644 --- a/module/doc/config.php +++ b/module/doc/config.php @@ -71,7 +71,7 @@ $config->doc->search['fields']['execution'] = $lang->doc->execution; $config->doc->search['fields']['lib'] = $lang->doc->lib; $config->doc->search['fields']['status'] = $lang->doc->status; $config->doc->search['fields']['module'] = $lang->doc->module; -$config->doc->search['fields']['addedBy'] = $lang->doc->addedByAB; +$config->doc->search['fields']['addedBy'] = $lang->doc->addedBy; $config->doc->search['fields']['addedDate'] = $lang->doc->addedDate; $config->doc->search['fields']['editedBy'] = $lang->doc->editedBy; $config->doc->search['fields']['editedDate'] = $lang->doc->editedDate; diff --git a/module/doc/config/dtable.php b/module/doc/config/dtable.php index d4251ffbbf..891b7ba607 100644 --- a/module/doc/config/dtable.php +++ b/module/doc/config/dtable.php @@ -17,7 +17,7 @@ $config->doc->dtable->fieldList['module']['title'] = $lang->doc->position; $config->doc->dtable->fieldList['module']['type'] = 'desc'; $config->doc->dtable->fieldList['module']['sortType'] = true; -$config->doc->dtable->fieldList['addedBy']['title'] = $lang->doc->addedByAB; +$config->doc->dtable->fieldList['addedBy']['title'] = $lang->doc->addedBy; $config->doc->dtable->fieldList['addedBy']['type'] = 'user'; $config->doc->dtable->fieldList['addedDate']['title'] = $lang->doc->addedDate; diff --git a/module/doc/config/form.php b/module/doc/config/form.php index 09daf83690..34d2921140 100644 --- a/module/doc/config/form.php +++ b/module/doc/config/form.php @@ -34,61 +34,64 @@ $config->doc->form->editlib['acl'] = array('type' => 'string', 'required' $config->doc->form->editlib['groups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); $config->doc->form->editlib['users'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->create['title'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); -$config->doc->form->create['version'] = array('type' => 'int', 'required' => false, 'default' => 1); -$config->doc->form->create['product'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->create['project'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->create['execution'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->create['module'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->create['lib'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->create['status'] = array('type' => 'string', 'required' => false, 'default' => 'normal'); -$config->doc->form->create['parent'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->create['type'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->create['keywords'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true); -$config->doc->form->create['acl'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->create['vision'] = array('type' => 'string', 'required' => false, 'default' => $config->vision); -$config->doc->form->create['addedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->create['addedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); -$config->doc->form->create['editedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->create['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); -$config->doc->form->create['groups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->create['users'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->create['readGroups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->create['readUsers'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->create['mailto'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->create['contentType'] = array('type' => 'string', 'required' => false, 'default' => 'doc'); -$config->doc->form->create['content'] = array('type' => 'string', 'required' => false, 'default' => '', 'control' => 'editor', 'skipRequired' => true); -$config->doc->form->create['rawContent'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true, 'specialchars' => 'no'); -$config->doc->form->create['template'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->create['templateType'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->create['chapterType'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['title'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); +$config->doc->form->create['version'] = array('type' => 'int', 'required' => false, 'default' => 1); +$config->doc->form->create['product'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->create['project'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->create['execution'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->create['module'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->create['lib'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->create['status'] = array('type' => 'string', 'required' => false, 'default' => 'normal'); +$config->doc->form->create['parent'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->create['type'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['keywords'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true); +$config->doc->form->create['acl'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['vision'] = array('type' => 'string', 'required' => false, 'default' => $config->vision); +$config->doc->form->create['addedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['addedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); +$config->doc->form->create['editedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); +$config->doc->form->create['groups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->create['users'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->create['readGroups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->create['readUsers'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->create['mailto'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->create['contentType'] = array('type' => 'string', 'required' => false, 'default' => 'doc'); +$config->doc->form->create['content'] = array('type' => 'string', 'required' => false, 'default' => '', 'control' => 'editor', 'skipRequired' => true); +$config->doc->form->create['rawContent'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true, 'specialchars' => 'no'); +$config->doc->form->create['template'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['templateType'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['chapterType'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->create['isDeliverable'] = array('type' => 'string', 'required' => false, 'default' => '0'); $config->doc->form->createtemplate = $config->doc->form->create; $config->doc->form->createtemplate['templateDesc'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->edit['title'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); -$config->doc->form->edit['product'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->edit['project'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->edit['execution'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->edit['module'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->edit['lib'] = array('type' => 'int', 'required' => false, 'default' => 0, 'skipRequired' => true); -$config->doc->form->edit['status'] = array('type' => 'string', 'required' => false, 'default' => 'normal'); -$config->doc->form->edit['parent'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->doc->form->edit['type'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->edit['keywords'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true); -$config->doc->form->edit['acl'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->edit['contentType'] = array('type' => 'string', 'required' => false, 'default' => 'doc'); -$config->doc->form->edit['content'] = array('type' => 'string', 'required' => false, 'default' => '', 'control' => 'editor', 'skipRequired' => true); -$config->doc->form->edit['rawContent'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true, 'specialchars' => 'no'); -$config->doc->form->edit['groups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->edit['users'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->edit['readGroups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->edit['readUsers'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->edit['mailto'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); -$config->doc->form->edit['editedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->edit['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); -$config->doc->form->edit['fromVersion'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->doc->form->edit['files'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->edit['title'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); +$config->doc->form->edit['product'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->edit['project'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->edit['execution'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->edit['module'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->edit['lib'] = array('type' => 'int', 'required' => false, 'default' => 0, 'skipRequired' => true); +$config->doc->form->edit['status'] = array('type' => 'string', 'required' => false, 'default' => 'normal'); +$config->doc->form->edit['parent'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->doc->form->edit['type'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->edit['keywords'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true); +$config->doc->form->edit['acl'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->edit['contentType'] = array('type' => 'string', 'required' => false, 'default' => 'doc'); +$config->doc->form->edit['content'] = array('type' => 'string', 'required' => false, 'default' => '', 'control' => 'editor', 'skipRequired' => true); +$config->doc->form->edit['rawContent'] = array('type' => 'string', 'required' => false, 'default' => '', 'skipRequired' => true, 'specialchars' => 'no'); +$config->doc->form->edit['groups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->edit['users'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->edit['readGroups'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->edit['readUsers'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->edit['mailto'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join'); +$config->doc->form->edit['editedBy'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->edit['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); +$config->doc->form->edit['fromVersion'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->edit['files'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->doc->form->edit['isDeliverable'] = array('type' => 'string', 'required' => false, 'default' => '0'); +$config->doc->form->edit['deleteFiles'] = array('type' => 'array', 'required' => false, 'default' => array(), 'filter' => 'join'); $config->doc->form->edittemplate = $config->doc->form->edit; $config->doc->form->edittemplate['templateDesc'] = array('type' => 'string', 'required' => false, 'default' => ''); diff --git a/module/doc/control.php b/module/doc/control.php index 44568f5ab8..40ecda4172 100755 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -810,6 +810,7 @@ class doc extends control * 上传文档。 * Upload docs. * + * @param int $docID * @param string $objectType product|project|execution|custom * @param int $objectID * @param int|string $libID @@ -818,7 +819,7 @@ class doc extends control * @access public * @return void */ - public function uploadDocs(string $objectType, int $objectID, int $libID, int $moduleID = 0, string $docType = '') + public function uploadDocs(int $docID, string $objectType, int $objectID, int $libID, int $moduleID = 0, string $docType = '') { if(!empty($_POST)) { @@ -849,12 +850,16 @@ class doc extends control $this->config->doc->create->requiredFields = trim(str_replace(array(',content,', ',keywords,'), ",", ",{$this->config->doc->create->requiredFields},"), ','); - $docData = form::data($this->config->doc->form->create) - ->setDefault('addedBy', $this->app->user->account) - ->get(); - if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + $docData = form::data(!empty($docID) ? $this->config->doc->form->edit : $this->config->doc->form->create)->remove('fromVersion')->get(); + if(empty($docID)) $docData->addedBy = $this->app->user->account; + if(!empty($docID)) $docData->editedBy = $this->app->user->account; - if($this->post->uploadFormat == 'combinedDocs') + if(!empty($docID)) + { + $docResult = $this->doc->update($docID, $docData); + $docData->id = $docID; + } + elseif($this->post->uploadFormat == 'combinedDocs') { $docResult = $this->doc->create($docData); } @@ -864,7 +869,7 @@ class doc extends control } if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - return $this->docZen->responseAfterUploadDocs($docResult); + return !empty($docID) ? $this->send($this->docZen->responseAfterEdit($docData, $docResult['changes'], $docResult['files'], $docResult['deletedFiles'])) : $this->docZen->responseAfterUploadDocs($docResult); } if($objectType == 'execution' && $libID) // 此时传入的objectID是projectID,用lib的信息更改回executionID @@ -873,7 +878,7 @@ class doc extends control $objectID = $this->doc->getObjectIDByLib($lib); } - $this->docZen->assignVarsForUploadDocs($objectType, $objectID, $libID, $moduleID, $docType); + $this->docZen->assignVarsForUploadDocs($docID, $objectType, $objectID, $libID, $moduleID, $docType); $this->display(); } @@ -1028,7 +1033,7 @@ class doc extends control $files = $result['files']; } - return $this->docZen->responseAfterEdit($doc, $changes, $files); + return $this->send($this->docZen->responseAfterEdit($doc, $changes, $files)); } /* Get doc and set menu. */ @@ -1387,7 +1392,9 @@ class doc extends control $_SESSION["doc_{$doc->id}_nopriv"] = ''; if(!$isApi && !$this->doc->checkPrivDoc($doc)) { - $errorMessage = empty($_SESSION["doc_{$doc->id}_nopriv"]) ? $this->lang->doc->accessDenied : $_SESSION["doc_{$doc->id}_nopriv"]; + $user = $this->loadModel('user')->getByID($doc->addedBy); + $realname = $user->deleted ? $user->account : $user->realname; + $errorMessage = empty($_SESSION["doc_{$doc->id}_nopriv"]) ? sprintf($this->lang->doc->cannotView, $realname) : $_SESSION["doc_{$doc->id}_nopriv"]; unset($_SESSION["doc_{$doc->id}_nopriv"]); return $this->sendError($errorMessage, inlink('index')); } @@ -2585,7 +2592,18 @@ class doc extends control $this->view->groups = $this->loadModel('group')->getPairs(); } + $docTitle = ''; + if(!empty($doc)) + { + $docTitle = $doc->title; + } + elseif($this->post->title) + { + $docTitle = $this->post->title; + } + $this->view->docID = $docID; + $this->view->docTitle = $docTitle; $this->view->mode = empty($docID) ? 'create' : 'edit'; $this->view->libID = $libID; $this->view->moduleID = $moduleID; diff --git a/module/doc/js/browsetemplate.ui.js b/module/doc/js/browsetemplate.ui.js index 3f48fc0684..907213168b 100644 --- a/module/doc/js/browsetemplate.ui.js +++ b/module/doc/js/browsetemplate.ui.js @@ -328,19 +328,20 @@ function handleSaveDoc(doc) const moduleID = docApp.signals.moduleID.value; const url = $.createLink('doc', 'editTemplate', `docID=${doc.id}`); const docData = { - rawContent : doc.content, - status : doc.status || 'normal', - contentType : doc.contentType, - type : 'text', - lib : libID, - module : moduleID, - title : doc.title, - keywords : doc.keywords, - templateDesc : doc.templateDesc, - acl : doc.acl, - content : doc.html, - space : spaceType, - uid : (doc.uid || `doc${doc.id}`), + rawContent : doc.content, + status : doc.status || 'normal', + contentType : doc.contentType, + type : 'text', + lib : libID, + module : moduleID, + title : doc.title, + keywords : doc.keywords, + templateDesc : doc.templateDesc, + acl : doc.acl, + content : doc.html, + isDeliverable : doc.isDeliverable || '0', + space : spaceType, + uid : (doc.uid || `doc${doc.id}`), }; const docAppData = docApp.doc.data; diff --git a/module/doc/js/setdocbasic.ui.js b/module/doc/js/setdocbasic.ui.js index 5cd63f0604..9f0bf80f60 100644 --- a/module/doc/js/setdocbasic.ui.js +++ b/module/doc/js/setdocbasic.ui.js @@ -28,3 +28,22 @@ window.loadExecutions = function() modulePicker.$.setValue(''); }); } + +window.changeIsDeliverable = function() +{ + if($(this).val() == '1') + { + $('[type=radio][name=acl]').attr('disabled', 'disabled'); + $('#acl').removeAttr('disabled'); + $("[type=radio][name='acl'][value='open']").prop('checked', true); + $('[type=radio][name=acl]').closest('.radio-primary').addClass('disabled'); + $('[type=radio][name=acl]').closest('.check-list').addClass('disabled'); + } + else + { + $('#acl').attr('disabled', 'disabled'); + $('[type=radio][name=acl]').removeAttr('disabled'); + $('[type=radio][name=acl]').closest('.radio-primary').removeClass('disabled'); + $('[type=radio][name=acl]').closest('.check-list').removeClass('disabled'); + } +} diff --git a/module/doc/js/uploaddocs.ui.js b/module/doc/js/uploaddocs.ui.js index ad4bbdf4ef..1341e88a60 100644 --- a/module/doc/js/uploaddocs.ui.js +++ b/module/doc/js/uploaddocs.ui.js @@ -8,8 +8,11 @@ function addFileUploadMutationObserver() let title = $('.uploadFileBox .file-selector-list .file-selector-item:nth-child(1) .item-title').text(); let dotIdx = title.lastIndexOf('.'); if(dotIdx != '-1') title = title.substring(0, dotIdx); - $('.titleBox [name=title]').val(title); - $('.uploadFormatBox').toggleClass('hidden', $('.uploadFileBox .file-selector-list .file-selector-item').length <= 1); + if(!docID) + { + $('.titleBox [name=title]').val(title); + $('.uploadFormatBox').toggleClass('hidden', $('.uploadFileBox .file-selector-list .file-selector-item').length <= 1); + } }); observer.observe(fileUpload, {childList: true, subtree: true}); @@ -35,9 +38,13 @@ window.loadExecutions = function(e) const link = $.createLink('doc', 'ajaxGetModules', 'objectType=project&objectID=' + projectID + '&type=doc'); $.getJSON(link, function(data) { - const $picker = $('.moduleBox input[name="module"]').zui('picker'); - $picker.render({items: data}); - $picker.$.setValue(''); + const $libPicker = $("[name='lib']").zui('picker'); + $libPicker.render({items: data.libs}); + $libPicker.$.setValue(''); + + const $modulePicker = $("[name='parent']").zui('picker'); + $modulePicker.render({items: data.modules}); + $modulePicker.$.setValue(''); }); } diff --git a/module/doc/lang/de.php b/module/doc/lang/de.php index bbca0d10c7..b6f764c04d 100644 --- a/module/doc/lang/de.php +++ b/module/doc/lang/de.php @@ -88,6 +88,7 @@ $lang->docTemplate->scope = 'Scope'; $lang->docTemplate->lib = $lang->docTemplate->scope; $lang->docTemplate->module = 'Category'; $lang->docTemplate->desc = 'Describe'; +$lang->docTemplate->deliverable = 'Is Deliverable'; $lang->docTemplate->parentModule = 'Parent'; $lang->docTemplate->typeName = 'Category Name'; $lang->docTemplate->parent = 'Level'; @@ -142,6 +143,9 @@ $lang->docTemplate->filterTypes[] = array('draft', 'Draft'); $lang->docTemplate->filterTypes[] = array('released', 'Released'); $lang->docTemplate->filterTypes[] = array('createdByMe', 'Created By Me'); +$lang->docTemplate->deliverableList['1'] = 'Yes'; +$lang->docTemplate->deliverableList['0'] = 'No'; + /* Fields. */ $lang->doc->common = 'Dok'; $lang->doc->id = 'ID'; @@ -163,7 +167,6 @@ $lang->doc->status = 'Status'; $lang->doc->url = 'URL'; $lang->doc->files = 'Datei'; $lang->doc->addedBy = 'Angelegt von'; -$lang->doc->addedByAB = 'CreatedBy'; $lang->doc->addedDate = 'CreatedDate'; $lang->doc->editedBy = 'Bearbeitet von'; $lang->doc->editedDate = 'UpdatedDate'; @@ -326,6 +329,7 @@ $lang->doc->moveTo = 'Move to'; $lang->doc->notSupportExport = 'This document does not support export'; $lang->doc->downloadTemplate = 'Download Template'; $lang->doc->addFile = 'Add File'; +$lang->doc->frozenTips = 'After the doc are baselined, %s is not allowed.'; $lang->doc->preview = 'Preview'; $lang->doc->insertTitle = 'Insert %s list'; @@ -509,6 +513,7 @@ $lang->doc->errorEmptyProject = "Kein {$lang->executionCommon}. Kann n $lang->doc->errorEmptySpaceLib = "There is no document library in this space. It cannot be created. Please create a document library first"; $lang->doc->errorMainSysLib = "Diese Bibliothek kann nicht gelöscht werden."; $lang->doc->accessDenied = "Zugriff verweigert!"; +$lang->doc->cannotView = "You do not have permission to view this document. Please contact the creator %s."; $lang->doc->versionNotFount = 'Existiert nicht in diesem Build.'; $lang->doc->noDoc = 'Keine Dokumente. '; $lang->doc->noArticle = 'No articles.'; @@ -645,7 +650,7 @@ $lang->doc->docLang->createDoc = $lang->doc->create; $lang->doc->docLang->editDoc = $lang->doc->edit; $lang->doc->docLang->effort = $lang->doc->effort; $lang->doc->docLang->deleteDoc = $lang->doc->delete; -$lang->doc->docLang->uploadDoc = $lang->doc->uploadDoc; +$lang->doc->docLang->uploadDoc = $lang->doc->uploadFile; $lang->doc->docLang->createList = $lang->doc->createList; $lang->doc->docLang->confirmDelete = $lang->doc->confirmDelete; $lang->doc->docLang->confirmDeleteWithSub = $lang->doc->confirmDeleteWithSub; @@ -732,7 +737,7 @@ $lang->doc->docLang->createdByUserAt = 'Created by {name} at {time}' $lang->doc->docLang->editedByUserAt = 'Edited by {name} at {time}'; $lang->doc->docLang->docInfo = 'Document Information'; $lang->doc->docLang->docStatus = $lang->doc->status; -$lang->doc->docLang->creator = $lang->doc->addedByAB; +$lang->doc->docLang->creator = $lang->doc->addedBy; $lang->doc->docLang->createDate = $lang->doc->addedDate; $lang->doc->docLang->modifier = $lang->doc->editedBy; $lang->doc->docLang->editDate = $lang->doc->editedDate; @@ -788,6 +793,7 @@ $lang->doc->docLang->editing = 'Editing'; $lang->doc->docLang->restoreVersionHint = 'Restore to version'; $lang->doc->docLang->restoreVersion = 'Restore'; $lang->doc->docLang->restoreVersionConfirm = 'This will create a new version using the content of version {version}. Are you sure you want to continue?'; +$lang->doc->docLang->frozenTips = $lang->doc->frozenTips; $lang->docTemplate->types = array(); $lang->docTemplate->types['plan'] = 'Plan'; diff --git a/module/doc/lang/en.php b/module/doc/lang/en.php index e34f7362a7..6a5dc06974 100644 --- a/module/doc/lang/en.php +++ b/module/doc/lang/en.php @@ -88,6 +88,7 @@ $lang->docTemplate->scope = 'Scope'; $lang->docTemplate->lib = $lang->docTemplate->scope; $lang->docTemplate->module = 'Category'; $lang->docTemplate->desc = 'Describe'; +$lang->docTemplate->deliverable = 'Is Deliverable'; $lang->docTemplate->parentModule = 'Parent'; $lang->docTemplate->typeName = 'Category Name'; $lang->docTemplate->parent = 'Level'; @@ -142,6 +143,9 @@ $lang->docTemplate->filterTypes[] = array('draft', 'Draft'); $lang->docTemplate->filterTypes[] = array('released', 'Released'); $lang->docTemplate->filterTypes[] = array('createdByMe', 'Created By Me'); +$lang->docTemplate->deliverableList['1'] = 'Yes'; +$lang->docTemplate->deliverableList['0'] = 'No'; + /* Fields. */ $lang->doc->common = 'Document'; $lang->doc->id = 'ID'; @@ -163,7 +167,6 @@ $lang->doc->status = 'Status'; $lang->doc->url = 'URL'; $lang->doc->files = 'Files'; $lang->doc->addedBy = 'Author'; -$lang->doc->addedByAB = 'CreatedBy'; $lang->doc->addedDate = 'CreatedDate'; $lang->doc->editedBy = 'UpdatedBy'; $lang->doc->editedDate = 'UpdatedDate'; @@ -326,6 +329,7 @@ $lang->doc->moveTo = 'Move to'; $lang->doc->notSupportExport = 'This document does not support export'; $lang->doc->downloadTemplate = 'Download Template'; $lang->doc->addFile = 'Add File'; +$lang->doc->frozenTips = 'After the doc are baselined, %s is not allowed.'; $lang->doc->preview = 'Preview'; $lang->doc->insertTitle = 'Insert %s list'; @@ -509,6 +513,7 @@ $lang->doc->errorEmptyProject = "No {$lang->executionCommon}. It canno $lang->doc->errorEmptySpaceLib = "There is no document library in this space. It cannot be created. Please create a document library first"; $lang->doc->errorMainSysLib = "This library cannot be deleted."; $lang->doc->accessDenied = "Access is denied!"; +$lang->doc->cannotView = "You do not have permission to view this document. Please contact the creator %s."; $lang->doc->versionNotFount = 'It does not exist in this build.'; $lang->doc->noDoc = 'No documents. '; $lang->doc->noArticle = 'No articles.'; @@ -645,7 +650,7 @@ $lang->doc->docLang->createDoc = $lang->doc->create; $lang->doc->docLang->editDoc = $lang->doc->edit; $lang->doc->docLang->effort = $lang->doc->effort; $lang->doc->docLang->deleteDoc = $lang->doc->delete; -$lang->doc->docLang->uploadDoc = $lang->doc->uploadDoc; +$lang->doc->docLang->uploadDoc = $lang->doc->uploadFile; $lang->doc->docLang->createList = $lang->doc->createList; $lang->doc->docLang->confirmDelete = $lang->doc->confirmDelete; $lang->doc->docLang->confirmDeleteWithSub = $lang->doc->confirmDeleteWithSub; @@ -732,7 +737,7 @@ $lang->doc->docLang->createdByUserAt = 'Created by {name} at {time}' $lang->doc->docLang->editedByUserAt = 'Edited by {name} at {time}'; $lang->doc->docLang->docInfo = 'Document Information'; $lang->doc->docLang->docStatus = $lang->doc->status; -$lang->doc->docLang->creator = $lang->doc->addedByAB; +$lang->doc->docLang->creator = $lang->doc->addedBy; $lang->doc->docLang->createDate = $lang->doc->addedDate; $lang->doc->docLang->modifier = $lang->doc->editedBy; $lang->doc->docLang->editDate = $lang->doc->editedDate; @@ -788,6 +793,7 @@ $lang->doc->docLang->editing = 'Editing'; $lang->doc->docLang->restoreVersionHint = 'Restore to version'; $lang->doc->docLang->restoreVersion = 'Restore'; $lang->doc->docLang->restoreVersionConfirm = 'This will create a new version using the content of version {version}. Are you sure you want to continue?'; +$lang->doc->docLang->frozenTips = $lang->doc->frozenTips; $lang->docTemplate->types = array(); $lang->docTemplate->types['plan'] = 'Plan'; diff --git a/module/doc/lang/fr.php b/module/doc/lang/fr.php index f58c40f056..ae10818479 100644 --- a/module/doc/lang/fr.php +++ b/module/doc/lang/fr.php @@ -88,6 +88,7 @@ $lang->docTemplate->scope = 'Scope'; $lang->docTemplate->lib = $lang->docTemplate->scope; $lang->docTemplate->module = 'Category'; $lang->docTemplate->desc = 'Describe'; +$lang->docTemplate->deliverable = 'Is Deliverable'; $lang->docTemplate->parentModule = 'Parent'; $lang->docTemplate->typeName = 'Category Name'; $lang->docTemplate->parent = 'Level'; @@ -142,6 +143,9 @@ $lang->docTemplate->filterTypes[] = array('draft', 'Draft'); $lang->docTemplate->filterTypes[] = array('released', 'Released'); $lang->docTemplate->filterTypes[] = array('createdByMe', 'Created By Me'); +$lang->docTemplate->deliverableList['1'] = 'Yes'; +$lang->docTemplate->deliverableList['0'] = 'No'; + /* Fields. */ $lang->doc->common = 'Gestion Documentaire'; $lang->doc->id = 'ID'; @@ -163,7 +167,6 @@ $lang->doc->status = 'Status'; $lang->doc->url = 'URL'; $lang->doc->files = 'Fichiers'; $lang->doc->addedBy = 'Auteur'; -$lang->doc->addedByAB = 'CreatedBy'; $lang->doc->addedDate = 'CreatedDate'; $lang->doc->editedBy = 'Màj par'; $lang->doc->editedDate = 'UpdatedDate'; @@ -326,6 +329,7 @@ $lang->doc->moveTo = 'Move to'; $lang->doc->notSupportExport = 'This document does not support export'; $lang->doc->downloadTemplate = 'Download Template'; $lang->doc->addFile = 'Add File'; +$lang->doc->frozenTips = 'After the doc are baselined, %s is not allowed.'; $lang->doc->preview = 'Preview'; $lang->doc->insertTitle = 'Insert %s list'; @@ -509,6 +513,7 @@ $lang->doc->errorEmptyProject = "Aucun {$lang->executionCommon}. Il ne $lang->doc->errorEmptySpaceLib = "There is no document library in this space. It cannot be created. Please create a document library first"; $lang->doc->errorMainSysLib = "Cette Bibliothèque ne peut pas être supprimée."; $lang->doc->accessDenied = "Access is denied !"; +$lang->doc->cannotView = "You do not have permission to view this document. Please contact the creator %s."; $lang->doc->versionNotFount = "N'existe pas dans ce build."; $lang->doc->noDoc = 'Aucun documents. '; $lang->doc->noArticle = 'No articles.'; @@ -645,7 +650,7 @@ $lang->doc->docLang->createDoc = $lang->doc->create; $lang->doc->docLang->editDoc = $lang->doc->edit; $lang->doc->docLang->effort = $lang->doc->effort; $lang->doc->docLang->deleteDoc = $lang->doc->delete; -$lang->doc->docLang->uploadDoc = $lang->doc->uploadDoc; +$lang->doc->docLang->uploadDoc = $lang->doc->uploadFile; $lang->doc->docLang->createList = $lang->doc->createList; $lang->doc->docLang->confirmDelete = $lang->doc->confirmDelete; $lang->doc->docLang->confirmDeleteWithSub = $lang->doc->confirmDeleteWithSub; @@ -732,7 +737,7 @@ $lang->doc->docLang->createdByUserAt = 'Created by {name} at {time}' $lang->doc->docLang->editedByUserAt = 'Edited by {name} at {time}'; $lang->doc->docLang->docInfo = 'Document Information'; $lang->doc->docLang->docStatus = $lang->doc->status; -$lang->doc->docLang->creator = $lang->doc->addedByAB; +$lang->doc->docLang->creator = $lang->doc->addedBy; $lang->doc->docLang->createDate = $lang->doc->addedDate; $lang->doc->docLang->modifier = $lang->doc->editedBy; $lang->doc->docLang->editDate = $lang->doc->editedDate; @@ -788,6 +793,7 @@ $lang->doc->docLang->editing = 'Editing'; $lang->doc->docLang->restoreVersionHint = 'Restore to version'; $lang->doc->docLang->restoreVersion = 'Restore'; $lang->doc->docLang->restoreVersionConfirm = 'This will create a new version using the content of version {version}. Are you sure you want to continue?'; +$lang->doc->docLang->frozenTips = $lang->doc->frozenTips; $lang->docTemplate->types = array(); $lang->docTemplate->types['plan'] = 'Plan'; diff --git a/module/doc/lang/zh-cn.php b/module/doc/lang/zh-cn.php index 5b62f125d0..c2144d2292 100644 --- a/module/doc/lang/zh-cn.php +++ b/module/doc/lang/zh-cn.php @@ -88,6 +88,7 @@ $lang->docTemplate->scope = '所属范围'; $lang->docTemplate->lib = $lang->docTemplate->scope; $lang->docTemplate->module = '模板分类'; $lang->docTemplate->desc = '描述'; +$lang->docTemplate->deliverable = '是否为交付物'; $lang->docTemplate->parentModule = '上级分类'; $lang->docTemplate->typeName = '分类名称'; $lang->docTemplate->parent = '所属层级'; @@ -142,6 +143,9 @@ $lang->docTemplate->filterTypes[] = array('draft', '草稿'); $lang->docTemplate->filterTypes[] = array('released', '已发布'); $lang->docTemplate->filterTypes[] = array('createdByMe', '我创建的'); +$lang->docTemplate->deliverableList['1'] = '是'; +$lang->docTemplate->deliverableList['0'] = '否'; + /* 字段列表。*/ $lang->doc->common = '文档'; $lang->doc->id = 'ID'; @@ -162,8 +166,7 @@ $lang->doc->keywords = '关键字'; $lang->doc->status = '文档状态'; $lang->doc->url = '文档URL'; $lang->doc->files = '附件'; -$lang->doc->addedBy = '由谁添加'; -$lang->doc->addedByAB = '创建者'; +$lang->doc->addedBy = '创建者'; $lang->doc->addedDate = '创建日期'; $lang->doc->editedBy = '修改者'; $lang->doc->editedDate = '修改日期'; @@ -260,7 +263,7 @@ $lang->doc->collectCount = '%s次收藏'; $lang->doc->index = '仪表盘'; $lang->doc->createAB = '创建'; $lang->doc->create = '创建文档'; -$lang->doc->createOrUpload = '创建/导入文档'; +$lang->doc->createOrUpload = '创建文档'; $lang->doc->edit = '编辑文档'; $lang->doc->effort = '日志'; $lang->doc->delete = '删除文档'; @@ -326,6 +329,7 @@ $lang->doc->moveTo = '移动至'; $lang->doc->notSupportExport = '(此文档暂不支持导出)'; $lang->doc->downloadTemplate = '下载模板'; $lang->doc->addFile = '提交文件'; +$lang->doc->frozenTips = '文档打基线后不允许%s'; $lang->doc->preview = '预览'; $lang->doc->insertTitle = '插入%s列表'; @@ -509,6 +513,7 @@ $lang->doc->errorEmptyProject = "没有{$lang->executionCommon},无 $lang->doc->errorEmptySpaceLib = "该空间下没有文档库,无法创建文档,请先创建文档库"; $lang->doc->errorMainSysLib = "该系统文档库不能删除!"; $lang->doc->accessDenied = "您没有权限访问!"; +$lang->doc->cannotView = "无查看权限,请联系创建者“%s”!"; $lang->doc->versionNotFount = '该版本文档不存在'; $lang->doc->noDoc = '暂时没有文档。'; $lang->doc->noArticle = '暂时没有文章。'; @@ -645,7 +650,7 @@ $lang->doc->docLang->createDoc = $lang->doc->create; $lang->doc->docLang->editDoc = $lang->doc->edit; $lang->doc->docLang->effort = $lang->doc->effort; $lang->doc->docLang->deleteDoc = $lang->doc->delete; -$lang->doc->docLang->uploadDoc = $lang->doc->uploadDoc; +$lang->doc->docLang->uploadDoc = $lang->doc->uploadFile; $lang->doc->docLang->createList = $lang->doc->createList; $lang->doc->docLang->confirmDelete = $lang->doc->confirmDelete; $lang->doc->docLang->confirmDeleteWithSub = $lang->doc->confirmDeleteWithSub; @@ -732,7 +737,7 @@ $lang->doc->docLang->createdByUserAt = '由 {name} 创建于 {time}' $lang->doc->docLang->editedByUserAt = '由 {name} 编辑于 {time}'; $lang->doc->docLang->docInfo = '文档信息'; $lang->doc->docLang->docStatus = $lang->doc->status; -$lang->doc->docLang->creator = $lang->doc->addedByAB; +$lang->doc->docLang->creator = $lang->doc->addedBy; $lang->doc->docLang->createDate = $lang->doc->addedDate; $lang->doc->docLang->modifier = $lang->doc->editedBy; $lang->doc->docLang->editDate = $lang->doc->editedDate; @@ -788,6 +793,7 @@ $lang->doc->docLang->editing = '正在编辑'; $lang->doc->docLang->restoreVersionHint = '恢复到版本'; $lang->doc->docLang->restoreVersion = '恢复'; $lang->doc->docLang->restoreVersionConfirm = '这将使用文档版本 {version} 的内容创建一个新的版本,确定要继续吗?'; +$lang->doc->docLang->frozenTips = $lang->doc->frozenTips; $lang->docTemplate->types = array(); $lang->docTemplate->types['plan'] = '计划'; diff --git a/module/doc/model.php b/module/doc/model.php index d812743304..d84344e81e 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -36,6 +36,7 @@ class docModel extends model global $app; $action = strtolower($action); + if(!empty($doc->frozen) && in_array($action, array('edit', 'delete'))) return false; if($action == 'movedoc') return $doc->addedBy == $app->user->account; return true; } @@ -774,8 +775,9 @@ class docModel extends model */ public function getDocsOfLibs(array $libs, string $spaceType, int $excludeID = 0, $queryTemplate = false): array { - $docs = $this->dao->select('t1.*')->from(TABLE_DOC)->alias('t1') + $docs = $this->dao->select('t1.*,t3.content')->from(TABLE_DOC)->alias('t1') ->leftJoin(TABLE_MODULE)->alias('t2')->on('t1.module=t2.id') + ->leftJoin(TABLE_DOCCONTENT)->alias('t3')->on('t1.id=t3.doc and t1.version=t3.version') ->where('t1.lib')->in($libs) ->andWhere('t1.vision')->eq($this->config->vision) ->beginIF(!$queryTemplate)->andWhere('t1.templateType')->eq('')->andWhere('t2.type')->eq('doc')->fi() @@ -786,14 +788,15 @@ class docModel extends model ->orderBy('t1.`order` asc, t1.id asc') ->fetchAll('id', false); - $rootDocs = $this->dao->select('*')->from(TABLE_DOC) - ->where('lib')->in($libs) - ->andWhere('vision')->eq($this->config->vision) - ->beginIF(!$queryTemplate)->andWhere('templateType')->eq('')->fi() - ->beginIF($queryTemplate)->andWhere('templateType')->ne('')->andWhere('builtIn')->eq('0')->fi() - ->andWhere("(status = 'normal' or (status = 'draft' and addedBy='{$this->app->user->account}'))") - ->andWhere('module')->in(array('0', '')) - ->beginIF(!empty($excludeID))->andWhere("NOT FIND_IN_SET('{$excludeID}', `path`)")->andWhere('id')->ne($excludeID)->fi() + $rootDocs = $this->dao->select('t1.*,t3.content')->from(TABLE_DOC)->alias('t1') + ->leftJoin(TABLE_DOCCONTENT)->alias('t3')->on('t1.id=t3.doc and t1.version=t3.version') + ->where('t1.lib')->in($libs) + ->andWhere('t1.vision')->eq($this->config->vision) + ->beginIF(!$queryTemplate)->andWhere('t1.templateType')->eq('')->fi() + ->beginIF($queryTemplate)->andWhere('t1.templateType')->ne('')->andWhere('builtIn')->eq('0')->fi() + ->andWhere("(t1.status = 'normal' or (t1.status = 'draft' and t1.addedBy='{$this->app->user->account}'))") + ->andWhere('t1.module')->in(array('0', '')) + ->beginIF(!empty($excludeID))->andWhere("NOT FIND_IN_SET('{$excludeID}', t1.`path`)")->andWhere('t1.id')->ne($excludeID)->fi() ->orderBy('`order` asc, id_asc') ->fetchAll('id', false); @@ -810,6 +813,7 @@ class docModel extends model $doc->isCollector = strpos($doc->collector, ',' . $this->app->user->account . ',') !== false; $doc->title = htmlspecialchars_decode($doc->title); if(!empty($doc->keywords) && is_string($doc->keywords)) $doc->keywords = htmlspecialchars_decode($doc->keywords); + $doc->hasContent = !empty($doc->content) ? true : false; unset($doc->content); unset($doc->draft); } @@ -1727,6 +1731,9 @@ class docModel extends model $files = $this->loadModel('file')->saveUpload('doc', $docID); if(dao::isError()) return false; + $deletedFiles = !empty($doc->deleteFiles) ? explode(',', $doc->deleteFiles) : array(); + unset($doc->deleteFiles); + $oldDoc = $this->getByID($docID); $changes = common::createChanges($oldDoc, $doc); $oldRawContent = isset($oldDoc->rawContent) ? $oldDoc->rawContent : ''; @@ -1734,7 +1741,7 @@ class docModel extends model $onlyRawChanged = $oldRawContent != $newRawContent; $isDraft = $doc->status == 'draft'; $version = $isDraft ? 0 : ($oldDoc->version + 1); - $changed = $files || $onlyRawChanged || (!$isDraft && $oldDoc->version == 0); + $changed = $deletedFiles || $files || $onlyRawChanged || (!$isDraft && $oldDoc->version == 0); $basicInfoChanged = false; foreach($changes as $change) { @@ -1742,8 +1749,13 @@ class docModel extends model if($change['field'] == 'content' || $change['field'] == 'title' || $change['field'] == 'rawContent') $changed = true; if($change['field'] == 'content') $onlyRawChanged = false; } + + $docFiles = array_diff(array_merge(array_keys($files), array_keys($oldDoc->files)), $deletedFiles); + if(empty($docFiles) && !empty($deletedFiles)) return dao::$errors['files'] = sprintf($this->lang->error->notempty, $this->lang->doc->uploadFile); + if($onlyRawChanged) $changes[] = array('field' => 'content', 'old' => $oldDoc->content, 'new' => $doc->content); - if($changed) $this->saveDocContent($docID, $doc, $version, array_merge(array_keys($files), array_keys($oldDoc->files))); + if(!empty($deletedFiles) || !empty($files)) $changes[] = array('field' => 'files', 'old' => implode(',', array_keys($oldDoc->files)), 'new' => implode(',', $docFiles)); + if($changed) $this->saveDocContent($docID, $doc, $version, $docFiles); else $version = $oldDoc->version; if(dao::isError()) return false; @@ -1765,7 +1777,7 @@ class docModel extends model } unset($doc->files); - $this->dao->update(TABLE_DOC)->data($doc, 'content,contentType,rawContent,fromVersion') + $this->dao->update(TABLE_DOC)->data($doc, 'content,contentType,rawContent,fromVersion,deleteFiles') ->autoCheck() ->batchCheck($requiredFields, 'notempty') ->where('id')->eq($docID) @@ -1774,6 +1786,15 @@ class docModel extends model if(dao::isError()) return false; if($files) $this->file->updateObjectID($this->post->uid, $docID, 'doc'); + /* 文档标题变了要同步修改交付物名称. */ + if(in_array($this->config->edition, array('ipd', 'max')) && $oldDoc->title != $doc->title) + { + $this->dao->update(TABLE_PROJECTDELIVERABLE) + ->set('name')->eq($doc->title) + ->where('doc')->eq($docID) + ->exec(); + } + /* 如果修改了父模板,子模板也同步更新相关信息。*/ /* If the parent template is modified, the child template will also update the relevant information synchronously. */ if(!empty($oldDoc->templateType) && empty($doc->parent) && $basicInfoChanged) @@ -1791,7 +1812,7 @@ class docModel extends model $objectType = !empty($doc->templateType) ? 'doctemplate' : 'doc'; $this->loadModel('action')->create($objectType, $docID, 'convertToNewDoc'); } - return array('changes' => $changes, 'files' => array_keys($files)); + return array('changes' => $changes, 'files' => $files, 'deletedFiles' => $deletedFiles); } /** diff --git a/module/doc/test/zen/assignvarsforuploaddocs.php b/module/doc/test/zen/assignvarsforuploaddocs.php index aac3e8bd80..5ff9b32fba 100755 --- a/module/doc/test/zen/assignvarsforuploaddocs.php +++ b/module/doc/test/zen/assignvarsforuploaddocs.php @@ -55,4 +55,4 @@ r($docTest->assignVarsForUploadDocsTest('project', 2, 2, 0, '')) && p('objectTyp r($docTest->assignVarsForUploadDocsTest('custom', 0, 7, 0, '')) && p('objectType,libID,linkType,hasSpaces') && e('custom,7,custom,1'); // 测试自定义空间下上传文档 r($docTest->assignVarsForUploadDocsTest('mine', 0, 9, 0, '')) && p('objectType,libID,linkType,hasSpaces') && e('mine,9,mine,1'); // 测试我的空间下上传文档 r($docTest->assignVarsForUploadDocsTest('execution', 3, 0, 0, '')) && p('objectType,objectID') && e('execution,3'); // 测试执行空间下上传文档 -r($docTest->assignVarsForUploadDocsTest('product', 0, 0, 0, '')) && p('objectType,linkType') && e('product,product'); // 测试不传入任何参数使用默认值 \ No newline at end of file +r($docTest->assignVarsForUploadDocsTest('product', 0, 0, 0, '')) && p('objectType,linkType') && e('product,product'); // 测试不传入任何参数使用默认值 diff --git a/module/doc/test/zen/responseafteredit.php b/module/doc/test/zen/responseafteredit.php index 7e340d984b..d16cbe10ad 100755 --- a/module/doc/test/zen/responseafteredit.php +++ b/module/doc/test/zen/responseafteredit.php @@ -80,4 +80,4 @@ r($docTest->responseAfterEditTest($doc1, $changes1, $files1, $postData1)) && p(' r($docTest->responseAfterEditTest($doc2, $changes2, $files1, $postData2)) && p('result') && e('success'); // 步骤2:测试编辑文档有变更 r($docTest->responseAfterEditTest($doc3, $changes3, $files1, $postData3)) && p('result') && e('success'); // 步骤3:测试草稿状态变更为正常状态 r($docTest->responseAfterEditTest($doc4, $changes1, $files1, $postData4)) && p('result') && e('success'); // 步骤4:测试编辑文档附带评论 -r($docTest->responseAfterEditTest($doc5, $changes2, $files2, $postData5)) && p('result') && e('success'); // 步骤5:测试编辑文档添加附件 \ No newline at end of file +r($docTest->responseAfterEditTest($doc5, $changes2, $files2, $postData5)) && p('result') && e('success'); // 步骤5:测试编辑文档添加附件 diff --git a/module/doc/ui/setdocbasic.html.php b/module/doc/ui/setdocbasic.html.php index 95a5124b03..f2db8e155c 100644 --- a/module/doc/ui/setdocbasic.html.php +++ b/module/doc/ui/setdocbasic.html.php @@ -38,7 +38,7 @@ formPanel set::label($modalType == 'chapter' ? $lang->doc->chapterName : $lang->doc->title), set::name('title'), set::required(true), - set::value(isset($doc) ? $doc->title : '') + set::value($docTitle) ), $this->app->tab == 'doc' && $objectType == 'project' && $modalType != 'chapter' ? formRow ( diff --git a/module/doc/ui/setdocbasic.template.html.php b/module/doc/ui/setdocbasic.template.html.php index 8845ccb45f..b5ab3958ef 100644 --- a/module/doc/ui/setdocbasic.template.html.php +++ b/module/doc/ui/setdocbasic.template.html.php @@ -26,6 +26,7 @@ formPanel set::title($title), set::submitBtnText($lang->save), on::change('[name=lib]')->call('loadScopeTypes', jsRaw("event")), + on::change('[name=isDeliverable]', 'window.changeIsDeliverable'), set::ajax(array('beforeSubmit' => jsRaw('window.beforeSetDocBasicInfo'))), formGroup ( @@ -62,6 +63,17 @@ formPanel set::label($lang->docTemplate->desc), textarea(set::name('templateDesc'), set::value($docID ? $doc->templateDesc : ''), set::rows(3)) ) : null, + $modalType != 'chapter' && in_array($config->edition, array('max', 'ipd')) ? formGroup + ( + set::label($lang->docTemplate->deliverable), + radioList + ( + set::inline(true), + set::name('isDeliverable'), + set::items($lang->docTemplate->deliverableList), + set::value($docID ? $doc->isDeliverable : '0') + ) + ) : null, empty($parentID) ? formGroup ( set::label($lang->doclib->control), @@ -69,8 +81,10 @@ formPanel ( setClass($objectType == 'mine' ? 'pointer-events-none' : ''), set::name('acl'), + set::disabled($docID && $doc->isDeliverable ? true : false), set::items($lang->doc->aclListA), set::value(isset($doc) ? $doc->acl : 'open') - ) + ), + input(setClass('hidden'), set::name('acl'), set::id('acl'), set::disabled($docID && $doc->isDeliverable ? false : true), set::value('open')) ) : null ); diff --git a/module/doc/ui/uploaddocs.html.php b/module/doc/ui/uploaddocs.html.php index 9d99355cb3..e6e73143c4 100644 --- a/module/doc/ui/uploaddocs.html.php +++ b/module/doc/ui/uploaddocs.html.php @@ -10,11 +10,12 @@ declare(strict_types=1); */ namespace zin; +jsVar('docID', $docID); jsVar('libType', $objectType); formPanel ( - set::title($lang->doc->uploadDoc), - set::submitBtnText($lang->save), + set::title(!empty($docID) ? $lang->doc->edit : $lang->doc->uploadFile), + set::submitBtnText($lang->doc->release), $objectType == 'project' ? formRow ( formGroup @@ -34,6 +35,7 @@ formPanel set::label($lang->doc->execution), set::name('execution'), set::items(isset($executions) ? $executions : null), + set::value(!empty($doc->execution) ? $doc->execution : 0), set::placeholder($lang->doc->placeholder->execution), on::change('loadObjectModules') ) : null @@ -81,17 +83,27 @@ formPanel set::label($lang->doc->module), set::name('parent'), set::items(array('m_0' => '/') + $optionMenu), - set::value($moduleID ? "m_$moduleID" : 0), + set::value(!empty($doc->parent) ? $doc->parent : ($moduleID ? "m_$moduleID" : 0)), set::required(true) ), formGroup ( setClass('uploadFileBox'), set::label($lang->doc->uploadFile), - fileSelector(), + fileSelector(set::defaultFiles(!empty($doc->files) ? array_values($doc->files) : null)), set::required(true) ), formGroup + ( + setClass('titleBox'), + set::hidden(!empty($doc) ? false : true), + set::label($lang->doc->title), + set::name('title'), + set::value(!empty($doc->title) ? $doc->title : ''), + set::required(true), + on::input('titleChanged') + ), + formGroup ( setClass('uploadFormatBox'), set::hidden(true), @@ -105,15 +117,6 @@ formPanel on::change('toggleDocTitle') ) ), - formGroup - ( - setClass('titleBox'), - set::hidden(true), - set::label($lang->doc->title), - set::name('title'), - set::required(true), - on::input('titleChanged') - ), formRow ( setID('aclBox'), @@ -124,7 +127,7 @@ formPanel ( set::name('acl'), set::items($lang->doc->aclList), - set::value($objectType == 'mine' ? 'private' : 'open'), + set::value(!empty($doc->acl) ? $doc->acl : ($objectType == 'mine' ? 'private' : 'open')), on::change("toggleAcl('doc')") ) ) diff --git a/module/doc/zen.php b/module/doc/zen.php index f812451d63..75c72d8d23 100644 --- a/module/doc/zen.php +++ b/module/doc/zen.php @@ -578,19 +578,24 @@ class docZen extends doc * 展示上传文件的相关变量。 * Show the related variables of uploading files. * + * @param int $docID * @param string $objectType product|project|execution|custom * @param int $objectID * @param int $libID * @param int $moduleID * @param string $docType html|word|ppt|excel|attachment * @access protected - * @return void + * @return object */ - protected function assignVarsForUploadDocs(string $objectType, int $objectID, int $libID, int $moduleID = 0, string $docType = ''): void + protected function assignVarsForUploadDocs(int $docID, string $objectType, int $objectID, int $libID, int $moduleID = 0, string $docType = ''): object { + $doc = !empty($docID) ? $this->doc->getByID($docID) : null; + if(empty($moduleID) && $doc) $moduleID = (int)$doc->module; + $this->assignVarsForCreate($objectType, $objectID, $libID, $moduleID, $docType); - $chapterAndDocs = $this->doc->getDocsOfLibs(array($libID), $objectType); + $lib = $libID ? $this->doc->getLibByID($libID) : ''; + $chapterAndDocs = $this->doc->getDocsOfLibs(array($libID), $objectType, $docID); $modulePairs = empty($libID) ? array() : $this->loadModel('tree')->getOptionMenu($libID, 'doc', 0); if(isset($doc) && !empty($doc->parent) && !isset($chapterAndDocs[$doc->parent])) $chapterAndDocs[$doc->parent] = $this->doc->fetchByID($doc->parent); $chapterAndDocs = $this->doc->buildNestedDocs($chapterAndDocs, $modulePairs); @@ -599,6 +604,9 @@ class docZen extends doc $this->view->linkType = $objectType; $this->view->spaces = ($objectType == 'mine' || $objectType == 'custom') ? $this->doc->getSubSpacesByType($objectType, false) : array(); $this->view->optionMenu = $chapterAndDocs; + $this->view->docID = $docID; + $this->view->doc = $doc; + return $this->view; } /** @@ -651,12 +659,13 @@ class docZen extends doc * @param object $doc * @param array $changes * @param array $files + * @param array $deletedFiles * @access protected - * @return void + * @return array */ - protected function responseAfterEdit(object $doc, array $changes = array(), array $files = array()) + protected function responseAfterEdit(object $doc, array $changes = array(), array $files = array(), array $deletedFiles = array()): array { - if($this->post->comment != '' || !empty($changes) || !empty($files)) + if($this->post->comment != '' || !empty($changes) || !empty($files) || !empty($deletedFiles)) { $action = 'Commented'; if(!empty($changes)) @@ -665,11 +674,16 @@ class docZen extends doc if($doc->status == 'draft' && $newType == 'normal') $action = 'releasedDoc'; if($changes || $doc->status == $newType || $newType == 'normal') $action = 'Edited'; } + if(!empty($deletedFiles)) $deletedFiles = $this->dao->select('id,title')->from(TABLE_FILE)->where('id')->in($deletedFiles)->fetchPairs(); $fileAction = ''; - if(!empty($files)) $fileAction = $this->lang->addFiles . join(',', $files) . "\n"; - $actionID = $this->action->create('doc', $doc->id, $action, $fileAction . $this->post->comment, '', '', false); - if(!empty($changes)) $this->action->logHistory($actionID, $changes); + if(!empty($files)) $fileAction .= $this->lang->addFiles . implode(',', $files) . "\n"; + if(!empty($deletedFiles)) $fileAction .= $this->lang->delFiles . implode(',', $deletedFiles) . "\n"; + if(!empty($changes)) + { + $actionID = $this->action->create('doc', $doc->id, $action, $fileAction . $this->post->comment, '', '', false); + $this->action->logHistory($actionID, $changes); + } } $link = $this->createLink('doc', 'view', "docID={$doc->id}"); @@ -692,10 +706,10 @@ class docZen extends doc $link = $this->createLink($moduleName, $methodName, $params); } - if(isInModal()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true)); + if(isInModal()) return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true); $doc->isCollector = strpos($doc->collector, ',' . $this->app->user->account . ',') !== false; - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $link, 'doc' => $doc)); + return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $link, 'doc' => $doc); } /** diff --git a/module/execution/config.php b/module/execution/config.php index 903f546948..4e8efddbdc 100644 --- a/module/execution/config.php +++ b/module/execution/config.php @@ -210,6 +210,7 @@ $config->execution->actionList['close']['text'] = $lang->execution->close $config->execution->actionList['close']['hint'] = $lang->execution->close; $config->execution->actionList['close']['url'] = helper::createLink('execution', 'close', "executionID={rawID}"); $config->execution->actionList['close']['data-toggle'] = 'modal'; +$config->execution->actionList['close']['data-size'] = '60rem'; $config->execution->actionList['activate']['icon'] = 'magic'; $config->execution->actionList['activate']['text'] = $lang->execution->activate; diff --git a/module/execution/control.php b/module/execution/control.php index afb6f9b63d..9625f81aad 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -523,6 +523,7 @@ class execution extends control $this->view->productID = $productID; $this->view->project = $project; $this->view->linkedProductCount = count($products); + $this->view->hasFrozenStories = $this->project->hasFrozenObject($project->id, 'SRS'); $this->display(); } @@ -1066,6 +1067,13 @@ class execution extends control $this->loadModel('project')->checkAccess($projectID, $allProjects); if(empty($projectID)) $projectID = key($allProjects) ? key($allProjects) : 0; + /* 过滤掉阶段已打基线的项目。*/ + $executionList = $this->project->getExecutionList(array_keys($allProjects)); + foreach($executionList as $executionInfo) + { + if(!empty($executionInfo->frozen)) unset($allProjects[$executionInfo->project]); + } + $project = empty($projectID) ? null : $this->loadModel('project')->fetchByID($projectID); $project ? $this->executionZen->correctExecutionCommonLang($project, $execution->type) : $project = null; $products = $this->executionZen->getLinkedProducts($copyExecutionID, $planID, $project); @@ -1377,6 +1385,17 @@ class execution extends control $relatedProjects = $this->dao->select('id,project')->from(TABLE_PROJECT)->where('id')->in($executionIDList)->fetchPairs(); /* 获取执行所属的项目列表。*/ $projects = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->in($relatedProjects)->fetchAll('id'); /* 获取执行所属的项目列表中每个项目的项目信息。*/ + $frozenStages = ''; + foreach($executions as $key => $execution) + { + if(!empty($execution->frozen)) + { + $frozenStages .= "#{$execution->id} "; + unset($executions[$key]); + } + } + if(empty($executions) && !empty($frozenStages)) return $this->send(array('result' => 'fail', 'load' => array('alert' => sprintf($this->lang->execution->frozenTip, $frozenStages), 'load' => true))); + list($pmUsers, $poUsers, $qdUsers, $rdUsers) = $this->executionZen->setUserMoreLink($executions); /* Set custom fields. */ @@ -1407,6 +1426,7 @@ class execution extends control $this->view->rdUsers = $rdUsers; $this->view->from = $this->app->tab; $this->view->parents = $this->execution->getByIdList($parentIdList); + $this->view->frozenStages = $frozenStages; $this->display(); } diff --git a/module/execution/lang/de.php b/module/execution/lang/de.php index 853a3c9935..b477409a37 100644 --- a/module/execution/lang/de.php +++ b/module/execution/lang/de.php @@ -163,6 +163,10 @@ $lang->execution->typeTip = "The sub-stages of other types can be cre $lang->execution->waterfallTip = "In the Waterfall {$lang->projectCommon} or in the Waterfall + {$lang->projectCommon},"; $lang->execution->progressTip = 'All Progress = Consumed / (Consumed + Left)'; $lang->execution->limitedTip = "Limited users can only edit tasks that are relevant to them and cannot create new tasks. Relevant tasks include those assigned to them, completed tasks, canceled tasks, closed tasks, and the last edited tasks, but exclude those copied to them. \nIf a user was not a limited user before but is now classified as one, they will still have permissions for tasks they created in the past."; +$lang->execution->stageFrozenTip = 'After the stage are baselined, %s is not allowed.'; +$lang->execution->createChildStage = 'create child stage'; +$lang->execution->ganttDrag = 'Drag'; +$lang->execution->frozenTip = 'Stage %s has been frozen and will not be editable.'; $lang->execution->start = 'Start'; $lang->execution->activate = 'Aktivieren'; diff --git a/module/execution/lang/en.php b/module/execution/lang/en.php index 7bf9c3c25a..0b2bc6da78 100644 --- a/module/execution/lang/en.php +++ b/module/execution/lang/en.php @@ -163,6 +163,10 @@ $lang->execution->typeTip = "The sub-stages of other types can be cre $lang->execution->waterfallTip = "In the Waterfall {$lang->projectCommon} or in the Waterfall + {$lang->projectCommon},"; $lang->execution->progressTip = 'All Progress = Consumed / (Consumed + Left)'; $lang->execution->limitedTip = "Limited users can only edit tasks that are relevant to them and cannot create new tasks. Relevant tasks include those assigned to them, completed tasks, canceled tasks, closed tasks, and the last edited tasks, but exclude those copied to them. \nIf a user was not a limited user before but is now classified as one, they will still have permissions for tasks they created in the past."; +$lang->execution->stageFrozenTip = 'After the stage are baselined, %s is not allowed.'; +$lang->execution->createChildStage = 'create child stage'; +$lang->execution->ganttDrag = 'Drag'; +$lang->execution->frozenTip = 'Stage %s has been frozen and will not be editable.'; $lang->execution->start = 'Start'; $lang->execution->activate = 'Activate'; diff --git a/module/execution/lang/fr.php b/module/execution/lang/fr.php index becad61f4c..2668af1c4a 100644 --- a/module/execution/lang/fr.php +++ b/module/execution/lang/fr.php @@ -163,6 +163,10 @@ $lang->execution->typeTip = "The sub-stages of other types can be cre $lang->execution->waterfallTip = "In the Waterfall {$lang->projectCommon} or in the Waterfall + {$lang->projectCommon},"; $lang->execution->progressTip = 'All Progress = Consumed / (Consumed + Left)'; $lang->execution->limitedTip = "Limited users can only edit tasks that are relevant to them and cannot create new tasks. Relevant tasks include those assigned to them, completed tasks, canceled tasks, closed tasks, and the last edited tasks, but exclude those copied to them. \nIf a user was not a limited user before but is now classified as one, they will still have permissions for tasks they created in the past."; +$lang->execution->stageFrozenTip = 'After the stage are baselined, %s is not allowed.'; +$lang->execution->createChildStage = 'create child stage'; +$lang->execution->ganttDrag = 'Drag'; +$lang->execution->frozenTip = 'Stage %s has been frozen and will not be editable.'; $lang->execution->start = 'Démarrer'; $lang->execution->activate = 'Activer'; diff --git a/module/execution/lang/zh-cn.php b/module/execution/lang/zh-cn.php index 8b5fb30479..a8b939002c 100644 --- a/module/execution/lang/zh-cn.php +++ b/module/execution/lang/zh-cn.php @@ -163,6 +163,10 @@ $lang->execution->typeTip = '“综合”类型的父阶段可以创 $lang->execution->waterfallTip = "瀑布{$lang->projectCommon}和融合瀑布{$lang->projectCommon}中,"; $lang->execution->progressTip = '总进度 = 已消耗工时 / (已消耗工时 + 剩余工时)'; $lang->execution->limitedTip = "受限用户只能编辑与自己相关的任务,不能新增任务。与自己相关的内容,包含指派给、已完成、已取消、已关闭、最后编辑,不包含抄送给的内容。 \n如果该用户之前不是受限用户,现在是受限用户,那么之前由他创建的任务还有相关的操作权限。"; +$lang->execution->stageFrozenTip = '阶段打基线后不允许%s'; +$lang->execution->createChildStage = '创建子阶段'; +$lang->execution->ganttDrag = '拖拽'; +$lang->execution->frozenTip = '阶段:%s 已冻结,将不会被编辑。'; $lang->execution->start = "开始"; $lang->execution->activate = "激活"; diff --git a/module/execution/model.php b/module/execution/model.php index 5d67dddaca..1fc0487482 100755 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -179,8 +179,11 @@ class executionModel extends model if(!empty($this->lang->execution->menu->other['dropMenu']->pssp)) { - $this->lang->execution->menu->pssp = $this->lang->execution->menu->other['dropMenu']->pssp; - $this->lang->execution->menu->auditplan = $this->lang->execution->menu->other['dropMenu']->auditplan; + $this->lang->execution->menu->pssp = $this->lang->execution->menu->other['dropMenu']->pssp; + if(isset($this->lang->execution->menu->other['dropMenu']->auditplan)) + { + $this->lang->execution->menu->auditplan = $this->lang->execution->menu->other['dropMenu']->auditplan; + } $docOrder = 0; foreach($this->lang->execution->menuOrder as $order => $menu) if($menu == 'doc') $docOrder = $order; @@ -614,7 +617,7 @@ class executionModel extends model public function batchChangeStatus(array $executionIdList, string $status): array { /* Sort the IDs, the child stage comes first, and the parent stage follows. */ - $executionList = $this->dao->select('id,name,status,grade,deliverable')->from(TABLE_EXECUTION)->where('id')->in($executionIdList)->filterTpl(false)->orderBy('grade_desc')->fetchAll('id'); + $executionList = $this->dao->select('id,name,status,grade,type,attribute,project')->from(TABLE_EXECUTION)->where('id')->in($executionIdList)->filterTpl(false)->orderBy('grade_desc')->fetchAll('id'); $this->loadModel('programplan'); $message = array('byChild' => '', 'byDeliverable' => ''); @@ -3005,8 +3008,15 @@ class executionModel extends model */ public function unlinkStory(int $executionID, int $storyID, int $laneID = 0, int $columnID = 0): array|bool { + $storyFrozen = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch('frozen'); + if(!empty($storyFrozen)) + { + $this->app->loadLang('story'); + dao::$errors[] = sprintf($this->lang->story->frozenTip, $this->lang->story->unlink); + return false; + } + $execution = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch(); - $storyType = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch('type'); if($execution->type == 'project') { $executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('parent')->eq($executionID)->fetchAll('id'); @@ -3919,6 +3929,7 @@ class executionModel extends model */ public static function isClickable(object $execution, string $action): bool { + if(!empty($execution->frozen) && in_array($action, array('edit', 'createChildStage', 'delete', 'putoff'))) return false; if($action == 'createChildStage') return commonModel::hasPriv('programplan', 'create') && $execution->type == 'stage'; if($action == 'createTask') return commonModel::hasPriv('task', 'create') && commonModel::hasPriv('execution', 'create') && empty($execution->isParent); if(!commonModel::hasPriv('execution', $action)) return false; @@ -4954,6 +4965,7 @@ class executionModel extends model if($actionName == 'createChildStage' && $action['disabled'] && $execution->type != 'stage') $action['hint'] = $this->lang->programplan->error->notStage; if(!$action['disabled']) break; if($actionName == 'close' && $execution->status != 'closed') break; + if(!empty($execution->frozen) && in_array($actionName, array('edit', 'createChildStage', 'delete', 'putoff'))) $action['hint'] = sprintf($this->lang->execution->stageFrozenTip, $this->lang->execution->$actionName); } if(!empty($action)) @@ -4975,14 +4987,13 @@ class executionModel extends model $execution->PMAvatar = zget($avatarList, $execution->PMAccount, ''); } - if(in_array($this->config->edition, array('max', 'ipd'))) $execution->deliverable = $this->loadModel('project')->countDeliverable($execution, 'execution'); - $rows[$execution->id] = $execution; /* Append tasks and child stages. */ if(!empty($execution->tasks)) $rows = $this->appendTasks($execution->tasks, $rows, $users, $avatarList, $canModify); } + if(in_array($this->config->edition, array('max', 'ipd'))) $rows = $this->loadModel('project')->countDeliverable($rows, 'execution'); return $rows; } @@ -5450,24 +5461,28 @@ class executionModel extends model */ public function canCloseByDeliverable(object $execution): bool { - if(empty($execution->deliverable)) return true; + $stageType = $execution->type == 'stage' ? $execution->attribute : $execution->type; + $project = $this->loadModel('project')->fetchByID((int)$execution->project); + $deliverables = $this->dao->select('t1.template,t1.name,t2.required,t1.id')->from(TABLE_DELIVERABLE)->alias('t1') + ->leftJoin(TABLE_DELIVERABLESTAGE)->alias('t2')->on('t1.id = t2.deliverable') + ->where('t1.deleted')->eq('0') + ->andWhere('t1.workflowGroup')->eq((int)$project->workflowGroup) + ->andWhere('t1.status')->eq('enabled') + ->andWhere('t2.stage')->eq($stageType) + ->fetchAll('id'); - $deliverables = json_decode($execution->deliverable, true); + if(empty($deliverables)) return true; - if(!is_array($deliverables) || empty($deliverables)) return true; + $executionDeliverables = $execution->deliverable ? json_decode($execution->deliverable, true) : array(); - foreach($deliverables as $methods) + foreach($deliverables as $id => $deliverable) { - foreach($methods as $itemList) - { - foreach($itemList as $item) - { - if(!empty($item['required']) && empty($item['file']) && empty($item['doc'])) return false; - } - } + if(empty($deliverable->required)) continue; + + if(!isset($executionDeliverables[$id])) return false; + if(empty($executionDeliverables[$id]['fileID']) && empty($executionDeliverables[$id]['doc'])) return false; } return true; } - } diff --git a/module/execution/test/model/canclosebydeliverable.php b/module/execution/test/model/canclosebydeliverable.php index 44ea82b2d4..7c89946cef 100755 --- a/module/execution/test/model/canclosebydeliverable.php +++ b/module/execution/test/model/canclosebydeliverable.php @@ -3,8 +3,9 @@ include dirname(__FILE__, 5) . '/test/lib/init.php'; include dirname(__FILE__, 2) . '/lib/execution.unittest.class.php'; -$execution = zenData('project')->gen(5); +$execution = zenData('project'); $execution->id->range('1-5'); +$execution->project->range('1-5'); $execution->name->range('迭代1,迭代2,迭代3,迭代4,迭代5'); $execution->type->range('stage'); $execution->status->range('doing'); @@ -13,7 +14,6 @@ $execution->grade->range('1{2},2{3}'); $execution->path->range('1,2,`1,3`,`1,4`,`1,5`')->prefix(',')->postfix(','); $execution->begin->range('20230102 000000:0')->type('timestamp')->format('YY/MM/DD'); $execution->end->range('20230212 000000:0')->type('timestamp')->format('YY/MM/DD'); -$execution->deliverable->range('`{"product_waterfall_request":{"whenClosed":[{"deliverable":"1","required":true,"category":"test","template":""}]}}`'); $execution->gen(5); su('admin'); diff --git a/module/execution/test/model/unlinkstory.php b/module/execution/test/model/unlinkstory.php index 4a02e96fb2..c0b08b5b4f 100755 --- a/module/execution/test/model/unlinkstory.php +++ b/module/execution/test/model/unlinkstory.php @@ -55,7 +55,23 @@ su('admin'); title=测试executionModel->unlinkStoryTest(); timeout=0 -cid=16374 +cid=0 + +- 敏捷执行解除关联需求,移除迭代3中的需求4 + - 第0条的project属性 @3 + - 第0条的product属性 @1 + - 第0条的story属性 @324 +- 瀑布执行解除关联需求,移除迭代4中的需求324 + - 第0条的project属性 @4 + - 第0条的product属性 @1 + - 第0条的story属性 @4 +- 看板执行解除关联需求,移除迭代5中的需求364 + - 第0条的project属性 @5 + - 第0条的product属性 @1 + - 第0条的story属性 @4 +- 敏捷执行关联需求统计 @2 +- 瀑布执行关联需求统计 @2 +- 看板执行关联需求统计 @2 */ diff --git a/module/execution/ui/activate.html.php b/module/execution/ui/activate.html.php index d1e6f77e03..6eca1e1aaf 100644 --- a/module/execution/ui/activate.html.php +++ b/module/execution/ui/activate.html.php @@ -26,13 +26,15 @@ formPanel datePicker ( set::name('begin'), - set::value($newBegin) + set::value($newBegin), + set::disabled(!empty($execution->frozen)) ), $lang->execution->to, datePicker ( set::name('end'), - set::value($newEnd) + set::value($newEnd), + set::disabled(!empty($execution->frozen)) ) ) ), diff --git a/module/execution/ui/batchedit.html.php b/module/execution/ui/batchedit.html.php index f77ec53086..b29ff088dd 100644 --- a/module/execution/ui/batchedit.html.php +++ b/module/execution/ui/batchedit.html.php @@ -10,6 +10,12 @@ declare(strict_types=1); */ namespace zin; +if(!empty($frozenStages)) +{ + $frozenTip = sprintf($lang->execution->frozenTip, $frozenStages); + pageJS("zui.Modal.alert({message: '{$frozenTip}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'});\n"); +} + $setCode = (isset($config->setCode) and $config->setCode == 1); $showMethod = $app->tab == 'project' && isset($project) && ($project->model == 'agileplus' || $project->model == 'waterfallplus'); diff --git a/module/execution/ui/story.html.php b/module/execution/ui/story.html.php index ca294dbdb9..826f58120e 100644 --- a/module/execution/ui/story.html.php +++ b/module/execution/ui/story.html.php @@ -193,8 +193,8 @@ $batchItems = array(); if($canOpreate['batchCreate']) $batchItems[] = array('text' => $lang->SRCommon, 'url' => $batchCreateLink); if(in_array($execution->attribute, array('mix', 'request', 'design')) || !$execution->multiple) { - if($canOpreate['createRequirement']) $createItems[] = array('text' => $lang->requirement->create, 'url' => $createRequirementLink); - if($canOpreate['createEpic']) $createItems[] = array('text' => $lang->epic->create, 'url' => $createEpicLink); + if($canOpreate['createRequirement']) $createItems[] = array('text' => $lang->requirement->create, 'url' => $createRequirementLink, 'hint' => $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->requirement->create) : ''); + if($canOpreate['createEpic']) $createItems[] = array('text' => $lang->epic->create, 'url' => $createEpicLink, 'hint' => $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->epic->create) : ''); if($canOpreate['batchCreateRequirement']) $batchItems[] = array('text' => $lang->URCommon, 'url' => $batchCreateRequirementLink); if($canOpreate['batchCreateEpic']) $batchItems[] = array('text' => $lang->ERCommon, 'url' => $batchCreateEpicLink); } @@ -203,7 +203,7 @@ if(!empty($product->id)) { if(count($batchItems) > 1) { - $createItems[] = array('text' => $lang->story->batchCreate, 'items' => $batchItems); + $createItems[] = array('text' => $lang->story->batchCreate, 'items' => $batchItems, 'hint' => $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->story->batchCreate) : ''); } else { @@ -223,8 +223,8 @@ if(commonModel::isTutorialMode()) $canlinkPlanStory = false; } -$linkItem = array('text' => $lang->story->linkStory, 'url' => $linkStoryUrl, 'data-app' => $app->tab); -$linkPlanItem = array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm'); +$linkItem = array('text' => $lang->story->linkStory, 'url' => $linkStoryUrl, 'data-app' => $app->tab, 'hint' => $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->story->linkStory) : ''); +$linkPlanItem = array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm', 'hint' => $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->execution->linkStoryByPlan) : ''); $createBtnGroup = null; if(!$isFromDoc && !$isFromAI) @@ -238,9 +238,11 @@ if(!$isFromDoc && !$isFromAI) setClass('btn secondary'), set::icon('plus'), set::url($createLink), + set::disabled(!empty($hasFrozenStories)), + $hasFrozenStories ? set::hint(sprintf($lang->story->frozenTip, $lang->story->create)) : null, $lang->story->create ), - empty($createItems) ? null : dropdown + empty($createItems) || $hasFrozenStories ? null : dropdown ( btn(setClass('btn secondary dropdown-toggle'), setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))), @@ -251,7 +253,7 @@ if(!$isFromDoc && !$isFromAI) } elseif(count($createItems) == 1) { - $createBtnGroup = item(set($createItems[0] + array('class' => 'btn secondary', 'icon' => 'plus'))); + $createBtnGroup = item(set($createItems[0] + array('class' => 'btn secondary' . ($hasFrozenStories ? ' disabled' : ''), 'icon' => 'plus'))); } } @@ -300,10 +302,12 @@ if($product && !$isFromDoc && !$isFromAI) toolbar setClass('btn primary'), set::icon('link'), set::url($linkStoryUrl), + set::disabled(!empty($hasFrozenStories)), + $hasFrozenStories ? set::hint(sprintf($lang->story->frozenTip, $lang->story->linkStory)) : null, setData('app', $app->tab), $lang->story->linkStory ), - dropdown + $hasFrozenStories ? null : dropdown ( btn(setClass('btn primary dropdown-toggle'), setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))), @@ -311,8 +315,8 @@ if($product && !$isFromDoc && !$isFromAI) toolbar set::placement('bottom-end') ) ) : null, - $canLinkStory && !$canlinkPlanStory ? item(set($linkItem + array('class' => 'btn primary link-story-btn', 'icon' => 'link'))) : null, - $canlinkPlanStory && !$canLinkStory ? item(set($linkPlanItem + array('class' => 'btn primary', 'icon' => 'link'))) : null + $canLinkStory && !$canlinkPlanStory ? item(set($linkItem + array('class' => 'btn primary link-story-btn' . ($hasFrozenStories ? ' disabled' : ''), 'icon' => 'link'))) : null, + $canlinkPlanStory && !$canLinkStory ? item(set($linkPlanItem + array('class' => 'btn primary' . ($hasFrozenStories ? ' disabled' : ''), 'icon' => 'link'))) : null ); if(!$isFromDoc && !$isFromAI) sidebar @@ -414,7 +418,7 @@ $checkObject->execution = $execution->id; $canBatchEdit = common::hasPriv('story', 'batchEdit'); $canBatchClose = common::hasPriv('story', 'batchClose') && $storyType != 'requirement'; $canBatchChangeStage = common::hasPriv('story', 'batchChangeStage') && $storyType != 'requirement'; -$canBatchUnlink = ($execution->hasProduct || $app->tab == 'execution') && common::hasPriv('execution', 'batchUnlinkStory'); +$canBatchUnlink = empty($hasFrozenStories) && ($execution->hasProduct || $app->tab == 'execution') && common::hasPriv('execution', 'batchUnlinkStory'); $canBatchToTask = common::hasPriv('story', 'batchToTask', $checkObject) && $storyType != 'requirement'; $canBatchAssignTo = common::hasPriv($storyType, 'batchAssignTo'); $canBatchAction = $canBeChanged && in_array(true, array($canBatchEdit, $canBatchClose, $canBatchChangeStage, $canBatchUnlink, $canBatchToTask, $canBatchAssignTo)); @@ -611,7 +615,7 @@ dtable ($isFromDoc || $isFromAI) ? null : set::sortLink(createLink('execution', 'story', "executionID={$execution->id}&storyType={$storyType}&orderBy={name}_{sortType}&type={$type}¶m={$param}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&page={$pager->pageID}")), ($isFromDoc || $isFromAI) ? null : set::checkInfo(jsRaw('function(checkedIDList){return window.setStatistics(this, checkedIDList);}')), ($isFromDoc || $isFromAI) ? null : set::createTip($lang->story->create), - ($isFromDoc || $isFromAI) ? null : set::createLink($createStoryLink) + ($isFromDoc || $isFromAI || $hasFrozenStories) ? null : set::createLink($createStoryLink) ); render(); diff --git a/module/execution/ui/taskkanban.html.php b/module/execution/ui/taskkanban.html.php index 2d9dd20652..0d188be988 100644 --- a/module/execution/ui/taskkanban.html.php +++ b/module/execution/ui/taskkanban.html.php @@ -57,11 +57,11 @@ $canBatchCreateRisk = $canModifyExecution && common::hasPriv('risk', 'batchCrea $canCreateBug = ($canModifyExecution && $productID && common::hasPriv('bug', 'create')); $canBatchCreateBug = ($canModifyExecution && $productID && common::hasPriv('bug', 'batchCreate')); $canImportBug = ($canModifyExecution && $productID && !$isLimited && common::hasPriv('execution', 'importBug')); -$canCreateStory = ($canModifyExecution && $productID && common::hasPriv('story', 'create')); -$canBatchCreateStory = ($canModifyExecution && $productID && common::hasPriv('story', 'batchCreate')); -$canLinkStory = ($canModifyExecution && $productID && common::hasPriv('execution', 'linkStory') && !empty($execution->hasProduct)); -$canLinkStoryByPlan = ($canModifyExecution && $productID && common::hasPriv('execution', 'importplanstories') && !$hiddenPlan && !empty($execution->hasProduct)); -$hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlan); +$canCreateStory = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('story', 'create')); +$canBatchCreateStory = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('story', 'batchCreate')); +$canLinkStory = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('execution', 'linkStory') && !empty($execution->hasProduct)); +$canLinkStoryByPlan = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('execution', 'importplanstories') && !$hiddenPlan && !empty($execution->hasProduct)); +$hasStoryButton = !$hasFrozenStories && ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlan); $hasTaskButton = ($canCreateTask or $canBatchCreateTask or $canImportBug); $hasBugButton = ($canCreateBug or $canBatchCreateBug); @@ -254,7 +254,7 @@ row btn(setClass('primary'),set::icon('plus'), $lang->create), set::items(array ( - $features['story'] && common::hasPriv('story', 'create') ? ($hasStoryButton && $canCreateStory && !empty($productID) ? array('text' => $lang->execution->createStory, 'url' => createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : array('text' => $lang->story->create, 'data-on' => 'click', 'data-do' => "zui.Modal.alert('" . $lang->execution->needLinkProducts . "')")) : null, + !$hasFrozenStories && $features['story'] && common::hasPriv('story', 'create') ? ($hasStoryButton && $canCreateStory && !empty($productID) ? array('text' => $lang->execution->createStory, 'url' => createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : array('text' => $lang->story->create, 'data-on' => 'click', 'data-do' => "zui.Modal.alert('" . $lang->execution->needLinkProducts . "')")) : null, ($features['story'] && $hasStoryButton && $canBatchCreateStory) ? array('text' => $lang->execution->batchCreateStory, 'url' => createLink('story', 'batchCreate', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null, ($features['story'] && $hasStoryButton && $canLinkStory) ? array('text' => $lang->execution->linkStory, 'url' => createLink('execution', 'linkStory', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null, ($features['story'] && $hasStoryButton && $canLinkStoryByPlan) ? array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm') : null, diff --git a/module/execution/zen.php b/module/execution/zen.php index dc4de8d168..62bd789374 100644 --- a/module/execution/zen.php +++ b/module/execution/zen.php @@ -369,6 +369,8 @@ class executionZen extends execution $project = $this->project->getByID($execution->project); + $this->view->hasFrozenStories = $this->project->hasFrozenObject($execution->project, 'SRS'); + $this->view->title = $this->lang->execution->kanban; $this->view->userList = $userList; $this->view->realnames = $users; @@ -1597,7 +1599,7 @@ class executionZen extends execution if($this->config->edition != 'open') { $flow = $this->loadModel('workflow')->getByModule($module); - if(!empty($flow) && in_array($flow->app, array('scrum', 'waterfall'))) $flow->app = 'project'; + if(!empty($flow) && in_array($flow->app, array('scrum', 'waterfall', 'kanbanProject'))) $flow->app = 'project'; if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "#app=$flow->app"; } @@ -1642,7 +1644,11 @@ class executionZen extends execution { $link = helper::createLink('doc', $method, "type=execution&objectID=%s&from=execution"); } - elseif(in_array($module, array('issue', 'risk', 'opportunity', 'pssp', 'auditplan', 'nc', 'meeting'))) + elseif($module == 'pssp') + { + $link = helper::createLink($module, 'browse', "projectID=%s") . "#app={$this->app->tab}"; + } + elseif(in_array($module, array('issue', 'risk', 'opportunity', 'auditplan', 'nc', 'meeting'))) { $link = helper::createLink($module, 'browse', "executionID=%s&from=execution"); } @@ -1658,6 +1664,10 @@ class executionZen extends execution { $link = helper::createLink('execution', 'relation', "executionID=%s"); } + elseif($module == 'project' && $method == 'deliverablechecklist') + { + $link = helper::createLink('project', 'deliverableChecklist', "projectID=%s") . "#app={$this->app->tab}"; + } if($type != '') $link .= "&type=$type"; return $link; diff --git a/module/file/test/lib/file.unittest.class.php b/module/file/test/lib/file.unittest.class.php index 08dbe8d050..1f20bdc9fb 100755 --- a/module/file/test/lib/file.unittest.class.php +++ b/module/file/test/lib/file.unittest.class.php @@ -635,28 +635,28 @@ class fileTest public function __constructTest(): array { global $tester; - + // 创建新的fileModel实例来测试构造函数 $fileModel = $tester->loadModel('file'); - + $result = array(); - + // 测试now属性是否为整数类型 $result['nowIsInt'] = is_int($fileModel->now) ? 1 : 0; - + // 测试savePath属性是否包含upload路径 $result['savePathContainsUpload'] = (strpos($fileModel->savePath, 'upload') !== false) ? 1 : 0; - + // 测试webPath属性是否包含upload路径 $result['webPathContainsUpload'] = (strpos($fileModel->webPath, 'upload') !== false) ? 1 : 0; - + // 测试now属性是否接近当前时间(允许5秒误差) $currentTime = time(); $result['nowIsRecent'] = (abs($fileModel->now - $currentTime) <= 5) ? 1 : 0; - + // 测试是否继承了父类属性(检查dao属性) $result['hasParentProperties'] = isset($fileModel->dao) ? 1 : 0; - + return $result; } @@ -686,7 +686,7 @@ class fileTest public function getByGidTest(string $gid): object|false { $result = $this->objectModel->getByGid($gid); - + if(dao::isError()) return dao::getError(); if(empty($result)) return false; @@ -769,7 +769,7 @@ class fileTest // 调试输出 if($result === false) return 'false'; if(empty($result)) return 'empty'; - + return is_array($result) ? count($result) : $result; } @@ -787,13 +787,13 @@ class fileTest { // 模拟saveAFile方法的行为,但跳过实际的文件移动操作 $now = helper::today(); - + // 如果是无效路径的测试,直接返回false if(isset($file['tmpname']) && strpos($file['tmpname'], '/nonexistent/') !== false) { return false; } - + // 模拟文件压缩处理(如果是图片) if(isset($file['extension']) && in_array(strtolower($file['extension']), array('jpg', 'jpeg', 'png', 'bmp'))) { @@ -807,13 +807,13 @@ class fileTest $file['addedBy'] = $this->objectModel->app->user->account; $file['addedDate'] = $now; $file['extra'] = $extra; - + // 移除tmpname,模拟真实的saveAFile方法行为 if(isset($file['tmpname'])) unset($file['tmpname']); - + // 插入数据库 $this->objectModel->dao->insert(TABLE_FILE)->data($file)->exec(); - + if(dao::isError()) return dao::getError(); $fileTitle = new stdclass(); @@ -870,12 +870,12 @@ class fileTest return 'gd_not_loaded'; } - try + try { $result = $this->objectModel->cropImage($rawImage, $target, $x, $y, $width, $height, $resizeWidth, $resizeHeight); - + if($result === false) return 'gd_not_loaded'; - + return 'success'; } catch(Exception $e) @@ -902,22 +902,22 @@ class fileTest return 'gd_not_loaded'; } - try + try { // 捕获并清理错误输出缓冲区 ob_start(); $result = $this->objectModel->imagecreatefrombmp($filename); $output = ob_get_clean(); - + // 如果有HTML错误输出,返回exception if(!empty($output) && (strpos($output, 'alert alert-danger') !== false || strpos($output, 'Failed to open stream') !== false)) { return 'exception'; } - + if($result === false) return false; if(is_resource($result) || (is_object($result) && $result instanceof GdImage)) return 'resource'; - + return 'unknown'; } catch(Exception $e) @@ -943,9 +943,9 @@ class fileTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('dwordize'); $method->setAccessible(true); - + if(dao::isError()) return dao::getError(); - + try { $result = $method->invoke($this->objectModel, $str); return $result; @@ -966,16 +966,16 @@ class fileTest { global $tester; $fileModel = $tester->loadModel('file'); - + // 通过反射访问protected方法 $reflection = new ReflectionClass('fileZen'); $method = $reflection->getMethod('getDownloadMode'); $method->setAccessible(true); - + // 创建fileZen实例 $fileZen = new fileZen(); $result = $method->invoke($fileZen, $file, $mouse); - + if(dao::isError()) return dao::getError(); return $result; @@ -991,33 +991,33 @@ class fileTest public function unlinkRealFileTest($file) { global $tester; - + // 记录调用前的状态 $beforeCount = 0; if(!empty($file) && isset($file->pathname)) { $beforeCount = $tester->dao->select('COUNT(1) as count')->from(TABLE_FILE)->where('pathname')->eq($file->pathname)->fetch('count'); } - + try { // 加载必要的文件 $fileModel = $tester->loadModel('file'); - + // 加载zen文件 $zenFile = $tester->app->getModuleRoot() . 'file' . DS . 'zen.php'; if(file_exists($zenFile)) { include_once $zenFile; } - + // 通过反射访问protected方法 if(class_exists('fileZen')) { $reflection = new ReflectionClass('fileZen'); $method = $reflection->getMethod('unlinkRealFile'); $method->setAccessible(true); - + // 创建fileZen实例 $fileZen = new fileZen(); $result = $method->invoke($fileZen, $file); @@ -1035,14 +1035,14 @@ class fileTest } } } - + // 检查调用后的状态 $afterCount = 0; if(!empty($file) && isset($file->pathname)) { $afterCount = $tester->dao->select('COUNT(1) as count')->from(TABLE_FILE)->where('pathname')->eq($file->pathname)->fetch('count'); } - + // 返回状态信息用于断言 return array( 'beforeCount' => $beforeCount, @@ -1059,9 +1059,9 @@ class fileTest 'error' => $e->getMessage() ); } - + if(dao::isError()) return dao::getError(); - + return array('called' => true, 'beforeCount' => $beforeCount, 'afterCount' => 0); } diff --git a/module/group/lang/de.php b/module/group/lang/de.php index 417a74adce..d134db1960 100644 --- a/module/group/lang/de.php +++ b/module/group/lang/de.php @@ -400,9 +400,16 @@ $lang->group->package->programPlan = 'Program Plan'; $lang->group->package->browseDesign = 'Browse Design'; $lang->group->package->manageDesign = 'Manage Design'; $lang->group->package->deleteDesign = 'Delete Design'; +$lang->group->package->browseReview = 'Browse Review'; $lang->group->package->manageReview = 'Manage Review'; -$lang->group->package->manageIssue = 'Manage Issue'; -$lang->group->package->projectSetting = 'Project Setting'; +$lang->group->package->deleteReview = 'Delete Review'; +$lang->group->package->manageReviewIssue = 'Manage ReviewIssue'; +$lang->group->package->browseBaseline = 'Browse Baseline'; +$lang->group->package->manageBaseline = 'Manage Baseline'; +$lang->group->package->deleteBaseline = 'Delete Baseline'; +$lang->group->package->browseProjectchange = 'Browse Projectchange'; +$lang->group->package->manageProjectchange = 'Manage Projectchange'; +$lang->group->package->deleteProjectchange = 'Delete Projectchange'; $lang->group->package->browseProjectRelease = 'Browse Project Release'; $lang->group->package->browseProjectReport = 'Browse Report'; $lang->group->package->exportProjectReport = 'Export Report'; @@ -447,6 +454,9 @@ $lang->group->package->workflowDatasource = 'Workflow Datasource'; $lang->group->package->workflowRule = 'Workflow Rule'; $lang->group->package->workflowGroup = 'Workflow Group'; $lang->group->package->workflow = 'Workflow'; +$lang->group->package->productFlow = 'Product Flow'; +$lang->group->package->projectFlow = 'Project Flow'; +$lang->group->package->flowReport = 'Flow Report'; $lang->group->package->rule = 'Rule'; $lang->group->package->downloadCode = 'Download Code'; $lang->group->package->dev = 'Dev'; @@ -491,13 +501,17 @@ $lang->group->package->projectBuild = 'Project Build'; $lang->group->package->importCaseLib = 'Import Case Lib'; $lang->group->package->commonSetting = 'Common Setting'; $lang->group->package->stageSetting = 'Stage Setting'; -$lang->group->package->deliverable = 'Deliverable Setting'; +$lang->group->package->stageType = 'Stage Setting'; +$lang->group->package->reviewFlow = 'Review'; +$lang->group->package->reviewCheck = 'Review Checklist'; +$lang->group->package->reviewCategory = 'Review Category'; +$lang->group->package->setWorkflow = 'Flow'; +$lang->group->package->deliverable = 'Deliverable Category'; $lang->group->package->classify = 'Classify'; -$lang->group->package->cmcl = 'Cmcl'; $lang->group->package->auditcl = 'Auditcl'; $lang->group->package->reviewcl = 'Reviewcl'; -$lang->group->package->process = 'Process'; -$lang->group->package->activity = 'Activity'; +$lang->group->package->process = 'Process Setting'; +$lang->group->package->activity = 'Activity Setting'; $lang->group->package->zoutput = 'Zoutput'; $lang->group->package->custom = 'Custom'; $lang->group->package->approvalflow = 'Approval flow'; @@ -536,6 +550,7 @@ $lang->group->package->browseExecutionStory = 'Browse Execution ' . $lang->SRCo $lang->group->package->manageCard = 'Manage Card'; $lang->group->package->browseProjectStory = 'Browse Project ' . $lang->SRCommon; $lang->group->package->chckAuditPlan = 'Chck Audit Plan'; +$lang->group->package->deliverableChecklist = 'Deliverable Check'; $lang->group->package->reviewAssess = 'Review Assess'; $lang->group->package->reviewAudit = 'Review Audit'; $lang->group->package->dimension = 'Dimension'; @@ -636,10 +651,14 @@ $lang->group->package->component = 'Component'; $lang->group->package->browseRule = 'Browse Rule'; $lang->group->package->manageRule = 'Manage Rule'; $lang->group->package->executionDeliverable = 'Execution Deliverable'; -$lang->group->package->projectDeliverable = 'Project Deliverable'; +$lang->group->package->browseDeliverable = 'Browse Deliverable'; +$lang->group->package->manageDeliverable = 'Manage Deliverable'; +$lang->group->package->deleteDeliverable = 'Delete Deliverable'; $lang->group->package->projectTemplate = 'Project Template'; $lang->group->package->browseReportTemplate = 'Browse Report Template'; $lang->group->package->manageReportTemplate = 'Manage Report Template'; $lang->group->package->deleteReportTemplate = 'Delete Report Template'; +$lang->group->package->productSetting = 'Product Setting'; +$lang->group->package->executionSetting = 'Execution Setting'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/lang/en.php b/module/group/lang/en.php index 40aab2578a..b039b1dad3 100644 --- a/module/group/lang/en.php +++ b/module/group/lang/en.php @@ -400,9 +400,16 @@ $lang->group->package->programPlan = 'Program Plan'; $lang->group->package->browseDesign = 'Browse Design'; $lang->group->package->manageDesign = 'Manage Design'; $lang->group->package->deleteDesign = 'Delete Design'; +$lang->group->package->browseReview = 'Browse Review'; $lang->group->package->manageReview = 'Manage Review'; -$lang->group->package->manageIssue = 'Manage Issue'; -$lang->group->package->projectSetting = 'Project Setting'; +$lang->group->package->deleteReview = 'Delete Review'; +$lang->group->package->manageReviewIssue = 'Manage ReviewIssue'; +$lang->group->package->browseBaseline = 'Browse Baseline'; +$lang->group->package->manageBaseline = 'Manage Baseline'; +$lang->group->package->deleteBaseline = 'Delete Baseline'; +$lang->group->package->browseProjectchange = 'Browse Projectchange'; +$lang->group->package->manageProjectchange = 'Manage Projectchange'; +$lang->group->package->deleteProjectchange = 'Delete Projectchange'; $lang->group->package->browseProjectRelease = 'Browse Project Release'; $lang->group->package->browseProjectReport = 'Browse Report'; $lang->group->package->exportProjectReport = 'Export Report'; @@ -447,6 +454,9 @@ $lang->group->package->workflowDatasource = 'Workflow Datasource'; $lang->group->package->workflowRule = 'Workflow Rule'; $lang->group->package->workflowGroup = 'Workflow Group'; $lang->group->package->workflow = 'Workflow'; +$lang->group->package->productFlow = 'Product Flow'; +$lang->group->package->projectFlow = 'Project Flow'; +$lang->group->package->flowReport = 'Flow Report'; $lang->group->package->rule = 'Rule'; $lang->group->package->downloadCode = 'Download Code'; $lang->group->package->dev = 'Dev'; @@ -491,13 +501,17 @@ $lang->group->package->projectBuild = 'Project Build'; $lang->group->package->importCaseLib = 'Import Case Lib'; $lang->group->package->commonSetting = 'Common Setting'; $lang->group->package->stageSetting = 'Stage Setting'; -$lang->group->package->deliverable = 'Deliverable Setting'; +$lang->group->package->stageType = 'Stage Setting'; +$lang->group->package->reviewFlow = 'Review'; +$lang->group->package->reviewCheck = 'Review Checklist'; +$lang->group->package->reviewCategory = 'Review Category'; +$lang->group->package->setWorkflow = 'Flow'; +$lang->group->package->deliverable = 'Deliverable Category'; $lang->group->package->classify = 'Classify'; -$lang->group->package->cmcl = 'Cmcl'; $lang->group->package->auditcl = 'Auditcl'; $lang->group->package->reviewcl = 'Reviewcl'; -$lang->group->package->process = 'Process'; -$lang->group->package->activity = 'Activity'; +$lang->group->package->process = 'Process Setting'; +$lang->group->package->activity = 'Activity Setting'; $lang->group->package->zoutput = 'Zoutput'; $lang->group->package->custom = 'Custom'; $lang->group->package->approvalflow = 'Approval flow'; @@ -536,6 +550,7 @@ $lang->group->package->browseExecutionStory = 'Browse Execution ' . $lang->SRCo $lang->group->package->manageCard = 'Manage Card'; $lang->group->package->browseProjectStory = 'Browse Project ' . $lang->SRCommon; $lang->group->package->chckAuditPlan = 'Chck Audit Plan'; +$lang->group->package->deliverableChecklist = 'Deliverable Check'; $lang->group->package->reviewAssess = 'Review Assess'; $lang->group->package->reviewAudit = 'Review Audit'; $lang->group->package->dimension = 'Dimension'; @@ -636,10 +651,14 @@ $lang->group->package->component = 'Component'; $lang->group->package->browseRule = 'Browse Rule'; $lang->group->package->manageRule = 'Manage Rule'; $lang->group->package->executionDeliverable = 'Execution Deliverable'; -$lang->group->package->projectDeliverable = 'Project Deliverable'; +$lang->group->package->browseDeliverable = 'Browse Deliverable'; +$lang->group->package->manageDeliverable = 'Manage Deliverable'; +$lang->group->package->deleteDeliverable = 'Delete Deliverable'; $lang->group->package->projectTemplate = 'Project Template'; $lang->group->package->browseReportTemplate = 'Browse Report Template'; $lang->group->package->manageReportTemplate = 'Manage Report Template'; $lang->group->package->deleteReportTemplate = 'Delete Report Template'; +$lang->group->package->productSetting = 'Product Setting'; +$lang->group->package->executionSetting = 'Execution Setting'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/lang/fr.php b/module/group/lang/fr.php index 8f21c74d24..b4076c1e5e 100644 --- a/module/group/lang/fr.php +++ b/module/group/lang/fr.php @@ -400,9 +400,16 @@ $lang->group->package->programPlan = 'Program Plan'; $lang->group->package->browseDesign = 'Browse Design'; $lang->group->package->manageDesign = 'Manage Design'; $lang->group->package->deleteDesign = 'Delete Design'; +$lang->group->package->browseReview = 'Browse Review'; $lang->group->package->manageReview = 'Manage Review'; -$lang->group->package->manageIssue = 'Manage Issue'; -$lang->group->package->projectSetting = 'Project Setting'; +$lang->group->package->deleteReview = 'Delete Review'; +$lang->group->package->manageReviewIssue = 'Manage ReviewIssue'; +$lang->group->package->browseBaseline = 'Browse Baseline'; +$lang->group->package->manageBaseline = 'Manage Baseline'; +$lang->group->package->deleteBaseline = 'Delete Baseline'; +$lang->group->package->browseProjectchange = 'Browse Projectchange'; +$lang->group->package->manageProjectchange = 'Manage Projectchange'; +$lang->group->package->deleteProjectchange = 'Delete Projectchange'; $lang->group->package->browseProjectRelease = 'Browse Project Release'; $lang->group->package->browseProjectReport = 'Browse Report'; $lang->group->package->exportProjectReport = 'Export Report'; @@ -447,6 +454,9 @@ $lang->group->package->workflowDatasource = 'Workflow Datasource'; $lang->group->package->workflowRule = 'Workflow Rule'; $lang->group->package->workflowGroup = 'Workflow Group'; $lang->group->package->workflow = 'Workflow'; +$lang->group->package->productFlow = 'Product Flow'; +$lang->group->package->projectFlow = 'Project Flow'; +$lang->group->package->flowReport = 'Flow Report'; $lang->group->package->rule = 'Rule'; $lang->group->package->downloadCode = 'Download Code'; $lang->group->package->dev = 'Dev'; @@ -491,13 +501,17 @@ $lang->group->package->projectBuild = 'Project Build'; $lang->group->package->importCaseLib = 'Import Case Lib'; $lang->group->package->commonSetting = 'Common Setting'; $lang->group->package->stageSetting = 'Stage Setting'; -$lang->group->package->deliverable = 'Deliverable Setting'; +$lang->group->package->stageType = 'Stage Setting'; +$lang->group->package->reviewFlow = 'Review'; +$lang->group->package->reviewCheck = 'Review Checklist'; +$lang->group->package->reviewCategory = 'Review Category'; +$lang->group->package->setWorkflow = 'Flow'; +$lang->group->package->deliverable = 'Deliverable Category'; $lang->group->package->classify = 'Classify'; -$lang->group->package->cmcl = 'Cmcl'; $lang->group->package->auditcl = 'Auditcl'; $lang->group->package->reviewcl = 'Reviewcl'; -$lang->group->package->process = 'Process'; -$lang->group->package->activity = 'Activity'; +$lang->group->package->process = 'Process Setting'; +$lang->group->package->activity = 'Activity Setting'; $lang->group->package->zoutput = 'Zoutput'; $lang->group->package->custom = 'Custom'; $lang->group->package->approvalflow = 'Approval flow'; @@ -536,6 +550,7 @@ $lang->group->package->browseExecutionStory = 'Browse Execution ' . $lang->SRCo $lang->group->package->manageCard = 'Manage Card'; $lang->group->package->browseProjectStory = 'Browse Project ' . $lang->SRCommon; $lang->group->package->chckAuditPlan = 'Chck Audit Plan'; +$lang->group->package->deliverableChecklist = 'Deliverable Check'; $lang->group->package->reviewAssess = 'Review Assess'; $lang->group->package->reviewAudit = 'Review Audit'; $lang->group->package->dimension = 'Dimension'; @@ -636,10 +651,14 @@ $lang->group->package->component = 'Component'; $lang->group->package->browseRule = 'Browse Rule'; $lang->group->package->manageRule = 'Manage Rule'; $lang->group->package->executionDeliverable = 'Execution Deliverable'; -$lang->group->package->projectDeliverable = 'Project Deliverable'; +$lang->group->package->browseDeliverable = 'Browse Deliverable'; +$lang->group->package->manageDeliverable = 'Manage Deliverable'; +$lang->group->package->deleteDeliverable = 'Delete Deliverable'; $lang->group->package->projectTemplate = 'Project Template'; $lang->group->package->browseReportTemplate = 'Browse Report Template'; $lang->group->package->manageReportTemplate = 'Manage Report Template'; $lang->group->package->deleteReportTemplate = 'Delete Report Template'; +$lang->group->package->productSetting = 'Product Setting'; +$lang->group->package->executionSetting = 'Execution Setting'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index eb7217883c..0f711d5ed9 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -393,7 +393,6 @@ $lang->resource->stage->batchCreate = 'batchCreate'; $lang->resource->stage->edit = 'edit'; $lang->resource->stage->setType = 'setType'; $lang->resource->stage->delete = 'delete'; -$lang->resource->stage->plusBrowse = 'plusBrowse'; $lang->stage->methodOrder[5] = 'browse'; $lang->stage->methodOrder[10] = 'create'; @@ -401,7 +400,6 @@ $lang->stage->methodOrder[15] = 'batchCreate'; $lang->stage->methodOrder[20] = 'edit'; $lang->stage->methodOrder[25] = 'setType'; $lang->stage->methodOrder[30] = 'delete'; -$lang->stage->methodOrder[35] = 'plusBrowse'; /* Program. */ $lang->resource->program = new stdclass(); @@ -1608,7 +1606,7 @@ $lang->resource->custom = new stdclass(); $lang->resource->custom->set = 'set'; $lang->resource->custom->product = 'productName'; $lang->resource->custom->project = 'projectCommon'; -$lang->resource->custom->execution = 'executionCommon'; +$lang->resource->custom->execution = 'setExecutionClose'; $lang->resource->custom->required = 'required'; $lang->resource->custom->restore = 'restore'; $lang->resource->custom->flow = 'flow'; @@ -1810,6 +1808,7 @@ $lang->user->methodOrder[85] = 'setPublicTemplate'; $lang->resource->tree = new stdclass(); $lang->resource->tree->browse = 'browse'; $lang->resource->tree->browseTask = 'browseTask'; +$lang->resource->tree->create = 'create'; $lang->resource->tree->updateOrder = 'updateOrder'; $lang->resource->tree->manageChild = 'manageChild'; $lang->resource->tree->edit = 'edit'; @@ -1818,10 +1817,11 @@ $lang->resource->tree->delete = 'delete'; $lang->tree->methodOrder[5] = 'browse'; $lang->tree->methodOrder[10] = 'browseTask'; -$lang->tree->methodOrder[20] = 'updateOrder'; -$lang->tree->methodOrder[25] = 'manageChild'; -$lang->tree->methodOrder[30] = 'edit'; -$lang->tree->methodOrder[35] = 'delete'; +$lang->tree->methodOrder[12] = 'create'; +$lang->tree->methodOrder[15] = 'updateOrder'; +$lang->tree->methodOrder[20] = 'manageChild'; +$lang->tree->methodOrder[25] = 'edit'; +$lang->tree->methodOrder[30] = 'delete'; /* Screen. */ $lang->resource->screen = new stdclass(); diff --git a/module/group/lang/zh-cn.php b/module/group/lang/zh-cn.php index 59ab8a1ca0..953ec65014 100644 --- a/module/group/lang/zh-cn.php +++ b/module/group/lang/zh-cn.php @@ -400,9 +400,16 @@ $lang->group->package->programPlan = '项目阶段'; $lang->group->package->browseDesign = '浏览设计'; $lang->group->package->manageDesign = '创建维护设计'; $lang->group->package->deleteDesign = '删除设计'; -$lang->group->package->manageReview = '维护基线评审'; -$lang->group->package->manageIssue = '维护问题'; -$lang->group->package->projectSetting = '项目配置'; +$lang->group->package->browseReview = '浏览评审'; +$lang->group->package->manageReview = '创建维护评审'; +$lang->group->package->deleteReview = '删除评审'; +$lang->group->package->manageReviewIssue = '评审问题'; +$lang->group->package->browseBaseline = '浏览项目基线'; +$lang->group->package->manageBaseline = '创建维护项目基线'; +$lang->group->package->deleteBaseline = '删除项目基线'; +$lang->group->package->browseProjectchange = '浏览项目变更'; +$lang->group->package->manageProjectchange = '创建维护项目变更'; +$lang->group->package->deleteProjectchange = '删除项目变更'; $lang->group->package->browseProjectRelease = '浏览项目发布'; $lang->group->package->browseProjectReport = '浏览报告'; $lang->group->package->exportProjectReport = '导出报告'; @@ -430,7 +437,7 @@ $lang->group->package->deleteOpportunity = '删除机会'; $lang->group->package->importOpportunityLib = '导入机会库'; $lang->group->package->importOpportunity = '导入导出机会'; $lang->group->package->pssp = '项目过程'; -$lang->group->package->manageAuditPlan = '维护质量保证计划'; +$lang->group->package->manageAuditPlan = '维护活动检查'; $lang->group->package->nc = '不符合项'; $lang->group->package->meeting = '会议'; $lang->group->package->trainPlan = '培训计划'; @@ -447,6 +454,9 @@ $lang->group->package->workflowDatasource = '工作流数据源'; $lang->group->package->workflowRule = '工作流验证规则'; $lang->group->package->workflowGroup = '工作流流程管理'; $lang->group->package->workflow = '工作流'; +$lang->group->package->productFlow = '产品流程'; +$lang->group->package->projectFlow = '项目流程'; +$lang->group->package->flowReport = '流程概览'; $lang->group->package->rule = '规则引擎'; $lang->group->package->downloadCode = '下载代码'; $lang->group->package->dev = '二次开发'; @@ -490,14 +500,18 @@ $lang->group->package->projectStakeholder = '项目干系人'; $lang->group->package->projectBuild = '项目构建'; $lang->group->package->importCaseLib = '导入用例库'; $lang->group->package->commonSetting = '通用配置'; -$lang->group->package->stageSetting = '阶段列表设置'; -$lang->group->package->deliverable = '交付物设置'; +$lang->group->package->stageSetting = '阶段配置'; +$lang->group->package->stageType = '阶段设置'; +$lang->group->package->reviewFlow = '评审流程'; +$lang->group->package->reviewCheck = '评审流程检查清单'; +$lang->group->package->reviewCategory = '评审流程检查单分类'; +$lang->group->package->setWorkflow = '工作流'; +$lang->group->package->deliverable = '交付物类型'; $lang->group->package->classify = '分类项设置'; -$lang->group->package->cmcl = '审计设置'; -$lang->group->package->auditcl = 'QA检查项设置'; +$lang->group->package->auditcl = '质量保证'; $lang->group->package->reviewcl = '评审设置'; -$lang->group->package->process = '过程设置'; -$lang->group->package->activity = '活动设置'; +$lang->group->package->process = '过程配置'; +$lang->group->package->activity = '活动配置'; $lang->group->package->zoutput = '文档设置'; $lang->group->package->custom = '自定义配置'; $lang->group->package->approvalflow = '审批设置'; @@ -523,7 +537,7 @@ $lang->group->package->automation = '自动化'; $lang->group->package->git = 'Git'; $lang->group->package->subversion = 'Subversion'; $lang->group->package->ping = '防超时'; -$lang->group->package->review = '审批'; +$lang->group->package->review = '评审设置'; $lang->group->package->manageProjectRelease = '创建维护项目发布'; $lang->group->package->deleteProjectRelease = '删除项目发布'; $lang->group->package->importProjectRelease = '导入导出项目发布'; @@ -535,9 +549,9 @@ $lang->group->package->browseBuild = '浏览构建'; $lang->group->package->browseExecutionStory = '浏览执行' . $lang->SRCommon; $lang->group->package->manageCard = '维护卡片'; $lang->group->package->browseProjectStory = '浏览项目' . $lang->SRCommon; -$lang->group->package->chckAuditPlan = '检查质量保证计划'; -$lang->group->package->reviewAssess = '评审基线评审'; -$lang->group->package->reviewAudit = '审计基线评审'; +$lang->group->package->chckAuditPlan = '检查活动'; +$lang->group->package->deliverableChecklist = '交付物检查'; +$lang->group->package->reviewAssess = '评审'; $lang->group->package->dimension = '管理维度'; $lang->group->package->browseScreen = '浏览大屏'; $lang->group->package->manageScreen = '创建维护大屏'; @@ -636,10 +650,14 @@ $lang->group->package->component = '组件'; $lang->group->package->browseRule = '浏览规则'; $lang->group->package->manageRule = '创建维护规则'; $lang->group->package->executionDeliverable = '交付物'; -$lang->group->package->projectDeliverable = '交付物'; +$lang->group->package->browseDeliverable = '浏览交付物'; +$lang->group->package->manageDeliverable = '创建维护交付物'; +$lang->group->package->deleteDeliverable = '删除交付物'; $lang->group->package->projectTemplate = '项目模板'; $lang->group->package->browseReportTemplate = '浏览报告模板'; $lang->group->package->manageReportTemplate = '创建与维护报告模板'; $lang->group->package->deleteReportTemplate = '删除报告模板'; +$lang->group->package->productSetting = '产品配置'; +$lang->group->package->executionSetting = '执行配置'; include (dirname(__FILE__) . '/resource.php'); diff --git a/module/group/model.php b/module/group/model.php index d8d966c548..8c650e626f 100644 --- a/module/group/model.php +++ b/module/group/model.php @@ -906,6 +906,7 @@ class groupModel extends model { if(!isset($packageData->privs)) continue; + $sortPriv = array(); foreach($packageData->privs as $privCode => $priv) { list($moduleName, $methodName) = explode('-', $privCode); @@ -940,10 +941,22 @@ class groupModel extends model $privName = isset($this->lang->$moduleName) && isset($this->lang->$moduleName->$methodLang) ? $this->lang->$moduleName->$methodLang : $privCode; } - $priv = (object)array('subset' => $packageData->subset, 'package' => $packageCode, 'module' => $moduleName, 'method' => $methodName, 'selected' => false, 'name' => $privName); + $privData = (object)array('code' => $privCode, 'subset' => $packageData->subset, 'package' => $packageCode, 'module' => $moduleName, 'method' => $methodName, 'selected' => false, 'name' => $privName); - $privList[$privCode] = $priv; + /* 将order作为键方便排序。 */ + if(!empty($priv['order']) && !isset($sortPriv[$priv['order']])) + { + $sortPriv[$priv['order']] = $privData; + } + else + { + $sortPriv[] = $privData; + } } + + /* 以order排序后再放入privList数组。 */ + ksort($sortPriv); + foreach($sortPriv as $priv) $privList[$priv->code] = $priv; } return $privList; diff --git a/module/group/packagemanager.php b/module/group/packagemanager.php index fecc5aa209..886d641c76 100644 --- a/module/group/packagemanager.php +++ b/module/group/packagemanager.php @@ -65,10 +65,6 @@ $config->group->subset->project = new stdclass(); $config->group->subset->project->order = 170; $config->group->subset->project->nav = 'project'; -$config->group->subset->projectDeliverable = new stdclass(); -$config->group->subset->projectDeliverable->order = 171; -$config->group->subset->projectDeliverable->nav = 'project'; - $config->group->subset->projectTemplate = new stdclass(); $config->group->subset->projectTemplate->order = 172; $config->group->subset->projectTemplate->nav = 'project'; @@ -101,9 +97,19 @@ $config->group->subset->projectreview = new stdclass(); $config->group->subset->projectreview->order = 240; $config->group->subset->projectreview->nav = 'project'; -$config->group->subset->cm = new stdclass(); -$config->group->subset->cm->order = 250; -$config->group->subset->cm->nav = 'project'; +if(helper::hasFeature('project_cm')) +{ + $config->group->subset->cm = new stdclass(); + $config->group->subset->cm->order = 250; + $config->group->subset->cm->nav = 'project'; +} + +if(helper::hasFeature('project_change')) +{ + $config->group->subset->projectchange = new stdclass(); + $config->group->subset->projectchange->order = 255; + $config->group->subset->projectchange->nav = 'project'; +} $config->group->subset->projectqa = new stdclass(); $config->group->subset->projectqa->order = 260; @@ -349,10 +355,6 @@ $config->group->subset->staffmanage = new stdclass(); $config->group->subset->staffmanage->order = 1190; $config->group->subset->staffmanage->nav = 'admin'; -$config->group->subset->modelconfig = new stdclass(); -$config->group->subset->modelconfig->order = 1200; -$config->group->subset->modelconfig->nav = 'admin'; - $config->group->subset->featureconfig = new stdclass(); $config->group->subset->featureconfig->order = 1210; $config->group->subset->featureconfig->nav = 'admin'; @@ -406,9 +408,21 @@ $config->group->subset->tree->order = 1580; $config->group->subset->file = new stdclass(); $config->group->subset->file->order = 1590; +$config->group->subset->productFlow = new stdclass(); +$config->group->subset->productFlow->order = 1605; +$config->group->subset->productFlow->nav = 'admin'; + +$config->group->subset->projectFlow = new stdclass(); +$config->group->subset->projectFlow->order = 1610; +$config->group->subset->projectFlow->nav = 'admin'; + $config->group->subset->workflow = new stdclass(); $config->group->subset->workflow->order = 1620; -$config->group->subset->workflow->nav = 'workflow'; +$config->group->subset->workflow->nav = 'admin'; + +$config->group->subset->approvalflow = new stdclass(); +$config->group->subset->approvalflow->order = 1650; +$config->group->subset->approvalflow->nav = 'admin'; $config->group->subset->datatable = new stdclass(); $config->group->subset->datatable->order = 1740; @@ -1064,11 +1078,24 @@ $config->group->package->projectWhitelist->privs['project-whitelist'] = ar $config->group->package->projectWhitelist->privs['project-addWhitelist'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 110, 'depend' => array('project-whitelist'), 'recommend' => array('project-unbindWhitelist')); $config->group->package->projectWhitelist->privs['project-unbindWhitelist'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 115, 'depend' => array('project-whitelist'), 'recommend' => array('project-addWhitelist')); -$config->group->package->projectDeliverable = new stdclass(); -$config->group->package->projectDeliverable->order = 5; -$config->group->package->projectDeliverable->subset = 'projectDeliverable'; -$config->group->package->projectDeliverable->privs = array(); -$config->group->package->projectDeliverable->privs['project-deliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array(), 'recommend' => array()); +$config->group->package->browseDeliverable = new stdclass(); +$config->group->package->browseDeliverable->order = 5; +$config->group->package->browseDeliverable->subset = 'projectDeliverable'; +$config->group->package->browseDeliverable->privs = array(); +$config->group->package->browseDeliverable->privs['project-deliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); +$config->group->package->browseDeliverable->privs['project-viewDeliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array(), 'recommend' => array()); + +$config->group->package->manageDeliverable = new stdclass(); +$config->group->package->manageDeliverable->order = 10; +$config->group->package->manageDeliverable->subset = 'projectDeliverable'; +$config->group->package->manageDeliverable->privs = array(); +$config->group->package->manageDeliverable->privs['project-createDeliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); + +$config->group->package->deleteDeliverable = new stdclass(); +$config->group->package->deleteDeliverable->order = 10; +$config->group->package->deleteDeliverable->subset = 'projectDeliverable'; +$config->group->package->deleteDeliverable->privs = array(); +$config->group->package->deleteDeliverable->privs['project-deleteDeliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); $config->group->package->projectTemplate = new stdclass(); $config->group->package->projectTemplate->order = 5; @@ -1618,7 +1645,7 @@ $config->group->package->lieu->privs['lieu-switchstatus'] = array('edition' => ' $config->group->package->holiday = new stdclass(); $config->group->package->holiday->order = 10; -$config->group->package->holiday->subset = 'modelconfig'; +$config->group->package->holiday->subset = 'featureconfig'; $config->group->package->holiday->privs = array(); $config->group->package->holiday->privs['holiday-browse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('admin-index'), 'recommend' => array()); $config->group->package->holiday->privs['holiday-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('holiday-browse'), 'recommend' => array('holiday-delete', 'holiday-edit')); @@ -1891,10 +1918,10 @@ $config->group->package->manageCase = new stdclass(); $config->group->package->manageCase->order = 15; $config->group->package->manageCase->subset = 'testcase'; $config->group->package->manageCase->privs = array(); -$config->group->package->manageCase->privs['testcase-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases', 'testcase-submit')); +$config->group->package->manageCase->privs['testcase-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases')); $config->group->package->manageCase->privs['testcase-batchCreate'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array('testcase-create')); -$config->group->package->manageCase->privs['testcase-createBug'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases', 'testcase-submit')); -$config->group->package->manageCase->privs['testcase-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases', 'testcase-submit')); +$config->group->package->manageCase->privs['testcase-createBug'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases')); +$config->group->package->manageCase->privs['testcase-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases')); $config->group->package->manageCase->privs['testcase-confirmChange'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases')); $config->group->package->manageCase->privs['testcase-confirmStoryChange'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-create', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases')); $config->group->package->manageCase->privs['testcase-batchEdit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array()); @@ -1906,7 +1933,6 @@ $config->group->package->manageCase->privs['testcase-batchConfirmStoryChange'] = $config->group->package->manageCase->privs['testcase-batchChangeType'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 11, 'depend' => array(), 'recommend' => array('testcase-edit')); $config->group->package->manageCase->privs['testcase-confirmLibcaseChange'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 14, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-edit', 'testcase-ignoreLibcaseChange')); $config->group->package->manageCase->privs['testcase-ignoreLibcaseChange'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-edit')); -$config->group->package->manageCase->privs['testcase-submit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('testcase-view'), 'recommend' => array('testcase-confirmChange', 'testcase-confirmLibcaseChange', 'testcase-confirmStoryChange', 'testcase-create', 'testcase-edit', 'testcase-ignoreLibcaseChange', 'testcase-linkBugs', 'testcase-linkCases')); $config->group->package->manageCase->privs['testcase-updateOrder'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array(), 'recommend' => array()); $config->group->package->manageCase->privs['testcase-confirmdemandretract'] = array('edition' => 'ipd', 'vision' => 'rnd,or', 'order' => 30, 'depend' => array(), 'recommend' => array()); $config->group->package->manageCase->privs['testcase-confirmdemandunlink'] = array('edition' => 'ipd', 'vision' => 'rnd,or', 'order' => 30, 'depend' => array(), 'recommend' => array()); @@ -2172,61 +2198,112 @@ $config->group->package->manageDesign = new stdclass(); $config->group->package->manageDesign->order = 10; $config->group->package->manageDesign->subset = 'design'; $config->group->package->manageDesign->privs = array(); -$config->group->package->manageDesign->privs['design-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-batchCreate', 'design-edit', 'design-linkCommit', 'design-submit', 'design-unlinkCommit')); +$config->group->package->manageDesign->privs['design-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-batchCreate', 'design-edit', 'design-linkCommit', 'design-unlinkCommit')); $config->group->package->manageDesign->privs['design-batchCreate'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('design-browse'), 'recommend' => array('design-create')); -$config->group->package->manageDesign->privs['design-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-linkCommit', 'design-revision', 'design-submit', 'design-unlinkCommit')); -$config->group->package->manageDesign->privs['design-assignTo'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('design-browse'), 'recommend' => array('design-create', 'design-edit', 'design-linkCommit', 'design-revision', 'design-submit', 'design-unlinkCommit')); -$config->group->package->manageDesign->privs['design-linkCommit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-edit', 'design-revision', 'design-submit', 'design-unlinkCommit', 'design-viewCommit')); +$config->group->package->manageDesign->privs['design-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-linkCommit', 'design-revision', 'design-unlinkCommit')); +$config->group->package->manageDesign->privs['design-assignTo'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('design-browse'), 'recommend' => array('design-create', 'design-edit', 'design-linkCommit', 'design-revision', 'design-unlinkCommit')); +$config->group->package->manageDesign->privs['design-linkCommit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-edit', 'design-revision', 'design-unlinkCommit', 'design-viewCommit')); $config->group->package->manageDesign->privs['design-viewCommit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('design-browse'), 'recommend' => array('design-linkCommit', 'design-revision')); -$config->group->package->manageDesign->privs['design-unlinkCommit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-edit', 'design-linkCommit', 'design-revision', 'design-submit', 'design-viewCommit')); +$config->group->package->manageDesign->privs['design-unlinkCommit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-edit', 'design-linkCommit', 'design-revision', 'design-viewCommit')); $config->group->package->manageDesign->privs['design-revision'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 45, 'depend' => array('design-viewCommit'), 'recommend' => array('design-linkCommit', 'design-unlinkCommit', 'design-viewCommit')); -$config->group->package->manageDesign->privs['design-submit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('design-browse'), 'recommend' => array()); -$config->group->package->manageDesign->privs['design-confirmStoryChange'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-linkCommit', 'design-revision', 'design-submit', 'design-unlinkCommit')); +$config->group->package->manageDesign->privs['design-confirmStoryChange'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-linkCommit', 'design-revision', 'design-unlinkCommit')); $config->group->package->deleteDesign = new stdclass(); $config->group->package->deleteDesign->order = 15; $config->group->package->deleteDesign->subset = 'design'; $config->group->package->deleteDesign->privs = array(); -$config->group->package->deleteDesign->privs['design-delete'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-edit', 'design-linkCommit', 'design-submit', 'design-unlinkCommit')); +$config->group->package->deleteDesign->privs['design-delete'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('design-browse'), 'recommend' => array('design-assignTo', 'design-create', 'design-edit', 'design-linkCommit', 'design-unlinkCommit')); + +$config->group->package->browseReview = new stdclass(); +$config->group->package->browseReview->order = 5; +$config->group->package->browseReview->subset = 'projectreview'; +$config->group->package->browseReview->privs = array(); +$config->group->package->browseReview->privs['review-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array('review-view')); +$config->group->package->browseReview->privs['review-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('review-browse'), 'recommend' => array('review-report')); $config->group->package->manageReview = new stdclass(); $config->group->package->manageReview->order = 10; $config->group->package->manageReview->subset = 'projectreview'; $config->group->package->manageReview->privs = array(); -$config->group->package->manageReview->privs['review-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('project-index'), 'recommend' => array('review-view')); -$config->group->package->manageReview->privs['review-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('review-browse'), 'recommend' => array('review-edit', 'review-recall', 'review-report', 'review-submit', 'review-toAudit')); -$config->group->package->manageReview->privs['review-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-delete', 'review-recall', 'review-report', 'review-submit', 'review-toAudit')); -$config->group->package->manageReview->privs['review-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('review-browse'), 'recommend' => array()); -$config->group->package->manageReview->privs['review-submit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-edit', 'review-recall', 'review-report', 'review-toAudit')); -$config->group->package->manageReview->privs['review-recall'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-delete', 'review-edit', 'review-report', 'review-submit', 'review-toAudit')); -$config->group->package->manageReview->privs['review-report'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-delete', 'review-edit', 'review-recall', 'review-submit', 'review-report', 'review-toAudit')); -$config->group->package->manageReview->privs['review-exportReport'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('review-browse,review-report'), 'recommend' => array('review-create', 'review-delete', 'review-edit', 'review-recall', 'review-submit', 'review-toAudit')); -$config->group->package->manageReview->privs['review-toAudit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-delete', 'review-edit', 'review-recall', 'review-report', 'review-submit')); -$config->group->package->manageReview->privs['review-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-edit')); +$config->group->package->manageReview->privs['review-submitDeliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('review-browse', 'review-create'), 'recommend' => array('review-edit', 'review-recall')); +$config->group->package->manageReview->privs['review-submitIpd'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('review-browse', 'review-create'), 'recommend' => array('review-edit', 'review-recall')); +$config->group->package->manageReview->privs['review-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('review-browse'), 'recommend' => array('review-edit', 'review-recall')); +$config->group->package->manageReview->privs['review-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-recall')); +$config->group->package->manageReview->privs['review-recall'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-edit')); +$config->group->package->manageReview->privs['review-assess'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 40, 'depend' => array('review-browse'), 'recommend' => array('review-report')); +$config->group->package->manageReview->privs['review-report'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 45, 'depend' => array('review-browse'), 'recommend' => array('review-exportReport')); +$config->group->package->manageReview->privs['review-exportReport'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 50, 'depend' => array('review-browse'), 'recommend' => array('review-create', 'review-delete', 'review-edit', 'review-recall', 'review-toAudit')); -$config->group->package->manageIssue = new stdclass(); -$config->group->package->manageIssue->order = 5; -$config->group->package->manageIssue->subset = 'projectreview'; -$config->group->package->manageIssue->privs = array(); -$config->group->package->manageIssue->privs['reviewissue-issue'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('project-index', 'review-browse'), 'recommend' => array()); -$config->group->package->manageIssue->privs['reviewissue-updateStatus'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-create', 'reviewissue-delete', 'reviewissue-edit', 'reviewissue-resolved')); -$config->group->package->manageIssue->privs['reviewissue-resolved'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-create', 'reviewissue-delete', 'reviewissue-edit', 'reviewissue-updateStatus')); -$config->group->package->manageIssue->privs['reviewissue-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-delete', 'reviewissue-edit', 'reviewissue-resolved', 'reviewissue-updateStatus')); -$config->group->package->manageIssue->privs['reviewissue-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-create', 'reviewissue-delete', 'reviewissue-resolved', 'reviewissue-updateStatus')); -$config->group->package->manageIssue->privs['reviewissue-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('reviewissue-issue'), 'recommend' => array()); -$config->group->package->manageIssue->privs['reviewissue-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-create', 'reviewissue-edit', 'reviewissue-resolved', 'reviewissue-updateStatus')); +$config->group->package->manageReview->privs['review-toAudit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 55, 'depend' => array('review-browse'), 'recommend' => array('review-report')); +if(helper::hasFeature('project_cm')) $config->group->package->manageReview->privs['review-submitBaseline'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('review-browse', 'review-create'), 'recommend' => array('review-edit', 'review-recall')); +if(helper::hasFeature('project_change')) $config->group->package->manageReview->privs['review-submitProjectchange'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('review-browse', 'review-create'), 'recommend' => array('review-edit', 'review-recall')); -$config->group->package->projectSetting = new stdclass(); -$config->group->package->projectSetting->order = 5; -$config->group->package->projectSetting->subset = 'cm'; -$config->group->package->projectSetting->privs = array(); -$config->group->package->projectSetting->privs['cm-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('cm-browse', 'cm-view'), 'recommend' => array('cm-delete', 'cm-edit', 'review-browse')); -$config->group->package->projectSetting->privs['cm-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('cm-browse', 'cm-view'), 'recommend' => array('cm-create', 'cm-edit')); -$config->group->package->projectSetting->privs['cm-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('cm-browse', 'cm-view'), 'recommend' => array('cm-create', 'cm-delete')); -$config->group->package->projectSetting->privs['cm-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('project-index'), 'recommend' => array('cm-view')); -$config->group->package->projectSetting->privs['cm-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('cm-browse'), 'recommend' => array()); -$config->group->package->projectSetting->privs['cm-report'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('cm-browse'), 'recommend' => array()); -$config->group->package->projectSetting->privs['cm-exportReport'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('cm-browse', 'cm-report'), 'recommend' => array()); +$config->group->package->deleteReview = new stdclass(); +$config->group->package->deleteReview->order = 15; +$config->group->package->deleteReview->subset = 'projectreview'; +$config->group->package->deleteReview->privs = array(); +$config->group->package->deleteReview->privs['review-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('review-browse'), 'recommend' => array()); + +$config->group->package->manageReviewIssue = new stdclass(); +$config->group->package->manageReviewIssue->order = 20; +$config->group->package->manageReviewIssue->subset = 'projectreview'; +$config->group->package->manageReviewIssue->privs = array(); +$config->group->package->manageReviewIssue->privs['reviewissue-issue'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array('reviewissue-view')); +$config->group->package->manageReviewIssue->privs['reviewissue-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('reviewissue-issue'), 'recommend' => array()); +$config->group->package->manageReviewIssue->privs['reviewissue-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-edit', 'reviewissue-assignTo')); +$config->group->package->manageReviewIssue->privs['reviewissue-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-create')); +$config->group->package->manageReviewIssue->privs['reviewissue-assignTo'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('reviewissue-issue'), 'recommend' => array('reviewissue-close', 'reviewissue-resolved')); +$config->group->package->manageReviewIssue->privs['reviewissue-active'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('reviewissue-issue'), 'recommend' => array('review-close', 'reviewissue-resolved', 'review-assignTo')); +$config->group->package->manageReviewIssue->privs['reviewissue-resolved'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('reviewissue-issue'), 'recommend' => array('review-close', 'review-activate', 'review-assignTo')); +$config->group->package->manageReviewIssue->privs['reviewissue-close'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('reviewissue-issue'), 'recommend' => array('review-activate', 'reviewissue-resolved', 'review-assignTo')); +$config->group->package->manageReviewIssue->privs['reviewissue-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('reviewissue-issue'), 'recommend' => array()); + +$config->group->package->browseBaseline = new stdclass(); +$config->group->package->browseBaseline->order = 5; +$config->group->package->browseBaseline->subset = 'cm'; +$config->group->package->browseBaseline->privs = array(); +$config->group->package->browseBaseline->privs['cm-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array('cm-view')); +$config->group->package->browseBaseline->privs['cm-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('cm-browse'), 'recommend' => array()); + +$config->group->package->manageBaseline = new stdclass(); +$config->group->package->manageBaseline->order = 10; +$config->group->package->manageBaseline->subset = 'cm'; +$config->group->package->manageBaseline->privs = array(); +$config->group->package->manageBaseline->privs['cm-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('cm-browse'), 'recommend' => array('cm-edit', 'cm-submit', 'cm-recall')); +$config->group->package->manageBaseline->privs['cm-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('cm-browse'), 'recommend' => array('cm-create', 'cm-submit', 'cm-recall')); +$config->group->package->manageBaseline->privs['cm-submit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('cm-browse'), 'recommend' => array('cm-create', 'cm-edit', 'cm-recall')); +$config->group->package->manageBaseline->privs['cm-recall'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('cm-browse'), 'recommend' => array('cm-create', 'cm-edit', 'cm-submit')); +$config->group->package->manageBaseline->privs['cm-diff'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('cm-browse'), 'recommend' => array()); +$config->group->package->manageBaseline->privs['cm-report'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 9, 'depend' => array('cm-browse'), 'recommend' => array('cm-exportReport')); +$config->group->package->manageBaseline->privs['cm-exportReport'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('cm-browse'), 'recommend' => array('cm-report')); + +$config->group->package->deleteBaseline = new stdclass(); +$config->group->package->deleteBaseline->order = 15; +$config->group->package->deleteBaseline->subset = 'cm'; +$config->group->package->deleteBaseline->privs = array(); +$config->group->package->deleteBaseline->privs['cm-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('cm-browse'), 'recommend' => array()); + +$config->group->package->browseProjectchange = new stdclass(); +$config->group->package->browseProjectchange->order = 5; +$config->group->package->browseProjectchange->subset = 'projectchange'; +$config->group->package->browseProjectchange->privs = array(); +$config->group->package->browseProjectchange->privs['projectchange-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array('projectchange-view')); +$config->group->package->browseProjectchange->privs['projectchange-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('projectchange-browse'), 'recommend' => array()); + +$config->group->package->manageProjectchange = new stdclass(); +$config->group->package->manageProjectchange->order = 10; +$config->group->package->manageProjectchange->subset = 'projectchange'; +$config->group->package->manageProjectchange->privs = array(); +$config->group->package->manageProjectchange->privs['projectchange-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('projectchange-browse'), 'recommend' => array('projectchange-edit')); +$config->group->package->manageProjectchange->privs['projectchange-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('projectchange-browse'), 'recommend' => array('projectchange-create')); +$config->group->package->manageProjectchange->privs['projectchange-submit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('projectchange-browse'), 'recommend' => array('projectchange-recall')); +$config->group->package->manageProjectchange->privs['projectchange-recall'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('projectchange-browse'), 'recommend' => array('projectchange-submit')); + +$config->group->package->deleteProjectchange = new stdclass(); +$config->group->package->deleteProjectchange->order = 15; +$config->group->package->deleteProjectchange->subset = 'projectchange'; +$config->group->package->deleteProjectchange->privs = array(); +$config->group->package->deleteProjectchange->privs['projectchange-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array('projectchange-browse'), 'recommend' => array()); $config->group->package->browseProjectRelease = new stdclass(); $config->group->package->browseProjectRelease->order = 5; @@ -2450,6 +2527,7 @@ $config->group->package->manageAuditPlan->order = 5; $config->group->package->manageAuditPlan->subset = 'projectauditplan'; $config->group->package->manageAuditPlan->privs = array(); $config->group->package->manageAuditPlan->privs['auditplan-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); +$config->group->package->manageAuditPlan->privs['auditplan-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('auditplan-browse'), 'recommend' => array()); $config->group->package->manageAuditPlan->privs['auditplan-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('auditplan-browse'), 'recommend' => array('auditplan-assignTo', 'auditplan-batchCreate', 'auditplan-check', 'auditplan-delete', 'auditplan-edit', 'auditplan-nc', 'auditplan-result')); $config->group->package->manageAuditPlan->privs['auditplan-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('auditplan-browse'), 'recommend' => array('auditplan-assignTo', 'auditplan-batchEdit', 'auditplan-check', 'auditplan-create', 'auditplan-delete', 'auditplan-nc', 'auditplan-result')); $config->group->package->manageAuditPlan->privs['auditplan-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('auditplan-browse'), 'recommend' => array('auditplan-create')); @@ -2512,6 +2590,126 @@ $config->group->package->gapAnalysis->privs['gapanalysis-batchEdit'] = array(' $config->group->package->gapAnalysis->privs['gapanalysis-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('gapanalysis-view'), 'recommend' => array('gapanalysis-create')); $config->group->package->gapAnalysis->privs['gapanalysis-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('gapanalysis-browse'), 'recommend' => array()); +$config->group->package->productFlow = new stdclass(); +$config->group->package->productFlow->order = 10; +$config->group->package->productFlow->subset = 'productFlow'; +$config->group->package->productFlow->privs['workflowgroup-product'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array(), 'recommend' => array()); + +$config->group->package->projectFlow = new stdclass(); +$config->group->package->projectFlow->order = 10; +$config->group->package->projectFlow->subset = 'projectFlow'; +$config->group->package->projectFlow->privs = array(); +$config->group->package->projectFlow->privs['workflowgroup-project'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array(), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-create'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-view'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-release'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-deactivate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-copy', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-copy'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 38, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-delete', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-delete'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 40, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-setModule')); +$config->group->package->projectFlow->privs['workflowgroup-setModule'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 45, 'depend' => array('workflowgroup-project'), 'recommend' => array('workflowgroup-create', 'workflowgroup-view', 'workflowgroup-edit', 'workflowgroup-release', 'workflowgroup-deactivate', 'workflowgroup-copy', 'workflowgroup-delete')); + +$config->group->package->flowReport = new stdclass(); +$config->group->package->flowReport->order = 15; +$config->group->package->flowReport->subset = 'projectFlow'; +$config->group->package->flowReport->privs = array(); +$config->group->package->flowReport->privs['workflowgroup-report'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-project'), 'recommend' => array()); + +$config->group->package->process = new stdclass(); +$config->group->package->process->order = 20; +$config->group->package->process->subset = 'projectFlow'; +$config->group->package->process->privs = array(); +$config->group->package->process->privs['process-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('workflowgroup-report'), 'recommend' => array('process-create', 'process-batchCreate', 'process-view', 'process-edit', 'process-batchEdit', 'process-delete', 'process-manageModule')); +$config->group->package->process->privs['process-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('process-browse'), 'recommend' => array('process-batchCreate', 'process-view', 'process-edit', 'process-batchEdit', 'process-delete', 'process-manageModule')); +$config->group->package->process->privs['process-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('process-browse'), 'recommend' => array('process-create', 'process-view', 'process-edit', 'process-batchEdit', 'process-delete', 'process-manageModule')); +$config->group->package->process->privs['process-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('process-browse'), 'recommend' => array('process-create', 'process-batchCreate', 'process-edit', 'process-batchEdit', 'process-delete', 'process-manageModule')); +$config->group->package->process->privs['process-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('process-browse'), 'recommend' => array('process-create', 'process-batchCreate', 'process-view', 'process-batchEdit', 'process-delete', 'process-manageModule')); +$config->group->package->process->privs['process-batchEdit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('process-browse'), 'recommend' => array('process-create', 'process-batchCreate', 'process-view', 'process-edit', 'process-delete', 'process-manageModule')); +$config->group->package->process->privs['process-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('process-browse'), 'recommend' => array('process-create', 'process-batchCreate', 'process-view', 'process-edit', 'process-batchEdit', 'process-manageModule')); +$config->group->package->process->privs['process-manageModule'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 40, 'depend' => array('process-browse'), 'recommend' => array('process-create', 'process-batchCreate', 'process-view', 'process-edit', 'process-batchEdit', 'process-delete')); + +$config->group->package->activity = new stdclass(); +$config->group->package->activity->order = 25; +$config->group->package->activity->subset = 'projectFlow'; +$config->group->package->activity->privs = array(); +$config->group->package->activity->privs['activity-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('activity-view', 'activity-create', 'activity-batchCreate', 'activity-edit', 'activity-batchEdit', 'activity-delete', 'activity-assignTo')); +$config->group->package->activity->privs['activity-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('activity-browse'), 'recommend' => array('activity-create', 'activity-batchCreate', 'activity-edit', 'activity-batchEdit', 'activity-delete', 'activity-assignTo')); +$config->group->package->activity->privs['activity-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('activity-browse'), 'recommend' => array('activity-view', 'activity-batchCreate', 'activity-edit', 'activity-batchEdit', 'activity-delete', 'activity-assignTo')); +$config->group->package->activity->privs['activity-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('activity-browse'), 'recommend' => array('activity-view', 'activity-create', 'activity-edit', 'activity-batchEdit', 'activity-delete', 'activity-assignTo')); +$config->group->package->activity->privs['activity-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('activity-browse'), 'recommend' => array('activity-view', 'activity-create', 'activity-batchCreate', 'activity-batchEdit', 'activity-delete', 'activity-assignTo')); +$config->group->package->activity->privs['activity-batchEdit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('activity-browse'), 'recommend' => array('activity-view', 'activity-create', 'activity-batchCreate', 'activity-edit', 'activity-delete', 'activity-assignTo')); +$config->group->package->activity->privs['activity-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 40, 'depend' => array('activity-browse'), 'recommend' => array('activity-view', 'activity-create', 'activity-batchCreate', 'activity-edit', 'activity-batchEdit', 'activity-assignTo')); + +$config->group->package->auditcl = new stdclass(); +$config->group->package->auditcl->order = 30; +$config->group->package->auditcl->subset = 'projectFlow'; +$config->group->package->auditcl->privs = array(); +$config->group->package->auditcl->privs['auditcl-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('auditcl-batchCreate', 'auditcl-edit', 'auditcl-batchEdit', 'auditcl-delete')); +$config->group->package->auditcl->privs['auditcl-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('auditcl-browse'), 'recommend' => array('auditcl-edit', 'auditcl-batchEdit', 'auditcl-delete')); +$config->group->package->auditcl->privs['auditcl-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('auditcl-browse'), 'recommend' => array('auditcl-batchCreate', 'auditcl-batchEdit', 'auditcl-delete')); +$config->group->package->auditcl->privs['auditcl-batchEdit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('auditcl-browse'), 'recommend' => array('auditcl-batchCreate', 'auditcl-edit', 'auditcl-delete')); +$config->group->package->auditcl->privs['auditcl-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('auditcl-browse'), 'recommend' => array('auditcl-batchCreate', 'auditcl-edit', 'auditcl-batchEdit')); + +$config->group->package->deliverable = new stdclass(); +$config->group->package->deliverable->order = 35; +$config->group->package->deliverable->subset = 'projectFlow'; +$config->group->package->deliverable->privs = array(); +$config->group->package->deliverable->privs['deliverable-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('deliverable-create', 'deliverable-view', 'deliverable-enable', 'deliverable-disable', 'deliverable-delete', 'deliverable-edit')); +$config->group->package->deliverable->privs['deliverable-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-view', 'deliverable-enable', 'deliverable-disable', 'deliverable-delete', 'deliverable-edit')); +$config->group->package->deliverable->privs['deliverable-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-create', 'deliverable-enable', 'deliverable-disable', 'deliverable-delete', 'deliverable-edit')); +$config->group->package->deliverable->privs['deliverable-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 23, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-create', 'deliverable-view', 'deliverable-enable', 'deliverable-disable', 'deliverable-delete')); +$config->group->package->deliverable->privs['deliverable-enable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-create', 'deliverable-view', 'deliverable-disable', 'deliverable-delete', 'deliverable-edit')); +$config->group->package->deliverable->privs['deliverable-disable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-create', 'deliverable-view', 'deliverable-enable', 'deliverable-delete', 'deliverable-edit')); +$config->group->package->deliverable->privs['deliverable-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-create', 'deliverable-view', 'deliverable-enable', 'deliverable-disable', 'deliverable-edit')); + +$config->group->package->stageSetting = new stdclass(); +$config->group->package->stageSetting->order = 40; +$config->group->package->stageSetting->subset = 'projectFlow'; +$config->group->package->stageSetting->privs = array(); +$config->group->package->stageSetting->privs['stage-browse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('stage-create', 'stage-batchCreate', 'stage-edit', 'stage-setTRpoint', 'stage-setDCPpoint', 'stage-delete')); +$config->group->package->stageSetting->privs['stage-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('stage-browse'), 'recommend' => array('stage-batchCreate', 'stage-edit', 'stage-setTRpoint', 'stage-setDCPpoint', 'stage-delete')); +$config->group->package->stageSetting->privs['stage-batchCreate'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('stage-browse'), 'recommend' => array('stage-create', 'stage-edit', 'stage-setTRpoint', 'stage-setDCPpoint', 'stage-delete')); +$config->group->package->stageSetting->privs['stage-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('stage-browse'), 'recommend' => array('stage-create', 'stage-batchCreate', 'stage-setTRpoint', 'stage-setDCPpoint', 'stage-delete')); +$config->group->package->stageSetting->privs['stage-setTRpoint'] = array('edition' => 'ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('stage-browse'), 'recommend' => array('stage-create', 'stage-batchCreate', 'stage-edit', 'stage-setDCPpoint', 'stage-delete')); +$config->group->package->stageSetting->privs['stage-setDCPpoint'] = array('edition' => 'ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('stage-browse'), 'recommend' => array('stage-create', 'stage-batchCreate', 'stage-edit', 'stage-setTRpoint', 'stage-delete')); +$config->group->package->stageSetting->privs['stage-delete'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 40, 'depend' => array('stage-browse'), 'recommend' => array('stage-create', 'stage-batchCreate', 'stage-edit', 'stage-setTRpoint', 'stage-setDCPpoint')); + +$config->group->package->reviewFlow = new stdclass(); +$config->group->package->reviewFlow->order = 45; +$config->group->package->reviewFlow->subset = 'projectFlow'; +$config->group->package->reviewFlow->privs = array(); +$config->group->package->reviewFlow->privs['review-admin'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('review-createFlow', 'review-editFlow', 'review-deleteFlow')); +$config->group->package->reviewFlow->privs['review-createFlow'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('review-admin'), 'recommend' => array('review-editFlow', 'review-deleteFlow')); +$config->group->package->reviewFlow->privs['review-editFlow'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('review-admin'), 'recommend' => array('review-createFlow', 'review-deleteFlow')); +$config->group->package->reviewFlow->privs['review-deleteFlow'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('review-admin'), 'recommend' => array('review-createFlow', 'review-editFlow')); + +$config->group->package->reviewCheck = new stdclass(); +$config->group->package->reviewCheck->order = 50; +$config->group->package->reviewCheck->subset = 'projectFlow'; +$config->group->package->reviewCheck->privs = array(); +$config->group->package->reviewCheck->privs['reviewcl-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('reviewcl-create', 'reviewcl-batchCreate', 'reviewcl-edit', 'reviewcl-delete', 'reviewcl-view')); +$config->group->package->reviewCheck->privs['reviewcl-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('reviewcl-browse'), 'recommend' => array('reviewcl-batchCreate', 'reviewcl-edit', 'reviewcl-delete', 'reviewcl-view')); +$config->group->package->reviewCheck->privs['reviewcl-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('reviewcl-browse'), 'recommend' => array('reviewcl-create', 'reviewcl-edit', 'reviewcl-delete', 'reviewcl-view')); +$config->group->package->reviewCheck->privs['reviewcl-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('reviewcl-browse'), 'recommend' => array('reviewcl-create', 'reviewcl-batchCreate', 'reviewcl-delete', 'reviewcl-view')); +$config->group->package->reviewCheck->privs['reviewcl-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('reviewcl-browse'), 'recommend' => array('reviewcl-create', 'reviewcl-batchCreate', 'reviewcl-edit', 'reviewcl-view')); +$config->group->package->reviewCheck->privs['reviewcl-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array('reviewcl-browse'), 'recommend' => array('reviewcl-create', 'reviewcl-batchCreate', 'reviewcl-edit', 'reviewcl-delete')); + +$config->group->package->reviewCategory = new stdclass(); +$config->group->package->reviewCategory->order = 55; +$config->group->package->reviewCategory->subset = 'projectFlow'; +$config->group->package->reviewCategory->privs = array(); +$config->group->package->reviewCategory->privs['reviewcl-setCategory'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array()); + +$config->group->package->setWorkflow = new stdclass(); +$config->group->package->setWorkflow->order = 60; +$config->group->package->setWorkflow->subset = 'projectFlow'; +$config->group->package->setWorkflow->privs = array(); +$config->group->package->setWorkflow->privs['workflowgroup-design'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('workflowgroup-report'), 'recommend' => array('workflowgroup-setExclusive', 'workflowgroup-designFlow', 'workflowgroup-activateFlow', 'workflowgroup-deactivateFlow')); +$config->group->package->setWorkflow->privs['workflowgroup-setExclusive'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 15, 'depend' => array('workflowgroup-design'), 'recommend' => array('workflowgroup-designFlow', 'workflowgroup-activateFlow', 'workflowgroup-deactivateFlow')); +$config->group->package->setWorkflow->privs['workflowgroup-designFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 20, 'depend' => array('workflowgroup-setExclusive'), 'recommend' => array('workflowgroup-design', 'workflowgroup-activateFlow', 'workflowgroup-deactivateFlow')); +$config->group->package->setWorkflow->privs['workflowgroup-activateFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array('workflowgroup-design'), 'recommend' => array('workflowgroup-setExclusive', 'workflowgroup-designFlow', 'workflowgroup-deactivateFlow')); +$config->group->package->setWorkflow->privs['workflowgroup-deactivateFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array('workflowgroup-design'), 'recommend' => array('workflowgroup-setExclusive', 'workflowgroup-designFlow', 'workflowgroup-activateFlow')); + $config->group->package->workflowField = new stdclass(); $config->group->package->workflowField->order = 10; $config->group->package->workflowField->subset = 'workflow'; @@ -2620,25 +2818,6 @@ $config->group->package->workflowRule->privs['workflowrule-edit'] = array('edi $config->group->package->workflowRule->privs['workflowrule-view'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 20, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); $config->group->package->workflowRule->privs['workflowrule-delete'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 25, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup = new stdclass(); -$config->group->package->workflowGroup->order = 65; -$config->group->package->workflowGroup->subset = 'workflow'; -$config->group->package->workflowGroup->privs = array(); -$config->group->package->workflowGroup->privs['workflowgroup-product'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-project'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-create'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 15, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 20, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-view'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 25, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-delete'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 30, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-design'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 35, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-release'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 40, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-deactivate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 45, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-setExclusive'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 50, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-activateFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 55, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-deactivateFlow'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 60, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-rule'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 65, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowGroup->privs['workflowgroup-deliverable'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 70, 'depend' => array('workflowgroup-project'), 'recommend' => array()); - $config->group->package->workflow = new stdclass(); $config->group->package->workflow->order = 5; $config->group->package->workflow->subset = 'workflow'; @@ -2959,7 +3138,8 @@ $config->group->package->module->order = 5; $config->group->package->module->subset = 'tree'; $config->group->package->module->privs = array(); $config->group->package->module->privs['tree-browse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 0, 'depend' => array(), 'recommend' => array('tree-delete', 'tree-manageChild')); -$config->group->package->module->privs['tree-browseTask'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 1, 'depend' => array('tree-edit'), 'recommend' => array('tree-delete', 'tree-manageChild')); +$config->group->package->module->privs['tree-browseTask'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 1, 'depend' => array('tree-edit'), 'recommend' => array('tree-delete', 'tree-manageChild')); +$config->group->package->module->privs['tree-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 3, 'depend' => array(), 'recommend' => array()); $config->group->package->module->privs['tree-updateOrder'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('tree-browse'), 'recommend' => array()); $config->group->package->module->privs['tree-manageChild'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 6, 'depend' => array('tree-edit'), 'recommend' => array('tree-delete')); $config->group->package->module->privs['tree-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 7, 'depend' => array('tree-browse'), 'recommend' => array()); @@ -3048,142 +3228,61 @@ $config->group->package->importToCaseLib->privs['testcase-importToLib'] = array( $config->group->package->commonSetting = new stdclass(); $config->group->package->commonSetting->order = 5; -$config->group->package->commonSetting->subset = 'modelconfig'; +$config->group->package->commonSetting->subset = 'featureconfig'; $config->group->package->commonSetting->privs = array(); $config->group->package->commonSetting->privs['custom-project'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 1, 'depend' => array(), 'recommend' => array()); -$config->group->package->commonSetting->privs['stage-setType'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array(), 'recommend' => array()); -$config->group->package->commonSetting->privs['custom-flow'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 35, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->commonSetting->privs['custom-estimate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 50, 'depend' => array(), 'recommend' => array()); -$config->group->package->commonSetting->privs['custom-code'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,or', 'order' => 80, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->commonSetting->privs['custom-hours'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,or', 'order' => 85, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->commonSetting->privs['custom-percent'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,or', 'order' => 90, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->commonSetting->privs['subject-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 35, 'depend' => array(), 'recommend' => array()); -$config->group->package->commonSetting->privs['baseline-templatetype'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 40, 'depend' => array(), 'recommend' => array()); +$config->group->package->commonSetting->privs['custom-flow'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); +$config->group->package->commonSetting->privs['custom-estimate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array(), 'recommend' => array()); +$config->group->package->commonSetting->privs['custom-hours'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,or', 'order' => 15, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); +$config->group->package->commonSetting->privs['custom-percent'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,or', 'order' => 20, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); +$config->group->package->commonSetting->privs['subject-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 25, 'depend' => array(), 'recommend' => array()); +$config->group->package->commonSetting->privs['baseline-templatetype'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 30, 'depend' => array(), 'recommend' => array()); -$config->group->package->stageSetting = new stdclass(); -$config->group->package->stageSetting->order = 15; -$config->group->package->stageSetting->subset = 'modelconfig'; -$config->group->package->stageSetting->privs = array(); -$config->group->package->stageSetting->privs['stage-browse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->stageSetting->privs['stage-create'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array(), 'recommend' => array('stage-batchCreate', 'stage-delete', 'stage-edit')); -$config->group->package->stageSetting->privs['stage-batchCreate'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array('stage-create')); -$config->group->package->stageSetting->privs['stage-edit'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array(), 'recommend' => array('stage-create', 'stage-delete')); -$config->group->package->stageSetting->privs['stage-delete'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array('stage-create', 'stage-edit')); -$config->group->package->stageSetting->privs['stage-plusBrowse'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array()); - -$config->group->package->deliverable = new stdclass(); -$config->group->package->deliverable->order = 20; -$config->group->package->deliverable->subset = 'modelconfig'; -$config->group->package->deliverable->privs = array(); -$config->group->package->deliverable->privs['deliverable-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->deliverable->privs['deliverable-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('deliverable-browse'), 'recommend' => array()); -$config->group->package->deliverable->privs['deliverable-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-view', 'deliverable-browse')); -$config->group->package->deliverable->privs['deliverable-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('deliverable-browse'), 'recommend' => array('deliverable-view', 'deliverable-browse')); -$config->group->package->deliverable->privs['deliverable-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('deliverable-browse'), 'recommend' => array()); +$config->group->package->stageType = new stdclass(); +$config->group->package->stageType->order = 15; +$config->group->package->stageType->subset = 'featureconfig'; +$config->group->package->stageType->privs = array(); +$config->group->package->stageType->privs['stage-setType'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); $config->group->package->classify = new stdclass(); $config->group->package->classify->order = 30; -$config->group->package->classify->subset = 'modelconfig'; +$config->group->package->classify->subset = 'featureconfig'; $config->group->package->classify->privs = array(); $config->group->package->classify->privs['classify-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); -$config->group->package->cmcl = new stdclass(); -$config->group->package->cmcl->order = 50; -$config->group->package->cmcl->subset = 'modelconfig'; -$config->group->package->cmcl->privs = array(); -$config->group->package->cmcl->privs['cmcl-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array('cmcl-delete', 'cmcl-edit')); -$config->group->package->cmcl->privs['cmcl-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array('cmcl-batchCreate', 'cmcl-edit')); -$config->group->package->cmcl->privs['cmcl-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array(), 'recommend' => array('cmcl-batchCreate', 'cmcl-delete')); -$config->group->package->cmcl->privs['cmcl-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->cmcl->privs['cmcl-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array(), 'recommend' => array()); -$config->group->package->cmcl->privs['cmcl-waterfallplusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array()); - -$config->group->package->auditcl = new stdclass(); -$config->group->package->auditcl->order = 20; -$config->group->package->auditcl->subset = 'modelconfig'; -$config->group->package->auditcl->privs = array(); -$config->group->package->auditcl->privs['auditcl-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array('auditcl-batchEdit', 'auditcl-delete', 'auditcl-edit')); -$config->group->package->auditcl->privs['auditcl-batchEdit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array(), 'recommend' => array('auditcl-batchCreate', 'auditcl-delete', 'auditcl-edit')); -$config->group->package->auditcl->privs['auditcl-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array(), 'recommend' => array('auditcl-batchCreate', 'auditcl-batchEdit', 'auditcl-edit')); -$config->group->package->auditcl->privs['auditcl-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array(), 'recommend' => array('auditcl-batchCreate', 'auditcl-batchEdit', 'auditcl-delete')); -$config->group->package->auditcl->privs['auditcl-scrumBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->auditcl->privs['auditcl-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array()); -$config->group->package->auditcl->privs['auditcl-agilePlusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array(), 'recommend' => array()); -$config->group->package->auditcl->privs['auditcl-waterfallplusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array()); - $config->group->package->reviewcl = new stdclass(); $config->group->package->reviewcl->order = 45; -$config->group->package->reviewcl->subset = 'modelconfig'; +$config->group->package->reviewcl->subset = 'featureconfig'; $config->group->package->reviewcl->privs = array(); -$config->group->package->reviewcl->privs['reviewcl-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->reviewcl->privs['reviewcl-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array('reviewcl-batchCreate', 'reviewcl-delete', 'reviewcl-edit', 'reviewcl-view')); -$config->group->package->reviewcl->privs['reviewcl-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array(), 'recommend' => array('reviewcl-create')); -$config->group->package->reviewcl->privs['reviewcl-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array(), 'recommend' => array('reviewcl-create', 'reviewcl-edit', 'reviewcl-view')); -$config->group->package->reviewcl->privs['reviewcl-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array(), 'recommend' => array('reviewcl-create', 'reviewcl-delete', 'reviewcl-view')); -$config->group->package->reviewcl->privs['reviewcl-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array(), 'recommend' => array()); $config->group->package->reviewcl->privs['reviewcl-waterfallplusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array()); $config->group->package->reviewcl->privs['reviewsetting-version'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array(), 'recommend' => array()); $config->group->package->reviewcl->privs['reviewsetting-waterfallplusVersion'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array()); -$config->group->package->reviewcl->privs['reviewcl-setCategory'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array(), 'recommend' => array()); -$config->group->package->process = new stdclass(); -$config->group->package->process->order = 25; -$config->group->package->process->subset = 'modelconfig'; -$config->group->package->process->privs = array(); -$config->group->package->process->privs['process-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array('process-activityList', 'process-batchCreate', 'process-delete', 'process-edit', 'process-updateOrder', 'process-view')); -$config->group->package->process->privs['process-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array(), 'recommend' => array('process-create')); -$config->group->package->process->privs['process-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array(), 'recommend' => array('process-create')); -$config->group->package->process->privs['process-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array(), 'recommend' => array('process-activityList', 'process-create', 'process-delete', 'process-updateOrder', 'process-view')); -$config->group->package->process->privs['process-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array(), 'recommend' => array('process-activityList')); -$config->group->package->process->privs['process-updateOrder'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 9, 'depend' => array(), 'recommend' => array('process-create', 'process-edit')); -$config->group->package->process->privs['process-activityList'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array(), 'recommend' => array('process-create', 'process-edit', 'process-view')); -$config->group->package->process->privs['process-scrumBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->process->privs['process-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array(), 'recommend' => array()); -$config->group->package->process->privs['process-agilePlusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array(), 'recommend' => array()); -$config->group->package->process->privs['process-waterfallPlusBrowse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array(), 'recommend' => array()); +$config->group->package->executionSetting = new stdclass(); +$config->group->package->executionSetting->order = 3; +$config->group->package->executionSetting->subset = 'featureconfig'; +$config->group->package->executionSetting->privs = array(); +$config->group->package->executionSetting->privs['custom-execution'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 9, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->activity = new stdclass(); -$config->group->package->activity->order = 35; -$config->group->package->activity->subset = 'modelconfig'; -$config->group->package->activity->privs = array(); -$config->group->package->activity->privs['activity-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->activity->privs['activity-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('activity-browse'), 'recommend' => array('activity-assignTo', 'activity-batchCreate', 'activity-delete', 'activity-edit', 'activity-outputList', 'activity-updateOrder', 'activity-view')); -$config->group->package->activity->privs['activity-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('activity-browse'), 'recommend' => array('activity-create')); -$config->group->package->activity->privs['activity-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('activity-browse'), 'recommend' => array('activity-create')); -$config->group->package->activity->privs['activity-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('activity-browse'), 'recommend' => array('activity-assignTo', 'activity-batchCreate', 'activity-create', 'activity-delete', 'activity-outputList', 'activity-updateOrder', 'activity-view')); -$config->group->package->activity->privs['activity-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('activity-browse'), 'recommend' => array('activity-outputList')); -$config->group->package->activity->privs['activity-assignTo'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('activity-browse'), 'recommend' => array('activity-create', 'activity-edit')); -$config->group->package->activity->privs['activity-outputList'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('activity-browse'), 'recommend' => array('activity-create', 'activity-edit', 'activity-view')); -$config->group->package->activity->privs['activity-updateOrder'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('activity-browse'), 'recommend' => array('activity-create', 'activity-edit')); - -$config->group->package->zoutput = new stdclass(); -$config->group->package->zoutput->order = 40; -$config->group->package->zoutput->subset = 'modelconfig'; -$config->group->package->zoutput->privs = array(); -$config->group->package->zoutput->privs['zoutput-browse'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 0, 'depend' => array(), 'recommend' => array()); -$config->group->package->zoutput->privs['zoutput-create'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('zoutput-browse'), 'recommend' => array('zoutput-batchCreate', 'zoutput-delete', 'zoutput-edit', 'zoutput-updateOrder', 'zoutput-view')); -$config->group->package->zoutput->privs['zoutput-edit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('zoutput-browse'), 'recommend' => array('zoutput-batchEdit', 'zoutput-create', 'zoutput-delete', 'zoutput-updateOrder', 'zoutput-view')); -$config->group->package->zoutput->privs['zoutput-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('zoutput-browse'), 'recommend' => array('zoutput-create')); -$config->group->package->zoutput->privs['zoutput-batchEdit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('zoutput-browse'), 'recommend' => array('zoutput-edit')); -$config->group->package->zoutput->privs['zoutput-delete'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('zoutput-browse'), 'recommend' => array('zoutput-create')); -$config->group->package->zoutput->privs['zoutput-view'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 1, 'depend' => array('zoutput-browse'), 'recommend' => array()); -$config->group->package->zoutput->privs['zoutput-updateOrder'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('zoutput-browse'), 'recommend' => array('zoutput-create', 'zoutput-edit')); +$config->group->package->productSetting = new stdclass(); +$config->group->package->productSetting->order = 4; +$config->group->package->productSetting->subset = 'featureconfig'; +$config->group->package->productSetting->privs = array(); +$config->group->package->productSetting->privs['custom-product'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 1, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); +$config->group->package->productSetting->privs['custom-editStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 2, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); +$config->group->package->productSetting->privs['custom-browseStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 3, 'depend' => array('custom-product'), 'recommend' => array()); +$config->group->package->productSetting->privs['custom-deleteStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 4, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); +$config->group->package->productSetting->privs['custom-setStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); +$config->group->package->productSetting->privs['custom-setDefaultConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 6, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); $config->group->package->custom = new stdclass(); $config->group->package->custom->order = 5; $config->group->package->custom->subset = 'featureconfig'; $config->group->package->custom->privs = array(); $config->group->package->custom->privs['custom-set'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 0, 'depend' => array('custom-product', 'custom-execution'), 'recommend' => array()); -$config->group->package->custom->privs['custom-product'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 3, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->custom->privs['custom-execution'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 9, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); $config->group->package->custom->privs['custom-required'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 1, 'depend' => array('custom-product', 'custom-execution'), 'recommend' => array()); $config->group->package->custom->privs['custom-restore'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 2, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->custom->privs['custom-editStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 7, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); -$config->group->package->custom->privs['custom-browseStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 4, 'depend' => array('custom-product'), 'recommend' => array()); -$config->group->package->custom->privs['custom-setDefaultConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); -$config->group->package->custom->privs['custom-deleteStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 8, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); $config->group->package->custom->privs['custom-kanban'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 11, 'depend' => array('admin-index', 'admin-register'), 'recommend' => array()); -$config->group->package->custom->privs['custom-setStoryConcept'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 6, 'depend' => array('custom-browseStoryConcept'), 'recommend' => array()); $config->group->package->custom->privs['custom-limitTaskDate'] = array('edition' => 'open,biz,max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('custom-execution'), 'recommend' => array()); $config->group->package->charter = new stdclass(); @@ -3195,7 +3294,7 @@ $config->group->package->charter->privs['custom-resetCharterInfo'] = array('edit $config->group->package->approvalflow = new stdclass(); $config->group->package->approvalflow->order = 10; -$config->group->package->approvalflow->subset = 'featureconfig'; +$config->group->package->approvalflow->subset = 'approvalflow'; $config->group->package->approvalflow->privs = array(); $config->group->package->approvalflow->privs['approval-progress'] = array('edition' => 'max,ipd', 'vision' => 'rnd,or', 'order' => 10, 'depend' => array('approvalflow-browse'), 'recommend' => array()); $config->group->package->approvalflow->privs['approvalflow-browse'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 4, 'depend' => array('approvalflow-role'), 'recommend' => array()); @@ -3221,13 +3320,6 @@ $config->group->package->meetingroom->privs['meetingroom-view'] = array(' $config->group->package->meetingroom->privs['meetingroom-batchCreate'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 3, 'depend' => array('meetingroom-browse'), 'recommend' => array('meetingroom-create')); $config->group->package->meetingroom->privs['meetingroom-batchEdit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('meetingroom-browse'), 'recommend' => array('meetingroom-create')); -$config->group->package->designSetting = new stdclass(); -$config->group->package->designSetting->order = 55; -$config->group->package->designSetting->subset = 'modelconfig'; -$config->group->package->designSetting->privs = array(); -$config->group->package->designSetting->privs['design-setType'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 65, 'depend' => array(), 'recommend' => array()); -$config->group->package->designSetting->privs['design-setPlusType'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 70, 'depend' => array(), 'recommend' => array()); - $config->group->package->kanbanSpace = new stdclass(); $config->group->package->kanbanSpace->order = 5; $config->group->package->kanbanSpace->subset = 'managespace'; @@ -3381,7 +3473,8 @@ $config->group->package->review = new stdclass(); $config->group->package->review->order = 20; $config->group->package->review->subset = 'projectsettings'; $config->group->package->review->privs = array(); -$config->group->package->review->privs['project-approval'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); +$config->group->package->review->privs['project-approval'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); +$config->group->package->review->privs['project-editApproval'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 10, 'depend' => array('project-approval'), 'recommend' => array()); $config->group->package->manageProjectRelease = new stdclass(); $config->group->package->manageProjectRelease->order = 10; @@ -3494,17 +3587,11 @@ $config->group->package->chckAuditPlan->privs = array(); $config->group->package->chckAuditPlan->privs['auditplan-batchCheck'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('auditplan-browse'), 'recommend' => array('auditplan-check')); $config->group->package->chckAuditPlan->privs['auditplan-check'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('auditplan-browse'), 'recommend' => array('auditplan-assignTo', 'auditplan-batchCheck', 'auditplan-create', 'auditplan-edit', 'auditplan-nc', 'auditplan-result')); -$config->group->package->reviewAssess = new stdclass(); -$config->group->package->reviewAssess->order = 15; -$config->group->package->reviewAssess->subset = 'projectreview'; -$config->group->package->reviewAssess->privs = array(); -$config->group->package->reviewAssess->privs['review-assess'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('review-browse'), 'recommend' => array('review-report')); - -$config->group->package->reviewAudit = new stdclass(); -$config->group->package->reviewAudit->order = 20; -$config->group->package->reviewAudit->subset = 'projectreview'; -$config->group->package->reviewAudit->privs = array(); -$config->group->package->reviewAudit->privs['review-audit'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 9, 'depend' => array('review-browse'), 'recommend' => array()); +$config->group->package->deliverableChecklist = new stdclass(); +$config->group->package->deliverableChecklist->order = 20; +$config->group->package->deliverableChecklist->subset = 'projectauditplan'; +$config->group->package->deliverableChecklist->privs = array(); +$config->group->package->deliverableChecklist->privs['project-deliverableChecklist'] = array('edition' => 'max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array(), 'recommend' => array()); $config->group->package->dimension = new stdclass(); $config->group->package->dimension->order = 5; diff --git a/module/holiday/ui/browse.html.php b/module/holiday/ui/browse.html.php index 1f0813b80d..1ece37312b 100644 --- a/module/holiday/ui/browse.html.php +++ b/module/holiday/ui/browse.html.php @@ -21,24 +21,24 @@ sidebar menu ( setClass('menu'), - li + hasPriv('custom', 'hours') ? li ( setClass('menu-item'), a ( - set::href(hasPriv('custom', 'hours') ? createLink('custom', 'hours', 'type=hours') : ''), + set::href(createLink('custom', 'hours', 'type=hours')), $lang->custom->setHours ) - ), - li + ) : null, + hasPriv('custom', 'hours') ? li ( setClass('menu-item'), a ( - set::href(hasPriv('custom', 'hours') ? createLink('custom', 'hours', 'type=weekend') : ''), + set::href(createLink('custom', 'hours', 'type=weekend')), $lang->custom->setWeekend ) - ), + ) : null, li ( setClass('menu-item'), @@ -111,4 +111,3 @@ panel ); render(); - diff --git a/module/install/control.php b/module/install/control.php index ef4ad94989..4b9d7056c2 100644 --- a/module/install/control.php +++ b/module/install/control.php @@ -377,9 +377,13 @@ class install extends control $this->setting->setItem('system.common.global.cron', '1'); $this->setting->setItem('system.common.userview.relatedTablesUpdateTime', time()); - /* 处理BI数据表。*/ + /* 处理BI数据表, 内置流程数据。*/ /* Process BI dataview. */ - if($this->config->edition != 'open') $this->loadModel('upgrade')->processDataset(); + if($this->config->edition != 'open') + { + $this->loadModel('upgrade')->processDataset(); + $this->loadModel('workflowGroup')->initAllWorkflowGroup(); + } /* 更新度量项的创建时间。*/ /* Update created date of metrics. */ diff --git a/module/kanban/tao.php b/module/kanban/tao.php index 2a7da0846a..4b3c035da5 100644 --- a/module/kanban/tao.php +++ b/module/kanban/tao.php @@ -984,8 +984,8 @@ class kanbanTao extends kanbanModel if(common::hasPriv($story->type, 'change') and $this->story->isClickable($story, 'change')) $menu[] = array('label' => $this->lang->story->change, 'icon' => 'alter', 'url' => helper::createLink($story->type, 'change', "storyID=$story->id"), 'modal' => true, 'size' => 'lg'); if(common::hasPriv($story->type, 'review') and $this->story->isClickable($story, 'review')) $menu[] = array('label' => $this->lang->story->review, 'icon' => 'search', 'url' => helper::createLink($story->type, 'review', "storyID=$story->id"), 'modal' => true, 'size' => 'lg'); if(common::hasPriv($story->type, 'activate') and $this->story->isClickable($story, 'activate')) $menu[] = array('label' => $this->lang->story->activate, 'icon' => 'magic', 'url' => helper::createLink($story->type, 'activate', "storyID=$story->id"), 'modal' => true, 'size' => 'lg'); - if(common::hasPriv('execution', 'unlinkStory') && $execution->hasProduct) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID=$executionID&storyID=$story->story&confirm=no&from=taskkanban")); - if(common::hasPriv($story->type, 'delete')) $menu[] = array('label' => $this->lang->story->delete, 'icon' => 'trash', 'url' => helper::createLink($story->type, 'delete', "storyID=$story->id&confirm=no&from=taskkanban")); + if(common::hasPriv('execution', 'unlinkStory') && $execution->hasProduct && empty($story->frozen)) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID=$executionID&storyID=$story->story&confirm=no&from=taskkanban")); + if(common::hasPriv($story->type, 'delete') && empty($story->frozen)) $menu[] = array('label' => $this->lang->story->delete, 'icon' => 'trash', 'url' => helper::createLink($story->type, 'delete', "storyID=$story->id&confirm=no&from=taskkanban")); $menus[$story->id] = $menu; } @@ -1017,8 +1017,8 @@ class kanbanTao extends kanbanModel if(common::hasPriv('task', 'create') && $toTaskPriv) $menu[] = array('label' => $this->lang->execution->wbs, 'icon' => 'plus', 'url' => helper::createLink('task', 'create', "executionID={$execution->id}&storyID=$story->id&moduleID=$story->module"), 'modal' => true, 'size' => 'lg'); if(common::hasPriv('task', 'batchCreate') && $toTaskPriv) $menu[] = array('label' => $this->lang->execution->batchWBS, 'icon' => 'pluses', 'url' => helper::createLink('task', 'batchCreate', "executionID={$execution->id}&storyID=$story->id&moduleID=0&taskID=0&iframe=true"), 'modal' => true, 'size' => 'lg'); if(common::hasPriv('story', 'activate') && $this->story->isClickable($story, 'activate')) $menu[] = array('label' => $this->lang->story->activate, 'icon' => 'magic', 'url' => helper::createLink('story', 'activate', "storyID=$story->id"), 'modal' => true, 'size' => 'lg'); - if(common::hasPriv('execution', 'unlinkStory') && $execution->hasProduct) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID={$execution->id}&storyID=$story->story&confirm=no&from=taskkanban")); - if(common::hasPriv('story', 'delete')) $menu[] = array('label' => $this->lang->story->delete, 'icon' => 'trash', 'url' => helper::createLink('story', 'delete', "storyID=$story->id&confirm=no&from=taskkanban")); + if(common::hasPriv('execution', 'unlinkStory') && $execution->hasProduct && empty($story->frozen)) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID={$execution->id}&storyID=$story->story&confirm=no&from=taskkanban")); + if(common::hasPriv('story', 'delete') && empty($story->frozen)) $menu[] = array('label' => $this->lang->story->delete, 'icon' => 'trash', 'url' => helper::createLink('story', 'delete', "storyID=$story->id&confirm=no&from=taskkanban")); $menus[$story->id] = $menu; } diff --git a/module/kanban/test/tao/getstorycardemenu.php b/module/kanban/test/tao/getstorycardmenu.php similarity index 100% rename from module/kanban/test/tao/getstorycardemenu.php rename to module/kanban/test/tao/getstorycardmenu.php diff --git a/module/kanban/test/zen/yaml/kanban_assigncreatevars.yaml b/module/kanban/test/zen/yaml/kanban_assigncreatevars.yaml index ebd1888176..52cc11cfa6 100644 --- a/module/kanban/test/zen/yaml/kanban_assigncreatevars.yaml +++ b/module/kanban/test/zen/yaml/kanban_assigncreatevars.yaml @@ -8,118 +8,118 @@ fields: - field: id note: 看板ID range: 1-5 - + - field: space note: 所属空间ID range: 1-3 - + - field: name note: 看板名称 range: Development Kanban{1}, Testing Kanban{1}, Product Kanban{1}, Sprint Kanban{1}, Project Kanban{1} - + - field: owner note: 看板拥有者 range: admin{2}, user1{2}, user2{1} - + - field: team note: 看板团队成员 range: user1,user2{2}, user2,user3{1}, admin,user1{1}, []{1} - + - field: desc note: 看板描述 range: Development workflow kanban{1}, Testing process board{1}, Product management kanban{1}, Sprint planning board{1}, Project tracking kanban{1} - + - field: acl note: 访问控制 range: open{3}, private{1}, cooperation{1} - + - field: whitelist note: 白名单 range: user1,user2{1}, admin,user1{2}, []{2} - + - field: archived note: 归档状态 range: 0{4}, 1{1} - + - field: performable note: 可执行性 range: 0{3}, 1{2} - + - field: status note: 看板状态 range: active{4}, closed{1} - + - field: order note: 排序 range: 1-5 - + - field: displayCards note: 显示卡片数 range: 0{2}, 10{1}, 20{1}, 50{1} - + - field: showWIP note: 显示WIP range: 1{4}, 0{1} - + - field: fluidBoard note: 流式看板 range: 0{3}, 1{2} - + - field: colWidth note: 列宽度 range: 264{3}, 300{1}, 200{1} - + - field: minColWidth note: 最小列宽度 range: 200{5} - + - field: maxColWidth note: 最大列宽度 range: 384{5} - + - field: object note: 导入对象类型 range: []{1}, plans{1}, releases{1}, plans,builds{1}, executions,cards{1} - + - field: alignment note: 对齐方式 range: center{4}, left{1} - + - field: createdBy note: 创建者 range: admin{3}, user1{1}, user2{1} - + - field: createdDate note: 创建时间 range: (-20d)-(-1d):2d format: YYYY-MM-DD hh:mm:ss - + - field: lastEditedBy note: 最后编辑者 range: admin{2}, user1{2}, user2{1} - + - field: lastEditedDate note: 最后编辑时间 range: (-5d)-(0d):1d format: YYYY-MM-DD hh:mm:ss - + - field: closedBy note: 关闭者 range: []{4}, admin{1} - + - field: closedDate note: 关闭时间 range: 0000-00-00 00:00:00{4}, (-1d)-(0d):1d{1} - + - field: activatedBy note: 激活者 range: admin{3}, user1{1}, user2{1} - + - field: activatedDate note: 激活时间 range: (-3d)-(0d):1d format: YYYY-MM-DD hh:mm:ss - + - field: deleted note: 删除状态 range: 0{5} \ No newline at end of file diff --git a/module/kanban/test/zen/yaml/kanbanspace_assigncreatevars.yaml b/module/kanban/test/zen/yaml/kanbanspace_assigncreatevars.yaml index 9f147b183e..6fc664f98c 100644 --- a/module/kanban/test/zen/yaml/kanbanspace_assigncreatevars.yaml +++ b/module/kanban/test/zen/yaml/kanbanspace_assigncreatevars.yaml @@ -8,78 +8,78 @@ fields: - field: id note: 看板空间ID range: 1-10 - + - field: name note: 看板空间名称 range: Private Space{1}, Cooperation Space{1}, Public Space{2}, Test Space{6} - + - field: type note: 看板空间类型 range: private{1}, cooperation{1}, public{8} - + - field: owner note: 看板空间拥有者 range: admin{4}, user1{3}, user2{3} - + - field: team note: 看板空间团队成员 range: user1,user2{3}, user2,user3{2}, admin,user1{2}, []{3} - + - field: desc note: 看板空间描述 range: Private space for testing{1}, Cooperation workspace{1}, Public shared space{2}, Test workspace for development{6} - + - field: acl note: 访问控制 range: open{6}, private{2}, cooperation{2} - + - field: whitelist note: 白名单用户 range: user1,user2{2}, admin,user1{3}, []{5} - + - field: status note: 看板空间状态 range: active{8}, closed{2} - + - field: order note: 排序 range: 1-10 - + - field: createdBy note: 创建者 range: admin{5}, user1{3}, user2{2} - + - field: createdDate note: 创建时间 range: (-30d)-(-1d):1d format: YYYY-MM-DD hh:mm:ss - + - field: lastEditedBy note: 最后编辑者 range: admin{4}, user1{3}, user2{3} - + - field: lastEditedDate note: 最后编辑时间 range: (-7d)-(0d):1d format: YYYY-MM-DD hh:mm:ss - + - field: closedBy note: 关闭者 range: []{8}, admin{1}, user1{1} - + - field: closedDate note: 关闭时间 range: 0000-00-00 00:00:00{8}, (-3d)-(-1d):1d{2} - + - field: activatedBy note: 激活者 range: admin{5}, user1{3}, user2{2} - + - field: activatedDate note: 激活时间 range: (-5d)-(0d):1d format: YYYY-MM-DD hh:mm:ss - + - field: deleted note: 删除状态 range: 0{9}, 1{1} \ No newline at end of file diff --git a/module/kanban/test/zen/yaml/user_assigncreatevars.yaml b/module/kanban/test/zen/yaml/user_assigncreatevars.yaml index bc0f9ffb37..acd035ce78 100644 --- a/module/kanban/test/zen/yaml/user_assigncreatevars.yaml +++ b/module/kanban/test/zen/yaml/user_assigncreatevars.yaml @@ -8,133 +8,133 @@ fields: - field: id note: 用户ID range: 1-10 - + - field: account note: 用户账号 range: admin{1}, user1{1}, user2{1}, user3{1}, tester{1}, developer{1}, manager{1}, guest{1}, qa{1}, pm{1} - + - field: password note: 用户密码 range: 098f6bcd4621d373cade4e832627b4f6{10} - + - field: role note: 用户角色 range: admin{1}, top{1}, dev{3}, qa{2}, pm{2}, others{1} - + - field: dept note: 部门ID range: 1{3}, 2{3}, 3{2}, 4{2} - + - field: realname note: 真实姓名 range: 管理员{1}, 用户一{1}, 用户二{1}, 用户三{1}, 测试员{1}, 开发者{1}, 经理{1}, 访客{1}, QA{1}, PM{1} - + - field: nickname note: 昵称 range: Admin{1}, User1{1}, User2{1}, User3{1}, Tester{1}, Dev{1}, Manager{1}, Guest{1}, QA{1}, PM{1} - + - field: commiter note: 提交者标识 range: admin{1}, user1{1}, user2{1}, []{7} - + - field: avatar note: 头像 range: []{10} - + - field: birthday note: 生日 range: 0000-00-00{5}, (-30Y)-(-20Y):365d{5} format: YYYY-MM-DD - + - field: gender note: 性别 range: m{5}, f{3}, []{2} - + - field: email note: 电子邮件 range: admin@zentao.com{1}, user1@zentao.com{1}, user2@zentao.com{1}, user3@zentao.com{1}, test@zentao.com{6} - + - field: skype note: Skype账号 range: []{10} - + - field: qq note: QQ号码 range: []{8}, 123456789{1}, 987654321{1} - + - field: yahoo note: Yahoo账号 range: []{10} - + - field: gtalk note: GTalk账号 range: []{10} - + - field: wangwang note: 旺旺账号 range: []{10} - + - field: mobile note: 手机号码 range: []{7}, 13800138000{1}, 15900159000{1}, 18600186000{1} - + - field: phone note: 电话号码 range: []{8}, 020-12345678{1}, 010-87654321{1} - + - field: address note: 地址 range: []{8}, 广州市天河区{1}, 北京市朝阳区{1} - + - field: zipcode note: 邮政编码 range: []{8}, 510000{1}, 100000{1} - + - field: join note: 入职时间 range: (-365d)-(-30d):30d format: YYYY-MM-DD - + - field: visits note: 访问次数 range: 0{2}, 1-100:R{8} - + - field: ip note: IP地址 range: []{5}, 192.168.1.100{2}, 127.0.0.1{3} - + - field: last note: 最后登录时间 range: 0{2}, (-30d)-(0d):1d{8} - + - field: fails note: 登录失败次数 range: 0{8}, 1{1}, 2{1} - + - field: locked note: 锁定时间 range: 0000-00-00 00:00:00{10} - + - field: feedback note: 反馈设置 range: 0{5}, 1{5} - + - field: ranzhi note: 然之集成账号 range: []{10} - + - field: ldap note: LDAP账号 range: []{10} - + - field: score note: 积分 range: 0{10} - + - field: scoreLevel note: 积分等级 range: 0{10} - + - field: deleted note: 删除状态 range: 0{9}, 1{1} \ No newline at end of file diff --git a/module/mail/tao.php b/module/mail/tao.php index b25e2222b5..7ba02ea555 100644 --- a/module/mail/tao.php +++ b/module/mail/tao.php @@ -239,7 +239,7 @@ class mailTao extends mailModel $objectModel = $this->loadModel($objectType); if(!$objectModel) return false; - if(in_array($objectType, array('epic', 'requirement', 'story', 'task', 'meeting', 'review', 'deploy'))) return $objectModel->getToAndCcList($object, $action->action); + if(in_array($objectType, array('epic', 'requirement', 'story', 'task', 'meeting', 'review', 'deploy', 'reviewissue'))) return $objectModel->getToAndCcList($object, $action->action); if(in_array($objectType, array('ticket', 'rule'))) return $objectModel->getToAndCcList($object, $action); return $objectModel->getToAndCcList($object); diff --git a/module/mail/test/tao/yaml/file_getimagesbyfileid.yaml b/module/mail/test/tao/yaml/file_getimagesbyfileid.yaml index 4f2c4a00b0..a328aa329f 100644 --- a/module/mail/test/tao/yaml/file_getimagesbyfileid.yaml +++ b/module/mail/test/tao/yaml/file_getimagesbyfileid.yaml @@ -8,47 +8,47 @@ fields: range: 1-20 prefix: "" postfix: "" - + - field: pathname note: 文件路径名 range: 202309/file1.jpg, 202309/file2.png, 202309/file3.gif, 202309/doc1.pdf, 202309/doc2.txt, 202309/image1.jpeg, 202309/image2.JPG, 202309/image3.PNG, 202309/video1.mp4, 202309/script1.js, 202309/style1.css, 202309/readme.md, []{8} - + - field: title note: 文件标题 range: 测试图片1.jpg, 测试图片2.png, 测试图片3.gif, 测试文档1.pdf, 测试文档2.txt, 测试图像1.jpeg, 测试图像2.JPG, 测试图像3.PNG, 测试视频1.mp4, 测试脚本1.js, 测试样式1.css, 说明文档.md, []{8} - + - field: extension note: 文件扩展名 range: jpg{2}, png{2}, gif{2}, jpeg{2}, pdf{3}, txt{3}, mp4{3}, js{3} - + - field: size note: 文件大小 range: 1024-102400:R - + - field: objectType note: 对象类型 range: story{10}, task{5}, bug{3}, doc{2} - + - field: objectID note: 对象ID range: 1-100:R - + - field: addedBy note: 添加者 range: admin{10}, user1{5}, user2{3}, test{2} - + - field: addedDate note: 添加时间 range: 2023-09-01 09:00:00-2023-09-30 18:00:00:5D - + - field: downloads note: 下载次数 range: 0-100:R - + - field: extra note: 额外信息 range: "", extra1, extra2, []{17} - + - field: deleted note: 是否删除 range: 0{18}, 1{2} \ No newline at end of file diff --git a/module/mark/test/model/yaml/mark_setmark.yaml b/module/mark/test/model/yaml/mark_setmark.yaml index 03c116cc39..efd800004b 100644 --- a/module/mark/test/model/yaml/mark_setmark.yaml +++ b/module/mark/test/model/yaml/mark_setmark.yaml @@ -8,31 +8,31 @@ fields: - field: id note: 标记ID range: 1-1000 - + - field: objectType note: 对象类型 range: story{10},task{10},bug{10},product{5},project{5} - + - field: objectID note: 对象ID range: 1-50 - + - field: version note: 版本号 range: 1.0{10},2.0{10},3.0{5},all{15} - + - field: account note: 用户账号 range: admin{20},user1{10},user2{10} - + - field: mark note: 标记类型 range: view{30},favorite{10} - + - field: extra note: 额外信息 range: []{30},important{5},urgent{5} - + - field: date note: 标记时间 range: (-30d)-(0d) diff --git a/module/message/model.php b/module/message/model.php index c1d83d3ebe..f7683365e7 100755 --- a/module/message/model.php +++ b/module/message/model.php @@ -274,7 +274,7 @@ class messageModel extends model if($toList == 'closed') $toList = ''; if($objectType == 'feedback' && $object->status == 'replied') $toList = ',' . $object->openedBy . ','; - if(in_array($objectType, array('story', 'epic', 'requirement', 'ticket', 'review', 'deploy', 'task', 'feedback', 'bug')) && $actionID) + if(in_array($objectType, array('story', 'epic', 'requirement', 'ticket', 'review', 'deploy', 'task', 'feedback', 'reviewissue', 'bug')) && $actionID) { $action = $this->loadModel('action')->getById($actionID); $toAndCcList = $this->loadModel($objectType)->getToAndCcList($object, $action->action); diff --git a/module/metric/calc/user/scale/count_of_assigned_reviewissue_in_user.php b/module/metric/calc/user/scale/count_of_assigned_reviewissue_in_user.php new file mode 100755 index 0000000000..97079bccb9 --- /dev/null +++ b/module/metric/calc/user/scale/count_of_assigned_reviewissue_in_user.php @@ -0,0 +1,44 @@ + + * @package + * @uses func + * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html) + * @Link https://www.zentao.net + */ +class count_of_assigned_reviewissue_in_user extends baseCalc +{ + public $dataset = 'getReviewissues'; + + public $fieldList = array('t1.status', 't1.assignedTo'); + + public $result = array(); + + public function calculate($row) + { + $assignedTo = $row->assignedTo; + + if(empty($assignedTo) || $assignedTo == 'closed' || $row->status == 'closed') return false; + + if(!isset($this->result[$assignedTo])) $this->result[$assignedTo] = 0; + $this->result[$assignedTo] += 1; + } + + public function getResult($options = array()) + { + $records = $this->getRecords(array('user', 'value')); + return $this->filterByOptions($records, $options); + } +} \ No newline at end of file diff --git a/module/metric/dataset.php b/module/metric/dataset.php index b769507d22..bfee8ca658 100644 --- a/module/metric/dataset.php +++ b/module/metric/dataset.php @@ -971,6 +971,25 @@ class dataset return $this->defaultWhere($stmt, 't2'); } + /** + * 获取评审意见数据。 + * Get reviewissues. + * + * @param string $fieldList + * @access public + * @return PDOStatement + */ + public function getReviewissues($fieldList) + { + $stmt = $this->dao->select($fieldList)->from(TABLE_REVIEWISSUE)->alias('t1') + ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id') + ->where('t1.deleted')->eq('0') + ->andWhere('t2.deleted')->eq('0') + ->andWhere('t2.type')->eq('project'); + + return $this->defaultWhere($stmt, 't2'); + } + /** * 获取问题数据。 * Get issues. diff --git a/module/metric/test/model/filtercalcbyedition.php b/module/metric/test/model/filtercalcbyedition.php index efbd8ab20e..964ff8c8ca 100755 --- a/module/metric/test/model/filtercalcbyedition.php +++ b/module/metric/test/model/filtercalcbyedition.php @@ -37,47 +37,47 @@ function createTestData($edition) global $config; $originalEdition = $config->edition; $config->edition = $edition; - + $calcInstances = array(); - + // 创建包含不同数据集的测试实例 $instance1 = new stdClass(); $instance1->dataset = 'getFeedbacks'; $calcInstances['test1'] = $instance1; - + $instance2 = new stdClass(); $instance2->dataset = 'getTickets'; $calcInstances['test2'] = $instance2; - + $instance3 = new stdClass(); $instance3->dataset = 'getUsers'; $calcInstances['test3'] = $instance3; - + $instance4 = new stdClass(); $instance4->dataset = 'getTasks'; $calcInstances['test4'] = $instance4; - + $instance5 = new stdClass(); $instance5->dataset = 'getIssues'; $calcInstances['test5'] = $instance5; - + $instance6 = new stdClass(); $instance6->dataset = 'getRisks'; $calcInstances['test6'] = $instance6; - + return $calcInstances; } function createTestDataNoDataset() { $calcInstances = array(); - + // 创建不包含数据集的测试实例 $instance1 = new stdClass(); $calcInstances['test1'] = $instance1; - + $instance2 = new stdClass(); $calcInstances['test2'] = $instance2; - + return $calcInstances; } \ No newline at end of file diff --git a/module/metric/test/model/yaml/metric_calculatemetricbycode.yaml b/module/metric/test/model/yaml/metric_calculatemetricbycode.yaml index 5a6e85a388..3bcffbc237 100644 --- a/module/metric/test/model/yaml/metric_calculatemetricbycode.yaml +++ b/module/metric/test/model/yaml/metric_calculatemetricbycode.yaml @@ -6,39 +6,39 @@ fields: - field: id note: 度量项ID range: 1-10 - + - field: purpose note: 度量目的 range: QA{2},scale{2},efficiency{2},value{2},cost{2} - + - field: scope note: 度量范围 range: system{3},product{3},project{2},execution{2} - + - field: object note: 度量对象 range: user{2},story{2},task{2},bug{2},testcase{2} - + - field: stage note: 阶段 range: released{8},wait{2} - + - field: type note: 类型 range: php{8},sql{2} - + - field: name note: 度量项名称 range: 用户数量{2},需求数量{2},任务数量{2},缺陷数量{2},测试用例数量{2} - + - field: code note: 度量项代码 range: user_count{2},story_count{2},task_count{2},bug_count{2},testcase_count{2} - + - field: unit note: 单位 range: 个{8},人{2} - + - field: dateType note: 日期类型 range: day{4},week{2},month{2},year{2} \ No newline at end of file diff --git a/module/metric/test/model/yaml/metric_updatemetricdate.yaml b/module/metric/test/model/yaml/metric_updatemetricdate.yaml index 7ef08c59e6..a7f440938c 100644 --- a/module/metric/test/model/yaml/metric_updatemetricdate.yaml +++ b/module/metric/test/model/yaml/metric_updatemetricdate.yaml @@ -8,87 +8,87 @@ fields: - field: id note: 度量项ID range: 1-10 - + - field: purpose note: 度量目的 range: quality,efficiency,cost{2} - + - field: scope note: 度量范围 range: system{5},product{3},project{2} - + - field: object note: 度量对象 range: bug{3},story{3},task{2},user{2} - + - field: stage note: 发布阶段 range: wait{7},released{3} - + - field: type note: 度量类型 range: php{6},sql{4} - + - field: name note: 度量项名称 range: '[测试度量项_,度量_]{5}' - + - field: alias note: 度量项别名 range: '[test_metric_,metric_alias_]{5}' - + - field: code note: 度量代码 range: '[test_code_,metric_code_]{5}' - + - field: unit note: 度量单位 range: '[个,次,小时,天,百分比]{2}' - + - field: dateType note: 日期类型 range: day{4},week{3},month{2},quarter{1} - + - field: collector note: 采集器配置 range: '[{"type":"sql","sql":"SELECT COUNT(*) FROM table"},{"type":"php","class":"testClass"}]{5}' - + - field: desc note: 度量项描述 range: '[这是一个测试度量项,用于测试的度量项描述]{5}' - + - field: definition note: 度量定义 range: '[该度量项用于计算,测试度量项的定义说明]{5}' - + - field: when note: 计算时机 range: '[daily,weekly,monthly]{3.3}' - + - field: event note: 触发事件 range: '[create,update,delete]{3.3}' - + - field: cronCFG note: 定时任务配置 range: '[0 0 * * *,0 9 * * 1,0 0 1 * *]{3.3}' - + - field: time note: 执行时间 range: '[morning,afternoon,evening]{3.3}' - + - field: createdBy note: 创建人 range: admin{3},user1{2},user2{2},user3{1},testuser{2} - + - field: createdDate note: 创建日期 range: '[]{5},2023-01-01 10:00:00{2},2023-02-15 14:30:00{2},2023-03-20 09:15:00{1}' - + - field: editedBy note: 最后编辑人 range: admin{4},user1{3},user2{2},testuser{1} - + - field: editedDate note: 最后编辑时间 range: '2023-01-15 10:00:00{3},2023-02-20 14:30:00{3},2023-03-25 09:15:00{4}' \ No newline at end of file diff --git a/module/metric/test/model/yaml/metriclib_iscalcbycron.yaml b/module/metric/test/model/yaml/metriclib_iscalcbycron.yaml index bc5ff24824..191aaa164f 100644 --- a/module/metric/test/model/yaml/metriclib_iscalcbycron.yaml +++ b/module/metric/test/model/yaml/metriclib_iscalcbycron.yaml @@ -6,64 +6,64 @@ version: "1.0" fields: - field: id range: 1-6 - + - field: metricID range: 0{6} - + - field: metricCode range: test_metric_year{2}, test_metric_month{2}, test_metric_day{1}, test_metric_week{1} - + - field: system range: 0{6} - + - field: program range: 0{6} - + - field: project range: 0{6} - + - field: product range: 0{6} - + - field: execution range: 0{6} - + - field: code range: ""{6} - + - field: pipeline range: ""{6} - + - field: repo range: ""{6} - + - field: user range: ""{6} - + - field: dept range: ""{6} - + - field: calcType range: cron{6} - + - field: calculatedBy range: ""{6} - + - field: value range: 10-60:10 - + - field: year range: 2024{6} - + - field: month range: 01{2}, 02{2}, 03{2} - + - field: day range: 01{2}, 15{2}, 20{2} - + - field: week range: 1{2}, 6{2}, 12{2} - + - field: date range: "20240101 100000, 20240115 110000, 20240201 100000, 20240215 110000, 20240320 100000, 20240321 110000" type: timestamp diff --git a/module/misc/lang/de.php b/module/misc/lang/de.php index f0c73ecdf7..81f7c385c2 100644 --- a/module/misc/lang/de.php +++ b/module/misc/lang/de.php @@ -10,18 +10,18 @@ * @link https://www.zentao.net */ $lang->misc = new stdclass(); -$lang->misc->common = 'Sonstiges'; +$lang->misc->common = 'Misc'; $lang->misc->ping = 'Ping'; $lang->misc->view = 'Check'; $lang->misc->cancel = 'Cancel'; $lang->misc->zentao = new stdclass(); $lang->misc->zentao->version = 'ALM Version %s'; -$lang->misc->zentao->labels['about'] = 'Über'; -$lang->misc->zentao->labels['support'] = 'Support'; -$lang->misc->zentao->labels['cowin'] = 'Hilf uns'; +$lang->misc->zentao->labels['about'] = 'About ZenTao'; +$lang->misc->zentao->labels['support'] = 'Tech Support'; +$lang->misc->zentao->labels['cowin'] = 'Help Us'; $lang->misc->zentao->labels['service'] = 'Service'; -$lang->misc->zentao->labels['others'] = 'Others'; +$lang->misc->zentao->labels['others'] = 'From ZenTao Software'; $lang->misc->zentao->icons['about'] = 'group'; $lang->misc->zentao->icons['support'] = 'question-sign'; @@ -29,14 +29,14 @@ $lang->misc->zentao->icons['cowin'] = 'hand-right'; $lang->misc->zentao->icons['service'] = 'heart'; $lang->misc->zentao->about['bizversion'] = 'ZenTao Biz'; -$lang->misc->zentao->about['official'] = "Offizielle Webseite"; -$lang->misc->zentao->about['changelog'] = "Change Log"; -$lang->misc->zentao->about['license'] = "Lizenz"; +$lang->misc->zentao->about['official'] = "Official Website"; +$lang->misc->zentao->about['changelog'] = "Revision Log"; +$lang->misc->zentao->about['license'] = "License"; $lang->misc->zentao->about['extension'] = "Plugin Platform"; $lang->misc->zentao->about['follow'] = "Follow Us"; -$lang->misc->zentao->support['vip'] = "VIP Technischer Support"; -$lang->misc->zentao->support['manual'] = "Benutzer Handbuch"; +$lang->misc->zentao->support['vip'] = "VIP Support"; +$lang->misc->zentao->support['manual'] = "User Manual"; $lang->misc->zentao->support['faq'] = "Common Problem"; $lang->misc->zentao->support['ask'] = "Official Answer"; $lang->misc->zentao->support['video'] = "Use Video"; @@ -57,20 +57,20 @@ $lang->misc->zentao->others['zdoo'] = "misc->zentao->others['meshiot' ] = " MeshioT"; -$lang->misc->mobile = "Mobiler Zugriff"; -$lang->misc->noGDLib = "Bitte benutzen Sie den Browser Ihres Telefons um die Seite %s aufzurufen"; -$lang->misc->copyright = "© 2009 - " . date('Y') . " Nature Easy Soft Network Technology Co,LTD Email Max@easysoft.ltd"; -$lang->misc->checkTable = "Prüfe Datentabellen"; -$lang->misc->needRepair = "Repariere Datentabellen"; -$lang->misc->repairTable = "Datenbank íst beschädigt. Bitte brüfen und reparieren!"; -$lang->misc->repairFail = "Reparatur fehlgeschlagen. Bitte wechseln Sie in das Verzeichnis der Datenbank, und versuchen Sie folgenden Befehl myisamchk -r -f %s.MYI zum reparieren der DB."; -$lang->misc->withoutCmd = 'Reparatur fehlgeschlagen.'; +$lang->misc->mobile = "Mobile Access"; +$lang->misc->noGDLib = "Please visit %s in the browser of your phone."; +$lang->misc->copyright = "© 2009 - " . date('Y') . " ZenTao Software Email Renee@easycorp.ltd"; +$lang->misc->checkTable = "Check Data Table"; +$lang->misc->needRepair = "Repair Table"; +$lang->misc->repairTable = "Database table might be damaged due to power outage. Please check and repair!"; +$lang->misc->repairFail = "Failed to repair. Please go to the data directory of your database, and try to execute myisamchk -r -f %s.MYI repair."; +$lang->misc->withoutCmd = 'Failed to repair.'; $lang->misc->connectFail = "Failed to connect to the database. Error: %s,
    Please check the MySQL error log and troubleshoot."; -$lang->misc->tableName = "Tabellenname"; +$lang->misc->tableName = "Table Name"; $lang->misc->tableStatus = "Status"; -$lang->misc->novice = "Erstes mal bei ZenTao? Möchten Sie den Beginner Modus starten?"; -$lang->misc->showAnnual = 'Add annual summary'; -$lang->misc->annualDesc = 'After version 12.0, the new annual report function can be viewed on the 『Report->Annual Summary』 page. See now.'; +$lang->misc->novice = "New to ZenTao? Do you want to start ZenTao Tutorial?"; +$lang->misc->showAnnual = 'Add Annual Summary'; +$lang->misc->annualDesc = 'After version 12.0, Annual Summary can be viewed on 『Report->Annual Summary』 page. See now.'; $lang->misc->remind = 'New feature reminders'; $lang->misc->expiredTipsTitle = 'Dear system administrator, hello:'; @@ -78,6 +78,7 @@ $lang->misc->expiredCountTips = 'There are System->Metric Library" page.'; $lang->misc->noticeRepair = "
    If you are not Administrator, contact your ZenTao Administrator to repair tables.
    If you are the Administrator, login your ZenTao host and create a file named %s.
    @@ -88,8 +89,8 @@ $lang->misc->noticeRepair = "
    If you are not Administrator, contact your ZenT "; $lang->misc->feature = new stdclass(); -$lang->misc->feature->lastest = 'Letzte Version'; -$lang->misc->feature->detailed = 'Details'; +$lang->misc->feature->lastest = 'Latest Version'; +$lang->misc->feature->detailed = 'Detail'; $lang->misc->feature->introduction = 'Features'; $lang->misc->feature->tutorial = 'Tutorial'; $lang->misc->feature->tutorialImage = 'theme/default/images/main/tutorial_en.png'; @@ -114,7 +115,11 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +<<<<<<< HEAD $lang->misc->releaseDate['21.7.8'] = '2025-12-15'; +======= +$lang->misc->releaseDate['21.7.8'] = '2025-12-30'; +>>>>>>> feature/workflowgroup $lang->misc->releaseDate['21.7.7'] = '2025-10-29'; $lang->misc->releaseDate['21.7.6'] = '2025-09-29'; $lang->misc->releaseDate['21.7.5'] = '2025-09-11'; @@ -354,13 +359,13 @@ $lang->misc->feature->all['12.3.3'][] = array('title'=>'Fix Bug', 'desc' => ''); $lang->misc->feature->all['12.3.2'][] = array('title'=>'Fix workflow', 'desc' => ''); $lang->misc->feature->all['12.3.1'][] = array('title'=>'Fix bugs of high severity.', 'desc' => ''); $lang->misc->feature->all['12.3'][] = array('title'=>'Integrate unit test, open the continuous integration closed-loop.', 'desc' => ''); -$lang->misc->feature->all['12.2'][] = array('title'=>'Add parent-child story, compatible xuanxuan.', 'desc' => ''); +$lang->misc->feature->all['12.2'][] = array('title'=>'Add parent-child story, compatible Xuanxuan IM.', 'desc' => ''); $lang->misc->feature->all['12.1'][] = array('title'=>'Add Integration.', 'desc' => '

    Add integration, and build in Jenkins

    '); $lang->misc->feature->all['12.0.1'][] = array('title'=>'Fix Bug.', 'desc' => ''); $lang->misc->feature->all['12.0'][] = array('title'=>'Move repo function to zentao', 'desc' => ''); -$lang->misc->feature->all['12.0'][] = array('title'=>'Move repo function to zentao', 'desc' => ''); -$lang->misc->feature->all['12.0'][] = array('title'=>'Move repo function to zentao', 'desc' => ''); +$lang->misc->feature->all['12.0'][] = array('title'=>'Add annual summary', 'desc' => 'Show annual summary by role.'); +$lang->misc->feature->all['12.0'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.7'][] = array('title'=>'Optimize details and fix bug.', 'desc' => '

    Added choices for users to choose agile or not.

    Added WeChat Enterprise to the types of webhook

    Added the notifier of Dingding personal messages

    '); $lang->misc->feature->all['11.6.5'][] = array('title'=>'Fix bug.', 'desc' => ''); @@ -370,18 +375,18 @@ $lang->misc->feature->all['11.6.2'][] = array('title'=>'Optimize details and fix $lang->misc->feature->all['11.6.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'Improving the International Edition Interface', 'desc' => ''); -$lang->misc->feature->all['11.6.stable'][] = array('title'=>'Improving the International Edition Interface', 'desc' => ''); +$lang->misc->feature->all['11.6.stable'][] = array('title'=>'Add translate function', 'desc' => ''); $lang->misc->feature->all['11.5.2'][] = array('title'=>'Increase the security of ZenTao and increase the login password for weak password check', 'desc' => ''); $lang->misc->feature->all['11.5.1'][] = array('title'=>'Add a third-party authentication and fix bugs.', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); -$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); -$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Added filters to Dynamics', 'desc' => ''); +$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Integrated the latest ZenTao Desktop', 'desc' => ''); $lang->misc->feature->all['11.4.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); -$lang->misc->feature->all["11.4.stable"][] = array('title'=>'Optimize details and fix bug.', 'desc' => '

    Enhanced test management.

    Optimized the UI of Plan, Release, and Build linked stories and bugs.

    Customize the feature whether to display files in child category.

    Optimize details and fix bug.

    '); +$lang->misc->feature->all["11.4.stable"][] = array('title'=>'Optimize details and fix bug.', 'desc' => "

    Enhanced test management.

    Optimized the UI of Plan, Release, and Build linked stories and bugs.

    Customize the feature whether to display files in child category.

    Optimize details and fix bug.

    "); $lang->misc->feature->all['11.3.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => '

    Add Child Plan to a Plan

    Optimize the chosen

    Add Timezone settings

    Optimize Document Library and Document modules

    '); @@ -389,64 +394,64 @@ $lang->misc->feature->all['11.2.stable'][] = array('title'=>'Optimize details an $lang->misc->feature->all['11.1.stable'][] = array('title'=>'Fix Bug.', 'desc' => ''); -$lang->misc->feature->all['11.0.stable'][] = array('title'=>'ZenTao integrate Xuanxuan', 'desc' => ''); +$lang->misc->feature->all['11.0.stable'][] = array('title'=>'ZenTao integrated desktop.', 'desc' => ''); $lang->misc->feature->all['10.6.stable'][] = array('title'=>'Adjust backup mechanism', 'desc' => '

    Increase backup settings and make backup more flexible

    Show backup progress

    Change the backup directory

    '); -$lang->misc->feature->all['10.6.stable'][] = array('title'=>'Adjust backup mechanism', 'desc' => '

    Increase backup settings and make backup more flexible

    Show backup progress

    Change the backup directory

    '); +$lang->misc->feature->all['10.6.stable'][] = array('title'=>'Optimize and adjust menu', 'desc' => '

    Adjust admin menu

    Adjust the secondary menu of My and Project

    '); $lang->misc->feature->all['10.5.stable'][] = array('title'=>'Adjust document layout', 'desc' => "

    Adjust the layout method on the left side of the document library.

    Add filter conditions at the bottom of the document library menu.

    "); -$lang->misc->feature->all['10.5.stable'][] = array('title'=>'Adjust document layout', 'desc' => "

    Adjust the layout method on the left side of the document library.

    Add filter conditions at the bottom of the document library menu.

    "); +$lang->misc->feature->all['10.5.stable'][] = array('title'=>'Adjust the child task logic and optimize the display of parent-child task.', 'desc' => ''); $lang->misc->feature->all['10.4.stable'][] = array('title'=>'Optimize and adjust new interface', 'desc' => '

    Detail page restore to the previous layout.

    Refactore forms to add user pages

    When use cases are executed, do not update the use case stause if the user manually chooses to pass and write the results.

    '); -$lang->misc->feature->all['10.4.stable'][] = array('title'=>'Optimize and adjust new interface', 'desc' => '

    Detail page restore to the previous layout.

    Refactore forms to add user pages

    When use cases are executed, do not update the use case stause if the user manually chooses to pass and write the results.

    '); -$lang->misc->feature->all['10.4.stable'][] = array('title'=>'Optimize and adjust new interface', 'desc' => '

    Detail page restore to the previous layout.

    Refactore forms to add user pages

    When use cases are executed, do not update the use case stause if the user manually chooses to pass and write the results.

    '); +$lang->misc->feature->all['10.4.stable'][] = array('title'=>'After the user machine hibernates and the login fails, the session will be refreshed again.', 'desc' => ''); +$lang->misc->feature->all['10.4.stable'][] = array('title'=>'Upgrade existing interface mechanisms', 'desc' => ''); $lang->misc->feature->all['10.3.stable'][] = array('title'=>'Fix Bug.', 'desc' => ''); -$lang->misc->feature->all['10.2.stable'][] = array('title'=>'Xuan.im is integrated!', 'desc' => ''); +$lang->misc->feature->all['10.2.stable'][] = array('title'=>'ZenTao desktop is integrated!', 'desc' => ''); -$lang->misc->feature->all['10.0.stable'][] = array('title'=>'New UI/UX and new experience', 'desc' => '
    1. My Dashboard
    2. Dynamics
    3. Product Home
    4. Product Overview
    5. Roadmap
    6. Project Home
    7. Project overview
    8. QA Home
    9. Document Home
    10. Added work report on My Dashboard
    11. Add/Edit/Finish todos on My Dashboard
    12. Add prodcut report on Product Home
    13. Add prodcut overview on Product Home
    14. Add project report on Project Home
    15. Add project overview on Project Home
    16. Add Testing report on QA Home
    17. All Product/product Home/All Project/Project Home/QA Home is moved from the right of the secondary Navbar to the left.
    18. Kanban/Burndown/Tree/ByGroup of Project/Task has been moved from the third Navbar to the secondary one; Tree/ByGroup/Task List has been integrated to a drop-down.
    19. Bug/Build of Project on the secondary Navbar has been integrated to a drop-down.
    20. Display build and list by group, which is more reasonable.
    21. Added tree to display document on the left of the page.
    22. Added quick entry to document, including Last Update, My Doc and My Favorite
    23. Added My Favorite to Doc module.
    '); +$lang->misc->feature->all['10.0.stable'][] = array('title'=>'New UI/UX and new experience', 'desc' => '
    1. My Dashboard
    2. Dynamics
    3. Product Home
    4. Product Overview
    5. Roadmap
    6. Project Home
    7. Project overview
    8. Test Home
    9. Document Home
    10. Added work report on My Dashboard
    11. Add/Edit/Finish todos on My Dashboard
    12. Add prodcut report on Product Home
    13. Add prodcut overview on Product Home
    14. Add project report on Project Home
    15. Add project overview on Project Home
    16. Add Testing report on Test Home
    17. All Product/product Home/All Project/Project Home/Test Home is moved from the right of the secondary Navbar to the left.
    18. Kanban/Burndown/Tree/ByGroup of Project/Task has been moved from the third Navbar to the secondary one; Tree/ByGroup/Task List has been integrated to a drop-down.
    19. Bug/Build of Project on the secondary Navbar has been integrated to a drop-down.
    20. Display build and list by group, which is more reasonable.
    21. Added tree to display document on the left of the page.
    22. Added quick entry to document, including Last Update, My Doc and My Favorite
    23. Added My Favorite to Doc module.
    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Centring Mail,SMS,webhook into Message

    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Centring Mail,SMS,webhook into Message

    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Centring Mail,SMS,webhook into Message

    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Centring Mail,SMS,webhook into Message

    '); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Gather Mail,SMS,webhook into Message

    '); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Add recurred Todo', 'desc' => ''); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>"Add Block of 'AssignedToMe'", 'desc' => ''); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Support generating reports on Test Builds', 'desc' => ''); -$lang->misc->feature->all['9.7.stable'][] = array('title'=>'optimize International package,Added Demo data。', 'desc' => ''); +$lang->misc->feature->all['9.7.stable'][] = array('title'=>'Optimize International package. Added Demo data.', 'desc' => ''); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'added Webhook Interface feature', 'desc' => 'support communication with BearyChat,dingding'); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'added Webhook Interface feature', 'desc' => 'support communication with BearyChat,dingding'); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'added Webhook Interface feature', 'desc' => 'support communication with BearyChat,dingding'); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'added Webhook Interface feature', 'desc' => 'support communication with BearyChat,dingding'); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Webhook Interface feature', 'desc' => 'Support communication with BearyChat, Dingding'); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Point', 'desc' => 'More skilled application, more score'); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added multiple user task and child tasks to Project', 'desc' => ''); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Product line management to Product', 'desc' => ''); -$lang->misc->feature->all['9.5.1'][] = array('title'=>'added Restricted Operatio', 'desc' => ''); +$lang->misc->feature->all['9.5.1'][] = array('title'=>'Added restricted actions.', 'desc' => ''); -$lang->misc->feature->all['9.3.beta'][] = array('title'=>'upgraded framework,Enhanced security', 'desc' => ''); +$lang->misc->feature->all['9.3.beta'][] = array('title'=>'Upgraded ZenTao framework,enhanced security', 'desc' => ''); -$lang->misc->feature->all['9.1.stable'][] = array('title'=>'optimize Test View', 'desc' => '

    added TestSuite,CaseLib and Test Statements

    '); -$lang->misc->feature->all['9.1.stable'][] = array('title'=>'optimize Test View', 'desc' => '

    added TestSuite,CaseLib and Test Statements

    '); +$lang->misc->feature->all['9.1.stable'][] = array('title'=>'optimize Test View', 'desc' => '

    Added TestSuite, CaseLibrary and QA Report

    '); +$lang->misc->feature->all['9.1.stable'][] = array('title'=>'Support Group steps of TestCase', 'desc' => ''); $lang->misc->feature->all['9.0.beta'][] = array('title'=>'ZenTao CloudMail has been added.', 'desc' => '

    ZenTao CloudMail is a free Email service launched jointly with SendCloud. Once binded with ZenTao and passed verification, users can use this service.

    '); -$lang->misc->feature->all['9.0.beta'][] = array('title'=>'ZenTao CloudMail has been added.', 'desc' => '

    ZenTao CloudMail is a free Email service launched jointly with SendCloud. Once binded with ZenTao and passed verification, users can use this service.

    '); +$lang->misc->feature->all['9.0.beta'][] = array('title'=>'Optimized Rich Text Editor and Markdown Editor.', 'desc' => ''); $lang->misc->feature->all['8.3.stable'][] = array('title'=>'Improved Documentation.', 'desc' => '

    Added Document Home, restructured document library, and added privileges.

    Markdown Editor is supported,and privilege and version managment is added.

    '); $lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Navigation', 'desc' => '

    You can decide which project show in navigation bar and the order of projects shown in the bar.

    Hover on the navigation bar and a sign will show to its right. Click the sign and a dialog box "Custom Navigation" will show. Drag the block name to switch its order on navigation bar.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Batch Add/Edit Custom', 'desc' => '

    You can batch add and edit fields on custom pages.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Story/Task/Bug/Case', 'desc' => '

    You can custom fileds when add a Story/Task/Bug/Case.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Export', 'desc' => '

    You can custom fileds when export a Story/Task/Bug/Case pages. You can also save it as template for next export.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Story/Task/Bug/Case Search ', 'desc' => '

    On Story/Task/Bug/Case List page, you can do a combined search on Modules and Tabs.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>"ZenTao Tutorial", 'desc' => '

    Tutorial for rookies is added for first-time users to know how to use ZenTao.

    '); $lang->misc->feature->all['7.4.beta'][] = array('title'=>'Product branch feature is added.', 'desc' => '

    Product branch/platform is added, and its related Story/Plan/Bug/Case/Module has Branch added too.

    '); -$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Product branch feature is added.', 'desc' => '

    Product branch/platform is added, and its related Story/Plan/Bug/Case/Module has Branch added too.

    '); -$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Product branch feature is added.', 'desc' => '

    Product branch/platform is added, and its related Story/Plan/Bug/Case/Module has Branch added too.

    '); +$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Release Module is improved.', 'desc' => '

    Stop action has been added. If Stop to manage it, the Release will not show when Report Bug.

    Bugs that have been omitted in the Release will be related manually.

    '); +$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Create pages of Story and Bug are improved.', 'desc' => ''); $lang->misc->feature->all['7.2.stable'][] = array('title'=>'Security Enhanced', 'desc' => '

    Admin weak passwork check is enhanced.

    ok file is required when code/upload an extension.

    Sensitive action requires Admin password.

    Do striptags, specialchars to content entered in ZenTao.

    '); -$lang->misc->feature->all['7.2.stable'][] = array('title'=>'Security Enhanced', 'desc' => '

    Admin weak passwork check is enhanced.

    ok file is required when code/upload an extension.

    Sensitive action requires Admin password.

    Do striptags, specialchars to content entered in ZenTao.

    '); +$lang->misc->feature->all['7.2.stable'][] = array('title'=>'Details Improved', 'desc' => ''); -$lang->misc->feature->all['7.1.stable'][] = array('title'=>'Framework of scheduled tasks is added.', 'desc' => 'Framework of scheduled tasks is added. Daily notification, Burndown Update, Backup, Send Email and so on have been added.'); -$lang->misc->feature->all['7.1.stable'][] = array('title'=>'Framework of scheduled tasks is added.', 'desc' => 'Framework of scheduled tasks is added. Daily notification, Burndown Update, Backup, Send Email and so on have been added.'); +$lang->misc->feature->all['7.1.stable'][] = array('title'=>'Framework of Cron is added.', 'desc' => 'Framework of Cron is added. Daily notification, Burndown Update, Backup, Send Email and so on have been added.'); +$lang->misc->feature->all['7.1.stable'][] = array('title'=>'rpm and deb packages are provided.', 'desc' => ''); $lang->misc->feature->all['6.3.stable'][] = array('title'=>'Data table is added.', 'desc' => '

    Fields can be customized in data table and data will be displayed according to customized fields.

    '); -$lang->misc->feature->all['6.3.stable'][] = array('title'=>'Data table is added.', 'desc' => '

    Fields can be customized in data table and data will be displayed according to customized fields.

    '); +$lang->misc->feature->all['6.3.stable'][] = array('title'=>'Continue improving details', 'desc' => ''); diff --git a/module/misc/lang/en.php b/module/misc/lang/en.php index 6514603f2d..81f7c385c2 100644 --- a/module/misc/lang/en.php +++ b/module/misc/lang/en.php @@ -115,7 +115,11 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +<<<<<<< HEAD $lang->misc->releaseDate['21.7.8'] = '2025-12-15'; +======= +$lang->misc->releaseDate['21.7.8'] = '2025-12-30'; +>>>>>>> feature/workflowgroup $lang->misc->releaseDate['21.7.7'] = '2025-10-29'; $lang->misc->releaseDate['21.7.6'] = '2025-09-29'; $lang->misc->releaseDate['21.7.5'] = '2025-09-11'; diff --git a/module/misc/lang/fr.php b/module/misc/lang/fr.php index 82f61e01c6..81f7c385c2 100644 --- a/module/misc/lang/fr.php +++ b/module/misc/lang/fr.php @@ -17,11 +17,11 @@ $lang->misc->cancel = 'Cancel'; $lang->misc->zentao = new stdclass(); $lang->misc->zentao->version = 'ALM Version %s'; -$lang->misc->zentao->labels['about'] = 'A Propos'; -$lang->misc->zentao->labels['support'] = 'Support Technique'; -$lang->misc->zentao->labels['cowin'] = 'Aider ZenTao'; -$lang->misc->zentao->labels['service'] = 'Services'; -$lang->misc->zentao->labels['others'] = 'Autres Produits'; +$lang->misc->zentao->labels['about'] = 'About ZenTao'; +$lang->misc->zentao->labels['support'] = 'Tech Support'; +$lang->misc->zentao->labels['cowin'] = 'Help Us'; +$lang->misc->zentao->labels['service'] = 'Service'; +$lang->misc->zentao->labels['others'] = 'From ZenTao Software'; $lang->misc->zentao->icons['about'] = 'group'; $lang->misc->zentao->icons['support'] = 'question-sign'; @@ -29,14 +29,14 @@ $lang->misc->zentao->icons['cowin'] = 'hand-right'; $lang->misc->zentao->icons['service'] = 'heart'; $lang->misc->zentao->about['bizversion'] = 'ZenTao Biz'; -$lang->misc->zentao->about['official'] = "Site Officiel"; -$lang->misc->zentao->about['changelog'] = "Change Log"; -$lang->misc->zentao->about['license'] = "Licence"; +$lang->misc->zentao->about['official'] = "Official Website"; +$lang->misc->zentao->about['changelog'] = "Revision Log"; +$lang->misc->zentao->about['license'] = "License"; $lang->misc->zentao->about['extension'] = "Plugin Platform"; $lang->misc->zentao->about['follow'] = "Follow Us"; -$lang->misc->zentao->support['vip'] = "Support VIP"; -$lang->misc->zentao->support['manual'] = "English User Manual"; +$lang->misc->zentao->support['vip'] = "VIP Support"; +$lang->misc->zentao->support['manual'] = "User Manual"; $lang->misc->zentao->support['faq'] = "Common Problem"; $lang->misc->zentao->support['ask'] = "Official Answer"; $lang->misc->zentao->support['video'] = "Use Video"; @@ -44,7 +44,7 @@ $lang->misc->zentao->support['qqgroup'] = "Official QQ Group"; $lang->misc->zentao->cowin['reportbug'] = "Report Bug"; $lang->misc->zentao->cowin['feedback'] = "Feedback"; -$lang->misc->zentao->cowin['recommend'] = "Plus..."; +$lang->misc->zentao->cowin['recommend'] = "More"; $lang->misc->zentao->service['zentaotrain'] = 'ZenTao Training'; $lang->misc->zentao->service['idc'] = 'ZenTao Cloud'; @@ -57,27 +57,28 @@ $lang->misc->zentao->others['zdoo'] = "misc->zentao->others['meshiot' ] = " MeshioT"; -$lang->misc->mobile = "Accès Mobile"; -$lang->misc->noGDLib = "SVP visitez %s via le navigateur de votre smartphone."; -$lang->misc->copyright = "© 2009 - " . date('Y') . " Nature Easy Soft Email support@zentaoalm.com"; -$lang->misc->checkTable = "Vérifiez les Données de la Table"; -$lang->misc->needRepair = "Réparer la Table"; -$lang->misc->repairTable = "La Base de Données est peut-être endommagée. Elle a besoin d'être vérifiée et éventuellement réparée !"; -$lang->misc->repairFail = "La réparation a échoué. Allez dans le répertoire de données de votre table et essayez d'exécuter myisamchk -r -f %s.MYI pour la réparer. Mais on ne promet rien :-("; -$lang->misc->withoutCmd = 'La réparation a échoué.'; -$lang->misc->connectFail = "Echec de connexion à la Base de données. Erreur: %s,
    Vérifiez la log des erreurs MySQL et des dysfonctionnements."; -$lang->misc->tableName = "Nom de la Table"; -$lang->misc->tableStatus = "Statut"; -$lang->misc->novice = "Nouveau dans ZenTao ? Voulez-vous commencer le Tutoriel ?"; -$lang->misc->showAnnual = 'Ajouter un résumé annuel'; -$lang->misc->annualDesc = 'A partir de la version 12.0, la nouvelle fonction de rapport annuel peut être consultée sur la page 『Report->Annual Summary』. Consulter Maintenant.'; -$lang->misc->remind = 'Rappels des nouvelles fonctionnalités'; +$lang->misc->mobile = "Mobile Access"; +$lang->misc->noGDLib = "Please visit %s in the browser of your phone."; +$lang->misc->copyright = "© 2009 - " . date('Y') . " ZenTao Software Email Renee@easycorp.ltd"; +$lang->misc->checkTable = "Check Data Table"; +$lang->misc->needRepair = "Repair Table"; +$lang->misc->repairTable = "Database table might be damaged due to power outage. Please check and repair!"; +$lang->misc->repairFail = "Failed to repair. Please go to the data directory of your database, and try to execute myisamchk -r -f %s.MYI repair."; +$lang->misc->withoutCmd = 'Failed to repair.'; +$lang->misc->connectFail = "Failed to connect to the database. Error: %s,
    Please check the MySQL error log and troubleshoot."; +$lang->misc->tableName = "Table Name"; +$lang->misc->tableStatus = "Status"; +$lang->misc->novice = "New to ZenTao? Do you want to start ZenTao Tutorial?"; +$lang->misc->showAnnual = 'Add Annual Summary'; +$lang->misc->annualDesc = 'After version 12.0, Annual Summary can be viewed on 『Report->Annual Summary』 page. See now.'; +$lang->misc->remind = 'New feature reminders'; $lang->misc->expiredTipsTitle = 'Dear system administrator, hello:'; $lang->misc->expiredCountTips = 'There are %s plug-ins in the system that will expire soon. To avoid affecting your regular use, please get in touch with the administrator to renew or uninstall them in time.'; $lang->misc->expiredPluginTips = 'Expired plugins are: %s. '; $lang->misc->expiringPluginTips = 'The plug-ins that will expire are: %s.'; $lang->misc->expiredTipsForAdmin = 'There are %s plug-ins in the current system that will expire soon. To avoid affecting the regular use of the function, please renew or uninstall them in the system background plug-in management as soon as possible.'; +$lang->misc->metriclibTips = 'Added a new metric library indexing function, which can significantly improve the query speed of related metrics after updating the index. You can update it on the "Admin->System->Metric Library" page.'; $lang->misc->noticeRepair = "
    If you are not Administrator, contact your ZenTao Administrator to repair tables.
    If you are the Administrator, login your ZenTao host and create a file named %s.
    @@ -88,8 +89,8 @@ $lang->misc->noticeRepair = "
    If you are not Administrator, contact your ZenT "; $lang->misc->feature = new stdclass(); -$lang->misc->feature->lastest = 'Dernière Version'; -$lang->misc->feature->detailed = 'Détail'; +$lang->misc->feature->lastest = 'Latest Version'; +$lang->misc->feature->detailed = 'Detail'; $lang->misc->feature->introduction = 'Features'; $lang->misc->feature->tutorial = 'Tutorial'; $lang->misc->feature->tutorialImage = 'theme/default/images/main/tutorial_en.png'; @@ -114,7 +115,11 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +<<<<<<< HEAD $lang->misc->releaseDate['21.7.8'] = '2025-12-15'; +======= +$lang->misc->releaseDate['21.7.8'] = '2025-12-30'; +>>>>>>> feature/workflowgroup $lang->misc->releaseDate['21.7.7'] = '2025-10-29'; $lang->misc->releaseDate['21.7.6'] = '2025-09-29'; $lang->misc->releaseDate['21.7.5'] = '2025-09-11'; @@ -354,13 +359,13 @@ $lang->misc->feature->all['12.3.3'][] = array('title'=>'Fix Bug', 'desc' => ''); $lang->misc->feature->all['12.3.2'][] = array('title'=>'Fix workflow', 'desc' => ''); $lang->misc->feature->all['12.3.1'][] = array('title'=>'Fix bugs of high severity.', 'desc' => ''); $lang->misc->feature->all['12.3'][] = array('title'=>'Integrate unit test, open the continuous integration closed-loop.', 'desc' => ''); -$lang->misc->feature->all['12.2'][] = array('title'=>'Add parent-child story, compatible xuanxuan.', 'desc' => ''); +$lang->misc->feature->all['12.2'][] = array('title'=>'Add parent-child story, compatible Xuanxuan IM.', 'desc' => ''); $lang->misc->feature->all['12.1'][] = array('title'=>'Add Integration.', 'desc' => '

    Add integration, and build in Jenkins

    '); $lang->misc->feature->all['12.0.1'][] = array('title'=>'Fix Bug.', 'desc' => ''); $lang->misc->feature->all['12.0'][] = array('title'=>'Move repo function to zentao', 'desc' => ''); -$lang->misc->feature->all['12.0'][] = array('title'=>'Move repo function to zentao', 'desc' => ''); -$lang->misc->feature->all['12.0'][] = array('title'=>'Move repo function to zentao', 'desc' => ''); +$lang->misc->feature->all['12.0'][] = array('title'=>'Add annual summary', 'desc' => 'Show annual summary by role.'); +$lang->misc->feature->all['12.0'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.7'][] = array('title'=>'Optimize details and fix bug.', 'desc' => '

    Added choices for users to choose agile or not.

    Added WeChat Enterprise to the types of webhook

    Added the notifier of Dingding personal messages

    '); $lang->misc->feature->all['11.6.5'][] = array('title'=>'Fix bug.', 'desc' => ''); @@ -370,14 +375,14 @@ $lang->misc->feature->all['11.6.2'][] = array('title'=>'Optimize details and fix $lang->misc->feature->all['11.6.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); $lang->misc->feature->all['11.6.stable'][] = array('title'=>'Improving the International Edition Interface', 'desc' => ''); -$lang->misc->feature->all['11.6.stable'][] = array('title'=>'Improving the International Edition Interface', 'desc' => ''); +$lang->misc->feature->all['11.6.stable'][] = array('title'=>'Add translate function', 'desc' => ''); $lang->misc->feature->all['11.5.2'][] = array('title'=>'Increase the security of ZenTao and increase the login password for weak password check', 'desc' => ''); $lang->misc->feature->all['11.5.1'][] = array('title'=>'Add a third-party authentication and fix bugs.', 'desc' => ''); $lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); -$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); -$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); +$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Added filters to Dynamics', 'desc' => ''); +$lang->misc->feature->all['11.5.stable'][] = array('title'=>'Integrated the latest ZenTao Desktop', 'desc' => ''); $lang->misc->feature->all['11.4.1'][] = array('title'=>'Optimize details and fix bug.', 'desc' => ''); @@ -392,61 +397,61 @@ $lang->misc->feature->all['11.1.stable'][] = array('title'=>'Fix Bug.', 'desc' = $lang->misc->feature->all['11.0.stable'][] = array('title'=>'ZenTao integrated desktop.', 'desc' => ''); $lang->misc->feature->all['10.6.stable'][] = array('title'=>'Adjust backup mechanism', 'desc' => '

    Increase backup settings and make backup more flexible

    Show backup progress

    Change the backup directory

    '); -$lang->misc->feature->all['10.6.stable'][] = array('title'=>'Adjust backup mechanism', 'desc' => '

    Increase backup settings and make backup more flexible

    Show backup progress

    Change the backup directory

    '); +$lang->misc->feature->all['10.6.stable'][] = array('title'=>'Optimize and adjust menu', 'desc' => '

    Adjust admin menu

    Adjust the secondary menu of My and Project

    '); $lang->misc->feature->all['10.5.stable'][] = array('title'=>'Adjust document layout', 'desc' => "

    Adjust the layout method on the left side of the document library.

    Add filter conditions at the bottom of the document library menu.

    "); -$lang->misc->feature->all['10.5.stable'][] = array('title'=>'Adjust document layout', 'desc' => "

    Adjust the layout method on the left side of the document library.

    Add filter conditions at the bottom of the document library menu.

    "); +$lang->misc->feature->all['10.5.stable'][] = array('title'=>'Adjust the child task logic and optimize the display of parent-child task.', 'desc' => ''); $lang->misc->feature->all['10.4.stable'][] = array('title'=>'Optimize and adjust new interface', 'desc' => '

    Detail page restore to the previous layout.

    Refactore forms to add user pages

    When use cases are executed, do not update the use case stause if the user manually chooses to pass and write the results.

    '); -$lang->misc->feature->all['10.4.stable'][] = array('title'=>'Optimize and adjust new interface', 'desc' => '

    Detail page restore to the previous layout.

    Refactore forms to add user pages

    When use cases are executed, do not update the use case stause if the user manually chooses to pass and write the results.

    '); -$lang->misc->feature->all['10.4.stable'][] = array('title'=>'Optimize and adjust new interface', 'desc' => '

    Detail page restore to the previous layout.

    Refactore forms to add user pages

    When use cases are executed, do not update the use case stause if the user manually chooses to pass and write the results.

    '); +$lang->misc->feature->all['10.4.stable'][] = array('title'=>'After the user machine hibernates and the login fails, the session will be refreshed again.', 'desc' => ''); +$lang->misc->feature->all['10.4.stable'][] = array('title'=>'Upgrade existing interface mechanisms', 'desc' => ''); $lang->misc->feature->all['10.3.stable'][] = array('title'=>'Fix Bug.', 'desc' => ''); $lang->misc->feature->all['10.2.stable'][] = array('title'=>'ZenTao desktop is integrated!', 'desc' => ''); -$lang->misc->feature->all['10.0.stable'][] = array('title'=>'New UI/UX and new experience', 'desc' => '
    1. My Dashboard
    2. Dynamics
    3. Product Home
    4. Product Overview
    5. Roadmap
    6. Project Home
    7. Project overview
    8. QA Home
    9. Document Home
    10. Added work report on My Dashboard
    11. Add/Edit/Finish todos on My Dashboard
    12. Add prodcut report on Product Home
    13. Add prodcut overview on Product Home
    14. Add project report on Project Home
    15. Add project overview on Project Home
    16. Add Testing report on QA Home
    17. All Product/product Home/All Project/Project Home/QA Home is moved from the right of the secondary Navbar to the left.
    18. Kanban/Burndown/Tree/ByGroup of Project/Task has been moved from the third Navbar to the secondary one; Tree/ByGroup/Task List has been integrated to a drop-down.
    19. Bug/Build of Project on the secondary Navbar has been integrated to a drop-down.
    20. Display build and list by group, which is more reasonable.
    21. Added tree to display document on the left of the page.
    22. Added quick entry to document, including Last Update, My Doc and My Favorite
    23. Added My Favorite to Doc module.
    '); +$lang->misc->feature->all['10.0.stable'][] = array('title'=>'New UI/UX and new experience', 'desc' => '
    1. My Dashboard
    2. Dynamics
    3. Product Home
    4. Product Overview
    5. Roadmap
    6. Project Home
    7. Project overview
    8. Test Home
    9. Document Home
    10. Added work report on My Dashboard
    11. Add/Edit/Finish todos on My Dashboard
    12. Add prodcut report on Product Home
    13. Add prodcut overview on Product Home
    14. Add project report on Project Home
    15. Add project overview on Project Home
    16. Add Testing report on Test Home
    17. All Product/product Home/All Project/Project Home/Test Home is moved from the right of the secondary Navbar to the left.
    18. Kanban/Burndown/Tree/ByGroup of Project/Task has been moved from the third Navbar to the secondary one; Tree/ByGroup/Task List has been integrated to a drop-down.
    19. Bug/Build of Project on the secondary Navbar has been integrated to a drop-down.
    20. Display build and list by group, which is more reasonable.
    21. Added tree to display document on the left of the page.
    22. Added quick entry to document, including Last Update, My Doc and My Favorite
    23. Added My Favorite to Doc module.
    '); $lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Gather Mail,SMS,webhook into Message

    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Gather Mail,SMS,webhook into Message

    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Gather Mail,SMS,webhook into Message

    '); -$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Message centralized management', 'desc' => '

    Gather Mail,SMS,webhook into Message

    '); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Add recurred Todo', 'desc' => ''); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>"Add Block of 'AssignedToMe'", 'desc' => ''); +$lang->misc->feature->all['9.8.stable'][] = array('title'=>'Support generating reports on Test Builds', 'desc' => ''); $lang->misc->feature->all['9.7.stable'][] = array('title'=>'Optimize International package. Added Demo data.', 'desc' => ''); $lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Webhook Interface feature', 'desc' => 'Support communication with BearyChat, Dingding'); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Webhook Interface feature', 'desc' => 'Support communication with BearyChat, Dingding'); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Webhook Interface feature', 'desc' => 'Support communication with BearyChat, Dingding'); -$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Webhook Interface feature', 'desc' => 'Support communication with BearyChat, Dingding'); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Point', 'desc' => 'More skilled application, more score'); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added multiple user task and child tasks to Project', 'desc' => ''); +$lang->misc->feature->all['9.6.stable'][] = array('title'=>'Added Product line management to Product', 'desc' => ''); $lang->misc->feature->all['9.5.1'][] = array('title'=>'Added restricted actions.', 'desc' => ''); $lang->misc->feature->all['9.3.beta'][] = array('title'=>'Upgraded ZenTao framework,enhanced security', 'desc' => ''); $lang->misc->feature->all['9.1.stable'][] = array('title'=>'optimize Test View', 'desc' => '

    Added TestSuite, CaseLibrary and QA Report

    '); -$lang->misc->feature->all['9.1.stable'][] = array('title'=>'optimize Test View', 'desc' => '

    Added TestSuite, CaseLibrary and QA Report

    '); +$lang->misc->feature->all['9.1.stable'][] = array('title'=>'Support Group steps of TestCase', 'desc' => ''); $lang->misc->feature->all['9.0.beta'][] = array('title'=>'ZenTao CloudMail has been added.', 'desc' => '

    ZenTao CloudMail is a free Email service launched jointly with SendCloud. Once binded with ZenTao and passed verification, users can use this service.

    '); -$lang->misc->feature->all['9.0.beta'][] = array('title'=>'ZenTao CloudMail has been added.', 'desc' => '

    ZenTao CloudMail is a free Email service launched jointly with SendCloud. Once binded with ZenTao and passed verification, users can use this service.

    '); +$lang->misc->feature->all['9.0.beta'][] = array('title'=>'Optimized Rich Text Editor and Markdown Editor.', 'desc' => ''); $lang->misc->feature->all['8.3.stable'][] = array('title'=>'Improved Documentation.', 'desc' => '

    Added Document Home, restructured document library, and added privileges.

    Markdown Editor is supported,and privilege and version managment is added.

    '); $lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); -$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Home', 'desc' => '

    You can add blocks to Dashboard and arrange the layout.

    My Zone, Product, Project, and QA all support home custom mentioned before.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Navigation', 'desc' => '

    You can decide which project show in navigation bar and the order of projects shown in the bar.

    Hover on the navigation bar and a sign will show to its right. Click the sign and a dialog box "Custom Navigation" will show. Drag the block name to switch its order on navigation bar.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Batch Add/Edit Custom', 'desc' => '

    You can batch add and edit fields on custom pages.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Story/Task/Bug/Case', 'desc' => '

    You can custom fileds when add a Story/Task/Bug/Case.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Custom Export', 'desc' => '

    You can custom fileds when export a Story/Task/Bug/Case pages. You can also save it as template for next export.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>'Story/Task/Bug/Case Search ', 'desc' => '

    On Story/Task/Bug/Case List page, you can do a combined search on Modules and Tabs.

    '); +$lang->misc->feature->all['8.2.stable'][] = array('title'=>"ZenTao Tutorial", 'desc' => '

    Tutorial for rookies is added for first-time users to know how to use ZenTao.

    '); $lang->misc->feature->all['7.4.beta'][] = array('title'=>'Product branch feature is added.', 'desc' => '

    Product branch/platform is added, and its related Story/Plan/Bug/Case/Module has Branch added too.

    '); -$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Product branch feature is added.', 'desc' => '

    Product branch/platform is added, and its related Story/Plan/Bug/Case/Module has Branch added too.

    '); -$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Product branch feature is added.', 'desc' => '

    Product branch/platform is added, and its related Story/Plan/Bug/Case/Module has Branch added too.

    '); +$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Release Module is improved.', 'desc' => '

    Stop action has been added. If Stop to manage it, the Release will not show when Report Bug.

    Bugs that have been omitted in the Release will be related manually.

    '); +$lang->misc->feature->all['7.4.beta'][] = array('title'=>'Create pages of Story and Bug are improved.', 'desc' => ''); $lang->misc->feature->all['7.2.stable'][] = array('title'=>'Security Enhanced', 'desc' => '

    Admin weak passwork check is enhanced.

    ok file is required when code/upload an extension.

    Sensitive action requires Admin password.

    Do striptags, specialchars to content entered in ZenTao.

    '); -$lang->misc->feature->all['7.2.stable'][] = array('title'=>'Security Enhanced', 'desc' => '

    Admin weak passwork check is enhanced.

    ok file is required when code/upload an extension.

    Sensitive action requires Admin password.

    Do striptags, specialchars to content entered in ZenTao.

    '); +$lang->misc->feature->all['7.2.stable'][] = array('title'=>'Details Improved', 'desc' => ''); $lang->misc->feature->all['7.1.stable'][] = array('title'=>'Framework of Cron is added.', 'desc' => 'Framework of Cron is added. Daily notification, Burndown Update, Backup, Send Email and so on have been added.'); -$lang->misc->feature->all['7.1.stable'][] = array('title'=>'Framework of Cron is added.', 'desc' => 'Framework of Cron is added. Daily notification, Burndown Update, Backup, Send Email and so on have been added.'); +$lang->misc->feature->all['7.1.stable'][] = array('title'=>'rpm and deb packages are provided.', 'desc' => ''); $lang->misc->feature->all['6.3.stable'][] = array('title'=>'Data table is added.', 'desc' => '

    Fields can be customized in data table and data will be displayed according to customized fields.

    '); -$lang->misc->feature->all['6.3.stable'][] = array('title'=>'Data table is added.', 'desc' => '

    Fields can be customized in data table and data will be displayed according to customized fields.

    '); +$lang->misc->feature->all['6.3.stable'][] = array('title'=>'Continue improving details', 'desc' => ''); diff --git a/module/misc/lang/zh-cn.php b/module/misc/lang/zh-cn.php index 40043a73f6..df2c1db97b 100644 --- a/module/misc/lang/zh-cn.php +++ b/module/misc/lang/zh-cn.php @@ -115,7 +115,7 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ -$lang->misc->releaseDate['21.7.8'] = '2025-12-15'; +$lang->misc->releaseDate['21.7.8'] = '2025-12-30'; $lang->misc->releaseDate['21.7.7'] = '2025-10-29'; $lang->misc->releaseDate['21.7.6'] = '2025-09-29'; $lang->misc->releaseDate['21.7.5'] = '2025-09-11'; diff --git a/module/mr/test/lib/mr.unittest.class.php b/module/mr/test/lib/mr.unittest.class.php index be271dbdd3..263aa3de3c 100755 --- a/module/mr/test/lib/mr.unittest.class.php +++ b/module/mr/test/lib/mr.unittest.class.php @@ -648,7 +648,7 @@ class mrTest { global $app; $originalRawModule = $app->rawModule ?? ''; - + try { $app->rawModule = $rawModule; $mrModel = new mrModel($appName); @@ -672,18 +672,18 @@ class mrTest { if(empty($repo) || !is_object($repo)) return array(); if(empty($repo->SCM)) return array(); - + $scm = strtolower($repo->SCM); $methodName = 'get' . ucfirst($scm) . 'Projects'; - + if(!in_array($scm, array('gitlab', 'gitea', 'gogs'))) return array(); - + if(isset($repo->serviceHost) && isset($repo->serviceProject)) { $projectList = array($repo->serviceProject => $repo->serviceProject); return $this->objectModel->{$methodName}((int)$repo->serviceHost, $projectList); } - + return array(); } @@ -698,24 +698,24 @@ class mrTest public function assignEditDataTest($MR, $scm) { if(!is_object($MR) || empty($scm)) return false; - + if(!isset($MR->hostID) || !isset($MR->sourceProject)) return false; - + try { global $app, $lang; - + $app->loadLang('mr'); $app->loadConfig('pipeline'); - + // 模拟assignEditData的核心逻辑 $MR->canDeleteBranch = true; - + // 检查输入参数有效性 if(in_array($scm, array('gitlab', 'gitea', 'gogs')) && isset($MR->repoID) && $MR->repoID > 0) { $sourceProject = $targetProject = $MR->sourceProject; if($MR->sourceProject != $MR->targetProject) $targetProject = $MR->targetProject; - + $viewData = array( 'title' => $lang->mr->edit ?? '编辑', 'MR' => $MR, @@ -726,7 +726,7 @@ class mrTest 'targetProject' => $targetProject, 'repo' => (object)array('id' => $MR->repoID, 'name' => 'test-repo') ); - + return $viewData; } else @@ -754,29 +754,29 @@ class mrTest if($MRID <= 0) return 'invalid_mrid'; if($repoID <= 0) return 'invalid_repoid'; if(empty($orderBy)) return 'empty_orderby'; - + try { global $tester, $config; - + // 模拟方法的核心逻辑进行测试 // 因为实际方法会进行复杂的配置操作,我们简化测试 - + // 模拟配置设置 if(!isset($config->product)) $config->product = new stdClass(); if(!isset($config->product->search)) $config->product->search = array(); - + // 模拟核心业务逻辑 $config->product->search['queryID'] = $queryID; $config->product->search['style'] = 'simple'; $config->product->search['actionURL'] = "mr-linkStory-MRID={$MRID}&repoID={$repoID}&browseType=bySearch¶m=myQueryID&orderBy={$orderBy}"; - + // 模拟移除字段 unset($config->product->search['fields']['plan']); unset($config->product->search['fields']['module']); unset($config->product->search['fields']['product']); unset($config->product->search['fields']['branch']); unset($config->product->search['fields']['grade']); - + // 返回配置结果验证 $result = array( 'queryID' => $config->product->search['queryID'], @@ -788,7 +788,7 @@ class mrTest 'product' => !isset($config->product->search['fields']['product']) ? 'removed' : 'exists' ) ); - + return $result; } catch (Exception $e) { return 'error: ' . $e->getMessage(); @@ -811,19 +811,19 @@ class mrTest if($MRID <= 0) return 'invalid_mrid'; if($repoID <= 0) return 'invalid_repoid'; if(empty($orderBy)) return 'empty_orderby'; - + try { global $tester, $config; - + // 模拟方法的核心逻辑进行测试 // 因为实际方法会进行复杂的配置操作,我们简化测试 - + // 模拟配置设置 if(!isset($config->bug)) $config->bug = new stdClass(); if(!isset($config->bug->search)) $config->bug->search = array(); if(!isset($config->bug->search['fields'])) $config->bug->search['fields'] = array(); if(!isset($config->bug->search['params'])) $config->bug->search['params'] = array(); - + // 初始化fields确保存在被移除的字段 $config->bug->search['fields']['product'] = 'product'; $config->bug->search['fields']['plan'] = 'plan'; @@ -832,12 +832,12 @@ class mrTest $config->bug->search['fields']['openedBuild'] = 'openedBuild'; $config->bug->search['fields']['resolvedBuild'] = 'resolvedBuild'; $config->bug->search['fields']['branch'] = 'branch'; - + // 模拟核心业务逻辑 $config->bug->search['queryID'] = $queryID; $config->bug->search['style'] = 'simple'; $config->bug->search['actionURL'] = "mr-linkBug-MRID={$MRID}&repoID={$repoID}&browseType=bySearch¶m=myQueryID&orderBy={$orderBy}"; - + // 模拟移除字段 unset($config->bug->search['fields']['product']); unset($config->bug->search['params']['product']); @@ -853,7 +853,7 @@ class mrTest unset($config->bug->search['params']['resolvedBuild']); unset($config->bug->search['fields']['branch']); unset($config->bug->search['params']['branch']); - + // 返回配置结果验证 $result = array( 'queryID' => $config->bug->search['queryID'], @@ -869,7 +869,7 @@ class mrTest 'branch' => !isset($config->bug->search['fields']['branch']) ? 'removed' : 'exists' ) ); - + return $result; } catch (Exception $e) { return 'error: ' . $e->getMessage(); @@ -894,32 +894,32 @@ class mrTest if($repoID <= 0) return 'invalid_repoid'; if(empty($orderBy)) return 'empty_orderby'; if(!is_array($productExecutions)) return 'invalid_executions'; - + try { global $tester, $config; - + // 模拟方法的核心逻辑进行测试 // 因为实际方法会进行复杂的配置操作,我们简化测试 - + // 模拟配置设置 if(!isset($config->execution)) $config->execution = new stdClass(); if(!isset($config->execution->search)) $config->execution->search = array(); if(!isset($config->execution->search['fields'])) $config->execution->search['fields'] = array(); if(!isset($config->execution->search['params'])) $config->execution->search['params'] = array(); - + // 初始化fields确保存在被移除的字段 $config->execution->search['fields']['module'] = 'module'; $config->execution->search['params']['module'] = array(); - + // 模拟核心业务逻辑 $config->execution->search['actionURL'] = "mr-linkTask-MRID={$MRID}&repoID={$repoID}&browseType=bySearch¶m=myQueryID&orderBy={$orderBy}"; $config->execution->search['queryID'] = $queryID; $config->execution->search['params']['execution']['values'] = array_filter($productExecutions); - + // 模拟移除字段 unset($config->execution->search['fields']['module']); unset($config->execution->search['params']['module']); - + // 返回配置结果验证 $result = array( 'queryID' => $config->execution->search['queryID'], @@ -929,7 +929,7 @@ class mrTest 'module' => !isset($config->execution->search['fields']['module']) ? 'removed' : 'exists' ) ); - + return $result; } catch (Exception $e) { return 'error: ' . $e->getMessage(); @@ -953,24 +953,24 @@ class mrTest if($recPerPage <= 0) return 'invalid_recperpage'; if($pageID <= 0) return 'invalid_pageid'; if(!is_array($allTasks)) return 'invalid_alltasks'; - + try { global $app; - + // 模拟分页器的基本功能 $originalTaskCount = count($allTasks); $pageTotal = $originalTaskCount > 0 ? ceil($originalTaskCount / $recPerPage) : 1; - + // 如果当前页超过总页数,设置为最后一页 if($pageID > $pageTotal && $pageTotal > 0) { $pageID = $pageTotal; } - + // 计算分页范围 $count = 1; $limitMin = ($pageID - 1) * $recPerPage; $limitMax = $pageID * $recPerPage; - + // 过滤任务数组 $filteredTasks = array(); foreach($allTasks as $key => $task) { @@ -979,7 +979,7 @@ class mrTest } $count++; } - + // 返回测试结果 return array( 'original_task_count' => $originalTaskCount, @@ -992,7 +992,7 @@ class mrTest 'limit_max' => $limitMax, 'filtered_tasks' => $filteredTasks ); - + } catch (Exception $e) { return 'error: ' . $e->getMessage(); } @@ -1013,44 +1013,44 @@ class mrTest if(!is_string($hostType) || empty($hostType)) return 'invalid_hosttype'; if(!is_object($sourceProject)) return 'invalid_sourceproject'; if(!is_object($MR)) return 'invalid_mr'; - + try { global $tester, $app; - + // 模拟checkProjectEdit方法的权限检查逻辑 switch($hostType) { case 'gitlab': // 模拟gitlab权限检查 if(!isset($MR->hostID) || !isset($sourceProject->id)) return 'missing_required_fields'; - + // 模拟检查用户是否为开发者权限 $mockGitUsers = array('admin' => 'admin', 'developer' => 'developer'); $currentUser = $app->user->account ?? 'admin'; $isDeveloper = true; // 模拟开发者权限检查结果 - + if(isset($mockGitUsers[$currentUser]) && $isDeveloper) { return true; } break; - + case 'gitea': // 模拟gitea权限检查 if(!isset($sourceProject->allow_merge_commits)) return 'missing_merge_permission_field'; return (bool)$sourceProject->allow_merge_commits; - + case 'gogs': // 模拟gogs权限检查 if(!isset($sourceProject->permissions) || !isset($sourceProject->permissions->push)) { return 'missing_push_permission_field'; } return (bool)$sourceProject->permissions->push; - + default: return 'unsupported_hosttype'; } - + return false; - + } catch (Exception $e) { return 'error: ' . $e->getMessage(); } @@ -1071,16 +1071,16 @@ class mrTest if(!is_object($host)) return 'invalid_host'; if(empty($projectID)) return 'empty_project_id'; if(empty($branch) || !is_string($branch)) return 'invalid_branch'; - + try { global $tester; - + // 模拟getBranchUrl方法的逻辑 if(!isset($host->type) || !isset($host->id)) return 'missing_host_fields'; - + // 检查主机类型是否支持 if(!in_array($host->type, array('gitlab', 'gitea', 'gogs'))) return 'unsupported_host_type'; - + // 模拟apiGetSingleBranch的返回值 $mockBranchData = array( 'gitlab' => array( @@ -1102,21 +1102,21 @@ class mrTest 'nonexistent' => null ) ); - + // 获取模拟分支数据 if(isset($mockBranchData[$host->type][$branch])) { $branchData = $mockBranchData[$host->type][$branch]; } else { $branchData = null; } - + // 模拟原方法的返回逻辑 if($branchData && isset($branchData['web_url'])) { return $branchData['web_url']; } - + return ''; - + } catch (Exception $e) { return 'error: ' . $e->getMessage(); } @@ -1141,14 +1141,14 @@ class mrTest if(!is_string($projectID) || empty($projectID)) return 'invalid_projectid'; if(!is_int($mriid) || $mriid <= 0) return 'invalid_mriid'; if(!is_string($lastTime) || empty($lastTime)) return 'invalid_lasttime'; - + try { global $tester; - + // 模拟checkNewCommit方法的逻辑 // 检查主机类型是否支持 if(!in_array($hostType, array('gitlab', 'gitea', 'gogs'))) return 'unsupported_hosttype'; - + // 模拟apiGetMRCommits的返回值 $mockCommitLogs = array( 'gitlab' => array( @@ -1216,30 +1216,30 @@ class mrTest 999 => array() // 空提交记录 ) ); - + // 获取模拟提交记录 $commitLogs = null; if(isset($mockCommitLogs[$hostType][$mriid])) { $commitLogs = $mockCommitLogs[$hostType][$mriid]; } - + // 模拟原方法的逻辑 if($commitLogs && count($commitLogs) > 0) { $lastCommit = ''; - + if($hostType == 'gitlab') { $lastCommit = isset($commitLogs[0]['committed_date']) ? $commitLogs[0]['committed_date'] : ''; } elseif(in_array($hostType, array('gitea', 'gogs'))) { $lastCommit = isset($commitLogs[0]->author->committer->date) ? $commitLogs[0]->author->committer->date : ''; } - + if($lastCommit && $lastCommit > $lastTime) { return true; } } - + return false; - + } catch (Exception $e) { return 'error: ' . $e->getMessage(); } diff --git a/module/my/config.php b/module/my/config.php index 3da7c0d587..7e041fee47 100644 --- a/module/my/config.php +++ b/module/my/config.php @@ -20,10 +20,12 @@ $config->mobile->storyBar = array('assignedTo', 'openedBy', 'reviewedBy'); $config->my->notFlowModules = array(); -$config->my->openedDateField['product'] = 'createdDate'; -$config->my->openedDateField['productplan'] = 'createdDate'; -$config->my->openedDateField['release'] = 'createdDate'; -$config->my->openedDateField['build'] = 'createdDate'; -$config->my->openedDateField['testtask'] = 'createdDate'; -$config->my->openedDateField['testsuite'] = 'addedDate'; -$config->my->openedDateField['caselib'] = 'addedDate'; +$config->my->openedDateField['product'] = 'createdDate'; +$config->my->openedDateField['productplan'] = 'createdDate'; +$config->my->openedDateField['release'] = 'createdDate'; +$config->my->openedDateField['build'] = 'createdDate'; +$config->my->openedDateField['testtask'] = 'createdDate'; +$config->my->openedDateField['testsuite'] = 'addedDate'; +$config->my->openedDateField['caselib'] = 'addedDate'; +$config->my->openedDateField['baseline'] = 'createdDate'; +$config->my->openedDateField['projectchange'] = 'createdDate'; diff --git a/module/my/control.php b/module/my/control.php index c456324326..21dadbfdb3 100755 --- a/module/my/control.php +++ b/module/my/control.php @@ -111,7 +111,7 @@ class my extends control */ public function contribute(string $mode = 'task', string $type = 'openedBy', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { - if(($mode == 'issue' || $mode == 'risk') && $type == 'openedBy') $type = 'createdBy'; + if((in_array($mode, array('issue', 'risk', 'reviewissue'))) && $type == 'openedBy') $type = 'createdBy'; if($mode == 'testtask' && $type == 'openedBy') $type = 'done'; if(($mode == 'doc' || $mode == 'testcase') && $type == 'openedBy') $type = 'openedbyme'; $this->lang->my->featureBar[$this->app->rawMethod] = $this->lang->my->featureBar[$this->app->rawMethod][strtolower($mode)]; @@ -878,6 +878,61 @@ class my extends control $this->display(); } + /** + * 评审意见列表。 + * My reviewissues. + * + * @param string $type + * @param int $param + * @param string $orderBy + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @access public + * @return void + */ + public function reviewissue(string $type = 'assignedTo', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) + { + $this->loadModel('reviewissue'); + + /* Set the pager. */ + $this->app->loadClass('pager', true); + $pager = new pager($recTotal, $recPerPage, $pageID); + + /* Build the search form. */ + $currentMethod = $this->app->rawMethod; + $queryID = $type == 'bysearch' ? (int)$param : 0; + $actionURL = $this->createLink('my', $currentMethod, "mode=reviewissue&type=bysearch¶m=myQueryID"); + $this->my->buildReviewissueSearchForm($queryID, $actionURL); + + /* Get reviewissues by type*/ + if($type == 'assignedBy') + { + $reviewissues = $this->my->getAssignedByMe($this->app->user->account, $pager, $orderBy, 'reviewissue'); + } + elseif($type == 'bysearch') + { + $reviewissues = $this->my->getReviewissuesBySearch($queryID, $currentMethod, $orderBy, $pager); + } + else + { + $reviewissues = $this->reviewissue->getUserReviewissues($type, $this->app->user->account, $orderBy, $pager); + } + + $this->myZen->showWorkCount($recTotal, $recPerPage, $pageID); + + $this->view->title = $this->lang->my->reviewissue; + $this->view->reviewissues = $reviewissues; + $this->view->users = $this->user->getPairs('noclosed|noletter'); + $this->view->orderBy = $orderBy; + $this->view->pager = $pager; + $this->view->type = $type; + $this->view->param = $param; + $this->view->mode = 'reviewissue'; + $this->view->projectList = array(0 => '') + $this->loadModel('project')->getPairsByProgram(); + $this->display(); + } + /** * 评审列表。 * My audits. @@ -962,26 +1017,23 @@ class my extends control if($this->app->getViewType() == 'mhtml') $recPerPage = 10; $pager = pager::init($recTotal, $recPerPage, $pageID); - $auditplans = $this->loadModel('auditplan')->getList(0, 'my', $browseType, $param, $orderBy, $pager); + $auditplans = $this->loadModel('auditplan')->getList(0, $browseType, $param, $orderBy, $pager); $this->myZen->showWorkCount($recTotal, $recPerPage, $pageID); - $this->app->loadLang('process'); - $this->view->executions = $this->loadModel('execution')->getPairs(); - $this->view->projects = $this->loadModel('project')->getPairs(); - $this->view->processTypeList = $this->lang->process->classify; - $this->view->processes = $this->loadModel('pssp')->getProcesses(); - $this->view->activities = $this->pssp->getActivityPairs(); - $this->view->outputs = $this->pssp->getOutputPairs(); - - $this->view->title = $this->lang->my->common . $this->lang->hyphen . $this->lang->my->auditplan; - $this->view->browseType = $browseType; - $this->view->auditplans = $auditplans; - $this->view->users = $this->user->getPairs('noclosed|noletter'); - $this->view->pager = $pager; - $this->view->orderBy = $orderBy; - $this->view->param = $param; - $this->view->mode = 'auditplan'; + $this->view->title = $this->lang->my->common . $this->lang->hyphen . $this->lang->my->auditplan; + $this->view->browseType = $browseType; + $this->view->param = $param; + $this->view->orderBy = $orderBy; + $this->view->pager = $pager; + $this->view->mode = 'auditplan'; + $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); + $this->view->executions = $this->loadModel('execution')->getPairs(); + $this->view->processList = $this->loadModel('pssp')->getProcessPairs(); + $this->view->activityList = $this->pssp->getActivityPairs(); + $this->view->outputList = $this->pssp->getOutputPairs(); + $this->view->auditplans = $auditplans; + $this->view->projects = $this->loadModel('project')->getPairs(); $this->display(); } diff --git a/module/my/lang/de.php b/module/my/lang/de.php index dae4943c6b..977712c8f1 100644 --- a/module/my/lang/de.php +++ b/module/my/lang/de.php @@ -21,6 +21,7 @@ $lang->my->execution = "My {$lang->execution->common}s"; $lang->my->audit = 'Review'; $lang->my->issue = 'My Issues'; $lang->my->risk = 'My Risks'; +$lang->my->reviewissue = 'My Review Issue'; $lang->my->profile = 'My Profile'; $lang->my->dynamic = 'My Dynamics'; $lang->my->team = 'My Team'; diff --git a/module/my/lang/en.php b/module/my/lang/en.php index dae4943c6b..977712c8f1 100644 --- a/module/my/lang/en.php +++ b/module/my/lang/en.php @@ -21,6 +21,7 @@ $lang->my->execution = "My {$lang->execution->common}s"; $lang->my->audit = 'Review'; $lang->my->issue = 'My Issues'; $lang->my->risk = 'My Risks'; +$lang->my->reviewissue = 'My Review Issue'; $lang->my->profile = 'My Profile'; $lang->my->dynamic = 'My Dynamics'; $lang->my->team = 'My Team'; diff --git a/module/my/lang/fr.php b/module/my/lang/fr.php index dae4943c6b..977712c8f1 100644 --- a/module/my/lang/fr.php +++ b/module/my/lang/fr.php @@ -21,6 +21,7 @@ $lang->my->execution = "My {$lang->execution->common}s"; $lang->my->audit = 'Review'; $lang->my->issue = 'My Issues'; $lang->my->risk = 'My Risks'; +$lang->my->reviewissue = 'My Review Issue'; $lang->my->profile = 'My Profile'; $lang->my->dynamic = 'My Dynamics'; $lang->my->team = 'My Team'; diff --git a/module/my/lang/zh-cn.php b/module/my/lang/zh-cn.php index d416a78baa..b35573fddf 100644 --- a/module/my/lang/zh-cn.php +++ b/module/my/lang/zh-cn.php @@ -21,6 +21,7 @@ $lang->my->execution = "我的{$lang->execution->common}"; $lang->my->audit = '审批'; $lang->my->issue = '我的问题'; $lang->my->risk = '我的风险'; +$lang->my->reviewissue = '我的评审意见'; $lang->my->profile = '我的档案'; $lang->my->dynamic = '我的动态'; $lang->my->team = '团队'; diff --git a/module/my/model.php b/module/my/model.php index f732eb5848..24fa46216e 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -274,7 +274,7 @@ class myModel extends model if($objectType == 'task') return $this->getTaskAssignedByMe($pager, $orderBy, $objectIdList); if($objectType == 'requirement' || $objectType == 'story' || $objectType == 'bug') return $this->myTao->getProductRelatedAssignedByMe($objectIdList, $objectType, $module, $orderBy, $pager); - if($objectType == 'risk' || $objectType == 'issue' || $objectType == 'nc') + if(in_array($objectType, array('risk', 'issue', 'nc'))) { return $this->dao->select('t1.*')->from($this->config->objectTables[$module])->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') @@ -297,6 +297,16 @@ class myModel extends model ->page($pager) ->fetchAll('id'); } + if($objectType == 'reviewissue') + { + return $this->dao->select('t1.*, t2.type')->from($this->config->objectTables[$module])->alias('t1') + ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.review = t2.id') + ->where('t1.deleted')->eq(0) + ->andWhere('t1.id')->in($objectIdList) + ->orderBy('t1.' . $orderBy) + ->page($pager) + ->fetchAll('id'); + } return $this->dao->select('*')->from($this->config->objectTables[$module]) ->where('deleted')->eq(0) ->andWhere('id')->in($objectIdList) @@ -588,7 +598,6 @@ class myModel extends model * * @param int $queryID * @param string $actionURL - * @param string $type risk|contribute * @access public * @return void */ @@ -617,6 +626,40 @@ class myModel extends model $this->loadModel('search')->setSearchParams($this->config->risk->search); } + /* + * 构建评审意见搜索表单。 + * Build reviewissue search form. + * + * @param int $queryID + * @param string $actionURL + * @access public + * @return void + */ + public function buildReviewissueSearchForm(int $queryID, string $actionURL): void + { + $projects = $this->dao->select('id, name')->from(TABLE_PROJECT)->where('type')->eq('project') + ->andWhere('deleted')->eq('0') + ->andWhere('vision')->eq($this->config->vision) + ->andWhere('model')->ne('kanban') + ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() + ->orderBy('order_asc') + ->fetchPairs(); + $queryName = $this->app->rawMethod . 'Reviewissue'; + + $this->app->loadConfig('reviewissue'); + $this->config->reviewissue->search['module'] = $queryName; + $this->config->reviewissue->search['actionURL'] = $actionURL; + $this->config->reviewissue->search['queryID'] = $queryID; + + $this->config->reviewissue->search['params']['project']['values'] = array('') + $projects; + $this->config->reviewissue->search['params']['type']['values'] = arrayUnion(array('' => ''), $this->lang->reviewissue->issueType); + + if(!isset($this->config->reviewissue->search['fields'])) $this->config->reviewissue->search['fields'] = array(); + unset($this->config->reviewissue->search['fields']['module']); + + $this->loadModel('search')->setSearchParams($this->config->reviewissue->search); + } + /** * 通过搜索获取风险。 * Get risks by search. @@ -678,6 +721,70 @@ class myModel extends model return $risks; } + /** + * 通过搜索获取评审问题。 + * Get review issues by search. + * + * @param int $queryID + * @param string $type + * @param string $orderBy + * @param object $pager + * @access public + * @return array + */ + public function getReviewissuesBySearch(int $queryID, string $type, string $orderBy, ?object $pager = null): array + { + $queryName = $type == 'contribute' ? 'contributeReviewissueQuery' : 'workReviewissueQuery'; + if($queryID && $queryID != 'myQueryID') + { + $query = $this->loadModel('search')->getQuery($queryID); + if($query) + { + $this->session->set($queryName, $query->sql); + $this->session->set($queryName . 'Form', $query->form); + } + else + { + $this->session->set($queryName, ' 1 = 1'); + } + } + else + { + if($this->session->{$queryName} == false) $this->session->set($queryName, ' 1 = 1'); + } + + $reviewissueQuery = preg_replace('/`([^`]+)`/', 't1.$0', $this->session->{$queryName}); + if(strpos($reviewissueQuery, 't1.`review`') !== false) $reviewissueQuery = str_replace('t1.`review`', 't2.`object`', $reviewissueQuery); + if(strpos($reviewissueQuery, 't1.`type`') !== false) $reviewissueQuery = str_replace('t1.`type`', 't2.`type`', $reviewissueQuery); + + if($type == 'contribute') + { + $assignedByMe = $this->getAssignedByMe($this->app->user->account, null, 'id_desc', 'reviewissue'); + $reviewissues = $this->dao->select('t1.*, t2.type')->from(TABLE_REVIEWISSUE)->alias('t1') + ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.review = t2.id') + ->where($reviewissueQuery) + ->andWhere('t1.deleted')->eq('0') + ->andWhere('t1.createdBy',1)->eq($this->app->user->account) + ->orWhere('t1.id')->in(array_keys($assignedByMe)) + ->markRight(1) + ->orderBy("t1.$orderBy") + ->page($pager) + ->fetchAll('id', false); + } + elseif($type == 'work') + { + $reviewissues = $this->dao->select('t1.*, t2.type')->from(TABLE_REVIEWISSUE)->alias('t1') + ->leftJoin(TABLE_REVIEW)->alias('t2')->on('t1.review = t2.id') + ->where($reviewissueQuery) + ->andWhere('t1.deleted')->eq('0') + ->andWhere('t1.assignedTo')->eq($this->app->user->account) + ->orderBy("t1.$orderBy") + ->page($pager) + ->fetchAll('id', false); + } + return $reviewissues; + } + /** * 构建需求搜索表单。 * Build Story search form. @@ -1209,6 +1316,11 @@ class myModel extends model ->andWhere('t1.account')->eq($this->app->user->account) ->andWhere('t1.status')->eq('doing') ->andWhere('t1.type')->eq('review') + ->beginIF(!helper::hasFeature('project_cm'))->andWhere('objectType')->ne('baseline')->fi() + ->beginIF(!helper::hasFeature('project_change'))->andWhere('objectType')->ne('projectchange')->fi() + ->beginIF(!helper::hasFeature('project_risk'))->andWhere('objectType')->ne('risk')->fi() + ->beginIF(!helper::hasFeature('project_issue'))->andWhere('objectType')->ne('issue')->fi() + ->beginIF(!helper::hasFeature('project_opportunity'))->andWhere('objectType')->ne('opportunity')->fi() ->orderBy("t2.{$orderBy}") ->beginIF($checkExists)->limit(1)->fi() ->fetchAll(); diff --git a/module/my/test/lib/my.unittest.class.php b/module/my/test/lib/my.unittest.class.php index 9c368628f0..5f9a830610 100644 --- a/module/my/test/lib/my.unittest.class.php +++ b/module/my/test/lib/my.unittest.class.php @@ -328,6 +328,24 @@ class myTest return $tester->config->risk->search; } + /** + * 测试构建评审意见搜索表单。 + * Test build reviewissue search form. + * + * @param int $queryID + * @param string $actionURL + * @access public + * @return array + */ + public function buildReviewissueSearchFormTest(int $queryID, string $actionURL): array + { + $this->objectModel->buildReviewissueSearchForm($queryID, $actionURL); + + if(dao::isError()) return dao::getError(); + global $tester; + return $tester->config->reviewissue->search; + } + /** * 通过搜索获取风险。 * Get risks by search. @@ -347,6 +365,25 @@ class myTest return array_keys($objects); } + /** + * 通过搜索获取评审意见。 + * Get reviewissues by search. + * + * @param int $queryID + * @param string $type + * @param string $orderBy + * @access public + * @return array + */ + public function getReviewissuesBySearchTest(int $queryID, string $type, string $orderBy): array + { + $objects = $this->objectModel->getReviewissuesBySearch($queryID, $type, $orderBy , null); + + if(dao::isError()) return dao::getError(); + + return array_keys($objects); + } + /** * 测试构建需求搜索表单。 * Test build story search form. @@ -662,7 +699,7 @@ class myTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('getProductRelatedData'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectModel, [$productKeys]); if(dao::isError()) return dao::getError(); @@ -684,7 +721,7 @@ class myTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('getTaskAssignedByMe'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectModel, [$pager, $orderBy, $objectIdList]); if(dao::isError()) return dao::getError(); @@ -705,7 +742,7 @@ class myTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('buildReviewedList'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectModel, [$objectGroup, $actions, $flows]); if(dao::isError()) return dao::getError(); @@ -728,7 +765,7 @@ class myTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getProductRelatedAssignedByMe'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectTao, [$objectIdList, $objectType, $module, $orderBy, $pager]); if(dao::isError()) return dao::getError(); @@ -781,7 +818,7 @@ class myTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('fetchStoriesBySearch'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectTao, [$myStoryQuery, $type, $orderBy, $pager, $storiesAssignedByMe]); if(dao::isError()) return dao::getError(); @@ -804,7 +841,7 @@ class myTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('fetchEpicsBySearch'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectTao, [$myEpicQuery, $type, $orderBy, $pager, $epicIDList]); if(dao::isError()) return dao::getError(); @@ -827,7 +864,7 @@ class myTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('fetchRequirementsBySearch'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectTao, [$myRequirementQuery, $type, $orderBy, $pager, $requirementIDList]); if(dao::isError()) return dao::getError(); @@ -848,7 +885,7 @@ class myTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('buildReviewingFlows'); $method->setAccessible(true); - + $result = $method->invokeArgs($this->objectTao, [$objectGroup, $flows, $objectNameFields]); if(dao::isError()) return dao::getError(); @@ -865,7 +902,7 @@ class myTest public function buildTaskDataTest(array $tasks) { if(empty($tasks)) return array(); - + // 直接模拟buildTaskData方法的核心逻辑,避免复杂的依赖 foreach($tasks as $task) { @@ -873,17 +910,17 @@ class myTest $task->estimateLabel = $task->estimate . '工时'; $task->consumedLabel = $task->consumed . '工时'; $task->leftLabel = $task->left . '工时'; - + // 状态检查:如果需求状态为active且版本不一致,则设为changed $task->status = !empty($task->storyStatus) && $task->storyStatus == 'active' && $task->latestStoryVersion > $task->storyVersion && !in_array($task->status, array('cancel', 'closed')) ? 'changed' : $task->status; - + // 设置其他属性 $task->canBeChanged = true; // 简化处理 $task->isChild = false; $task->parentName = ''; if($task->status == 'changed') $task->rawStatus = 'changed'; - + // 处理父子关系 if($task->parent > 0) { @@ -912,22 +949,22 @@ class myTest public function buildCaseDataTest(array $cases, string $type) { if(empty($cases)) return $cases; - + global $tester; - + // 模拟buildCaseData方法的核心逻辑,避免复杂的类依赖 $failCount = 0; foreach($cases as $case) { // 模拟story模块的checkNeedConfirm处理 if(isset($case->needconfirm)) $case->needconfirm = $case->needconfirm; - + // 模拟testcase模块的appendData处理 // 这里简化处理,直接保持原有属性 - + // 统计失败数量 if(isset($case->lastRunResult) && $case->lastRunResult && $case->lastRunResult != 'pass') $failCount++; - + // 处理需要确认的状态 if(isset($case->needconfirm) && $case->needconfirm) { @@ -938,17 +975,17 @@ class myTest { $case->status = $tester->lang->testcase->changed ?? '用例已变更'; } - + // 处理空的执行结果 - if(!isset($case->lastRunResult) || !$case->lastRunResult) + if(!isset($case->lastRunResult) || !$case->lastRunResult) { $case->lastRunResult = $tester->lang->testcase->unexecuted ?? '未执行'; } } - + // 模拟设置视图变量 if(isset($tester->view)) $tester->view->failCount = $failCount; - + if(dao::isError()) return dao::getError(); return $cases; @@ -964,7 +1001,7 @@ class myTest public function assignRelatedDataTest(array $feedbacks): array { if(empty($feedbacks)) return array(); - + // 模拟assignRelatedData方法的核心逻辑 $storyIdList = $bugIdList = $todoIdList = $taskIdList = $ticketIdList = array(); foreach($feedbacks as $feedback) @@ -975,7 +1012,7 @@ class myTest if($feedback->solution == 'totask') $taskIdList[] = $feedback->result; if($feedback->solution == 'ticket') $ticketIdList[] = $feedback->result; } - + // 模拟获取关联数据(简化实现,只统计ID数量) $result = array(); $result['bugs'] = count($bugIdList); @@ -983,7 +1020,7 @@ class myTest $result['todos'] = count($todoIdList); $result['tasks'] = count($taskIdList); $result['tickets'] = count($ticketIdList); - + return $result; } @@ -999,39 +1036,39 @@ class myTest public function buildSearchFormForFeedbackTest(int $queryID, string $orderBy, string $rawMethod = 'feedback'): array { global $tester; - + // 保存原始rawMethod并设置新值 $originalRawMethod = $tester->app->rawMethod ?? ''; $tester->app->rawMethod = $rawMethod; - + // 模拟buildSearchFormForFeedback方法的核心逻辑 // 由于直接调用zen层方法比较复杂,我们模拟其主要功能 - + // 初始化feedback搜索配置 if(!isset($tester->config->feedback)) $tester->config->feedback = new stdclass(); if(!isset($tester->config->feedback->search)) $tester->config->feedback->search = array(); - + // 设置搜索模块名 $tester->config->feedback->search['module'] = $rawMethod . 'Feedback'; - + // 设置搜索动作URL $tester->config->feedback->search['actionURL'] = "inlink({$rawMethod}, 'mode=feedback&browseType=bysearch¶m=myQueryID&orderBy={$orderBy}')"; - + // 设置queryID $tester->config->feedback->search['queryID'] = $queryID; - + // 设置搜索参数(模拟从其他模块获取数据) if(!isset($tester->config->feedback->search['params'])) $tester->config->feedback->search['params'] = array(); - + // 模拟产品数据 $tester->config->feedback->search['params']['product']['values'] = array(1 => 'Product 1', 2 => 'Product 2'); - - // 模拟模块数据 + + // 模拟模块数据 $tester->config->feedback->search['params']['module']['values'] = array(1 => 'Module 1', 2 => 'Module 2'); - + // 模拟处理人数据 $tester->config->feedback->search['params']['processedBy']['values'] = array('admin' => 'Admin', 'user1' => 'User1'); - + // 如果是work方法,需要移除某些字段 if($rawMethod == 'work') { @@ -1043,12 +1080,12 @@ class myTest unset($tester->config->feedback->search['fields'][$field]); } } - + // 恢复原始rawMethod $tester->app->rawMethod = $originalRawMethod; - + if(dao::isError()) return dao::getError(); - + // 返回搜索配置信息用于验证 return array( 'module' => $tester->config->feedback->search['module'] ?? '', @@ -1072,11 +1109,11 @@ class myTest public function showWorkCountTest(int $recTotal = 0, int $recPerPage = 20, int $pageID = 1): array { global $tester; - + // 模拟showWorkCount方法的核心功能 // 初始化计数数组 $count = array('task' => 0, 'story' => 0, 'bug' => 0, 'case' => 0, 'testtask' => 0, 'requirement' => 0, 'issue' => 0, 'risk' => 0, 'qa' => 0, 'meeting' => 0, 'ticket' => 0, 'feedback' => 0); - + // 模拟pager初始化 if(!class_exists('pager')) { @@ -1091,9 +1128,9 @@ class myTest } '); } - + $pager = pager::init($recTotal, $recPerPage, $pageID); - + // 模拟获取当前用户分配的任务数量 $tasks = $tester->dao->select('*')->from(TABLE_TASK) ->where('assignedTo')->eq($tester->app->user->account ?? 'admin') @@ -1101,7 +1138,7 @@ class myTest ->andWhere('status')->in('wait,doing') ->fetchAll(); $count['task'] = count($tasks); - + // 模拟获取当前用户分配的需求数量 $stories = $tester->dao->select('*')->from(TABLE_STORY) ->where('assignedTo')->eq($tester->app->user->account ?? 'admin') @@ -1109,7 +1146,7 @@ class myTest ->andWhere('status')->in('active,reviewing') ->fetchAll(); $count['story'] = count($stories); - + // 模拟获取当前用户分配的bug数量 $bugs = $tester->dao->select('*')->from(TABLE_BUG) ->where('assignedTo')->eq($tester->app->user->account ?? 'admin') @@ -1117,7 +1154,7 @@ class myTest ->andWhere('status')->in('active,confirmed') ->fetchAll(); $count['bug'] = count($bugs); - + // 其他工作项默认为0(在测试数据中没有) $count['case'] = 0; $count['testtask'] = 0; @@ -1128,14 +1165,14 @@ class myTest $count['meeting'] = 0; $count['ticket'] = 0; $count['feedback'] = 0; - + if(dao::isError()) return dao::getError(); - + // 模拟设置视图变量 if(!isset($tester->view)) $tester->view = new stdclass(); $tester->view->todoCount = $count; $tester->view->isOpenedURAndSR = false; - + return $count; } @@ -1152,37 +1189,37 @@ class myTest public function showWorkCountNotInOpenTest(array $count, object $pager, string $edition = 'open', string $vision = 'rnd'): array { global $tester; - + // 保存原始配置并设置测试配置 $originalEdition = $tester->config->edition ?? 'open'; $originalVision = $tester->config->vision ?? 'rnd'; $tester->config->edition = $edition; $tester->config->vision = $vision; - + // 创建测试用的pager对象,如果传入的不是有效对象 if(!is_object($pager) || !property_exists($pager, 'recTotal')) { $pager = new stdclass(); $pager->recTotal = 0; } - + // 确定版本标志 $isBiz = $edition == 'biz' ? 1 : 0; $isMax = $edition == 'max' ? 1 : 0; $isIPD = $edition == 'ipd' ? 1 : 0; - + // 如果不是开源版,统计反馈和工单 if($edition != 'open') { // 模拟反馈和工单数量(避免查询不存在的表) $count['feedback'] = 0; // 测试环境中默认为0 $count['ticket'] = 0; // 测试环境中默认为0 - + // 模拟从session设置ticketBrowseType if(!isset($tester->session)) $tester->session = new stdclass(); $tester->session->ticketBrowseType = 'assignedtome'; } - + // 如果是MAX或IPD版本 if($isMax || $isIPD) { @@ -1191,15 +1228,15 @@ class myTest // 模拟获取问题、风险、质量检查、会议数量(避免查询不存在的表) $count['issue'] = 0; // 测试环境中默认为0 $count['risk'] = 0; // 测试环境中默认为0 - + // 模拟质量检查数量(NC + 审计计划) $ncCount = 2; // 模拟数据 $auditplanCount = 1; // 模拟数据 $count['qa'] = $ncCount + $auditplanCount; - + $count['meeting'] = 0; // 测试环境中默认为0 } - + // IPD版本且是OR视野的需求处理 if($isIPD && $vision == 'or') { @@ -1209,19 +1246,19 @@ class myTest $count['demand'] = $assignedToDemandCount + $reviewByDemandCount; } } - + // 恢复原始配置 $tester->config->edition = $originalEdition; $tester->config->vision = $originalVision; - + // 模拟设置视图变量 if(!isset($tester->view)) $tester->view = new stdclass(); $tester->view->isBiz = $isBiz; $tester->view->isMax = $isMax; $tester->view->isIPD = $isIPD; - + if(dao::isError()) return dao::getError(); - + return $count; } } diff --git a/module/my/test/lib/myzen.unittest.class.php b/module/my/test/lib/myzen.unittest.class.php new file mode 100644 index 0000000000..72128ef548 --- /dev/null +++ b/module/my/test/lib/myzen.unittest.class.php @@ -0,0 +1,69 @@ +tester = $tester; + $tester->app->setModuleName('my'); + $this->objectModel = $tester->loadModel('my'); + + $this->myZenTest = initReference('my'); + } + + /** + * 获取待处理各项数据。 + * Get work count. + * + * @param int $recTotal + * @param string $recPerPage + * @param string $pageID + * @access public + * @return array + */ + public function showWorkCountTest(int $recTotal = 0, int $recPerPage = 20, int $pageID = 1): array + { + global $tester; + $tester->app->rawModule = 'my'; + $tester->app->rawMethod = 'work'; + + $method = $this->myZenTest->getMethod('showWorkCount'); + $method->setAccessible(true); + + $count = $method->invokeArgs($this->myZenTest->newInstance(), [$recTotal, $recPerPage, $pageID]); + + if(dao::isError()) return dao::getError(); + return $count; + } + + /** + * 获取收费版待处理各项数据。 + * Get work count not in open edition. + * + * @param array $count + * @param int $recTotal + * @param string $recPerPage + * @param string $pageID + * @access public + * @return array + */ + public function showWorkCountNotInOpenTest(array $count = [], int $recTotal = 0, int $recPerPage = 20, int $pageID = 1): array + { + global $tester; + $tester->app->rawModule = 'my'; + $tester->app->rawMethod = 'work'; + + $tester->app->loadClass('pager', true); + $pager = pager::init($recTotal, $recPerPage, $pageID); + + $method = $this->myZenTest->getMethod('showWorkCountNotInOpen'); + $method->setAccessible(true); + + $count = $method->invokeArgs($this->myZenTest->newInstance(), [$count, $pager]); + + if(dao::isError()) return dao::getError(); + return $count; + } +} diff --git a/module/my/test/model/buildreviewissuesearchform.php b/module/my/test/model/buildreviewissuesearchform.php new file mode 100755 index 0000000000..302c6e1d14 --- /dev/null +++ b/module/my/test/model/buildreviewissuesearchform.php @@ -0,0 +1,35 @@ +#!/usr/bin/env php +gen('1'); + +su('admin'); + +/** + +title=测试 myModel->buildReviewIssueSearchForm(); +timeout=0 +cid=1 + +- 测试获取queryID 1 actionURL actionURL1 的搜索表单 + - 属性module @Reviewissue + - 属性queryID @0 + - 属性actionURL @actionURL1 +- 测试获取queryID 0 actionURL actionURL2 的搜索表单 + - 属性module @Reviewissue + - 属性queryID @1 + - 属性actionURL @actionURL2 + +*/ + +$my = new myTest(); + +$queryID = array(0, 1); +$actionURL = array('actionURL1', 'actionURL2'); +$config1 = $my->buildReviewissueSearchFormTest($queryID[0], $actionURL[0]); +$config2 = $my->buildReviewissueSearchFormTest($queryID[1], $actionURL[1]); +r($config1) && p('module,queryID,actionURL') && e('Reviewissue,0,actionURL1'); // 测试获取queryID 1 actionURL actionURL1 的搜索表单 +r($config2) && p('module,queryID,actionURL') && e('Reviewissue,1,actionURL2'); // 测试获取queryID 0 actionURL actionURL2 的搜索表单 \ No newline at end of file diff --git a/module/my/test/model/getassignedbyme.php b/module/my/test/model/getassignedbyme.php index 5c8a54da97..dd8b9f23f2 100755 --- a/module/my/test/model/getassignedbyme.php +++ b/module/my/test/model/getassignedbyme.php @@ -49,4 +49,4 @@ r($myTest->getAssignedByMeTest('nonexist', null, 'id_desc', 'task')) && p() && e r($myTest->getAssignedByMeTest('admin', null, 'id_desc', 'story')) && p() && e('2'); // 步骤4:获取admin用户指派的story r($myTest->getAssignedByMeTest('admin', null, 'id_asc', 'bug')) && p() && e('4'); // 步骤5:测试排序功能 r($myTest->getAssignedByMeTest('user1', null, 'id_desc', 'task')) && p() && e('0'); // 步骤6:测试其他用户指派的记录 -r($myTest->getAssignedByMeTest('admin', null, 'id_desc', '')) && p() && e('0'); // 步骤7:测试空类型默认行为 \ No newline at end of file +r($myTest->getAssignedByMeTest('admin', null, 'id_desc', '')) && p() && e('0'); // 步骤7:测试空类型默认行为 diff --git a/module/my/test/model/getreviewissuesbysearch.php b/module/my/test/model/getreviewissuesbysearch.php new file mode 100755 index 0000000000..979e0816a1 --- /dev/null +++ b/module/my/test/model/getreviewissuesbysearch.php @@ -0,0 +1,58 @@ +#!/usr/bin/env php +project->range('1'); +$reviewissue->review->range('1-5'); +$reviewissue->type->range('review'); +$reviewissue->opinionDate->range('`2025-01-01`'); +$reviewissue->resolutionDate->range('`2025-01-01`'); +$reviewissue->createdDate->range('`2025-01-01`'); +$reviewissue->assignedTo->range('admin,user1,user2'); +$reviewissue->gen(20); +zenData('userquery')->loadYaml('userquery')->gen('1'); +zenData('user')->gen('1'); + +su('admin'); + +/** + +title=测试 myModel->getReviewissuesBySearch(); +cid=1 + +- 测试通过搜索获取 query id 0 类型 work id_desc 评审意见id @19,16,13,10,7,4,1 +- 测试通过搜索获取 query id 0 类型 work id_desc 评审意见数量 @7 +- 测试通过搜索获取 query id 0 类型 work id_asc 评审意见id @1,4,7,10,13,16,19 +- 测试通过搜索获取 query id 0 类型 work id_asc 评审意见数量 @7 +- 测试通过搜索获取 query id 5 类型 work id_desc 评审意见id @19,16,13,10,7,4,1 +- 测试通过搜索获取 query id 5 类型 work id_desc 评审意见数量 @7 +- 测试通过搜索获取 query id 5 类型 work id_asc 评审意见id @1,4,7,10,13,16,19 +- 测试通过搜索获取 query id 5 类型 work id_asc 评审意见数量 @7 + +*/ + +$my = new myTest(); +$queryID = array(0, 1); +$type = array('work', 'contribute'); +$orderBy = array('id_desc', 'id_asc'); + +global $tester; +$tester->session->set('workReviewissueQuery', "`title` like '%问题%'"); +$tester->session->set('contributeReviewissueQuery', "`title` like '%问题%'"); + +$tasks1 = $my->getReviewissuesBySearchTest($queryID[0], $type[0], $orderBy[0]); +$tasks2 = $my->getReviewissuesBySearchTest($queryID[0], $type[0], $orderBy[1]); +$tasks3 = $my->getReviewissuesBySearchTest($queryID[0], $type[0], $orderBy[0]); +$tasks4 = $my->getReviewissuesBySearchTest($queryID[0], $type[0], $orderBy[1]); + +r(implode(',', $tasks1)) && p() && e('19,16,13,10,7,4,1'); // 测试通过搜索获取 query id 0 类型 work id_desc 评审意见id +r(count($tasks1)) && p() && e('7'); // 测试通过搜索获取 query id 0 类型 work id_desc 评审意见数量 +r(implode(',', $tasks2)) && p() && e('1,4,7,10,13,16,19'); // 测试通过搜索获取 query id 0 类型 work id_asc 评审意见id +r(count($tasks2)) && p() && e('7'); // 测试通过搜索获取 query id 0 类型 work id_asc 评审意见数量 +r(implode(',', $tasks3)) && p() && e('19,16,13,10,7,4,1'); // 测试通过搜索获取 query id 5 类型 work id_desc 评审意见id +r(count($tasks3)) && p() && e('7'); // 测试通过搜索获取 query id 5 类型 work id_desc 评审意见数量 +r(implode(',', $tasks4)) && p() && e('1,4,7,10,13,16,19'); // 测试通过搜索获取 query id 5 类型 work id_asc 评审意见id +r(count($tasks4)) && p() && e('7'); // 测试通过搜索获取 query id 5 类型 work id_asc 评审意见数量 diff --git a/module/my/test/tao/yaml/product_fetchrequirementsbysearch.yaml b/module/my/test/tao/yaml/product_fetchrequirementsbysearch.yaml index 660d6c76f3..6be155c746 100644 --- a/module/my/test/tao/yaml/product_fetchrequirementsbysearch.yaml +++ b/module/my/test/tao/yaml/product_fetchrequirementsbysearch.yaml @@ -7,18 +7,18 @@ version: "1.0" fields: - field: id range: 1-3 - + - field: name range: '产品A,产品B,产品C' - + - field: code range: 'PROD-A,PROD-B,PROD-C' - + - field: type range: normal{3} - + - field: status range: normal{3} - + - field: deleted range: 0{3} \ No newline at end of file diff --git a/module/my/test/tao/yaml/story_fetchrequirementsbysearch.yaml b/module/my/test/tao/yaml/story_fetchrequirementsbysearch.yaml index f45adc7e02..033438a90a 100644 --- a/module/my/test/tao/yaml/story_fetchrequirementsbysearch.yaml +++ b/module/my/test/tao/yaml/story_fetchrequirementsbysearch.yaml @@ -7,60 +7,60 @@ version: "1.0" fields: - field: id range: 1-10 - + - field: vision range: rnd{10} - + - field: parent range: 0{10} - + - field: isParent range: 0{10} - + - field: root range: 0{10} - + - field: product range: 1-3 - + - field: branch range: 0{10} - + - field: module range: 0{10} - + - field: title range: '用户需求{1-10}' - + - field: type range: requirement{10} - + - field: category range: feature{10} - + - field: pri range: 1-4 - + - field: estimate range: 1-10 - + - field: status range: active{3},reviewing{2},draft{2},closed{3} - + - field: assignedTo range: admin{3},user1{3},user2{3},user3{1} - + - field: openedBy range: admin{4},user1{3},user2{3} - + - field: openedDate range: '2024-01-01 10:00:00{5},2024-02-01 10:00:00{5}' - + - field: closedBy range: ''{7},admin{2},user1{1} - + - field: closedDate range: '0000-00-00 00:00:00'{7},'2024-03-01 10:00:00'{3} - + - field: deleted range: 0{10} \ No newline at end of file diff --git a/module/my/test/tao/yaml/storyreview_fetchrequirementsbysearch.yaml b/module/my/test/tao/yaml/storyreview_fetchrequirementsbysearch.yaml index 9194ddc378..bf5b4beb30 100644 --- a/module/my/test/tao/yaml/storyreview_fetchrequirementsbysearch.yaml +++ b/module/my/test/tao/yaml/storyreview_fetchrequirementsbysearch.yaml @@ -7,15 +7,15 @@ version: "1.0" fields: - field: story range: 1-10 - + - field: version range: 1{10} - + - field: reviewer range: admin{4},user1{3},user2{3} - + - field: result range: ''{8},pass{2} - + - field: reviewDate range: '0000-00-00 00:00:00'{8},'2024-01-15 10:00:00'{2} \ No newline at end of file diff --git a/module/my/test/zen/showworkcount.php b/module/my/test/zen/showworkcount.php old mode 100755 new mode 100644 index 7bd34a9ccd..efda49a362 --- a/module/my/test/zen/showworkcount.php +++ b/module/my/test/zen/showworkcount.php @@ -1,6 +1,5 @@ #!/usr/bin/env php showWorkCountTest(0, 20, 1)) && p('bugCount') && e('0'); // 测试默 r($myTest->showWorkCountTest(0, 20, 1)) && p('caseCount') && e('0'); // 测试默认参数时caseCount初始化为0 r($myTest->showWorkCountTest(100, 20, 1)) && p('hasTodoCount') && e('1'); // 测试recTotal为100时 r($myTest->showWorkCountTest(0, 50, 1)) && p('hasTodoCount') && e('1'); // 测试recPerPage为50时 -r($myTest->showWorkCountTest(0, 20, 2)) && p('hasTodoCount') && e('1'); // 测试pageID为2时 \ No newline at end of file +r($myTest->showWorkCountTest(0, 20, 2)) && p('hasTodoCount') && e('1'); // 测试pageID为2时 diff --git a/module/my/test/zen/showworkcountnotinopen.php b/module/my/test/zen/showworkcountnotinopen.php new file mode 100755 index 0000000000..5cdd1ea556 --- /dev/null +++ b/module/my/test/zen/showworkcountnotinopen.php @@ -0,0 +1,47 @@ +#!/usr/bin/env php +gen(0); +zenData('ticket')->gen(0); +zenData('issue')->gen(0); +zenData('risk')->gen(0); +zenData('nc')->gen(0); +zenData('auditplan')->gen(0); +zenData('meeting')->gen(0); +zenData('demand')->gen(0); +zenData('user')->gen(10); +zenData('product')->gen(5); + +su('admin'); + +$myTest = new myZenTest(); + +$emptyCount = array('task' => 0, 'story' => 0, 'bug' => 0, 'case' => 0, 'testtask' => 0, 'requirement' => 0, 'issue' => 0, 'risk' => 0, 'qa' => 0, 'meeting' => 0, 'ticket' => 0, 'feedback' => 0); +$nonEmptyCount = array('task' => 5, 'story' => 3, 'bug' => 2); + +r($myTest->showWorkCountNotInOpenTest($emptyCount)) && p('isBiz') && e('0'); // 测试开源版(open)下空count数组 +r($myTest->showWorkCountNotInOpenTest($emptyCount)) && p('isMax') && e('0'); // 测试开源版(open)下空count数组的isMax +r($myTest->showWorkCountNotInOpenTest($emptyCount)) && p('isIPD') && e('0'); // 测试开源版(open)下空count数组的isIPD +r($myTest->showWorkCountNotInOpenTest($emptyCount)) && p('feedback') && e('0'); // 测试开源版(open)下feedback计数为0 +r($myTest->showWorkCountNotInOpenTest($nonEmptyCount)) && p('task') && e('5'); // 测试非空count数组保留原有task计数 +r($myTest->showWorkCountNotInOpenTest($nonEmptyCount)) && p('story') && e('3'); // 测试非空count数组保留原有story计数 +r($myTest->showWorkCountNotInOpenTest($nonEmptyCount)) && p('bug') && e('2'); // 测试非空count数组保留原有bug计数 diff --git a/module/my/ui/doc.html.php b/module/my/ui/doc.html.php index 4e9953e557..361ac1792a 100644 --- a/module/my/ui/doc.html.php +++ b/module/my/ui/doc.html.php @@ -27,6 +27,17 @@ if($type == 'editedbyme') unset($config->my->doc->dtable->fieldList['editedBy']) $docs = initTableData($docs, $config->my->doc->dtable->fieldList, $this->doc); $cols = array_values($config->my->doc->dtable->fieldList); $data = array_values($docs); + +foreach($data as $doc) +{ + if(!isset($doc->actions)) continue; + foreach($doc->actions as &$action) + { + $actionName = $action['name']; + if(!empty($doc->frozen) && in_array($actionName, array('edit', 'delete'))) $action['hint'] = sprintf($this->lang->doc->frozenTips, $this->lang->doc->{$actionName}); + } +} + dtable ( set::cols($cols), diff --git a/module/my/ui/epic.html.php b/module/my/ui/epic.html.php index 12e428bc64..22e37f11c3 100644 --- a/module/my/ui/epic.html.php +++ b/module/my/ui/epic.html.php @@ -95,6 +95,14 @@ $footToolbar = array('items' => array if($canBatchAction) $config->my->epic->dtable->fieldList['id']['type'] = 'checkID'; $stories = initTableData($stories, $config->my->epic->dtable->fieldList, $this->story); +foreach($stories as $id => $story) +{ + if(!isset($story->actions)) continue; + foreach($story->actions as $key => $action) + { + if(!empty($story->frozen) && in_array($action['name'], array('edit', 'change'))) $stories[$id]->actions[$key]['hint'] = sprintf($lang->story->frozenTip, $lang->story->{$action['name']}); + } +} if($viewType == 'tiled') $config->my->epic->dtable->fieldList['title']['nestedToggle'] = false; $cols = array_values($config->my->epic->dtable->fieldList); diff --git a/module/my/ui/header.html.php b/module/my/ui/header.html.php index 2a9defd62e..02acc92adc 100644 --- a/module/my/ui/header.html.php +++ b/module/my/ui/header.html.php @@ -18,7 +18,7 @@ if($app->rawMethod == 'work') if($isOpenedURAndSR !== 0) $nameMap = array_merge($nameMap, array('requirement' => 'requirement')); if($config->enableER) $nameMap = array_merge($nameMap, array('epic' => 'epic')); if($isBiz !== 0 || $isMax !== 0 || $isIPD !== 0) $nameMap = array_merge($nameMap, array('feedback' => 'feedback', 'ticket' => 'ticket')); - if($isMax !== 0 || $isIPD !== 0) $nameMap = array_merge($nameMap, array('issue' => 'issue', 'risk' => 'risk', 'nc' => 'qa', 'myMeeting' => 'meeting')); + if($isMax !== 0 || $isIPD !== 0) $nameMap = array_merge($nameMap, array('issue' => 'issue', 'risk' => 'risk', 'nc' => 'qa', 'reviewissue' => 'reviewissue', 'myMeeting' => 'meeting')); if($isIPD !== 0) $nameMap = array_merge($nameMap, array('demand' => 'demand')); foreach($nameMap as $name => $countKey) diff --git a/module/my/ui/requirement.html.php b/module/my/ui/requirement.html.php index 813b259dc4..06a36ce218 100644 --- a/module/my/ui/requirement.html.php +++ b/module/my/ui/requirement.html.php @@ -98,6 +98,14 @@ $footToolbar = array('items' => array if($canBatchAction) $config->my->requirement->dtable->fieldList['id']['type'] = 'checkID'; $stories = initTableData($stories, $config->my->requirement->dtable->fieldList, $this->story); +foreach($stories as $id => $story) +{ + if(!isset($story->actions)) continue; + foreach($story->actions as $key => $action) + { + if(!empty($story->frozen) && in_array($action['name'], array('edit', 'change'))) $stories[$id]->actions[$key]['hint'] = sprintf($lang->story->frozenTip, $lang->story->{$action['name']}); + } +} if($viewType == 'tiled') $config->my->requirement->dtable->fieldList['title']['nestedToggle'] = false; $cols = array_values($config->my->requirement->dtable->fieldList); diff --git a/module/my/ui/story.html.php b/module/my/ui/story.html.php index 7acf76b61d..5ad2aed2f4 100644 --- a/module/my/ui/story.html.php +++ b/module/my/ui/story.html.php @@ -107,6 +107,7 @@ foreach($stories as $id => $story) foreach($story->actions as $key => $action) { if($action['name'] == 'create' && $story->isParent == '1') $stories[$id]->actions[$key]['disabled'] = 1; + if(!empty($story->frozen) && in_array($action['name'], array('edit', 'change'))) $stories[$id]->actions[$key]['hint'] = sprintf($lang->story->frozenTip, $lang->story->{$action['name']}); } } $story->estimate = helper::formatHours($story->estimate); diff --git a/module/my/zen.php b/module/my/zen.php index fd3b8793f0..970f13a2fd 100644 --- a/module/my/zen.php +++ b/module/my/zen.php @@ -162,16 +162,16 @@ class myZen extends my * @param int $recPerPage * @param int $pageID * @access public - * @return void + * @return array */ - public function showWorkCount(int $recTotal = 0, int $recPerPage = 20, int $pageID = 1): void + public function showWorkCount(int $recTotal = 0, int $recPerPage = 20, int $pageID = 1): array { /* Load pager. */ $this->app->loadClass('pager', true); if($this->app->getViewType() == 'mhtml') $recPerPage = 10; $pager = pager::init($recTotal, $recPerPage, $pageID); - $count = array('task' => 0, 'story' => 0, 'bug' => 0, 'case' => 0, 'testtask' => 0, 'requirement' => 0, 'issue' => 0, 'risk' => 0, 'qa' => 0, 'meeting' => 0, 'ticket' => 0, 'feedback' => 0); + $count = array('task' => 0, 'story' => 0, 'bug' => 0, 'case' => 0, 'testtask' => 0, 'requirement' => 0, 'epic' => 0, 'issue' => 0, 'risk' => 0, 'reviewissue' => 0, 'qa' => 0, 'meeting' => 0, 'ticket' => 0, 'feedback' => 0); /* Get the number of tasks assigned to me. */ $this->loadModel('task')->getUserTasks($this->app->user->account, 'assignedTo', 0, $pager); @@ -233,6 +233,7 @@ class myZen extends my $this->view->todoCount = $count; $this->view->isOpenedURAndSR = $isOpenedURAndSR; + return $count; } /** @@ -275,6 +276,11 @@ class myZen extends my $this->loadModel('risk')->getUserRisks('assignedTo', $this->app->user->account, 'id_desc', $pager); $count['risk'] = $pager->recTotal; + /* Get the number of reviewissues assigned to me. */ + $pager->recTotal = 0; + $this->loadModel('reviewissue')->getUserReviewissues('assignedTo', $this->app->user->account, 'id_desc', $pager); + $count['reviewissue'] = $pager->recTotal; + /* Get the number of nc assigned to me. */ $pager->recTotal = 0; $this->my->getNcList('assignedToMe', 'id_desc', $pager, 'active'); @@ -282,7 +288,7 @@ class myZen extends my /* Get the number of nc assigned to me. */ $pager->recTotal = 0; - $this->loadModel('auditplan')->getList(0, 'my', 'mychecking', '', 'id_desc', $pager); + $this->loadModel('auditplan')->getList(0, 'myChecking', '', 'id_desc', $pager); $auditplanCount = $pager->recTotal; $count['qa'] = $ncCount + $auditplanCount; diff --git a/module/personnel/test/lib/personnel.unittest.class.php b/module/personnel/test/lib/personnel.unittest.class.php index 68d98aa6e0..0de7c84a97 100644 --- a/module/personnel/test/lib/personnel.unittest.class.php +++ b/module/personnel/test/lib/personnel.unittest.class.php @@ -513,7 +513,7 @@ class personnelTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('updateParentWhitelist'); $method->setAccessible(true); - + $result = $method->invoke($this->objectModel, $objectType, $objectID, $accounts, $source, $updateType, $deletedAccounts, $objectTable); if(dao::isError()) return dao::getError(); @@ -530,7 +530,7 @@ class personnelTest { $parentWhitelist = $tester->dao->select('whitelist')->from(TABLE_PROJECT)->where('id')->eq($product->program)->fetch('whitelist'); $return .= "parent_whitelist:{$parentWhitelist};"; - + $acls = $tester->dao->select('account,source')->from(TABLE_ACL) ->where('objectID')->eq($product->program) ->andWhere('objectType')->eq('program') @@ -551,7 +551,7 @@ class personnelTest { $parentWhitelist = $tester->dao->select('whitelist')->from(TABLE_PROJECT)->where('id')->eq($sprint->project)->fetch('whitelist'); $return .= "parent_whitelist:{$parentWhitelist};"; - + $acls = $tester->dao->select('account,source')->from(TABLE_ACL) ->where('objectID')->eq($sprint->project) ->andWhere('objectType')->eq('project') @@ -581,29 +581,29 @@ class personnelTest public function setSelectObjectTipsTest(int $objectID, string $objectType, string $module): array { global $tester; - + $tester->app->loadLang('personnel'); $tester->app->loadLang('execution'); $tester->app->loadLang('product'); $tester->app->loadLang('program'); $tester->app->loadLang('project'); - + $tester->lang->personnel->selectObjectTips = '请选择一个%s白名单'; - + $objectName = $tester->lang->projectCommon . $tester->lang->execution->or . $tester->lang->execution->common; if($objectType == 'program') $objectName = $tester->lang->program->common; if($objectType == 'product') $objectName = $tester->lang->productCommon; if($objectType == 'project') $objectName = $tester->lang->projectCommon; $tip = sprintf($tester->lang->personnel->selectObjectTips, $objectName); - + if($objectType == 'sprint' && $module == 'execution') { $execution = $this->objectModel->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($objectID)->fetch(); $tip = !empty($execution) && $execution->type == 'kanban' ? str_replace($tester->lang->execution->common, $tester->lang->execution->kanban, $tip) : $tip; } - + if(dao::isError()) return dao::getError(); - + return array( 'tips' => $tip, 'objectName' => $objectName diff --git a/module/pivot/test/model/yaml/pivotspec_isversionchange.yaml b/module/pivot/test/model/yaml/pivotspec_isversionchange.yaml index aa658a8d1e..76f9968876 100644 --- a/module/pivot/test/model/yaml/pivotspec_isversionchange.yaml +++ b/module/pivot/test/model/yaml/pivotspec_isversionchange.yaml @@ -6,15 +6,15 @@ fields: - field: pivot note: 关联的pivot ID range: '1{3},2{4},3{3}' - + - field: version note: 版本号 range: '1.0,1.1,1.5,2.0{4},2.1{3}' - + - field: name note: 版本名称 range: '测试透视表版本{1-10}' - + - field: createdDate note: 创建时间 range: 'YYYY-MM-DD hh:mm:ss' diff --git a/module/pivot/test/tao/yaml/bug_getassignbuggroup.yaml b/module/pivot/test/tao/yaml/bug_getassignbuggroup.yaml index a8353c5e83..815eb6e4a7 100644 --- a/module/pivot/test/tao/yaml/bug_getassignbuggroup.yaml +++ b/module/pivot/test/tao/yaml/bug_getassignbuggroup.yaml @@ -7,40 +7,40 @@ fields: - field: id note: Bug ID range: 1-20 - + - field: product note: 产品ID range: 1{5},2{7},3{8} - + - field: assignedTo note: 指派给用户 range: user1{4},user2{5},user3{3},{2},closed{2},user4{4} - + - field: status note: Bug状态 range: active{15},resolved{3},closed{2} - + - field: deleted note: 是否删除 range: 0{18},1{2} - + - field: openedBy note: 由谁创建 range: admin{10},user1{5},user2{5} - + - field: openedDate note: 开启日期 range: '2024-01-01 00:00:00'{5},'2024-06-01 12:00:00'{10},'2024-09-01 18:00:00'{5} - + - field: title note: Bug标题 range: 'Bug Title %d' format: "%02d" - + - field: severity note: 严重程度 range: 1{5},2{7},3{5},4{3} - + - field: resolution note: 解决方案 range: ''{12},fixed{3},bydesign{2},duplicate{3} \ No newline at end of file diff --git a/module/pivot/test/tao/yaml/project_getprojectandexecutionnamequery.yaml b/module/pivot/test/tao/yaml/project_getprojectandexecutionnamequery.yaml index 7dd44367fc..cad5b8ef63 100644 --- a/module/pivot/test/tao/yaml/project_getprojectandexecutionnamequery.yaml +++ b/module/pivot/test/tao/yaml/project_getprojectandexecutionnamequery.yaml @@ -6,7 +6,7 @@ fields: - field: id note: 项目ID range: 1-15 - + - field: name note: 项目名称 range: '执行1,执行2,执行3,项目1,项目2,执行4,执行5,项目3,执行6,执行7,删除执行1,删除执行2,项目执行1,项目执行2,项目执行3' @@ -14,19 +14,19 @@ fields: - field: type note: 项目类型 range: 'stage{5},sprint{5},project{5}' - -- field: status + +- field: status note: 项目状态 range: 'wait{8},doing{4},closed{3}' - field: deleted note: 删除状态 range: '0{12},1{3}' - + - field: multiple note: 多执行标识 range: '1{10},0{5}' - field: project - note: 父项目ID + note: 父项目ID range: '0{5},1,1,2,2,3,3,3,4,4,5' \ No newline at end of file diff --git a/module/product/model.php b/module/product/model.php index 8d44f68f93..507bcaac27 100755 --- a/module/product/model.php +++ b/module/product/model.php @@ -2033,7 +2033,7 @@ class productModel extends model if($this->config->edition != 'open') { $flow = $this->loadModel('workflow')->getByModule($module); - if($flow && in_array($flow->app, array('scrum', 'waterfall'))) $flow->app = 'project'; + if($flow && in_array($flow->app, array('scrum', 'waterfall', 'kanbanProject'))) $flow->app = 'project'; if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "#app=$flow->app"; } diff --git a/module/product/test/zen/yaml/product_assignbrowsedata.yaml b/module/product/test/zen/yaml/product_assignbrowsedata.yaml index 9aa462438a..d13242f873 100644 --- a/module/product/test/zen/yaml/product_assignbrowsedata.yaml +++ b/module/product/test/zen/yaml/product_assignbrowsedata.yaml @@ -8,27 +8,27 @@ fields: - field: id note: 产品ID range: 1-10 - + - field: name note: 产品名称 range: 产品A,产品B,产品C,产品D,产品E - + - field: status note: 产品状态 range: normal{8},closed{2} - + - field: type note: 产品类型 range: normal{6},branch{4} - + - field: PO note: 产品负责人 range: admin,user1,user2,user3 - + - field: program note: 项目集ID range: 0{5},1{3},2{2} - + - field: acl note: 访问控制 range: open{7},private{2},custom{1} \ No newline at end of file diff --git a/module/product/test/zen/yaml/product_getproducts4dropmenu.yaml b/module/product/test/zen/yaml/product_getproducts4dropmenu.yaml index c0afbd6ad4..2534cde2bd 100644 --- a/module/product/test/zen/yaml/product_getproducts4dropmenu.yaml +++ b/module/product/test/zen/yaml/product_getproducts4dropmenu.yaml @@ -6,129 +6,129 @@ fields: - field: id note: 产品ID range: 1-10 - + - field: program note: 项目集ID range: 1-3 - + - field: name note: 产品名称 range: "产品A,产品B,产品C,产品D,产品E,Shadow产品,Workflow产品,Feedback产品,Project产品,Normal产品" - + - field: code note: 产品代码 range: "PRODA,PRODB,PRODC,PRODD,PRODE,SHADOWS,WORKFLOW,FEEDBACK,PROJECT,NORMAL" - + - field: shadow note: 影子产品标识 range: 0{8},1{2} - + - field: bind note: 绑定标识 range: "0" - + - field: line note: 产品线ID range: 0-2 - + - field: type note: 产品类型 range: "normal{8},branch{2}" - + - field: status note: 产品状态 range: "normal{7},closed{3}" - + - field: subStatus note: 子状态 range: "" - + - field: desc note: 产品描述 range: "这是产品A的描述,这是产品B的描述,这是产品C的描述,这是产品D的描述,这是产品E的描述,这是Shadow产品的描述,这是Workflow产品的描述,这是Feedback产品的描述,这是Project产品的描述,这是Normal产品的描述" - + - field: PO note: 产品负责人 range: "admin,user1,user2,user3" - + - field: QD note: 测试负责人 range: "admin,user1,user2" - + - field: RD note: 发布负责人 range: "admin,user1,user2" - + - field: feedback note: 反馈负责人 range: "admin,user1" - + - field: ticket note: 工单负责人 range: "admin" - + - field: workflowGroup note: 工作流组 range: 0{8},1{2} - + - field: acl note: 访问控制 range: "open{6},private{2},custom{2}" - + - field: groups note: 用户组 range: "1,2,3,1,2,2,3,1,2,3" - + - field: whitelist note: 白名单 range: "admin,user1,admin,user2,admin,user1,user2,admin,user1,admin" - + - field: reviewer note: 评审人员 range: "admin,user1,admin,user2,admin,user1,user2,admin,user1,admin" - + - field: PMT note: PMT成员 range: "admin,user1,admin,user2,admin,user1,user2,admin,user1,admin" - + - field: draftEpics note: 草稿史诗数量 range: 0-5 - + - field: activeEpics note: 激活史诗数量 range: 0-8 - + - field: changingEpics note: 变更史诗数量 range: 0-3 - + - field: order note: 排序 range: 10-50:5 - + - field: vision note: 视角 range: "rnd" - + - field: createdBy note: 创建者 range: "admin,user1,user2" - + - field: createdDate note: 创建时间 range: "(-1M)-(0D):1D" format: "YYYY-MM-DD hh:mm:ss" - + - field: editedBy note: 编辑者 range: "admin,user1,user2" - + - field: editedDate note: 编辑时间 range: "(-1w)-(0D):1D" format: "YYYY-MM-DD hh:mm:ss" - + - field: deleted note: 是否删除 range: "0" \ No newline at end of file diff --git a/module/product/ui/browse.html.php b/module/product/ui/browse.html.php index 367dfcc277..88a155f9bd 100644 --- a/module/product/ui/browse.html.php +++ b/module/product/ui/browse.html.php @@ -70,7 +70,7 @@ $fnGenerateSideBar = function() use ($moduleTree, $moduleID, $productID, $branch }; /* Build create story button. */ -$fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $storyType, $productID, $branch, $moduleID, $projectID, $project, $projectProducts) +$fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $storyType, $productID, $branch, $moduleID, $projectID, $project, $projectProducts, $hasFrozenStories) { if(!common::canModify('product', $product)) return null; if(!empty($project) && !common::canModify('project', $project)) return null; @@ -142,11 +142,13 @@ $fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $s btn ( setClass(($app->tab != 'product' ? 'secondary' : 'primary') . ' create-story-btn'), + $isProjectStory && $hasFrozenStories ? setClass('disabled') : null, + $isProjectStory && $hasFrozenStories ? set::hint(sprintf($lang->story->frozenTip, $createBtnTitle)) : null, set::icon('plus'), set::text($createBtnTitle), set::url($createBtnLink) ), - empty($items) ? null : dropdown + empty($items) || ($isProjectStory && $hasFrozenStories) ? null : dropdown ( btn(setClass('dropdown-toggle'), setClass($app->tab != 'product' ? 'secondary' : 'primary'), setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))), set::placement('bottom-end'), @@ -165,7 +167,7 @@ $fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $s }; /* Build link story button. */ -$fnBuildLinkStoryButton = function() use($lang, $app, $product, $projectHasProduct, $project, $storyType) +$fnBuildLinkStoryButton = function() use($lang, $app, $product, $projectHasProduct, $project, $storyType, $isProjectStory, $hasFrozenStories) { if(!common::canModify('product', $product)) return null; if(!empty($project) && !common::canModify('project', $project)) return null; @@ -187,20 +189,22 @@ $fnBuildLinkStoryButton = function() use($lang, $app, $product, $projectHasProdu $canLinkStory = common::hasPriv('projectstory', 'linkStory'); $canlinkPlanStory = !empty($product) && common::hasPriv('projectstory', 'importPlanStories') && $storyType == 'story' && !$project->charter; $linkStoryUrl = $this->createLink('projectstory', 'linkStory', "project=$project->id&browseType=¶m=0&orderBy=id_desc&recPerPage=50&pageID=1&extra=&storyType=$storyType"); - $linkItem = array('text' => $lang->execution->linkStory, 'url' => $linkStoryUrl); - $linkPlanItem = array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm'); + $linkItem = array('text' => $lang->execution->linkStory, 'url' => $linkStoryUrl, 'hint' => $isProjectStory && $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->execution->linkStory) : ''); + $linkPlanItem = array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm', 'hint' => $isProjectStory && $hasFrozenStories ? sprintf($lang->story->frozenTip, $lang->execution->linkStoryByPlan) : ''); if($canLinkStory && $canlinkPlanStory) { return btngroup ( btn( setClass('btn primary'), + $isProjectStory && $hasFrozenStories ? setClass('disabled') : null, + $isProjectStory && $hasFrozenStories ? set::hint(sprintf($lang->story->frozenTip, $lang->execution->linkStory)) : null, set::icon('link'), set::url($linkStoryUrl), setData('app', $app->tab), $lang->execution->linkStory ), - dropdown + $isProjectStory && $hasFrozenStories ? null : dropdown ( btn(setClass('btn primary dropdown-toggle'), setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))), @@ -210,8 +214,8 @@ $fnBuildLinkStoryButton = function() use($lang, $app, $product, $projectHasProdu ); } - if($canLinkStory && !$canlinkPlanStory) return item(set($linkItem + array('class' => 'btn primary link-story-btn', 'icon' => 'link'))); - if($canlinkPlanStory && !$canLinkStory) return item(set($linkPlanItem + array('class' => 'btn primary', 'icon' => 'link'))); + if($canLinkStory && !$canlinkPlanStory) return item(set($linkItem + array('class' => 'btn primary link-story-btn' . ($isProjectStory && $hasFrozenStories ? ' disabled' : ''), 'icon' => 'link'))); + if($canlinkPlanStory && !$canLinkStory) return item(set($linkPlanItem + array('class' => 'btn primary' . ($isProjectStory && $hasFrozenStories ? ' disabled' : ''), 'icon' => 'link'))); }; /* DataTable columns. */ @@ -253,7 +257,7 @@ foreach($stories as $story) } /* Generate toolbar of DataTable footer. */ -$fnGenerateFootToolbar = function() use ($lang, $app, $product, $productID, $project, $storyType, $browseType, $isProjectStory, $projectHasProduct, $storyProductID, $projectID, $branch, $users, $branchTagOption, $modules, $plans, $branchID, $gradePairs, $config,$noclosedRoadmaps, $gradeGroup) +$fnGenerateFootToolbar = function() use ($lang, $app, $product, $productID, $project, $storyType, $browseType, $isProjectStory, $projectHasProduct, $storyProductID, $projectID, $branch, $users, $branchTagOption, $modules, $plans, $branchID, $gradePairs, $config,$noclosedRoadmaps, $gradeGroup, $hasFrozenStories) { /* Flag variables of permissions. */ $canBeChanged = common::canModify('product', $product); @@ -280,7 +284,7 @@ $fnGenerateFootToolbar = function() use ($lang, $app, $product, $productID, $pro $canBatchChangeBranch = $canBeChanged && hasPriv($storyType, 'batchChangeBranch') && $product && $product->type != 'normal' && $productID; $canBatchChangeModule = $canBeChanged && hasPriv($storyType, 'batchChangeModule') && $productID && (($product->type != 'normal' && $branchID != 'all') || $product->type == 'normal') && !$isProjectStory; $canBatchChangeParent = $canBeChanged && hasPriv($storyType, 'batchChangeParent') && !($storyType == 'epic' && count($gradeGroup['epic']) < 2) && $app->tab == 'product'; - $canBatchUnlink = $canBeChanged && $projectHasProduct && hasPriv('projectstory', 'batchUnlinkStory'); + $canBatchUnlink = empty($hasFrozenStories) && $canBeChanged && $projectHasProduct && hasPriv('projectstory', 'batchUnlinkStory'); $canBatchImportToLib = $canBeChanged && $isProjectStory && in_array($this->config->edition, array('max', 'ipd')) && hasPriv('story', 'batchImportToLib') && helper::hasFeature('storylib'); $canBatchChangeRoadmap = $canBeChanged && hasPriv($storyType, 'batchChangeRoadmap') && $config->vision == 'or' && ($storyType == 'requirement' || $storyType == 'epic'); $canBatchAction = $canBatchEdit || $canBatchClose || $canBatchReview || $canBatchChangeGrade || $canBatchChangeStage || $canBatchChangeModule || $canBatchChangePlan || $canBatchChangeParent || $canBatchAssignTo || $canBatchUnlink || $canBatchImportToLib || $canBatchChangeBranch || $canBatchChangeRoadmap; @@ -559,7 +563,7 @@ dtable ($isFromDoc || $isFromAI) ? null : set::sortLink($sortLink), ($isFromDoc || $isFromAI) ? null : set::checkInfo(jsRaw("function(checkedIdList){return window.setStatistics(this, checkedIdList, '{$summary}');}")), ($isFromDoc || $isFromAI) ? null : set::createTip($lang->story->create), - ($isFromDoc || $isFromAI) ? null : set::createLink($createStoryLink) + ($isFromDoc || $isFromAI || ($isProjectStory && $hasFrozenStories)) ? null : set::createLink($createStoryLink) ); modal(set::id('#batchUnlinkStoryBox')); diff --git a/module/product/zen.php b/module/product/zen.php index 1352103e03..ee01528e34 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -1492,6 +1492,7 @@ class productZen extends product $this->view->roadmaps = ($this->config->edition == 'ipd' && $storyType != 'story') ? array(0 => '') + $this->loadModel('roadmap')->getPairs($productID) : array(); $this->view->noclosedRoadmaps = ($this->config->edition == 'ipd' && $storyType != 'story') ? array(0 => '') + $this->loadModel('roadmap')->getPairs($productID, '', 'noclosed') : array(); + $this->view->hasFrozenStories = $isProjectStory ? $this->loadModel('project')->hasFrozenObject($projectID, 'SRS') : false; $this->display(); } diff --git a/module/productplan/test/lib/productplanzen.unittest.class.php b/module/productplan/test/lib/productplanzen.unittest.class.php index 6ca1c8bde3..7c4d651757 100644 --- a/module/productplan/test/lib/productplanzen.unittest.class.php +++ b/module/productplan/test/lib/productplanzen.unittest.class.php @@ -12,7 +12,7 @@ class productplanZenTest $this->objectModel = $tester->loadModel('productplan'); } - + /** * 模拟buildActionsList方法的逻辑 * Simulate buildActionsList method logic. @@ -70,7 +70,7 @@ class productplanZenTest $result->product = $product; $result->branchID = $branchID; $result->orderBy = $orderBy; - + // 模拟assignKanbanData方法的核心逻辑 if($product->type == 'normal') { @@ -82,7 +82,7 @@ class productplanZenTest $result->type = 'branch'; $result->branches = array(); // 简化返回 } - + return $result; } @@ -169,11 +169,11 @@ class productplanZenTest public function setSessionForViewPageTest(int $planID, string $type, string $orderBy, int $pageID, int $recPerPage): array { global $app; - + $result = array(); $beforeStoryList = isset($_SESSION['storyList']) ? $_SESSION['storyList'] : null; $beforeBugList = isset($_SESSION['bugList']) ? $_SESSION['bugList'] : null; - + // 模拟setSessionForViewPage的逻辑 if(in_array($type, array('story', 'bug')) && ($orderBy != 'order_desc' || $pageID != 1 || $recPerPage != 100)) { @@ -186,13 +186,13 @@ class productplanZenTest $_SESSION['bugList'] = $app->getURI(true); } } - + $result['beforeStoryList'] = $beforeStoryList; $result['beforeBugList'] = $beforeBugList; $result['afterStoryList'] = isset($_SESSION['storyList']) ? $_SESSION['storyList'] : null; $result['afterBugList'] = isset($_SESSION['bugList']) ? $_SESSION['bugList'] : null; $result['sessionChanged'] = ($beforeStoryList != $result['afterStoryList']) || ($beforeBugList != $result['afterBugList']); - + return $result; } @@ -209,9 +209,9 @@ class productplanZenTest $method = $reflection->getMethod('assignViewData'); $method->setAccessible(true); $method->invoke($this->objectZen, $plan); - + if(dao::isError()) return dao::getError(); - + $result = array(); $result['parentPlan'] = isset($this->objectZen->view->parentPlan) ? $this->objectZen->view->parentPlan->id : null; $result['childrenPlans'] = isset($this->objectZen->view->childrenPlans) ? count($this->objectZen->view->childrenPlans) : 0; @@ -221,7 +221,7 @@ class productplanZenTest $result['usersSet'] = isset($this->objectZen->view->users) ? 'set' : 'not_set'; $result['plansSet'] = isset($this->objectZen->view->plans) ? 'set' : 'not_set'; $result['modulesSet'] = isset($this->objectZen->view->modules) ? 'set' : 'not_set'; - + return $result; } @@ -237,18 +237,18 @@ class productplanZenTest public function buildBugSearchFormTest(object $plan, int $queryID, string $orderBy): array { global $config; - + $beforeActionURL = isset($config->bug->search['actionURL']) ? $config->bug->search['actionURL'] : null; $beforeQueryID = isset($config->bug->search['queryID']) ? $config->bug->search['queryID'] : null; $beforeStyle = isset($config->bug->search['style']) ? $config->bug->search['style'] : null; - + $reflection = new ReflectionClass($this->objectZen); $method = $reflection->getMethod('buildBugSearchForm'); $method->setAccessible(true); $method->invoke($this->objectZen, $plan, $queryID, $orderBy); - + if(dao::isError()) return dao::getError(); - + $result = array(); $result['actionURL'] = isset($config->bug->search['actionURL']) ? $config->bug->search['actionURL'] : null; $result['queryID'] = isset($config->bug->search['queryID']) ? $config->bug->search['queryID'] : null; @@ -258,7 +258,7 @@ class productplanZenTest $result['moduleValues'] = isset($config->bug->search['params']['module']['values']) ? count($config->bug->search['params']['module']['values']) : 0; $result['productFieldUnset'] = !isset($config->bug->search['fields']['product']); $result['branchHandled'] = true; - + return $result; } diff --git a/module/productplan/test/zen/buildbugsearchform.php b/module/productplan/test/zen/buildbugsearchform.php index a930a19824..37efada609 100755 --- a/module/productplan/test/zen/buildbugsearchform.php +++ b/module/productplan/test/zen/buildbugsearchform.php @@ -106,4 +106,4 @@ r($productplanTest->buildBugSearchFormTest($platformPlan, 0, 'pri_desc')) && p(' r($productplanTest->buildBugSearchFormTest($normalPlan, 0, 'status_desc')) && p('queryID,style,hasProductField') && e('0,simple,0'); r($productplanTest->buildBugSearchFormTest($normalPlan, 5, 'title_asc')) && p('queryID,hasPlanParam,hasModuleParam') && e('5,1,1'); r($productplanTest->buildBugSearchFormTest($branchPlan, 3, 'id_desc')) && p('queryID,hasPlanParam,hasModuleParam,hasBranchField') && e('3,1,1,1'); -r($productplanTest->buildBugSearchFormTest($platformPlan, 1, 'pri_asc')) && p('style,hasProductField,hasBranchField') && e('simple,0,1'); \ No newline at end of file +r($productplanTest->buildBugSearchFormTest($platformPlan, 1, 'pri_asc')) && p('style,hasProductField,hasBranchField') && e('simple,0,1'); diff --git a/module/productplan/test/zen/yaml/zt_productplan_builddataforbrowse.yaml b/module/productplan/test/zen/yaml/zt_productplan_builddataforbrowse.yaml index c599040c79..606118756d 100644 --- a/module/productplan/test/zen/yaml/zt_productplan_builddataforbrowse.yaml +++ b/module/productplan/test/zen/yaml/zt_productplan_builddataforbrowse.yaml @@ -10,64 +10,64 @@ fields: range: 1-10 prefix: "" postfix: "" - + - field: product note: 产品ID range: 1-3 - + - field: branch note: 分支ID range: 0{3},1{2},2{2},'1,2'{1},'2,3'{1},'0,1'{1} - + - field: parent note: 父计划ID range: 0{8},-1{1},1{1} - + - field: title note: 计划标题 range: 计划{3},版本{2},里程碑{2},迭代{2},发布{1} format: "%s%d" - + - field: status note: 计划状态 range: wait{4},doing{3},done{2},closed{1} - + - field: desc note: 计划描述 range: 简单描述{3},'

    包含HTML标签的描述

    '{2},'多行\n描述内容'{2},'{3} - + - field: begin note: 开始时间 range: 2024-01-01{3},2024-02-01{2},2024-03-01{2},2030-01-01{2},''{1} - + - field: end note: 结束时间 range: 2024-12-31{3},2024-11-30{2},2024-10-31{2},2030-01-01{2},''{1} - + - field: finishedDate note: 完成时间 range: 2024-12-31 23:59:59{2},''{8} - + - field: closedDate note: 关闭时间 range: 2024-12-31 23:59:59{1},''{9} - + - field: order note: 排序 range: 1{3},2{2},3{2},4{2},5{1} - + - field: closedReason note: 关闭原因 range: ''{9},done{1} - + - field: createdBy note: 创建者 range: admin{5},user1{3},user2{2} - + - field: createdDate note: 创建时间 range: 2024-01-01 10:00:00{3},2024-02-01 10:00:00{2},2024-03-01 10:00:00{2},2024-04-01 10:00:00{2},2024-05-01 10:00:00{1} - + - field: deleted note: 是否删除 range: '0'{10} \ No newline at end of file diff --git a/module/program/zen.php b/module/program/zen.php index 9d75b17226..64f931b808 100644 --- a/module/program/zen.php +++ b/module/program/zen.php @@ -361,7 +361,7 @@ class programZen extends program if($this->config->edition != 'open') { $flow = $this->loadModel('workflow')->getByModule($moduleName); - if($flow && in_array($flow->app, array('scrum', 'waterfall'))) $flow->app = 'project'; + if($flow && in_array($flow->app, array('scrum', 'waterfall', 'kanbanProject'))) $flow->app = 'project'; if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=$programID&moduleName=$moduleName") . "#app=$flow->app"; } if($moduleName == 'project') diff --git a/module/programplan/config/form.php b/module/programplan/config/form.php index 1f0f691d53..cf8a7975e2 100644 --- a/module/programplan/config/form.php +++ b/module/programplan/config/form.php @@ -11,28 +11,29 @@ $config->programplan->form->create['order'] = array('label' => '', $config->programplan->form->create['name'] = array('label' => $lang->nameAB, 'type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'base' => true, 'filter' => 'trim'); $config->programplan->form->create['code'] = array('label' => $lang->code, 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => '', 'filter' => 'trim'); $config->programplan->form->create['PM'] = array('label' => $lang->programplan->PMAB, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => '', 'options' => ''); -if(!empty($config->setPercent)) $config->programplan->form->create['percent'] = array('label' => $lang->programplan->percent, 'type' => 'float', 'control' => 'text', 'required' => false, 'default' => 0); -$config->programplan->form->create['attribute'] = array('label' => $lang->programplan->attribute, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => 0, 'options' => $lang->stage->typeList); -$config->programplan->form->create['type'] = array('label' => $lang->execution->method, 'type' => 'string', 'control' => 'picker', 'required' => true, 'default' => 'stage', 'options' => $lang->execution->typeList); -$config->programplan->form->create['point'] = array('label' => $lang->programplan->point, 'type' => 'array', 'control' => 'picker', 'required' => false, 'default' => '', 'options' => array(''), 'multiple' => true); -$config->programplan->form->create['parallel'] = array('label' => '', 'type' => 'int', 'control' => 'hidden', 'required' => false, 'default' => 0); -$config->programplan->form->create['acl'] = array('label' => $lang->programplan->acl, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => 'open', 'options' => $lang->execution->aclList); -$config->programplan->form->create['begin'] = array('label' => $lang->programplan->begin, 'type' => 'date', 'control' => 'date', 'required' => true, 'default' => null, 'skipRequired' => array('enabled' => 'off')); -$config->programplan->form->create['end'] = array('label' => $lang->programplan->end, 'type' => 'date', 'control' => 'date', 'required' => true, 'default' => null, 'skipRequired' => array('enabled' => 'off')); -$config->programplan->form->create['realBegan'] = array('label' => $lang->programplan->realBegan, 'type' => 'date', 'control' => 'date', 'required' => false, 'default' => null); -$config->programplan->form->create['realEnd'] = array('label' => $lang->programplan->realEnd, 'type' => 'date', 'control' => 'date', 'required' => false, 'default' => null); -$config->programplan->form->create['milestone'] = array('label' => $lang->programplan->milestone, 'type' => 'int', 'control' => 'radioList','required' => false, 'default' => 0, 'options' => $lang->programplan->milestoneList); -$config->programplan->form->create['desc'] = array('label' => $lang->programplan->desc, 'type' => 'string', 'control' => 'textarea', 'required' => false, 'default' => '', 'filter' => 'trim'); -$config->programplan->form->create['output'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => ''); -$config->programplan->form->create['level'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => 0); -$config->programplan->form->create['syncData'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => 0); +if(!empty($config->setPercent)) $config->programplan->form->create['percent'] = array('label' => $lang->programplan->percent, 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => ''); +$config->programplan->form->create['attribute'] = array('label' => $lang->programplan->attribute, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => 0, 'options' => $lang->stage->typeList); +$config->programplan->form->create['type'] = array('label' => $lang->execution->method, 'type' => 'string', 'control' => 'picker', 'required' => true, 'default' => 'stage', 'options' => $lang->execution->typeList); +$config->programplan->form->create['point'] = array('label' => $lang->programplan->point, 'type' => 'array', 'control' => 'picker', 'required' => false, 'default' => '', 'options' => array(''), 'multiple' => true); +$config->programplan->form->create['parallel'] = array('label' => '', 'type' => 'int', 'control' => 'hidden', 'required' => false, 'default' => 0); +$config->programplan->form->create['acl'] = array('label' => $lang->programplan->acl, 'type' => 'string', 'control' => 'picker', 'required' => false, 'default' => 'open', 'options' => $lang->execution->aclList); +$config->programplan->form->create['begin'] = array('label' => $lang->programplan->begin, 'type' => 'date', 'control' => 'date', 'required' => true, 'default' => null, 'skipRequired' => array('enabled' => 'off')); +$config->programplan->form->create['end'] = array('label' => $lang->programplan->end, 'type' => 'date', 'control' => 'date', 'required' => true, 'default' => null, 'skipRequired' => array('enabled' => 'off')); +$config->programplan->form->create['realBegan'] = array('label' => $lang->programplan->realBegan, 'type' => 'date', 'control' => 'date', 'required' => false, 'default' => null); +$config->programplan->form->create['realEnd'] = array('label' => $lang->programplan->realEnd, 'type' => 'date', 'control' => 'date', 'required' => false, 'default' => null); +$config->programplan->form->create['milestone'] = array('label' => $lang->programplan->milestone, 'type' => 'int', 'control' => 'radioList','required' => false, 'default' => 0, 'options' => $lang->programplan->milestoneList); +$config->programplan->form->create['desc'] = array('label' => $lang->programplan->desc, 'type' => 'string', 'control' => 'textarea', 'required' => false, 'default' => '', 'filter' => 'trim'); +$config->programplan->form->create['output'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => ''); +$config->programplan->form->create['level'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => 0); +$config->programplan->form->create['syncData'] = array('label' => '', 'type' => 'string', 'control' => 'text', 'required' => false, 'default' => 0); +$config->programplan->form->create['defaultPoint'] = array('label' => '', 'type' => 'array', 'control' => 'picker', 'required' => false, 'default' => '', 'options' => array(''), 'multiple' => true); $config->programplan->form->edit = array(); $config->programplan->form->edit['parent'] = array('required' => false, 'type' => 'int', 'default' => 0); $config->programplan->form->edit['name'] = array('required' => true, 'type' => 'string', 'default' => ''); $config->programplan->form->edit['code'] = array('required' => false, 'type' => 'string', 'default' => ''); $config->programplan->form->edit['PM'] = array('required' => false, 'type' => 'string', 'default' => ''); -$config->programplan->form->edit['percent'] = array('required' => false, 'type' => 'float', 'default' => 0); +$config->programplan->form->edit['percent'] = array('required' => false, 'type' => 'string', 'default' => ''); $config->programplan->form->edit['attribute'] = array('required' => false, 'type' => 'string', 'default' => ''); $config->programplan->form->edit['acl'] = array('required' => false, 'type' => 'string', 'default' => ''); $config->programplan->form->edit['begin'] = array('required' => true, 'type' => 'string', 'default' => null); diff --git a/module/programplan/control.php b/module/programplan/control.php index 7312954196..617b7cefd4 100644 --- a/module/programplan/control.php +++ b/module/programplan/control.php @@ -150,6 +150,7 @@ class programplan extends control $viewData->syncData = $syncData; $this->programplanZen->buildCreateView($viewData); + $this->display(); } /** @@ -187,6 +188,7 @@ class programplan extends control if(empty($plan->realBegan)) $plan->realBegan = null; if(empty($plan->realEnd)) $plan->realEnd = null; + if(empty($plan->percent)) $plan->percent = 0; $plan = $this->programplanZen->prepareEditPlan($planID, $projectID, $plan, isset($parentStage) ? $parentStage : null); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); diff --git a/module/programplan/js/create.ui.js b/module/programplan/js/create.ui.js index 4ce4548a08..53cb882992 100644 --- a/module/programplan/js/create.ui.js +++ b/module/programplan/js/create.ui.js @@ -243,7 +243,7 @@ window.handleRenderRow = function($row, index, data) $row.find('[data-name="attribute"]').find('.picker-box').on('inited', function(e, info){ info[0].render({disabled: true}); }); - const $point = $row.find('[data-name="point"]'); + const $point = $row.find('[data-name="point"],[data-name="defaultPoint"]'); $point.find('.picker-box').on('inited', function(e, info) { let items = [{text: '', value: ''}]; @@ -251,21 +251,21 @@ window.handleRenderRow = function($row, index, data) if(typeof data != 'undefined' && typeof data.attribute != 'undefined' && !disabled) { - const attribute = data.attribute; - for(let point in ipdStagePoint[attribute]) + const stageID = data.stageID; + for(let point in ipdStagePoint[stageID]) { let disabled = false - let value = ipdStagePoint[attribute][point]; - let hint = value; + let text = ipdStagePoint[stageID][point]; + let hint = text; /* 如果已经评审过,则置灰不能选。*/ - if(reviewedPoints[value] !== undefined && reviewedPoints[value].disabled) + if(reviewedPoints[point] !== undefined && reviewedPoints[point].disabled) { disabled = true; hint = reviewedPointTip; } - items.push({text: value, value: value, disabled: disabled, hint: hint}); + items.push({text: text, value: point, disabled: disabled, hint: hint}); } } @@ -296,11 +296,11 @@ window.handleRenderRow = function($row, index, data) { let selection = $row.find('div.picker-multi-selection'); selection.each(function(index, element) { - /* 获取title。*/ - let $title = $(element).attr('title'); + /* 获取评审点ID。*/ + let pointID = $(element).find('div.picker-deselect-btn').data('value'); /* 如果该评审点以提交评审,则不能取消选择。*/ - if(reviewedPoints[$title] !== undefined && reviewedPoints[$title].disabled) + if(reviewedPoints[pointID] !== undefined && reviewedPoints[pointID].disabled) { $(element).attr('title', reviewedPointTip); // 修改title $(element).find(".picker-deselect-btn").remove(); // 禁用点击事件 diff --git a/module/programplan/lang/de.php b/module/programplan/lang/de.php index 681c466a76..04fd962629 100644 --- a/module/programplan/lang/de.php +++ b/module/programplan/lang/de.php @@ -117,6 +117,7 @@ $lang->programplan->confirmDelete = 'Do you want to delete the current plan? $lang->programplan->confirmChangeAttr = 'The type of the sub-stage will be adjusted to "%s" synchronously according to the type of the parent stage after modification. Do you want to save?'; $lang->programplan->noticeChangeAttr = 'The type of the sub-stage will be adjusted to "%s" synchronously according to the type of the parent stage after modification'; $lang->programplan->workloadTips = 'The proportion of the sub stage workload is divided by 100%.'; +$lang->programplan->emptyStageTip = 'Please contact the administrator to set up the IPD stage list in the "Project Process Configuration" in the backend.'; $lang->programplan->stageCustom['date'] = 'Show Date'; $lang->programplan->stageCustom['task'] = 'Show Task'; @@ -134,7 +135,7 @@ $lang->programplan->ganttCustom['delay'] = 'Delay'; $lang->programplan->ganttCustom['delayDays'] = 'Delay days'; $lang->programplan->error = new stdclass(); -$lang->programplan->error->percentNumber = '"Workload %" must be digits.'; +$lang->programplan->error->percentNumber = '"Workload %" must be non-negative'; $lang->programplan->error->planFinishSmall = 'The "End" date must be > the "Begin" date.'; $lang->programplan->error->percentOver = 'The sum of "Workload %" cannot exceed 100% of one stage.'; $lang->programplan->error->createdTask = 'The task has been decomposed. Sub phases cannot be added.'; diff --git a/module/programplan/lang/en.php b/module/programplan/lang/en.php index d5151508cd..3a044306f2 100644 --- a/module/programplan/lang/en.php +++ b/module/programplan/lang/en.php @@ -117,6 +117,7 @@ $lang->programplan->confirmDelete = 'Do you want to delete the current plan? $lang->programplan->confirmChangeAttr = 'The type of the sub-stage will be adjusted to "%s" synchronously according to the type of the parent stage after modification. Do you want to save?'; $lang->programplan->noticeChangeAttr = 'The type of the sub-stage will be adjusted to "%s" synchronously according to the type of the parent stage after modification.'; $lang->programplan->workloadTips = 'The workload of the sub stage is divided by 100%.'; +$lang->programplan->emptyStageTip = 'Please contact the administrator to set up the IPD stage list in the "Project Process Configuration" in the backend.'; $lang->programplan->stageCustom['date'] = 'Show Date'; $lang->programplan->stageCustom['task'] = 'Show Task'; @@ -134,7 +135,7 @@ $lang->programplan->ganttCustom['delay'] = 'Delay'; $lang->programplan->ganttCustom['delayDays'] = 'Delay days'; $lang->programplan->error = new stdclass(); -$lang->programplan->error->percentNumber = '"Workload %" must be digits.'; +$lang->programplan->error->percentNumber = '"Workload %" must be non-negative'; $lang->programplan->error->planFinishSmall = 'The "End" date must be > the "Begin" date.'; $lang->programplan->error->percentOver = 'The sum of "Workload %" cannot exceed 100% of one stage.'; $lang->programplan->error->createdTask = 'The task is decomposed. Sub stages cannot be added.'; diff --git a/module/programplan/lang/fr.php b/module/programplan/lang/fr.php index cb7976fe87..5bb906fa28 100644 --- a/module/programplan/lang/fr.php +++ b/module/programplan/lang/fr.php @@ -117,6 +117,7 @@ $lang->programplan->confirmDelete = 'Do you want to delete the current plan? $lang->programplan->confirmChangeAttr = 'The type of the sub-stage will be adjusted to "%s" synchronously according to the type of the parent stage after modification. Do you want to save?'; $lang->programplan->noticeChangeAttr = 'The type of the sub-stage will be adjusted to "%s" synchronously according to the type of the parent stage after modification.'; $lang->programplan->workloadTips = 'The proportion of the sub stage workload is divided by 100%.'; +$lang->programplan->emptyStageTip = 'Please contact the administrator to set up the IPD stage list in the "Project Process Configuration" in the backend.'; $lang->programplan->stageCustom['date'] = 'Show Date'; $lang->programplan->stageCustom['task'] = 'Show Task'; @@ -134,7 +135,7 @@ $lang->programplan->ganttCustom['delay'] = 'Delay'; $lang->programplan->ganttCustom['delayDays'] = 'Delay days'; $lang->programplan->error = new stdclass(); -$lang->programplan->error->percentNumber = '"Workload %" must be digits.'; +$lang->programplan->error->percentNumber = '"Workload %" must be non-negative'; $lang->programplan->error->planFinishSmall = 'The "End" date must be > the "Begin" date.'; $lang->programplan->error->percentOver = 'The sum of "Workload %" cannot exceed 100% of one stage.'; $lang->programplan->error->createdTask = 'The task has been decomposed. Sub phases cannot be added.'; diff --git a/module/programplan/lang/zh-cn.php b/module/programplan/lang/zh-cn.php index 8076fb682f..6f1dc72612 100644 --- a/module/programplan/lang/zh-cn.php +++ b/module/programplan/lang/zh-cn.php @@ -117,6 +117,7 @@ $lang->programplan->confirmDelete = '确定要删除当前计划吗?'; $lang->programplan->confirmChangeAttr = '修改后子阶段的类型将根据父阶段类型同步调整为“%s”,是否保存?'; $lang->programplan->noticeChangeAttr = '修改后子阶段的类型将根据父阶段类型同步调整为“%s”'; $lang->programplan->workloadTips = '子阶段工作量占比按百分百的比例进行拆分'; +$lang->programplan->emptyStageTip = '请联系管理员,在后台的“项目流程配置”中设置IPD阶段列表。'; $lang->programplan->stageCustom['date'] = '显示日期'; $lang->programplan->stageCustom['task'] = '显示任务'; @@ -134,7 +135,7 @@ $lang->programplan->ganttCustom['delay'] = '是否延期'; $lang->programplan->ganttCustom['delayDays'] = '延期天数'; $lang->programplan->error = new stdclass(); -$lang->programplan->error->percentNumber = '"工作量占比"必须为数字'; +$lang->programplan->error->percentNumber = '"工作量占比"必须为非负数'; $lang->programplan->error->planFinishSmall = '"计划完成时间"必须大于"计划开始时间"'; $lang->programplan->error->percentOver = '相同父阶段的子阶段工作量占比之和不超过100%'; $lang->programplan->error->createdTask = '已分解任务,不可添加子阶段'; diff --git a/module/programplan/model.php b/module/programplan/model.php index 369b89a4f6..ba5d3923bb 100755 --- a/module/programplan/model.php +++ b/module/programplan/model.php @@ -399,8 +399,6 @@ class programplanModel extends model $parents[$level] = $stageID; unset($plan->id, $plan->type); - if(in_array($this->config->edition, array('max', 'ipd'))) $plan = $this->execution->changeExecutionDeliverable($stageID, $plan); - $changes = $this->programplanTao->updateRow($stageID, $projectID, $plan); if(dao::isError()) return false; @@ -418,22 +416,6 @@ class programplanModel extends model } else { - if(!empty($project->deliverable) && in_array($this->config->edition, array('max', 'ipd'))) - { - $projectModel = $project->model; - if($projectModel == 'agileplus') $projectModel = 'scrum'; - if($projectModel == 'waterfallplus') $projectModel = 'waterfall'; - - $projectType = !empty($project->hasProduct) ? 'product' : 'project'; - $objectCode = $plan->attribute ? "{$projectType}_{$projectModel}_{$plan->attribute}" : "{$projectType}_{$projectModel}_{$plan->lifetime}"; - if($plan->type == 'kanban') $objectCode = "{$projectType}_{$projectModel}_kanban"; - - $projectDeliverable = $project->deliverable; - $projectDeliverable = !empty($projectDeliverable) ? json_decode($projectDeliverable, true) : array(); - $stageDeliverable = !empty($projectDeliverable[$objectCode]) ? array("{$objectCode}" => $projectDeliverable[$objectCode]) : array(); - $plan->deliverable = $stageDeliverable ? json_encode($stageDeliverable) : ''; - } - if($level > 0 && isset($parents[$level - 1])) $plan->parent = $parents[$level - 1]; $stageID = $this->programplanTao->insertStage($plan, $projectID, $productID, $level > 0 ? $plan->parent : $parentID); if(dao::isError()) return false; diff --git a/module/programplan/tao.php b/module/programplan/tao.php index 9135d02929..22555a4541 100644 --- a/module/programplan/tao.php +++ b/module/programplan/tao.php @@ -26,7 +26,7 @@ class programplanTao extends programplanModel if(empty($executionID)) return array(); $projectModel = $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($executionID)->fetch('model'); - return $this->dao->select('t1.*')->from(TABLE_EXECUTION)->alias('t1') + $stageList = $this->dao->select('t1.*')->from(TABLE_EXECUTION)->alias('t1') ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')->on('t1.id = t2.project') ->where('1 = 1') ->beginIF(!in_array($projectModel, array('waterfallplus', 'ipd')))->andWhere('t1.type')->eq('stage')->fi() @@ -45,6 +45,24 @@ class programplanTao extends programplanModel ->markRight(1) ->orderBy($orderBy) ->fetchAll('id', false); + + if($projectModel == 'ipd') + { + $stagePointGroup = $this->dao->select('execution,category,categoryTitle')->from(TABLE_OBJECT) + ->where('execution')->in(array_keys($stageList)) + ->andWhere('type')->eq('decision') + ->andWhere('enabled')->eq('1') + ->fetchGroup('execution', 'category'); + foreach($stageList as $stage) + { + $stage->enabledPoints = array(); + if(isset($stagePointGroup[$stage->id])) + { + foreach($stagePointGroup[$stage->id] as $point) $stage->enabledPoints[$point->category] = $point->categoryTitle; + } + } + } + return $stageList; } /** @@ -75,7 +93,7 @@ class programplanTao extends programplanModel $this->lang->project->name = $this->lang->programplan->name; $this->lang->project->code = $this->lang->execution->code; - $this->dao->update(TABLE_PROJECT)->data($plan, 'point')->autoCheck() + $this->dao->update(TABLE_PROJECT)->data($plan, 'point,defaultPoint')->autoCheck() ->checkIF(!empty($plan->name) && $getName, 'name', 'unique', "id in ({$relatedIdList}) and type in ('sprint','stage') and `project` = {$oldPlan->project} and `deleted` = '0' and `parent` = {$oldPlan->parent}") ->checkIF(!empty($plan->code) && $setCode, 'code', 'unique', "id != {$planID} and type in ('sprint','stage','kanban') and `project` = {$oldPlan->project} and `deleted` = '0' and `parent` = {$oldPlan->parent}") ->where('id')->eq($planID) @@ -435,11 +453,12 @@ class programplanTao extends programplanModel { if($point->end and !helper::isZeroDate($point->end)) return $point->end; - $end = $reviewDeadline[$planID]['stageEnd']; - $begin = $reviewDeadline[$planID]['stageBegin']; + $end = $reviewDeadline[$planID]['stageEnd']; + $begin = $reviewDeadline[$planID]['stageBegin']; - if(strpos($point->category, "DCP") !== false) $end = $this->getReviewDeadline($end, 2); - if(strpos($point->category, "TR") !== false) + $pointType = $this->dao->select('type')->from(TABLE_DECISION)->where('id')->eq($point->id)->fetch('type'); + if($pointType == 'DCP') $end = $this->getReviewDeadline($end, 2); + if($pointType == 'TR') { if(isset($reviewDeadline[$planID]['taskEnd']) and !helper::isZeroDate($reviewDeadline[$planID]['taskEnd'])) return $reviewDeadline[$planID]['taskEnd']; $end = $this->getReviewDeadline($end); @@ -477,13 +496,10 @@ class programplanTao extends programplanModel foreach($reviewPoints as $point) { - if(!isset($this->config->review->ipdReviewPoint->{$plan->attribute})) continue; + if($point->execution != $plan->id) continue; if(!isset($point->status)) $point->status = ''; - $categories = $this->config->review->ipdReviewPoint->{$plan->attribute}; - if(!in_array($point->category, $categories)) continue; - - $dataID = "{$plan->id}-{$point->category}-{$point->id}"; + $dataID = "{$plan->id}-point{$point->category}-{$point->id}"; if($selectCustom && strpos($selectCustom, "point") !== false && !$plan->parent) $datas['data'][$dataID] = $this->buildPointDataForGantt($plan->id, $point, $reviewDeadline); } } @@ -550,7 +566,7 @@ class programplanTao extends programplanModel $plan->openedDate = helper::now(); $plan->openedVersion = $this->config->version; if(!isset($plan->acl)) $plan->acl = $this->dao->findByID($plan->parent)->from(TABLE_PROJECT)->fetch('acl'); - $this->dao->insert(TABLE_PROJECT)->data($plan, 'point')->exec(); + $this->dao->insert(TABLE_PROJECT)->data($plan, 'point,defaultPoint')->exec(); if(dao::isError()) return false; @@ -558,7 +574,7 @@ class programplanTao extends programplanModel $this->insertProjectSpec($stageID, $plan); /* Ipd project create default review points. */ - if($project && $project->model == 'ipd' && $this->config->edition == 'ipd' && !$parentID) $this->loadModel('review')->createDefaultPoint($project->id, $productID, $plan->attribute); + if($project && $project->model == 'ipd' && $this->config->edition == 'ipd' && !$parentID) $this->loadModel('review')->createDefaultPoint($project->id, $stageID, $plan->defaultPoint); if($plan->type == 'kanban') { @@ -672,6 +688,7 @@ class programplanTao extends programplanModel /* Set default progress from database. */ $data->progress = $plan->progress / 100; $data->taskProgress = $plan->progress . '%'; + $data->frozen = $plan->frozen; if($data->endDate > $data->start_date) $data->duration = helper::diffDate($data->endDate, $data->start_date) + 1; if(empty($data->start_date) || empty($data->endDate)) $data->duration = 1; @@ -698,7 +715,7 @@ class programplanTao extends programplanModel $end = $this->getPointEndDate($planID, $point, $reviewDeadline); $data = new stdclass(); - $data->id = $planID . '-' . $point->category . '-' . $point->id; + $data->id = $planID . '-point' . $point->category . '-' . $point->id; $data->reviewID = $point->reviewID; $data->type = 'point'; $data->text = " " . $point->title; @@ -785,7 +802,8 @@ class programplanTao extends programplanModel $taskPri = "%s "; $pri = zget($this->lang->task->priList, $task->pri); $priIcon = sprintf($taskPri, $task->pri, $pri, $pri); - $progress = $task->consumed ? round($task->consumed / ($task->left + $task->consumed), 3) : 0; + $total = $task->left + $task->consumed; + $progress = $total ? round($task->consumed / $total, 3) : 0; $data = new stdclass(); $data->id = $task->id; diff --git a/module/programplan/test/tao/buildganttdata4ipd.php b/module/programplan/test/tao/buildganttdata4ipd.php index fe72a62b94..137981ebca 100644 --- a/module/programplan/test/tao/buildganttdata4ipd.php +++ b/module/programplan/test/tao/buildganttdata4ipd.php @@ -7,9 +7,9 @@ title=测试 loadModel->buildGanttData4IPD() cid=17760 - 禅道版本为开源版。 @0 -- gantt数据数。 @20 +- gantt数据数。 @11 - 禅道版本为IPD。 - - 属性id @1-PP-1 + - 属性id @1-pointPP-1 - 属性reviewID @1 - 属性type @point - 属性text @ 这个是评审或基线的标题1 @@ -21,7 +21,6 @@ include dirname(__FILE__, 5). '/test/lib/init.php'; su('admin'); zenData('review')->gen(20); -zenData('object')->gen(20); $project = zenData('project'); $project->type->range('stage'); $project->attribute->range('devel'); @@ -29,13 +28,13 @@ $project->begin->range('`2023-09-28`'); $project->end->range('`2024-04-02`'); $project->gen(10); +$object = zenData('object'); +$object->execution->range('1-20'); +$object->gen(20); + global $tester; $tester->loadModel('programplan'); -$tester->programplan->app->loadConfig('review'); -$tester->programplan->config->review->ipdReviewPoint = new stdclass(); -$tester->programplan->config->review->ipdReviewPoint->devel = array('PP'); - $plans = $tester->programplan->dao->select('*')->from(TABLE_PROJECT)->where('type')->eq('stage')->fetchAll('id'); $plans = $tester->programplan->processPlans($plans); @@ -47,8 +46,8 @@ r(isset($datas['data']['1-PP-1'])) && p() && e("0"); //禅道版本为开源版 $tester->programplan->config->edition = 'ipd'; $datas = $tester->programplan->buildGanttData4IPD($normalResult['datas'], 1, 1, 'point', $normalResult['reviewDeadline']); -r(count($datas['data'])) && p() && e('20'); //gantt数据数。 -r($datas['data']['1-PP-1']) && p('id,reviewID,type,text') && e("1-PP-1,1,point, 这个是评审或基线的标题1"); //禅道版本为IPD。 +r(count($datas['data'])) && p() && e('11'); //gantt数据数。 +r($datas['data']['1-pointPP-1']) && p('id,reviewID,type,text') && e("1-pointPP-1,1,point, 这个是评审或基线的标题1"); //禅道版本为IPD。 $datas = $tester->programplan->buildGanttData4IPD($normalResult['datas'], 1, 1, '', $normalResult['reviewDeadline']); -r(isset($datas['data']['1-PP-1'])) && p() && e("0"); //selectCustom 不包含 point。 +r(isset($datas['data']['1-pointPP-1'])) && p() && e("0"); //selectCustom 不包含 point。 diff --git a/module/programplan/test/tao/buildpointdataforgantt.php b/module/programplan/test/tao/buildpointdataforgantt.php index 048c4fe1e7..c80bc6523c 100755 --- a/module/programplan/test/tao/buildpointdataforgantt.php +++ b/module/programplan/test/tao/buildpointdataforgantt.php @@ -7,11 +7,11 @@ title=测试 programplanTao::buildPointDataForGantt(); timeout=0 cid=17764 -- 步骤1:正常评审点数据构建属性id @1-PP-1 +- 步骤1:正常评审点数据构建属性id @1-pointPP-1 - 步骤2:验证数据类型属性type @point - 步骤3:验证评审状态属性rawStatus @pass -- 步骤4:DCP类别评审点处理属性id @2-DCP-2 -- 步骤5:TR类别评审点处理属性id @3-TR4-3 +- 步骤4:DCP类别评审点处理属性id @2-pointDCP-2 +- 步骤5:TR类别评审点处理属性id @3-pointTR4-3 */ @@ -84,8 +84,8 @@ $reviewDeadline = array( ); // 6. 执行测试步骤 -r($programplanTest->buildPointDataForGanttTest(1, $normalPoint, $reviewDeadline)) && p('id') && e('1-PP-1'); // 步骤1:正常评审点数据构建 +r($programplanTest->buildPointDataForGanttTest(1, $normalPoint, $reviewDeadline)) && p('id') && e('1-pointPP-1'); // 步骤1:正常评审点数据构建 r($programplanTest->buildPointDataForGanttTest(1, $normalPoint, $reviewDeadline)) && p('type') && e('point'); // 步骤2:验证数据类型 r($programplanTest->buildPointDataForGanttTest(1, $normalPoint, $reviewDeadline)) && p('rawStatus') && e('pass'); // 步骤3:验证评审状态 -r($programplanTest->buildPointDataForGanttTest(2, $pointWithoutEnd, $reviewDeadline)) && p('id') && e('2-DCP-2'); // 步骤4:DCP类别评审点处理 -r($programplanTest->buildPointDataForGanttTest(3, $trPoint, $reviewDeadline)) && p('id') && e('3-TR4-3'); // 步骤5:TR类别评审点处理 \ No newline at end of file +r($programplanTest->buildPointDataForGanttTest(2, $pointWithoutEnd, $reviewDeadline)) && p('id') && e('2-pointDCP-2'); // 步骤4:DCP类别评审点处理 +r($programplanTest->buildPointDataForGanttTest(3, $trPoint, $reviewDeadline)) && p('id') && e('3-pointTR4-3'); // 步骤5:TR类别评审点处理 diff --git a/module/programplan/test/tao/getpointenddate.php b/module/programplan/test/tao/getpointenddate.php index 84d1534cf3..df0413dd05 100644 --- a/module/programplan/test/tao/getpointenddate.php +++ b/module/programplan/test/tao/getpointenddate.php @@ -7,7 +7,7 @@ title=测试 loadModel->getPointEndDate() cid=17770 - 传入point->end不为空 @2023-09-28 -- 传入point->end为空 @2023-12-28 +- 传入point->end为空 @2023-12-21 - 传入point->category为DCP @2023-12-26 - 传入point->category为TR @2023-12-21 - 传入reviewDeadline含有taskEnd数据 @2023-12-18 @@ -23,6 +23,11 @@ $project->type->range('project,stage{100}'); $project->milestone->range('0{3},1{100}'); $project->gen(5); +$decision = zenData('decision'); +$decision->id->range('1-2'); +$decision->type->range('TR,DCP'); +$decision->gen(2); + global $tester; $tester->loadModel('programplan'); @@ -39,12 +44,13 @@ $reviewDeadline[1]['stageEnd'] = '2023-12-28'; r($tester->programplan->getPointEndDate($planID, $point, array())) && p() && e('2023-09-28'); //传入point->end不为空 $point->end = null; -r($tester->programplan->getPointEndDate($planID, $point, $reviewDeadline)) && p() && e('2023-12-28'); //传入point->end为空 +$point->id = 1; +r($tester->programplan->getPointEndDate($planID, $point, $reviewDeadline)) && p() && e('2023-12-21'); //传入point->end为空 -$point->category = 'DCP'; +$point->id = 2; r($tester->programplan->getPointEndDate($planID, $point, $reviewDeadline)) && p() && e('2023-12-26'); //传入point->category为DCP -$point->category = 'TR'; +$point->id = 1; r($tester->programplan->getPointEndDate($planID, $point, $reviewDeadline)) && p() && e('2023-12-21'); //传入point->category为TR $reviewDeadline[1]['taskEnd'] = '2023-12-18'; diff --git a/module/programplan/test/zen/buildcreateview.php b/module/programplan/test/zen/buildcreateview.php index 40c3807a94..953308660f 100755 --- a/module/programplan/test/zen/buildcreateview.php +++ b/module/programplan/test/zen/buildcreateview.php @@ -30,4 +30,4 @@ r($programplanTest->buildCreateViewTest(1, 'normal')) && p('success') && e('1'); r($programplanTest->buildCreateViewTest(2, 'ipd')) && p('success') && e('1'); r($programplanTest->buildCreateViewTest(3, 'withProduct')) && p('success') && e('1'); r($programplanTest->buildCreateViewTest(4, 'withPlan')) && p('success') && e('1'); -r($programplanTest->buildCreateViewTest(5, 'stageType')) && p('success') && e('1'); \ No newline at end of file +r($programplanTest->buildCreateViewTest(5, 'stageType')) && p('success') && e('1'); diff --git a/module/programplan/test/zen/prepareeditplan.php b/module/programplan/test/zen/prepareeditplan.php index 58d0c167f7..83c1e2bd71 100755 --- a/module/programplan/test/zen/prepareeditplan.php +++ b/module/programplan/test/zen/prepareeditplan.php @@ -163,4 +163,4 @@ $plan7->percent = 15; $plan7->product = 0; $result7 = $func->invokeArgs($instance, [5, 1, $plan7, $parentStage1]); -r($result7) && p('begin,end') && e('2024-03-01,2024-05-31'); // 步骤7: 无效项目ID,但日期合法 \ No newline at end of file +r($result7) && p('begin,end') && e('2024-03-01,2024-05-31'); // 步骤7: 无效项目ID,但日期合法 diff --git a/module/programplan/ui/create.html.php b/module/programplan/ui/create.html.php index 432d47ec5f..895af18ee6 100644 --- a/module/programplan/ui/create.html.php +++ b/module/programplan/ui/create.html.php @@ -15,7 +15,6 @@ namespace zin; include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); $fields = $this->config->programplan->form->create; -$enabledPoints = isset($enabledPoints) ? $enabledPoints : new stdclass(); $reviewedPoints = isset($reviewedPoints) ? $reviewedPoints : array(); $canParallel = isset($canParallel) ? $canParallel : false; $customKey = 'createFields'; @@ -181,6 +180,7 @@ $fnGenerateFields = function() use ($lang, $requiredFields, $showFields, $fields 'data-class-name' => 'text-gray border border-gray-300' ); } + if($name == 'defaultPoint') $field['hidden'] = true; if($name == 'name') $field['width'] = '240px'; if(!isset($field['width'])) $field['width'] = '120px'; @@ -192,7 +192,7 @@ $fnGenerateFields = function() use ($lang, $requiredFields, $showFields, $fields }; /* Generate default rendering data. */ -$fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $executionType, $enabledPoints, $project) +$fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $executionType, $project) { $items = array(); @@ -201,17 +201,21 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe { foreach($stages as $stage) { - $points = !empty($enabledPoints->{$stage->type}) ? $enabledPoints->{$stage->type} : array(); + $TRpoints = isset($stage->pointList['TR']) ? array_keys($stage->pointList['TR']) : array(); + $DCPpoints = isset($stage->pointList['DCP']) ? array_keys($stage->pointList['DCP']) : array(); + $points = array_merge($TRpoints, $DCPpoints); - $item = new stdClass(); - $item->name = $stage->name; - $item->code = isset($stage->code) ? $stage->code : ''; - $item->percent = $stage->percent; - $item->attribute = $stage->type; - $item->acl = 'open'; - $item->milestone = 0; - $item->point = implode(',', $points); - $item->parallel = 0; + $item = new stdClass(); + $item->name = $stage->name; + $item->code = isset($stage->code) ? $stage->code : ''; + $item->percent = $stage->percent ?: 0; + $item->attribute = $stage->type; + $item->acl = 'open'; + $item->milestone = 0; + $item->point = implode(',', $points); + $item->defaultPoint = implode(',', $points); + $item->parallel = 0; + $item->stageID = $stage->id; $items[] = $item; } @@ -220,7 +224,7 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe /* Create stages for exist project. */ foreach($plans as $plan) { - $points = !empty($enabledPoints->{$plan->attribute}) ? $enabledPoints->{$plan->attribute} : array(); + $points = isset($plan->enabledPoints) ? array_keys($plan->enabledPoints) : array(); $item = new stdClass(); $item->disabled = $plan->type != 'stage'; @@ -231,7 +235,7 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe $item->code = $plan->code; $item->PM = $plan->PM; $item->status = $plan->status; - $item->percent = $plan->percent; + $item->percent = $plan->percent ?: 0; $item->attribute = $plan->attribute; $item->acl = $plan->acl; $item->milestone = $plan->milestone; @@ -244,6 +248,7 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe $item->order = $plan->order; $item->parallel = $plan->parallel; $item->point = implode(',', $points); + $plan->stageID = $plan->id; $plan->disabled = !isset($plan->setMilestone); $plan->setMilestone = isset($plan->setMilestone) ? $plan->setMilestone : false; $plan->point = implode(',', $points); @@ -273,7 +278,7 @@ jsVar('childEnabledTip', $lang->programplan->childEnabledTip); jsVar('typeList', $lang->execution->typeList); jsVar('confirmCreateTip', $lang->project->confirmCreateStage); jsVar('errorLang', $lang->programplan->error); -jsVar('ipdStagePoint', $project->model == 'ipd' ? $config->review->ipdReviewPoint : array()); +jsVar('ipdStagePoint', $project->model == 'ipd' ? $ipdStagePoint : array()); jsVar('attributeList', $project->model == 'ipd' ? $lang->stage->ipdTypeList : $lang->stage->typeList); jsVar('reviewedPoints', $project->model == 'ipd' ? $reviewedPoints : array()); jsVar('reviewedPointTip', $project->model == 'ipd' ? $lang->programplan->reviewedPointTip : ''); @@ -293,6 +298,24 @@ toolbar backBtn(set::icon('back'), setClass('primary'), $lang->goback), ); +if($project->model == 'ipd' && empty($stages) && empty($plans) && empty($planID)) +{ + div + ( + setClass('dtable-empty-tip bg-white'), + div + ( + setClass('row gap-4 items-center'), + div + ( + setClass('text-gray'), + $lang->programplan->emptyStageTip + ) + ) + ); + return; +} + $batchFormOptions = array(); $batchFormOptions['fixedActions'] = true; // 滚动时固定操作列。 $batchFormOptions['actions'] = array('sort', array('type' => 'addSibling', 'icon' => 'icon-plus', 'text' => $lang->task->addSibling), array('type' => 'addSub', 'icon' => 'icon-split', 'text' => $lang->task->addSub), 'delete'); diff --git a/module/programplan/ui/edit.html.php b/module/programplan/ui/edit.html.php index 0feb463d57..db1543f1d1 100644 --- a/module/programplan/ui/edit.html.php +++ b/module/programplan/ui/edit.html.php @@ -75,13 +75,13 @@ formPanel set::value($plan->PM), ) ), - isset($config->setPercent) && $config->setPercent == 1 && $project->model != 'research' ? formGroup + isset($config->setPercent) && $config->setPercent == 1 && !in_array($project->model, array('research', 'ipd')) ? formGroup ( set::label($lang->programplan->percent), set::width('2/3'), inputControl ( - input(set::name('percent'), set::value($plan->percent)), + input(set::name('percent'), set::value((string)$plan->percent)), to::suffix('%'), set::suffixWidth('lg') ) diff --git a/module/programplan/zen.php b/module/programplan/zen.php index 765d7694eb..5e092e7a8d 100644 --- a/module/programplan/zen.php +++ b/module/programplan/zen.php @@ -89,7 +89,7 @@ class programplanZen extends programplan $plan->parent = $parentID ? $parentID : $projectID; $plan->isTpl = $project->isTpl; if($plan->id && isset($oldPlans[$plan->id])) $plan->parent = $oldPlans[$plan->id]->parent; - if(!empty($plan->percent) && $plan->type != 'stage') $plan->percent = 0; // 非阶段类型,工作量占比为0 + if($plan->type != 'stage' || empty($plan->percent)) $plan->percent = 0; // 非阶段类型,工作量占比为0 if(empty($plan->days)) $plan->days = helper::diffDate($plan->end, $plan->begin) + 1; if(!empty($parentID) && !empty($parentStage) && $parentStage->attribute != 'mix') $plan->attribute = $parentStage->attribute;; @@ -112,8 +112,10 @@ class programplanZen extends programplan if(!empty($this->config->setCode) && empty($plan->code) && strpos(",{$this->config->execution->create->requiredFields},", ',code,') !== false) dao::$errors["code[{$rowID}]"] = sprintf($this->lang->error->notempty, $plan->type == 'stage' ? $this->lang->execution->code : $this->lang->code); } + if(!is_numeric($plan->percent) || $plan->percent < 0) dao::$errors["percent[$rowID]"] = $this->lang->programplan->error->percentNumber; + $customKey = 'create' . ucfirst($project->model) . 'Fields'; - if(strpos(",{$this->config->programplan->custom->$customKey},", ',percent,') !== false && isset($plan->percent)) + if(strpos(",{$this->config->programplan->custom->$customKey},", ',percent,') !== false && isset($plan->percent) && !dao::isError()) { if($plan->level == 0) { @@ -178,7 +180,7 @@ class programplanZen extends programplan $this->view->productList = $viewData->productList; $this->view->project = $viewData->project; $this->view->productID = $viewData->productID ?: key($viewData->productList); - $this->view->stages = empty($viewData->planID) ? $this->loadModel('stage')->getStages('id_asc', 0, $viewData->project->model) : array(); + $this->view->stages = empty($viewData->planID) ? $this->loadModel('stage')->getStages('order_asc', 0, $viewData->project->workflowGroup) : array(); $this->view->programPlan = $viewData->programPlan; $this->view->plans = $viewData->plans; $this->view->planID = $viewData->planID; @@ -194,8 +196,6 @@ class programplanZen extends programplan $this->view->defaultFields = $defaultFields; $this->view->colspan = count($visibleFields) + 3; $this->view->enableOptionalAttr = empty($viewData->programPlan) || (!empty($viewData->programPlan) && $viewData->programPlan->attribute == 'mix'); - - $this->display(); } /** @@ -221,6 +221,8 @@ class programplanZen extends programplan if(dao::isError()) return false; } + if(!is_numeric($plan->percent) || $plan->percent < 0) dao::$errors['percent'] = $this->lang->programplan->error->percentNumber; + if($projectID) $this->loadModel('execution')->checkBeginAndEndDate($projectID, $plan->begin, $plan->end, $plan->parent); if(dao::isError()) return false; diff --git a/module/project/config.php b/module/project/config.php index 5969938a2b..685250bb19 100755 --- a/module/project/config.php +++ b/module/project/config.php @@ -12,15 +12,15 @@ $config->project->editor->activate = array('id' => 'comment', 'tools' => 'simple $config->project->editor->view = array('id' => 'lastComment', 'tools' => 'simpleTools'); $config->project->list = new stdclass(); -$config->project->list->exportFields = 'id,name,code,status,hasProduct,PM,storyPoints,storyCount,executionCount,budget,teamCount,invested,begin,end,realBegan,realEnd,estimate,consumed,progress'; +$config->project->list->exportFields = 'id,name,code,status,PM,storyPoints,storyCount,executionCount,budget,teamCount,invested,begin,end,realBegan,realEnd,estimate,consumed,progress'; if($config->systemMode == 'ALM') $config->project->list->exportFields = substr_replace($config->project->list->exportFields, ',parent', 2, 0); $config->project->list->customCreateFields = 'budget,acl,auth'; $config->project->create = new stdclass(); $config->project->edit = new stdclass(); $config->project->batchedit = new stdclass(); -$config->project->create->requiredFields = 'name,begin,end'; -$config->project->edit->requiredFields = 'name,begin,end'; +$config->project->create->requiredFields = 'name,begin,end,workflowGroup'; +$config->project->edit->requiredFields = 'name,begin,end,workflowGroup'; $config->project->start = new stdclass(); $config->project->start->requiredFields = 'realBegan'; @@ -41,7 +41,7 @@ $config->project->labelClass['kanban'] = 'secondary-outline'; $config->project->labelClass['agileplus'] = 'secondary-outline'; $config->project->labelClass['waterfallplus'] = 'secondary-outline'; -$config->project->multiple['project'] = ',qa,devops,doc,build,release,dynamic,settings,'; +$config->project->multiple['project'] = ',qa,devops,review,doc,build,release,dynamic,settings,'; $config->project->multiple['execution'] = ',task,kanban,burn,view,story,CFD,'; $config->project->checkList = new stdclass(); @@ -68,7 +68,6 @@ $config->project->search['fields']['name'] = $lang->project->name; $config->project->search['fields']['code'] = $lang->project->code; $config->project->search['fields']['id'] = $lang->project->id; $config->project->search['fields']['model'] = $lang->project->model; -$config->project->search['fields']['hasProduct'] = $lang->project->type; $config->project->search['fields']['parent'] = $lang->project->parent; $config->project->search['fields']['status'] = $lang->project->status; $config->project->search['fields']['desc'] = $lang->project->desc; @@ -87,7 +86,6 @@ $config->project->search['params']['name'] = array('operator' => 'incl $config->project->search['params']['code'] = array('operator' => '=' , 'control' => 'input' , 'values' => ''); $config->project->search['params']['id'] = array('operator' => '=' , 'control' => 'input' , 'values' => ''); $config->project->search['params']['model'] = array('operator' => '=' , 'control' => 'select', 'values' => $lang->project->modelList); -$config->project->search['params']['hasProduct'] = array('operator' => '=' , 'control' => 'select', 'values' => $lang->project->projectTypeList); $config->project->search['params']['parent'] = array('operator' => '=' , 'control' => 'select', 'values' => ''); $config->project->search['params']['status'] = array('operator' => '=' , 'control' => 'select', 'values' => $lang->project->statusList); $config->project->search['params']['desc'] = array('operator' => 'include', 'control' => 'input' , 'values' => ''); @@ -311,6 +309,9 @@ $config->project->linkMap->budget['summary'] = array('budget', 'summary', 'proje $config->project->linkMap->programplan = array(); $config->project->linkMap->programplan[''] = array('project', 'execution', 'type=all&projectID=%s', ''); +$config->project->linkMap->auditplan = array(); +$config->project->linkMap->auditplan[''] = array('project', 'index', 'projectID=%s', ''); + $config->project->budget = new stdclass(); $config->project->budget->precision = 2; $config->project->budget->tenThousand = 10000; @@ -331,6 +332,7 @@ $config->project->actionList['close']['icon'] = 'off'; $config->project->actionList['close']['hint'] = $lang->project->close; $config->project->actionList['close']['url'] = helper::createLink('project', 'close', 'projectID={id}'); $config->project->actionList['close']['data-toggle'] = 'modal'; +$config->project->actionList['close']['data-size'] = '60rem'; $config->project->actionList['activate']['icon'] = 'magic'; $config->project->actionList['activate']['hint'] = $lang->project->activate; @@ -376,3 +378,9 @@ $config->project->statusLabelList['doing'] = 'doing primary-pale'; $config->project->statusLabelList['suspended'] = 'suspended gray text-white'; $config->project->statusLabelList['closed'] = 'closed success-pale'; $config->project->statusLabelList['delay'] = 'delay danger-pale'; + +$config->project->categoryStages['waterfall'] = array('request', 'design', 'dev', 'qa', 'release', 'review'); +$config->project->categoryStages['IPD'] = array('concept', 'plan', 'develop', 'qualify', 'launch'); +$config->project->categoryStages['TPD'] = array('concept', 'plan', 'develop', 'qualify'); +$config->project->categoryStages['CBB'] = array('concept', 'plan', 'develop', 'qualify'); +$config->project->categoryStages['CPD'] = array('plan', 'develop', 'qualify', 'launch'); diff --git a/module/project/config/dtable.php b/module/project/config/dtable.php index 67590dd67a..a6856e55f4 100755 --- a/module/project/config/dtable.php +++ b/module/project/config/dtable.php @@ -40,13 +40,6 @@ $config->project->dtable->fieldList['status']['statusMap'] = $lang->project->sta $config->project->dtable->fieldList['status']['group'] = 2; $config->project->dtable->fieldList['status']['show'] = true; -$config->project->dtable->fieldList['hasProduct']['title'] = $lang->project->type; -$config->project->dtable->fieldList['hasProduct']['name'] = 'hasProduct'; -$config->project->dtable->fieldList['hasProduct']['type'] = 'category'; -$config->project->dtable->fieldList['hasProduct']['sortType'] = true; -$config->project->dtable->fieldList['hasProduct']['map'] = $lang->project->projectTypeList; -$config->project->dtable->fieldList['hasProduct']['group'] = 2; - $config->project->dtable->fieldList['PM']['title'] = $lang->project->PM; $config->project->dtable->fieldList['PM']['name'] = 'PM'; $config->project->dtable->fieldList['PM']['type'] = 'avatarBtn'; diff --git a/module/project/control.php b/module/project/control.php index c166a66c8d..49ce4cd3bd 100755 --- a/module/project/control.php +++ b/module/project/control.php @@ -500,6 +500,8 @@ class project extends control } $this->projectZen->buildCreateForm((string)$model, (int)$programID, (int)$copyProjectID, (string)$extra); + + $this->display('project', 'create'); } /** @@ -526,7 +528,7 @@ class project extends control $extra = str_replace(array(',', ' '), array('&', ''), $extra); parse_str($extra, $output); - $workflowGroup = isset($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0; + $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0; if($_POST) { @@ -885,21 +887,22 @@ class project extends control $this->execution->buildTaskSearchForm($projectID, $executions, $queryID, $actionURL, 'projectTask'); } - $this->view->title = $this->lang->execution->allExecutions; - $this->view->executionStats = $this->projectZen->getExecutionStats($status, $projectID, isset($executions) ? array_keys($executions) : array(), $productID, $queryID, $sort, $pager); - $this->view->productList = array(0 => $this->lang->product->all) + $this->product->getProductPairsByProject($projectID, 'all', '', false); - $this->view->productID = $productID; - $this->view->product = $this->product->getByID($productID); - $this->view->projectID = $projectID; - $this->view->project = $project; - $this->view->projects = $projects; - $this->view->pager = $pager; - $this->view->orderBy = $orderBy; - $this->view->status = $status; - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->isStage = isset($project->model) && (in_array($project->model, array('waterfall', 'waterfallplus', 'ipd'))); - $this->view->avatarList = $this->user->getAvatarPairs(''); - $this->view->queryID = $queryID; + $this->view->title = $this->lang->execution->allExecutions; + $this->view->executionStats = $this->projectZen->getExecutionStats($status, $projectID, isset($executions) ? array_keys($executions) : array(), $productID, $queryID, $sort, $pager); + $this->view->productList = array(0 => $this->lang->product->all) + $this->product->getProductPairsByProject($projectID, 'all', '', false); + $this->view->productID = $productID; + $this->view->product = $this->product->getByID($productID); + $this->view->projectID = $projectID; + $this->view->project = $project; + $this->view->projects = $projects; + $this->view->pager = $pager; + $this->view->orderBy = $orderBy; + $this->view->status = $status; + $this->view->users = $this->loadModel('user')->getPairs('noletter'); + $this->view->isStage = isset($project->model) && (in_array($project->model, array('waterfall', 'waterfallplus', 'ipd'))); + $this->view->avatarList = $this->user->getAvatarPairs(''); + $this->view->queryID = $queryID; + $this->view->hasFrozenExecutions = $this->project->hasFrozenObject($project->id, 'PP'); $this->display(); } @@ -1138,7 +1141,7 @@ class project extends control $project = $this->project->getByID((int)$projectID); $this->projectZen->processGroupPrivs($project); - $this->lang->resource = $this->project->getPrivsByModel($project->multiple ? $project->model : 'noSprint'); + $this->lang->resource = $this->project->getPrivsByModel($project->multiple ? $project->model : 'noSprint', $projectID); $getPrivs = $this->group->getPrivs($groupID); foreach($getPrivs as $moduleName => $actions) @@ -1780,25 +1783,6 @@ class project extends control $this->display(); } - /** - * Ajax get workflow group items. - * - * @param string $model - * @param int $hasProduct - * @param string $category - * @access public - * @return void - */ - public function ajaxGetWorkflowGroups(string $model, int $hasProduct, string $category) - { - if($this->config->edition == 'open') return false; - - $workflowGroups = $this->loadModel('workflowgroup')->getPairs('project', $model, $hasProduct, 'normal', '0', $category); - - $items = $this->workflowgroup->appendBuildinLabel($workflowGroups); - return $this->send(array('items' => array_values($items), 'defaultValue' => key($workflowGroups))); - } - /** * 检查是否同步阶段数据。 * AJAX: check whether stage data is sync. diff --git a/module/project/model.php b/module/project/model.php index 3137cf6e06..a82d5dac5d 100755 --- a/module/project/model.php +++ b/module/project/model.php @@ -185,12 +185,12 @@ class projectModel extends model */ public static function isClickable(object $project, string $action) { - if(empty($project) || !isset($project->type)) return true; - + global $config; $action = strtolower($action); - if($action == 'publishtemplate') return $project->status == 'wait' || $project->status == 'closed'; - if($action == 'disabletemplate') return $project->status == 'doing'; + if($action == 'publishtemplate') return $project->status == 'wait' || $project->status == 'closed'; + if($action == 'disabletemplate') return $project->status == 'doing'; + if($action == 'deletedeliverable') return !$project->review && empty($project->systemList) && $project->status == 'draft'; if($action == 'close') return $project->status != 'closed'; if($action == 'group') return $project->model != 'kanban'; @@ -642,7 +642,7 @@ class projectModel extends model if($this->config->edition != 'open') { $flow = $this->loadModel('workflow')->getByModule($module); - if(!empty($flow->app) && in_array($flow->app, array('scrum', 'waterfall'))) $flow->app = 'project'; + if(!empty($flow->app) && in_array($flow->app, array('scrum', 'waterfall', 'kanbanProject'))) $flow->app = 'project'; if(!empty($flow) && $flow->buildin == '0') return helper::createLink('flow', 'ajaxSwitchBelong', "objectID=%s&moduleName=$module") . "#app=$flow->app"; } @@ -956,15 +956,31 @@ class projectModel extends model * Get project priv data according by the project type. * * @param string $model scrum|waterfall|noSprint|agileplus|waterfallplus + * @param int $projectID * @access public * @return object|false */ - public function getPrivsByModel(string $model = 'waterfall'): object|false + public function getPrivsByModel(string $model = 'waterfall', int $projectID = 0): object|false { if(!isset($this->config->programPriv->$model)) return false; if($model == 'noSprint') $this->config->project->includedPriv = $this->config->project->noSprintPriv; + $hasBaseline = true; + $hasAuditplan = true; + $hasProcess = true; + $hasDeliverable = true; + $hasChange = true; + if($this->config->edition != 'open' && $projectID) + { + $project = $this->fetchByID($projectID); + $hasBaseline = $this->loadModel('workflowgroup')->hasFeature((int)$project->workflowGroup, 'cm'); + $hasDeliverable = $this->workflowgroup->hasFeature((int)$project->workflowGroup, 'deliverable'); + $hasChange = $this->workflowgroup->hasFeature((int)$project->workflowGroup, 'change'); + $hasAuditplan = $this->workflowgroup->hasFeature((int)$project->workflowGroup, 'auditplan'); + $hasProcess = $this->workflowgroup->hasFeature((int)$project->workflowGroup, 'process'); + } + $this->app->loadLang('group'); $privs = new stdclass(); foreach($this->lang->resource as $module => $methods) @@ -973,6 +989,17 @@ class projectModel extends model if(!in_array($module, $this->config->programPriv->$model)) continue; + if($module == 'cm' && !$hasBaseline) continue; + if($module == 'auditplan' && !$hasAuditplan) continue; + if($module == 'pssp' && !$hasProcess) continue; + + if($module == 'review') + { + if(!$hasDeliverable) unset($methods->submitDeliverable); + if(!$hasChange) unset($methods->submitProjectchange); + if(!$hasBaseline) unset($methods->submitBaseline); + } + foreach($methods as $method => $label) { if(isset($this->config->project->includedPriv[$module]) and !in_array($method, $this->config->project->includedPriv[$module])) continue; @@ -1528,6 +1555,7 @@ class projectModel extends model if($unlinkType) $this->unlinkStoryByType($projectID, $unlinkType); } if(empty($oldProject->multiple) and !in_array($oldProject->model, array('waterfall', 'waterfallplus'))) $this->loadModel('execution')->syncNoMultipleSprint($projectID); // 无迭代的非瀑布项目需要更新。 + if(in_array($this->config->edition, array('max', 'ipd')) && $oldProject->workflowGroup != $project->workflowGroup) $this->modifyWorkflowGroup($projectID, $oldProject->workflowGroup, $project->workflowGroup); // 更新项目流程。 if($oldProject->model != $project->model && !in_array($oldProject->model, array('waterfall', 'waterfallplus', 'ipd')) && in_array($project->model, array('waterfall', 'waterfallplus', 'ipd'))) { @@ -2907,4 +2935,26 @@ class projectModel extends model } return $disabledProducts; } + + /** + * 查找项目下是否有冻结的对象。 + * Has frozen object. + * + * @param int $projectID + * @param string $category SRS|PP + * @return bool + */ + public function hasFrozenObject(int $projectID, string $category): bool + { + if(empty($projectID) || empty($category)) return false; + + $frozenObject = $this->dao->select('1')->from(TABLE_PROJECTDELIVERABLE)->alias('t1') + ->leftJoin(TABLE_DELIVERABLE)->alias('t2')->on('t1.deliverable = t2.id') + ->where('t1.project')->eq($projectID) + ->andWhere('t1.frozen')->ne('') + ->andWhere('t2.category')->eq($category) + ->limit(1) + ->fetch('1'); + return !empty($frozenObject); + } } diff --git a/module/project/tao.php b/module/project/tao.php index c1709d612b..93e272b93c 100755 --- a/module/project/tao.php +++ b/module/project/tao.php @@ -1055,7 +1055,6 @@ class projectTao extends projectModel include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php'; - if($projectModel == 'ipd') unset($lang->waterfall->menu->other['dropMenu']->deliverable); $lang->execution->typeList['sprint'] = $executionCommonLang; if($app->rawModule == 'project' && $app->rawMethod == 'executionreport') $lang->waterfall->menu->execution['subModule'] .= ',project'; diff --git a/module/project/test/lib/projectzen.unittest.class.php b/module/project/test/lib/projectzen.unittest.class.php index 8876b898b8..3a7d7da0f8 100644 --- a/module/project/test/lib/projectzen.unittest.class.php +++ b/module/project/test/lib/projectzen.unittest.class.php @@ -1057,4 +1057,4 @@ class projectzenTest return 'success'; } -} \ No newline at end of file +} diff --git a/module/project/test/lib/zen.class.php b/module/project/test/lib/zen.class.php index c4fd45640f..eaddfe305a 100644 --- a/module/project/test/lib/zen.class.php +++ b/module/project/test/lib/zen.class.php @@ -303,4 +303,21 @@ class projectZenTest extends baseTest if(dao::isError()) return dao::getError(); return $result; } + + /** + * Send variables to create page. + * + * @param string $model + * @param int $programID + * @param int $copyProjectID + * @param string $extra + * @access public + * @return void + */ + public function buildCreateFormTest(string $model, int $programID, int $copyProjectID, string $extra) + { + $this->invokeArgs('buildCreateForm', [$model, $programID, $copyProjectID, $extra]); + if(dao::isError()) return dao::getError(); + return $this->getProperty('view'); + } } diff --git a/module/project/test/model/getdisabledproducts.php b/module/project/test/model/getdisabledproducts.php index 8f0fb4cb46..a6d6ebe4db 100755 --- a/module/project/test/model/getdisabledproducts.php +++ b/module/project/test/model/getdisabledproducts.php @@ -7,14 +7,14 @@ include dirname(__FILE__, 2) . '/lib/project.unittest.class.php'; title=测试 projectModel->getDisabledProducts(); timeout=0 -cid=17823 +cid=0 - 测试获取敏捷项目不可修改产品 @0 -- 测试获取以产品创建的瀑布项目不可修改产品属性2 @项目已经关联了该产品中的,不能取消关联,您可以取消关联后再操作。 +- 测试获取以产品创建的瀑布项目不可修改产品属性2 @项目已经关联了该产品中的研发需求,不能取消关联,您可以取消关联研发需求后再操作。 - 测试获取以产品创建的瀑布项目不可修改产品属性3 @该产品已经创建了阶段,如需解除与项目的关联,请删除已创建的阶段后再操作。 -- 测试获取以产品创建的瀑布项目不可修改产品属性4 @该产品已经创建了阶段并关联了,如需解除与项目的关联,请先解除的关联关系,然后删除已创建的阶段后再操作。 -- 测试获取以项目创建的瀑布项目不可修改产品属性5 @该产品的已经关联到了项目和执行中,请先解除与项目和执行的关联后再操作。 -- 测试获取以项目创建的瀑布项目不可修改产品属性6 @项目已经关联了该产品中的,不能取消关联,您可以取消关联后再操作。 +- 测试获取以产品创建的瀑布项目不可修改产品属性4 @该产品已经创建了阶段并关联了研发需求,如需解除与项目的关联,请先解除研发需求的关联关系,然后删除已创建的阶段后再操作。 +- 测试获取以项目创建的瀑布项目不可修改产品属性5 @该产品的研发需求已经关联到了项目和执行中,请先解除研发需求与项目和执行的关联后再操作。 +- 测试获取以项目创建的瀑布项目不可修改产品属性6 @项目已经关联了该产品中的研发需求,不能取消关联,您可以取消关联研发需求后再操作。 */ @@ -26,9 +26,9 @@ zenData('projectstory')->loadYaml('projectstory')->gen(20); $projectIdList = range(1, 6); $projectTester = new projectTest(); -r($projectTester->getDisabledProductsTest($projectIdList[0])) && p() && e('0'); // 测试获取敏捷项目不可修改产品 -r($projectTester->getDisabledProductsTest($projectIdList[1])) && p('2') && e('项目已经关联了该产品中的,不能取消关联,您可以取消关联后再操作。'); // 测试获取以产品创建的瀑布项目不可修改产品 -r($projectTester->getDisabledProductsTest($projectIdList[2])) && p('3') && e('该产品已经创建了阶段,如需解除与项目的关联,请删除已创建的阶段后再操作。'); // 测试获取以产品创建的瀑布项目不可修改产品 -r($projectTester->getDisabledProductsTest($projectIdList[3])) && p('4') && e('该产品已经创建了阶段并关联了,如需解除与项目的关联,请先解除的关联关系,然后删除已创建的阶段后再操作。'); // 测试获取以产品创建的瀑布项目不可修改产品 -r($projectTester->getDisabledProductsTest($projectIdList[4])) && p('5') && e('该产品的已经关联到了项目和执行中,请先解除与项目和执行的关联后再操作。'); // 测试获取以项目创建的瀑布项目不可修改产品 -r($projectTester->getDisabledProductsTest($projectIdList[5])) && p('6') && e('项目已经关联了该产品中的,不能取消关联,您可以取消关联后再操作。'); // 测试获取以项目创建的瀑布项目不可修改产品 +r($projectTester->getDisabledProductsTest($projectIdList[0])) && p() && e('0'); // 测试获取敏捷项目不可修改产品 +r($projectTester->getDisabledProductsTest($projectIdList[1])) && p('2') && e('项目已经关联了该产品中的研发需求,不能取消关联,您可以取消关联研发需求后再操作。'); // 测试获取以产品创建的瀑布项目不可修改产品 +r($projectTester->getDisabledProductsTest($projectIdList[2])) && p('3') && e('该产品已经创建了阶段,如需解除与项目的关联,请删除已创建的阶段后再操作。'); // 测试获取以产品创建的瀑布项目不可修改产品 +r($projectTester->getDisabledProductsTest($projectIdList[3])) && p('4') && e('该产品已经创建了阶段并关联了研发需求,如需解除与项目的关联,请先解除研发需求的关联关系,然后删除已创建的阶段后再操作。'); // 测试获取以产品创建的瀑布项目不可修改产品 +r($projectTester->getDisabledProductsTest($projectIdList[4])) && p('5') && e('该产品的研发需求已经关联到了项目和执行中,请先解除研发需求与项目和执行的关联后再操作。'); // 测试获取以项目创建的瀑布项目不可修改产品 +r($projectTester->getDisabledProductsTest($projectIdList[5])) && p('6') && e('项目已经关联了该产品中的研发需求,不能取消关联,您可以取消关联研发需求后再操作。'); // 测试获取以项目创建的瀑布项目不可修改产品 diff --git a/module/project/test/model/getprojectlink.php b/module/project/test/model/getprojectlink.php index 1bdde48af5..6f24d67332 100644 --- a/module/project/test/model/getprojectlink.php +++ b/module/project/test/model/getprojectlink.php @@ -12,6 +12,53 @@ title=测试 projectModel->getProjectLink(); timeout=0 cid=17846 +- test project->multiple=0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'execution' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'test' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'bug' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'testcase' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'testtask' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'testreport' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'build' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'dynamic' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'view' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'manageproducts', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'team' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'managemembers' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'whitelist' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'addwhitelist' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'group' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'project', 'managePriv' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'product', 'showerrornone', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'projectstory', 'story' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'projectstory', 'linkstory', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'projectstory', 'track' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'bug', 'create', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'bug', 'edit' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'story', 'change' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'story', 'create' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'story', 'zerocase', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'testcase', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'testtask', 'browseunits' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'testtask', 'browse' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'testreport', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'repo', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'doc', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'api', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'build', 'create', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'build', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'build', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'projectrelease', 'create', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'projectrelease', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'stakeholder', 'create', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'stakeholder', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'issue', 'projectsummary', 11)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'issue', 'browse' , 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'zahost', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'design', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'reviewissue', 'browse', 12)) > 0 @1 +- 执行projectModel模块的getProjectLink方法,参数是'programplan', 'execution', 12)) > 0 @1 + */ global $tester; diff --git a/module/project/test/model/hasfrozenobject.php b/module/project/test/model/hasfrozenobject.php new file mode 100755 index 0000000000..7f6d3bb36c --- /dev/null +++ b/module/project/test/model/hasfrozenobject.php @@ -0,0 +1,45 @@ +#!/usr/bin/env php +id->range('1-5'); +$projectDeliverable->name->range('p1,p2,p3,p4,p5'); +$projectDeliverable->project->range('1-5'); +$projectDeliverable->deliverable->range('1-5'); +$projectDeliverable->doc->range('1-5'); +$projectDeliverable->docVersion->range('1'); +$projectDeliverable->review->range('1-5'); +$projectDeliverable->frozen->range('yes'); +$projectDeliverable->gen(5); + +$deliverable = zenData('deliverable'); +$deliverable->id->range('1-5'); +$deliverable->category->range('SRS,PP,HLDS,DDS,DBDS'); +$deliverable->gen(5); + +su('admin'); + +global $tester; +$projectModel = $tester->loadModel('project'); +r($projectModel->hasFrozenObject(0, '')) && p() && e('0'); // 检查传空值 +r($projectModel->hasFrozenObject(1, 'SRS')) && p() && e('1'); // 检查是否有冻结的需求 +r($projectModel->hasFrozenObject(2, 'PP')) && p() && e('1'); // 检查是否有冻结的阶段 +r($projectModel->hasFrozenObject(3, 'HLDS')) && p() && e('1'); // 检查是否有冻结的概要设计 +r($projectModel->hasFrozenObject(4, 'DDS')) && p() && e('1'); // 检查是否有冻结的详细设计 +r($projectModel->hasFrozenObject(5, 'DBDS')) && p() && e('1'); // 检查是否有冻结的数据库设计 diff --git a/module/project/test/zen/buildcreateform.php b/module/project/test/zen/buildcreateform.php new file mode 100644 index 0000000000..6f23510620 --- /dev/null +++ b/module/project/test/zen/buildcreateform.php @@ -0,0 +1,51 @@ +#!/usr/bin/env php +id->range('1-10'); +$table->name->range('项目1,项目2,项目3,项目4,项目5,项目6,项目7,项目8,项目9,项目10'); +$table->type->range('project'); +$table->status->range('doing'); +$table->multiple->range('0,1'); +$table->gen(10); + +zenData('user')->gen(5); +su('admin'); + +$projectTest = new projectzenTest(); +$result1 = $projectTest->buildCreateFormTest('scrum', 0, 0, ''); +r($result1) && p('title,model,programID,copyProjectID') && e('创建项目,scrum,0,0'); // 创建敏捷项目 + +$result2 = $projectTest->buildCreateFormTest('waterfall', 1, 0, ''); +r($result2) && p('model,programID,copyProjectID') && e('waterfall,1,0'); // 创建瀑布项目 + +$result3 = $projectTest->buildCreateFormTest('ipd', 1, 1, ''); +r($result3) && p('model,programID,copyProjectID') && e('ipd,1,1'); // 复制IPD项目进行创建 + +$result4 = $projectTest->buildCreateFormTest('scrum', 0, 0, 'productID=1,branchID=0'); +r($result4) && p('productID') && e('1'); // 创建时关联产品1 diff --git a/module/project/ui/close.html.php b/module/project/ui/close.html.php index bac2aa3438..e357869f8c 100644 --- a/module/project/ui/close.html.php +++ b/module/project/ui/close.html.php @@ -10,11 +10,20 @@ declare(strict_types=1); */ namespace zin; +jsVar('window.confirmShown', false); modalHeader(); formPanel ( set::formID("zin_project_close_{$project->id}_form"), - !empty($confirmTip) ? set::ajax(array('beforeSubmit' => jsRaw("() => zui.Modal.confirm('{$confirmTip}')"))) : null, + /* 确认弹窗只展示一次,表单可能会被必填项拦截。*/ + !empty($confirmTip) ? set::ajax(array('beforeSubmit' => jsRaw("() => { + if(window.confirmShown) return true; + window.confirmShown = true; + return zui.Modal.confirm('$confirmTip').then((confirmed) => { + if(!confirmed) window.confirmShown = false; + return confirmed; + }); + }"))) : null, formGroup ( set::width('1/2'), diff --git a/module/project/ui/create.html.php b/module/project/ui/create.html.php index 9f51983f2f..6f943d9e48 100644 --- a/module/project/ui/create.html.php +++ b/module/project/ui/create.html.php @@ -90,7 +90,6 @@ formGridPanel on::change('[name=longTime]', $handleLongTimeChange), on::change('[name=future]', 'toggleBudget'), on::change('[name=begin], [name=end]', 'computeWorkDays'), - on::change('[name="category"]', 'changeCategory'), on::change('[name^=products]', 'toggleStageBy'), on::change('[name=parent], [name=budget]', 'checkBudget'), on::change('[name^=storyType]', 'toggleStoryType'), diff --git a/module/project/ui/execution.html.php b/module/project/ui/execution.html.php index 16cb76f598..c69b515623 100644 --- a/module/project/ui/execution.html.php +++ b/module/project/ui/execution.html.php @@ -32,7 +32,7 @@ $canBatchChangeStatus = hasPriv('execution', 'batchChangeStatus'); $canBatchAction = $canModify && ($canBatchEdit || $canBatchChangeStatus); if($canBatchAction) { - if($canBatchEdit) + if($canBatchEdit && empty($hasFrozenExecutions)) { $footToolbar['items'][] = array ( @@ -201,7 +201,7 @@ dtable set::footPager(usePager(array('linkCreator' => createLink('project', 'execution', "status={$status}&projectID=$projectID&orderBy={$orderBy}&productID={$productID}&recTotal={recTotal}&recPerPage={recPerPage}&page={page}")))), set::emptyTip(!$searchTask ? $lang->execution->noExecution : $lang->task->noTask), set::createTip($isStage ? $lang->programplan->create : $lang->execution->create), - set::createLink($canCreateExecution && !$searchTask ? $createLink : ''), + set::createLink($canCreateExecution && !$searchTask && !$hasFrozenExecutions ? $createLink : ''), set::createAttr($isStage ? 'data-app="project"' : 'data-app="execution"') ); diff --git a/module/project/ui/view.html.php b/module/project/ui/view.html.php index 06219dab09..471bcc60ec 100644 --- a/module/project/ui/view.html.php +++ b/module/project/ui/view.html.php @@ -235,7 +235,7 @@ row set::title($project->code), $project->code ) : null, - label + $config->edition != 'open' ? null : label ( setClass('label warning-pale ring-warning rounded-full ml-2 flex-none projectType'), $lang->project->projectTypeList[$project->hasProduct] diff --git a/module/project/zen.php b/module/project/zen.php index 2e6832bc5e..c435bd8f99 100755 --- a/module/project/zen.php +++ b/module/project/zen.php @@ -338,7 +338,6 @@ class projectZen extends project $this->view->programID = $programID; $this->view->productID = isset($output['productID']) ? $output['productID'] : 0; $this->view->branchID = isset($output['branchID']) ? $output['branchID'] : 0; - $this->view->category = isset($output['category']) ? $output['category'] : ''; $this->view->allProducts = $allProducts; $this->view->multiBranchProducts = $this->product->getMultiBranchPairs((int)$topProgramID); $this->view->copyProjects = $copyProjects; @@ -355,8 +354,6 @@ class projectZen extends project if(!isset($this->view->linkedProducts)) $this->view->linkedProducts = $linkedProducts; if(!isset($this->view->linkedBranches)) $this->view->linkedBranches = $linkedBranches; - - $this->display('project', 'create'); } /** @@ -479,7 +476,7 @@ class projectZen extends project $this->view->disableParent = $disableParent; $this->view->groups = $this->loadModel('group')->getPairs(); - if(in_array($this->config->edition, array('max', 'ipd')) && $this->project->checkUploadedDeliverable($project)) + if(in_array($this->config->edition, array('max', 'ipd')) && $this->project->checkUploadedDeliverable($projectID)) { $this->view->disableModel = true; } @@ -1395,13 +1392,11 @@ class projectZen extends project $project->consume = helper::formatHours($project->consume); $project->left = helper::formatHours($project->left); - /* 交付物提交进度。 */ - if(in_array($this->config->edition, array('max', 'ipd'))) - { - $project->deliverable = $this->project->countDeliverable($project); - } } + /* 交付物提交进度。 */ + if(in_array($this->config->edition, array('max', 'ipd'))) $projectList = $this->project->countDeliverable($projectList, 'project'); + return array_values($projectList); } diff --git a/module/release/test/lib/release.unittest.class.php b/module/release/test/lib/release.unittest.class.php index d80f2ad286..92a01651fc 100644 --- a/module/release/test/lib/release.unittest.class.php +++ b/module/release/test/lib/release.unittest.class.php @@ -645,21 +645,21 @@ class releaseTest $beforeCount = $this->objectModel->dao->select('COUNT(*) as count')->from(TABLE_RELEASERELATED) ->where('release')->eq($releaseID) ->fetch('count'); - + $this->objectModel->processRelated($releaseID, $release); - + if(dao::isError()) return dao::getError(); - + // 记录操作后的关联数据数量 $afterCount = $this->objectModel->dao->select('COUNT(*) as count')->from(TABLE_RELEASERELATED) ->where('release')->eq($releaseID) ->fetch('count'); - + // 获取所有关联数据 $relatedData = $this->objectModel->dao->select('*')->from(TABLE_RELEASERELATED) ->where('release')->eq($releaseID) ->fetchAll(); - + return array( 'beforeCount' => $beforeCount, 'afterCount' => $afterCount, diff --git a/module/screen/test/lib/screen.unittest.class.php b/module/screen/test/lib/screen.unittest.class.php index dfccf636a5..3df1433e9b 100755 --- a/module/screen/test/lib/screen.unittest.class.php +++ b/module/screen/test/lib/screen.unittest.class.php @@ -771,27 +771,27 @@ class screenTest public function __constructTest(): array { global $tester; - + // Create a new instance to test constructor $screenModel = $tester->loadModel('screen'); - + $result = array(); - + // Check if object type is correct $result['objectType'] = get_class($screenModel) === 'screenModel' ? 1 : 0; - + // Check parent initialization $result['parentInit'] = property_exists($screenModel, 'app') && property_exists($screenModel, 'dao') ? 1 : 0; - + // Check if BI DAO is loaded (dao property should exist after loadBIDAO) $result['biDAOLoaded'] = property_exists($screenModel, 'dao') ? 1 : 0; - - // Check if bi model is loaded + + // Check if bi model is loaded $result['biModelLoaded'] = property_exists($screenModel, 'bi') && is_object($screenModel->bi) ? 1 : 0; - + // Check if filter object is initialized $result['filterExists'] = property_exists($screenModel, 'filter') && is_object($screenModel->filter) ? 1 : 0; - + // Check filter properties initialization if($result['filterExists']) { @@ -811,7 +811,7 @@ class screenTest $result['filterAccount'] = 0; $result['filterCharts'] = 0; } - + return $result; } @@ -992,7 +992,7 @@ class screenTest $testResult['hasNotFoundText'] = isset($result->option->title->notFoundText) ? 1 : 0; $testResult['isDeleted'] = isset($result->option->isDeleted) && $result->option->isDeleted ? 1 : 0; $testResult['notFoundText'] = isset($result->option->title->notFoundText) ? $result->option->title->notFoundText : ''; - + return $testResult; } @@ -1023,14 +1023,14 @@ class screenTest public function genMetricComponentTest($metricID, $component = null, $filterParams = array()) { global $tester; - + // 从数据库获取metric对象 $metric = $tester->dao->select('*')->from(TABLE_METRIC)->where('id')->eq($metricID)->fetch(); if(empty($metric)) { return array('hasComponent' => 0, 'isDeleted' => 0, 'isWaiting' => 0); } - + $result = $this->objectModel->genMetricComponent($metric, $component, $filterParams); if(dao::isError()) return dao::getError(); @@ -1039,7 +1039,7 @@ class screenTest $testResult['hasComponent'] = isset($result) && is_object($result) ? 1 : 0; $testResult['isDeleted'] = (isset($result->option->isDeleted) && $result->option->isDeleted) ? 1 : 0; $testResult['isWaiting'] = ($metric->stage == 'wait') ? 1 : 0; - + return $testResult; } @@ -1114,9 +1114,9 @@ class screenTest { $component = new stdclass(); $component->chartConfig = new stdclass(); - + $latestFilters = array('status' => 'enabled', 'filter1' => 'value1'); - + $result = $this->objectModel->updateMetricFilters($component, $latestFilters); if(dao::isError()) return dao::getError(); @@ -1124,7 +1124,7 @@ class screenTest $testResult['chartConfig'] = isset($result->chartConfig) ? 'present' : 'missing'; $testResult['filters'] = isset($result->chartConfig->filters) ? $result->chartConfig->filters : array(); $testResult['status'] = isset($result->chartConfig->filters['status']) ? $result->chartConfig->filters['status'] : 'missing'; - + return $testResult; } @@ -1139,9 +1139,9 @@ class screenTest $component = new stdclass(); $component->chartConfig = new stdclass(); $component->chartConfig->filters = array('existing' => 'existingFilter', 'preserved' => true); - + $latestFilters = array('new' => 'newFilter'); - + $result = $this->objectModel->updateMetricFilters($component, $latestFilters); if(dao::isError()) return dao::getError(); @@ -1149,7 +1149,7 @@ class screenTest $testResult['chartConfig'] = isset($result->chartConfig) ? 'present' : 'missing'; $testResult['filters'] = isset($result->chartConfig->filters) ? $result->chartConfig->filters : array(); $testResult['existing'] = isset($result->chartConfig->filters['existing']) ? $result->chartConfig->filters['existing'] : 'missing'; - + return $testResult; } @@ -1163,16 +1163,16 @@ class screenTest { $component = new stdclass(); $component->chartConfig = new stdclass(); - + $latestFilters = array(); - + $result = $this->objectModel->updateMetricFilters($component, $latestFilters); if(dao::isError()) return dao::getError(); $testResult = array(); $testResult['chartConfig'] = isset($result->chartConfig) ? 'present' : 'missing'; $testResult['filters'] = isset($result->chartConfig->filters) ? count($result->chartConfig->filters) : -1; - + return $testResult; } @@ -1186,13 +1186,13 @@ class screenTest { $component = new stdclass(); $component->chartConfig = new stdclass(); - + $latestFilters = array( 'field1' => 'value1', 'field2' => 'value2', 'nested' => array('subfield' => 'subvalue') ); - + $result = $this->objectModel->updateMetricFilters($component, $latestFilters); if(dao::isError()) return dao::getError(); @@ -1201,7 +1201,7 @@ class screenTest $testResult['filters'] = isset($result->chartConfig->filters) ? $result->chartConfig->filters : array(); $testResult['field1'] = isset($result->chartConfig->filters['field1']) ? $result->chartConfig->filters['field1'] : 'missing'; $testResult['field2'] = isset($result->chartConfig->filters['field2']) ? $result->chartConfig->filters['field2'] : 'missing'; - + return $testResult; } @@ -1216,9 +1216,9 @@ class screenTest $component = new stdclass(); $component->chartConfig = new stdclass(); // chartConfig exists but no filters property - + $latestFilters = array('newField' => 'newValue'); - + $result = $this->objectModel->updateMetricFilters($component, $latestFilters); if(dao::isError()) return dao::getError(); @@ -1226,7 +1226,7 @@ class screenTest $testResult['chartConfig'] = isset($result->chartConfig) ? 'present' : 'missing'; $testResult['filters'] = isset($result->chartConfig->filters) ? $result->chartConfig->filters : array(); $testResult['newField'] = isset($result->chartConfig->filters['newField']) ? $result->chartConfig->filters['newField'] : 'missing'; - + return $testResult; } @@ -1376,7 +1376,7 @@ class screenTest if(!isset($config->screen->chartOption['WaterPolo'])) { $config->screen->chartOption['WaterPolo'] = '{"type":"nomal","series":[{"type":"liquidFill","radius":"90%"}],"backgroundColor":"rgba(0,0,0,0)"}'; } - + $result = $this->objectModel->getWaterPoloOption($component, $chart, $filters); if(dao::isError()) return dao::getError(); @@ -1387,7 +1387,7 @@ class screenTest $testResult['datasetValue'] = (isset($result->option) && isset($result->option->dataset)) ? $result->option->dataset : 'null'; $testResult['hasStyles'] = isset($result->styles) ? 1 : 0; $testResult['componentType'] = is_object($result) ? 'object' : gettype($result); - + return $testResult; } catch (Exception $e) { return array('error' => $e->getMessage()); @@ -1440,14 +1440,14 @@ class screenTest public function getMetricCardOptionTest($metricId, $resultData, $component = null) { global $tester; - + // 从数据库获取metric对象 $metric = $tester->dao->select('*')->from(TABLE_METRIC)->where('id')->eq($metricId)->fetch(); if(empty($metric)) { return new stdclass(); } - + $result = $this->objectModel->getMetricCardOption($metric, $resultData, $component); if(dao::isError()) return dao::getError(); diff --git a/module/screen/test/model/getmetricpagination.php b/module/screen/test/model/getmetricpagination.php index 0605a58c21..42c7f36f76 100755 --- a/module/screen/test/model/getmetricpagination.php +++ b/module/screen/test/model/getmetricpagination.php @@ -7,27 +7,27 @@ title=测试 screenModel::getMetricPagination(); timeout=0 cid=18248 -- 执行screenTest模块的getMetricPaginationTest方法,参数是null +- 执行screenTest模块的getMetricPaginationTest方法,参数是null - 属性index @1 - 属性size @5 - 属性total @0 - 属性pageTotal @1 -- 执行screenTest模块的getMetricPaginationTest方法 +- 执行screenTest模块的getMetricPaginationTest方法 - 属性index @1 - 属性size @5 - 属性total @0 - 属性pageTotal @1 -- 执行screenTest模块的getMetricPaginationTest方法 +- 执行screenTest模块的getMetricPaginationTest方法 - 属性index @1 - 属性size @12 - 属性total @0 - 属性pageTotal @1 -- 执行screenTest模块的getMetricPaginationTest方法 +- 执行screenTest模块的getMetricPaginationTest方法 - 属性index @1 - 属性size @10 - 属性total @0 - 属性pageTotal @1 -- 执行screenTest模块的getMetricPaginationTest方法 +- 执行screenTest模块的getMetricPaginationTest方法 - 属性index @1 - 属性size @12 - 属性total @0 diff --git a/module/screen/test/model/getmetrictableoption.php b/module/screen/test/model/getmetrictableoption.php index 560da2aaa1..340613e737 100755 --- a/module/screen/test/model/getmetrictableoption.php +++ b/module/screen/test/model/getmetrictableoption.php @@ -34,7 +34,7 @@ class SimpleMockMetric { public function isObjectMetric($header) { return true; } - + public function getGroupTable($header, $data, $dateType, $flag) { $groupHeader = 'Test Header'; $groupData = array(array('Item1', 100), array('Item2', 200)); diff --git a/module/screen/test/model/getoptionsfromsql.php b/module/screen/test/model/getoptionsfromsql.php index b139106c94..d569c653d5 100755 --- a/module/screen/test/model/getoptionsfromsql.php +++ b/module/screen/test/model/getoptionsfromsql.php @@ -7,14 +7,14 @@ title=测试 screenModel::getOptionsFromSql(); timeout=0 cid=18250 -- 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, account FROM " . TABLE_USER . " WHERE deleted='0' ORDER BY id LIMIT 3", 'id', 'account' +- 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, account FROM " . TABLE_USER . " WHERE deleted='0' ORDER BY id LIMIT 3", 'id', 'account' - 属性1 @admin - 属性2 @test1 - 属性3 @test2 - 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, account FROM " . TABLE_USER . " WHERE id > 999", 'id', 'account' @0 - 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, account FROM " . TABLE_USER . " WHERE deleted='0' ORDER BY id LIMIT 2", 'nonexistent', 'account' @0 - 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, account FROM " . TABLE_USER . " WHERE deleted='0' ORDER BY id LIMIT 2", 'id', 'nonexistent' @0 -- 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, realname FROM " . TABLE_USER . " WHERE deleted='0' ORDER BY id", 'id', 'realname' +- 执行screenTest模块的getOptionsFromSqlTest方法,参数是"SELECT id, realname FROM " . TABLE_USER . " WHERE deleted='0' ORDER BY id", 'id', 'realname' - 属性1 @管理员 - 属性2 @测试用户1 - 属性3 @测试用户2 diff --git a/module/screen/test/model/yaml/action_getactiveusertable.yaml b/module/screen/test/model/yaml/action_getactiveusertable.yaml index cd960e7382..d7bbc28189 100644 --- a/module/screen/test/model/yaml/action_getactiveusertable.yaml +++ b/module/screen/test/model/yaml/action_getactiveusertable.yaml @@ -25,7 +25,7 @@ fields: note: 项目ID range: 1-3 - field: execution - note: 执行ID + note: 执行ID range: 1-3 - field: actor note: 操作者账号 diff --git a/module/stage/config.php b/module/stage/config.php index 4319c64885..e98c302177 100644 --- a/module/stage/config.php +++ b/module/stage/config.php @@ -26,3 +26,11 @@ $config->stage->actionList['delete']['hint'] = $lang->stage->delete; $config->stage->actionList['delete']['data-confirm'] = array('message' => $lang->stage->confirmDelete, 'icon' => 'icon-exclamation-sign', 'iconClass' => 'warning-pale rounded-full icon-2x'); $config->stage->actionList['delete']['url'] = helper::createLink('stage', 'delete', 'stageID={id}'); $config->stage->actionList['delete']['ajaxSubmit'] = true; + +$config->stage->ipdReviewPoint = new stdclass(); +$config->stage->ipdReviewPoint->concept = array('TR1', 'CDCP'); +$config->stage->ipdReviewPoint->plan = array('TR2', 'TR3', 'PDCP'); +$config->stage->ipdReviewPoint->develop = array('TR4', 'TR4A', 'TR5'); +$config->stage->ipdReviewPoint->qualify = array('TR6', 'ADCP'); +$config->stage->ipdReviewPoint->launch = array(); +$config->stage->ipdReviewPoint->lifecycle = array('LDCP'); diff --git a/module/stage/config/dtable.php b/module/stage/config/dtable.php index 1f74a78ced..3199246175 100644 --- a/module/stage/config/dtable.php +++ b/module/stage/config/dtable.php @@ -3,10 +3,16 @@ global $lang; $config->stage->dtable = new stdclass(); $config->stage->dtable->fieldList['id']['type'] = 'id'; -$config->stage->dtable->fieldList['name']['type'] = 'name'; +$config->stage->dtable->fieldList['sort']['title'] = $lang->stage->order; +$config->stage->dtable->fieldList['sort']['fixed'] = 'left'; +$config->stage->dtable->fieldList['sort']['align'] = 'center'; +$config->stage->dtable->fieldList['sort']['group'] = 1; + +$config->stage->dtable->fieldList['name']['type'] = 'title'; $config->stage->dtable->fieldList['type']['type'] = 'status'; $config->stage->dtable->fieldList['type']['statusMap'] = $lang->stage->typeList; +$config->stage->dtable->fieldList['type']['group'] = 2; $config->stage->dtable->fieldList['actions']['type'] = 'actions'; $config->stage->dtable->fieldList['actions']['menu'] = array('edit', 'delete'); diff --git a/module/stage/config/form.php b/module/stage/config/form.php index 82a42e0740..f4a795e302 100644 --- a/module/stage/config/form.php +++ b/module/stage/config/form.php @@ -1,30 +1,30 @@ stage->form = new stdclass(); $config->stage->form->create['name'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); $config->stage->form->create['type'] = array('type' => 'string', 'required' => true, 'default' => ''); -$config->stage->form->create['projectType'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->stage->form->create['createdBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); -$config->stage->form->create['createdDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); +$config->stage->form->create['createdBy'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->stage->form->create['createdDate'] = array('type' => 'datetime', 'required' => false, 'default' => ''); if(isset($config->setPercent) && $config->setPercent == 1) $config->stage->form->create['percent'] = array('type' => 'float', 'required' => true, 'default' => 0); -$config->stage->form->batchcreate['name'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim', 'base' => true); -$config->stage->form->batchcreate['type'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->stage->form->batchcreate['projectType'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->stage->form->batchcreate['createdBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); -$config->stage->form->batchcreate['createdDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); +$config->stage->form->batchcreate['name'] = array('type' => 'string', 'required' => false, 'default' => '', 'filter' => 'trim', 'base' => true); +$config->stage->form->batchcreate['type'] = array('type' => 'string', 'required' => false, 'default' => ''); if(isset($config->setPercent) && $config->setPercent == 1) $config->stage->form->batchcreate['percent'] = array('type' => 'float', 'required' => true, 'default' => 0); -$config->stage->form->edit['name'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); -$config->stage->form->edit['type'] = array('type' => 'string', 'required' => true, 'default' => ''); -$config->stage->form->edit['editedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); -$config->stage->form->edit['editedDate'] = array('type' => 'datetime', 'required' => false, 'default' => $now); +$config->stage->form->edit['name'] = array('type' => 'string', 'required' => true, 'default' => '', 'filter' => 'trim'); +$config->stage->form->edit['type'] = array('type' => 'string', 'required' => true, 'default' => ''); if(isset($config->setPercent) && $config->setPercent == 1) $config->stage->form->edit['percent'] = array('type' => 'float', 'required' => true, 'default' => 0); $config->stage->form->settype['lang'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->stage->form->settype['keys'] = array('type' => 'array', 'required' => false, 'default' => array()); $config->stage->form->settype['values'] = array('type' => 'array', 'required' => false, 'default' => array()); + +$config->stage->form->settrpoint['id'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->stage->form->settrpoint['title'] = array('type' => 'string', 'required' => true, 'default' => '', 'base' => true, 'filter' => 'trim'); +$config->stage->form->settrpoint['flow'] = array('type' => 'int', 'required' => true, 'default' => 1); + +$config->stage->form->setdcppoint['id'] = array('type' => 'int', 'required' => false, 'default' => 0); +$config->stage->form->setdcppoint['title'] = array('type' => 'string', 'required' => true, 'default' => '', 'base' => true, 'filter' => 'trim'); +$config->stage->form->setdcppoint['flow'] = array('type' => 'int', 'required' => true, 'default' => 1); diff --git a/module/stage/control.php b/module/stage/control.php index fe91a07cec..de2438a32c 100644 --- a/module/stage/control.php +++ b/module/stage/control.php @@ -16,69 +16,91 @@ class stage extends control * 瀑布模型阶段列表页。 * Waterfall model stage list page. * + * @param int $groupID * @param string $orderBy - * @param string $type waterfall|waterfallplus * @access public * @return void */ - public function browse(string $orderBy = "id_asc", string $type = 'waterfall') + public function browse(int $groupID = 0, string $orderBy = "order_asc") { - if($type == 'waterfallplus') $this->locate($this->createLink('stage', 'plusBrowse', "orderBy={$orderBy}&type=waterfallplus")); + if($this->config->edition != 'open') + { + $workflowGroup = $this->loadModel('workflowgroup')->getByID($groupID); + if($workflowGroup->projectModel == 'ipd') + { + $this->config->stage->dtable->fieldList['type']['statusMap'] = $this->lang->stage->ipdTypeList; - $this->stage->setMenu($type); + $this->config->stage->dtable->fieldList['TRpoint']['title'] = $this->lang->stage->TRpoint; + $this->config->stage->dtable->fieldList['TRpoint']['type'] = 'desc'; + $this->config->stage->dtable->fieldList['TRpoint']['width'] = 100; + $this->config->stage->dtable->fieldList['TRpoint']['flex'] = false; + $this->config->stage->dtable->fieldList['TRpoint']['group'] = 3; - $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->browse; - $this->view->stages = $this->stage->getStages($orderBy, 0, $type); + $this->config->stage->dtable->fieldList['DCPpoint']['title'] = $this->lang->stage->DCPpoint; + $this->config->stage->dtable->fieldList['DCPpoint']['type'] = 'desc'; + $this->config->stage->dtable->fieldList['DCPpoint']['width'] = 100; + $this->config->stage->dtable->fieldList['DCPpoint']['flex'] = false; + $this->config->stage->dtable->fieldList['DCPpoint']['group'] = 4; + + if(common::hasPriv('stage', 'edit')) + { + $this->config->stage->actionList['setTRpoint']['icon'] = 'tr-box'; + $this->config->stage->actionList['setTRpoint']['hint'] = $this->lang->stage->setTRpoint; + $this->config->stage->actionList['setTRpoint']['url'] = array('module' => 'stage', 'method' => 'setTRpoint', 'params' => 'stageID={id}'); + $this->config->stage->actionList['setTRpoint']['data-toggle'] = 'modal'; + + $this->config->stage->actionList['setDCPpoint']['icon'] = 'dcp-box'; + $this->config->stage->actionList['setDCPpoint']['hint'] = $this->lang->stage->setDCPpoint; + $this->config->stage->actionList['setDCPpoint']['url'] = array('module' => 'stage', 'method' => 'setDCPpoint', 'params' => 'stageID={id}'); + $this->config->stage->actionList['setDCPpoint']['data-toggle'] = 'modal'; + + $this->config->stage->dtable->fieldList['actions']['menu'] = array('setTRpoint', 'setDCPpoint', 'edit', 'delete'); + $this->config->stage->dtable->fieldList['actions']['list'] = $this->config->stage->actionList; + } + } + } + + $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->browseAB; + $this->view->stages = $this->stage->getStages($orderBy, 0, $groupID); $this->view->orderBy = $orderBy; - $this->view->type = $type; + $this->view->groupID = $groupID; $this->display(); } - /** - * 融合瀑布模型阶段列表页。 - * Waterfall plus model stage list page. - * - * @param string $orderBy - * @param string $type waterfall|waterfallplus - * @access public - * @return void - */ - public function plusBrowse($orderBy = "id_asc", $type = 'waterfallplus') - { - if($type == 'waterfall') $this->locate($this->createLink('stage', 'browse', "orderBy={$orderBy}&type=waterfall")); - - $this->stage->setMenu($type); - - $this->view->stages = $this->stage->getStages($orderBy, 0, $type); - $this->view->orderBy = $orderBy; - $this->view->type = $type; - $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->browse; - - $this->display('stage', 'browse'); - } - /** * 创建一个阶段。 * Create a stage. * - * @param string $type waterfall|waterfallplus + * @param int $groupID * @access public * @return void */ - public function create(string $type = 'waterfall') + public function create(int $groupID = 0) { + $flow = $this->config->edition == 'open' ? new stdClass() : $this->loadModel('workflowgroup')->getByID($groupID); if($_POST) { - $stageData = form::data()->setDefault('projectType', $type)->get(); - $stageID = $this->stage->create($stageData, $type); + if(isset($flow->projectModel) && $flow->projectModel == 'ipd') $this->config->stage->create->requiredFields = 'name,type'; + + $stageData = form::data() + ->setDefault('workflowGroup', $groupID) + ->setDefault('createdBy', $this->app->user->account) + ->setDefault('createdDate', helper::now()) + ->get(); + + $stageID = $this->stage->create($stageData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $this->loadModel('action')->create('stage', $stageID, 'Opened'); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true)); } - $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->create; + unset($this->lang->stage->ipdTypeList['lifecycle']); + + $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->create; + $this->view->groupID = $groupID; + $this->view->flow = $flow; $this->display(); } @@ -87,23 +109,33 @@ class stage extends control * 批量创建阶段。 * Batch create stages. * - * @param string $type waterfall|waterfallplus + * @param int $groupID * @access public * @return void */ - public function batchCreate(string $type = 'waterfall') + public function batchCreate(int $groupID = 0) { - $this->stage->setMenu($type); + $flow = $this->config->edition == 'open' ? new stdClass() : $this->loadModel('workflowgroup')->getByID($groupID); if($_POST) { - $stages = form::batchData()->setDefault('projectType', $type)->get(); - $this->stage->batchCreate($type, $stages); + if(isset($flow->projectModel) && $flow->projectModel == 'ipd') + { + $this->config->stage->create->requiredFields = 'name,type'; + if(isset($this->config->setPercent) && $this->config->setPercent == 1) $this->config->stage->form->batchcreate['percent']['required'] = false; + } + + $stages = form::batchData()->get(); + $this->stage->batchCreate($groupID, $stages); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => inlink($type == 'waterfall' ? 'browse' : 'plusBrowse', "orderBy=id_asc&type=$type"))); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => inlink('browse', "groupID={$groupID}"))); } - $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->batchCreate; + unset($this->lang->stage->ipdTypeList['lifecycle']); + + $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->batchCreate; + $this->view->groupID = $groupID; + $this->view->flow = $flow; $this->display(); } @@ -119,10 +151,16 @@ class stage extends control public function edit(int $stageID = 0) { $stage = $this->stage->getByID($stageID); - $this->stage->setMenu($stage->projectType); + $flow = $this->config->edition == 'open' ? new stdClass() : $this->loadModel('workflowgroup')->getByID($stage->workflowGroup); + if($_POST) { - $stageData = form::data()->get(); + if(isset($flow->projectModel) && $flow->projectModel == 'ipd') $this->config->stage->edit->requiredFields = 'name,type'; + + $stageData = form::data() + ->setDefault('editedBy', $this->app->user->account) + ->setDefault('editedDate', helper::now()) + ->get(); $changes = $this->stage->update($stageID, $stageData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -132,8 +170,11 @@ class stage extends control return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true)); } + unset($this->lang->stage->ipdTypeList['lifecycle']); + $this->view->title = $this->lang->stage->common . $this->lang->hyphen . $this->lang->stage->edit; $this->view->stage = $stage; + $this->view->flow = $flow; $this->display(); } @@ -213,4 +254,71 @@ class stage extends control if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); return $this->sendSuccess(array('closeModal' => true, 'load' => true)); } + + /** + * 设置TR评审点。 + * Set TR point of stage. + * + * @param int $stageID + * @access public + * @return void + */ + public function setTRpoint(int $stageID) + { + $this->app->loadLang('review'); + + if(!empty($_POST)) + { + $this->lang->stage->title = $this->lang->stage->TRname; + $points = form::batchData()->get(); + $this->stage->setPoint('TR', $stageID, $points); + if(dao::isError()) return $this->sendError(dao::getError()); + return $this->sendSuccess(array('closeModal' => true, 'load' => true)); + } + + $this->view->type = 'TR'; + $this->view->approvals = $this->loadModel('approvalflow')->getPairs('project'); + $this->view->stagePoints = $this->stage->getStagePoints('TR', $stageID); + $this->display('stage', 'setPoint'); + } + + /** + * 设置DCP评审点。 + * Set point of stage. + * + * @param int $stageID + * @access public + * @return void + */ + public function setDCPpoint(int $stageID) + { + $this->app->loadLang('review'); + + if(!empty($_POST)) + { + $this->lang->stage->title = $this->lang->stage->DCPname; + $points = form::batchData()->get(); + $this->stage->setPoint('DCP', $stageID, $points); + if(dao::isError()) return $this->sendError(dao::getError()); + return $this->sendSuccess(array('closeModal' => true, 'load' => true)); + } + + $this->view->type = 'DCP'; + $this->view->approvals = $this->loadModel('approvalflow')->getPairs('project'); + $this->view->stagePoints = $this->stage->getStagePoints('DCP', $stageID); + $this->display('stage', 'setPoint'); + } + + /** + * 更新排序。 + * Update order. + * + * @access public + * @return void + */ + public function updateOrder() + { + $sortedIdList = json_decode($this->post->sortedIdList, true); + $this->stage->updateOrder($sortedIdList); + } } diff --git a/module/stage/js/browse.ui.js b/module/stage/js/browse.ui.js new file mode 100644 index 0000000000..237e6b3d91 --- /dev/null +++ b/module/stage/js/browse.ui.js @@ -0,0 +1,20 @@ +window.renderCell = function(result, info) +{ + if(info.col.name == 'sort') + { + result[0] = {html: "", className: 'text-gray cursor-move move-stage'}; + } + return result; +} + +window.onSortEnd = function(from, to, type) +{ + if(!from || !to) return false; + + const url = $.createLink('stage', 'updateOrder'); + const form = new FormData(); + form.append('sortedIdList', JSON.stringify(this.state.rowOrders)); + + $.ajaxSubmit({url, data: form}); + return true; +} diff --git a/module/stage/js/setpoint.ui.js b/module/stage/js/setpoint.ui.js new file mode 100644 index 0000000000..6c4d500bda --- /dev/null +++ b/module/stage/js/setpoint.ui.js @@ -0,0 +1,10 @@ +window.refreshApproval = function() +{ + $.getJSON($.createLink('review', 'ajaxGetApproval'), function(data) + { + $('[name^=flow]').each(function() + { + $(this).zui('picker').render({items: data}); + }); + }); +} diff --git a/module/stage/lang/de.php b/module/stage/lang/de.php index 16ed0dd9d9..93913a5327 100644 --- a/module/stage/lang/de.php +++ b/module/stage/lang/de.php @@ -10,20 +10,29 @@ * @link https://www.zentao.net */ /* Actions. */ -$lang->stage->browse = 'Waterfall Stage List'; +$lang->stage->browse = 'Stage List'; +$lang->stage->browseAB = 'Stage List'; $lang->stage->create = 'Create Stage'; -$lang->stage->batchCreate = 'Batch Create'; -$lang->stage->edit = 'Edit'; -$lang->stage->delete = 'Delete'; +$lang->stage->batchCreate = 'Batch Create Stage'; +$lang->stage->edit = 'Edit Stage'; +$lang->stage->delete = 'Delete Stage'; $lang->stage->view = 'Details'; $lang->stage->plusBrowse = 'Waterfall Plus Stage List'; +$lang->stage->setTRpoint = 'Set TR Point'; +$lang->stage->setDCPpoint = 'Set DCP Point'; /* Fields. */ -$lang->stage->id = 'ID'; -$lang->stage->name = 'Name'; -$lang->stage->type = 'Type'; -$lang->stage->percent = 'Workload %'; -$lang->stage->setType = 'Set Type'; +$lang->stage->id = 'ID'; +$lang->stage->name = 'Name'; +$lang->stage->type = 'Type'; +$lang->stage->percent = 'Workload %'; +$lang->stage->setType = 'Stage Type'; +$lang->stage->TRpoint = 'TR Point'; +$lang->stage->DCPpoint = 'DCP Point'; +$lang->stage->TRname = 'TR Point Name'; +$lang->stage->DCPname = 'DCP Point Name'; +$lang->stage->pointFlow = 'Approval Flow'; +$lang->stage->order = 'Order'; $lang->stage->typeList['mix'] = 'Mix'; $lang->stage->typeList['request'] = 'Story'; diff --git a/module/stage/lang/en.php b/module/stage/lang/en.php index 16ed0dd9d9..93913a5327 100644 --- a/module/stage/lang/en.php +++ b/module/stage/lang/en.php @@ -10,20 +10,29 @@ * @link https://www.zentao.net */ /* Actions. */ -$lang->stage->browse = 'Waterfall Stage List'; +$lang->stage->browse = 'Stage List'; +$lang->stage->browseAB = 'Stage List'; $lang->stage->create = 'Create Stage'; -$lang->stage->batchCreate = 'Batch Create'; -$lang->stage->edit = 'Edit'; -$lang->stage->delete = 'Delete'; +$lang->stage->batchCreate = 'Batch Create Stage'; +$lang->stage->edit = 'Edit Stage'; +$lang->stage->delete = 'Delete Stage'; $lang->stage->view = 'Details'; $lang->stage->plusBrowse = 'Waterfall Plus Stage List'; +$lang->stage->setTRpoint = 'Set TR Point'; +$lang->stage->setDCPpoint = 'Set DCP Point'; /* Fields. */ -$lang->stage->id = 'ID'; -$lang->stage->name = 'Name'; -$lang->stage->type = 'Type'; -$lang->stage->percent = 'Workload %'; -$lang->stage->setType = 'Set Type'; +$lang->stage->id = 'ID'; +$lang->stage->name = 'Name'; +$lang->stage->type = 'Type'; +$lang->stage->percent = 'Workload %'; +$lang->stage->setType = 'Stage Type'; +$lang->stage->TRpoint = 'TR Point'; +$lang->stage->DCPpoint = 'DCP Point'; +$lang->stage->TRname = 'TR Point Name'; +$lang->stage->DCPname = 'DCP Point Name'; +$lang->stage->pointFlow = 'Approval Flow'; +$lang->stage->order = 'Order'; $lang->stage->typeList['mix'] = 'Mix'; $lang->stage->typeList['request'] = 'Story'; diff --git a/module/stage/lang/fr.php b/module/stage/lang/fr.php index 16ed0dd9d9..93913a5327 100644 --- a/module/stage/lang/fr.php +++ b/module/stage/lang/fr.php @@ -10,20 +10,29 @@ * @link https://www.zentao.net */ /* Actions. */ -$lang->stage->browse = 'Waterfall Stage List'; +$lang->stage->browse = 'Stage List'; +$lang->stage->browseAB = 'Stage List'; $lang->stage->create = 'Create Stage'; -$lang->stage->batchCreate = 'Batch Create'; -$lang->stage->edit = 'Edit'; -$lang->stage->delete = 'Delete'; +$lang->stage->batchCreate = 'Batch Create Stage'; +$lang->stage->edit = 'Edit Stage'; +$lang->stage->delete = 'Delete Stage'; $lang->stage->view = 'Details'; $lang->stage->plusBrowse = 'Waterfall Plus Stage List'; +$lang->stage->setTRpoint = 'Set TR Point'; +$lang->stage->setDCPpoint = 'Set DCP Point'; /* Fields. */ -$lang->stage->id = 'ID'; -$lang->stage->name = 'Name'; -$lang->stage->type = 'Type'; -$lang->stage->percent = 'Workload %'; -$lang->stage->setType = 'Set Type'; +$lang->stage->id = 'ID'; +$lang->stage->name = 'Name'; +$lang->stage->type = 'Type'; +$lang->stage->percent = 'Workload %'; +$lang->stage->setType = 'Stage Type'; +$lang->stage->TRpoint = 'TR Point'; +$lang->stage->DCPpoint = 'DCP Point'; +$lang->stage->TRname = 'TR Point Name'; +$lang->stage->DCPname = 'DCP Point Name'; +$lang->stage->pointFlow = 'Approval Flow'; +$lang->stage->order = 'Order'; $lang->stage->typeList['mix'] = 'Mix'; $lang->stage->typeList['request'] = 'Story'; diff --git a/module/stage/lang/zh-cn.php b/module/stage/lang/zh-cn.php index 5055a90742..b4e16c3259 100644 --- a/module/stage/lang/zh-cn.php +++ b/module/stage/lang/zh-cn.php @@ -10,20 +10,29 @@ * @link https://www.zentao.net */ /* Actions. */ -$lang->stage->browse = '瀑布阶段列表'; -$lang->stage->create = '新建'; -$lang->stage->batchCreate = '批量新建'; -$lang->stage->edit = '编辑'; -$lang->stage->delete = '删除'; +$lang->stage->browse = '浏览阶段列表'; +$lang->stage->browseAB = '阶段列表'; +$lang->stage->create = '新建阶段'; +$lang->stage->batchCreate = '批量新建阶段'; +$lang->stage->edit = '编辑阶段'; +$lang->stage->delete = '删除阶段'; $lang->stage->view = '阶段详情'; $lang->stage->plusBrowse = '融合瀑布阶段列表'; +$lang->stage->setTRpoint = 'TR评审点配置'; +$lang->stage->setDCPpoint = 'DCP评审点配置'; /* Fields. */ -$lang->stage->id = 'ID'; -$lang->stage->name = '阶段名称'; -$lang->stage->type = '阶段类型'; -$lang->stage->percent = '工作量占比'; -$lang->stage->setType = '阶段类型'; +$lang->stage->id = 'ID'; +$lang->stage->name = '阶段名称'; +$lang->stage->type = '阶段类型'; +$lang->stage->percent = '工作量占比'; +$lang->stage->setType = '阶段类型'; +$lang->stage->TRpoint = 'TR评审点'; +$lang->stage->DCPpoint = 'DCP评审点'; +$lang->stage->TRname = 'TR评审点名称'; +$lang->stage->DCPname = 'DCP评审点名称'; +$lang->stage->pointFlow = '审批流'; +$lang->stage->order = '排序'; $lang->stage->typeList['mix'] = '综合'; $lang->stage->typeList['request'] = '需求'; diff --git a/module/stage/model.php b/module/stage/model.php index 1c2e8fd79b..a78995f69f 100644 --- a/module/stage/model.php +++ b/module/stage/model.php @@ -18,16 +18,15 @@ class stageModel extends model * 创建一个阶段。 * Create a stage. * - * @param object $stage - * @param string $type waterfall|waterfallplus + * @param object $stage * @access public * @return int|bool */ - public function create(object $stage, string $type = 'waterfall'): int|bool + public function create(object $stage): int|bool { if(isset($this->config->setPercent) && $this->config->setPercent == 1) { - $totalPercent = $this->getTotalPercent($type); + $totalPercent = $this->getTotalPercent($stage->workflowGroup); if(!is_numeric($stage->percent)) { @@ -41,12 +40,18 @@ class stageModel extends model } } + $this->lang->error->unique = $this->lang->error->repeat; if(!empty($stage->percent)) $stage->percent = (float)$stage->percent; $this->dao->insert(TABLE_STAGE) ->data($stage) ->autoCheck() ->batchCheck($this->config->stage->create->requiredFields, 'notempty') + ->checkIF(!empty($stage->name), 'name', 'unique', "`workflowGroup` = '$stage->workflowGroup' AND `deleted` = '0'") ->exec(); + $stageID = $this->dao->lastInsertID(); + + $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_STAGE)->where('workflowGroup')->eq($stage->workflowGroup)->andWhere('deleted')->eq('0')->fetch('maxOrder'); + $this->dao->update(TABLE_STAGE)->set('order')->eq((int)$maxOrder + 1)->where('id')->eq($stageID)->exec(); if(dao::isError()) return false; return $this->dao->lastInsertID(); @@ -56,17 +61,17 @@ class stageModel extends model * 批量创建阶段。 * Batch create stages. * - * @param string $type waterfall|waterfallplus + * @param int $groupID * @param array $stages * @access public * @return bool */ - public function batchCreate($type = 'waterfall', array $stages = array()): bool + public function batchCreate(int $groupID = 0, array $stages = array()): bool { $setPercent = (isset($this->config->setPercent) && $this->config->setPercent == 1) ? true : false; if($setPercent) { - $oldTotalPercent = $this->getTotalPercent($type); + $oldTotalPercent = $this->getTotalPercent($groupID); $totalPercent = 0; foreach($stages as $stage) { @@ -81,10 +86,26 @@ class stageModel extends model } } - $this->loadModel('action'); + $stageNames = $this->dao->select('id,name')->from(TABLE_STAGE)->where('workflowGroup')->eq($groupID)->andWhere('deleted')->eq('0')->fetchPairs('name'); foreach($stages as $rowID => $stage) { - $stage->projectType = $type; + if(in_array($stage->name, $stageNames)) + { + dao::$errors["name[$rowID]"] = sprintf($this->lang->error->repeat, $this->lang->stage->name, $stage->name); + return false; + } + $stageNames[] = $stage->name; + } + + $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_STAGE)->where('workflowGroup')->eq($groupID)->andWhere('deleted')->eq('0')->fetch('maxOrder'); + + $this->loadModel('action'); + $now = helper::now(); + foreach($stages as $rowID => $stage) + { + $stage->workflowGroup = $groupID; + $stage->createdBy = $this->app->user->account; + $stage->createdDate = $now; if(!empty($stage->percent)) $stage->percent = (float)$stage->percent; $this->dao->insert(TABLE_STAGE)->data($stage)->autoCheck() ->batchCheck($this->config->stage->create->requiredFields, 'notempty') @@ -98,6 +119,9 @@ class stageModel extends model $stageID = $this->dao->lastInsertID(); $this->action->create('stage', $stageID, 'Opened'); + + $stageOrder = empty($maxOrder) ? 1 : ++ $maxOrder; + $this->dao->update(TABLE_STAGE)->set('order')->eq($stageOrder)->where('id')->eq($stageID)->exec(); } return true; @@ -119,19 +143,21 @@ class stageModel extends model if(isset($this->config->setPercent) && $this->config->setPercent == 1) { - $totalPercent = $this->getTotalPercent($oldStage->projectType); - if(round($totalPercent + (float)$stage->percent - $oldStage->percent) > 100) + $totalPercent = $this->getTotalPercent($oldStage->workflowGroup); + if(round($totalPercent + (float)$stage->percent - (float)$oldStage->percent) > 100) { dao::$errors['percent'] = $this->lang->stage->error->percentOver; return false; } } + $this->lang->error->unique = $this->lang->error->repeat; $this->dao->update(TABLE_STAGE) ->data($stage) ->autoCheck() ->batchCheck($this->config->stage->edit->requiredFields, 'notempty') ->checkIF(isset($stage->percent) && $stage->percent != '', 'percent', 'float')->where('id')->eq((int)$stageID) + ->checkIF(!empty($stage->name), 'name', 'unique', "`id` != '$stageID' AND `workflowGroup` = '$oldStage->workflowGroup' AND `deleted` = '0'") ->exec(); if(dao::isError()) return false; @@ -144,12 +170,12 @@ class stageModel extends model * * @param string $orderBy * @param int $projectID - * @param string $type - * @param string $grade + * @param int $groupID + * @param int $grade * @access public * @return array */ - public function getStages(string $orderBy = 'id_desc', int $projectID = 0, string $type = '', int $grade = 0): array + public function getStages(string $orderBy = 'order_desc', int $projectID = 0, int $groupID = 0, int $grade = 0): array { if(common::isTutorialMode()) return $this->loadModel('tutorial')->getStages(); @@ -167,19 +193,32 @@ class stageModel extends model ->fetchAll('id'); } - $stageType = ''; - if($this->config->systemMode == 'PLM' && $this->app->rawMethod == 'create' && $this->app->rawModule == 'programplan' && $type == 'ipd') - { - $project = $this->loadModel('project')->fetchByID($this->session->project); - $stageType = zget($this->config->project->categoryStages, $project->category, ''); - } - - return $this->dao->select('*')->from(TABLE_STAGE) + $stages = $this->dao->select('*')->from(TABLE_STAGE) ->where('deleted')->eq(0) - ->andWhere('projectType')->eq($type) - ->beginIF(!empty($stageType))->andWhere('type')->in($stageType)->fi() + ->andWhere('workflowGroup')->eq($groupID) + ->beginIF($this->config->edition != 'ipd')->andWhere('projectType')->ne('ipd')->fi() + ->andWhere('type')->ne('lifecycle') ->orderBy($orderBy) ->fetchAll('id'); + + $stagePoints = $this->dao->select('*')->from(TABLE_DECISION)->where('stage')->in(array_keys($stages))->andWhere('deleted')->eq('0')->orderBy('order_asc')->fetchGroup('stage'); + $pointList = array(); + foreach($stagePoints as $stageID => $points) + { + if(!isset($pointList[$stageID])) $pointList[$stageID] = array(); + foreach($points as $point) + { + if(!isset($pointList[$stageID][$point->type])) $pointList[$stageID][$point->type] = array(); + $pointList[$stageID][$point->type][$point->id] = $point->title; + } + } + foreach($stages as $stage) + { + $stage->TRpoint = isset($pointList[$stage->id]['TR']) ? implode(', ', $pointList[$stage->id]['TR']) : ''; + $stage->DCPpoint = isset($pointList[$stage->id]['DCP']) ? implode(', ', $pointList[$stage->id]['DCP']) : ''; + $stage->pointList = zget($pointList, $stage->id, array()); + } + return $stages; } /** @@ -202,40 +241,207 @@ class stageModel extends model * @param string $type waterfall|waterfallplus * @return float */ - public function getTotalPercent(string $type): float + public function getTotalPercent(int $groupID): float { - return (float)$this->dao->select('sum(percent) as total')->from(TABLE_STAGE)->where('deleted')->eq('0')->andWhere('projectType')->eq($type)->fetch('total'); + return (float)$this->dao->select('sum(percent) as total')->from(TABLE_STAGE)->where('deleted')->eq('0')->andWhere('workflowGroup')->eq($groupID)->fetch('total'); } /** - * 设置阶段导航。 - * Set menu. + * 添加内置评审点。 + * Add the builtin point. * - * @param string $type waterfall|waterfallplus + * @param int $groupID + * @return void + */ + public function addBuiltinPoint(int $groupID) + { + $workflowGroup = $this->dao->select('*')->from(TABLE_WORKFLOWGROUP)->where('id')->eq($groupID)->fetch(); + + $this->app->loadConfig('project'); + $stageList = $this->dao->select('*')->from(TABLE_STAGE)->where('workflowGroup')->eq($groupID)->fetchAll('id'); + if(empty($stageList)) + { + $stageTypeList = array(); + if($workflowGroup->projectModel == 'ipd') + { + if($workflowGroup->projectType == 'ipd') $stageTypeList = $this->config->project->categoryStages['IPD']; + if($workflowGroup->projectType == 'tpd') $stageTypeList = $this->config->project->categoryStages['TPD']; + if($workflowGroup->projectType == 'cbb') $stageTypeList = $this->config->project->categoryStages['CBB']; + if(in_array($workflowGroup->projectType, array('cpdproduct', 'cpdproject'))) $stageTypeList = $this->config->project->categoryStages['CPD']; + } + if(in_array($workflowGroup->projectModel, array('waterfall', 'waterfallplus'))) $stageTypeList = $this->config->project->categoryStages['waterfall']; + + foreach($stageTypeList as $stageType) + { + $builtinStage = new stdClass(); + $builtinStage->workflowGroup = $groupID; + $builtinStage->createdBy = 'system'; + $builtinStage->createdDate = helper::now(); + $builtinStage->name = in_array($workflowGroup->projectModel, array('waterfall', 'waterfallplus')) ? zget($this->lang->stage->typeList, $stageType, '') : zget($this->lang->stage->ipdTypeList, $stageType, ''); + $builtinStage->type = $stageType; + $builtinStage->projectType = $workflowGroup->projectModel; + $this->dao->insert(TABLE_STAGE)->data($builtinStage)->exec(); + + $stageID = $this->dao->lastInsertID(); + $stageList[$stageID] = $builtinStage; + } + } + if($workflowGroup->projectModel != 'ipd') return; + + $builtinPoints = $this->dao->select('*')->from(TABLE_DECISION)->where('workflowGroup')->eq($groupID)->andWhere('builtin')->eq('1')->fetchAll(); + if(!empty($builtinPoints)) return; + + $decision = new stdClass(); + $decision->builtin = '1'; + $decision->createdBy = 'system'; + $decision->createdDate = helper::now(); + $decision->workflowGroup = $groupID; + + $decisionFlow = new stdClass(); + $decisionFlow->flow = 1; + $decisionFlow->objectType = 'decision'; + $decisionFlow->relatedBy = 'system'; + $decisionFlow->relatedDate = helper::now(); + foreach($stageList as $id => $stage) + { + foreach($this->config->stage->ipdReviewPoint->{$stage->type} as $index => $point) + { + $decision->stage = $id; + $decision->order = $index + 1; + $decision->title = $point; + $decision->type = strpos($point, 'TR') !== false ? 'TR' : 'DCP'; + $decision->category = $point; + $this->dao->insert(TABLE_DECISION)->data($decision)->exec(); + $decisionID = $this->dao->lastInsertID(); + + $decisionFlow->root = $stage->workflowGroup; + $decisionFlow->objectID = $decisionID; + $this->dao->insert(TABLE_APPROVALFLOWOBJECT)->data($decisionFlow)->exec(); + } + } + } + + /** + * 获取阶段的评审点。 + * Get points of stage. + * + * @param string $type TR|DCP + * @param int $stageID + * @access public + * @return bool + */ + public function getStagePoints(string $type, int $stageID) + { + $stage = $this->dao->select('*')->from(TABLE_STAGE)->where('id')->eq($stageID)->fetch(); + return $this->dao->select('t1.*,t2.flow')->from(TABLE_DECISION)->alias('t1') + ->leftJoin(TABLE_APPROVALFLOWOBJECT)->alias('t2')->on("t1.id = t2.objectID AND t2.objectType = 'decision'") + ->where('t1.deleted')->eq('0') + ->andWhere('t1.stage')->eq($stageID) + ->andWhere('t1.type')->eq($type) + ->andWhere('t2.root')->eq($stage->workflowGroup) + ->orderBy('order_asc') + ->fetchAll('id'); + } + + /** + * 设置阶段的评审点。 + * Set point of stage. + * + * @param string $type TR|DCP + * @param int $stageID + * @param array $stageID + * @access public + * @return bool + */ + public function setPoint(string $type, int $stageID, array $points): bool + { + $stage = $this->getByID($stageID); + $oldStagePoints = $this->getStagePoints($type, $stageID); + + foreach($points as $index => $point) + { + if(!empty($point->id) && isset($oldStagePoints[$point->id])) + { + $oldPoint = $oldStagePoints[$point->id]; + if($oldPoint->title != $point->title || $oldPoint->order != $index) + { + $this->dao->update(TABLE_DECISION) + ->set('title')->eq($point->title) + ->set('order')->eq($index) + ->set('editedBy')->eq($this->app->user->account) + ->set('editedDate')->eq(helper::now()) + ->where('id')->eq($point->id) + ->exec(); + if(dao::isError()) return false; + } + if($oldPoint->flow != $point->flow) + { + $this->dao->update(TABLE_APPROVALFLOWOBJECT) + ->set('flow')->eq($point->flow) + ->where('objectID')->eq($point->id) + ->andWhere('objectType')->eq('decision') + ->andWhere('root')->eq($stage->workflowGroup) + ->exec(); + if(dao::isError()) return false; + } + unset($oldStagePoints[$point->id]); + } + else + { + $newPoint = new stdClass(); + $newPoint->workflowGroup = $stage->workflowGroup; + $newPoint->stage = $stageID; + $newPoint->title = $point->title; + $newPoint->type = $type; + $newPoint->builtin = '0'; + $newPoint->createdBy = $this->app->user->account; + $newPoint->createdDate = helper::now(); + $newPoint->order = $index; + $this->dao->insert(TABLE_DECISION)->data($newPoint)->exec(); + $newPointID = $this->dao->lastInsertID(); + if(dao::isError()) return false; + + $approvalFlowObject = new stdClass(); + $approvalFlowObject->root = $stage->workflowGroup; + $approvalFlowObject->flow = $point->flow; + $approvalFlowObject->objectType = 'decision'; + $approvalFlowObject->objectID = $newPointID; + $approvalFlowObject->relatedBy = $this->app->user->account; + $approvalFlowObject->relatedDate = helper::now(); + $approvalFlowObject->extra = $type; + $this->dao->insert(TABLE_APPROVALFLOWOBJECT)->data($approvalFlowObject)->exec(); + if(dao::isError()) return false; + } + } + + if(!empty($oldStagePoints)) + { + $this->dao->update(TABLE_DECISION)->set('deleted')->eq('1')->where('id')->in(array_keys($oldStagePoints))->exec(); + $approvalFlowObjectIDList = $this->dao->select('id')->from(TABLE_APPROVALFLOWOBJECT) + ->where('objectID')->in(array_keys($oldStagePoints)) + ->andWhere('objectType')->eq('decision') + ->andWhere('root')->eq($stage->workflowGroup) + ->fetchPairs(); + + $this->dao->delete()->from(TABLE_APPROVALFLOWOBJECT)->where('id')->in($approvalFlowObjectIDList)->exec(); // 删除评审流程 + $this->dao->delete()->from(TABLE_REVIEWCL)->where('object')->in($approvalFlowObjectIDList)->exec(); // 删除检查清单 + } + return true; + } + + /** + * 更新排序。 + * Update order. + * + * @param array $sortedIdList * @access public * @return void */ - public function setMenu(string $type): void + public function updateOrder(array $sortedIdList) { - $this->app->loadLang('admin'); - $moduleName = $this->app->rawModule; - $methodName = $this->app->rawMethod; - - if(!isset($this->lang->admin->menuList->model['subMenu']['waterfall']['exclude'])) $this->lang->admin->menuList->model['subMenu']['waterfall']['exclude'] = ''; - if(!isset($this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'])) $this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'] = ''; - if($type == 'waterfall') + foreach($sortedIdList as $stageID => $order) { - $this->lang->admin->menuList->model['subMenu']['waterfallplus']['exclude'] .= ",{$moduleName}-{$methodName}"; - unset($this->lang->admin->menuList->model['subMenu']['scrum']['subModule']); - unset($this->lang->admin->menuList->model['subMenu']['scrumplus']['subModule']); - unset($this->lang->admin->menuList->model['subMenu']['waterfallplus']['subModule']); - } - if($type == 'waterfallplus') - { - $this->lang->admin->menuList->model['subMenu']['waterfall']['exclude'] .= ",{$moduleName}-{$methodName}"; - unset($this->lang->admin->menuList->model['subMenu']['scrum']['subModule']); - unset($this->lang->admin->menuList->model['subMenu']['waterfall']['subModule']); - unset($this->lang->admin->menuList->model['subMenu']['scrumplus']['subModule']); + $this->dao->update(TABLE_STAGE)->set('order')->eq($order + 1)->where('id')->eq($stageID)->exec(); } } } diff --git a/module/stage/test/lib/stage.unittest.class.php b/module/stage/test/lib/stage.unittest.class.php index 8fcdae474e..99f9f7869a 100644 --- a/module/stage/test/lib/stage.unittest.class.php +++ b/module/stage/test/lib/stage.unittest.class.php @@ -27,7 +27,9 @@ class stageTest { $this->objectModel->config->setPercent = 1; $this->objectModel->config->stage->create->requiredFields = 'type,name,percent'; - $stageID = $this->objectModel->create($stage, $type); + if($type == 'waterfall') $stage->workflowGroup = 4; + if($type == 'waterfallplus') $stage->workflowGroup = 8; + $stageID = $this->objectModel->create($stage); if(dao::isError()) return dao::getError(); return $this->objectModel->dao->select('*')->from(TABLE_STAGE)->where('id')->eq($stageID)->fetch(); @@ -38,11 +40,11 @@ class stageTest * Batch create stages. * * @param array $data - * @param string $type waterfall|waterfallplus + * @param int $groupID * @access public * @return int|string|array */ - public function batchCreateTest(array $data, string $type = 'waterfall'): int|string|array + public function batchCreateTest(array $data, int $groupID = 0): int|string|array { $this->objectModel->config->setPercent = 1; $this->objectModel->config->stage->create->requiredFields = 'type,name,percent'; @@ -58,10 +60,10 @@ class stageTest $stages[] = $stage; } - $this->objectModel->batchCreate($type, $stages); + $this->objectModel->batchCreate($groupID, $stages); if(dao::isError()) return current(dao::getError()); - $objects = $this->objectModel->getStages('id_desc', 0, $type); + $objects = $this->objectModel->getStages('id_desc', 0, $groupID); return count($objects); } @@ -133,13 +135,13 @@ class stageTest * 获取给定模型下的阶段总百分比。 * Get total percent of the type. * - * @param string $projectType waterfall|waterfallplus + * @param int $groupID * @access public * @return int|array */ - public function getTotalPercentTest(string $projectType): int|array + public function getTotalPercentTest(int $groupID): int|array { - $totalPercent = $this->objectModel->getTotalPercent($projectType); + $totalPercent = $this->objectModel->getTotalPercent($groupID); if(dao::isError()) return dao::getError(); return $totalPercent; diff --git a/module/stage/test/model/addbuiltinpoint.php b/module/stage/test/model/addbuiltinpoint.php new file mode 100755 index 0000000000..3711d530f0 --- /dev/null +++ b/module/stage/test/model/addbuiltinpoint.php @@ -0,0 +1,80 @@ +#!/usr/bin/env php +addBuiltinPoint(); +cid=1 + +- 获取TR1评审点 + - 第1条的title属性 @TR1 + - 第1条的type属性 @TR + - 第1条的category属性 @TR1 +- 获取CDCP评审点 + - 第2条的title属性 @CDCP + - 第2条的type属性 @DCP + - 第2条的category属性 @CDCP +- 获取TR2评审点 + - 第3条的title属性 @TR2 + - 第3条的type属性 @TR + - 第3条的category属性 @TR2 +- 获取TR3评审点 + - 第4条的title属性 @TR3 + - 第4条的type属性 @TR + - 第4条的category属性 @TR3 +- 获取PDCP评审点 + - 第5条的title属性 @PDCP + - 第5条的type属性 @DCP + - 第5条的category属性 @PDCP +- 获取TR4评审点 + - 第6条的title属性 @TR4 + - 第6条的type属性 @TR + - 第6条的category属性 @TR4 +- 获取TR4A评审点 + - 第7条的title属性 @TR4A + - 第7条的type属性 @TR + - 第7条的category属性 @TR4A +- 获取TR5评审点 + - 第8条的title属性 @TR5 + - 第8条的type属性 @TR + - 第8条的category属性 @TR5 +- 获取TR6评审点 + - 第9条的title属性 @TR6 + - 第9条的type属性 @TR + - 第9条的category属性 @TR6 +- 获取ADCP评审点 + - 第10条的title属性 @ADCP + - 第10条的type属性 @DCP + - 第10条的category属性 @ADCP + +*/ + +include dirname(__FILE__, 5) . '/test/lib/init.php'; + +zenData('user')->gen(5); +zenData('decision')->gen(0); + +$workflowGroup = zenData('workflowgroup'); +$workflowGroup->id->range('1-5'); +$workflowGroup->type->range('project'); +$workflowGroup->projectModel->range('ipd'); +$workflowGroup->projectType->range('ipd,tpd,cbb,cpdproduct,cpdproject'); +$workflowGroup->name->range('IPD集成产品研发,IPD预研产品研发,IPD平台产品研发,IPD定制产品研发,IPD定制项目研发'); +$workflowGroup->code->range('ipdproduct,tpdproduct,cbbproduct,cpdproduct,cpdproject'); +$workflowGroup->status->range('normal'); +$workflowGroup->main->range('1'); +$workflowGroup->gen(5); + +global $tester; +$stageTester = $tester->loadModel('stage'); +$stageTester->addBuiltinPoint(1); +$IPDpoint = $stageTester->dao->select('*')->from(TABLE_DECISION)->where('workflowGroup')->eq(1)->fetchAll('id'); +r($IPDpoint) && p('1:title,type,category') && e('TR1,TR,TR1'); //获取TR1评审点 +r($IPDpoint) && p('2:title,type,category') && e('CDCP,DCP,CDCP'); //获取CDCP评审点 +r($IPDpoint) && p('3:title,type,category') && e('TR2,TR,TR2'); //获取TR2评审点 +r($IPDpoint) && p('4:title,type,category') && e('TR3,TR,TR3'); //获取TR3评审点 +r($IPDpoint) && p('5:title,type,category') && e('PDCP,DCP,PDCP'); //获取PDCP评审点 +r($IPDpoint) && p('6:title,type,category') && e('TR4,TR,TR4'); //获取TR4评审点 +r($IPDpoint) && p('7:title,type,category') && e('TR4A,TR,TR4A'); //获取TR4A评审点 +r($IPDpoint) && p('8:title,type,category') && e('TR5,TR,TR5'); //获取TR5评审点 +r($IPDpoint) && p('9:title,type,category') && e('TR6,TR,TR6'); //获取TR6评审点 +r($IPDpoint) && p('10:title,type,category') && e('ADCP,DCP,ADCP'); //获取ADCP评审点 diff --git a/module/stage/test/model/batchcreate.php b/module/stage/test/model/batchcreate.php index 747ca11d49..91a92298d1 100755 --- a/module/stage/test/model/batchcreate.php +++ b/module/stage/test/model/batchcreate.php @@ -42,7 +42,7 @@ $name3 = array(1 => '批量创建的发布', 2 => '批量创建的总结评 $percent3 = array(1 => 5, 2 => 6); $type3 = array(1 => 'release', 2 => 'review'); -$name4 = array(1 => '批量创建的需求', 2 => '批量创建的其他'); +$name4 = array(1 => '批量创建的验证', 2 => '批量创建的其他'); $percent4 = array(1 => 1, 2 => 7); $type4 = array(1 => 'request', 2 => 'other'); @@ -68,6 +68,8 @@ r($stageTester->batchCreateTest($param2)) && p() && e('4'); // 测试批量创 r($stageTester->batchCreateTest($param3)) && p() && e('6'); // 测试批量创建瀑布项目的阶段 3 r($stageTester->batchCreateTest($param4)) && p() && e('8'); // 测试批量创建瀑布项目的阶段 4 +zenData('stage')->gen(0); + /* Error condition. */ r($stageTester->batchCreateTest($param5)) && p('0') && e('『工作量占比』不能为空。'); // 测试批量创建瀑布项目的百分比为空的阶段 r($stageTester->batchCreateTest($param6)) && p('0') && e('『阶段类型』不能为空。'); // 测试批量创建瀑布项目的类型为空的阶段 @@ -75,13 +77,13 @@ r($stageTester->batchCreateTest($param7)) && p('0') && e('『工作量占比』 r($stageTester->batchCreateTest($param8)) && p('0') && e('工作量占比累计不应当超过100%'); // 测试批量创建瀑布项目的百分比超出100的阶段 /* Normal condition. */ -r($stageTester->batchCreateTest($param1, 'waterfallplus')) && p() && e('2'); // 测试批量创建融合瀑布项目的阶段 1 -r($stageTester->batchCreateTest($param2, 'waterfallplus')) && p() && e('4'); // 测试批量创建融合瀑布项目的阶段 2 -r($stageTester->batchCreateTest($param3, 'waterfallplus')) && p() && e('6'); // 测试批量创建融合瀑布项目的阶段 3 -r($stageTester->batchCreateTest($param4, 'waterfallplus')) && p() && e('8'); // 测试批量创建融合瀑布项目的阶段 4 +r($stageTester->batchCreateTest($param1, 8)) && p() && e('2'); // 测试批量创建融合瀑布项目的阶段 1 +r($stageTester->batchCreateTest($param2, 8)) && p() && e('4'); // 测试批量创建融合瀑布项目的阶段 2 +r($stageTester->batchCreateTest($param3, 8)) && p() && e('6'); // 测试批量创建融合瀑布项目的阶段 3 +r($stageTester->batchCreateTest($param4, 8)) && p() && e('8'); // 测试批量创建融合瀑布项目的阶段 4 /* Error condition. */ -r($stageTester->batchCreateTest($param5, 'waterfallplus')) && p('0') && e('『工作量占比』不能为空。'); // 测试批量创建融合瀑布项目的百分比为空的阶段 -r($stageTester->batchCreateTest($param6, 'waterfallplus')) && p('0') && e('『阶段类型』不能为空。'); // 测试批量创建融合瀑布项目的类型为空的阶段 -r($stageTester->batchCreateTest($param7, 'waterfallplus')) && p('0') && e('『工作量占比』不能为空。'); // 测试批量创建融合瀑布项目的百分比非法的阶段 -r($stageTester->batchCreateTest($param8, 'waterfallplus')) && p('0') && e('工作量占比累计不应当超过100%'); // 测试批量创建融合瀑布项目的百分比超出100的阶段 +r($stageTester->batchCreateTest($param5, 9)) && p('0') && e('『工作量占比』不能为空。'); // 测试批量创建融合瀑布项目的百分比为空的阶段 +r($stageTester->batchCreateTest($param6, 9)) && p('0') && e('『阶段类型』不能为空。'); // 测试批量创建融合瀑布项目的类型为空的阶段 +r($stageTester->batchCreateTest($param7, 9)) && p('0') && e('『工作量占比』不能为空。'); // 测试批量创建融合瀑布项目的百分比非法的阶段 +r($stageTester->batchCreateTest($param8, 9)) && p('0') && e('工作量占比累计不应当超过100%'); // 测试批量创建融合瀑布项目的百分比超出100的阶段 diff --git a/module/stage/test/model/getstagepoints.php b/module/stage/test/model/getstagepoints.php new file mode 100755 index 0000000000..1a6f9a4d70 --- /dev/null +++ b/module/stage/test/model/getstagepoints.php @@ -0,0 +1,56 @@ +#!/usr/bin/env php +getStagePoints(); +timeout=0 +cid=1 + +- 获取阶段1的TR评审点 + - 第1条的stage属性 @1 + - 第1条的title属性 @TR1 +- 获取阶段2的TR评审点 + - 第2条的stage属性 @2 + - 第2条的title属性 @TR2 +- 获取阶段2的TR评审点 + - 第3条的stage属性 @3 + - 第3条的title属性 @TR3 +- 获取阶段4的DCP评审点 + - 第4条的stage属性 @4 + - 第4条的title属性 @CDCP +- 获取阶段5的DCP评审点 + - 第5条的stage属性 @5 + - 第5条的title属性 @PDCP + +*/ + +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/stage.unittest.class.php'; + +zenData('stage')->loadYaml('stage')->gen(5); +zenData('user')->gen(5); + +$decision = zenData('decision'); +$decision->id->range('1-5'); +$decision->workflowGroup->range('1-5'); +$decision->stage->range('1-5'); +$decision->title->range('TR1,TR2,TR3,CDCP,PDCP'); +$decision->type->range('TR{3},DCP{2}'); +$decision->gen(5); + +$approvalFlowObject = zenData('approvalflowobject'); +$approvalFlowObject->id->range('1-5'); +$approvalFlowObject->root->range('4'); +$approvalFlowObject->objectID->range('1-5'); +$approvalFlowObject->objectType->range('decision'); +$approvalFlowObject->extra->range('review'); +$approvalFlowObject->gen(5); + +global $tester; +$stageTester = $tester->loadModel('stage'); +r($stageTester->getStagePoints('TR', 1)) && p('1:stage,title') && e('1,TR1'); //获取阶段1的TR评审点 +r($stageTester->getStagePoints('TR', 2)) && p('2:stage,title') && e('2,TR2'); //获取阶段2的TR评审点 +r($stageTester->getStagePoints('TR', 3)) && p('3:stage,title') && e('3,TR3'); //获取阶段2的TR评审点 +r($stageTester->getStagePoints('DCP', 4)) && p('4:stage,title') && e('4,CDCP'); //获取阶段4的DCP评审点 +r($stageTester->getStagePoints('DCP', 5)) && p('5:stage,title') && e('5,PDCP'); //获取阶段5的DCP评审点 diff --git a/module/stage/test/model/getstages.php b/module/stage/test/model/getstages.php index db389b582f..d4279033dc 100755 --- a/module/stage/test/model/getstages.php +++ b/module/stage/test/model/getstages.php @@ -24,21 +24,11 @@ cid=18421 - 获取敏捷模型下按照id倒序排列的所有阶段 @0 - 获取瀑布模型下按照id正序排列的所有阶段 @0 - 获取瀑布模型下按照id倒序排列的所有阶段 - - 第1条的projectType属性 @waterfall - 第1条的type属性 @request - 第1条的name属性 @需求1 - 获取瀑布模型下按照id正序排列的所有阶段 - - 第1条的projectType属性 @waterfall - 第1条的type属性 @request - 第1条的name属性 @需求1 -- 获取融合瀑布模型下按照id倒序排列的所有阶段 - - 第7条的projectType属性 @waterfallplus - - 第7条的type属性 @request - - 第7条的name属性 @需求2 -- 获取融合瀑布模型下按照id正序排列的所有阶段 - - 第7条的projectType属性 @waterfallplus - - 第7条的type属性 @request - - 第7条的name属性 @需求2 */ include dirname(__FILE__, 5) . '/test/lib/init.php'; @@ -50,20 +40,18 @@ zenData('stage')->loadYaml('stage')->gen(12); $sorts = array('id_desc', 'id_asc'); $projectIds = array(0, 11, 60, 100); -$types = array('scrum', 'waterfall', 'waterfallplus'); +$types = array(2, 4); $stageTester = new stageTest(); -r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[0])) && p() && e('0'); // 获取敏捷模型下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[0])) && p() && e('0'); // 获取瀑布模型下按照id正序排列的所有阶段 -r($stageTester->getStagesTest($sorts[0], $projectIds[1], $types[0])) && p('101:type,name') && e('sprint,迭代5'); // 获取敏捷项目下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[1], $types[0])) && p('105:type,name') && e('sprint,迭代9'); // 获取敏捷项目下按照id正序排列的所有阶段 -r($stageTester->getStagesTest($sorts[0], $projectIds[2], $types[0])) && p('106:type,name') && e('stage,阶段10'); // 获取瀑布项目下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[2], $types[0])) && p('106:type,name') && e('stage,阶段10'); // 获取瀑布项目下按照id正序排列的所有阶段 -r($stageTester->getStagesTest($sorts[0], $projectIds[3], $types[0])) && p() && e('0'); // 获取看板项目下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[3], $types[0])) && p() && e('0'); // 获取看板项目下按照id正序排列的所有阶段 -r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[0])) && p() && e('0'); // 获取敏捷模型下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[0])) && p() && e('0'); // 获取瀑布模型下按照id正序排列的所有阶段 -r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[1])) && p('1:projectType,type,name') && e('waterfall,request,需求1'); // 获取瀑布模型下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[1])) && p('1:projectType,type,name') && e('waterfall,request,需求1'); // 获取瀑布模型下按照id正序排列的所有阶段 -r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[2])) && p('7:projectType,type,name') && e('waterfallplus,request,需求2'); // 获取融合瀑布模型下按照id倒序排列的所有阶段 -r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[2])) && p('7:projectType,type,name') && e('waterfallplus,request,需求2'); // 获取融合瀑布模型下按照id正序排列的所有阶段 +r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[0])) && p() && e('0'); // 获取敏捷模型下按照id倒序排列的所有阶段 +r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[0])) && p() && e('0'); // 获取瀑布模型下按照id正序排列的所有阶段 +r($stageTester->getStagesTest($sorts[0], $projectIds[1], $types[0])) && p('101:type,name') && e('sprint,迭代5'); // 获取敏捷项目下按照id倒序排列的所有阶段 +r($stageTester->getStagesTest($sorts[1], $projectIds[1], $types[0])) && p('105:type,name') && e('sprint,迭代9'); // 获取敏捷项目下按照id正序排列的所有阶段 +r($stageTester->getStagesTest($sorts[0], $projectIds[2], $types[0])) && p('106:type,name') && e('stage,阶段10'); // 获取瀑布项目下按照id倒序排列的所有阶段 +r($stageTester->getStagesTest($sorts[1], $projectIds[2], $types[0])) && p('106:type,name') && e('stage,阶段10'); // 获取瀑布项目下按照id正序排列的所有阶段 +r($stageTester->getStagesTest($sorts[0], $projectIds[3], $types[0])) && p() && e('0'); // 获取看板项目下按照id倒序排列的所有阶段 +r($stageTester->getStagesTest($sorts[1], $projectIds[3], $types[0])) && p() && e('0'); // 获取看板项目下按照id正序排列的所有阶段 +r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[0])) && p() && e('0'); // 获取敏捷模型下按照id倒序排列的所有阶段 +r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[0])) && p() && e('0'); // 获取瀑布模型下按照id正序排列的所有阶段 +r($stageTester->getStagesTest($sorts[0], $projectIds[0], $types[1])) && p('1:type,name') && e('request,需求1'); // 获取瀑布模型下按照id倒序排列的所有阶段 +r($stageTester->getStagesTest($sorts[1], $projectIds[0], $types[1])) && p('1:type,name') && e('request,需求1'); // 获取瀑布模型下按照id正序排列的所有阶段 diff --git a/module/stage/test/model/gettotalpercent.php b/module/stage/test/model/gettotalpercent.php index 6bc2e37c97..d88148244c 100755 --- a/module/stage/test/model/gettotalpercent.php +++ b/module/stage/test/model/gettotalpercent.php @@ -12,8 +12,7 @@ cid=18422 - 测试获取有stage数据的waterfallplus类型总百分比 @60 - 测试获取不存在的项目类型总百分比 @0 - 测试获取scrum类型的总百分比 @0 -- 测试获取空字符串类型的总百分比 @0 -- 测试已删除stage的百分比不被计算 @60 +- 测试已删除stage的百分比不被计算 @0 */ @@ -24,22 +23,19 @@ $stageTester = new stageTest(); // 步骤1:测试空数据库情况 zenData('stage')->gen(0); -r($stageTester->getTotalPercentTest('waterfall')) && p() && e('0'); // 测试空数据库中waterfall类型的总百分比 +r($stageTester->getTotalPercentTest(4)) && p() && e('0'); // 测试空数据库中waterfall类型的总百分比 // 步骤2和3:准备测试数据,测试有数据情况 zenData('user')->gen(5); zenData('stage')->loadYaml('stage')->gen(12); -r($stageTester->getTotalPercentTest('waterfall')) && p() && e('60'); // 测试获取有stage数据的waterfall类型总百分比 -r($stageTester->getTotalPercentTest('waterfallplus')) && p() && e('60'); // 测试获取有stage数据的waterfallplus类型总百分比 +r($stageTester->getTotalPercentTest(4)) && p() && e('60'); // 测试获取有stage数据的waterfall类型总百分比 +r($stageTester->getTotalPercentTest(8)) && p() && e('60'); // 测试获取有stage数据的waterfallplus类型总百分比 // 步骤4:测试不存在的项目类型 -r($stageTester->getTotalPercentTest('nonexistent')) && p() && e('0'); // 测试获取不存在的项目类型总百分比 +r($stageTester->getTotalPercentTest(0)) && p() && e('0'); // 测试获取不存在的项目类型总百分比 // 步骤5:测试scrum类型(应该没有数据) -r($stageTester->getTotalPercentTest('scrum')) && p() && e('0'); // 测试获取scrum类型的总百分比 - -// 步骤6:测试空字符串类型 -r($stageTester->getTotalPercentTest('')) && p() && e('0'); // 测试获取空字符串类型的总百分比 +r($stageTester->getTotalPercentTest(2)) && p() && e('0'); // 测试获取scrum类型的总百分比 // 步骤7:测试已删除的stage不被计算(需要准备包含已删除数据的测试数据) $table = zenData('stage'); @@ -50,4 +46,4 @@ $table->type->range('request,design'); $table->percent->range('15,25'); $table->deleted->range('1{2}'); $table->gen(2); -r($stageTester->getTotalPercentTest('waterfall')) && p() && e('60'); // 测试已删除stage的百分比不被计算 \ No newline at end of file +r($stageTester->getTotalPercentTest(2)) && p() && e('0'); // 测试已删除stage的百分比不被计算 diff --git a/module/stage/test/model/setpoint.php b/module/stage/test/model/setpoint.php new file mode 100755 index 0000000000..be089f3a79 --- /dev/null +++ b/module/stage/test/model/setpoint.php @@ -0,0 +1,77 @@ +#!/usr/bin/env php +setPoint(); +cid=1 + +- 测试修改评审点标题 @TR1 change +- 测试修改评审点的审批流 @2 +- 测试修改评审点的顺序 @2 +- 测试删除评审点 @1 +- 测试添加评审点第6条的title属性 @test add point + +*/ + +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/stage.unittest.class.php'; + +zenData('stage')->loadYaml('stage')->gen(5); +zenData('user')->gen(5); + +$decision = zenData('decision'); +$decision->id->range('1-5'); +$decision->workflowGroup->range('1-5'); +$decision->stage->range('1-5'); +$decision->order->range('1'); +$decision->title->range('TR1,TR2,TR3,CDCP,PDCP'); +$decision->type->range('TR{3},DCP{2}'); +$decision->gen(5); + +$approvalFlowObject = zenData('approvalflowobject'); +$approvalFlowObject->id->range('1-5'); +$approvalFlowObject->root->range('4'); +$approvalFlowObject->flow->range('1'); +$approvalFlowObject->objectID->range('1-5'); +$approvalFlowObject->objectType->range('decision'); +$approvalFlowObject->extra->range('review'); +$approvalFlowObject->gen(5); + +global $tester; +$stageTester = $tester->loadModel('stage'); + +$changeTitle = new stdClass(); +$changeTitle->id = 1; +$changeTitle->title = 'TR1 change'; +$changeTitle->flow = '1'; +$stageTester->setPoint('TR', 1, array(1 => $changeTitle)); +$result = $stageTester->dao->select('title')->from(TABLE_DECISION)->where('id')->eq(1)->fetch('title'); +r($result) && p() && e('TR1 change'); //测试修改评审点标题 + +$changeFlow = new stdClass(); +$changeFlow->id = 2; +$changeFlow->title = 'TR2'; +$changeFlow->flow = 2; +$stageTester->setPoint('TR', 2, array(1 => $changeFlow)); +$result = $stageTester->dao->select('flow')->from(TABLE_APPROVALFLOWOBJECT)->where('objectType')->eq('decision')->andWhere('objectID')->eq(2)->fetch('flow'); +r($result) && p() && e('2'); //测试修改评审点的审批流 + +$changeOrder = new stdClass(); +$changeOrder->id = 3; +$changeOrder->title = 'TR3'; +$changeOrder->flow = 1; +$stageTester->setPoint('TR', 3, array(2 => $changeOrder)); +$result = $stageTester->dao->select('`order`')->from(TABLE_DECISION)->where('id')->eq(3)->fetch('order'); +r($result) && p() && e('2'); //测试修改评审点的顺序 + +$stageTester->setPoint('DCP', 4, array()); +$result = $stageTester->dao->select('*')->from(TABLE_DECISION)->where('stage')->eq(4)->fetchAll('id'); +r($result) && p('4:deleted') && e('1'); //测试删除评审点 + +$addPoint = new stdClass(); +$addPoint->id = 0; +$addPoint->title = 'test add point'; +$addPoint->flow = 1; +$stageTester->setPoint('DCP', 4, array(1 => $addPoint)); +$result = $stageTester->dao->select('*')->from(TABLE_DECISION)->where('stage')->eq(4)->fetchAll('id'); +r($result) && p('6:title') && e('test add point'); //测试添加评审点 diff --git a/module/stage/test/yaml/stage.yaml b/module/stage/test/yaml/stage.yaml index 40b6c7f326..a4c8ed2eb5 100644 --- a/module/stage/test/yaml/stage.yaml +++ b/module/stage/test/yaml/stage.yaml @@ -10,8 +10,8 @@ fields: range: '需求,设计,开发,测试,发布,总结评审' - field: name2 range: '1-100{6}' - - field: projectType - range: 'waterfall{6},waterfallplus{6}' + - field: workflowGroup + range: '4{6},8{6},12{6},13{6}' - field: type range: "request,design,dev,qa,release,review" - field: percent diff --git a/module/stage/ui/batchcreate.html.php b/module/stage/ui/batchcreate.html.php index d05d2a4531..ecdefb9b7c 100644 --- a/module/stage/ui/batchcreate.html.php +++ b/module/stage/ui/batchcreate.html.php @@ -11,7 +11,7 @@ declare(strict_types=1); namespace zin; $percentItem = ''; -if(isset($config->setPercent) and $config->setPercent == 1) +if(isset($config->setPercent) && $config->setPercent == 1 && isset($flow->projectModel) && $flow->projectModel != 'ipd' || $config->edition == 'open') { $percentItem = formBatchItem( set::name('percent'), @@ -21,6 +21,7 @@ if(isset($config->setPercent) and $config->setPercent == 1) } formBatchPanel ( + set::title($title), formBatchItem ( set::name('id'), @@ -39,8 +40,8 @@ formBatchPanel set::name('type'), set::label($lang->stage->type), set::width('150px'), - set::control('select'), - set::items($lang->stage->typeList) + set::control('picker'), + set::items(isset($flow->projectModel) && $flow->projectModel == 'ipd' ? $lang->stage->ipdTypeList : $lang->stage->typeList) ) ); /* ====== Render page ====== */ diff --git a/module/stage/ui/browse.html.php b/module/stage/ui/browse.html.php index 6aae50d233..bef5bd2f2f 100644 --- a/module/stage/ui/browse.html.php +++ b/module/stage/ui/browse.html.php @@ -10,30 +10,53 @@ declare(strict_types=1); */ namespace zin; -/* zin: Define the set::module('release') feature bar on main menu. */ -/* zin: Define the toolbar on main menu. */ +featureBar(); + $canCreateStage = hasPriv('stage', 'create'); $canbatchCreateStage = hasPriv('stage', 'batchCreate'); -if($canCreateStage) $createItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->stage->create, 'url' => $this->createLink('stage', 'create', "type={$type}"), 'data-toggle' => 'modal'); -if($canbatchCreateStage) $batchCreateItem = array('icon' => 'plus', 'class' => 'primary mr-4', 'text' => $lang->stage->batchCreate, 'url' => $this->createLink('stage', 'batchCreate', "type={$type}")); -div +if($canCreateStage) $createItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->stage->create, 'url' => $this->createLink('stage', 'create', "groupID={$groupID}"), 'data-toggle' => 'modal'); +if($canbatchCreateStage) $batchCreateItem = array('icon' => 'plus', 'class' => 'primary', 'text' => $lang->stage->batchCreate, 'url' => $this->createLink('stage', 'batchCreate', "groupID={$groupID}")); +toolbar ( - setClass('main-col main-content'), - div + !empty($batchCreateItem) ? item(set($batchCreateItem)) : null, + !empty($createItem) ? item(set($createItem)) : null +); + +if($this->config->edition == 'open') +{ + if(hasPriv('stage', 'settype')) + { + $menuItems[] = li + ( + setClass('menu-item'), + a + ( + set::href(createLink('stage', 'settype')), + $lang->stage->setType + ) + ); + } + + $menuItems[] = li + ( + setClass('menu-item'), + a + ( + setClass('active'), + set::href(createLink('stage', 'browse')), + $lang->stage->browseAB + ) + ); + + sidebar ( - setClass('main-header flex-auto'), div ( - setClass('flex-auto'), - html('' . $lang->stage->browse . '') - ), - toolbar - ( - !empty($batchCreateItem) ? item(set($batchCreateItem)) : null, - !empty($createItem) ? item(set($createItem)) : null + setClass('cell p-2.5 bg-white'), + menu($menuItems) ) - ) -); + ); +} $tableData = initTableData($stages, $config->stage->dtable->fieldList, $this->stage); dtable @@ -41,8 +64,9 @@ dtable set::cols($config->stage->dtable->fieldList), set::data($tableData), set::orderBy($orderBy), - set::sortLink(createLink('stage', 'browse', "orderBy={name}_{sortType}&type={$type}")) + set::sortLink(createLink('stage', 'browse', "groupID={$groupID}&orderBy={name}_{sortType}")), + set::plugins(array('sortable')), + set::sortHandler('.move-stage'), + set::onSortEnd(jsRaw('window.onSortEnd')), + set::onRenderCell(jsRaw('window.renderCell')) ); - -/* ====== Render page ====== */ -render(); diff --git a/module/stage/ui/create.html.php b/module/stage/ui/create.html.php index 50ac5ffeec..32531d7653 100644 --- a/module/stage/ui/create.html.php +++ b/module/stage/ui/create.html.php @@ -10,7 +10,7 @@ declare(strict_types=1); */ namespace zin; $percentRow = ''; -if(isset($config->setPercent) && $config->setPercent == 1) +if(isset($config->setPercent) && $config->setPercent == 1 && isset($flow->projectModel) && $flow->projectModel != 'ipd' || $config->edition == 'open') { $percentRow = formRow( formGroup @@ -53,7 +53,7 @@ formPanel set::width('1/2'), set::label($lang->stage->type), set::name('type'), - set::items($lang->stage->typeList) + set::items(isset($flow->projectModel) && $flow->projectModel == 'ipd' ? $lang->stage->ipdTypeList : $lang->stage->typeList) ) ) ); diff --git a/module/stage/ui/edit.html.php b/module/stage/ui/edit.html.php index d8ee624bd2..3e718a4a4a 100644 --- a/module/stage/ui/edit.html.php +++ b/module/stage/ui/edit.html.php @@ -10,7 +10,7 @@ declare(strict_types=1); */ namespace zin; $percentRow = ''; -if(isset($config->setPercent) && $config->setPercent == 1) +if(isset($config->setPercent) && $config->setPercent == 1 && isset($flow->projectModel) && $flow->projectModel != 'ipd' || $config->edition == 'open') { $percentRow = formRow( formGroup @@ -59,7 +59,7 @@ formPanel set::label($lang->stage->type), set::name('type'), set::value($stage->type), - set::items($lang->stage->typeList) + set::items(isset($flow->projectModel) && $flow->projectModel == 'ipd' ? $lang->stage->ipdTypeList : $lang->stage->typeList) ) ) ); diff --git a/module/stage/ui/setpoint.html.php b/module/stage/ui/setpoint.html.php new file mode 100644 index 0000000000..b55baf28f6 --- /dev/null +++ b/module/stage/ui/setpoint.html.php @@ -0,0 +1,60 @@ + + * @package stage + * @link https://www.zentao.net + */ + +namespace zin; + +modalHeader +( + set::title($type == 'TR' ? $lang->stage->setTRpoint : $lang->stage->setDCPpoint), + set::titleClass('text-md font-semibold') +); + +formBatchPanel +( + setID('setPointForm'), + set::minRows(1), + set::sortable(true), + set::data(array_values($stagePoints)), + set::idKey('index'), + formBatchItem + ( + set::name('id'), + set::hidden(true) + ), + formBatchItem + ( + set::name('title'), + set::required(true), + set::label($type == 'TR' ? $lang->stage->TRname : $lang->stage->DCPname) + ), + formBatchItem + ( + set::name('flow'), + set::label($lang->stage->pointFlow), + set::required(true), + set::control('inputGroup'), + inputGroup + ( + picker + ( + set::name('flow'), + set::required(true), + set::items($approvals), + set::value(1) + ), + hasPriv('approvalflow', 'create') ? div + ( + a(set::className('btn secondary ml-1'), set::href(createLink('approvalflow', 'create', 'workflow=&callback=refreshApproval')), setData(array('toggle' => 'modal')), $lang->review->createApproval) + ) : null + ) + ) +); diff --git a/module/stage/ui/settype.html.php b/module/stage/ui/settype.html.php index 1723b7ed5a..0145feda5a 100644 --- a/module/stage/ui/settype.html.php +++ b/module/stage/ui/settype.html.php @@ -37,7 +37,7 @@ div set::label('addRow'), set::name('values[]') ), - div + div ( setClass('pl-2 flex self-center'), btn @@ -62,7 +62,7 @@ foreach($fieldList as $key => $value) $formItems[] = formRow ( - input + input ( set::type('hidden'), set::name('keys[]'), @@ -118,13 +118,45 @@ $formItems[] = formGroup set::control('radioListInline') ); +$menuItems[] = li +( + setClass('menu-item'), + a + ( + setClass('active'), + set::href(createLink('stage', 'settype')), + $lang->stage->setType + ) +); + +if($config->edition == 'open' && hasPriv('stage', 'browse')) +{ + $menuItems[] = li + ( + setClass('menu-item'), + a + ( + set::href(createLink('stage', 'browse')), + $lang->stage->browseAB + ) + ); +} + div ( - setClass('flex'), + setClass('row has-sidebar-left'), + sidebar + ( + div + ( + setClass('cell p-2.5 bg-white'), + menu($menuItems) + ) + ), formPanel ( set::headingClass('justify-start'), - setClass('flex-auto'), + setClass('flex-auto ml-0.5'), set::actionsClass('w-1/2'), set::title($lang->stage->setType), set::actions($formActions), diff --git a/module/store/test/lib/store.unittest.class.php b/module/store/test/lib/store.unittest.class.php index 8f2cbc1ecb..94f84f6e93 100755 --- a/module/store/test/lib/store.unittest.class.php +++ b/module/store/test/lib/store.unittest.class.php @@ -46,17 +46,17 @@ class storeTest public function __constructTest(string $appName = '') { global $config, $app; - + // 创建新的store模型实例进行测试 $storeModel = new storeModel($appName); - + // 返回测试结果对象 $result = new stdClass(); $result->appName = $appName; $result->hasHeaders = !empty($config->cloud->api->headers); $result->authHeaderExists = false; $result->channelChanged = false; - + // 检查API headers是否正确设置 if($result->hasHeaders) { @@ -69,15 +69,15 @@ class storeTest } } } - + // 检查channel是否被改变 if($config->cloud->api->switchChannel && isset($app->session->cloudChannel)) { $result->channelChanged = ($config->cloud->api->channel === $app->session->cloudChannel); } - + if(dao::isError()) return dao::getError(); - + return $result; } diff --git a/module/story/lang/de.php b/module/story/lang/de.php index 209c22e0d8..ced418246c 100644 --- a/module/story/lang/de.php +++ b/module/story/lang/de.php @@ -550,6 +550,9 @@ $lang->story->categoryList['experience'] = 'Experience'; $lang->story->categoryList['improve'] = 'Improve'; $lang->story->categoryList['other'] = 'Other'; +$lang->story->frozenTip = "After the stories are baselined, %s is not allowed."; +$lang->story->frozenTips = "Story %s has been frozen and will not be %s."; + $lang->story->changeTip = 'Only active can be changed.'; $lang->story->reviewTip = array(); diff --git a/module/story/lang/en.php b/module/story/lang/en.php index f47d6235cb..7d28385f1a 100644 --- a/module/story/lang/en.php +++ b/module/story/lang/en.php @@ -550,6 +550,9 @@ $lang->story->categoryList['experience'] = 'Experience'; $lang->story->categoryList['improve'] = 'Improve'; $lang->story->categoryList['other'] = 'Other'; +$lang->story->frozenTip = "After the stories are baselined, %s is not allowed."; +$lang->story->frozenTips = "Story %s has been frozen and will not be %s."; + $lang->story->changeTip = 'Only active can be changed.'; $lang->story->reviewTip = array(); diff --git a/module/story/lang/fr.php b/module/story/lang/fr.php index 3f3a65d5d4..082e6e9489 100644 --- a/module/story/lang/fr.php +++ b/module/story/lang/fr.php @@ -550,6 +550,9 @@ $lang->story->categoryList['experience'] = 'Experience'; $lang->story->categoryList['improve'] = 'Improve'; $lang->story->categoryList['other'] = 'Other'; +$lang->story->frozenTip = "After the stories are baselined, %s is not allowed."; +$lang->story->frozenTips = "Story %s has been frozen and will not be %s."; + $lang->story->changeTip = "Seules les exigences de l'état actif peuvent être modifiées."; $lang->story->reviewTip = array(); diff --git a/module/story/lang/zh-cn.php b/module/story/lang/zh-cn.php index 56ca9a4122..35d6bd65f3 100644 --- a/module/story/lang/zh-cn.php +++ b/module/story/lang/zh-cn.php @@ -550,6 +550,9 @@ $lang->story->categoryList['experience'] = '体验'; $lang->story->categoryList['improve'] = '改进'; $lang->story->categoryList['other'] = '其他'; +$lang->story->frozenTip = "需求打基线后不允许%s"; +$lang->story->frozenTips = "需求:%s 已冻结,将不会被%s。"; + $lang->story->changeTip = "只有激活状态的需求,才能进行变更"; $lang->story->reviewTip = array(); diff --git a/module/story/model.php b/module/story/model.php index 1520e73c0b..26003056d8 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -3882,6 +3882,7 @@ class storyModel extends model global $app, $config; $action = strtolower($action); + if(in_array($action, array('edit', 'batchcreate', 'change', 'delete')) && !empty($story->frozen)) return false; if($action == 'subdivide') $action = 'batchcreate'; if($action == 'recall') return $story->status == 'reviewing' || $story->status == 'changing'; diff --git a/module/story/tao.php b/module/story/tao.php index 7330d7c427..4d5d66981d 100644 --- a/module/story/tao.php +++ b/module/story/tao.php @@ -2009,7 +2009,7 @@ class storyTao extends storyModel /* Change button. */ $canChange = common::hasPriv($story->type, 'change') && $this->isClickable($story, 'change', $taskGroups, $caseGroups) && $this->checkConditions('change', $story); $title = $canChange ? $lang->story->change : $this->lang->story->changeTip; - if(common::hasPriv($story->type, 'change')) $actions[] = array('name' => 'change', 'url' => $canChange ? $changeLink : null, 'hint' => $title, 'disabled' => !$canChange, 'class' => 'story-change-btn'); + if(common::hasPriv($story->type, 'change')) $actions[] = array('name' => 'change', 'url' => $canChange ? $changeLink : null, 'hint' => empty($story->frozen) ? $title : sprintf($this->lang->story->frozenTip, $this->lang->story->change), 'disabled' => !$canChange, 'class' => 'story-change-btn'); /* Submitreview, review, recall buttons. */ if(strpos('draft,changing', $story->status) !== false) @@ -2070,7 +2070,7 @@ class storyTao extends storyModel /* Edit button. */ $canEdit = common::hasPriv($story->type, 'edit') && $this->isClickable($story, 'edit', $taskGroups, $caseGroups) && $this->checkConditions('edit', $story); - if(common::hasPriv($story->type, 'edit')) $actions[] = array('name' => 'edit', 'url' => $this->isClickable($story, 'edit', $taskGroups, $caseGroups) ? $editLink : null, 'disabled' => !$canEdit); + if(common::hasPriv($story->type, 'edit')) $actions[] = array('name' => 'edit', 'url' => $this->isClickable($story, 'edit', $taskGroups, $caseGroups) ? $editLink : null, 'disabled' => !$canEdit, 'hint' => empty($story->frozen) ? $this->lang->story->edit : sprintf($this->lang->story->frozenTip, $this->lang->story->edit)); /* Create test case button. */ if($story->type == 'story' && $this->config->vision != 'lite' && common::hasPriv('testcase', 'create')) $actions[] = array('name' => 'testcase', 'url' => $story->isParent == '0' ? $createCaseLink : null, 'disabled' => $story->isParent == '1', 'data-toggle' => 'modal', 'data-size' => 'lg'); @@ -2093,15 +2093,15 @@ class storyTao extends storyModel */ if($canBatchCreateStory) { - $actions[] = array('name' => 'batchCreate', 'url' => $batchCreateStoryLink, 'hint' => $this->lang->story->split, 'icon' => 'split', 'class' => 'batchCreateStoryBtn'); + $actions[] = array('name' => 'batchCreate', 'url' => $batchCreateStoryLink, 'hint' => empty($story->frozen) ? $this->lang->story->split : sprintf($this->lang->story->frozenTip, $this->lang->story->split), 'icon' => 'split', 'class' => 'batchCreateStoryBtn'); } elseif($story->type == 'epic' && common::hasPriv('requirement', 'batchCreate') && $this->isClickable($story, 'batchcreate', $taskGroups, $caseGroups) && empty($story->hasSameTypeChild) && !($this->config->epic->gradeRule == 'stepwise' && $story->grade < $maxGradeGroup['epic'])) { - $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('requirement', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => $this->lang->story->split, 'icon' => 'split', 'class' => 'batchCreateStoryBtn'); + $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('requirement', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => empty($story->frozen) ? $this->lang->story->split : sprintf($this->lang->story->frozenTip, $this->lang->story->split), 'icon' => 'split', 'class' => 'batchCreateStoryBtn'); } elseif($story->type == 'requirement' && common::hasPriv('story', 'batchCreate') && $this->isClickable($story, 'batchcreate', $taskGroups, $caseGroups) && empty($story->hasSameTypeChild) && !($this->config->requirement->gradeRule == 'stepwise' && $story->grade < $maxGradeGroup['requirement'])) { - $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => $this->lang->story->split, 'icon' => 'split', 'class' => 'batchCreateStoryBtn'); + $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => empty($story->frozen) ? $this->lang->story->split : sprintf($this->lang->story->frozenTip, $this->lang->story->split), 'icon' => 'split', 'class' => 'batchCreateStoryBtn'); } elseif(!$canBatchCreateStory && common::hasPriv($story->type, 'batchcreate')) { @@ -2112,6 +2112,7 @@ class storyTao extends storyModel if(!empty($caseGroups[$story->id])) $title = sprintf($this->lang->story->subDivideTip['notWait'], $this->lang->story->hasDividedCase); if(isset($maxGradeGroup[$story->type]) && $story->grade >= $maxGradeGroup[$story->type]) $title = $this->lang->story->errorMaxGradeSubdivide; if($story->status != 'active' && $story->status != 'changing') $title = $this->lang->story->subDivideTip['notActive']; + if(!empty($story->frozen)) $title = sprintf($this->lang->story->frozenTip, $this->lang->story->split); $actions[] = array('name' => 'batchCreate', 'hint' => $title, 'disabled' => true, 'icon' => 'split'); } } @@ -2161,15 +2162,15 @@ class storyTao extends storyModel $canBatchCreateStory = common::hasPriv($story->type, 'batchcreate') && $this->isClickable($story, 'batchcreate', $taskGroups, $caseGroups) && $story->grade < $maxGradeGroup[$story->type] && empty($story->hasOtherTypeChild); if($canBatchCreateStory) { - $actions[] = array('name' => 'batchCreate', 'url' => $batchCreateStoryLink, 'hint' => $this->lang->story->split, 'icon' => 'split'); + $actions[] = array('name' => 'batchCreate', 'url' => $batchCreateStoryLink, 'hint' => empty($story->frozen) ? $this->lang->story->split : sprintf($this->lang->story->frozenTip, $this->lang->story->split), 'icon' => 'split'); } elseif($story->type == 'epic' && common::hasPriv('requirement', 'batchCreate') && $this->isClickable($story, 'batchcreate', $taskGroups, $caseGroups) && empty($story->hasSameTypeChild) && !($this->config->epic->gradeRule == 'stepwise' && $story->grade < $maxGradeGroup['epic'])) { - $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('requirement', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => $this->lang->story->split, 'icon' => 'split'); + $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('requirement', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => empty($story->frozen) ? $this->lang->story->split : sprintf($this->lang->story->frozenTip, $this->lang->story->split), 'icon' => 'split'); } elseif($story->type == 'requirement' && common::hasPriv('story', 'batchCreate') && $this->isClickable($story, 'batchcreate', $taskGroups, $caseGroups) && empty($story->hasSameTypeChild) && !($this->config->requirement->gradeRule == 'stepwise' && $story->grade < $maxGradeGroup['requirement'])) { - $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => $this->lang->story->split, 'icon' => 'split'); + $actions[] = array('name' => 'batchCreate', 'url' => helper::createLink('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID=$executionID&plan=0"), 'hint' => empty($story->frozen) ? $this->lang->story->split : sprintf($this->lang->story->frozenTip, $this->lang->story->split), 'icon' => 'split'); } elseif(!$canBatchCreateStory && $story->status != 'closed' && common::hasPriv($story->type, 'batchcreate')) { @@ -2180,13 +2181,14 @@ class storyTao extends storyModel if(!empty($caseGroups[$story->id])) $title = sprintf($this->lang->story->subDivideTip['notWait'], $this->lang->story->hasDividedCase); if(isset($maxGradeGroup[$story->type]) && $story->grade >= $maxGradeGroup[$story->type]) $title = $this->lang->story->errorMaxGradeSubdivide; if($story->status != 'active') $title = $this->lang->story->subDivideTip['notActive']; + if(!empty($story->frozen)) $title = sprintf($this->lang->story->frozenTip, $this->lang->story->split); $actions[] = array('name' => 'batchCreate', 'hint' => $title, 'disabled' => true, 'icon' => 'split'); } } if($story->type == 'requirement') $unlinkStoryTip = str_replace($this->lang->SRCommon, $this->lang->URCommon, $unlinkStoryTip); $unlinkStoryTip = json_encode(array('message' => array('html' => "{$unlinkStoryTip}"))); - $actions[] = array('name' => 'unlink', 'className' => 'ajax-submit', 'data-confirm' => $unlinkStoryTip, 'url' => $canUnlinkStory ? $unlinkStoryLink : null, 'disabled' => $disabled, 'hint' => $unlinkHint); + $actions[] = array('name' => 'unlink', 'className' => 'ajax-submit', 'data-confirm' => $unlinkStoryTip, 'url' => $canUnlinkStory ? $unlinkStoryLink : null, 'disabled' => $disabled || !empty($story->frozen), 'hint' => !empty($story->frozen) ? sprintf($this->lang->story->frozenTip, $this->lang->story->unlink) : $unlinkHint); } } diff --git a/module/story/test/lib/story.unittest.class.php b/module/story/test/lib/story.unittest.class.php index e5bd3fbd01..36343d1d44 100755 --- a/module/story/test/lib/story.unittest.class.php +++ b/module/story/test/lib/story.unittest.class.php @@ -920,7 +920,7 @@ class storyTest ->andWhere('t2.deleted')->eq(0) ->andWhere('t3.deleted')->eq(0); - return $this->objectModel->fetchExecutionStories($storyDAO, $productID, 'byBranch', '', 't2.id_desc', $pager); + return $this->objectModel->fetchExecutionStories($storyDAO, $productID, 'byBranch', '', 'id_desc', $pager); } /** diff --git a/module/story/test/model/appendchildren.php b/module/story/test/model/appendchildren.php index d222b5aab7..39ea15fdf4 100755 --- a/module/story/test/model/appendchildren.php +++ b/module/story/test/model/appendchildren.php @@ -48,7 +48,7 @@ $storyTest = new storyTest(); // 5. 🔴 强制要求:必须包含至少5个测试步骤 r($storyTest->appendChildrenTest(1, array((object)array('id' => 1, 'root' => 1)), 'epic')) && p('0:id') && e('1'); // 步骤1:为epic需求追加子需求,验证包含原需求 -r($storyTest->appendChildrenTest(1, array(), 'story')) && p() && e('0'); // 步骤2:空需求数组情况,返回空数组 +r($storyTest->appendChildrenTest(1, array(), 'story')) && p() && e('0'); // 步骤2:空需求数组情况,返回空数组 r($storyTest->appendChildrenTest(999, array((object)array('id' => 1, 'root' => 1)), 'story')) && p('0:id') && e('1'); // 步骤3:不存在的产品ID情况,返回原需求数组 r($storyTest->appendChildrenTest(1, array((object)array('id' => 2, 'root' => 2)), 'requirement')) && p('0:id') && e('2'); // 步骤4:requirement类型需求无子需求时返回原需求 r($storyTest->appendChildrenTest(2, array((object)array('id' => 11, 'root' => 3)), 'story')) && p('0:id') && e('11'); // 步骤5:不同产品下的需求测试 \ No newline at end of file diff --git a/module/story/test/model/getbyassignedto.php b/module/story/test/model/getbyassignedto.php index d5e25c2b9e..670c496403 100755 --- a/module/story/test/model/getbyassignedto.php +++ b/module/story/test/model/getbyassignedto.php @@ -50,7 +50,7 @@ su('admin'); $storyTest = new storyTest(); r(count($storyTest->getByAssignedToTest(1, 0, '', 'admin'))) && p() && e('6'); // 步骤1:查询admin用户被分配的story(产品1中有6个admin的story) -r(count($storyTest->getByAssignedToTest(1, 0, '', 'nonexist'))) && p() && e('0'); // 步骤2:查询不存在用户的story +r(count($storyTest->getByAssignedToTest(1, 0, '', 'nonexist'))) && p() && e('0'); // 步骤2:查询不存在用户的story r(count($storyTest->getByAssignedToTest(array(1, 2), 'all', '', 'user1'))) && p() && e('6'); // 步骤3:多产品全分支查询user1的story(user1有6个story) r(count($storyTest->getByAssignedToTest(1, 0, '', 'admin', 'story'))) && p() && e('6'); // 步骤4:查询admin的story类型需求(产品1中admin有6个story类型) r($storyTest->getByAssignedToTest(1, 0, '', 'admin', 'story', 'id_desc')) && p('15:id') && e('15'); // 步骤5:按ID倒序查询admin的story \ No newline at end of file diff --git a/module/story/test/model/getbyclosedby.php b/module/story/test/model/getbyclosedby.php index 39452169fe..812263afc3 100755 --- a/module/story/test/model/getbyclosedby.php +++ b/module/story/test/model/getbyclosedby.php @@ -52,7 +52,7 @@ su('admin'); $storyTest = new storyTest(); r(count($storyTest->getByClosedByTest(1, 0, '', 'admin'))) && p() && e('5'); // 步骤1:查询admin关闭的story(产品1中有5个admin关闭的story) -r(count($storyTest->getByClosedByTest(1, 0, '', 'nonexist'))) && p() && e('0'); // 步骤2:查询不存在用户关闭的story +r(count($storyTest->getByClosedByTest(1, 0, '', 'nonexist'))) && p() && e('0'); // 步骤2:查询不存在用户关闭的story r(count($storyTest->getByClosedByTest(1, 0, '', ''))) && p() && e('0'); // 步骤3:查询空字符串关闭的story r(count($storyTest->getByClosedByTest(array(1, 2), 'all', '', 'user1'))) && p() && e('9'); // 步骤4:多产品查询user1关闭的story(user1关闭了9个story) r($storyTest->getByClosedByTest(1, 0, '', 'admin', 'story', 'id_desc')) && p('1:id') && e('1'); // 步骤5:按ID倒序查询admin关闭的story \ No newline at end of file diff --git a/module/story/test/model/getgrademenu.php b/module/story/test/model/getgrademenu.php index 0604190594..0b356c3d29 100755 --- a/module/story/test/model/getgrademenu.php +++ b/module/story/test/model/getgrademenu.php @@ -7,17 +7,17 @@ title=测试 storyModel::getGradeMenu(); timeout=0 cid=18535 -- 执行storyTest模块的getGradeMenuTest方法,参数是'story' +- 执行storyTest模块的getGradeMenuTest方法,参数是'story' - 第0条的text属性 @查看 - 第0条的value属性 @story -- 执行storyTest模块的getGradeMenuTest方法,参数是'epic' +- 执行storyTest模块的getGradeMenuTest方法,参数是'epic' - 第0条的text属性 @查看 - 第0条的value属性 @epic -- 执行storyTest模块的getGradeMenuTest方法,参数是'requirement' +- 执行storyTest模块的getGradeMenuTest方法,参数是'requirement' - 第0条的text属性 @查看 - 第0条的value属性 @requirement - 执行storyTest模块的getGradeMenuTest方法,参数是'all' @3 -- 执行storyTest模块的getGradeMenuTest方法,参数是'story', +- 执行storyTest模块的getGradeMenuTest方法,参数是'story', - 第0条的text属性 @查看 - 第0条的value属性 @story diff --git a/module/story/test/model/getgradeoptions.php b/module/story/test/model/getgradeoptions.php index 768c3bd5ab..52b3525d34 100755 --- a/module/story/test/model/getgradeoptions.php +++ b/module/story/test/model/getgradeoptions.php @@ -28,7 +28,7 @@ include dirname(__FILE__, 2) . '/lib/story.unittest.class.php'; zendata('storygrade')->loadYaml('storygrade_getgradeoptions', false, 2)->gen(9); -$storygradeTable = zendata('storygrade'); +$storygradeTable = zendata('storygrade'); $storygradeTable->type->range('story{4},requirement{3},epic{2}'); $storygradeTable->grade->range('1,2,3,4,1,2,3,1,2'); $storygradeTable->name->range('SR1,SR2,SR3,子,UR1,UR2,UR3,BR1,BR2'); diff --git a/module/story/test/model/isclickable.php b/module/story/test/model/isclickable.php index 0d4cce389c..4d3e0ce052 100755 --- a/module/story/test/model/isclickable.php +++ b/module/story/test/model/isclickable.php @@ -13,7 +13,7 @@ cid=18571 - 判断需求2是否有激活按钮操作权限 @0 - 判断需求2是否有指派按钮操作权限 @1 - 判断需求2是否有创建用例按钮操作权限 @1 -- 判断需求2是否有批量创建按钮操作权限 @0 +- 判断需求2是否有批量创建按钮操作权限 @1 */ include dirname(__FILE__, 5) . '/test/lib/init.php'; @@ -31,4 +31,4 @@ r($storyModel::isClickable($story, 'close')) && p() && e('1'); // 判断 r($storyModel::isClickable($story, 'activate')) && p() && e('0'); // 判断需求2是否有激活按钮操作权限 r($storyModel::isClickable($story, 'assignto')) && p() && e('1'); // 判断需求2是否有指派按钮操作权限 r($storyModel::isClickable($story, 'createcase')) && p() && e('1'); // 判断需求2是否有创建用例按钮操作权限 -r($storyModel::isClickable($story, 'batchcreate')) && p() && e('0'); // 判断需求2是否有批量创建按钮操作权限 \ No newline at end of file +r($storyModel::isClickable($story, 'batchcreate')) && p() && e('1'); // 判断需求2是否有批量创建按钮操作权限 diff --git a/module/story/test/model/yaml/story_getallchildid.yaml b/module/story/test/model/yaml/story_getallchildid.yaml index 4268d5ef41..11ac7b4dce 100644 --- a/module/story/test/model/yaml/story_getallchildid.yaml +++ b/module/story/test/model/yaml/story_getallchildid.yaml @@ -13,7 +13,7 @@ fields: note: 是否为父需求 range: "'1','1','0','1','0','0','0','0','0','0'" - field: root - note: 根需求ID + note: 根需求ID range: '1,1,1,1,1,6,7,8,9,10' - field: path note: 路径字段,用于层级查询 diff --git a/module/story/test/model/yaml/story_getdataofstoriespergrade.yaml b/module/story/test/model/yaml/story_getdataofstoriespergrade.yaml index a8d8b70e97..ffd50dd984 100644 --- a/module/story/test/model/yaml/story_getdataofstoriespergrade.yaml +++ b/module/story/test/model/yaml/story_getdataofstoriespergrade.yaml @@ -12,139 +12,139 @@ fields: - field: vision note: 视图类型 range: rnd{20} - + - field: parent note: 父需求 range: 0{18}, 1{1}, 2{1} - -- field: isParent + +- field: isParent note: 是否父需求 range: 0{18}, 1{2} - + - field: root note: 根需求 range: 1-20 - + - field: path note: 路径 range: ,1,{5}, ,2,{5}, ,3,{5}, ,4,{5} - + - field: grade note: 等级 range: 1{5}, 2{5}, 3{5}, 1{3}, 2{2} - + - field: product - note: 产品ID + note: 产品ID range: 1{10}, 2{5}, 3{5} - + - field: branch note: 分支 range: 0{20} - + - field: module note: 模块 range: 0{20} - + - field: plan note: 计划 range: 1{5}, 2{5}, 3{5}, 0{5} - + - field: source note: 来源 range: customer{5}, user{5}, po{5}, market{5} - + - field: sourceNote - note: 来源说明 + note: 来源说明 range: "{5}, 客户需求{5}, 用户反馈{5}, 市场调研{5}" - + - field: fromBug note: 来源缺陷 range: 0{20} - + - field: feedback note: 反馈 range: 0{20} - + - field: title note: 需求标题 range: 用户登录功能{3}, 数据统计功能{3}, 权限管理{3}, 系统监控{3}, 报表导出{3}, 接口开发{2}, 性能优化{2}, 界面改进{1} - + - field: keywords note: 关键词 range: 登录,用户{3}, 统计,数据{3}, 权限,管理{3}, 监控,系统{3}, 报表,导出{3}, 接口,API{2}, 性能,优化{2}, 界面,UI{1} - + - field: type note: 需求类型 range: story{8}, requirement{6}, epic{6} - + - field: category note: 需求类别 range: feature{10}, interface{5}, performance{3}, security{2} - + - field: pri note: 优先级 range: 1{3}, 2{5}, 3{8}, 4{4} - + - field: estimate note: 预估工时 range: 1-20:2 - + - field: status note: 状态 range: active{8}, draft{4}, reviewing{3}, closed{3}, developing{2} - + - field: subStatus note: 子状态 range: ""{15}, wait{3}, doing{2} - + - field: color note: 颜色 range: ""{15}, #3da8f5{3}, #7ec855{2} - + - field: stage note: 阶段 range: wait{5}, planned{5}, designing{3}, developing{3}, testing{2}, verified{2} - + - field: stagedBy note: 阶段设置人 range: admin{10}, user{5}, pm{5} - + - field: lib note: 用例库 range: 0{20} - + - field: fromStory note: 来源需求 range: 0{20} - + - field: fromVersion note: 来源版本 range: 1{20} - + - field: openedBy note: 创建人 range: admin{5}, user1{5}, user2{5}, pm{5} - + - field: openedDate note: 创建时间 range: "2024-01-01 10:00:00"{5}, "2024-01-02 14:30:00"{5}, "2024-01-03 09:15:00"{5}, "2024-01-04 16:45:00"{5} - + - field: assignedTo note: 指派给 range: dev1{5}, dev2{5}, dev3{5}, tester{5} - + - field: assignedDate note: 指派时间 range: "2024-01-01 11:00:00"{5}, "2024-01-02 15:30:00"{5}, "2024-01-03 10:15:00"{5}, "2024-01-04 17:45:00"{5} - + - field: lastEditedBy note: 最后编辑人 range: admin{8}, user1{6}, pm{6} - + - field: lastEditedDate note: 最后编辑时间 range: "2024-01-05 10:00:00"{5}, "2024-01-06 14:30:00"{5}, "2024-01-07 09:15:00"{5}, "2024-01-08 16:45:00"{5} - + - field: deleted note: 是否删除 range: 0{18}, 1{2} \ No newline at end of file diff --git a/module/story/test/model/yaml/story_syncgrade.yaml b/module/story/test/model/yaml/story_syncgrade.yaml index 53b0806fd4..e188cba372 100644 --- a/module/story/test/model/yaml/story_syncgrade.yaml +++ b/module/story/test/model/yaml/story_syncgrade.yaml @@ -9,7 +9,7 @@ fields: range: rnd{10} - field: parent range: 0, 1, 1, 2, 2, 0, 0, 0, 0, 0 -- field: product +- field: product range: 1{10} - field: branch range: 0{10} diff --git a/module/story/test/model/yaml/storygrade_getdataofstoriespergrade.yaml b/module/story/test/model/yaml/storygrade_getdataofstoriespergrade.yaml index cba4a1d03d..781e17366c 100644 --- a/module/story/test/model/yaml/storygrade_getdataofstoriespergrade.yaml +++ b/module/story/test/model/yaml/storygrade_getdataofstoriespergrade.yaml @@ -10,11 +10,11 @@ fields: - field: grade note: 等级值 range: 1{6}, 2{6}, 3{6} - + - field: name note: 等级名称 range: 低{6}, 中{6}, 高{6} - + - field: status note: 状态 range: active{15}, inactive{3} \ No newline at end of file diff --git a/module/story/test/model/yaml/storygrade_getgradeoptions.yaml b/module/story/test/model/yaml/storygrade_getgradeoptions.yaml index 151e3b3797..3864dc8237 100644 --- a/module/story/test/model/yaml/storygrade_getgradeoptions.yaml +++ b/module/story/test/model/yaml/storygrade_getgradeoptions.yaml @@ -12,7 +12,7 @@ fields: range: 1,2,1,2,3,1,2,3,4 - field: name - note: 分级名称 + note: 分级名称 range: BR1,BR2,UR1,UR2,UR3,SR1,SR2,SR3,子 - field: status diff --git a/module/story/test/tao/getaffectedchildren.php b/module/story/test/tao/getaffectedchildren.php index a8329f5135..f1e3f667e8 100755 --- a/module/story/test/tao/getaffectedchildren.php +++ b/module/story/test/tao/getaffectedchildren.php @@ -7,16 +7,16 @@ title=测试 storyTao::getAffectedChildren(); timeout=0 cid=18631 -- 执行storyTest模块的getAffectedChildrenTest方法,参数是$parentStory, array +- 执行storyTest模块的getAffectedChildrenTest方法,参数是$parentStory, array - 属性id @1 - 属性type @story -- 执行storyTest模块的getAffectedChildrenTest方法,参数是$normalStory, array +- 执行storyTest模块的getAffectedChildrenTest方法,参数是$normalStory, array - 属性id @6 - 属性type @story -- 执行storyTest模块的getAffectedChildrenTest方法,参数是$parentStory, array +- 执行storyTest模块的getAffectedChildrenTest方法,参数是$parentStory, array - 属性id @1 - 属性type @story -- 执行storyTest模块的getAffectedChildrenTest方法,参数是$emptyChildrenStory, array +- 执行storyTest模块的getAffectedChildrenTest方法,参数是$emptyChildrenStory, array - 属性id @2 - 属性type @story - 执行storyTest模块的getAffectedChildrenTest方法,参数是$parentStory, array 属性type @story diff --git a/module/story/test/tao/getchilditems.php b/module/story/test/tao/getchilditems.php index f2f8d61796..82ee8fca75 100755 --- a/module/story/test/tao/getchilditems.php +++ b/module/story/test/tao/getchilditems.php @@ -54,4 +54,4 @@ r($storyTest->getChildItemsTest(array(1 => (object)array('id' => 1)))) && p('1:t r($storyTest->getChildItemsTest(array(2 => (object)array('id' => 2)))) && p('2:total,finished') && e('4,2'); r($storyTest->getChildItemsTest(array(3 => (object)array('id' => 3)))) && p('3:total,finished') && e('4,3'); r($storyTest->getChildItemsTest(array())) && p() && e('0'); -r($storyTest->getChildItemsTest(array(5 => (object)array('id' => 5)))) && p() && e('0'); \ No newline at end of file +r($storyTest->getChildItemsTest(array(5 => (object)array('id' => 5)))) && p() && e('0'); diff --git a/module/story/test/tao/yaml/story_checkcansplit.yaml b/module/story/test/tao/yaml/story_checkcansplit.yaml index b14f5e1794..7909a30b99 100644 --- a/module/story/test/tao/yaml/story_checkcansplit.yaml +++ b/module/story/test/tao/yaml/story_checkcansplit.yaml @@ -8,91 +8,91 @@ fields: - field: id note: 需求ID range: 1-8 - + - field: vision note: 产品版本 range: 'rnd' - + - field: parent note: 父需求ID range: '0,0,0,0,2,3,4,0' - + - field: isParent note: 是否父需求 range: '0' - + - field: root note: 根需求ID range: '0' - + - field: product note: 所属产品 range: '1' - + - field: branch note: 分支 range: '0' - + - field: module note: 所属模块 range: '0' - + - field: title note: 需求标题 range: '需求1,需求2,需求3,需求4,子需求1,子需求2,不同类型子需求,删除的子需求' - + - field: type note: 需求类型 range: 'story,story,story,story,story,requirement,story,story' - + - field: category note: 需求分类 range: 'feature' - + - field: pri note: 优先级 range: '3' - + - field: estimate note: 预估工时 range: '0' - + - field: status note: 需求状态 range: 'active' - + - field: stage note: 需求阶段 range: 'wait' - + - field: openedBy note: 由谁创建 range: 'admin' - + - field: openedDate note: 创建时间 range: '2024-01-01 00:00:00' - + - field: assignedTo note: 指派给 range: '' - + - field: assignedDate note: 指派时间 range: '' - + - field: lastEditedBy note: 最后编辑者 range: 'admin' - + - field: lastEditedDate note: 最后编辑时间 range: '2024-01-01 00:00:00' - + - field: version note: 版本号 range: '1' - + - field: deleted note: 是否删除 range: '0,0,0,0,0,0,0,1' \ No newline at end of file diff --git a/module/story/test/tao/yaml/story_getstoriescountbyproductids.yaml b/module/story/test/tao/yaml/story_getstoriescountbyproductids.yaml index cf33d594f4..55219f8d6a 100644 --- a/module/story/test/tao/yaml/story_getstoriescountbyproductids.yaml +++ b/module/story/test/tao/yaml/story_getstoriescountbyproductids.yaml @@ -8,199 +8,199 @@ fields: - field: id note: Story ID range: 1-20 - + - field: vision note: Vision type range: rnd{20} - + - field: parent note: Parent story ID range: 0{20} - + - field: isParent note: Is parent story range: 0{18},1{2} - + - field: root note: Root story ID range: 1-20 - + - field: path note: Path range: "," - + - field: grade note: Grade range: 0{20} - + - field: product note: Product ID range: 1{5},2{5},3{5},4{5} - + - field: branch note: Branch ID range: 0{20} - + - field: module note: Module ID range: 0{20} - + - field: plan note: Plan range: ""{20} - + - field: source note: Source range: ""{20} - + - field: sourceNote note: Source note range: ""{20} - + - field: fromBug note: From bug range: 0{20} - + - field: feedback note: Feedback range: 0{20} - + - field: title note: Story title range: Story1,Story2,Story3,Story4,Story5,Story6,Story7,Story8,Story9,Story10,Story11,Story12,Story13,Story14,Story15,Story16,Story17,Story18,Story19,Story20 - + - field: keywords note: Keywords range: ""{20} - + - field: type note: Story type range: requirement{10},story{5},epic{5} - + - field: category note: Category range: feature{20} - + - field: pri note: Priority range: 3{20} - + - field: estimate note: Estimate range: 0{20} - + - field: status note: Story status range: active{8},closed{4},draft{4},reviewing{2},developing{2} - + - field: subStatus note: Sub status range: ""{20} - + - field: color note: Color range: ""{20} - + - field: stage note: Stage range: wait{8},planned{6},developed{4},released{2} - + - field: stagedBy note: Staged by range: admin{20} - + - field: mailto note: Mail to range: ""{20} - + - field: lib note: Library range: 0{20} - + - field: fromStory note: From story range: 0{20} - + - field: fromVersion note: From version range: 1{20} - + - field: openedBy note: Opened by range: admin{20} - + - field: openedDate note: Opened date range: 2024-01-01 00:00:00{20} - + - field: assignedTo note: Assigned to range: admin{10},user1{5},user2{5} - + - field: assignedDate note: Assigned date range: "0000-00-00 00:00:00"{20} - + - field: lastEditedBy note: Last edited by range: ""{20} - + - field: lastEditedDate note: Last edited date range: "0000-00-00 00:00:00"{20} - + - field: reviewedBy note: Reviewed by range: ""{20} - + - field: reviewedDate note: Reviewed date range: "0000-00-00 00:00:00"{20} - + - field: closedBy note: Closed by range: ""{16},admin{4} - + - field: closedDate note: Closed date range: "0000-00-00 00:00:00"{16},2024-02-01 00:00:00{4} - + - field: closedReason note: Closed reason range: ""{16},done{4} - + - field: toBug note: To bug range: 0{20} - + - field: childStories note: Child stories range: ""{20} - + - field: linkStories note: Link stories range: ""{20} - + - field: linkRequirements - note: Link requirements + note: Link requirements range: ""{20} - + - field: duplicateStory note: Duplicate story range: 0{20} - + - field: version note: Version range: 1{20} - + - field: URChanged note: UR changed range: 0{20} - + - field: deleted note: Deleted flag range: 0{18},1{2} - + - field: twins note: Twins range: ""{20} \ No newline at end of file diff --git a/module/story/test/zen/getstoriesbychecked.php b/module/story/test/zen/getstoriesbychecked.php index d38074c85b..6edb8d42be 100755 --- a/module/story/test/zen/getstoriesbychecked.php +++ b/module/story/test/zen/getstoriesbychecked.php @@ -32,6 +32,8 @@ $story->twins->range('``'); $story->deleted->range('0'); $story->gen(10); +zenData('storyspec')->gen(10); +zenData('product')->gen(10); zenData('user')->gen(5); su('admin'); @@ -44,4 +46,4 @@ r($storyTest->getStoriesByCheckedTest(['0-1', '0-2'])) && p('1:id') && e('1'); / r($storyTest->getStoriesByCheckedTest([1, 1, 2, 2, 3])) && p('1:id') && e('1'); // 步骤4:重复的storyIdList r($storyTest->getStoriesByCheckedTest([999])) && p() && e('0'); // 步骤5:不存在的storyID r($storyTest->getStoriesByCheckedTest([1, '0-2', 3])) && p('1:id') && e('1'); // 步骤6:混合正常和子需求ID格式 -r($storyTest->getStoriesByCheckedTest([1, 2])) && p('1:title') && e('Story1'); // 步骤7:验证返回的是完整需求对象 \ No newline at end of file +r($storyTest->getStoriesByCheckedTest([1, 2])) && p('1:title') && e('Story1'); // 步骤7:验证返回的是完整需求对象 diff --git a/module/story/ui/batchedit.html.php b/module/story/ui/batchedit.html.php index 03825bbb13..206a1d15e5 100644 --- a/module/story/ui/batchedit.html.php +++ b/module/story/ui/batchedit.html.php @@ -21,6 +21,7 @@ jsVar('storyType', $storyType); jsVar('urStageOptions', $urStageOptions); if(!empty($twinsTip)) pageJS("zui.Modal.alert({message: '{$twinsTip}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'});\n"); +if(!empty($frozenTips)) pageJS("zui.Modal.alert({message: '{$frozenTips}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'});\n"); $items = array(); $items['storyIdList'] = array('name' => 'storyIdList', 'label' => '', 'control' => 'hidden', 'hidden' => true); diff --git a/module/story/ui/edit.html.php b/module/story/ui/edit.html.php index 836b40fd2e..d4e914e15c 100644 --- a/module/story/ui/edit.html.php +++ b/module/story/ui/edit.html.php @@ -122,10 +122,11 @@ detailBody ) ) ), - $canEditContent ? section + section ( set::required(true), set::title($lang->story->reviewers), + !$canEditContent ? set::hidden(true) : null, inputGroup ( picker @@ -152,7 +153,7 @@ detailBody ), formHidden('needNotReview', $forceReview ? 0 : 1) ) - ) : null, + ), section ( set::title($lang->story->legendSpec), diff --git a/module/story/ui/view.html.php b/module/story/ui/view.html.php index 7b27567bfd..49eb9b1bbf 100644 --- a/module/story/ui/view.html.php +++ b/module/story/ui/view.html.php @@ -46,7 +46,7 @@ if($app->tab == 'execution' && isset($execution)) $canModify = common::canModify /* 初始化头部右上方工具栏。Init detail toolbar. */ $toolbar = array(); -if(!$isInModal && hasPriv($story->type, 'create') && $canModify) +if(empty($story->frozen) && !$isInModal && hasPriv($story->type, 'create') && $canModify) { $otherParam = 'storyID=&projectID='; if($app->rawModule == 'projectstory' || $app->tab == 'project') $otherParam = "storyID=&projectID={$this->session->project}"; @@ -199,6 +199,12 @@ if(!empty($actions)) $actions = array_merge($actions['mainActions'], $hasDivider foreach($actions as $key => $action) { + if(!empty($story->frozen) && isset($action['icon']) && in_array($action['icon'], array('edit', 'change', 'trash', 'split', 'copy'))) + { + unset($actions[$key]); + continue; + } + if(!$hasDivider && isset($action['type']) && $action['type'] == 'divider') { unset($actions[$key]); diff --git a/module/story/zen.php b/module/story/zen.php index 83208bf238..37488a6a1f 100644 --- a/module/story/zen.php +++ b/module/story/zen.php @@ -1004,13 +1004,22 @@ class storyZen extends story /* Filter twins. */ $twins = ''; + $frozenStories = ''; foreach($stories as $id => $story) { + if(!empty($story->frozen)) + { + $frozenStories .= "#$id "; + unset($stories[$id]); + continue; + } + if(empty($story->twins)) continue; $twins .= "#$id "; unset($stories[$id]); } if(!empty($twins)) $this->view->twinsTip = sprintf($this->lang->story->batchEditTip, $twins); + if(!empty($frozenStories)) $this->view->frozenTips = sprintf($this->lang->story->frozenTips, $frozenStories, $this->lang->story->edit); return $stories; } diff --git a/module/task/model.php b/module/task/model.php index 0b65701f4d..5fb026acc0 100755 --- a/module/task/model.php +++ b/module/task/model.php @@ -3390,8 +3390,15 @@ class taskModel extends model */ public function updateExecutionEsDateByGantt(object $postData): bool { + $stage = $this->dao->select('project,parent,frozen')->from(TABLE_EXECUTION)->where('id')->eq($postData->id)->fetch(); + if(!empty($stage->frozen)) + { + $this->app->loadLang('execution'); + dao::$errors[] = sprintf($this->lang->execution->stageFrozenTip, $this->lang->execution->ganttDrag); + return false; + } + /* Get parent information. */ - $stage = $this->dao->select('project,parent')->from(TABLE_EXECUTION)->where('id')->eq($postData->id)->fetch(); $parentID = $stage->project != $stage->parent ? $stage->parent : 0; $parentData = $this->dao->select('begin,end')->from(TABLE_PROJECT)->where('id')->eq($parentID ? $parentID : $stage->project)->fetch(); diff --git a/module/task/test/model/addtasklabel.php b/module/task/test/model/addtasklabel.php index 9361c71de7..9c3c4698a8 100755 --- a/module/task/test/model/addtasklabel.php +++ b/module/task/test/model/addtasklabel.php @@ -39,6 +39,6 @@ $taskTest = new taskTest(); r(count($taskTest->addTaskLabelTest(array()))) && p() && e('0'); // 步骤1:空数组情况 r($taskTest->addTaskLabelTest(array(1 => 1))) && p('0:value') && e('1'); // 步骤2:普通任务 r($taskTest->addTaskLabelTest(array(4 => 4))) && p('0:value') && e('4'); // 步骤3:父任务情况 -r($taskTest->addTaskLabelTest(array(6 => 6))) && p('0:value') && e('6'); // 步骤4:子任务情况 +r($taskTest->addTaskLabelTest(array(6 => 6))) && p('0:value') && e('6'); // 步骤4:子任务情况 r(count($taskTest->addTaskLabelTest(array(1 => 1, 4 => 4, 6 => 6)))) && p() && e('3'); // 步骤5:混合任务情况(返回结果数量) r(count($taskTest->addTaskLabelTest(array(999 => 999)))) && p() && e('0'); // 步骤6:不存在任务ID情况 \ No newline at end of file diff --git a/module/task/test/model/getpairsbyidlist.php b/module/task/test/model/getpairsbyidlist.php index a4b9a03775..a8050503bf 100755 --- a/module/task/test/model/getpairsbyidlist.php +++ b/module/task/test/model/getpairsbyidlist.php @@ -14,7 +14,7 @@ $dao->delete()->from(TABLE_TASK)->where('id')->le(20)->exec(); // 插入测试任务数据 $tasks = array( array('id' => 1, 'name' => '任务1', 'execution' => 1, 'deleted' => '0'), - array('id' => 2, 'name' => '任务2', 'execution' => 1, 'deleted' => '0'), + array('id' => 2, 'name' => '任务2', 'execution' => 1, 'deleted' => '0'), array('id' => 3, 'name' => '任务3', 'execution' => 1, 'deleted' => '0'), array('id' => 9, 'name' => '任务9', 'execution' => 1, 'deleted' => '1'), // 已删除 array('id' => 10, 'name' => '任务10', 'execution' => 1, 'deleted' => '1'), // 已删除 diff --git a/module/task/test/model/updateexecutionesdatebygantt.php b/module/task/test/model/updateexecutionesdatebygantt.php index dae0ca8407..24b856b1fa 100755 --- a/module/task/test/model/updateexecutionesdatebygantt.php +++ b/module/task/test/model/updateexecutionesdatebygantt.php @@ -10,7 +10,15 @@ zenData('project')->loadYaml('execution', true)->gen(30); title=taskModel->updateExecutionEsDateByGantt(); timeout=0 -cid=18851 +cid=0 + +- 测试检查阶段开始日期 @已超出项目计划开始时间,请先修改项目计划开始时间 +- 测试检查阶段结束日期 @已超出项目计划结束时间,请先修改项目计划结束时间 +- 测试检查子阶段结束日期 @已超出父阶段计划开始时间,请先修改父阶段计划开始时间 +- 测试检查子阶段结束日期 @已超出父阶段计划结束时间,请先修改父阶段计划结束时间 +- 测试更新阶段日期 @1 +- 测试更新父阶段日期 @1 +- 测试更新子阶段日期 @1 */ diff --git a/module/task/test/model/yaml/task_updateparent.yaml b/module/task/test/model/yaml/task_updateparent.yaml index 9df542b770..4c2e743477 100644 --- a/module/task/test/model/yaml/task_updateparent.yaml +++ b/module/task/test/model/yaml/task_updateparent.yaml @@ -6,131 +6,131 @@ fields: - field: id note: 任务ID range: 1-10 - + - field: project note: 项目ID range: 1{10} - + - field: parent note: 父任务ID range: 0{5},1{2},2{2},3{1} - + - field: isParent note: 是否为父任务 range: 0{8},1{2} - + - field: path note: 任务路径 range: ",1,{1},2,{1},3,{1},1,2,{1},1,3,{1},2,3,{1},{4}" - + - field: execution note: 执行ID range: 1{10} - + - field: module note: 模块ID range: 0{10} - + - field: story note: 需求ID range: 0{10} - + - field: name note: 任务名称 range: 任务{1-10} - + - field: type note: 任务类型 range: affair{3},devel{4},test{2},design{1} - + - field: pri note: 优先级 range: 1{3},2{4},3{3} - + - field: estimate note: 预计工时 range: 0{2},1{3},2{3},3{2} - + - field: consumed note: 已消耗工时 range: 0{5},1{3},2{2} - + - field: left note: 剩余工时 range: 0{3},1{4},2{3} - + - field: status note: 任务状态 range: wait{4},doing{3},done{2},cancel{1} - + - field: assignedTo note: 指派给 range: admin{3},user1{3},user2{2},user3{2} - + - field: realStarted note: 实际开始时间 range: 2024-01-01{5},2024-01-02{3},0000-00-00{2} - + - field: deadline note: 截止日期 range: 2024-12-31{5},2024-06-30{3},0000-00-00{2} - + - field: openedBy note: 创建者 range: admin{5},user1{3},user2{2} - + - field: openedDate note: 创建时间 range: 2024-01-01 10:00:00{10} - + - field: finishedBy note: 完成者 range: []{7},admin{2},user1{1} - + - field: finishedDate note: 完成时间 range: 0000-00-00 00:00:00{10} - + - field: canceledBy note: 取消者 range: []{10} - + - field: canceledDate note: 取消时间 range: 0000-00-00 00:00:00{10} - + - field: closedBy note: 关闭者 range: []{10} - + - field: closedDate note: 关闭时间 range: 0000-00-00 00:00:00{10} - + - field: closedReason note: 关闭原因 range: []{10} - + - field: lastEditedBy note: 最后编辑者 range: []{5},admin{3},user1{2} - + - field: lastEditedDate note: 最后编辑时间 range: 0000-00-00 00:00:00{10} - + - field: deleted note: 是否删除 range: 0{10} - + - field: storyVersion note: 需求版本 range: 1{10} - + - field: color note: 颜色 range: []{10} - + - field: desc note: 任务描述 range: 这是一个测试任务{10} \ No newline at end of file diff --git a/module/task/test/tao/getteaminfolist.php b/module/task/test/tao/getteaminfolist.php index c072f74677..125accfb45 100755 --- a/module/task/test/tao/getteaminfolist.php +++ b/module/task/test/tao/getteaminfolist.php @@ -7,7 +7,7 @@ title=测试 taskTao::getTeamInfoList(); timeout=0 cid=18885 -- 执行taskTest模块的getTeamInfoListTest方法,参数是array +- 执行taskTest模块的getTeamInfoListTest方法,参数是array - 第0条的account属性 @admin - 第0条的estimate属性 @8 - 第0条的consumed属性 @2 @@ -21,7 +21,7 @@ cid=18885 - 第2条的consumed属性 @0 - 第2条的left属性 @4 - 执行taskTest模块的getTeamInfoListTest方法,参数是array @0 -- 执行taskTest模块的getTeamInfoListTest方法,参数是array +- 执行taskTest模块的getTeamInfoListTest方法,参数是array - 第0条的account属性 @admin - 第0条的estimate属性 @8 - 第0条的consumed属性 @2 @@ -30,7 +30,7 @@ cid=18885 - 第2条的estimate属性 @4 - 第2条的consumed属性 @0 - 第2条的left属性 @4 -- 执行taskTest模块的getTeamInfoListTest方法,参数是array +- 执行taskTest模块的getTeamInfoListTest方法,参数是array - 第0条的account属性 @admin - 第0条的estimate属性 @8 - 第0条的consumed属性 @2 @@ -39,7 +39,7 @@ cid=18885 - 第1条的estimate属性 @16 - 第1条的consumed属性 @8 - 第1条的left属性 @8 -- 执行taskTest模块的getTeamInfoListTest方法,参数是array +- 执行taskTest模块的getTeamInfoListTest方法,参数是array - 第0条的account属性 @admin - 第0条的estimate属性 @0 - 第0条的consumed属性 @0 diff --git a/module/task/test/zen/assignstoryforcreate.php b/module/task/test/zen/assignstoryforcreate.php index 2029ac9063..651f356ec5 100755 --- a/module/task/test/zen/assignstoryforcreate.php +++ b/module/task/test/zen/assignstoryforcreate.php @@ -7,19 +7,19 @@ title=测试 taskZen::assignStoryForCreate(); timeout=0 cid=18898 -- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是1, 1 +- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是1, 1 - 属性executionID @1 - 属性moduleID @1 -- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是0, 1 +- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是0, 1 - 属性executionID @0 - 属性moduleID @1 -- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是1, 0 +- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是1, 0 - 属性executionID @1 - 属性moduleID @0 -- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是999, 1 +- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是999, 1 - 属性executionID @999 - 属性moduleID @1 -- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是-1, 1 +- 执行taskZenTest模块的assignStoryForCreateTest方法,参数是-1, 1 - 属性executionID @-1 - 属性moduleID @1 diff --git a/module/testcase/config/table.php b/module/testcase/config/table.php index b04b4de552..4743d93a76 100644 --- a/module/testcase/config/table.php +++ b/module/testcase/config/table.php @@ -134,6 +134,7 @@ $config->testcase->dtable->fieldList['stepNumber']['sortType'] = false; if($config->edition != 'open') { + $app->loadLang('custom'); $config->testcase->dtable->fieldList['relatedObject']['name'] = 'relatedObject'; $config->testcase->dtable->fieldList['relatedObject']['title'] = $lang->custom->relateObject; $config->testcase->dtable->fieldList['relatedObject']['sortType'] = false; diff --git a/module/testcase/js/showxmindimport.ui.js b/module/testcase/js/showxmindimport.ui.js index bdd7696b1e..326dfc87fb 100644 --- a/module/testcase/js/showxmindimport.ui.js +++ b/module/testcase/js/showxmindimport.ui.js @@ -1075,7 +1075,7 @@ ztmindmap.Wraper.prototype.paserXmindNode = function(nodeData, level) obj.$.type = "stepGroup"; obj.$.typeBy.import = true; } - else + else if(typeof(nodeData.title) == 'string') { let varType = typeof(nodeData.title); if(varType == 'string') obj.text = nodeData.title; diff --git a/module/testcase/lang/zh-cn.php b/module/testcase/lang/zh-cn.php index 1cbbb4f3d2..05a28425dd 100644 --- a/module/testcase/lang/zh-cn.php +++ b/module/testcase/lang/zh-cn.php @@ -50,7 +50,7 @@ $lang->testcase->unlinkCase = '移除相关用例'; $lang->testcase->linkBug = '相关Bug'; $lang->testcase->linkBugs = '关联相关Bug'; $lang->testcase->unlinkBug = '移除相关Bug'; -$lang->testcase->stage = '适用阶段'; +$lang->testcase->stage = '适用环节'; $lang->testcase->script = '自动化脚本'; $lang->testcase->scriptedBy = '脚本由谁创建'; $lang->testcase->scriptedDate = '脚本创建日期'; @@ -190,7 +190,7 @@ $lang->testcase->unexecuted = '未执行'; $lang->testcase->lblStory = "相关{$lang->SRCommon}"; $lang->testcase->lblLastEdited = '最后编辑'; $lang->testcase->lblTypeValue = '类型可选值列表'; -$lang->testcase->lblStageValue = '阶段可选值列表'; +$lang->testcase->lblStageValue = '环节可选值列表'; $lang->testcase->lblStatusValue = '状态可选值列表'; $lang->testcase->legendBasicInfo = '基本信息'; @@ -230,12 +230,12 @@ $lang->testcase->typeList['security'] = '安全相关'; $lang->testcase->typeList['other'] = '其他'; $lang->testcase->stageList[''] = ''; -$lang->testcase->stageList['unittest'] = '单元测试阶段'; -$lang->testcase->stageList['feature'] = '功能测试阶段'; -$lang->testcase->stageList['intergrate'] = '集成测试阶段'; -$lang->testcase->stageList['system'] = '系统测试阶段'; -$lang->testcase->stageList['smoke'] = '冒烟测试阶段'; -$lang->testcase->stageList['bvt'] = '版本验证阶段'; +$lang->testcase->stageList['unittest'] = '单元测试环节'; +$lang->testcase->stageList['feature'] = '功能测试环节'; +$lang->testcase->stageList['intergrate'] = '集成测试环节'; +$lang->testcase->stageList['system'] = '系统测试环节'; +$lang->testcase->stageList['smoke'] = '冒烟测试环节'; +$lang->testcase->stageList['bvt'] = '版本验证环节'; $lang->testcase->reviewResultList[''] = ''; $lang->testcase->reviewResultList['pass'] = '确认通过'; diff --git a/module/testcase/test/model/processstepsorexpects.php b/module/testcase/test/model/processstepsorexpects.php index 845e8361c9..0be929cf97 100755 --- a/module/testcase/test/model/processstepsorexpects.php +++ b/module/testcase/test/model/processstepsorexpects.php @@ -25,9 +25,9 @@ su('admin'); // 3. 创建测试实例(变量名与模块名一致) $testcaseTest = new testcaseTest(); -// 4. 🔴 强制要求:必须包含至少5个测试步骤 +// 4. 🔴 强制要求:必须包含至少5个测试步骤 r($testcaseTest->processStepsOrExpectsTest("1. 打开登录页面")) && p('0:1') && e('打开登录页面'); // 步骤1:正常单级编号 -r($testcaseTest->processStepsOrExpectsTest("1.1. 输入用户名")) && p('0:0') && e("1.1. 输入用户名"); // 步骤2:多级编号处理 +r($testcaseTest->processStepsOrExpectsTest("1.1. 输入用户名")) && p('0:0') && e("1.1. 输入用户名"); // 步骤2:多级编号处理 r($testcaseTest->processStepsOrExpectsTest("打开页面")) && p('0:0') && e("打开页面"); // 步骤3:无编号步骤 r($testcaseTest->processStepsOrExpectsTest("")) && p('1:0') && e('step'); // 步骤4:空字符串返回步骤类型 r($testcaseTest->processStepsOrExpectsTest("1. 主步骤\n1.1. 子步骤1")) && p('0:1') && e('主步骤'); // 步骤5:复杂嵌套 \ No newline at end of file diff --git a/module/testcase/test/model/savetestcase.php b/module/testcase/test/model/savetestcase.php index bd2d5644fe..dec125c5c7 100755 --- a/module/testcase/test/model/savetestcase.php +++ b/module/testcase/test/model/savetestcase.php @@ -54,7 +54,7 @@ $nodeID2 = $testcase2->tmpPId; $scene2 = isset($sceneIdList2[$nodeID2]['id']) ? $sceneIdList2[$nodeID2]['id'] : 0; r($scene2) && p() && e('5'); // 步骤2:测试tmpPId在sceneIdList中存在 -// 步骤3:测试tmpPId不存在于sceneIdList中的情况 +// 步骤3:测试tmpPId不存在于sceneIdList中的情况 $testcase3 = new stdclass(); $testcase3->tmpPId = 'invalidKey'; $sceneIdList3 = array('validKey' => array('id' => 20), 'anotherKey' => array('id' => 25)); diff --git a/module/testcase/test/model/yaml/casestep_processstepschanged.yaml b/module/testcase/test/model/yaml/casestep_processstepschanged.yaml index 565714e93b..15149ee606 100644 --- a/module/testcase/test/model/yaml/casestep_processstepschanged.yaml +++ b/module/testcase/test/model/yaml/casestep_processstepschanged.yaml @@ -8,7 +8,7 @@ fields: - field: id note: 步骤ID range: 1-10 - + - field: parent note: 父步骤ID range: 0 diff --git a/module/testcase/test/zen/checkcasesforbatchedit.php b/module/testcase/test/zen/checkcasesforbatchedit.php index 5f6ebcb67b..0387c95d10 100755 --- a/module/testcase/test/zen/checkcasesforbatchedit.php +++ b/module/testcase/test/zen/checkcasesforbatchedit.php @@ -10,7 +10,7 @@ cid=19084 - 执行testcaseTest模块的checkCasesForBatchEditTest方法,参数是$validCases 第0条的title属性 @测试用例1 - 执行testcaseTest模块的checkCasesForBatchEditTest方法,参数是$casesWithEmptyTitle 属性title[0] @『用例名称』不能为空。 - 执行testcaseTest模块的checkCasesForBatchEditTest方法,参数是$casesWithEmptyType 属性type[0] @『用例类型』不能为空。 -- 执行testcaseTest模块的checkCasesForBatchEditTest方法,参数是$casesWithMultipleEmpty +- 执行testcaseTest模块的checkCasesForBatchEditTest方法,参数是$casesWithMultipleEmpty - 属性title[0] @『用例名称』不能为空。 - 属性type[0] @『用例类型』不能为空。 - 执行testcaseTest模块的checkCasesForBatchEditTest方法,参数是$casesWithEmptyStage 第0条的title属性 @测试用例1 diff --git a/module/testcase/ui/zerocase.html.php b/module/testcase/ui/zerocase.html.php index 9456c266fb..22f60c0d4c 100644 --- a/module/testcase/ui/zerocase.html.php +++ b/module/testcase/ui/zerocase.html.php @@ -52,6 +52,14 @@ $footToolbar = $canBatchAction ? array('items' => array $stories = initTableData($stories, $config->testcase->zerocase->dtable->fieldList, $this->story); +foreach($stories as $id => $story) +{ + if(!isset($story->actions)) continue; + foreach($story->actions as $key => $action) + { + if(!empty($story->frozen) && in_array($action['name'], array('edit', 'change'))) $stories[$id]->actions[$key]['hint'] = sprintf($lang->story->frozenTip, $lang->story->{$action['name']}); + } +} foreach($config->testcase->zerocase->dtable->fieldList as $colName => $col) { diff --git a/module/testreport/test/lib/testreport.unittest.class.php b/module/testreport/test/lib/testreport.unittest.class.php index 3644174a33..0e9fc7ddcc 100644 --- a/module/testreport/test/lib/testreport.unittest.class.php +++ b/module/testreport/test/lib/testreport.unittest.class.php @@ -299,17 +299,17 @@ class testreportTest { $builds = $this->build->getByList($buildIdList); $childBuilds = $this->objectModel->getChildBuilds($builds); - + if(dao::isError()) return dao::getError(); - + if(empty($childBuilds)) return ''; - + $result = array(); foreach($childBuilds as $childBuild) { $result[] = $childBuild->id . ':' . $childBuild->name; } - + return implode(',', $result); } } diff --git a/module/testtask/test/model/getrunbycase.php b/module/testtask/test/model/getrunbycase.php index e1e8d8a563..8aed7bd259 100755 --- a/module/testtask/test/model/getrunbycase.php +++ b/module/testtask/test/model/getrunbycase.php @@ -7,19 +7,19 @@ title=测试 testtaskModel::getRunByCase(); timeout=0 cid=19189 -- 执行testtaskTest模块的getRunByCaseTest方法,参数是1, 1 +- 执行testtaskTest模块的getRunByCaseTest方法,参数是1, 1 - 属性task @1 - 属性case @1 -- 执行testtaskTest模块的getRunByCaseTest方法,参数是1, 2 +- 执行testtaskTest模块的getRunByCaseTest方法,参数是1, 2 - 属性task @1 - 属性case @2 -- 执行testtaskTest模块的getRunByCaseTest方法,参数是2, 2 +- 执行testtaskTest模块的getRunByCaseTest方法,参数是2, 2 - 属性task @2 - 属性case @2 -- 执行testtaskTest模块的getRunByCaseTest方法,参数是2, 3 +- 执行testtaskTest模块的getRunByCaseTest方法,参数是2, 3 - 属性task @2 - 属性case @3 -- 执行testtaskTest模块的getRunByCaseTest方法,参数是3, 4 +- 执行testtaskTest模块的getRunByCaseTest方法,参数是3, 4 - 属性task @3 - 属性case @4 diff --git a/module/testtask/test/model/initresult.php b/module/testtask/test/model/initresult.php index 965b240b7a..4f5ee424fa 100755 --- a/module/testtask/test/model/initresult.php +++ b/module/testtask/test/model/initresult.php @@ -7,7 +7,7 @@ title=测试 testtaskModel::initResult(); timeout=0 cid=19204 -- 执行testtaskTest模块的initResultTest方法,参数是$now +- 执行testtaskTest模块的initResultTest方法,参数是$now - 属性case @0 - 属性version @1 - 属性caseResult @pass diff --git a/module/testtask/test/model/processstepresults.php b/module/testtask/test/model/processstepresults.php index 23a06051ee..bf9668332d 100755 --- a/module/testtask/test/model/processstepresults.php +++ b/module/testtask/test/model/processstepresults.php @@ -41,7 +41,7 @@ $testtaskTest = new testtaskTest(); // 5. 执行测试步骤 r($testtaskTest->processStepResultsTest([1, 2], 1, 'pass', [], [])) && p('1:result') && e('pass'); // 步骤1:用例有步骤,结果为pass -r($testtaskTest->processStepResultsTest([1, 2], 1, 'fail', [1 => 'fail', 2 => 'pass', 3 => 'fail'], [1 => '实际结果1'])) && p('1:result') && e('fail'); // 步骤2:用例有步骤,结果为fail +r($testtaskTest->processStepResultsTest([1, 2], 1, 'fail', [1 => 'fail', 2 => 'pass', 3 => 'fail'], [1 => '实际结果1'])) && p('1:result') && e('fail'); // 步骤2:用例有步骤,结果为fail r($testtaskTest->processStepResultsTest([1, 2], 4, 'pass', [], [])) && p('0:result') && e('pass'); // 步骤3:用例无步骤,结果为pass r($testtaskTest->processStepResultsTest([1, 2], 4, 'fail', [], ['测试失败原因'])) && p('0:result') && e('fail'); // 步骤4:用例无步骤,结果为fail r($testtaskTest->processStepResultsTest([], 1, 'pass', [], [])) && p() && e('Array'); // 步骤5:空步骤数组测试 \ No newline at end of file diff --git a/module/testtask/test/model/yaml/testsuite_getexistsuitesofunitresult.yaml b/module/testtask/test/model/yaml/testsuite_getexistsuitesofunitresult.yaml index 35de10cd54..39d96cedd8 100644 --- a/module/testtask/test/model/yaml/testsuite_getexistsuitesofunitresult.yaml +++ b/module/testtask/test/model/yaml/testsuite_getexistsuitesofunitresult.yaml @@ -12,7 +12,7 @@ fields: note: 项目ID range: 1-3 - field: product - note: 产品ID + note: 产品ID range: 1-3 - field: name note: 测试套件名称 diff --git a/module/todo/test/lib/todo.unittest.class.php b/module/todo/test/lib/todo.unittest.class.php index b5d90bea14..08288b5635 100755 --- a/module/todo/test/lib/todo.unittest.class.php +++ b/module/todo/test/lib/todo.unittest.class.php @@ -365,7 +365,7 @@ class todoTest * @access public * @return int */ - public function getCycleListTest(bool $initCycle = true): int + public function getCycleListTest(bool $initCycle = true): array { $todoList = $this->objectModel->getValidCycleList(); @@ -373,7 +373,7 @@ class todoTest $cycleList = $this->objectModel->getCycleList($todoList); - return count($cycleList) > 0 ? 1 : 0; + return $cycleList; } /** @@ -581,7 +581,7 @@ class todoTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('dateRange'); $method->setAccessible(true); - + $result = $method->invoke($this->objectModel, $type); if(dao::isError()) return dao::getError(); diff --git a/module/todo/test/model/activate.php b/module/todo/test/model/activate.php index 89ff62e601..be28ebd145 100755 --- a/module/todo/test/model/activate.php +++ b/module/todo/test/model/activate.php @@ -21,7 +21,7 @@ su('admin'); zenData('todo')->gen(5); -$todoIDList = array('1', '2', '3'); +$todoIDList = range(1,5); $todo = new todoTest(); @@ -29,4 +29,4 @@ r($todo->activateTest(1)) && p('status') && e('wait'); // 激活一个状态为w r($todo->activateTest(2)) && p('status') && e('wait'); // 激活一个状态为doing的todo r($todo->activateTest(3)) && p('status') && e('wait'); // 激活一个状态为done的todo r($todo->activateTest(4)) && p('status') && e('wait'); // 激活一个状态为closed的todo -r($todo->activateTest(5)) && p('status') && e('wait'); // 激活一个状态为wait的todo +r($todo->activateTest(5)) && p('status') && e('wait'); // 激活一个状态为wait的todo \ No newline at end of file diff --git a/module/todo/test/model/batchfinish.php b/module/todo/test/model/batchfinish.php index 71246fa996..bbac9ec80c 100755 --- a/module/todo/test/model/batchfinish.php +++ b/module/todo/test/model/batchfinish.php @@ -36,4 +36,4 @@ $todo->batchFinishTest(array(1, 2, 3, 4)); r($todo->getByIdTest(1)) && p('status') && e('done'); // 获取批量完成后ID为1的待办状态 r($todo->getByIdTest(2)) && p('status') && e('done'); // 获取批量完成后ID为2的待办状态 r($todo->getByIdTest(3)) && p('status') && e('done'); // 获取批量完成后ID为3的待办状态 -r($todo->getByIdTest(4)) && p('status') && e('done'); // 获取批量完成后ID为4的待办状态 +r($todo->getByIdTest(4)) && p('status') && e('done'); // 获取批量完成后ID为4的待办状态 \ No newline at end of file diff --git a/module/todo/test/model/create.php b/module/todo/test/model/create.php index 8bf8a6284c..e1d073d465 100755 --- a/module/todo/test/model/create.php +++ b/module/todo/test/model/create.php @@ -57,4 +57,4 @@ r($todoTest->createTest($todoInvalidObjectID)) && p() && e('0'); // 判断创 dao::getError(); r($todoTest->createTest($todo)) && p() && e('6'); // 判断创建待办,返回结果id为6 r($todoTest->createTest($todoWithCycle)) && p() && e('7'); // 判断创建周期待办,返回结果id为7 -r($todoTest->createTest(null)) && p() && e('~~'); // 判断空待办 +r($todoTest->createTest(null)) && p() && e('~~'); // 判断空待办 \ No newline at end of file diff --git a/module/todo/test/model/createbycycle.php b/module/todo/test/model/createbycycle.php index 514d4673f5..a3ed14060b 100755 --- a/module/todo/test/model/createbycycle.php +++ b/module/todo/test/model/createbycycle.php @@ -38,6 +38,11 @@ $tester->loadModel('todo'); su('admin'); zenData('todo')->loadYaml('createbycycle')->gen(3); +global $tester; +$todos = $tester->loadModel('todo')->getList(); + +r(count($todos)) && p() && e('0'); // 测试前,待办数据为0 + $todo = new todoTest(); $todo->createByCycleTest(); $todoList = $tester->dao->select('*')->from(TABLE_TODO)->where('deleted')->eq('0')->fetchAll(); @@ -45,4 +50,4 @@ r($todoList) && p('0:id,name,status') && e('1,周期待办:提前1天,间隔 r($todoList) && p('1:id,name,status') && e('2,周期待办:每月的每天,提前1天生成,doing'); // 周期性待办ID为2的待办属性 r($todoList) && p('2:id,name,status') && e('3,周期待办:每周的每天,提前1天生成,doing'); // 周期性待办ID为3的待办属性 r($todoList) && p('3:id,name,status') && e('4,周期待办:每月的每天,提前1天生成,wait'); // 周期性待办ID为4的待办属性 -r($todoList) && p('8:id,name,status') && e('9,周期待办:每周的每天,提前1天生成,wait'); // 周期性待办ID为9的待办属性 +r($todoList) && p('8:id,name,status') && e('9,周期待办:每周的每天,提前1天生成,wait'); // 周期性待办ID为9的待办属性 \ No newline at end of file diff --git a/module/todo/test/model/editdate.php b/module/todo/test/model/editdate.php index 08594c27f8..d8929df620 100755 --- a/module/todo/test/model/editdate.php +++ b/module/todo/test/model/editdate.php @@ -39,4 +39,4 @@ r($todo) && p('date') && e('2023-02-27'); // 获取待办id为1的修改后的 $todo = $tester->todo->fetchByID(2); r($todo) && p('date') && e('2023-02-27'); // 获取待办id为2的修改后的日期 $todo = $tester->todo->fetchByID(3); -r($todo) && p('date') && e('2023-02-27'); // 获取待办id为3的修改后的日期 +r($todo) && p('date') && e('2023-02-27'); // 获取待办id为3的修改后的日期 \ No newline at end of file diff --git a/module/todo/test/model/getpribytodotype.php b/module/todo/test/model/getpribytodotype.php index c2b47b437c..f0ac4bd55b 100755 --- a/module/todo/test/model/getpribytodotype.php +++ b/module/todo/test/model/getpribytodotype.php @@ -30,4 +30,4 @@ r($todoTest->getPriByTodoTypeTest('bug', 1)) && p() && e('1'); // 获取类 r($todoTest->getPriByTodoTypeTest('task', 3)) && p() && e('3'); // 获取类型为bug,ID为1对象的优先级,结果为3 r($todoTest->getPriByTodoTypeTest('story', 5)) && p() && e('1'); // 获取类型为bug,ID为1对象的优先级,结果为1 r($todoTest->getPriByTodoTypeTest('testtask', 7)) && p() && e('3'); // 获取类型为bug,ID为1对象的优先级,结果为3 -r($todoTest->getPriByTodoTypeTest('bug', 9)) && p() && e('1'); // 获取类型为bug,ID为1对象的优先级,结果为1 +r($todoTest->getPriByTodoTypeTest('bug', 9)) && p() && e('1'); // 获取类型为bug,ID为1对象的优先级,结果为1 \ No newline at end of file diff --git a/module/todo/test/model/gettodoprojects.php b/module/todo/test/model/gettodoprojects.php index b50c1d04a8..326f8e3173 100755 --- a/module/todo/test/model/gettodoprojects.php +++ b/module/todo/test/model/gettodoprojects.php @@ -36,4 +36,4 @@ r(count($projectList['task'])) && p() && e('4'); // 验证task获得的键 r($projectList['task']) && p('1') && e('11'); // 验证taskID为1对应的projectID r($projectList['task']) && p('2') && e('12'); // 验证taskID为2对应的projectID r($projectList['task']) && p('3') && e('13'); // 验证taskID为3对应的projectID -r($projectList['task']) && p('4') && e('14'); // 验证taskID为4对应的projectID +r($projectList['task']) && p('4') && e('14'); // 验证taskID为4对应的projectID \ No newline at end of file diff --git a/module/todo/test/model/start.php b/module/todo/test/model/start.php index abc229253f..a4698e3822 100755 --- a/module/todo/test/model/start.php +++ b/module/todo/test/model/start.php @@ -26,4 +26,4 @@ r($todo->startTest(1)) && p('status') && e('doing'); //开启一个状态为wait r($todo->startTest(2)) && p('status') && e('doing'); //开启一个状态为doing的todo r($todo->startTest(3)) && p('status') && e('doing'); //开启一个状态为done的todo r($todo->startTest(4)) && p('status') && e('doing'); //开启一个状态为closed的todo -r($todo->startTest(5)) && p('status') && e('doing'); //开启一个状态为wait的todo +r($todo->startTest(5)) && p('status') && e('doing'); //开启一个状态为wait的todo \ No newline at end of file diff --git a/module/todo/test/model/yaml/activate/activate.yaml b/module/todo/test/model/yaml/activate/activate.yaml index 4b6f45b8c2..e9367baf2b 100755 --- a/module/todo/test/model/yaml/activate/activate.yaml +++ b/module/todo/test/model/yaml/activate/activate.yaml @@ -3,9 +3,9 @@ author: shenshulong version: '1.0' fields: - field: id - range: '1-3' + range: '1-5' - field: account - range: '1-3' + range: '1-5' prefix: 'admin' - field: begin range: '1710' @@ -16,23 +16,23 @@ fields: - field: pri range: "3" - field: name - range: '1-3' + range: '1-5' prefix: '测试待办' - field: desc range: '描述' - field: status range: 'wait,doing,done' - field: assignedTo - range: '1-3' + range: '1-5' prefix: 'admin' - field: assignedBy - range: '1-3' + range: '1-5' prefix: 'admin' - field: finishedBy - range: '1-3' + range: '1-5' prefix: 'admin' - field: closedBy - range: '1-3' + range: '1-5' prefix: 'admin' - field: vision range: '1.0' diff --git a/module/todo/test/tao/closetodo.php b/module/todo/test/tao/closetodo.php old mode 100644 new mode 100755 index 3adc740492..e707604c05 --- a/module/todo/test/tao/closetodo.php +++ b/module/todo/test/tao/closetodo.php @@ -4,6 +4,7 @@ include dirname(__FILE__, 5) . '/test/lib/init.php'; include dirname(__FILE__, 2) . '/lib/todo.unittest.class.php'; /** + title=测试Tao层的关闭待办 todoTao::closeTodo() timeout=0 cid=19269 @@ -41,4 +42,4 @@ r($todo->closeTodoTest(1)) && p('oldStatus,newStatus,isClosed') && e('wait,close r($todo->closeTodoTest(2)) && p('oldStatus,newStatus,isClosed') && e('doing,closed,1'); // 将wait状态更新为close状态并判断是否关闭成功 r($todo->closeTodoTest(3)) && p('oldStatus,newStatus,isClosed') && e('done,closed,1'); // 将wait状态更新为close状态并判断是否关闭成功 r($todo->closeTodoTest(4)) && p('oldStatus,newStatus,isClosed') && e('closed,closed,1'); // 将wait状态更新为close状态并判断是否关闭成功 -r($todo->closeTodoTest(5)) && p('oldStatus,newStatus,isClosed') && e('wait,closed,1'); // 将wait状态更新为close状态并判断是否关闭成功 +r($todo->closeTodoTest(5)) && p('oldStatus,newStatus,isClosed') && e('wait,closed,1'); // 将wait状态更新为close状态并判断是否关闭成功 \ No newline at end of file diff --git a/module/todo/test/tao/fetch.php b/module/todo/test/tao/fetch.php index 8068816464..87c8504619 100755 --- a/module/todo/test/tao/fetch.php +++ b/module/todo/test/tao/fetch.php @@ -67,4 +67,4 @@ r($tester->todo->fetch(3)) && p('id,status,account,type,name') && e('3,done,use r($tester->todo->fetch(5)) && p('id,status,account,type,name') && e('5,wait,user4,testtask,测试单5的待办'); // 查询id=5的todo属性 r($tester->todo->fetch(7)) && p('id,status,account,type,name') && e('7,done,user6,bug,BUG7的待办'); // 查询id=7的todo属性 r($tester->todo->fetch(9)) && p('id,status,account,type,name') && e('9,wait,user8,story,需求9的待办'); // 查询id=9的todo属性 -r($tester->todo->fetch(10)) && p('id,status,account,type,name') && e('10,doing,user9,testtask,测试单10的待办'); // 查询id=10的todo属性 +r($tester->todo->fetch(10)) && p('id,status,account,type,name') && e('10,doing,user9,testtask,测试单10的待办'); // 查询id=10的todo属性 \ No newline at end of file diff --git a/module/todo/test/tao/fetchrows.php b/module/todo/test/tao/fetchrows.php index efbec854d7..30049cbdd5 100755 --- a/module/todo/test/tao/fetchrows.php +++ b/module/todo/test/tao/fetchrows.php @@ -51,4 +51,4 @@ r($todoTest->fetchRowsTest(array(999 => 'test', 1000 => 'test'))) && p() && e('0 r($todoTest->fetchRowsTest(array())) && p() && e('0'); // 测试步骤5:获取单个待办记录 -r($todoTest->fetchRowsTest(array(1 => 'single'))) && p('1:id') && e('1'); \ No newline at end of file +r($todoTest->fetchRowsTest(array(1 => 'single'))) && p('1:id') && e('1'); diff --git a/module/todo/test/tao/getcyclelist.php b/module/todo/test/tao/getcyclelist.php index 5e250aa58c..ca6cf32f70 100755 --- a/module/todo/test/tao/getcyclelist.php +++ b/module/todo/test/tao/getcyclelist.php @@ -13,6 +13,14 @@ cid=19274 - 验证按ID倒序排序后第一个key是10 @10 - 验证混合输入只返回2条有效记录 @2 +- 获取通过周期待办的生成的待办数据为空的情况,结果为 0 @0 +- 获取有周期待办生成的待办数量,结果为 3 @3 +- 获取有周期待办生成的待办数据详细信息 + - 第2条的account属性 @admin + - 第2条的name属性 @周期待办:每月的每天,提前1天生成 + - 第2条的begin属性 @0801 + - 第2条的end属性 @1801 + */ include dirname(__FILE__, 5) . '/test/lib/init.php'; @@ -48,4 +56,4 @@ r(key($result4)) && p() && e('10'); // 验证按ID倒序排序后第一个key是 // 测试步骤5:测试混合有效无效objectID的todoList $mixedTodoList = array(1 => 'todo1', 999 => 'invalid', 6 => 'todo6', 1000 => 'invalid2'); $result5 = $todoTest->getCycleListTaoTest($mixedTodoList); -r(count($result5)) && p() && e('2'); // 验证混合输入只返回2条有效记录 \ No newline at end of file +r(count($result5)) && p() && e('2'); // 验证混合输入只返回2条有效记录 diff --git a/module/todo/test/tao/getcycletododate.php b/module/todo/test/tao/getcycletododate.php index 59645403ac..cab8bc72f0 100755 --- a/module/todo/test/tao/getcycletododate.php +++ b/module/todo/test/tao/getcycletododate.php @@ -15,6 +15,12 @@ cid=19275 - 空lastCycle按天类型,不符合间隔返回0 @0 - 过期配置应返回false或empty @0 +- 获取类型为按天生成的周期待办的日期,结果1 @1 +- 获取类型为按周生成的周期待办的日期,结果1 @1 +- 获取类型为按月生成的周期待办的日期,结果1 @1 +- 生成周期为按天生成的周期待办的日期,结果0 @0 +- 生成周期为空的周期待办的日期,结果0 @0 + */ include dirname(__FILE__, 5) . '/test/lib/init.php'; @@ -48,4 +54,4 @@ r($todoTest->getCycleTodoDateTestEdgeCase('invalid_type')) && p() && e('0'); // r($todoTest->getCycleTodoDateTestEdgeCase('empty_lastcycle')) && p() && e('0'); // 空lastCycle按天类型,不符合间隔返回0 // 测试步骤7:测试过期配置的情况 -r($todoTest->getCycleTodoDateTestEdgeCase('past_config')) && p() && e('0'); // 过期配置应返回false或empty \ No newline at end of file +r($todoTest->getCycleTodoDateTestEdgeCase('past_config')) && p() && e('0'); // 过期配置应返回false或empty diff --git a/module/todo/test/tao/getlistby.php b/module/todo/test/tao/getlistby.php index 98903efa31..9d9f8e5d53 100755 --- a/module/todo/test/tao/getlistby.php +++ b/module/todo/test/tao/getlistby.php @@ -40,4 +40,4 @@ r(is_array($todo->getListByTest('today', 'admin', 'all', '2025-08-24', '2025-08- r(count($todo->getListByTest('assignedtoother', 'admin', 'all', '', '', 0, 'date_desc'))) && p() && e('0'); // 获取空结果的待办列表 r(count($todo->getListByTest('today', 'admin', 'all', '2025-08-24', '2025-08-25', 0, 'date_desc'))) && p() && e('2'); // 按日期范围过滤待办列表 r(count($todo->getListByTest('today', 'admin', 'wait', '2025-08-24', '2025-08-25', 0, 'date_desc'))) && p() && e('2'); // 按状态过滤待办列表 -r(count($todo->getListByTest('today', 'admin', 'all', '2025-08-24', '2025-08-25', 1, 'date_desc'))) && p() && e('1'); // 限制返回数量的待办列表 \ No newline at end of file +r(count($todo->getListByTest('today', 'admin', 'all', '2025-08-24', '2025-08-25', 1, 'date_desc'))) && p() && e('1'); // 限制返回数量的待办列表 diff --git a/module/todo/test/tao/getprojectlist.php b/module/todo/test/tao/getprojectlist.php index c6d104a5ce..5c22e30a62 100755 --- a/module/todo/test/tao/getprojectlist.php +++ b/module/todo/test/tao/getprojectlist.php @@ -22,6 +22,12 @@ cid=19277 - 属性4 @2 - 步骤5:验证返回数组长度 @3 +- 验证task获得的键值对的个数 @1 +- 验证taskID为10的project为20属性10 @20 +- 验证project获得的键值对的个数 @1 +- 获取不到的情况属性10 @0 +- 验证bugID为10的project为24属性10 @14 + */ include dirname(__FILE__, 5) . '/test/lib/init.php'; @@ -57,4 +63,4 @@ r($todoTest->getProjectListTest('zt_task', array(1 => 1, 2 => 2, 3 => 3))) && p( r($todoTest->getProjectListTest('zt_task', array())) && p() && e(0); // 步骤2:空ID列表 r($todoTest->getProjectListTest('zt_bug', array(1 => 1, 3 => 3, 5 => 5))) && p('1,3,5') && e('2,2,3'); // 步骤3:bug表查询 r($todoTest->getProjectListTest('zt_testtask', array(1 => 1, 2 => 2, 4 => 4))) && p('1,2,4') && e('1,1,2'); // 步骤4:testtask表查询 -r(count($todoTest->getProjectListTest('zt_task', array(6 => 6, 8 => 8, 10 => 10)))) && p() && e('3'); // 步骤5:验证返回数组长度 \ No newline at end of file +r(count($todoTest->getProjectListTest('zt_task', array(6 => 6, 8 => 8, 10 => 10)))) && p() && e('3'); // 步骤5:验证返回数组长度 diff --git a/module/todo/test/tao/gettodocountbyaccount.php b/module/todo/test/tao/gettodocountbyaccount.php index fa72448c27..927cb6a65a 100755 --- a/module/todo/test/tao/gettodocountbyaccount.php +++ b/module/todo/test/tao/gettodocountbyaccount.php @@ -45,4 +45,4 @@ r($todoTest->getTodoCountByAccountTest('user2')) && p() && e('5'); // 步 r($todoTest->getTodoCountByAccountTest('user5')) && p() && e('2'); // 步骤4:user5用户待办数量 r($todoTest->getTodoCountByAccountTest('')) && p() && e('10'); // 步骤5:空用户名(匹配所有条件) r($todoTest->getTodoCountByAccountTest('test')) && p() && e('6'); // 步骤6:test用户待办数量 -r($todoTest->getTodoCountByAccountTest('nonexist')) && p() && e('0'); // 步骤7:不存在的用户 \ No newline at end of file +r($todoTest->getTodoCountByAccountTest('nonexist')) && p() && e('0'); // 步骤7:不存在的用户 diff --git a/module/todo/test/tao/settodonamebytype.php b/module/todo/test/tao/settodonamebytype.php index 615c6d3fff..b0db887380 100755 --- a/module/todo/test/tao/settodonamebytype.php +++ b/module/todo/test/tao/settodonamebytype.php @@ -91,4 +91,4 @@ r($todoTest->setTodoNameByTypeTest(5)) && p('name') && e('Original Todo Name'); r($todoTest->setTodoNameByTypeTest(6)) && p('name') && e('Custom Todo Name'); // 测试步骤7:业务规则测试当关联对象名称为空时的回退逻辑 -r($todoTest->setTodoNameByTypeTest(7)) && p('name') && e('Empty Object Todo Name'); \ No newline at end of file +r($todoTest->setTodoNameByTypeTest(7)) && p('name') && e('Empty Object Todo Name'); diff --git a/module/todo/test/tao/updatedate.php b/module/todo/test/tao/updatedate.php index ceae380900..c9151fa390 100755 --- a/module/todo/test/tao/updatedate.php +++ b/module/todo/test/tao/updatedate.php @@ -40,4 +40,4 @@ r($todoTest->updateDateTest($singleTodoList, $validDate1)) && p() && e('1'); r($todoTest->updateDateTest($multipleTodoList, $validDate2)) && p() && e('1'); r($todoTest->updateDateTest($emptyTodoList, $validDateTime)) && p() && e('1'); r($todoTest->updateDateTest($nonExistentList, $validDate1)) && p() && e('1'); -r($todoTest->updateDateTest($validTodoList, $edgeDate)) && p() && e('1'); \ No newline at end of file +r($todoTest->updateDateTest($validTodoList, $edgeDate)) && p() && e('1'); diff --git a/module/todo/test/tao/updaterow.php b/module/todo/test/tao/updaterow.php index 2c263f52bc..4bfaf3d820 100755 --- a/module/todo/test/tao/updaterow.php +++ b/module/todo/test/tao/updaterow.php @@ -76,4 +76,4 @@ r($tester->todo->updateRow(3, $invalidCustomTodoNoName)) && p() && e('0'); // r($tester->todo->updateRow(4, $invalidBugTodoNoObjectID)) && p() && e('0'); // 步骤4:更新bug类型待办缺少objectID r($tester->todo->updateRow(999, $validCustomTodo)) && p() && e('0'); // 步骤5:更新不存在的待办ID r($tester->todo->updateRow(5, $validCustomTodo)) && p() && e('1'); // 步骤6:重复正常更新测试 -r($tester->todo->updateRow(6, $validStoryTodo)) && p() && e('1'); // 步骤7:story类型待办更新测试 \ No newline at end of file +r($tester->todo->updateRow(6, $validStoryTodo)) && p() && e('1'); // 步骤7:story类型待办更新测试 diff --git a/module/transfer/test/lib/transfer.unittest.class.php b/module/transfer/test/lib/transfer.unittest.class.php index 21014f2358..c6906fc153 100755 --- a/module/transfer/test/lib/transfer.unittest.class.php +++ b/module/transfer/test/lib/transfer.unittest.class.php @@ -399,11 +399,11 @@ class transferTest // 设置cookie值 if($maxImport !== null) { - if($maxImport === '') + if($maxImport === '') { unset($_COOKIE['maxImport']); } - else + else { $_COOKIE['maxImport'] = (string)$maxImport; } @@ -412,10 +412,10 @@ class transferTest // 由于模型已经被加载,我们需要重新创建一个实例来测试构造函数 // 这里我们直接模拟构造函数的逻辑 $result = new stdClass(); - + // 模拟构造函数中的maxImport逻辑 $result->maxImport = isset($_COOKIE['maxImport']) ? (int)$_COOKIE['maxImport'] : 0; - + // 检查transfer配置是否存在 $transfer = $tester->loadModel('transfer'); $result->transferConfig = is_object($transfer->transferConfig) ? 1 : 0; @@ -466,7 +466,7 @@ class transferTest */ public function getPageDatasTest(array $datas = array(), int $pagerID = 1) { - try + try { $result = $this->objectModel->getPageDatas($datas, $pagerID); if(dao::isError()) return dao::getError(); @@ -619,7 +619,7 @@ class transferTest $originalSessionFileName = isset($_SESSION['fileImportFileName']) ? $_SESSION['fileImportFileName'] : null; $originalMaxImport = isset($_COOKIE['maxImport']) ? $_COOKIE['maxImport'] : null; - try + try { // 获取临时文件路径 $tmpPath = $tester->loadModel('file')->getPathOfImportedFile(); @@ -708,7 +708,7 @@ class transferTest { return '0'; } - + return $result; } catch(Exception $e) @@ -739,7 +739,7 @@ class transferTest // 清理测试文件 if(isset($testFile) && file_exists($testFile)) unlink($testFile); if(isset($tmpFile) && file_exists($tmpFile)) unlink($tmpFile); - + // 清理可能生成的临时文件(基于文件名MD5) $patterns = array('test_file.csv', 'default_test.csv', 'nonexistent_file.csv'); foreach($patterns as $pattern) @@ -766,7 +766,7 @@ class transferTest $originalSessionFileName = isset($_SESSION['fileImportFileName']) ? $_SESSION['fileImportFileName'] : null; $originalSessionExtension = isset($_SESSION['fileImportExtension']) ? $_SESSION['fileImportExtension'] : null; - try + try { // 清除之前的错误状态 dao::$errors = array(); @@ -790,7 +790,7 @@ class transferTest { // 测试场景3:处理空文件名 $_SESSION['fileImportFileName'] = ''; - + // 由于空文件名会导致错误,模拟这种情况 dao::$errors['message'] = 'Empty filename'; return 'false'; @@ -799,7 +799,7 @@ class transferTest { // 测试场景4:处理不存在的文件 $_SESSION['fileImportFileName'] = '/nonexistent/path/file.xlsx'; - + // 模拟文件不存在的错误 dao::$errors['message'] = 'File does not exist'; return 'false'; @@ -809,7 +809,7 @@ class transferTest // 测试场景5:验证错误清理机制 $tmpPath = $tester->loadModel('file')->getPathOfImportedFile(); $testFile = $tmpPath . '/cleanup_test.xlsx'; - + // 创建测试文件和session if(!is_dir($tmpPath)) mkdir($tmpPath, 0755, true); file_put_contents($testFile, 'test content for cleanup'); @@ -821,7 +821,7 @@ class transferTest unset($_SESSION['fileImportFileName']); unset($_SESSION['fileImportExtension']); dao::$errors['message'] = 'Error for cleanup test'; - + return 'false'; } @@ -857,11 +857,11 @@ class transferTest { $testFiles = array( $tmpPath . '/valid_test.csv', - $tmpPath . '/error_test.xlsx', + $tmpPath . '/error_test.xlsx', $tmpPath . '/cleanup_test.xlsx', $tmpPath . '/nonexistent_file.xlsx' ); - + foreach($testFiles as $file) { if(file_exists($file)) unlink($file); @@ -886,7 +886,7 @@ class transferTest $originalSessionFileName = isset($_SESSION['fileImportFileName']) ? $_SESSION['fileImportFileName'] : null; $originalSessionExtension = isset($_SESSION['fileImportExtension']) ? $_SESSION['fileImportExtension'] : null; - try + try { // 清除之前的错误状态 dao::$errors = array(); @@ -894,7 +894,7 @@ class transferTest // 设置临时文件路径用于可能的清理操作 $tmpPath = $tester->loadModel('file')->getPathOfImportedFile(); if(!is_dir($tmpPath)) mkdir($tmpPath, 0755, true); - + $testFile = $tmpPath . '/test_processrows.csv'; file_put_contents($testFile, 'test content'); $_SESSION['fileImportFileName'] = $testFile; diff --git a/module/tree/control.php b/module/tree/control.php index 6f42b96547..deff438647 100644 --- a/module/tree/control.php +++ b/module/tree/control.php @@ -321,6 +321,36 @@ class tree extends control } } + /** + * 新增一个模块。 + * Create a module. + * + * @param int $rootID + * @param string $viewType + * @access public + * @return void + */ + public function create(int $rootID, string $viewType) + { + if(!empty($_POST)) + { + if(empty($_POST['name'])) return $this->sendError(array('name' => sprintf($this->lang->error->notempty, $this->lang->tree->name))); + + $count = $this->dao->select(`order`)->from(TABLE_MODULE)->where('type')->eq($viewType)->andWhere('root')->eq($rootID)->andWhere('parent')->eq('0')->count(); + $_POST['parentModuleID'] = 0; + $_POST['modules'] = array($_POST['name']); + $_POST['shorts'] = array($_POST['short']); + $_POST['maxOrder'] = $count ? $count * 10 : 0; + + $this->tree->manageChild($rootID, $viewType); + if(dao::isError()) return $this->sendError(dao::getError()); + + return $this->sendSuccess(array('load' => true, 'closeModel' => true)); + } + $this->view->title = $this->lang->tree->create; + $this->display(); + } + /** * 查看模块历史记录。 * View module histories. diff --git a/module/tree/lang/de.php b/module/tree/lang/de.php index 6fdd8761e1..5bc42a8c25 100755 --- a/module/tree/lang/de.php +++ b/module/tree/lang/de.php @@ -10,61 +10,65 @@ * @link https://www.zentao.net */ $lang->tree = new stdclass(); -$lang->tree->common = 'Modul'; -$lang->tree->edit = 'Bearbeiten'; -$lang->tree->delete = 'Löschen'; -$lang->tree->browse = 'Allgemeine Module verwalten'; -$lang->tree->browseTask = 'Aufgaben Modul verwalten'; -$lang->tree->manage = 'Modul verwalten'; -$lang->tree->fix = 'Fix'; -$lang->tree->manageProduct = "Verwalte {$lang->productCommon}"; -$lang->tree->manageProject = "Verwalte {$lang->projectCommon}"; -$lang->tree->manageExecution = "Verwalte {$lang->execution->common}"; -$lang->tree->manageLine = "Manage Product Line"; -$lang->tree->manageBug = 'Verwalte Bugs'; -$lang->tree->manageCase = 'Verwalte Fälle'; -$lang->tree->manageCaselib = 'Verwalte Bibliothek'; -$lang->tree->manageCustomDoc = 'Verwalte Dokumente'; -$lang->tree->manageApiChild = 'Manage API Directory'; -$lang->tree->manageGroup = 'Manage group'; -$lang->tree->updateOrder = 'Sortierung'; -$lang->tree->manageChild = 'Verwalte Untermodul'; -$lang->tree->manageStoryChild = 'Verwalte Untermodul'; -$lang->tree->manageLineChild = 'Verwalte Produktlinie'; -$lang->tree->manageBugChild = 'Verwalte Bugs'; -$lang->tree->manageCaseChild = 'Verwalte Fälle'; -$lang->tree->manageCaselibChild = 'Verwalte Bibliothek'; -$lang->tree->manageDashboard = 'Manage Dashboard Module'; -$lang->tree->manageDashboardChild = 'Manage Dashboard Child Module'; -$lang->tree->manageProjectChild = "Verwalte {$lang->projectCommon}"; -$lang->tree->manageTaskChild = "Verwalte {$lang->execution->common}"; -$lang->tree->syncFromProduct = "Copy from Other {$lang->productCommon}s"; -$lang->tree->dragAndSort = "Ziehen und Sotieren"; -$lang->tree->sort = "Sortieren"; -$lang->tree->addChild = "Hinzufügen"; -$lang->tree->confirmDelete = 'Do you want to delete this module and its child modules?'; -$lang->tree->confirmDeleteMenu = 'Do you want to delete this menu and its child menus?'; -$lang->tree->confirmDelCategory = 'Möchten Sie diese Kategorie und ihre Kinderkategorien löschen?'; -$lang->tree->confirmDeleteLine = "Do you want to delete this product line?"; -$lang->tree->confirmDeleteGroup = 'Do you want to delete this group and its child groups?'; -$lang->tree->confirmRoot = "Any changes to the {$lang->productCommon} will change the stories, bugs, cases of {$lang->productCommon} it belongs to, as well as the linkage of {$lang->executionCommon} and {$lang->productCommon}, which is dangerous. Do you want to change it?"; -$lang->tree->confirmRoot4Doc = "Any changes to the library will change the document of library it belongs to, which is dangerous. Do you want to change it?"; -$lang->tree->noSubmodule = "There are no copyable submodules under the current module!"; -$lang->tree->successSave = 'Saved.'; -$lang->tree->successFixed = 'Fixed.'; -$lang->tree->repeatName = 'The name "%s" exists!'; -$lang->tree->repeatDirName = 'The name "%s" exists!'; -$lang->tree->shouldNotBlank = 'Module name should not be blank!'; -$lang->tree->syncProductModule = 'Sync Module'; -$lang->tree->host = 'Host'; -$lang->tree->editHost = 'Edit host group'; -$lang->tree->deleteHost = 'Delete host group'; -$lang->tree->manageHostChild = 'Manage child host'; -$lang->tree->groupMaintenance = 'Manage host group'; -$lang->tree->groupName = 'Group Name'; -$lang->tree->parentGroup = 'Parent grpup'; -$lang->tree->childGroup = 'Child'; -$lang->tree->confirmDeleteHost = 'Do you want to delete this host and its child hosts?'; +$lang->tree->common = 'Modul'; +$lang->tree->edit = 'Bearbeiten'; +$lang->tree->create = 'Erstellen'; +$lang->tree->delete = 'Löschen'; +$lang->tree->browse = 'Allgemeine Module verwalten'; +$lang->tree->browseTask = 'Aufgaben Modul verwalten'; +$lang->tree->manage = 'Modul verwalten'; +$lang->tree->fix = 'Fix'; +$lang->tree->manageProduct = "Verwalte {$lang->productCommon}"; +$lang->tree->manageProject = "Verwalte {$lang->projectCommon}"; +$lang->tree->manageExecution = "Verwalte {$lang->execution->common}"; +$lang->tree->manageLine = "Manage Product Line"; +$lang->tree->manageBug = 'Verwalte Bugs'; +$lang->tree->manageCase = 'Verwalte Fälle'; +$lang->tree->manageCaselib = 'Verwalte Bibliothek'; +$lang->tree->manageCustomDoc = 'Verwalte Dokumente'; +$lang->tree->manageApiChild = 'Manage API Directory'; +$lang->tree->manageGroup = 'Manage group'; +$lang->tree->updateOrder = 'Sortierung'; +$lang->tree->manageChild = 'Verwalte Untermodul'; +$lang->tree->manageStoryChild = 'Verwalte Untermodul'; +$lang->tree->manageLineChild = 'Verwalte Produktlinie'; +$lang->tree->manageBugChild = 'Verwalte Bugs'; +$lang->tree->manageCaseChild = 'Verwalte Fälle'; +$lang->tree->manageCaselibChild = 'Verwalte Bibliothek'; +$lang->tree->manageDashboard = 'Manage Dashboard Module'; +$lang->tree->manageDashboardChild = 'Manage Dashboard Child Module'; +$lang->tree->manageProjectChild = "Verwalte {$lang->projectCommon}"; +$lang->tree->manageTaskChild = "Verwalte {$lang->execution->common}"; +$lang->tree->manageDeliverableChild = "Manage Deliverable Category"; +$lang->tree->syncFromProduct = "Copy from Other {$lang->productCommon}s"; +$lang->tree->dragAndSort = "Ziehen und Sotieren"; +$lang->tree->sort = "Sortieren"; +$lang->tree->addChild = "Hinzufügen"; +$lang->tree->confirmDelete = 'Do you want to delete this module and its child modules?'; +$lang->tree->confirmDeleteMenu = 'Do you want to delete this menu and its child menus?'; +$lang->tree->confirmDelCategory = 'Möchten Sie diese Kategorie und ihre Kinderkategorien löschen?'; +$lang->tree->confirmDeleteLine = "Do you want to delete this product line?"; +$lang->tree->confirmDeleteGroup = 'Do you want to delete this group and its child groups?'; +$lang->tree->confirmRoot = "Any changes to the {$lang->productCommon} will change the stories, bugs, cases of {$lang->productCommon} it belongs to, as well as the linkage of {$lang->executionCommon} and {$lang->productCommon}, which is dangerous. Do you want to change it?"; +$lang->tree->confirmRoot4Doc = "Any changes to the library will change the document of library it belongs to, which is dangerous. Do you want to change it?"; +$lang->tree->noSubmodule = "There are no copyable submodules under the current module!"; +$lang->tree->successSave = 'Saved.'; +$lang->tree->successFixed = 'Fixed.'; +$lang->tree->repeatName = 'The name "%s" exists!'; +$lang->tree->repeatDirName = 'The name "%s" exists!'; +$lang->tree->shouldNotBlank = 'Module name should not be blank!'; +$lang->tree->syncProductModule = 'Sync Module'; +$lang->tree->host = 'Host'; +$lang->tree->editHost = 'Edit host group'; +$lang->tree->deleteHost = 'Delete host group'; +$lang->tree->manageHostChild = 'Manage child host'; +$lang->tree->groupMaintenance = 'Manage host group'; +$lang->tree->groupName = 'Group Name'; +$lang->tree->parentGroup = 'Parent grpup'; +$lang->tree->childGroup = 'Child'; +$lang->tree->confirmDeleteHost = 'Do you want to delete this host and its child hosts?'; +$lang->tree->designModule = 'Design'; +$lang->tree->otherModule = 'Other'; $lang->tree->module = 'Modul'; $lang->tree->name = 'Name'; diff --git a/module/tree/lang/en.php b/module/tree/lang/en.php index 59e70b8101..48250680d9 100755 --- a/module/tree/lang/en.php +++ b/module/tree/lang/en.php @@ -10,61 +10,65 @@ * @link https://www.zentao.net */ $lang->tree = new stdclass(); -$lang->tree->common = 'Module'; -$lang->tree->edit = 'Edit Module'; -$lang->tree->delete = 'Delete Module'; -$lang->tree->browse = 'Manage General Module'; -$lang->tree->browseTask = 'Manage Task Module'; -$lang->tree->manage = 'Manage Module'; -$lang->tree->fix = 'Fix Module'; -$lang->tree->manageProduct = "Manage {$lang->productCommon} Modules"; -$lang->tree->manageProject = "Manage {$lang->projectCommon} Modules"; -$lang->tree->manageExecution = "Manage {$lang->execution->common} Modules"; -$lang->tree->manageLine = "Manage Product Line"; -$lang->tree->manageBug = 'Manage Bug'; -$lang->tree->manageCase = 'Manage Case'; -$lang->tree->manageCaselib = 'Manage Library'; -$lang->tree->manageCustomDoc = 'Manage Document Library'; -$lang->tree->manageApiChild = 'Manage API Directory'; -$lang->tree->manageGroup = 'Manage group'; -$lang->tree->updateOrder = 'Rank Module'; -$lang->tree->manageChild = 'Manage Child Modules'; -$lang->tree->manageStoryChild = 'Manage Child Modules'; -$lang->tree->manageLineChild = "Manage Product Line"; -$lang->tree->manageBugChild = 'Manage Child Modules of Bugs'; -$lang->tree->manageCaseChild = 'Manage Child Cases'; -$lang->tree->manageCaselibChild = 'Manage Child Libraries'; -$lang->tree->manageDashboard = 'Manage Dashboard Module'; -$lang->tree->manageDashboardChild = 'Manage Dashboard Child Module'; -$lang->tree->manageProjectChild = "Manage Child {$lang->projectCommon} Modules"; -$lang->tree->manageTaskChild = "Manage Child {$lang->execution->common} Modules"; -$lang->tree->syncFromProduct = "Copy from Other {$lang->productCommon}s"; -$lang->tree->dragAndSort = "Drag to order"; -$lang->tree->sort = "Order"; -$lang->tree->addChild = "Add Child Module"; -$lang->tree->confirmDelete = 'Do you want to delete this module and its child modules?'; -$lang->tree->confirmDeleteMenu = 'Do you want to delete this menu and its child menus?'; -$lang->tree->confirmDelCategory = 'Do you want to delete this category and its child categories?'; -$lang->tree->confirmDeleteLine = "Do you want to delete this product line?"; -$lang->tree->confirmDeleteGroup = 'Do you want to delete this group and its child groups?'; -$lang->tree->confirmRoot = "Any changes to the {$lang->productCommon} will change the stories, bugs, cases of {$lang->productCommon} it belongs to, as well as the linkage of {$lang->executionCommon} and {$lang->productCommon}, which is dangerous. Do you want to change it?"; -$lang->tree->confirmRoot4Doc = "Any changes to the library will change the document of library it belongs to, which is dangerous. Do you want to change it?"; -$lang->tree->noSubmodule = "There are no copyable submodules under the current module!"; -$lang->tree->successSave = 'Saved.'; -$lang->tree->successFixed = 'Fixed.'; -$lang->tree->repeatName = 'The name "%s" exists!'; -$lang->tree->repeatDirName = 'The name "%s" exists!'; -$lang->tree->shouldNotBlank = 'Module name should not be blank!'; -$lang->tree->syncProductModule = 'Sync Module'; -$lang->tree->host = 'Host'; -$lang->tree->editHost = 'Edit host group'; -$lang->tree->deleteHost = 'Delete host group'; -$lang->tree->manageHostChild = 'Manage child host'; -$lang->tree->groupMaintenance = 'Manage host group'; -$lang->tree->groupName = 'Group Name'; -$lang->tree->parentGroup = 'Parent grpup'; -$lang->tree->childGroup = 'Child'; -$lang->tree->confirmDeleteHost = 'Do you want to delete this host and its child hosts?'; +$lang->tree->common = 'Module'; +$lang->tree->create = 'Create Module'; +$lang->tree->edit = 'Edit Module'; +$lang->tree->delete = 'Delete Module'; +$lang->tree->browse = 'Manage General Module'; +$lang->tree->browseTask = 'Manage Task Module'; +$lang->tree->manage = 'Manage Module'; +$lang->tree->fix = 'Fix Module'; +$lang->tree->manageProduct = "Manage {$lang->productCommon} Modules"; +$lang->tree->manageProject = "Manage {$lang->projectCommon} Modules"; +$lang->tree->manageExecution = "Manage {$lang->execution->common} Modules"; +$lang->tree->manageLine = "Manage Product Line"; +$lang->tree->manageBug = 'Manage Bug'; +$lang->tree->manageCase = 'Manage Case'; +$lang->tree->manageCaselib = 'Manage Library'; +$lang->tree->manageCustomDoc = 'Manage Document Library'; +$lang->tree->manageApiChild = 'Manage API Directory'; +$lang->tree->manageGroup = 'Manage group'; +$lang->tree->updateOrder = 'Rank Module'; +$lang->tree->manageChild = 'Manage Child Modules'; +$lang->tree->manageStoryChild = 'Manage Child Modules'; +$lang->tree->manageLineChild = "Manage Product Line"; +$lang->tree->manageBugChild = 'Manage Child Modules of Bugs'; +$lang->tree->manageCaseChild = 'Manage Child Cases'; +$lang->tree->manageCaselibChild = 'Manage Child Libraries'; +$lang->tree->manageDashboard = 'Manage Dashboard Module'; +$lang->tree->manageDashboardChild = 'Manage Dashboard Child Module'; +$lang->tree->manageProjectChild = "Manage Child {$lang->projectCommon} Modules"; +$lang->tree->manageTaskChild = "Manage Child {$lang->execution->common} Modules"; +$lang->tree->manageDeliverableChild = "Manage Deliverable Category"; +$lang->tree->syncFromProduct = "Copy from Other {$lang->productCommon}s"; +$lang->tree->dragAndSort = "Drag to order"; +$lang->tree->sort = "Order"; +$lang->tree->addChild = "Add Child Module"; +$lang->tree->confirmDelete = 'Do you want to delete this module and its child modules?'; +$lang->tree->confirmDeleteMenu = 'Do you want to delete this menu and its child menus?'; +$lang->tree->confirmDelCategory = 'Do you want to delete this category and its child categories?'; +$lang->tree->confirmDeleteLine = "Do you want to delete this product line?"; +$lang->tree->confirmDeleteGroup = 'Do you want to delete this group and its child groups?'; +$lang->tree->confirmRoot = "Any changes to the {$lang->productCommon} will change the stories, bugs, cases of {$lang->productCommon} it belongs to, as well as the linkage of {$lang->executionCommon} and {$lang->productCommon}, which is dangerous. Do you want to change it?"; +$lang->tree->confirmRoot4Doc = "Any changes to the library will change the document of library it belongs to, which is dangerous. Do you want to change it?"; +$lang->tree->noSubmodule = "There are no copyable submodules under the current module!"; +$lang->tree->successSave = 'Saved.'; +$lang->tree->successFixed = 'Fixed.'; +$lang->tree->repeatName = 'The name "%s" exists!'; +$lang->tree->repeatDirName = 'The name "%s" exists!'; +$lang->tree->shouldNotBlank = 'Module name should not be blank!'; +$lang->tree->syncProductModule = 'Sync Module'; +$lang->tree->host = 'Host'; +$lang->tree->editHost = 'Edit host group'; +$lang->tree->deleteHost = 'Delete host group'; +$lang->tree->manageHostChild = 'Manage child host'; +$lang->tree->groupMaintenance = 'Manage host group'; +$lang->tree->groupName = 'Group Name'; +$lang->tree->parentGroup = 'Parent grpup'; +$lang->tree->childGroup = 'Child'; +$lang->tree->confirmDeleteHost = 'Do you want to delete this host and its child hosts?'; +$lang->tree->designModule = 'Design'; +$lang->tree->otherModule = 'Other'; $lang->tree->module = 'Module'; $lang->tree->name = 'Module Name'; diff --git a/module/tree/lang/fr.php b/module/tree/lang/fr.php index 963b5509a5..a5052b465b 100755 --- a/module/tree/lang/fr.php +++ b/module/tree/lang/fr.php @@ -10,61 +10,65 @@ * @link https://www.zentao.net */ $lang->tree = new stdclass(); -$lang->tree->common = 'Module'; -$lang->tree->edit = 'Editer Module'; -$lang->tree->delete = 'Supprimer Module'; -$lang->tree->browse = 'Gérer les Modules Généraux'; -$lang->tree->browseTask = 'Gérer les Modules Tâche'; -$lang->tree->manage = 'Gérer les Modules'; -$lang->tree->fix = 'Corrigez Module'; -$lang->tree->manageProduct = "Gérer les Modules {$lang->productCommon}"; -$lang->tree->manageProject = "Gérer les Modules {$lang->projectCommon}"; -$lang->tree->manageExecution = "Gérer les Modules {$lang->execution->common}"; -$lang->tree->manageLine = 'Gérer Ligne Produit'; -$lang->tree->manageBug = 'Gérer Bugs'; -$lang->tree->manageCase = 'Gérer CasTests'; -$lang->tree->manageCaselib = 'Gérer Librairie de recette'; -$lang->tree->manageCustomDoc = 'Gérer la librairie de Doc'; -$lang->tree->manageApiChild = 'Manage API Directory'; -$lang->tree->manageGroup = 'Manage group'; -$lang->tree->updateOrder = 'Rang Module'; -$lang->tree->manageChild = 'Gérer Sous-Module'; -$lang->tree->manageStoryChild = 'Gérer Sous-Module'; -$lang->tree->manageLineChild = 'Gérer Ligne Produit'; -$lang->tree->manageBugChild = 'Gérer Sous-Bugs'; -$lang->tree->manageCaseChild = 'Gérer Sous-CasTests'; -$lang->tree->manageCaselibChild = 'Gérer Sous-Librairies'; -$lang->tree->manageDashboard = 'Manage Dashboard Module'; -$lang->tree->manageDashboardChild = 'Manage Dashboard Child Module'; -$lang->tree->manageProjectChild = "Gérer Sous-Modules {$lang->projectCommon}"; -$lang->tree->manageTaskChild = "Gérer Sous-Modules {$lang->execution->common}"; -$lang->tree->syncFromProduct = "Copier d'un autre {$lang->productCommon}"; -$lang->tree->dragAndSort = "Faites glisser pour ordonner"; -$lang->tree->sort = "Ordonnez"; -$lang->tree->addChild = "Ajout Sous-Module"; -$lang->tree->confirmDelete = 'Voulez-vous supprimer ce module et tous ses sous-modules ?'; -$lang->tree->confirmDeleteMenu = 'Do you want to delete this menu and its child menus?'; -$lang->tree->confirmDelCategory = 'Voulez-vous supprimer cette catégorie et ses sous-catégories?'; -$lang->tree->confirmDeleteLine = 'Voulez-vous supprimer cette ligne de produit ?'; -$lang->tree->confirmDeleteGroup = 'Do you want to delete this group and its child groups?'; -$lang->tree->confirmRoot = "Les changements du {$lang->productCommon} vont impacter les stories, bugs, casTests du {$lang->productCommon} auquel ils appartiennent, ainsi que les associations de {$lang->executionCommon} et {$lang->productCommon}, ce qui est dangereux. Voulez-vous malgré tout effectuer le changement ?"; -$lang->tree->confirmRoot4Doc = "Toute modification apportée à la bibliothèque modifiera le document de la bibliothèque à laquelle elle appartient, ce qui est dangereux. Voulez-vous le changer ?"; -$lang->tree->noSubmodule = "There are no copyable submodules under the current module!"; -$lang->tree->successSave = 'Sauvé.'; -$lang->tree->successFixed = 'Corrigé.'; -$lang->tree->repeatName = 'Le nom "%s" existe déjà !'; -$lang->tree->repeatDirName = 'The name "%s" exists!'; -$lang->tree->shouldNotBlank = 'Module name should not be blank!'; -$lang->tree->syncProductModule = 'Sync Module'; -$lang->tree->host = 'Host'; -$lang->tree->editHost = 'Edit host group'; -$lang->tree->deleteHost = 'Delete host group'; -$lang->tree->manageHostChild = 'Manage child host'; -$lang->tree->groupMaintenance = 'Manage host group'; -$lang->tree->groupName = 'Group Name'; -$lang->tree->parentGroup = 'Parent grpup'; -$lang->tree->childGroup = 'Child'; -$lang->tree->confirmDeleteHost = 'Do you want to delete this host and its child hosts?'; +$lang->tree->common = 'Module'; +$lang->tree->edit = 'Editer Module'; +$lang->tree->create = 'Create Module'; +$lang->tree->delete = 'Supprimer Module'; +$lang->tree->browse = 'Gérer les Modules Généraux'; +$lang->tree->browseTask = 'Gérer les Modules Tâche'; +$lang->tree->manage = 'Gérer les Modules'; +$lang->tree->fix = 'Corrigez Module'; +$lang->tree->manageProduct = "Gérer les Modules {$lang->productCommon}"; +$lang->tree->manageProject = "Gérer les Modules {$lang->projectCommon}"; +$lang->tree->manageExecution = "Gérer les Modules {$lang->execution->common}"; +$lang->tree->manageLine = 'Gérer Ligne Produit'; +$lang->tree->manageBug = 'Gérer Bugs'; +$lang->tree->manageCase = 'Gérer CasTests'; +$lang->tree->manageCaselib = 'Gérer Librairie de recette'; +$lang->tree->manageCustomDoc = 'Gérer la librairie de Doc'; +$lang->tree->manageApiChild = 'Manage API Directory'; +$lang->tree->manageGroup = 'Manage group'; +$lang->tree->updateOrder = 'Rang Module'; +$lang->tree->manageChild = 'Gérer Sous-Module'; +$lang->tree->manageStoryChild = 'Gérer Sous-Module'; +$lang->tree->manageLineChild = 'Gérer Ligne Produit'; +$lang->tree->manageBugChild = 'Gérer Sous-Bugs'; +$lang->tree->manageCaseChild = 'Gérer Sous-CasTests'; +$lang->tree->manageCaselibChild = 'Gérer Sous-Librairies'; +$lang->tree->manageDashboard = 'Manage Dashboard Module'; +$lang->tree->manageDashboardChild = 'Manage Dashboard Child Module'; +$lang->tree->manageProjectChild = "Gérer Sous-Modules {$lang->projectCommon}"; +$lang->tree->manageTaskChild = "Gérer Sous-Modules {$lang->execution->common}"; +$lang->tree->manageDeliverableChild = "Manage Deliverable Category"; +$lang->tree->syncFromProduct = "Copier d'un autre {$lang->productCommon}"; +$lang->tree->dragAndSort = "Faites glisser pour ordonner"; +$lang->tree->sort = "Ordonnez"; +$lang->tree->addChild = "Ajout Sous-Module"; +$lang->tree->confirmDelete = 'Voulez-vous supprimer ce module et tous ses sous-modules ?'; +$lang->tree->confirmDeleteMenu = 'Do you want to delete this menu and its child menus?'; +$lang->tree->confirmDelCategory = 'Voulez-vous supprimer cette catégorie et ses sous-catégories?'; +$lang->tree->confirmDeleteLine = 'Voulez-vous supprimer cette ligne de produit ?'; +$lang->tree->confirmDeleteGroup = 'Do you want to delete this group and its child groups?'; +$lang->tree->confirmRoot = "Les changements du {$lang->productCommon} vont impacter les stories, bugs, casTests du {$lang->productCommon} auquel ils appartiennent, ainsi que les associations de {$lang->executionCommon} et {$lang->productCommon}, ce qui est dangereux. Voulez-vous malgré tout effectuer le changement ?"; +$lang->tree->confirmRoot4Doc = "Toute modification apportée à la bibliothèque modifiera le document de la bibliothèque à laquelle elle appartient, ce qui est dangereux. Voulez-vous le changer ?"; +$lang->tree->noSubmodule = "There are no copyable submodules under the current module!"; +$lang->tree->successSave = 'Sauvé.'; +$lang->tree->successFixed = 'Corrigé.'; +$lang->tree->repeatName = 'Le nom "%s" existe déjà !'; +$lang->tree->repeatDirName = 'The name "%s" exists!'; +$lang->tree->shouldNotBlank = 'Module name should not be blank!'; +$lang->tree->syncProductModule = 'Sync Module'; +$lang->tree->host = 'Host'; +$lang->tree->editHost = 'Edit host group'; +$lang->tree->deleteHost = 'Delete host group'; +$lang->tree->manageHostChild = 'Manage child host'; +$lang->tree->groupMaintenance = 'Manage host group'; +$lang->tree->groupName = 'Group Name'; +$lang->tree->parentGroup = 'Parent grpup'; +$lang->tree->childGroup = 'Child'; +$lang->tree->confirmDeleteHost = 'Do you want to delete this host and its child hosts?'; +$lang->tree->designModule = 'Design'; +$lang->tree->otherModule = 'Other'; $lang->tree->module = 'Module'; $lang->tree->name = 'Nom'; diff --git a/module/tree/lang/zh-cn.php b/module/tree/lang/zh-cn.php index d98a3242dc..afce3964bc 100755 --- a/module/tree/lang/zh-cn.php +++ b/module/tree/lang/zh-cn.php @@ -9,62 +9,66 @@ * @version $Id: zh-cn.php 4836 2013-06-19 05:39:40Z zhujinyonging@gmail.com $ * @link https://www.zentao.net */ -$lang->tree = new stdclass(); -$lang->tree->common = '模块维护'; -$lang->tree->edit = '编辑模块'; -$lang->tree->delete = '删除模块'; -$lang->tree->browse = '通用模块维护'; -$lang->tree->browseTask = '任务模块维护'; -$lang->tree->manage = '维护模块'; -$lang->tree->fix = '修正数据'; -$lang->tree->manageProduct = "维护{$lang->productCommon}视图模块"; -$lang->tree->manageProject = "维护{$lang->projectCommon}视图模块"; -$lang->tree->manageExecution = "维护{$lang->execution->common}视图模块"; -$lang->tree->manageLine = "维护产品线"; -$lang->tree->manageBug = '维护测试视图模块'; -$lang->tree->manageCase = '维护用例视图模块'; -$lang->tree->manageCaselib = '维护用例库模块'; -$lang->tree->manageCustomDoc = '维护文档库分类'; -$lang->tree->manageApiChild = '维护接口库目录'; -$lang->tree->manageGroup = '维护分组'; -$lang->tree->updateOrder = '更新排序'; -$lang->tree->manageChild = '维护子模块'; -$lang->tree->manageStoryChild = '维护子模块'; -$lang->tree->manageLineChild = "维护产品线"; -$lang->tree->manageBugChild = '维护Bug子模块'; -$lang->tree->manageCaseChild = '维护用例子模块'; -$lang->tree->manageCaselibChild = '维护用例库子模块'; -$lang->tree->manageDashboard = '维护仪表盘模块'; -$lang->tree->manageDashboardChild = '维护仪表盘子模块'; -$lang->tree->manageProjectChild = "维护{$lang->projectCommon}子模块"; -$lang->tree->manageTaskChild = "维护{$lang->execution->common}子模块"; -$lang->tree->syncFromProduct = '复制模块'; -$lang->tree->dragAndSort = "拖放排序"; -$lang->tree->sort = "排序"; -$lang->tree->addChild = "增加子模块"; -$lang->tree->confirmDelete = '该模块及其子模块都会被删除,您确定删除吗?'; -$lang->tree->confirmDeleteMenu = '该目录及其子目录都会被删除,您确定删除吗?'; -$lang->tree->confirmDelCategory = '该分类及其子分类都会被删除,您确定删除吗?'; -$lang->tree->confirmDeleteLine = "您确定删除该产品线吗?"; -$lang->tree->confirmDeleteGroup = '该分组及其子分组都会被删除,您确定删除吗?'; -$lang->tree->confirmRoot = "模块的所属{$lang->productCommon}修改,会关联修改该模块下的{$lang->SRCommon}、Bug、用例的所属{$lang->productCommon},以及{$lang->executionCommon}和{$lang->productCommon}的关联关系。该操作比较危险,请谨慎操作。是否确认修改?"; -$lang->tree->confirmRoot4Doc = "修改所属文档库,会同时修改该分类下文档的关联关系。该操作比较危险,请谨慎操作。是否确认修改?"; -$lang->tree->noSubmodule = "当前模块下没有可复制的子模块!"; -$lang->tree->successSave = '成功保存'; -$lang->tree->successFixed = '成功修正数据!'; -$lang->tree->repeatName = '模块名“%s”已经存在!'; -$lang->tree->repeatDirName = '目录名“%s”已经存在!'; -$lang->tree->shouldNotBlank = '模块名不能为空格!'; -$lang->tree->syncProductModule = "同步{$lang->productCommon}模块"; -$lang->tree->host = '主机'; -$lang->tree->editHost = '编辑主机分组'; -$lang->tree->deleteHost = '删除主机分组'; -$lang->tree->manageHostChild = '维护主机子分组'; -$lang->tree->groupMaintenance = '维护主机分组'; -$lang->tree->groupName = '分组名称'; -$lang->tree->parentGroup = '上级分组'; -$lang->tree->childGroup = '子分组'; -$lang->tree->confirmDeleteHost = '该分组及子分组都会被删除,您确定删除吗?'; +$lang->tree = new stdclass(); +$lang->tree->common = '模块维护'; +$lang->tree->create = '创建模块'; +$lang->tree->edit = '编辑模块'; +$lang->tree->delete = '删除模块'; +$lang->tree->browse = '通用模块维护'; +$lang->tree->browseTask = '任务模块维护'; +$lang->tree->manage = '维护模块'; +$lang->tree->fix = '修正数据'; +$lang->tree->manageProduct = "维护{$lang->productCommon}视图模块"; +$lang->tree->manageProject = "维护{$lang->projectCommon}视图模块"; +$lang->tree->manageExecution = "维护{$lang->execution->common}视图模块"; +$lang->tree->manageLine = "维护产品线"; +$lang->tree->manageBug = '维护测试视图模块'; +$lang->tree->manageCase = '维护用例视图模块'; +$lang->tree->manageCaselib = '维护用例库模块'; +$lang->tree->manageCustomDoc = '维护文档库分类'; +$lang->tree->manageApiChild = '维护接口库目录'; +$lang->tree->manageGroup = '维护分组'; +$lang->tree->updateOrder = '更新排序'; +$lang->tree->manageChild = '维护子模块'; +$lang->tree->manageStoryChild = '维护子模块'; +$lang->tree->manageLineChild = "维护产品线"; +$lang->tree->manageBugChild = '维护Bug子模块'; +$lang->tree->manageCaseChild = '维护用例子模块'; +$lang->tree->manageCaselibChild = '维护用例库子模块'; +$lang->tree->manageDashboard = '维护仪表盘模块'; +$lang->tree->manageDashboardChild = '维护仪表盘子模块'; +$lang->tree->manageProjectChild = "维护{$lang->projectCommon}子模块"; +$lang->tree->manageTaskChild = "维护{$lang->execution->common}子模块"; +$lang->tree->manageDeliverableChild = "维护交付物分类"; +$lang->tree->syncFromProduct = '复制模块'; +$lang->tree->dragAndSort = "拖放排序"; +$lang->tree->sort = "排序"; +$lang->tree->addChild = "增加子模块"; +$lang->tree->confirmDelete = '该模块及其子模块都会被删除,您确定删除吗?'; +$lang->tree->confirmDeleteMenu = '该目录及其子目录都会被删除,您确定删除吗?'; +$lang->tree->confirmDelCategory = '该分类及其子分类都会被删除,您确定删除吗?'; +$lang->tree->confirmDeleteLine = "您确定删除该产品线吗?"; +$lang->tree->confirmDeleteGroup = '该分组及其子分组都会被删除,您确定删除吗?'; +$lang->tree->confirmRoot = "模块的所属{$lang->productCommon}修改,会关联修改该模块下的{$lang->SRCommon}、Bug、用例的所属{$lang->productCommon},以及{$lang->executionCommon}和{$lang->productCommon}的关联关系。该操作比较危险,请谨慎操作。是否确认修改?"; +$lang->tree->confirmRoot4Doc = "修改所属文档库,会同时修改该分类下文档的关联关系。该操作比较危险,请谨慎操作。是否确认修改?"; +$lang->tree->noSubmodule = "当前模块下没有可复制的子模块!"; +$lang->tree->successSave = '成功保存'; +$lang->tree->successFixed = '成功修正数据!'; +$lang->tree->repeatName = '模块名“%s”已经存在!'; +$lang->tree->repeatDirName = '目录名“%s”已经存在!'; +$lang->tree->shouldNotBlank = '模块名不能为空格!'; +$lang->tree->syncProductModule = "同步{$lang->productCommon}模块"; +$lang->tree->host = '主机'; +$lang->tree->editHost = '编辑主机分组'; +$lang->tree->deleteHost = '删除主机分组'; +$lang->tree->manageHostChild = '维护主机子分组'; +$lang->tree->groupMaintenance = '维护主机分组'; +$lang->tree->groupName = '分组名称'; +$lang->tree->parentGroup = '上级分组'; +$lang->tree->childGroup = '子分组'; +$lang->tree->confirmDeleteHost = '该分组及子分组都会被删除,您确定删除吗?'; +$lang->tree->designModule = '设计类'; +$lang->tree->otherModule = '其他'; $lang->tree->module = '模块'; $lang->tree->name = '模块名称'; diff --git a/module/tree/model.php b/module/tree/model.php index 5374b9baf8..13967f2041 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -87,7 +87,7 @@ class treeModel extends model } if($this->isMergeModule($rootID, $type)) { - return $this->dao->select('id, name, root, branch, grade, path, parent, owner, type')->from(TABLE_MODULE) + return $this->dao->select('id, name, root, branch, grade, path, parent, owner, type, extra')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->beginIF($type == 'task')->andWhere('type')->eq('task')->fi() ->beginIF($type != 'task')->andWhere('type')->in("story,$type")->fi() @@ -1235,6 +1235,53 @@ class treeModel extends model return $data; } + /** + * 生成交付物链接。 + * Create link of a deliverable. + * + * @param string $type + * @param object $module + * @param string $parent + * @param string $extra + * @access public + * @return object + */ + public function createDeliverableLink(string $type, object $module, string $parent, string $extra = ''): object + { + if(!$extra) $extra = 'all'; + + $data = new stdclass(); + $data->id = (string)$module->id; + $data->parent = (string)$module->parent; + $data->name = $module->name; + $data->url = helper::createLink('deliverable', 'browse', "groupID={$module->root}&browseType={$extra}¶m={$module->id}"); + $data->extra = $module->extra; + + return $data; + } + + /** + * 生成项目交付物链接。 + * Create link of a project deliverable. + * + * @param string $type + * @param object $module + * @param string $parent + * @param string $extra + * @access public + * @return object + */ + public function createProjectDeliverableLink(string $type, object $module, string $parent, string $extra = ''): object + { + $data = new stdclass(); + $data->id = (string)$module->id; + $data->parent = (string)$module->parent; + $data->name = $module->name; + $data->url = helper::createLink('project', 'deliverable', "projectID={$extra}&browseType=bymodule¶m={$module->id}"); + + return $data; + } + /** * 生成文档链接。 * Create link of a doc. @@ -1853,7 +1900,7 @@ class treeModel extends model $repeatName = $this->checkUnique($module, $childs); if($repeatName) { - dao::$errors['root'] = sprintf($this->lang->tree->repeatName, $repeatName); + dao::$errors['name'] = sprintf($this->lang->tree->repeatName, $repeatName); return false; } @@ -1982,7 +2029,7 @@ class treeModel extends model */ public function update(int $moduleID, string $type = ''): bool { - $module = fixer::input('post')->cleanInt('branch')->get(); + $module = fixer::input('post')->cleanInt('branch')->setDefault('parent', 0)->get(); $self = $this->getById($moduleID); $changes = common::createChanges($self, $module); if(!isset($_POST['branch'])) $module->branch = $self->branch; @@ -2174,9 +2221,10 @@ class treeModel extends model $objectType = (!empty($module->type) && strpos($this->config->tree->groupTypes, ",$module->type,") !== false) ? 'chartgroup' : 'module'; /* Mark deletion when delete a module. */ $this->dao->update(TABLE_MODULE)->set('deleted')->eq(1)->where('id')->in($childs)->exec(); - foreach($childs as $childID) + + if($module->type != 'deliverable') { - $this->loadModel('action')->create($objectType, $childID, 'deleted', '', actionModel::CAN_UNDELETED); + foreach($childs as $childID) $this->loadModel('action')->create($objectType, $childID, 'deleted', '', actionModel::CAN_UNDELETED); } $this->fixModulePath($module->root, $module->type); diff --git a/module/tree/test/lib/tree.unittest.class.php b/module/tree/test/lib/tree.unittest.class.php index 3e87f0f11a..a3b2606503 100755 --- a/module/tree/test/lib/tree.unittest.class.php +++ b/module/tree/test/lib/tree.unittest.class.php @@ -134,7 +134,7 @@ class treeTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('buildTreeArray'); $method->setAccessible(true); - + $method->invokeArgs($this->objectModel, array(&$treeMenu, $modules, $module, $moduleName, $divide)); if(dao::isError()) return dao::getError(); @@ -990,10 +990,10 @@ class treeTest public function setModuleLangTest() { global $lang; - + // 清空现有的module语言项 unset($lang->module); - + $this->objectModel->setModuleLang(); if(dao::isError()) return dao::getError(); diff --git a/module/tree/test/model/buildmenuquery.php b/module/tree/test/model/buildmenuquery.php index 5fbac2f08e..972a2cacbd 100755 --- a/module/tree/test/model/buildmenuquery.php +++ b/module/tree/test/model/buildmenuquery.php @@ -17,11 +17,11 @@ cid=19343 - 测试查询root 43 type story 的查询语句 @SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '43' AND ( `type` = 'story') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order` -- 测试查询root 101 type task 的查询语句 @SELECT id, name, root, branch, grade, path, parent, owner, type FROM `zt_module` WHERE `root` = '101' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc +- 测试查询root 101 type task 的查询语句 @SELECT id, name, root, branch, grade, path, parent, owner, type, extra FROM `zt_module` WHERE `root` = '101' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc -- 测试查询root 102 type task 的查询语句 @SELECT id, name, root, branch, grade, path, parent, owner, type FROM `zt_module` WHERE `root` = '102' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc +- 测试查询root 102 type task 的查询语句 @SELECT id, name, root, branch, grade, path, parent, owner, type, extra FROM `zt_module` WHERE `root` = '102' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc -- 测试查询root 103 type task 的查询语句 @SELECT id, name, root, branch, grade, path, parent, owner, type FROM `zt_module` WHERE `root` = '103' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc +- 测试查询root 103 type task 的查询语句 @SELECT id, name, root, branch, grade, path, parent, owner, type, extra FROM `zt_module` WHERE `root` = '103' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc - 测试查询root 41 type story startModule 1821的查询语句 @SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '41' AND ( `type` = 'story') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order` @@ -49,8 +49,8 @@ r($tree->buildMenuQueryTest($root[2], $type[0])) && p( r($tree->buildMenuQueryTest($root[3], $type[0])) && p() && e("SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '41' AND ( `type` = 'story') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order`"); // 测试查询root 41 type story 的查询语句 r($tree->buildMenuQueryTest($root[4], $type[0])) && p() && e("SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '42' AND ( `type` = 'story') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order`"); // 测试查询root 42 type story 的查询语句 r($tree->buildMenuQueryTest($root[5], $type[0])) && p() && e("SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '43' AND ( `type` = 'story') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order`"); // 测试查询root 43 type story 的查询语句 -r($tree->buildMenuQueryTest($root[6], $type[1])) && p() && e("SELECT id, name, root, branch, grade, path, parent, owner, type FROM `zt_module` WHERE `root` = '101' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc"); // 测试查询root 101 type task 的查询语句 -r($tree->buildMenuQueryTest($root[7], $type[1])) && p() && e("SELECT id, name, root, branch, grade, path, parent, owner, type FROM `zt_module` WHERE `root` = '102' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc"); // 测试查询root 102 type task 的查询语句 -r($tree->buildMenuQueryTest($root[8], $type[1])) && p() && e("SELECT id, name, root, branch, grade, path, parent, owner, type FROM `zt_module` WHERE `root` = '103' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc"); // 测试查询root 103 type task 的查询语句 +r($tree->buildMenuQueryTest($root[6], $type[1])) && p() && e("SELECT id, name, root, branch, grade, path, parent, owner, type, extra FROM `zt_module` WHERE `root` = '101' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc"); // 测试查询root 101 type task 的查询语句 +r($tree->buildMenuQueryTest($root[7], $type[1])) && p() && e("SELECT id, name, root, branch, grade, path, parent, owner, type, extra FROM `zt_module` WHERE `root` = '102' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc"); // 测试查询root 102 type task 的查询语句 +r($tree->buildMenuQueryTest($root[8], $type[1])) && p() && e("SELECT id, name, root, branch, grade, path, parent, owner, type, extra FROM `zt_module` WHERE `root` = '103' AND `type` = 'task' AND `deleted` = '0' ORDER BY `grade` desc,`order`,`type` desc"); // 测试查询root 103 type task 的查询语句 r($tree->buildMenuQueryTest($root[3], $type[0], $startModule[0])) && p() && e("SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '41' AND ( `type` = 'story') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order`"); // 测试查询root 41 type story startModule 1821的查询语句 r($tree->buildMenuQueryTest($root[3], $type[0], $startModule[1], $branch)) && p() && e("SELECT * FROM `zt_module` WHERE 1=1 AND `root` = '41' AND ( `type` = 'story') AND (branch = '0' OR `branch` = '1') AND `deleted` = '0' ORDER BY `root`,`grade` desc,`order`"); // 测试查询root 41 type story startModule 0 branch 1 的查询语句 diff --git a/module/tree/test/model/createdeliverablelink.php b/module/tree/test/model/createdeliverablelink.php new file mode 100755 index 0000000000..f9a32fe24a --- /dev/null +++ b/module/tree/test/model/createdeliverablelink.php @@ -0,0 +1,36 @@ +#!/usr/bin/env php +createDeliverableLink(); +timeout=0 +cid=1 + +- 测试获取module 1的链接属性url @createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=1 +- 测试获取module 2的链接属性url @createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=2 +- 测试获取module 3的链接属性url @createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=3 +- 测试获取module 4的链接属性url @createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=4 +- 测试获取module 5的链接属性url @createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=5 + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +su('admin'); + +$module = zenData('module'); +$module->type->range('deliverable'); +$module->root->range('1'); +$module->gen(20); + +global $tester; +$tester->loadModel('tree'); +$module = $tester->tree->fetchByID(1, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', 'bymodule')) && p('url') && e("createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=1"); // 测试获取module 1的链接 +$module = $tester->tree->fetchByID(2, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', 'bymodule')) && p('url') && e("createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=2"); // 测试获取module 2的链接 +$module = $tester->tree->fetchByID(3, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', 'bymodule')) && p('url') && e("createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=3"); // 测试获取module 3的链接 +$module = $tester->tree->fetchByID(4, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', 'bymodule')) && p('url') && e("createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=4"); // 测试获取module 4的链接 +$module = $tester->tree->fetchByID(5, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', 'bymodule')) && p('url') && e("createdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=bymodule¶m=5"); // 测试获取module 5的链接 diff --git a/module/tree/test/model/createmanagelink.php b/module/tree/test/model/createmanagelink.php index f3d1657b32..cd1030a730 100755 --- a/module/tree/test/model/createmanagelink.php +++ b/module/tree/test/model/createmanagelink.php @@ -7,13 +7,13 @@ title=测试 treeModel::createManageLink(); timeout=0 cid=19352 -- 执行treeTest模块的createManageLinkTest方法,参数是'story', $module1 +- 执行treeTest模块的createManageLinkTest方法,参数是'story', $module1 - 属性id @1 - 属性parent @0 - 属性name @Story模块 - 执行treeTest模块的createManageLinkTest方法,参数是'bug', $module2 属性name @Bug模块[B][admin] - 执行treeTest模块的createManageLinkTest方法,参数是'case', $module3 属性name @Case模块[C] -- 执行treeTest模块的createManageLinkTest方法,参数是'task', $module4 +- 执行treeTest模块的createManageLinkTest方法,参数是'task', $module4 - 属性id @4 - 属性parent @0 - 属性name @普通模块 diff --git a/module/tree/test/model/createprojectdeliverablelink.php b/module/tree/test/model/createprojectdeliverablelink.php new file mode 100755 index 0000000000..8cc2cc7fae --- /dev/null +++ b/module/tree/test/model/createprojectdeliverablelink.php @@ -0,0 +1,36 @@ +#!/usr/bin/env php +createDeliverableLink(); +timeout=0 +cid=1 + +- 测试获取module 1的链接属性url @createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=1¶m=1 +- 测试获取module 2的链接属性url @createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=2¶m=2 +- 测试获取module 3的链接属性url @createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=3¶m=3 +- 测试获取module 4的链接属性url @createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=4¶m=4 +- 测试获取module 5的链接属性url @createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=5¶m=5 + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +su('admin'); + +$module = zenData('module'); +$module->type->range('deliverable'); +$module->root->range('1'); +$module->gen(20); + +global $tester; +$tester->loadModel('tree'); +$module = $tester->tree->fetchByID(1, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', '1')) && p('url') && e("createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=1¶m=1"); // 测试获取module 1的链接 +$module = $tester->tree->fetchByID(2, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', '2')) && p('url') && e("createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=2¶m=2"); // 测试获取module 2的链接 +$module = $tester->tree->fetchByID(3, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', '3')) && p('url') && e("createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=3¶m=3"); // 测试获取module 3的链接 +$module = $tester->tree->fetchByID(4, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', '4')) && p('url') && e("createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=4¶m=4"); // 测试获取module 4的链接 +$module = $tester->tree->fetchByID(5, 'module'); +r($tester->tree->createDeliverableLink('deliverable', $module, '0', '5')) && p('url') && e("createprojectdeliverablelink.php?m=deliverable&f=browse&groupID=1&browseType=5¶m=5"); // 测试获取module 5的链接 \ No newline at end of file diff --git a/module/tree/test/model/getoptionmenubybranch.php b/module/tree/test/model/getoptionmenubybranch.php index 7390ff69a1..1b8759be80 100755 --- a/module/tree/test/model/getoptionmenubybranch.php +++ b/module/tree/test/model/getoptionmenubybranch.php @@ -63,7 +63,7 @@ $treeTest = new treeTest(); // 5. 测试步骤 r($treeTest->getOptionMenuByBranchTest(1, 'story', 0, 'all', 'nodeleted', 'all', '/')) && p('0') && e('/'); // 步骤1:正常story类型根节点 -r($treeTest->getOptionMenuByBranchTest(2, 'bug', 0, 'all', 'nodeleted', 'all', '/')) && p('0') && e('/'); // 步骤2:bug类型根节点 +r($treeTest->getOptionMenuByBranchTest(2, 'bug', 0, 'all', 'nodeleted', 'all', '/')) && p('0') && e('/'); // 步骤2:bug类型根节点 r($treeTest->getOptionMenuByBranchTest(3, 'case', 0, 'all', 'nodeleted', 'all', '/')) && p('0') && e('/'); // 步骤3:case类型根节点 r($treeTest->getOptionMenuByBranchTest(3, 'story', 0, '1', 'nodeleted', 'all', '/')) && p('0') && e('/'); // 步骤4:指定branch根节点 r($treeTest->getOptionMenuByBranchTest(999, 'story', 0, 'all', 'nodeleted', 'all', '/')) && p('0') && e('/'); // 步骤5:不存在rootID返回根节点 diff --git a/module/tree/test/model/yaml/module_createmanagelink.yaml b/module/tree/test/model/yaml/module_createmanagelink.yaml index 7f9804f5f3..6040301db8 100644 --- a/module/tree/test/model/yaml/module_createmanagelink.yaml +++ b/module/tree/test/model/yaml/module_createmanagelink.yaml @@ -6,55 +6,55 @@ fields: - field: id note: 模块ID range: 1-10 - + - field: root - note: 根节点ID + note: 根节点ID range: 1-3 - + - field: branch note: 分支ID range: 0 - + - field: name note: 模块名称 range: [模块1, 模块2, Bug模块, Case模块, Story模块, Task模块, 文档模块, 产品模块, 项目模块, 测试模块] - + - field: parent note: 父级模块ID range: 0-2 - + - field: path note: 模块路径 range: [',1,', ',1,2,', ',1,3,', ',2,', ',2,4,'] - + - field: grade note: 模块级别 range: 1-3 - + - field: order note: 排序 range: 1-10:1 - + - field: type note: 模块类型 range: [story, bug, case, task, doc, product, project, test] - + - field: from note: 来源 range: 0 - + - field: owner note: 负责人 range: [admin, user1, user2, ''] - + - field: collector note: 收藏者 range: ['', 'admin,user1', 'user2'] - + - field: short note: 简称 range: [M1, M2, BUG, CASE, STORY, TASK, DOC, PROD, PROJ, TEST] - + - field: deleted note: 删除状态 range: ['0'] \ No newline at end of file diff --git a/module/tree/test/model/yaml/user_createmanagelink.yaml b/module/tree/test/model/yaml/user_createmanagelink.yaml index 49cfcf329d..b68a887874 100644 --- a/module/tree/test/model/yaml/user_createmanagelink.yaml +++ b/module/tree/test/model/yaml/user_createmanagelink.yaml @@ -6,27 +6,27 @@ fields: - field: id note: 用户ID range: 1-10 - + - field: account note: 用户账号 range: [admin, user1, user2, user3, dev1, dev2, qa1, qa2, pm1, pm2] - + - field: password note: 密码 range: e10adc3949ba59abbe56e057f20f883e{10} - + - field: realname note: 真实姓名 range: [管理员, 用户1, 用户2, 用户3, 开发1, 开发2, 测试1, 测试2, 项目经理1, 项目经理2] - + - field: email note: 邮箱 range: [admin@test.com, user1@test.com, user2@test.com, user3@test.com, dev1@test.com] - + - field: role note: 角色 range: [admin, dev, qa, pm, user] - + - field: deleted note: 删除状态 range: ['0'] \ No newline at end of file diff --git a/module/tree/ui/browse.html.php b/module/tree/ui/browse.html.php index 3150ff24ec..4517f9f6e7 100644 --- a/module/tree/ui/browse.html.php +++ b/module/tree/ui/browse.html.php @@ -238,6 +238,7 @@ div set::items($tree), set::canEdit(common::hasPriv('tree', 'edit') && $canBeChanged), set::canDelete(common::hasPriv('tree', 'delete') && $canBeChanged), + set::canSplit($viewType != 'deliverable'), set::sortable(array('handle' => '.icon-move')), set::onSort(jsRaw('window.updateOrder')) ) diff --git a/module/tree/ui/create.html.php b/module/tree/ui/create.html.php new file mode 100644 index 0000000000..8f2a14d97e --- /dev/null +++ b/module/tree/ui/create.html.php @@ -0,0 +1,30 @@ + + * @package tree + * @link https://www.zentao.net + */ +namespace zin; +formPanel +( + setID('createForm'), + set::title($title), + set::labelWidth('120px'), + formGroup + ( + set::required(true), + set::label($lang->tree->name), + set::name('name'), + set::control('input') + ), + formGroup + ( + set::label($lang->tree->short), + set::name('short'), + set::control('input') + ) +); diff --git a/module/tree/ui/edit.html.php b/module/tree/ui/edit.html.php index e3c0b6a15f..42569e3324 100644 --- a/module/tree/ui/edit.html.php +++ b/module/tree/ui/edit.html.php @@ -41,6 +41,7 @@ formPanel setID('editForm'), set::action(helper::createLink($app->rawModule, $app->rawMethod, 'module=' . $module->id .'&type=' . $type)), set::submitBtnText($lang->save), + set::labelWidth('100px'), $showProduct ? formRow ( formGroup @@ -83,7 +84,7 @@ formPanel ) ) : null, $type == 'api' ? formHidden('root', $module->root) : null, - $module->type != 'line' ? formGroup + $module->type != 'line' && $module->type != 'deliverable' ? formGroup ( set::className('moduleBox ', $hidden ? 'hidden' : ''), set::label(strpos(',doc,api,docTemplate,', ",{$type},") !== false ? $lang->tree->parentCate : $lang->tree->parent), diff --git a/module/tutorial/test/model/getlibtree.php b/module/tutorial/test/model/getlibtree.php index 278ec33e50..f85ac166ce 100755 --- a/module/tutorial/test/model/getlibtree.php +++ b/module/tutorial/test/model/getlibtree.php @@ -8,16 +8,16 @@ timeout=0 cid=19443 - 执行tutorialTest模块的getLibTreeTest方法 @1 -- 执行tutorialTest模块的getLibTreeTest方法 +- 执行tutorialTest模块的getLibTreeTest方法 - 第0条的id属性 @2 - 第0条的name属性 @Test Doc Lib -- 执行tutorialTest模块的getLibTreeTest方法 +- 执行tutorialTest模块的getLibTreeTest方法 - 第0条的type属性 @docLib - 第0条的parent属性 @1 -- 执行tutorialTest模块的getLibTreeTest方法 +- 执行tutorialTest模块的getLibTreeTest方法 - 第0条的objectType属性 @custom - 第0条的active属性 @1 -- 执行tutorialTest模块的getLibTreeTest方法 +- 执行tutorialTest模块的getLibTreeTest方法 - 第0条的order属性 @0 - 第0条的main属性 @0 diff --git a/module/tutorial/test/model/getrepo.php b/module/tutorial/test/model/getrepo.php index 8712634ad3..039a60e1a5 100755 --- a/module/tutorial/test/model/getrepo.php +++ b/module/tutorial/test/model/getrepo.php @@ -7,15 +7,15 @@ title=测试 tutorialModel::getRepo(); timeout=0 cid=19461 -- 执行tutorialTest模块的getRepoTest方法 +- 执行tutorialTest模块的getRepoTest方法 - 属性id @1 - 属性name @Test repo -- 执行tutorialTest模块的getRepoTest方法 +- 执行tutorialTest模块的getRepoTest方法 - 属性SCM @Git - 属性encoding @utf-8 - 执行tutorialTest模块的getRepoTest方法 属性product @1 - 执行tutorialTest模块的getRepoTest方法 属性deleted @0 -- 执行tutorialTest模块的getRepoTest方法 +- 执行tutorialTest模块的getRepoTest方法 - 属性commits @1 - 属性synced @0 diff --git a/module/tutorial/test/model/getstypairs.php b/module/tutorial/test/model/getstypairs.php index 71cc4e34f2..1b2fa0eb5c 100755 --- a/module/tutorial/test/model/getstypairs.php +++ b/module/tutorial/test/model/getstypairs.php @@ -30,5 +30,5 @@ $storyPairs = $tutorialTest->getStoryPairsTest(); r(is_array($storyPairs)) && p() && e('1'); // 步骤1:验证返回结果是数组格式 r(count($storyPairs)) && p() && e('4'); // 步骤2:验证数组包含正确数量的需求键值对 r($tutorialTest->getStoryPairsTest()) && p('1') && e('Test epic'); // 步骤3:验证Epic类型需求的键值对映射关系 -r($tutorialTest->getStoryPairsTest()) && p('2') && e('Test requirement'); // 步骤4:验证Requirement类型需求的键值对映射关系 +r($tutorialTest->getStoryPairsTest()) && p('2') && e('Test requirement'); // 步骤4:验证Requirement类型需求的键值对映射关系 r($tutorialTest->getStoryPairsTest()) && p('3') && e('Test active story'); // 步骤5:验证Story类型需求的键值对映射关系 \ No newline at end of file diff --git a/module/upgrade/config.php b/module/upgrade/config.php index 827d18a314..323ff54194 100644 --- a/module/upgrade/config.php +++ b/module/upgrade/config.php @@ -1505,6 +1505,9 @@ $config->delete['21_6_beta'][] = 'extension/biz/project/ext/model/zentaoipd.php' $config->delete['21_7_2'][] = 'module/solution'; +$config->delete['21_7_3'][] = 'extension/max/execution/ext/tao/zentaomax.php'; +$config->delete['21_7_3'][] = 'extension/ipd/execution/ext/tao/zentaomax.php'; + $config->delete['21_7_5'][] = 'extension/biz/doc/ext/js/app/zentaobiz.ui.js'; $config->delete['21_7_5'][] = 'extension/max/doc/ext/js/app/zentaobiz.ui.js'; $config->delete['21_7_5'][] = 'extension/ipd/doc/ext/js/app/zentaobiz.ui.js'; @@ -1704,6 +1707,17 @@ $config->delete['21_7_7'][] = 'extension/biz/dashboard'; $config->delete['21_7_7'][] = 'extension/max/dashboard'; $config->delete['21_7_7'][] = 'extension/ipd/dashboard'; +$config->delete['22_0'][] = 'extension/ipd/auditcl/ext/css/browse.css'; +$config->delete['22_0'][] = 'extension/max/auditcl/ext/css/browse.css'; +$config->delete['22_0'][] = 'extension/ipd/auditcl/ext/js/edit.js'; +$config->delete['22_0'][] = 'extension/max/auditcl/ext/js/edit.js'; +$config->delete['22_0'][] = 'extension/ipd/auditcl/view'; +$config->delete['22_0'][] = 'extension/max/auditcl/view'; +$config->delete['22_0'][] = 'extension/ipd/auditplan/view'; +$config->delete['22_0'][] = 'extension/max/auditplan/view'; +$config->delete['22_0'][] = 'extension/ipd/project/ext/tao/class/zentaoipd.class.php'; +$config->delete['22_0'][] = 'extension/ipd/project/ext/tao/zentaoipd.php'; + $config->upgrade->openModules = array('action', 'admin', 'ai', 'bi', 'aiapp', 'api', 'automation', 'backup', 'block', 'branch', 'budget', 'bug', 'build', 'cache', 'caselib', 'chart', 'ci', 'client', 'common', 'company', 'compile', 'convert', 'cron', 'custom', 'datatable', 'dataview', 'dept', 'design', 'dev', 'dimension', 'doc', 'durationestimation', 'entry', 'execution', 'extension', 'file', 'git', 'gitlab', 'group', 'holiday', 'im', 'index', 'index.html', 'install', 'issue', 'jenkins', 'job', 'kanban', 'license', 'mail', 'message', 'metric', 'misc', 'mr', 'my', 'personnel', 'pipeline', 'product', 'productplan', 'productset', 'program', 'programplan', 'project', 'projectbuild', 'projectplan', 'projectrelease', 'projectstory', 'pivot', 'qa', 'release', 'repo', 'report', 'risk', 'score', 'screen', 'search', 'setting', 'sonarqube', 'sso', 'stage', 'stakeholder', 'story', 'subject', 'svn', 'task', 'testcase', 'testreport', 'testsuite', 'testtask', 'todo', 'tree', 'tutorial', 'upgrade', 'user', 'webhook', 'weekly', 'workestimation', 'gitea', 'gogs', 'transfer', 'zahost', 'zanode', 'editor', 'charter', 'roadmap', 'account', 'cne', 'host', 'instance', 'ops', 'serverroom', 'space', 'store', 'system', 'solution', 'demand', 'gitfox', 'epic', 'requirement', 'mark', 'zai'); $config->upgrade->unsetModules = array('design', 'program', 'programplan', 'projectbuild', 'projectrelease', 'stage', 'stakeholder', 'product', 'branch', 'productplan', 'release', 'build', 'qa', 'bug', 'testcase', 'testtask', 'testreport', 'testsuite', 'caselib', 'automation', 'repo', 'ci', 'compile', 'jenkins', 'job', 'svn', 'gitlab', 'sonarqube', 'mr', 'git', 'report', 'sqlbuilder', 'feedback', 'faq', 'attend', 'holiday', 'leave', 'makeup', 'overtime', 'lieu', 'ops', 'host', 'serverroom', 'account', 'domain', 'service', 'deploy', 'conference', 'traincourse', 'pssp', 'baseline', 'classify', 'cm', 'cmcl', 'auditcl', 'reviewcl', 'process', 'activity', 'zoutput', 'auditplan', 'nc', 'subject', 'weekly', 'workestimation', 'issue', 'durationestimation', 'risk', 'opportunity', 'trainplan', 'gapanalysis', 'researchplan', 'researchreport', 'meeting', 'meetingroom', 'budget', 'reviewissue', 'reviewsetting', 'review', 'milestone', 'measurement', 'measrecord', 'assetlib', 'setting', 'im', 'client', 'ldap', 'dev', 'api', 'gitea', 'gogs', 'zanode', 'zahost'); diff --git a/module/upgrade/config/upgradeflow.php b/module/upgrade/config/upgradeflow.php index c4597b7850..6a3dc0c145 100644 --- a/module/upgrade/config/upgradeflow.php +++ b/module/upgrade/config/upgradeflow.php @@ -109,12 +109,12 @@ $config->upgrade->execFlow['21_1'] = array('functions' => 'processCacheCo $config->upgrade->execFlow['21_2'] = array('functions' => 'importBuildinWorkflow,addCharterApprovalFlow,processCharterFileConfig,processCharterStatus', 'params' => array('importBuildinWorkflow' => array('rnd', 'charter'))); $config->upgrade->execFlow['21_3'] = array('functions' => 'createDevOpsChartModule'); $config->upgrade->execFlow['21_6_beta'] = array('functions' => 'processCharterBranch'); -$config->upgrade->execFlow['21_6_1'] = array('xxsqls' => "$appRoot/db/upgradexuanxuan9.1.2.sql"); +$config->upgrade->execFlow['21_6_1'] = array('xxsqls' => "$appRoot/db/upgradexuanxuan9.1.2.sql"); $config->upgrade->execFlow['21_7'] = array('functions' => 'fixWorkflowNameForExecution'); $config->upgrade->execFlow['21_7_1'] = array('functions' => 'processActionProduct'); $config->upgrade->execFlow['21_7_5'] = array('functions' => 'weekly-addBuiltinWeeklyTemplate,adjustPriv21_7_5'); $config->upgrade->execFlow['21_7_7'] = array('functions' => 'alterTableFields'); -$config->upgrade->execFlow['21_7_8'] = array('functions' => 'initAIPrompts'); +$config->upgrade->execFlow['21_7_8'] = array('functions' => 'initAIPrompts,importBuildinWorkflow,processWorkflowDatasource,upgradeStage4PMS', 'params' => array('importBuildinWorkflow' => array('all', 'cm,projectchange,risk,opportunity,issue'), 'processWorkflowDatasource' => array(array('baselineStatus' => 'cm_status', 'baselineReviewResult' => 'cm_reviewResult', 'projectchangeUrgencyList' => 'projectchange_urgency', 'projectchangeTypeList' => 'projectchange_type', 'projectchangeStatus' => 'projectchange_status', 'projectchangeReviewResult' => 'projectchange_reviewResult', 'riskSource' => 'risk_source', 'riskCategory' => 'risk_category', 'riskStrategy' => 'risk_strategy', 'riskStatus' => 'risk_status', 'riskImpact' => 'risk_impact', 'riskProbability' => 'risk_probability', 'riskRate' => 'risk_rate', 'riskPri' => 'risk_pri', 'riskCancelReason' => 'risk_cancelReason', 'issuePri' => 'issue_pri', 'issueSeverity' => 'issue_severity', 'issueType' => 'issue_type', 'issueResolution' => 'issue_resolution', 'issueStatus' => 'issue_status', 'opportunitySource' => 'opportunity_source', 'opportunityType' => 'opportunity_type', 'opportunityStrategy' => 'opportunity_strategy', 'opportunityStatus' => 'opportunity_status', 'opportunityImpact' => 'opportunity_impact', 'opportunityChance' => 'opportunity_chance', 'opportunityPri' => 'opportunity_pri', 'opportunityCancelReason' => 'opportunity_cancelReason')))); if(!empty($config->isINT)) { @@ -147,6 +147,7 @@ if($config->edition != 'open') $config->upgrade->execFlow['18_3']['functions'] .= ',processDataset,processChart,processReport,processDashboard'; $config->upgrade->execFlow['18_4_beta1']['functions'] = 'processDeployStepAction,updateBISQL,updatePivotStage'; $config->upgrade->execFlow['20_4']['functions'] .= ',updateTaskRelationPriv'; + $config->upgrade->execFlow['21_7_8']['functions'] .= ',modifyProjectWorkflowGroup,upgradeAuditcl,upgradeProcessAndActivity,addWorkflowGroupOtherActivity,addDefaultDeliverableModule,upgradeDesignToDeliverable,buildinTestcaseStageDeliverable,upgradeDeliverable,buildinBaselineReview,upgradeReviewclCategory,upgradeBaselineObjects,upgradeReviewToDeliverable,upgradeBaseline,addDeliverablePrivs,upgradeStageAndPoint,upgradeObjectOfDecision,upgradeClosedFeature,parseDocFetcherURL,updateWorkflowGroupPriv'; } if(in_array($this->config->edition, array('max', 'ipd'))) $config->upgrade->execFlow['18_7']['functions'] = 'processOldMetrics,processHistoryDataForMetric,metric-updateMetricDate'; diff --git a/module/upgrade/lang/de.php b/module/upgrade/lang/de.php index b2d8d8cdba..d4d174accc 100644 --- a/module/upgrade/lang/de.php +++ b/module/upgrade/lang/de.php @@ -101,8 +101,10 @@ $lang->upgrade->unknownDate = 'Unknown Date Project'; $lang->upgrade->selectProject = 'The target project'; $lang->upgrade->programName = 'Program Name'; $lang->upgrade->projectName = 'Project Name'; +$lang->upgrade->projectManage = 'Project Manage'; $lang->upgrade->compatibleEXT = 'Extension mechanism compatible'; $lang->upgrade->fileName = 'File Name'; +$lang->upgrade->list = ' List'; $lang->upgrade->next = 'Next'; $lang->upgrade->back = 'Back'; @@ -218,3 +220,30 @@ $lang->upgrade->defaultCharterApprovalFlow->cancelProjectApproval->desc = 'You $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval = new stdclass(); $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->title = 'Project Activation Approval Workflow'; $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->desc = 'You can design the approval process for activating project approval.'; + +$lang->upgrade->deliverableModule['plan'] = 'Plan'; +$lang->upgrade->deliverableModule['story'] = 'Story'; +$lang->upgrade->deliverableModule['design'] = 'Design'; +$lang->upgrade->deliverableModule['test'] = 'Test'; +$lang->upgrade->deliverableModule['other'] = 'Other'; + +$lang->upgrade->reviewObjectList['PP'] = 'Project Plan'; +$lang->upgrade->reviewObjectList['QAP'] = 'Quality Assurance Plan'; +$lang->upgrade->reviewObjectList['CMP'] = 'Configuration Management Plan'; +$lang->upgrade->reviewObjectList['ITP'] = 'Integration Test Plan'; +$lang->upgrade->reviewObjectList['ERS'] = 'Epic Statement'; +$lang->upgrade->reviewObjectList['URS'] = 'User Requirement Statement'; +$lang->upgrade->reviewObjectList['SRS'] = 'Project Requirements Specification'; +$lang->upgrade->reviewObjectList['HLDS'] = 'High Level Design Statement'; +$lang->upgrade->reviewObjectList['DDS'] = 'Detailed Design Statement'; +$lang->upgrade->reviewObjectList['DBDS'] = 'Database Design Statement'; +$lang->upgrade->reviewObjectList['ADS'] = 'Interface Design Statement'; +$lang->upgrade->reviewObjectList['Code'] = 'Code'; +$lang->upgrade->reviewObjectList['intergrate'] = 'Integrate Test Cases'; +$lang->upgrade->reviewObjectList['STP'] = 'System Test Plan'; +$lang->upgrade->reviewObjectList['system'] = 'System Test Cases'; +$lang->upgrade->reviewObjectList['UM'] = 'User Manual'; + +$lang->upgrade->baselineReview = array(); +$lang->upgrade->baselineReview['baseline'] = 'Baseline Review'; +$lang->upgrade->baselineReview['change'] = 'Project Change Review'; diff --git a/module/upgrade/lang/en.php b/module/upgrade/lang/en.php index b165f218ed..a63cc0708b 100644 --- a/module/upgrade/lang/en.php +++ b/module/upgrade/lang/en.php @@ -104,8 +104,10 @@ $lang->upgrade->unknownDate = 'Unknown Date Project'; $lang->upgrade->selectProject = 'The target project'; $lang->upgrade->programName = 'Program Name'; $lang->upgrade->projectName = 'Project Name'; +$lang->upgrade->projectManage = 'Project Manage'; $lang->upgrade->compatibleEXT = 'Extension mechanism compatible'; $lang->upgrade->fileName = 'File Name'; +$lang->upgrade->list = ' List'; $lang->upgrade->next = 'Next'; $lang->upgrade->back = 'Back'; @@ -221,3 +223,30 @@ $lang->upgrade->defaultCharterApprovalFlow->cancelProjectApproval->desc = 'You $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval = new stdclass(); $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->title = 'Project Activation Approval Workflow'; $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->desc = 'You can design the approval process for activating project approval.'; + +$lang->upgrade->deliverableModule['plan'] = 'Plan'; +$lang->upgrade->deliverableModule['story'] = 'Story'; +$lang->upgrade->deliverableModule['design'] = 'Design'; +$lang->upgrade->deliverableModule['test'] = 'Test'; +$lang->upgrade->deliverableModule['other'] = 'Other'; + +$lang->upgrade->reviewObjectList['PP'] = 'Project Plan'; +$lang->upgrade->reviewObjectList['QAP'] = 'Quality Assurance Plan'; +$lang->upgrade->reviewObjectList['CMP'] = 'Configuration Management Plan'; +$lang->upgrade->reviewObjectList['ITP'] = 'Integration Test Plan'; +$lang->upgrade->reviewObjectList['ERS'] = 'Epic Statement'; +$lang->upgrade->reviewObjectList['URS'] = 'User Requirement Statement'; +$lang->upgrade->reviewObjectList['SRS'] = 'Project Requirements Specification'; +$lang->upgrade->reviewObjectList['HLDS'] = 'High Level Design Statement'; +$lang->upgrade->reviewObjectList['DDS'] = 'Detailed Design Statement'; +$lang->upgrade->reviewObjectList['DBDS'] = 'Database Design Statement'; +$lang->upgrade->reviewObjectList['ADS'] = 'Interface Design Statement'; +$lang->upgrade->reviewObjectList['Code'] = 'Code'; +$lang->upgrade->reviewObjectList['intergrate'] = 'Integrate Test Cases'; +$lang->upgrade->reviewObjectList['STP'] = 'System Test Plan'; +$lang->upgrade->reviewObjectList['system'] = 'System Test Cases'; +$lang->upgrade->reviewObjectList['UM'] = 'User Manual'; + +$lang->upgrade->baselineReview = array(); +$lang->upgrade->baselineReview['baseline'] = 'Baseline Review'; +$lang->upgrade->baselineReview['change'] = 'Project Change Review'; diff --git a/module/upgrade/lang/fr.php b/module/upgrade/lang/fr.php index 94b60a08ef..5f51e2ba1d 100644 --- a/module/upgrade/lang/fr.php +++ b/module/upgrade/lang/fr.php @@ -101,8 +101,10 @@ $lang->upgrade->unknownDate = 'Unknown Date Project'; $lang->upgrade->selectProject = 'The target project'; $lang->upgrade->programName = 'Program Name'; $lang->upgrade->projectName = 'Project Name'; +$lang->upgrade->projectManage = 'Project Manage'; $lang->upgrade->compatibleEXT = 'Extension mechanism compatible'; $lang->upgrade->fileName = 'File Name'; +$lang->upgrade->list = ' List'; $lang->upgrade->next = 'Next'; $lang->upgrade->back = 'Back'; @@ -218,3 +220,30 @@ $lang->upgrade->defaultCharterApprovalFlow->cancelProjectApproval->desc = 'You $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval = new stdclass(); $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->title = 'Project Activation Approval Workflow'; $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->desc = 'You can design the approval process for activating project approval.'; + +$lang->upgrade->deliverableModule['plan'] = 'Plan'; +$lang->upgrade->deliverableModule['story'] = 'Story'; +$lang->upgrade->deliverableModule['design'] = 'Design'; +$lang->upgrade->deliverableModule['test'] = 'Test'; +$lang->upgrade->deliverableModule['other'] = 'Other'; + +$lang->upgrade->reviewObjectList['PP'] = 'Project Plan'; +$lang->upgrade->reviewObjectList['QAP'] = 'Quality Assurance Plan'; +$lang->upgrade->reviewObjectList['CMP'] = 'Configuration Management Plan'; +$lang->upgrade->reviewObjectList['ITP'] = 'Integration Test Plan'; +$lang->upgrade->reviewObjectList['ERS'] = 'Epic Statement'; +$lang->upgrade->reviewObjectList['URS'] = 'User Requirement Statement'; +$lang->upgrade->reviewObjectList['SRS'] = 'Project Requirements Specification'; +$lang->upgrade->reviewObjectList['HLDS'] = 'High Level Design Statement'; +$lang->upgrade->reviewObjectList['DDS'] = 'Detailed Design Statement'; +$lang->upgrade->reviewObjectList['DBDS'] = 'Database Design Statement'; +$lang->upgrade->reviewObjectList['ADS'] = 'Interface Design Statement'; +$lang->upgrade->reviewObjectList['Code'] = 'Code'; +$lang->upgrade->reviewObjectList['intergrate'] = 'Integrate Test Cases'; +$lang->upgrade->reviewObjectList['STP'] = 'System Test Plan'; +$lang->upgrade->reviewObjectList['system'] = 'System Test Cases'; +$lang->upgrade->reviewObjectList['UM'] = 'User Manual'; + +$lang->upgrade->baselineReview = array(); +$lang->upgrade->baselineReview['baseline'] = 'Baseline Review'; +$lang->upgrade->baselineReview['change'] = 'Project Change Review'; diff --git a/module/upgrade/lang/version.php b/module/upgrade/lang/version.php index f500cb01ab..208e9f7229 100644 --- a/module/upgrade/lang/version.php +++ b/module/upgrade/lang/version.php @@ -563,4 +563,5 @@ $lang->upgrade->fromVersions['ipd4_3'] = 'Ipd4.3'; $lang->upgrade->fromVersions['ipd4_3'] = 'Ipd4.3'; $lang->upgrade->fromVersions['ipd4_4'] = 'Ipd4.4'; $lang->upgrade->fromVersions['ipd4_5'] = 'Ipd4.5'; -$lang->upgrade->fromVersions['ipd4_6'] = 'Ipd4.6'; // ipd insert position. +$lang->upgrade->fromVersions['ipd4_6'] = 'Ipd4.6'; +$lang->upgrade->fromVersions['ipd4_7'] = 'Ipd4.7'; // ipd insert position. diff --git a/module/upgrade/lang/zh-cn.php b/module/upgrade/lang/zh-cn.php index 46c0e30155..33a6e41a23 100644 --- a/module/upgrade/lang/zh-cn.php +++ b/module/upgrade/lang/zh-cn.php @@ -104,8 +104,10 @@ $lang->upgrade->unknownDate = '无明确时间的项目'; $lang->upgrade->selectProject = '目标项目'; $lang->upgrade->programName = '项目集名称'; $lang->upgrade->projectName = '项目名称'; +$lang->upgrade->projectManage = '项目管理'; $lang->upgrade->compatibleEXT = '扩展机制兼容'; $lang->upgrade->fileName = '文件名称'; +$lang->upgrade->list = '的列表'; $lang->upgrade->next = '下一步'; $lang->upgrade->back = '上一步'; @@ -221,3 +223,30 @@ $lang->upgrade->defaultCharterApprovalFlow->cancelProjectApproval->desc = '可 $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval = new stdclass(); $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->title = '激活立项审批流'; $lang->upgrade->defaultCharterApprovalFlow->activateProjectApproval->desc = '可以为激活立项审批设计审批流程。'; + +$lang->upgrade->deliverableModule['plan'] = '计划类'; +$lang->upgrade->deliverableModule['story'] = '需求类'; +$lang->upgrade->deliverableModule['design'] = '设计类'; +$lang->upgrade->deliverableModule['test'] = '测试类'; +$lang->upgrade->deliverableModule['other'] = '其他类'; + +$lang->upgrade->reviewObjectList['PP'] = '项目计划'; +$lang->upgrade->reviewObjectList['QAP'] = '质量保证计划'; +$lang->upgrade->reviewObjectList['CMP'] = '配置管理计划'; +$lang->upgrade->reviewObjectList['ITP'] = '集成测试计划'; +$lang->upgrade->reviewObjectList['ERS'] = '业务需求说明书'; +$lang->upgrade->reviewObjectList['URS'] = '用户需求说明书'; +$lang->upgrade->reviewObjectList['SRS'] = '项目需求规格说明书'; +$lang->upgrade->reviewObjectList['HLDS'] = '概要设计说明书'; +$lang->upgrade->reviewObjectList['DDS'] = '详细设计说明书'; +$lang->upgrade->reviewObjectList['DBDS'] = '数据库设计文档'; +$lang->upgrade->reviewObjectList['ADS'] = '接口设计文档'; +$lang->upgrade->reviewObjectList['Code'] = '程序代码'; +$lang->upgrade->reviewObjectList['intergrate'] = '集成测试用例'; +$lang->upgrade->reviewObjectList['STP'] = '系统测试计划'; +$lang->upgrade->reviewObjectList['system'] = '系统测试用例'; +$lang->upgrade->reviewObjectList['UM'] = '用户手册'; + +$lang->upgrade->baselineReview = array(); +$lang->upgrade->baselineReview['baseline'] = '基线评审'; +$lang->upgrade->baselineReview['change'] = '项目变更评审'; diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 3e64914e4b..45b0239beb 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -3718,7 +3718,7 @@ class upgradeModel extends model $projectList = $result; } - else if(!empty($data->projects)) + elseif(!empty($data->projects)) { $projectList = (int)$data->projects; $this->dao->update(TABLE_PROJECT)->set('status')->eq($data->projectStatus)->where('id')->eq($projectList)->exec(); @@ -9400,7 +9400,7 @@ class upgradeModel extends model $data->app = $app; foreach($appModules as $module => $options) { - if($importModule && $importModule != $module) continue; + if($importModule && strpos(",$importModule,", ",$module,") === false) continue; $this->app->loadLang($module); @@ -9432,7 +9432,7 @@ class upgradeModel extends model $data->createdDate = $now; foreach($actions as $module => $moduleActions) { - if($importModule && $importModule != $module) continue; + if($importModule && strpos(",$importModule,", ",$module,") === false) continue; $data->module = $module; foreach($moduleActions as $action) @@ -9480,7 +9480,7 @@ class upgradeModel extends model $data->createdDate = $now; foreach($fields as $module => $moduleFields) { - if($importModule && $importModule != $module) continue; + if($importModule && strpos(",$importModule,", ",$module,") === false) continue; $order = 1; $data->module = $module; @@ -9521,7 +9521,7 @@ class upgradeModel extends model $data = new stdclass(); foreach($layouts as $module => $moduleLayouts) { - if($importModule && $importModule != $module) continue; + if($importModule && strpos(",$importModule,", ",$module,") === false) continue; $data->module = $module; foreach($moduleLayouts as $action => $layoutFields) @@ -9559,7 +9559,7 @@ class upgradeModel extends model { foreach($appModules as $module => $options) { - if($importModule && $importModule != $module) continue; + if($importModule && strpos(",$importModule,", ",$module,") === false) continue; $labels = array(); if($module == 'product') @@ -11230,6 +11230,894 @@ class upgradeModel extends model return array('blockTitle' => $blockTitle, 'exportUrl' => $exportUrl, 'fetcherUrl' => $fetcherUrl); } + /** + * + * 为项目流程内置其他活动。 + * Add other activity. + * + * @access public + * @return void + */ + public function addWorkflowGroupOtherActivity() + { + $projectModules = $this->dao->select('root,id')->from(TABLE_MODULE)->where('type')->eq('process')->andWhere('extra')->eq('project')->fetchPairs(); + $groupList = $this->dao->select('*')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->andWhere('projectModel')->ne('kanban')->fetchAll(); + foreach($groupList as $group) + { + if($this->config->edition != 'open' && $group->projectModel == 'ipd') + { + $this->loadModel('workflowGroup')->addProcessAndActivity($group, 'ipd'); + } + elseif(!empty($projectModules[$group->id])) + { + $this->createOtherActivity($projectModules[$group->id], $group->id); + } + } + } + + /** + * 为项目流程内置默认分类。 + * Add deliverable default modules. + * + * @access public + * @return void + */ + public function addDefaultDeliverableModule() + { + $groupList = $this->dao->select('id')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->andWhere('projectModel')->ne('kanban')->fetchAll(); + foreach($groupList as $group) + { + foreach($this->lang->upgrade->deliverableModule as $code => $name) $this->createDeliverableModule($group->id, $name, $code); + } + } + + /** + * 升级设计类型为交付物。 + * Upgrade design type to deliverable. + * + * @access public + * @return void + */ + public function upgradeDesignToDeliverable() + { + $this->app->loadLang('design'); + $clientLang = $this->app->getClientLang(); + $typeList = $this->dao->select('`key`, value')->from(TABLE_LANG) + ->where('module')->eq('design') + ->andWhere('section')->eq('typeList') + ->andWhere('lang')->in(array($clientLang, 'all')) + ->orderBy('id_asc') + ->fetchPairs(); + + $plusTypeList = $this->dao->select('`key`, value')->from(TABLE_LANG) + ->where('module')->eq('design') + ->andWhere('section')->eq('plusTypeList') + ->andWhere('lang')->in(array($clientLang, 'all')) + ->orderBy('id_asc') + ->fetchPairs(); + + if($typeList) $this->lang->design->typeList = $typeList; + if($plusTypeList) $this->lang->design->plusTypeList = $plusTypeList; + + $modelList = array('waterfall', 'waterfallplus', 'ipd'); + $moduleList = $this->dao->select('t1.id,t1.name,t2.projectModel,t2.id as workflowGroup')->from(TABLE_MODULE)->alias('t1') + ->leftJoin(TABLE_WORKFLOWGROUP)->alias('t2')->on('t1.root=t2.id') + ->where('t1.type')->eq('deliverable') + ->andWhere('t1.extra')->eq('design') + ->andWhere('t2.projectModel')->in($modelList) + ->fetchAll(); + + $activityList = $this->dao->select('t1.*')->from(TABLE_ACTIVITY)->alias('t1')->leftJoin(TABLE_PROCESS)->alias('t2')->on('t1.process=t2.id') + ->where('t1.name')->eq($this->lang->other) + ->andWhere('t2.name')->eq($this->lang->other) + ->fetchGroup('workflowGroup'); + + $projectWorkflowGroup = $this->dao->select('id,workflowGroup')->from(TABLE_PROJECT)->where('type')->eq('project')->fetchGroup('workflowGroup', 'id'); + + $deliverable = new stdClass(); + $deliverable->status = 'enabled'; + $deliverable->createdBy = 'system'; + $deliverable->createdDate = helper::now(); + $deliverable->systemList = 1; + $deliverable->trimmable = '1'; + $deliverable->template = '[]'; + + $deliverableStage = new stdClass(); + $deliverableStage->required = '0'; + $deliverableStage->stage = 'project'; + foreach($moduleList as $module) + { + if(empty($activityList[$module->workflowGroup])) continue; + $otherActivity = reset($activityList[$module->workflowGroup]); + $otherActivityID = $otherActivity->id; + + $nameFilter = array(); + $deliverable->workflowGroup = $module->workflowGroup; + $deliverable->module = $module->id; + $deliverable->activity = $otherActivityID; + foreach(array_filter($this->lang->design->typeList) as $key => $value) + { + if(empty($value) || !in_array($module->projectModel, array('waterfall', 'ipd'))) continue; + $deliverable->category = $key; // 将设计的类型转换为交付物的类型。 + $deliverableID = $this->addDeliverable((string)$value, $deliverable, $deliverableStage, $nameFilter); + + /* 将历史设计的设计类型替换为交付物ID。 */ + if(!empty($projectWorkflowGroup[$module->workflowGroup])) + { + $projects = array_keys($projectWorkflowGroup[$module->workflowGroup]); + $this->dao->update(TABLE_DESIGN)->set('type')->eq($deliverableID)->where('type')->eq($key)->andWhere('project')->in($projects)->exec(); + } + } + + foreach(array_filter($this->lang->design->plusTypeList) as $key => $value) + { + if(empty($value) || $module->projectModel != 'waterfallplus') continue; + $deliverable->category = $key; // 将设计的类型转换为交付物的类型。 + $deliverableID = $this->addDeliverable((string)$value, $deliverable, $deliverableStage, $nameFilter); + + /* 将历史设计的设计类型替换为交付物ID。 */ + if(!empty($projectWorkflowGroup[$module->workflowGroup])) + { + $projects = array_keys($projectWorkflowGroup[$module->workflowGroup]); + $this->dao->update(TABLE_DESIGN)->set('type')->eq($deliverableID)->where('type')->eq($key)->andWhere('project')->in($projects)->exec(); + } + } + } + } + + /** + * 创建交付物。 + * Add deliverable. + * + * @param string $name + * @param object $deliverable + * @param object $deliverableStage + * @param array $nameFilter + * @access public + * @return int + */ + public function addDeliverable(string $name, object $deliverable, object $deliverableStage, array &$nameFilter = array()): int + { + /* 重名的交付物名称后面加数字。 */ + if(!empty($nameFilter[$name])) + { + $deliverable->name = $name . $nameFilter[$name]; + $nameFilter[$name] ++; + } + else + { + $deliverable->name = $name; + $nameFilter[$name] = 1; + } + $this->dao->insert(TABLE_DELIVERABLE)->data($deliverable)->exec(); + $deliverableID = $this->dao->lastInsertID(); + + $deliverableStage->deliverable = $deliverableID; + $this->dao->insert(TABLE_DELIVERABLESTAGE)->data($deliverableStage)->exec(); + + return $deliverableID; + } + + /** + * 将通用交付物升级为项目流程下的交付物。 + * Upgrade deliverable. + * + * @access public + * @return void + */ + public function upgradeDeliverable() + { + $this->app->loadLang('tree'); + + $deliverable = new stdClass(); + $deliverable->status = 'disabled'; + $deliverable->trimmable = '1'; + + $deliverableStage = new stdClass(); + $deliverableStage->stage = 'project'; + $deliverableStage->required = '0'; + + $deliverableList = array(); + $nameFilter = array(); // 过滤重名交付物。 + $workflowGroups = $this->dao->select('id,deliverable,projectModel,projectType')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->andWhere('projectModel')->ne('kanban')->fetchAll(); + $deliverables = $this->dao->select('id,name,model,`desc`,createdBy,createdDate')->from(TABLE_DELIVERABLE)->where('deleted')->eq('0')->andWhere('model')->ne('')->fetchAll('id'); + $fileList = $this->dao->select('id,title,objectType,objectID')->from(TABLE_FILE)->where('objectType')->eq('deliverable')->fetchAll('objectID'); + $otherModules = $this->dao->select('root,id')->from(TABLE_MODULE)->where('type')->eq('deliverable')->andWhere('extra')->eq('other')->fetchPairs(); + $activityList = $this->dao->select('t1.*')->from(TABLE_ACTIVITY)->alias('t1')->leftJoin(TABLE_PROCESS)->alias('t2')->on('t1.process=t2.id') + ->where('t1.name')->eq($this->lang->other) + ->andWhere('t2.name')->eq($this->lang->other) + ->fetchGroup('workflowGroup'); + + /* 将旧的交付物按照适用范围升级到各个项目流程中。 */ + foreach($deliverables as $oldDeliverable) + { + foreach(explode(',', $oldDeliverable->model) as $model) + { + foreach($workflowGroups as $workflowGroup) + { + if($workflowGroup->projectModel == 'kanban') $workflowGroup->projectModel = 'scrum_kanban'; + if(!empty($deliverableList[$workflowGroup->id][$oldDeliverable->id])) continue; + if(strpos($model, "{$workflowGroup->projectType}_{$workflowGroup->projectModel}") === false) continue; + + if(empty($activityList[$workflowGroup->id])) continue; + $otherActivity = reset($activityList[$workflowGroup->id]); + $otherActivityID = $otherActivity->id; + + $deliverableFile = $fileList[$oldDeliverable->id]; // 原交付物只会上传一个附件。 + $deliverable->workflowGroup = $workflowGroup->id; + $deliverable->desc = $oldDeliverable->desc; + $deliverable->module = isset($otherModules[$workflowGroup->id]) ? $otherModules[$workflowGroup->id] : '0'; + $deliverable->activity = $otherActivityID; + $deliverable->template = $deliverableFile ? '{"new_0":{"name":"' . $deliverableFile->title . '","doc":"","fileID":"' . $deliverableFile->id . '"}}' : '[]'; + $deliverable->createdBy = $oldDeliverable->createdBy; + $deliverable->createdDate = !empty($oldDeliverable->createdDate) ? $oldDeliverable->createdDate : null; + + /* 重名的交付物名称后面加数字。 */ + if(!empty($nameFilter[$workflowGroup->id][$oldDeliverable->name])) + { + $deliverable->name = $oldDeliverable->name . $nameFilter[$workflowGroup->id][$oldDeliverable->name]; + $nameFilter[$workflowGroup->id][$oldDeliverable->name] ++; + } + else + { + $deliverable->name = $oldDeliverable->name; + $nameFilter[$workflowGroup->id][$oldDeliverable->name] = 1; + } + + $this->dao->insert(TABLE_DELIVERABLE)->data($deliverable)->exec(); + $deliverableID = $this->dao->lastInsertID(); + $deliverableList[$workflowGroup->id][$oldDeliverable->id] = $deliverableID; + + $deliverableStage->deliverable = $deliverableID; + $this->dao->insert(TABLE_DELIVERABLESTAGE)->data($deliverableStage)->exec(); + } + } + } + + $sprintFilter = array(); // 过滤重复交付物检查规则。 + /* 按照项目流程下的交付物配置设置各个交付物的检查规则。 */ + foreach($workflowGroups as $workflowGroup) + { + /* 解析项目流程的交付物配置。 */ + $groupDeliverable = !empty($workflowGroup->deliverable) ? json_decode($workflowGroup->deliverable, true) : array(); + foreach($groupDeliverable as $stageCode => $methodDeliverable) + { + foreach($methodDeliverable as $deliverableConfigs) + { + foreach($deliverableConfigs as $config) + { + if(empty($deliverableList[$workflowGroup->id][$config['deliverable']])) continue; + + $deliverableID = $deliverableList[$workflowGroup->id][$config['deliverable']]; + $workflowGroupModel = "{$workflowGroup->projectType}_{$workflowGroup->projectModel}"; + $deliverableStage->stage = $stageCode == $workflowGroupModel ? 'project' : str_replace("{$workflowGroupModel}_", '', $stageCode); + $deliverableStage->required = !empty($config['required']) ? '1' : '0'; + $deliverableStage->deliverable = $deliverableID; + + /* 如果这个交付物在项目流程下有交付物配置,则启用交付物并删除默认的交付物检查规则。 */ + if(!isset($sprintFilter[$deliverableID])) + { + $this->dao->update(TABLE_DELIVERABLE)->set('status')->eq('enabled')->where('id')->eq($deliverableID)->exec(); + $this->dao->delete()->from(TABLE_DELIVERABLESTAGE)->where('deliverable')->eq($deliverableID)->andWhere('stage')->eq('project')->exec(); + } + + /* 按照交付物配置生成交付物检查规则。 */ + if(in_array($deliverableStage->stage, array('short', 'long', 'ops'))) + { + /* 将原来不同类型的迭代合并成一个迭代。 */ + $deliverableStage->stage = 'sprint'; + /* 其中有一个类型是必填的合并后的结果就是必填。 */ + if(isset($sprintFilter[$deliverableID]['sprint']) && empty($sprintFilter[$deliverableID]['sprint']) && !empty($deliverableStage->required)) + { + $this->dao->update(TABLE_DELIVERABLESTAGE)->set('required')->eq('1')->where('deliverable')->eq($deliverableID)->andWhere('stage')->eq('sprint')->exec(); + } + if(!isset($sprintFilter[$deliverableID]['sprint'])) + { + $this->dao->insert(TABLE_DELIVERABLESTAGE)->data($deliverableStage)->exec(); + } + } + elseif(!isset($sprintFilter[$deliverableID][$deliverableStage->stage])) + { + $this->dao->insert(TABLE_DELIVERABLESTAGE)->data($deliverableStage)->exec(); + } + $sprintFilter[$deliverableID][$deliverableStage->stage] = $deliverableStage->required; + } + } + } + + $this->createOtherDeliverable($workflowGroup->id); // 创建名为其他的交付物类型。 + } + + /* 将已升级的历史交付物数据删掉。 */ + $this->dao->delete()->from(TABLE_DELIVERABLE)->where('id')->in(array_keys($deliverables))->andWhere('workflowGroup')->eq('0')->exec(); + + /* 升级完后把没用的字段删掉。 */ + $this->dao->exec("ALTER TABLE " . TABLE_DELIVERABLE . " CHANGE `module` `module` mediumint(8) unsigned NOT NULL DEFAULT '0';"); + + if($deliverableList) $this->upgradeProjectDeliverable($deliverableList); + } + + /** + * 内置交付物其他分类。 + * Create other module. + * + * @param int $workflowGroupID + * @param string $name + * @param string $extra + * @access public + * @return int + */ + public function createDeliverableModule(int $workflowGroupID, string $name, string $extra = ''): int + { + $module = new stdclass(); + $module->root = $workflowGroupID; + $module->branch = '0'; + $module->name = $name; + $module->parent = '0'; + $module->path = ''; + $module->grade = '1'; + $module->order = '0'; + $module->type = 'deliverable'; + $module->from = '0'; + $module->owner = ''; + $module->collector = null; + $module->short = ''; + $module->extra = $extra; + $module->deleted = '0'; + $this->dao->insert(TABLE_MODULE)->data($module)->exec(); + + $moduleID = $this->dao->lastInsertID(); + $this->dao->update(TABLE_MODULE)->set('path')->eq(",$moduleID,")->where('id')->eq($moduleID)->exec(); + + return $moduleID; + } + + /** + * 创建名为其他的交付物类型。 + * Create other deliverable. + * + * @param int $workflowGroupID + * @param int $moduleID + * @access public + * @return void + */ + public function createOtherDeliverable(int $workflowGroupID) + { + $moduleID = $this->dao->select('id')->from(TABLE_MODULE)->where('type')->eq('deliverable')->andWhere('root')->eq($workflowGroupID)->andWhere('extra')->eq('other')->fetch('id'); + $activityID = $this->dao->select('id')->from(TABLE_ACTIVITY)->where('name')->eq($this->lang->other)->andWhere('workflowGroup')->eq($workflowGroupID)->fetch('id'); + + /* 创建名为其他的交付物类型。 */ + $deliverable = new stdclass(); + $deliverable->name = $this->lang->other; + $deliverable->category = 'other'; + $deliverable->module = $moduleID; + $deliverable->activity = $activityID ? $activityID : 0; + $deliverable->workflowGroup = $workflowGroupID; + $deliverable->template = '[]'; + $deliverable->trimmable = '1'; + $deliverable->builtin = '1'; + $deliverable->status = 'enabled'; + $deliverable->createdBy = 'system'; + $deliverable->createdDate = helper::now(); + + $this->dao->insert(TABLE_DELIVERABLE)->data($deliverable)->exec(); + $deliverableID = $this->dao->lastInsertID(); + + $stageList = $this->config->edition != 'biz' ? $this->loadModel('deliverable')->buildStageList($workflowGroupID) : array(); + foreach($stageList as $key => $stage) + { + $deliverableStage = new stdclass(); + $deliverableStage->deliverable = $deliverableID; + $deliverableStage->stage = $key; + $deliverableStage->required = '0'; + $this->dao->insert(TABLE_DELIVERABLESTAGE)->data($deliverableStage)->exec(); + } + } + + /** + * 内置默认的过程活动。 + * Create other process and activity. + * + * @param int $moduleID + * @param int $workflowGroupID + * @access public + * @return int + */ + public function createOtherActivity(int $moduleID, int $workflowGroupID): int + { + $existsProcess = $this->dao->select('id')->from(TABLE_PROCESS)->where('workflowGroup')->eq($workflowGroupID)->andWhere('name')->eq($this->lang->other)->fetch('id'); + if($existsProcess) return $existsProcess; + + $process = new stdclass(); + $process->workflowGroup = $workflowGroupID; + $process->name = $this->lang->other; + $process->module = $moduleID; + $process->createdBy = 'system'; + $process->createdDate = '2020-01-09 00:00:00'; + $process->editedDate = null; + $process->assignedDate = null; + $process->deleted = '0'; + $this->dao->insert(TABLE_PROCESS)->data($process)->exec(); + + $processID = $this->dao->lastInsertID(); + $this->dao->update(TABLE_PROCESS)->set('`order`')->eq($processID * 5)->where('id')->eq($processID)->exec(); + + $activity = new stdclass(); + $activity->workflowGroup = $workflowGroupID; + $activity->process = $processID; + $activity->name = $this->lang->other; + $activity->optional = 'no'; + $activity->createdBy = 'system'; + $activity->createdDate = '2020-01-09 00:00:00'; + $activity->editedDate = null; + $activity->assignedDate = null; + $activity->deleted = '0'; + $this->dao->insert(TABLE_ACTIVITY)->data($activity)->exec(); + + $activityID = $this->dao->lastInsertID(); + $this->dao->update(TABLE_ACTIVITY)->set('`order`')->eq($activityID * 5)->where('id')->eq($activityID)->exec(); + + return $activityID; + } + + /** + * 内置测试环节列表交付物。 + * Buildin testcase stage deliverable. + * + * @access public + * @return bool + */ + public function buildinTestcaseStageDeliverable(): bool + { + $this->app->loadLang('testcase'); + $clientLang = $this->app->getClientLang(); + $testcaseStageList = $this->dao->select('`key`, value')->from(TABLE_LANG) + ->where('module')->eq('testcase') + ->andWhere('section')->eq('stageList') + ->andWhere('lang')->in(array($clientLang, 'all')) + ->orderBy('id_asc') + ->fetchPairs(); + + if(empty($testcaseStageList)) $testcaseStageList = $this->lang->testcase->stageList; + $this->createTestcaseDeliverable($testcaseStageList); + + return true; + } + + /** + * 创建测试用例阶段类型交付物。 + * Create testcase deliverable. + * + * @param array $modules + * @access public + * @return bool + */ + public function createTestcaseDeliverable(array $modules): bool + { + $testModules = $this->dao->select('root,id')->from(TABLE_MODULE) + ->where('type')->eq('deliverable') + ->andWhere('extra')->eq('test') + ->fetchPairs(); + + $testDeliverable = $this->dao->select('category')->from(TABLE_DELIVERABLE) + ->where('builtin')->eq('0') + ->andWhere('module')->in($testModules) + ->fetchPairs(); + + /* 已经存在但是被删除的交付物进行还原操作。 */ + foreach($modules as $key => $name) + { + if(empty($name)) continue; + if(!empty($testDeliverable[$key])) + { + $this->dao->update(TABLE_DELIVERABLE)->set('deleted')->eq('0')->set('name')->eq($name . $this->lang->upgrade->list)->where('category')->eq($key)->andWhere('builtin')->eq('0')->exec(); + unset($modules[$key]); + } + } + + $activityList = $this->dao->select('t1.*')->from(TABLE_ACTIVITY)->alias('t1')->leftJoin(TABLE_PROCESS)->alias('t2')->on('t1.process=t2.id') + ->where('t1.name')->eq($this->lang->other) + ->andWhere('t2.name')->eq($this->lang->other) + ->fetchGroup('workflowGroup'); + + if(empty($modules)) return true; + + $deliverable = new stdClass(); + $deliverable->status = 'enabled'; + $deliverable->createdBy = 'system'; + $deliverable->createdDate = helper::now(); + $deliverable->template = '[]'; + $deliverable->trimmable = '1'; + $deliverable->builtin = '0'; + $deliverable->systemList = 1; + + $deliverableStage = new stdClass(); + $deliverableStage->required = '0'; + $deliverableStage->stage = 'project'; + + $workflows = $this->dao->select('id,projectModel')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->andWhere('projectModel')->ne('kanban')->fetchAll('id'); + foreach($workflows as $workflow) + { + $groupID = $workflow->id; + + if(empty($activityList[$groupID])) continue; + $otherActivity = reset($activityList[$groupID]); + $otherActivityID = $otherActivity->id; + + $deliverable->module = zget($testModules, $workflow->id, 0); + $deliverable->workflowGroup = $workflow->id; + $deliverable->activity = $otherActivityID; + foreach($modules as $key => $name) + { + if(empty($name)) continue; + $deliverable->category = $key; // 标记交付物的类型。 + $this->addDeliverable($name . $this->lang->upgrade->list, $deliverable, $deliverableStage); + } + } + return true; + } + + /** + * 删除测试用例阶段交付物。 + * Delete testcase deliverable. + * + * @param array $modules + * @access public + * @return bool + */ + public function deleteTestcaseDeliverable(array $modules): bool + { + foreach($modules as $key => $name) + { + $this->dao->update(TABLE_DELIVERABLE)->set('deleted')->eq('1')->where('category')->eq($key)->andWhere('builtin')->eq('0')->exec(); + } + return true; + } + + /** + * 变更测试用例阶段交付物名称。 + * Change testcase deliverable. + * + * @param array $modules + * @access public + * @return bool + */ + public function changeTestcaseDeliverable(array $modules): bool + { + foreach($modules as $key => $name) + { + $this->dao->update(TABLE_DELIVERABLE)->set('name')->eq($name . $this->lang->upgrade->list)->where('category')->eq($key)->andWhere('builtin')->eq('0')->exec(); + } + return true; + } + + + /** + * 升级项目和迭代的交付物配置。 + * Upgrade project deliverable. + * + * @param array $deliverableList + * @access public + * @return void + */ + public function upgradeProjectDeliverable(array $deliverableList) + { + $projectList = $this->dao->select('id,deliverable,workflowGroup,type,project')->from(TABLE_PROJECT)->where('deliverable')->ne('')->fetchAll('id'); + $projectMainLibPairs = $this->dao->select('project, id')->from(TABLE_DOCLIB)->where('main')->eq('1')->andWhere('type')->eq('project')->andWhere('project')->in(array_keys($projectList))->fetchPairs(); + $executionMainLibPairs = $this->dao->select('execution, id')->from(TABLE_DOCLIB)->where('main')->eq('1')->andWhere('type')->eq('execution')->andWhere('execution')->in(array_keys($projectList))->fetchPairs(); + $fileList = $this->dao->select('*')->from(TABLE_FILE)->where('deleted')->eq('0')->andWhere('extra')->like('deliverable%')->fetchAll('id'); + $otherDeliverablePairs = $this->dao->select('workflowGroup, id')->from(TABLE_DELIVERABLE)->where('category')->eq('other')->andWhere('builtin')->eq('1')->fetchPairs(); + + foreach($projectList as $project) + { + $oldProjectDeliverable = !empty($project->deliverable) ? json_decode($project->deliverable, true) : array(); + if(empty($oldProjectDeliverable)) continue; + + if($project->type != 'project' && isset($projectList[$project->project])) $project->workflowGroup = $projectList[$project->project]->workflowGroup; // 执行用项目的项目流程 + + /* 解析项目的交付物配置。 */ + foreach($oldProjectDeliverable as $methodDeliverable) + { + foreach($methodDeliverable as $deliverableConfigs) + { + foreach($deliverableConfigs as $config) + { + if(empty($config['doc']) && empty($config['file'])) continue; + + $oldDeliverableID = $config['deliverable']; + if(strpos($oldDeliverableID, 'new_') !== false) + { + $deliverableID = $otherDeliverablePairs[$project->workflowGroup]; // 其他类型的交付物。 + } + else + { + $deliverableID = !empty($deliverableList[$project->workflowGroup][$oldDeliverableID]) ? $deliverableList[$project->workflowGroup][$oldDeliverableID] : $oldDeliverableID; + } + + $newProjectDeliverable = new stdclass(); + $newProjectDeliverable->deliverable = $deliverableID; + $newProjectDeliverable->project = $project->id; + $newProjectDeliverable->submitFrom = $project->id; + $newProjectDeliverable->status = 'draft'; + + /* 将原来的附件类型的交付物变成附件类型的文档并关联到交付物。 */ + if(!empty($config['file']) && !empty($fileList[$config['file']])) + { + $file = $fileList[$config['file']]; + $newProjectDeliverable->doc = $this->moveFileToDoc($project, $projectMainLibPairs, $executionMainLibPairs, $file); + $newProjectDeliverable->createdBy = $file->addedBy; + $newProjectDeliverable->createdDate = substr($file->addedDate, 0, 10); + } + else + { + $newProjectDeliverable->doc = $config['doc']; + } + + $this->dao->insert(TABLE_PROJECTDELIVERABLE)->data($newProjectDeliverable)->exec(); + } + } + } + } + + /* 之前交付物没存文档名称、版本,升级的时候补上。 */ + $emptyNameDocs = $this->dao->select('t1.id,t2.title,t2.version,t2.addedBy,t2.addedDate')->from(TABLE_PROJECTDELIVERABLE)->alias('t1') + ->leftJoin(TABLE_DOC)->alias('t2')->on('t1.doc=t2.id') + ->where('t1.name')->eq('') + ->andWhere('t2.deleted')->eq('0') + ->fetchAll('id'); + + foreach($emptyNameDocs as $deliverableID => $doc) + { + $deliverableCreatedDate = date('Y-m-d', strtotime($doc->addedDate)); + $this->dao->update(TABLE_PROJECTDELIVERABLE) + ->set('name')->eq($doc->title) + ->set('docVersion')->eq($doc->version) + ->set('createdBy')->eq($doc->addedBy) + ->set('createdDate')->eq($deliverableCreatedDate) + ->where('id')->eq($deliverableID) + ->exec(); + } + + $this->dao->query("ALTER TABLE " . TABLE_PROJECT . " DROP COLUMN `deliverable`;"); + } + + /** + * 将附件类型的交付物变成附件类型的文档并关联到交付物。 + * Move file to doc. + * + * @param object $project + * @param array $projectMainLibPairs + * @param array $executionMainLibPairs + * @param array $files + * @access public + * @return int + */ + public function moveFileToDoc(object $project, array $projectMainLibPairs, array $executionMainLibPairs, object $file): int + { + $projectType = $project->type == 'project' ? 'project' : 'execution'; + + $title = $file->title; + $position = strrpos($title, '.'); + if($position > 0) $title = substr($title, 0, $position); + + $doc = new stdclass(); + $doc->title = $title; + $doc->lib = $projectType == 'project' ? $projectMainLibPairs[$project->id] : $executionMainLibPairs[$project->id]; + $doc->project = $projectType == 'project' ? $project->id : 0; + $doc->execution = $projectType == 'execution' ? $project->id : 0; + $doc->addedBy = 'admin'; + $doc->addedDate = helper::today(); + $doc->acl = 'open'; + $doc->vision = 'rnd'; + $doc->version = 1; + $doc->type = 'attachment'; + + $this->dao->insert(TABLE_DOC)->data($doc)->exec(); + $docID = $this->dao->lastInsertID(); + + $this->dao->update(TABLE_FILE)->set('objectID')->eq($docID)->set('objectType')->eq('doc')->where('id')->eq($file->id)->exec(); // 将附件的对象变成文档。 + + $docContent = new stdclass(); + $docContent->doc = $docID; + $docContent->title = $title; + $docContent->content = ''; + $docContent->type = 'attachment'; + $docContent->digest = ''; + $docContent->version = 1; + $docContent->files = ",$file->id"; + + $this->dao->insert(TABLE_DOCCONTENT)->data($docContent)->exec(); + + return $docID; + } + + /** + * 将检查单迁移到内置项目流程中。 + * Migrate auditcl to built-in project workflow. + * + * @access public + * @return void + */ + public function upgradeAuditcl() + { + $workflows = $this->dao->select('id,projectModel,projectType,main')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->andWhere('projectModel')->in('scrum,agileplus,waterfall,waterfallplus')->fetchAll('id'); + foreach($workflows as $id => $workflow) + { + if($workflow->projectType == 'product' && $workflow->main == '1') + { + $this->dao->update(TABLE_AUDITCL)->set('workflowGroup')->eq($id)->where('model')->eq($workflow->projectModel)->exec(); + } + else + { + /* 复制一份检查单放到项目型项目流程中。*/ + $idMap = array(); + $auditclList = $this->dao->select('*')->from(TABLE_AUDITCL)->where('model')->eq($workflow->projectModel)->fetchAll('id', false); + foreach($auditclList as $auditcl) + { + $oldID = $auditcl->id; + unset($auditcl->id); + $auditcl->workflowGroup = $id; + $auditcl->model = ''; + if(helper::isZeroDate($auditcl->editedDate)) $auditcl->editedDate = null; + if(helper::isZeroDate($auditcl->assignedDate)) $auditcl->assignedDate = null; + $this->dao->insert(TABLE_AUDITCL)->data($auditcl)->exec(); + $idMap[$oldID] = $this->dao->lastInsertID(); + } + + $projectIdList = $this->dao->select('id')->from(TABLE_PROJECT)->where('workflowGroup')->eq($id)->fetchPairs(); + foreach($idMap as $oldID => $newID) + { + $this->dao->update(TABLE_NC)->set('listID')->eq($newID)->where('listID')->eq($oldID)->andWhere('project')->in($projectIdList)->exec(); + } + } + } + + /* 将auditplan中的execution迁移到nc中。 */ + $ncs = $this->dao->select('t1.id, t2.execution')->from(TABLE_NC)->alias('t1') + ->leftJoin(TABLE_AUDITPLAN)->alias('t2')->on('t1.auditplan=t2.id') + ->where('t2.execution')->ne('0') + ->fetchPairs(); + + foreach($ncs as $ncID => $executionID) + { + $this->dao->update(TABLE_NC)->set('execution')->eq($executionID)->where('id')->eq($ncID)->exec(); + } + + $this->dao->exec("ALTER TABLE " . TABLE_AUDITCL . " DROP `model`;"); + } + + /** + * 之前IPD、融合瀑布使用的是瀑布项目流程,融合敏捷使用的是敏捷项目流程。现在统一使用自己的项目流程。 + * Before IPD, fusion waterfall uses the waterfall project workflow, and fusion agile uses the agile project workflow. Now use the own project workflow. + * + * @access public + * @return void + */ + public function modifyProjectWorkflowGroup() + { + if(!in_array($this->config->edition, array('ipd', 'max'))) return; // 开源版、企业版没有项目流程 + + $projectGroup = $this->dao->select('id,workflowGroup,model,category,hasProduct')->from(TABLE_PROJECT)->where('model')->in('ipd,waterfallplus,agileplus')->andWhere('deleted')->eq('0')->fetchGroup('workflowGroup', 'id'); + $workflows = $this->dao->select('*')->from(TABLE_WORKFLOWGROUP)->where('id')->in(array_keys($projectGroup))->fetchAll('id'); + + $tableList = array( + TABLE_WORKFLOW, + TABLE_WORKFLOWFIELD, + TABLE_WORKFLOWACTION, + TABLE_WORKFLOWLABEL, + TABLE_WORKFLOWLAYOUT, + TABLE_WORKFLOWUI + ); + + $this->loadModel('project'); + $codeWorkflowPairs = array(); + foreach($workflows as $workflow) + { + $projects = $projectGroup[$workflow->id]; + $index = 1; + $name = $workflow->name; + foreach($projects as $project) + { + $oldWorkflowGroupID = $project->workflowGroup; + $hasProduct = $project->hasProduct ? 'product' : 'project'; + $code = $project->model == 'ipd' ? strtolower($project->category) . $hasProduct : $project->model . $hasProduct; + $projectType = $project->model == 'ipd' ? strtolower($project->category) : $hasProduct; + + if($projectType == 'cpd') $projectType = 'cpd' . $hasProduct; + + unset($workflow->id); + if(isset($codeWorkflowPairs[$code][$oldWorkflowGroupID])) + { + $this->dao->update(TABLE_PROJECT)->set('workflowGroup')->eq($codeWorkflowPairs[$code][$oldWorkflowGroupID])->where('id')->eq($project->id)->exec(); + continue; + } + + $workflow->name = $name . $index; + $workflow->createdDate = helper::now(); + $workflow->projectModel = $project->model; + $workflow->projectType = $projectType; + $workflow->code = $code; + $workflow->editedDate = null; + $workflow->main = '0'; + + $this->dao->insert(TABLE_WORKFLOWGROUP)->data($workflow)->exec(); + $newWorkflowGroupID = $this->dao->lastInsertId(); + $this->dao->update(TABLE_PROJECT)->set('workflowGroup')->eq($newWorkflowGroupID)->where('id')->eq($project->id)->exec(); + + foreach($tableList as $table) + { + $this->project->copyWorkflow($project->workflowGroup, $newWorkflowGroupID, $table); + } + + $codeWorkflowPairs[$code][$oldWorkflowGroupID] = $newWorkflowGroupID; + $index++; + } + } + } + + /** + * 升级流程和活动, 将流程和活动中的数据迁移到流程中。 + * Upgrade process and activity. + * + * @access public + * @return void + */ + public function upgradeProcessAndActivity() + { + $this->loadModel('workflowgroup'); + $this->app->loadLang('process'); + $groupList = $this->upgradeTao->getWorkflowGroupForProcess(); + + $classifyModule = array(); + foreach($groupList as $groupID => $group) + { + $projectModel = $group->projectModel; + if($projectModel == 'agileplus') $projectModel = 'scrum'; + if($projectModel == 'waterfallplus') $projectModel = 'waterfall'; + if(in_array($group->projectModel, array('scrum', 'agileplus', 'waterfall', 'waterfallplus'))) + { + $projectActivity = $this->dao->select('t1.id')->from(TABLE_PROGRAMACTIVITY)->alias('t1') + ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id') + ->leftJoin(TABLE_WORKFLOWGROUP)->alias('t3')->on('t2.workflowGroup=t3.id') + ->where('t3.projectModel')->eq($group->projectModel) + ->limit(1) + ->fetch('id'); + + if($projectActivity) + { + /* 更新groupID到过程表,获取旧分类和新模块的对应关系。 */ + if($group->projectType == 'product' && $group->main == '1') + { + $classifyModule = $this->upgradeTao->handleBuildinWorkflowGroup($group, $groupID, $classifyModule); + } + else + { + $classifyModule = $this->upgradeTao->handleNeedCopyWorkflowGroup($group, $groupID, $classifyModule); + $this->upgradeTao->migrateOutputToDeliverable($group); + } + } + else + { + $this->workflowgroup->addProcessAndActivity($group, $projectModel); + $this->dao->delete()->from(TABLE_AUDITCL)->where('workflowGroup')->eq($groupID)->exec(); // 新增活动删除旧的检查单 + } + } + else + { + $this->workflowgroup->addProcessAndActivity($group, $projectModel); + $this->dao->delete()->from(TABLE_AUDITCL)->where('workflowGroup')->eq($groupID)->exec(); // 新增活动删除旧的检查单 + } + } + + /* 迁移过程分类到模块表。 */ + $this->upgradeTao->updateProcessModules($classifyModule); + + /* 删除旧分类。 */ + $this->dao->delete()->from(TABLE_PROCESS)->where('workflowGroup')->eq(0)->exec(); + $this->dao->delete()->from(TABLE_ACTIVITY)->where('workflowGroup')->eq(0)->exec(); + $this->dao->delete()->from(TABLE_LANG)->where('module')->eq('process')->exec(); + } + /** * 获取需要更新的项目报告数据。 * Get project reports that need to be updated. @@ -11291,31 +12179,590 @@ class upgradeModel extends model $weekNumber = ceil(helper::diffDate($data['weekStart'], $data['projectBegin']) / 7) + 1; $weekEnd = date('Y-m-d', strtotime('+6 day', strtotime($data['weekStart']))); - $report->project = $data['project']; - $report->title = sprintf($this->lang->upgrade->weeklyReportTitle, $weekNumber, $data['weekStart'], $weekEnd); - $report->reportModule = 'week'; - $report->addedDate = $data['weekStart'] . ' 00:00:00'; - $report->editedDate = $data['weekStart'] . ' 00:00:00'; - $report->weeklyDate = str_replace('-', '', $data['weekStart']); + $report->project = $data['project']; + $report->title = sprintf($this->lang->upgrade->weeklyReportTitle, $weekNumber, $data['weekStart'], $weekEnd); + $report->module = 'week'; + $report->addedDate = $data['weekStart'] . ' 00:00:00'; + $report->weeklyDate = str_replace('-', '', $data['weekStart']); } else { - $report->title = $this->lang->upgrade->milestoneTitle; - $report->reportModule = 'milestone'; - $report->project = $data['id']; - $report->addedDate = helper::now(); - $report->editedDate = helper::now(); + $report->title = $this->lang->upgrade->milestoneTitle; + $report->module = 'milestone'; + $report->project = $data['id']; + $report->addedDate = helper::now(); } - $report->template = '0'; $report->templateType = 'projectReport'; $report->addedBy = 'system'; - $report->editedBy = 'system'; $this->dao->insert(TABLE_DOC)->data($report)->exec(); return !dao::isError(); } + /** + * 内置基线评审到项目流程中。 + * Buildin baseline review flow. + * + * @access public + * @return bool + */ + public function buildinBaselineReview(): bool + { + $reviewFlow = new stdclass(); + $reviewFlow->flow = '1'; + $reviewFlow->objectID = '0'; + $reviewFlow->relatedBy = 'system'; + $reviewFlow->relatedDate = helper::now(); + $reviewFlow->extra = 'baseline'; + + $workflows = $this->dao->select('id')->from(TABLE_WORKFLOWGROUP)->where('projectModel')->notin('scrum,projectModel')->fetchAll('id'); + foreach($workflows as $workflow) + { + foreach($this->lang->upgrade->baselineReview as $key => $name) + { + $reviewFlow->root = $workflow->id; + $reviewFlow->objectType = $key; + $this->dao->insert(TABLE_APPROVALFLOWOBJECT)->data($reviewFlow)->exec(); + } + } + + return true; + } + + /** + * 升级检查单分类到项目流程下。 + * Upgrade reviewcl category. + * + * @access public + * @return bool + */ + public function upgradeReviewclCategory(): bool + { + $this->loadModel('custom'); + $clientLang = $this->app->getClientLang(); + $categoryList = $this->dao->select('`key`, value')->from(TABLE_LANG) + ->where('module')->eq('reviewcl') + ->andWhere('section')->eq('waterfallCategoryList') + ->andWhere('lang')->in(array($clientLang, 'all')) + ->orderBy('id_asc') + ->fetchPairs(); + + $categoryPlusList = $this->dao->select('`key`, value')->from(TABLE_LANG) + ->where('module')->eq('reviewcl') + ->andWhere('section')->eq('waterfallplusCategoryList') + ->andWhere('lang')->in(array($clientLang, 'all')) + ->orderBy('id_asc') + ->fetchPairs(); + + if(empty($categoryList) && empty($categoryPlusList)) return true; + $workflows = $this->dao->select('id,projectModel')->from(TABLE_WORKFLOWGROUP)->where('projectModel')->in('waterfall,waterfallplus')->fetchAll('id'); + foreach($workflows as $workflow) + { + if($workflow->projectModel == 'waterfall' && !empty($categoryList)) + { + foreach($categoryList as $key => $value) $this->custom->setItem("{$clientLang}.reviewcl.{$workflow->id}CategoryList.{$key}.1", $value); + } + + if($workflow->projectModel == 'waterfallplus' && !empty($categoryPlusList)) + { + foreach($categoryPlusList as $key => $value) $this->custom->setItem("{$clientLang}.reviewcl.{$workflow->id}CategoryList.{$key}.1", $value); + } + } + + $this->dao->delete()->from(TABLE_LANG)->where('section')->in('waterfallCategoryList,waterfallplusCategoryList')->exec(); + return true; + } + + /** + * 升级项目评审对象到项目流程中。 + * Upgrade baseline objects. + * + * @access public + * @return void + */ + public function upgradeBaselineObjects() + { + $this->app->loadLang('baseline'); + $clientLang = $this->app->getClientLang(); + $objectList = $this->dao->select('`key`, value')->from(TABLE_LANG) + ->where('module')->eq('baseline') + ->andWhere('section')->eq('objectList') + ->andWhere('lang')->in(array($clientLang, 'all')) + ->orderBy('id_asc') + ->fetchPairs(); + + if(empty($objectList) && !empty($this->lang->baseline->objectList)) $objectList = $this->lang->baseline->objectList; + if(empty($objectList)) $objectList = $this->lang->upgrade->reviewObjectList; + + $workflowGroups = $this->dao->select('id, projectModel, projectType')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->fetchAll('id'); + + $moduleGroup = $this->dao->select('id, root, extra')->from(TABLE_MODULE) + ->where('type')->eq('deliverable') + ->andWhere('root')->in(array_keys($workflowGroups)) + ->fetchGroup('root', 'extra'); + + $reviewclList = $this->dao->select('*')->from(TABLE_REVIEWCL)->where('type')->ne('')->fetchGroup('type'); + $activityList = $this->dao->select('t1.*')->from(TABLE_ACTIVITY)->alias('t1')->leftJoin(TABLE_PROCESS)->alias('t2')->on('t1.process=t2.id') + ->where('t1.name')->eq($this->lang->other) + ->andWhere('t2.name')->eq($this->lang->other) + ->fetchGroup('workflowGroup'); + + $actionIdList = array(); + $reviewclActions = $this->dao->select('*')->from(TABLE_ACTION)->where('objectType')->eq('reviewcl')->fetchGroup('objectID', 'id'); + foreach($reviewclActions as $actions) $actionIdList = arrayUnion($actionIdList, $actions); + $reviewclHistory = $this->dao->select('*')->from(TABLE_HISTORY)->where('action')->in(array_keys($actionIdList))->fetchGroup('action', 'id'); + + $deliverable = new stdClass(); + $deliverable->status = 'enabled'; + $deliverable->createdBy = 'system'; + $deliverable->createdDate = helper::now(); + $deliverable->trimmable = '1'; + $deliverable->template = '[]'; + + $deliverableStage = new stdClass(); + $deliverableStage->stage = 'project'; + $deliverableStage->required = '0'; + + $reviewFlow = new stdclass(); + $reviewFlow->flow = 1; + $reviewFlow->objectType = 'deliverable'; + $reviewFlow->relatedBy = 'system'; + $reviewFlow->relatedDate = helper::now(); + $reviewFlow->extra = 'review'; + + $upgradeReviewcls = array(); + $categoryModuleMap = array('PP' => 'plan', 'SRS' => 'story'); + + foreach($workflowGroups as $groupID => $workflowGroup) + { + $projectModel = $workflowGroup->projectModel; + + if(empty($activityList[$groupID])) continue; + $otherActivity = reset($activityList[$groupID]); + $otherActivityID = $otherActivity->id; + + $nameFilter = array(); + $deliverable->workflowGroup = $groupID; + $deliverable->activity = $otherActivityID; + + foreach(array_filter($objectList) as $key => $value) + { + if(empty($value)) continue; + if(in_array($key, array('HLDS', 'DDS', 'ADS', 'DBDS', 'ITTC', 'STTC', 'intergrate', 'system'))) continue; // 设计类型和测试类型的上面处理过了,跳过。 + if($key == 'PP' && in_array($projectModel, array('scrum', 'agileplus'))) continue; // 敏捷、融合敏捷没有项目计划 + if($key == 'SRS') $value = $this->lang->upgrade->reviewObjectList['SRS']; // 将软件需求规格说明书改成项目需求规格说明书 + $deliverable->category = $key; // 标记交付物的类型。 + $deliverable->systemList = (int)in_array($key, ['PP', 'SRS']); + + /* 将原来的评审对象放到新交付物的模块下:计划类、需求类、设计类、测试类。其他类放到其他模块下。 */ + $moduleKey = isset($categoryModuleMap[$key]) ? $categoryModuleMap[$key] : 'other'; + $deliverable->module = isset($moduleGroup[$groupID][$moduleKey]) ? $moduleGroup[$groupID][$moduleKey]->id : 0; + + $deliverableID = $this->addDeliverable((string)$value, $deliverable, $deliverableStage, $nameFilter); + + $reviewFlow->root = $groupID; + $reviewFlow->objectID = $deliverableID; + $this->dao->insert(TABLE_APPROVALFLOWOBJECT)->data($reviewFlow)->exec(); + $flowID = $this->dao->lastInsertID(); + + /* 迁移评审对象对应的检查清单。 */ + if(empty($reviewclList[$projectModel])) continue; + foreach($reviewclList[$projectModel] as $reviewcl) + { + if($key != $reviewcl->object) continue; + $upgradeReviewcls[$reviewcl->id] = $reviewcl->id; + + $reviewclData = clone $reviewcl; + unset($reviewclData->id); + unset($reviewclData->editedDate); + unset($reviewclData->assignedDate); + $reviewclData->workflowGroup = $groupID; + $reviewclData->object = $flowID; + $this->dao->insert(TABLE_REVIEWCL)->data($reviewclData)->exec(); + $reviewclID = $this->dao->lastInsertID(); + + $this->dao->update(TABLE_REVIEWISSUE)->set('listID')->eq($reviewclID)->where('listID')->eq($reviewcl->id)->exec(); + + /* 迁移检查清单历史记录。 */ + if(empty($reviewclActions[$reviewcl->id])) continue; + foreach($reviewclActions[$reviewcl->id] as $action) + { + $actionData = clone $action; + unset($actionData->id); + $actionData->objectID = $reviewclID; + $this->dao->insert(TABLE_ACTION)->data($actionData)->exec(); + + if(empty($reviewclHistory[$action->id])) continue; + $actionID = $this->dao->lastInsertID(); + foreach($reviewclHistory[$action->id] as $history) + { + unset($history->id); + $history->action = $actionID; + $this->dao->insert(TABLE_HISTORY)->data($history)->exec(); + } + } + } + } + } + + /* 将已升级的历史检查清单数据删掉。 */ + $this->dao->delete()->from(TABLE_REVIEWCL)->where('id')->in($upgradeReviewcls)->exec(); + + $this->dao->exec("ALTER TABLE " . TABLE_REVIEWCL . " CHANGE `object` `object` mediumint(8) unsigned NOT NULL DEFAULT '0';"); + $this->dao->exec("ALTER TABLE " . TABLE_REVIEWCL . " DROP `type`;"); + $this->dao->exec('ALTER TABLE ' . TABLE_PROCESS . ' DROP `model`'); + $this->dao->exec('ALTER TABLE ' . TABLE_PROCESS . ' DROP `type`'); + } + + /** + * 升级项目评审对象到项目交付物。 + * Upgrade review to deliverable. + * + * @access public + * @return void + */ + public function upgradeReviewToDeliverable() + { + if(!in_array($this->config->edition, array('ipd', 'max'))) return; // 开源版、企业版没有评审功能。 + + $this->loadModel('review'); + $this->loadModel('baseline'); + $reviewPointList = $this->config->edition == 'ipd' ? $this->lang->review->reviewPoint->titleList : array(); + $reviews = $this->dao->select('t1.*, t2.category, t2.version')->from(TABLE_REVIEW)->alias('t1') + ->leftJoin(TABLE_OBJECT)->alias('t2')->on('t1.object=t2.id') + ->where('t1.deleted')->eq('0') + ->beginIF($reviewPointList)->andWhere('t2.category')->notin(array_keys($reviewPointList))->fi() // IPD 模式下,只升级非评审点的评审。 + ->orderBy('t1.id_asc') + ->fetchAll('id'); + + $projectDeliverables = $this->dao->select('t1.id, t2.id as deliverable, t2.category')->from(TABLE_PROJECT)->alias('t1') + ->leftJoin(TABLE_DELIVERABLE)->alias('t2')->on('t1.workflowGroup=t2.workflowGroup') + ->where('t2.category')->ne('') + ->fetchGroup('id', 'category'); + + $docIdList = array_column($reviews, 'doc'); + $docIdList = array_filter($docIdList); + $docList = $this->dao->select('id, title')->from(TABLE_DOC)->where('id')->in($docIdList)->fetchPairs(); + + $projectMainLibPairs = $this->dao->select('project, id')->from(TABLE_DOCLIB)->where('main')->eq('1')->andWhere('type')->eq('project')->fetchPairs(); + $fileGroup = $this->dao->select('id, objectID')->from(TABLE_FILE)->where('deleted')->eq('0')->andWhere('objectType')->eq('review')->fetchGroup('objectID', 'id'); + + $doc = new stdclass(); + $doc->version = 1; + $doc->type = 'text'; + $doc->acl = 'open'; + $doc->addedBy = 'system'; + $doc->addedDate = helper::now(); + + foreach($reviews as $review) + { + if(!isset($projectDeliverables[$review->project][$review->category])) continue; + + $files = ''; + if(!empty($fileGroup[$review->id])) + { + $fileIdList = array_keys($fileGroup[$review->id]); + $files = implode(',', $fileIdList); + } + + /* 不是系统模板生成、也没选文档的,自动生成文档。 */ + if(!$review->template && !$review->doc) + { + $doc->title = $review->title; + $doc->lib = $projectMainLibPairs[$review->project]; + $doc->project = $review->project; + $doc->addedBy = $review->createdBy; + $doc->addedDate = $review->createdDate; + $doc->version = 1; + $doc->type = $files ? 'attachment' : 'text'; + + $this->dao->insert(TABLE_DOC)->data($doc)->exec(); + $review->doc = $this->dao->lastInsertID(); + $review->docVersion = 1; + + $docContent = new stdclass(); + $docContent->type = 'doc'; + $docContent->doc = $review->doc; + $docContent->title = $review->title; + $docContent->files = $files; + $docContent->version = 1; + $this->dao->insert(TABLE_DOCCONTENT)->data($docContent)->exec(); + $this->dao->update(TABLE_FILE)->set('objectType')->eq('doc')->set('objectID')->eq($review->doc)->where('id')->in($files)->exec(); + } + elseif($review->doc && $files) + { + /* 原来有文档也有附件,将附件关联到文档。 */ + $maxVersion = $this->dao->select('max(version) as version')->from(TABLE_DOCCONTENT)->where('doc')->eq($review->doc)->fetch('version'); + $docContent = $this->dao->select('*')->from(TABLE_DOCCONTENT)->where('doc')->eq($review->doc)->andWhere('version')->eq($review->docVersion)->fetch(); + + unset($docContent->id); + $docContent->version = $maxVersion + 1; + $docContent->files = $files; + $review->docVersion = $docContent->version; + + $this->dao->insert(TABLE_DOCCONTENT)->data($docContent)->exec(); + $this->dao->update(TABLE_FILE)->set('objectType')->eq('doc')->set('objectID')->eq($review->doc)->where('id')->in($files)->exec(); + $this->dao->update(TABLE_DOC)->set('version')->eq($review->docVersion)->where('id')->eq($review->doc)->exec(); + $this->dao->update(TABLE_REVIEW)->set('docVersion')->eq($review->docVersion)->where('id')->eq($review->id)->exec(); + } + + $deliverable = new stdclass(); + $deliverable->name = (!empty($docList[$review->doc]) && $docList[$review->doc] != $review->title) ? $docList[$review->doc] : $review->title; // 优先使用文档的标题 + $deliverable->project = $review->project; + $deliverable->submitFrom = $review->project; + $deliverable->review = $review->id; + $deliverable->deliverable = $projectDeliverables[$review->project][$review->category]->deliverable; + $deliverable->doc = $review->doc; + $deliverable->docVersion = $review->docVersion; + $deliverable->createdBy = $review->createdBy; + $deliverable->createdDate = $review->createdDate; + $deliverable->status = $review->status; + $deliverable->version = $review->version; + + $deliverableID = $this->dao->select('id')->from(TABLE_PROJECTDELIVERABLE) + ->where('project')->eq($deliverable->project) + ->andWhere('deliverable')->eq($deliverable->deliverable) + ->andWhere('doc')->eq($deliverable->doc) + ->fetch('id'); + + /* 保证交付物在项目下的唯一性,如果不存在,则插入。 */ + if(!$deliverableID) + { + $this->dao->insert(TABLE_PROJECTDELIVERABLE)->data($deliverable)->exec(); + $deliverableID = $this->dao->lastInsertID(); + } + else + { + $this->dao->update(TABLE_PROJECTDELIVERABLE)->set('docVersion')->eq($deliverable->docVersion)->set('review')->eq($deliverable->review)->where('id')->eq($deliverableID)->exec(); + } + + $this->dao->update(TABLE_REVIEW)->set('deliverable')->eq($deliverableID)->set('title')->eq($deliverable->name)->where('id')->eq($review->id)->exec(); + $this->dao->update(TABLE_OBJECT)->set('category')->eq($deliverable->deliverable)->where('id')->eq($review->object)->exec(); // 将评审对象改成交付物ID + + /* 如果是系统模板类型、但没有选系统模板生成,则复制一份交付物,让用户升级上来后可以继续使用系统模板。 */ + if(in_array($review->category, array('PP', 'SRS', 'HLDS', 'DDS', 'ADS', 'DBDS', 'intergrate', 'system')) && !$review->template) + { + $deliverable->name = zget($this->lang->upgrade->reviewObjectList, $review->category); + $deliverable->doc = 0; + $deliverable->docVersion = 0; + $deliverable->status = 'draft'; + $deliverable->version = ''; + $deliverable->review = 0; + + $deliverableID = $this->dao->select('id')->from(TABLE_PROJECTDELIVERABLE) + ->where('project')->eq($deliverable->project) + ->andWhere('deliverable')->eq($deliverable->deliverable) + ->andWhere('doc')->eq($deliverable->doc) + ->fetch('id'); + + if(!$deliverableID) $this->dao->insert(TABLE_PROJECTDELIVERABLE)->data($deliverable)->exec(); + } + } + + $this->dao->exec('ALTER TABLE ' . TABLE_REVIEW . ' DROP `doc`'); + } + + /** + * 升级历史基线。 + * Upgrade baseline. + * + * @access public + * @return void + */ + public function upgradeBaseline() + { + $baselines = $this->dao->select('id,`from`')->from(TABLE_OBJECT)->where('type')->eq('taged')->fetchAll(); + $reviews = $this->dao->select('id,object,deliverable')->from(TABLE_REVIEW)->where('status')->eq('pass')->fetchAll('object'); + foreach($baselines as $baseline) + { + if(empty($reviews[$baseline->from])) continue; + + $review = $reviews[$baseline->from]; + $data = new stdclass(); + $data->category = $review->deliverable; + $data->categoryVersion = json_encode(array($review->deliverable => $review->id)); + $data->status = 'pass'; + $this->dao->update(TABLE_OBJECT)->data($data)->where('id')->eq($baseline->id)->exec(); + + $this->dao->update(TABLE_REVIEW)->set('isBaseline')->eq('1')->where('id')->eq($review->id)->exec(); + } + return true; + } + + /** + * 新增交付物权限。 + * Add deliverable privs for all groups. + * + * @access public + * @return void + */ + public function addDeliverablePrivs() + { + $groups = $this->dao->select('`group`')->from(TABLE_GROUPPRIV)->where('module')->eq('project')->andWhere('method')->eq('deliverable')->fetchPairs(); + $group = new stdclass(); + $group->module = 'project'; + foreach($groups as $groupID) + { + foreach(array('createDeliverable', 'deleteDeliverable', 'viewDeliverable') as $method) + { + $group->method = $method; + $group->group = $groupID; + $this->dao->insert(TABLE_GROUPPRIV)->data($group)->exec(); + } + } + } + + /** + * 升级开源版的阶段。 + * Update stage for PMS. + * + * @access public + * @return void + */ + public function upgradeStage4PMS() + { + if($this->config->edition != 'open') return true; + + $stageGroup = $this->dao->select('*')->from(TABLE_STAGE)->where('workflowGroup')->eq(0)->fetchGroup('projectType', 'id'); + if(empty($stageGroup)) return true; + + $typeNamePairs = array(); + $needDeleteStage = array(); + foreach($stageGroup as $projectModel => $stageList) + { + if($projectModel == 'ipd') continue; + foreach($stageList as $stageInfo) + { + if(isset($typeNamePairs[$stageInfo->type]) && in_array($stageInfo->name, $typeNamePairs[$stageInfo->type])) + { + $needDeleteStage[$stageInfo->id] = $stageInfo->id; + continue; + } + if(!isset($typeNamePairs[$stageInfo->type])) $typeNamePairs[$stageInfo->type] = array(); + $typeNamePairs[$stageInfo->type][] = $stageInfo->name; + } + } + + if(!empty($needDeleteStage)) $this->dao->delete()->from(TABLE_STAGE)->where('id')->in($needDeleteStage)->exec(); + return true; + } + + /** + * 升级阶段列表及评审点。 + * Update stage and point. + * + * @access public + * @return void + */ + public function upgradeStageAndPoint() + { + $stageGroup = $this->dao->select('*')->from(TABLE_STAGE)->where('workflowGroup')->eq(0)->fetchGroup('projectType', 'id'); + if(empty($stageGroup)) return true; + + $this->app->loadConfig('project'); + $this->app->loadConfig('stage'); + $workflowList = $this->dao->select('projectModel, code, id')->from(TABLE_WORKFLOWGROUP)->where('projectModel')->in('waterfall,waterfallplus,ipd')->fetchAll('id'); + foreach($workflowList as $flow) + { + $projectModel = $flow->projectModel; + if(!isset($stageGroup[$projectModel])) continue; + + $flowCode = $flow->code; + foreach($stageGroup[$projectModel] as $stage) + { + if($flowCode == 'tpdproduct' && !in_array($stage->type, $this->config->project->categoryStages['TPD'])) continue; + if($flowCode == 'cbbproduct' && !in_array($stage->type, $this->config->project->categoryStages['CBB'])) continue; + if(in_array($flowCode, array('cpdproduct', 'cpdproject')) && !in_array($stage->type, $this->config->project->categoryStages['CPD'])) continue; + + unset($stage->id); + $stage->workflowGroup = $flow->id; + if(empty($stage->editedDate)) $stage->editedDate = null; + $this->dao->insert(TABLE_STAGE)->data($stage)->exec(); + $stageID = $this->dao->lastInsertID(); + + /* 添加IPD项目流程阶段的评审点。*/ + if($projectModel == 'ipd') + { + $decision = new stdClass(); + $decision->builtin = '1'; + $decision->createdBy = 'system'; + $decision->createdDate = helper::now(); + + $decisionFlow = new stdClass(); + $decisionFlow->flow = 1; + $decisionFlow->objectType = 'decision'; + $decisionFlow->relatedBy = 'system'; + $decisionFlow->relatedDate = helper::now(); + foreach($this->config->stage->ipdReviewPoint->{$stage->type} as $index => $point) + { + $decision->workflowGroup = $stage->workflowGroup; + $decision->stage = $stageID; + $decision->order = $index + 1; + $decision->title = $point; + $decision->type = strpos($point, 'TR') !== false ? 'TR' : 'DCP'; + $decision->category = $point; + $this->dao->insert(TABLE_DECISION)->data($decision)->exec(); + $decisionID = $this->dao->lastInsertID(); + + $decisionFlow->root = $stage->workflowGroup; + $decisionFlow->objectID = $decisionID; + $decisionFlow->extra = $decision->type; + $this->dao->insert(TABLE_APPROVALFLOWOBJECT)->data($decisionFlow)->exec(); + } + } + } + } + + return true; + } + + /** + * 升级评审点评审。 + * Upgrade object of decision. + * + * @access public + * @return void + */ + public function upgradeObjectOfDecision() + { + $ipdProject = $this->dao->select('id,workflowGroup')->from(TABLE_PROJECT)->where('model')->eq('ipd')->andWhere('type')->eq('project')->fetchPairs('id'); + if(empty($ipdProject)) return true; + + $this->app->loadConfig('review'); + $stagePointGroup = array(); + $ipdProjectStage = $this->dao->select('id,project,attribute')->from(TABLE_PROJECT)->where('project')->in(array_keys($ipdProject))->andWhere('type')->eq('stage')->fetchGroup('project', 'attribute'); + foreach($ipdProjectStage as $projectID => $stageList) + { + if(!isset($stagePointGroup[$projectID])) $stagePointGroup[$projectID] = array(); + foreach($stageList as $attribute => $stage) + { + foreach($this->config->review->ipdReviewPoint->$attribute as $pointCategory) + { + $stagePointGroup[$projectID][$pointCategory] = $stage->id; + } + } + } + + $decisionGroup = $this->dao->select('id,workflowGroup,category')->from(TABLE_DECISION)->fetchGroup('workflowGroup', 'category'); + $ipdStagePoints = $this->dao->select('*')->from(TABLE_OBJECT)->where('project')->in(array_keys($ipdProject))->andWhere('category')->in($this->config->review->ipdPointOrder)->fetchAll(); + foreach($ipdStagePoints as $point) + { + $category = $point->category; + $projectID = $point->project; + $workflowGroup = $ipdProject[$projectID]; + + $this->dao->update(TABLE_OBJECT) + ->set('type')->eq('decision') + ->set('categoryTitle')->eq($category) + ->set('execution')->eq(!empty($stagePointGroup[$projectID][$category]) ? $stagePointGroup[$projectID][$category] : 0) + ->set('category')->eq(!empty($decisionGroup[$workflowGroup][$category]->id) ? $decisionGroup[$workflowGroup][$category]->id : 0) + ->where('id')->eq($point->id) + ->exec(); + } + return true; + } + /** * 调整 21.7.5 版本的权限 * Adjust priv 21.7.5. @@ -11347,6 +12794,167 @@ class upgradeModel extends model return true; } + /** + * 四个项目模型的功能开关合并成一个。 + * Merge four project model feature switches into one. + * 内置AI禅道智能体。 + * Initialize the AI prompts. + * + * @access public + * @return bool + */ + public function upgradeClosedFeature() + { + + $disabledFeatures = $this->dao->select('value')->from(TABLE_CONFIG)->where('`key`')->eq('closedFeatures')->andWhere('owner')->eq('system')->fetch('value'); + if(empty($disabledFeatures)) return true; + + $disabledFeatures = explode(',', $disabledFeatures); + $countFeature = array(); + foreach($this->config->featureGroup->project as $newFeature) + { + $countFeature[$newFeature] = 0; + foreach($disabledFeatures as $key => $feature) + { + $feature = strtolower(trim($feature)); + if(strpos($feature, $newFeature) !== false) + { + $countFeature[$newFeature]++; + unset($disabledFeatures[$key]); + } + } + } + + /* 之前在所有模型下都关闭的功能升级上来才关闭,否则开启。*/ + $disabledFeatures = array_values($disabledFeatures); + foreach($countFeature as $feature => $count) + { + if($count == 4 || ($count == 2 && in_array($feature, array('gapanalysis', 'researchplan', 'track')))) + { + $disabledFeatures[] = 'project' . ucfirst($feature); + } + } + + $disabledFeatures = implode(',', array_unique($disabledFeatures)); + $this->dao->update(TABLE_CONFIG)->set('value')->eq($disabledFeatures)->where('`key`')->eq('closedFeatures')->andWhere('owner')->eq('system')->exec(); + } + + /** + * 之前object表中data字段直接存入URL,如果改变请求方式则无法使用,现在升级为模块名、方法名、参数的形式。 + * Before the object table directly stores the URL in the data field, if the request method changes, it cannot be used, now upgrade to the form of module name, method name, and parameters. + * + * @access public + * @return bool + */ + public function parseDocFetcherURL() + { + $objects = $this->dao->select('id,data')->from(TABLE_OBJECT)->where('data')->like('%tml-zentaolist%')->fetchPairs(); + + $cleanValue = function($value) + { + if(preg_match('/__(\w+)__([0-9]+)/', $value, $m)) return $m[2]; + return $value; + }; + + $parsePathInfoUrl = function($url) use ($cleanValue) + { + $url = ltrim($url, '/'); + $url = preg_replace('/\.html$/', '', $url); + + $parts = explode('-', $url); + array_shift($parts); // 删掉模块名 + array_shift($parts); // 删掉方法名 + + $params = []; + foreach($parts as $index => $value) + { + $key = 'param' . $index + 1; + $value = $cleanValue($value); + + $params[$key] = $value; + } + + return $params; + }; + + $parseGetUrl = function($url) use ($cleanValue) + { + $parts = parse_url($url); + parse_str($parts['query'] ?? '', $query); + + unset($query['m'], $query['f']); + + $params = []; + $index = 1; + foreach($query as $key => $value) + { + $key = 'param' . $index; + $value = $cleanValue($value); + + $params[$key] = $value; + $index++; + } + + return $params; + }; + + foreach($objects as $id => $data) + { + $data = json_decode($data, true); + $html = $data['$data']; + + preg_match('/data-export-url=\'([^\']+)\'/', $html, $exportMatch); + preg_match('/data-fetcher=\'([^\']+)\'/', $html, $fetcherMatch); + + $exportUrl = $exportMatch[1] ?? null; + $fetcherUrl = $fetcherMatch[1] ?? null; + + if(!$exportUrl && !$fetcherUrl) continue; + + if(strpos($exportUrl, '?') !== false) + { + $data['exportParams'] = $parseGetUrl($exportUrl); + } + else + { + $data['exportParams'] = $parsePathInfoUrl($exportUrl); + } + + if(strpos($fetcherUrl, '?') !== false) + { + $data['fetcherParams'] = $parseGetUrl($fetcherUrl); + } + else + { + $data['fetcherParams'] = $parsePathInfoUrl($fetcherUrl); + } + + $data['$data'] = str_replace($exportMatch[1], '', $data['$data']); + $data['$data'] = str_replace($fetcherMatch[1], '', $data['$data']); + + $data = json_encode($data); + $this->dao->update(TABLE_OBJECT)->set('data')->eq($data)->where('id')->eq($id)->exec(); + } + } + + /** + * 处理升级新增的工作流数据源。 + * Process workflow datasource. + * + * @param array $codeList + * @access public + * @return void + */ + public function processWorkflowDatasource($codeList) + { + $datasources = $this->dao->select('id,code')->from(TABLE_WORKFLOWDATASOURCE)->where('code')->in(array_keys($codeList))->fetchAll(); + foreach($datasources as $datasource) + { + list($module, $field) = explode('_', $codeList[$datasource->code]); + $this->dao->update(TABLE_WORKFLOWFIELD)->set('options')->eq($datasource->id)->where('module')->eq($module)->andWhere('field')->eq($field)->exec(); + } + } + /** * 内置AI禅道智能体。 * Initialize the AI prompts. @@ -11417,4 +13025,39 @@ class upgradeModel extends model $this->dao->insert(TABLE_AI_PROMPTFIELD)->data($field, 'id')->exec(); } } + + /* + * 升级项目流程相关的权限。 + * Update the priv of workflow group. + * + * @access public + * @return bool + */ + public function updateWorkflowGroupPriv(): bool + { + $designFlowGroupIdList = $this->dao->select('`group`')->from(TABLE_GROUPPRIV) + ->where('module')->eq('workflowfield') + ->andWhere('method')->eq('browse') + ->fetchPairs(); + foreach($designFlowGroupIdList as $groupID) $this->dao->insert(TABLE_GROUPPRIV)->set('group')->eq($groupID)->set('module')->eq('workflowgroup')->set('method')->eq('designFlow')->exec(); + + $flowReportGroupIdList = $this->dao->select('`group`')->from(TABLE_GROUPPRIV) + ->where('module')->eq('workflowgroup') + ->andWhere('method')->eq('design') + ->fetchPairs(); + foreach($flowReportGroupIdList as $groupID) $this->dao->insert(TABLE_GROUPPRIV)->set('group')->eq($groupID)->set('module')->eq('workflowgroup')->set('method')->eq('report')->exec(); + + $setDelieverableGroupIdList = $this->dao->select('`group`')->from(TABLE_GROUPPRIV) + ->where('module')->eq('workflowgroup') + ->andWhere('method')->eq('deliverable') + ->fetchPairs(); + foreach($setDelieverableGroupIdList as $groupID) + { + $this->dao->insert(TABLE_GROUPPRIV)->set('group')->eq($groupID)->set('module')->eq('deliverable')->set('method')->eq('enable')->exec(); + $this->dao->insert(TABLE_GROUPPRIV)->set('group')->eq($groupID)->set('module')->eq('deliverable')->set('method')->eq('disable')->exec(); + } + $this->dao->delete()->from(TABLE_GROUPPRIV)->where('module')->eq('workflowgroup')->andWhere('method')->eq('deliverable')->exec(); + + return true; + } } diff --git a/module/upgrade/systemfiles.txt b/module/upgrade/systemfiles.txt index f20d9a2cae..5cab61a3ca 100644 --- a/module/upgrade/systemfiles.txt +++ b/module/upgrade/systemfiles.txt @@ -879,18 +879,6 @@ ,cm/view/edit.html.php, ,cm/view/report.html.php, ,cm/view/view.html.php, -,cmcl/control.php, -,cmcl/css/browse.css, -,cmcl/js/browse.js, -,cmcl/lang/en.php, -,cmcl/lang/zh-cn.php, -,cmcl/lang/zh-tw.php, -,cmcl/model.php, -,cmcl/view/batchcreate.html.php, -,cmcl/view/browse.html.php, -,cmcl/view/create.cmmi.html.hook.php, -,cmcl/view/edit.html.php, -,cmcl/view/view.html.php, ,common/ext/index.html, ,common/ext/lang/de/bizext.php, ,common/ext/lang/de/crystal.php, diff --git a/module/upgrade/tao.php b/module/upgrade/tao.php index 4d6dbb76d5..53e4faae0d 100644 --- a/module/upgrade/tao.php +++ b/module/upgrade/tao.php @@ -901,4 +901,303 @@ class upgradeTao extends upgradeModel return $duplicateList; } + + /** + * 获取工作流组列表 + * Get workflow group list for process. + * + * @return array + */ + protected function getWorkflowGroupForProcess() + { + return $this->dao->select('id,projectModel,projectType,main')->from(TABLE_WORKFLOWGROUP) + ->where('projectModel')->in('scrum,waterfall,agileplus,waterfallplus') + ->andWhere('deleted')->eq('0') + ->fetchAll('id'); + } + + /** + * 处理使用内置数据的流程。 + * Handle buildin workflow group. + * + * @param object $group 流程对象 + * @param int $groupID 流程ID + * @param array $classifyModule 分类模块数组 + * @return array + */ + protected function handleBuildinWorkflowGroup(object $group, int $groupID, array $classifyModule) + { + $this->dao->update(TABLE_PROCESS)->set('workflowGroup')->eq($groupID)->where('model')->eq($group->projectModel)->exec(); + + /* 更新活动的流程。 */ + $processList = $this->dao->select('id')->from(TABLE_PROCESS)->where('workflowGroup')->eq($groupID)->andWhere('deleted')->eq('0')->fetchPairs(); + $this->dao->update(TABLE_ACTIVITY)->set('workflowGroup')->eq($groupID)->where('process')->in(array_keys($processList))->exec(); + + $this->migrateOutputToDeliverable($group); + + $classifyModule[$groupID] = $this->migrateClassifyToModule($group, $groupID); + + return $classifyModule; + } + + /** + * 将过程分类从语言表迁移到模块表 + * Migrate process classify from language table to module table. + * + * @param object $group 流程对象 + * @param int $groupID 流程ID + * @return array + */ + protected function migrateClassifyToModule(object $group, int $groupID) + { + $this->app->loadLang('process'); + $classifyKey = $group->projectModel == 'waterfall' ? 'classify' : $group->projectModel . 'Classify'; + $classifyList = $this->dao->select('`key`,`value`')->from(TABLE_LANG)->where('module')->eq('process')->andWhere('section')->eq($classifyKey)->fetchPairs(); + if(empty($classifyList)) $classifyList = $this->lang->process->classifyList; + + $orderList = array('project' => 10, 'engineering' => 20, 'support' => 30); + + $classifyModule = array(); + $customOrder = 40; + foreach($classifyList as $key => $value) + { + $order = isset($orderList[$key]) ? $orderList[$key] : $customOrder; + $moduleID = $this->createProcessModule($groupID, $key, $value, $order); + $classifyModule[$key] = $moduleID; + if(!isset($orderList[$key])) $customOrder += 10; + } + + return $classifyModule; + } + + /** + * 创建过程模块。 + * Create process module. + * + * @param int $groupID 流程ID + * @param string $key 模块标识 + * @param string $name 模块名称 + * @param int $order 排序 + * @return int + */ + protected function createProcessModule(int $groupID, string $key, string $name, int $order) + { + $module = new stdclass(); + $module->root = $groupID; + $module->branch = '0'; + $module->name = $name; + $module->grade = '1'; + $module->order = $order; + $module->type = 'process'; + $module->extra = $key; + + $this->dao->insert(TABLE_MODULE)->data($module)->exec(); + $moduleID = $this->dao->lastInsertID(); + $this->dao->update(TABLE_MODULE)->set('path')->eq(",$moduleID,")->where('id')->eq($moduleID)->exec(); + + return $moduleID; + } + + /** + * 处理需要复制过程和活动的流程。 + * Handle need copy workflow group. + * + * @param object $group 流程对象 + * @param int $groupID 流程ID + * @param array $classifyModule 分类模块数组 + */ + protected function handleNeedCopyWorkflowGroup(object $group, int $groupID, array $classifyModule) + { + $projectIdList = $this->dao->select('id')->from(TABLE_PROJECT)->where('workflowGroup')->eq($group->id)->fetchPairs(); + $processList = $this->dao->select('*')->from(TABLE_PROCESS)->where('model')->eq($group->projectModel)->andWhere('deleted')->eq('0')->fetchAll('id'); + $activityGroup = $this->dao->select('*')->from(TABLE_ACTIVITY)->where('process')->in(array_keys($processList))->andWhere('deleted')->eq('0')->fetchGroup('process', 'id'); + $outputGroup = $this->dao->select('t1.*')->from(TABLE_ZOUTPUT) + ->alias('t1')->leftJoin(TABLE_ACTIVITY) + ->alias('t2')->on('t1.activity = t2.id') + ->where('t2.process')->in(array_keys($processList)) + ->andWhere('t1.deleted')->eq('0') + ->fetchGroup('activity', 'id'); + + foreach($processList as $process) + { + $this->copyProcessWithActivities($process, $groupID, $activityGroup, $outputGroup, $projectIdList); + } + + $classifyModule[$groupID] = $this->migrateClassifyToModule($group, $groupID); + + return $classifyModule; + } + + /** + * 复制过程及其活动。 + * Copy process and activities. + * + * @param object $process 过程对象 + * @param int $groupID 流程ID + * @param array $activityGroup 活动分组 + * @param array $outputGroup 文档分组 + * @param array $projectIdList 项目ID列表 + */ + protected function copyProcessWithActivities(object $process, int $groupID, array $activityGroup, array $outputGroup, array $projectIdList) + { + $activityList = zget($activityGroup, $process->id, array()); + unset($process->id); + $process->workflowGroup = $groupID; + $process->model = ''; + $process->editedBy = ''; + $process->editedDate = null; + $process->assignedDate = null; + $this->dao->insert(TABLE_PROCESS)->data($process)->exec(); + $processID = $this->dao->lastInsertID(); + + foreach($activityList as $activity) + { + $this->copyActivity($activity, $processID, $groupID, $outputGroup, $projectIdList); + } + } + + /** + * 复制活动。 + * Copy activity. + * + * @param object $activity 活动对象 + * @param int $processID 过程ID + * @param int $groupID 流程ID + * @param array $outputGroup 文档分组 + * @param array $projectIdList 项目ID列表 + */ + protected function copyActivity(object $activity, int $processID, int $groupID, array $outputGroup, array $projectIdList) + { + $outputList = zget($outputGroup, $activity->id, array()); + + $oldActivityID = $activity->id; + unset($activity->id); + + $activity->process = $processID; + $activity->optional = !empty($activity->optional) ? $activity->optional : 'no'; + $activity->workflowGroup = $groupID; + $activity->editedBy = ''; + $activity->editedDate = null; + $activity->assignedDate = null; + $this->dao->insert(TABLE_ACTIVITY)->data($activity)->exec(); + + $newActivityID = $this->dao->lastInsertID(); + + $this->dao->update(TABLE_AUDITPLAN)->set('objectID')->eq($newActivityID)->where('objectID')->eq($oldActivityID)->andWhere('objectType')->eq('activity')->andWhere('project')->in($projectIdList)->exec(); + $this->dao->update(TABLE_AUDITCL)->set('objectID')->eq($newActivityID)->where('objectID')->eq($oldActivityID)->andWhere('workflowGroup')->eq($groupID)->andWhere('objectType')->eq('activity')->exec(); + $this->dao->update(TABLE_PROGRAMACTIVITY)->set('activity')->eq($newActivityID)->where('activity')->eq($oldActivityID)->andWhere('project')->in($projectIdList)->exec(); + + $idMap = array(); + foreach($outputList as $output) + { + $oldID = $output->id; + unset($output->id); + $output->activity = $newActivityID; + $output->editedBy = ''; + $output->editedDate = null; + $this->dao->insert(TABLE_ZOUTPUT)->data($output)->exec(); + $newOutputID = $this->dao->lastInsertID(); + + $idMap[$oldID] = $newOutputID; + } + + foreach($idMap as $oldID => $newOutputID) + { + $this->dao->update(TABLE_AUDITCL)->set('objectID')->eq($newOutputID)->where('objectID')->eq($oldID)->andWhere('workflowGroup')->eq($groupID)->andWhere('objectType')->eq('zoutput')->exec(); + $this->dao->update(TABLE_AUDITPLAN)->set('objectID')->eq($newOutputID)->where('objectID')->eq($oldID)->andWhere('objectType')->eq('zoutput')->andWhere('project')->in($projectIdList)->exec(); + } + } + + /** + * 将过程文档迁移到交付物。 + * Migrate process output to deliverable. + * + * @param object $group + * @return void + */ + public function migrateOutputToDeliverable(object $group): void + { + $outputList = $this->dao->select('t1.*')->from(TABLE_ZOUTPUT) + ->alias('t1')->leftJoin(TABLE_ACTIVITY) + ->alias('t2')->on('t1.activity = t2.id') + ->where('t2.workflowGroup')->eq($group->id) + ->andWhere('t1.deleted')->eq('0') + ->fetchAll('id'); + + if(empty($outputList)) return; + + $otherModule = $this->dao->select('id')->from(TABLE_MODULE)->where('root')->eq($group->id)->andWhere('type')->eq('deliverable')->andWhere('deleted')->eq('0')->andWhere('extra')->eq('other')->fetch('id'); + + $deliverable = new stdclass(); + $deliverable->template = '[]'; + $deliverable->status = 'hidden'; + $deliverable->workflowGroup = $group->id; + $deliverable->module = $otherModule; + $deliverable->builtin = 1; + $deliverable->deleted = '1'; + + $idMap = array(); + foreach($outputList as $output) + { + $deliverable->name = $output->name; + $deliverable->activity = $output->activity; + $deliverable->trimmable = $output->optional == 'yes' ? '1' : '0'; + + $this->dao->insert(TABLE_DELIVERABLE)->data($deliverable)->exec(); + $deliverableID = $this->dao->lastInsertID(); + + $idMap[$output->id] = $deliverableID; + } + + $projectIdList = $this->dao->select('id')->from(TABLE_PROJECT)->where('workflowGroup')->eq($group->id)->fetchPairs(); + + $projectOutputList = $this->dao->select('id,output')->from(TABLE_PROGRAMOUTPUT)->where('project')->in($projectIdList)->andWhere('output')->in(array_keys($idMap))->fetchPairs(); + foreach($projectOutputList as $id => $outputID) + { + $newID = $idMap[$outputID]; + $this->dao->update(TABLE_PROGRAMOUTPUT)->set('output')->eq($newID)->where('id')->eq($id)->exec(); + } + + $auditplans = $this->dao->select('id,objectID')->from(TABLE_AUDITPLAN) + ->where('objectType')->eq('zoutput') + ->andWhere('objectID')->in(array_keys($idMap)) + ->andWhere('project')->in($projectIdList) + ->fetchPairs(); + + foreach($auditplans as $id => $objectID) + { + $newID = $idMap[$objectID]; + $this->dao->update(TABLE_AUDITPLAN)->set('objectID')->eq($newID)->where('id')->eq($id)->exec(); + } + + $auditcls = $this->dao->select('id, objectID')->from(TABLE_AUDITCL) + ->where('objectType')->eq('zoutput') + ->andWhere('workflowGroup')->eq($group->id) + ->andWhere('objectID')->in(array_keys($outputList)) + ->andWhere('deleted')->eq('0') + ->fetchPairs(); + + foreach($auditcls as $id => $objectID) + { + $newID = $idMap[$objectID]; + $this->dao->update(TABLE_AUDITCL)->set('objectID')->eq($newID)->where('id')->eq($id)->exec(); + } + } + + /** + * 更新过程的模块分类。 + * Update process modules. + * + * @param array $classifyModule + */ + protected function updateProcessModules(array $classifyModule) + { + foreach($classifyModule as $groupID => $modules) + { + foreach($modules as $key => $moduleID) + { + $this->dao->update(TABLE_PROCESS)->set('module')->eq($moduleID)->where('workflowGroup')->eq($groupID)->andWhere('type')->eq($key)->exec(); + } + } + } } diff --git a/module/upgrade/zen.php b/module/upgrade/zen.php index 025dfaf4cb..da12694cab 100644 --- a/module/upgrade/zen.php +++ b/module/upgrade/zen.php @@ -95,8 +95,6 @@ class upgradeZen extends upgrade { $this->loadModel('setting')->setItem('system.common.global.mode', 'PLM'); $this->setting->setItem('system.custom.URAndSR', '1'); - $this->setting->setItem('system.common.closedFeatures', ''); - $this->setting->setItem('system.common.disabledFeatures', ''); $this->upgrade->addORPriv($openVersion); } diff --git a/module/user/model.php b/module/user/model.php index 6098baf364..d42c6e6425 100644 --- a/module/user/model.php +++ b/module/user/model.php @@ -1322,10 +1322,10 @@ class userModel extends model $project->storyPoints = $projectStory ? round($projectStory->estimate, 1) : 0; $project->storyCount = $projectStory ? $projectStory->count : 0; $project->executionCount = zget($projectExecutionCount, $project->id, 0); - - if(in_array($this->config->edition, array('max', 'ipd'))) $project->deliverable = $this->project->countDeliverable($project); } + if(in_array($this->config->edition, array('max', 'ipd'))) $projects = $this->project->countDeliverable($projects, 'project'); + return $projects; } diff --git a/module/user/test/lib/user.unittest.class.php b/module/user/test/lib/user.unittest.class.php index 161da17881..764bf21e50 100755 --- a/module/user/test/lib/user.unittest.class.php +++ b/module/user/test/lib/user.unittest.class.php @@ -1340,7 +1340,7 @@ class userTest $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('getProgramStakeholder'); $method->setAccessible(true); - + $result = $method->invoke($this->objectModel, $programProduct); if(dao::isError()) return dao::getError(); diff --git a/module/user/test/model/checkprogrampriv.php b/module/user/test/model/checkprogrampriv.php index ec16b8c071..ea0ff17fa5 100755 --- a/module/user/test/model/checkprogrampriv.php +++ b/module/user/test/model/checkprogrampriv.php @@ -43,7 +43,7 @@ $projectModel = $tester->loadModel('project'); // 获取测试项目集 $openProgram = $projectModel->getByID(1); // 开放项目集 (acl: open, PM: admin, openedBy: admin) $privateProgram = $projectModel->getByID(4); // 私有项目集 (acl: private, PM: admin, openedBy: user1) -$pmProgram = $projectModel->getByID(5); // PM项目集 (acl: private, PM: testpm, openedBy: user2) +$pmProgram = $projectModel->getByID(5); // PM项目集 (acl: private, PM: testpm, openedBy: user2) $creatorProgram = $projectModel->getByID(6); // 创建者项目集 (acl: private, PM: testcreator, openedBy: testcreator) $childProgram = $projectModel->getByID(9); // 子级项目集 (parent: 1) diff --git a/module/user/test/model/getobjectsauthedusers.php b/module/user/test/model/getobjectsauthedusers.php index f1f222de5d..b96307b5f9 100755 --- a/module/user/test/model/getobjectsauthedusers.php +++ b/module/user/test/model/getobjectsauthedusers.php @@ -13,7 +13,7 @@ cid=19620 - 执行userTest模块的getObjectsAuthedUsersTest方法,参数是array 属性admin @admin - 执行userTest模块的getObjectsAuthedUsersTest方法,参数是array @0 - 执行userTest模块的getObjectsAuthedUsersTest方法,参数是array @0 -- 执行userTest模块的getObjectsAuthedUsersTest方法,参数是array +- 执行userTest模块的getObjectsAuthedUsersTest方法,参数是array - 属性admin @admin - 属性user4 @user4 diff --git a/module/user/test/model/getprogramstakeholder.php b/module/user/test/model/getprogramstakeholder.php index 63d549afe4..f7aa2d5c4e 100755 --- a/module/user/test/model/getprogramstakeholder.php +++ b/module/user/test/model/getprogramstakeholder.php @@ -42,7 +42,7 @@ $userTest = new userTest(); // 5. 测试步骤(必须包含至少5个) r($userTest->getProgramStakeholderTest(array(1 => array(1, 2), 2 => array(3)))) && p('1:admin') && e('admin'); // 步骤1:正常情况,检查产品1有admin -r($userTest->getProgramStakeholderTest(array())) && p() && e(0); // 步骤2:空数组 +r($userTest->getProgramStakeholderTest(array())) && p() && e(0); // 步骤2:空数组 r($userTest->getProgramStakeholderTest(array(999 => array()))) && p() && e(0); // 步骤3:不存在项目ID r($userTest->getProgramStakeholderTest(array(5 => array(5)))) && p('5:pm4') && e('pm4'); // 步骤4:仅项目经理 r($userTest->getProgramStakeholderTest(array(1 => array(1, 2)))) && p('1:admin;2:admin') && e('admin;admin'); // 步骤5:单项目多产品 \ No newline at end of file diff --git a/module/user/test/model/getuseracls.php b/module/user/test/model/getuseracls.php index b5cc3d177d..ff4695c112 100755 --- a/module/user/test/model/getuseracls.php +++ b/module/user/test/model/getuseracls.php @@ -26,7 +26,7 @@ su('admin'); $userTest = new userTest(); r(is_array($userTest->getUserAclsTest('guest'))) && p() && e('1'); // 步骤1:测试guest用户权限获取 -r(is_array($userTest->getUserAclsTest('admin'))) && p() && e('1'); // 步骤2:测试admin用户权限获取 +r(is_array($userTest->getUserAclsTest('admin'))) && p() && e('1'); // 步骤2:测试admin用户权限获取 r(is_array($userTest->getUserAclsTest('dev1'))) && p() && e('1'); // 步骤3:测试有权限组用户的权限获取 r(is_array($userTest->getUserAclsTest('nonexistent'))) && p() && e('1'); // 步骤4:测试不存在用户的权限获取 r(array_key_exists('programs', $userTest->getUserAclsTest('admin'))) && p() && e('1'); // 步骤5:测试返回结构包含programs字段 \ No newline at end of file diff --git a/module/user/test/model/islogon.php b/module/user/test/model/islogon.php index d6b09f1a6d..ab6ac39670 100755 --- a/module/user/test/model/islogon.php +++ b/module/user/test/model/islogon.php @@ -32,6 +32,9 @@ r($userTest->isLogonTest()) && p() && e(1); // 步骤2:正常登录用户user1 $_SESSION['user'] = (object)array('account' => 'guest'); r($userTest->isLogonTest()) && p() && e(0); // 步骤3:guest用户的登录状态检查 +$_SESSION['user'] = $userTest->getbyIdTest('test999'); +r($userTest->isLogonTest()) && p() && e(0); // session 中用户为 test999 时获取用户的登录状态。 + $_SESSION['user'] = false; r($userTest->isLogonTest()) && p() && e(0); // 步骤4:session中用户为false时的状态检查 @@ -42,4 +45,4 @@ $_SESSION['user'] = (object)array('account' => null); r($userTest->isLogonTest()) && p() && e(0); // 步骤6:用户对象存在但account为null的状态检查 unset($_SESSION['user']); -r($userTest->isLogonTest()) && p() && e(0); // 步骤7:session中完全没有user时的状态检查 \ No newline at end of file +r($userTest->isLogonTest()) && p() && e(0); // 步骤7:session中完全没有user时的状态检查 diff --git a/module/user/test/model/su.php b/module/user/test/model/su.php index e36510172a..2a9f6eb01b 100755 --- a/module/user/test/model/su.php +++ b/module/user/test/model/su.php @@ -58,4 +58,4 @@ catch(EndResponseException $e) $error = new stdClass(); $error->message = $e->getContent(); } -r($error) && p('message') && e('No admin users.'); \ No newline at end of file +r($error) && p('message') && e('No admin users.'); diff --git a/module/user/test/model/unbind.php b/module/user/test/model/unbind.php index 21d17ca495..57914af98b 100755 --- a/module/user/test/model/unbind.php +++ b/module/user/test/model/unbind.php @@ -39,4 +39,4 @@ r($userTest->unbindTest('user1')) && p('') && e('success'); // 步骤1:解绑 r($userTest->unbindTest('test999')) && p('') && e('success'); // 步骤2:解绑不存在的用户账号 r($userTest->unbindTest('user4')) && p('') && e('success'); // 步骤3:解绑已经为空的然之账号 r($userTest->unbindTest('')) && p('') && e('success'); // 步骤4:解绑空字符串账号参数 -r($userTest->unbindTest('user@#$%')) && p('') && e('success'); // 步骤5:解绑包含特殊字符的账号 \ No newline at end of file +r($userTest->unbindTest('user@#$%')) && p('') && e('success'); // 步骤5:解绑包含特殊字符的账号 diff --git a/module/user/test/model/uploadavatar.php b/module/user/test/model/uploadavatar.php index bfd82f8384..e7d9a2fcc9 100755 --- a/module/user/test/model/uploadavatar.php +++ b/module/user/test/model/uploadavatar.php @@ -31,6 +31,6 @@ $userTest = new userTest(); // 5. 🔴 强制要求:必须包含至少5个测试步骤 r($userTest->uploadAvatarTest()) && p('result') && e('fail'); // 步骤1:正常情况下无文件上传 r($userTest->uploadAvatarTest()) && p('result') && e('fail'); // 步骤2:模拟上传成功但无文件 -r($userTest->uploadAvatarTest()) && p('result') && e('fail'); // 步骤3:模拟获取文件信息失败 +r($userTest->uploadAvatarTest()) && p('result') && e('fail'); // 步骤3:模拟获取文件信息失败 r($userTest->uploadAvatarTest()) && p('result') && e('fail'); // 步骤4:模拟文件扩展名验证逻辑 r($userTest->uploadAvatarTest()) && p('result') && e('fail'); // 步骤5:测试方法返回数组结构 \ No newline at end of file diff --git a/module/user/test/model/yaml/project_getprojectview.yaml b/module/user/test/model/yaml/project_getprojectview.yaml index 62d260240f..4bbc61203f 100644 --- a/module/user/test/model/yaml/project_getprojectview.yaml +++ b/module/user/test/model/yaml/project_getprojectview.yaml @@ -6,39 +6,39 @@ fields: - field: id note: 项目ID range: 1-10 - + - field: name note: 项目名称 range: '项目{3}, 禅道项目{3}, 敏捷项目{2}, 瀑布项目{2}' - + - field: type note: 项目类型 range: 'project{5}, sprint{3}, kanban{2}' - + - field: acl note: 权限控制 range: 'open{4}, private{3}, program{3}' - + - field: status note: 项目状态 range: 'wait{2}, doing{5}, suspended{2}, closed{1}' - + - field: openedBy note: 创建者 range: 'admin{3}, user1{2}, user2{2}, manager{3}' - + - field: PM note: 项目经理 range: 'manager{5}, user1{2}, user2{3}' - + - field: begin note: 开始日期 range: '2024-01-01{3}, 2024-02-01{3}, 2024-03-01{4}' - + - field: end note: 结束日期 range: '2024-12-31{3}, 2024-06-30{3}, 2024-09-30{4}' - + - field: deleted note: 是否删除 range: '0{9}, 1{1}' \ No newline at end of file diff --git a/module/user/test/model/yaml/usertpl_saveoldusertemplate.yaml b/module/user/test/model/yaml/usertpl_saveoldusertemplate.yaml index 02f30ccc83..dbc35898bb 100644 --- a/module/user/test/model/yaml/usertpl_saveoldusertemplate.yaml +++ b/module/user/test/model/yaml/usertpl_saveoldusertemplate.yaml @@ -13,7 +13,7 @@ fields: - field: account note: 用户账号 range: admin{3}, user1{2}, user2{2}, user3{3} -- field: type +- field: type note: 模板类型 range: bug{5}, story{3}, task{2} - field: title diff --git a/module/user/zen.php b/module/user/zen.php index 715ff53d50..f141f01086 100644 --- a/module/user/zen.php +++ b/module/user/zen.php @@ -104,8 +104,8 @@ class userZen extends user if(common::hasPriv($moduleName, 'testcase')) $featureBarMenus['testcase'] = array('active' => false, 'url' => $this->createLink($moduleName, 'testcase', $params), 'text' => $this->lang->user->testCase); if(common::hasPriv($moduleName, 'execution') && in_array($this->config->systemMode, array('ALM', 'PLM'))) $featureBarMenus['execution'] = array('active' => false, 'url' => $this->createLink($moduleName, 'execution', $params), 'text' => $this->lang->user->execution); - if(common::hasPriv($moduleName, 'issue') && in_array($this->config->edition, array('max', 'ipd'))) $featureBarMenus['issue'] = array('active' => false, 'url' => $this->createLink($moduleName, 'issue', $params), 'text' => $this->lang->user->issue); - if(common::hasPriv($moduleName, 'risk') && in_array($this->config->edition, array('max', 'ipd'))) $featureBarMenus['risk'] = array('active' => false, 'url' => $this->createLink($moduleName, 'risk', $params), 'text' => $this->lang->user->risk); + if(common::hasPriv($moduleName, 'issue') && in_array($this->config->edition, array('max', 'ipd')) && helper::hasFeature('issue')) $featureBarMenus['issue'] = array('active' => false, 'url' => $this->createLink($moduleName, 'issue', $params), 'text' => $this->lang->user->issue); + if(common::hasPriv($moduleName, 'risk') && in_array($this->config->edition, array('max', 'ipd')) && helper::hasFeature('risk')) $featureBarMenus['risk'] = array('active' => false, 'url' => $this->createLink($moduleName, 'risk', $params), 'text' => $this->lang->user->risk); if(common::hasPriv($moduleName, 'dynamic')) $featureBarMenus['dynamic'] = array('active' => false, 'url' => $this->createLink($moduleName, 'dynamic', "$params&type=today"), 'text' => $this->lang->user->dynamic); if(common::hasPriv($moduleName, 'profile')) $featureBarMenus['profile'] = array('active' => false, 'url' => $this->createLink($moduleName, 'profile', $params), 'text' => $this->lang->user->profile); diff --git a/module/webhook/test/lib/webhook.unittest.class.php b/module/webhook/test/lib/webhook.unittest.class.php index 1c41ee2ab7..05a4176756 100644 --- a/module/webhook/test/lib/webhook.unittest.class.php +++ b/module/webhook/test/lib/webhook.unittest.class.php @@ -608,7 +608,7 @@ class webhookTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getDingdingSecret'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $webhook); if(dao::isError()) return dao::getError(); @@ -627,7 +627,7 @@ class webhookTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getWeixinSecret'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $webhook); if(dao::isError()) return dao::getError(); @@ -646,7 +646,7 @@ class webhookTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getFeishuSecret'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $webhook); if(dao::isError()) return dao::getError(); @@ -668,7 +668,7 @@ class webhookTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getActionText'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $data, $action, $object, $users); if(dao::isError()) return dao::getError(); diff --git a/module/webhook/test/model/bind.php b/module/webhook/test/model/bind.php index 57bd0dfb41..6a938934cb 100755 --- a/module/webhook/test/model/bind.php +++ b/module/webhook/test/model/bind.php @@ -1,6 +1,8 @@ #!/usr/bin/env php gen(0); + /** title=测试 webhookModel::bind(); @@ -44,4 +46,4 @@ r($webhookTest->bindTest(1, $duplicateUserList)) && p() && e('1'); // 步骤5:测试不存在的webhookID绑定 $validUserList2 = array('admin' => 'openid_admin_888'); -r($webhookTest->bindTest(999, $validUserList2)) && p() && e('1'); \ No newline at end of file +r($webhookTest->bindTest(999, $validUserList2)) && p() && e('1'); diff --git a/module/webhook/test/model/builddata.php b/module/webhook/test/model/builddata.php index cc885da343..202c012c07 100755 --- a/module/webhook/test/model/builddata.php +++ b/module/webhook/test/model/builddata.php @@ -38,4 +38,4 @@ r(true) && p() && e(true); // 步骤1:正常参数测试 r(true) && p() && e(true); // 步骤2:空objectType参数 r(true) && p() && e(true); // 步骤3:不存在的actionType(有数据) r(true) && p() && e(true); // 步骤4:不存在的actionID -r(true) && p() && e(true); // 步骤5:无匹配数据 \ No newline at end of file +r(true) && p() && e(true); // 步骤5:无匹配数据 diff --git a/module/webhook/test/model/getbindaccount.php b/module/webhook/test/model/getbindaccount.php index 98aef870c8..a183911929 100755 --- a/module/webhook/test/model/getbindaccount.php +++ b/module/webhook/test/model/getbindaccount.php @@ -38,4 +38,4 @@ r($webhookTest->getBindAccountTest(1, 'webhook', 'openid1')) && p() && e('admin' r($webhookTest->getBindAccountTest(999, 'webhook', 'openid1')) && p() && e('0'); // 步骤2:查询不存在的webhookID r($webhookTest->getBindAccountTest(1, 'nonexist', 'openid1')) && p() && e('0'); // 步骤3:查询不存在的webhookType r($webhookTest->getBindAccountTest(1, 'webhook', 'nonexist')) && p() && e('0'); // 步骤4:查询不存在的openID -r($webhookTest->getBindAccountTest(0, '', '')) && p() && e('0'); // 步骤5:查询空参数情况 \ No newline at end of file +r($webhookTest->getBindAccountTest(0, '', '')) && p() && e('0'); // 步骤5:查询空参数情况 diff --git a/module/webhook/test/model/savelog.php b/module/webhook/test/model/savelog.php index 28c1c6dc42..2b6cb2b6a1 100755 --- a/module/webhook/test/model/savelog.php +++ b/module/webhook/test/model/savelog.php @@ -65,4 +65,4 @@ r($saveResult) && p() && e('1'); r($webhookTest->saveLogTest($webhook2, 999, 'action test data', 'action test result')) && p('action,objectID') && e('999,2'); -r($webhookTest->saveLogTest($webhook3, 106, 'special chars: 中文', 'result with special chars')) && p('data,contentType') && e('special chars: 中文,application/xml'); \ No newline at end of file +r($webhookTest->saveLogTest($webhook3, 106, 'special chars: 中文', 'result with special chars')) && p('data,contentType') && e('special chars: 中文,application/xml'); diff --git a/module/weekly/test/model/save.php b/module/weekly/test/model/save.php index c8883405c7..7db0153601 100755 --- a/module/weekly/test/model/save.php +++ b/module/weekly/test/model/save.php @@ -7,19 +7,19 @@ title=测试 weeklyModel::save(); timeout=0 cid=19739 -- 执行weeklyTest模块的saveTest方法,参数是1, '2023-10-15' +- 执行weeklyTest模块的saveTest方法,参数是1, '2023-10-15' - 属性project @1 - 属性weekStart @2023-10-09 -- 执行weeklyTest模块的saveTest方法,参数是0, '2023-10-15' +- 执行weeklyTest模块的saveTest方法,参数是0, '2023-10-15' - 属性project @0 - 属性weekStart @2023-10-09 -- 执行weeklyTest模块的saveTest方法,参数是2, '2023-10-15' +- 执行weeklyTest模块的saveTest方法,参数是2, '2023-10-15' - 属性project @2 - 属性weekStart @2023-10-09 -- 执行weeklyTest模块的saveTest方法,参数是3, '2023-10-15' +- 执行weeklyTest模块的saveTest方法,参数是3, '2023-10-15' - 属性project @3 - 属性weekStart @2023-10-09 -- 执行weeklyTest模块的saveTest方法,参数是5, '2023-10-15' +- 执行weeklyTest模块的saveTest方法,参数是5, '2023-10-15' - 属性project @5 - 属性weekStart @2023-10-09 diff --git a/module/weekly/test/model/yaml/project_save.yaml b/module/weekly/test/model/yaml/project_save.yaml index 6ede924970..e42317b116 100644 --- a/module/weekly/test/model/yaml/project_save.yaml +++ b/module/weekly/test/model/yaml/project_save.yaml @@ -11,7 +11,7 @@ fields: range: project{1-10} - field: begin range: 2023-09-01{5},2023-10-01{5} -- field: end +- field: end range: 2023-12-31{5},2024-01-31{5} - field: realBegan range: 2023-09-01{5},2023-10-01{5} diff --git a/module/zahost/test/lib/zahost.unittest.class.php b/module/zahost/test/lib/zahost.unittest.class.php index 3c4cec3c73..e66b08c994 100755 --- a/module/zahost/test/lib/zahost.unittest.class.php +++ b/module/zahost/test/lib/zahost.unittest.class.php @@ -227,7 +227,7 @@ class zahostTest { $image = $this->objectModel->getImageByID($imageID); if(!$image) return false; - + $image->address = "https://pkg.qucheng.com/zenagent/image/{$image->name}.qcow2"; $result = $this->objectModel->downloadImage($image); diff --git a/module/zanode/test/lib/zanode.unittest.class.php b/module/zanode/test/lib/zanode.unittest.class.php index f451f42176..e48ceceaae 100755 --- a/module/zanode/test/lib/zanode.unittest.class.php +++ b/module/zanode/test/lib/zanode.unittest.class.php @@ -442,7 +442,7 @@ class zanodeTest if(!$oldNode) return '0'; $result = $this->destroy($id); - + if($result === '') return 'success'; return $result ? $result : '0'; } @@ -463,14 +463,14 @@ class zanodeTest ->leftJoin(TABLE_IMAGE)->alias('t3')->on('t3.id = t1.image') ->where('t1.id')->eq($nodeID) ->fetch(); - + if(empty($node)) return false; - + // 调用protected方法processNodeStatus $reflection = new ReflectionClass($this->objectModel); $method = $reflection->getMethod('processNodeStatus'); $method->setAccessible(true); - + return $method->invoke($this->objectModel, $node); } @@ -540,15 +540,15 @@ class zanodeTest // 检查必要参数是否存在 if(!isset($data->image) || !isset($data->parent)) return false; if($data->image === null || $data->parent === null) return false; - + // 检查镜像是否存在 $image = $this->getImageByID($data->image); if(!$image) return false; - + // 检查宿主机是否存在 $host = $this->getHostByID($data->parent); if(!$host) return false; - + $result = $this->linkAgentService($data); if(dao::isError()) return dao::getError(); @@ -638,7 +638,7 @@ class zanodeTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getSubZahostListByID'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $hostID, $orderBy); if(dao::isError()) return dao::getError(); @@ -674,7 +674,7 @@ class zanodeTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getZaNodeListByQuery'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $query, $orderBy, $pager); if(dao::isError()) return dao::getError(); @@ -716,7 +716,7 @@ class zanodeTest $reflection = new ReflectionClass($this->objectTao); $method = $reflection->getMethod('getHostsByIDList'); $method->setAccessible(true); - + $result = $method->invoke($this->objectTao, $hostIDList); if(dao::isError()) return dao::getError(); diff --git a/module/zanode/test/model/setmenu.php b/module/zanode/test/model/setmenu.php index 66601d7580..7b83ca1235 100755 --- a/module/zanode/test/model/setmenu.php +++ b/module/zanode/test/model/setmenu.php @@ -7,11 +7,11 @@ title=测试 zanodeModel::setMenu(); timeout=0 cid=19845 -- 执行zanodeTest模块的setMenuTest方法,参数是true +- 执行zanodeTest模块的setMenuTest方法,参数是true - 属性beforeCall @1 - 属性afterCall @1 - 属性createHost @1 -- 执行zanodeTest模块的setMenuTest方法,参数是false +- 执行zanodeTest模块的setMenuTest方法,参数是false - 属性beforeCall @1 - 属性afterCall @0 - 属性createHost @0 diff --git a/test/data/approvalflow.yaml b/test/data/approvalflow.yaml index b99c1cf235..7764f9d62f 100644 --- a/test/data/approvalflow.yaml +++ b/test/data/approvalflow.yaml @@ -18,5 +18,3 @@ fields: range: "(M)-(w)" type: timestamp format: "YY/MM/DD" - - field: type - range: 'project' diff --git a/test/data/approvalflowspec.yaml b/test/data/approvalflowspec.yaml index 4c22449e66..dbf091d046 100644 --- a/test/data/approvalflowspec.yaml +++ b/test/data/approvalflowspec.yaml @@ -1,4 +1,4 @@ -title: table zt_approvalflow +title: table zt_approvalflowspec desc: "" author: sun guangming version: "1.0" @@ -10,9 +10,8 @@ fields: - field: version range: 1 - field: nodes - range: `[{"type":"start","ccs":[]},{"id":"3ewcj92p55e","type":"approval","title":"审批","reviewType":"manual","multiple":"and","agentType":"pass","reviewers":[{"type":"select"}],"ccs":[]},{"type":"end","ccs":[]}]` - - field: code - range: 'simple' + range: '' + prefix: '[{"type":"start","ccs":[]},{"id":"3ewcj92p55e","type":"approval","title":"审批","reviewType":"manual","multiple":"and","percent":"50","commentType":"required","agentType":"pass","selfType":"selfReview","deletedType":"setAdmin","reviewers":[{"type":"select","users":[],"roles":[""],"depts":[""],"positions":[""],"userRange":"all","required":"yes"}],"ccs":[{"type":"select","users":[],"roles":[""],"depts":[""],"positions":[""],"userRange":"all"}]},{"id":"pxodxd2rq9","type":"approval","title":"审批","reviewType":"manual","multiple":"and","percent":"50","commentType":"noRequired","agentType":"pass","selfType":"selfReview","deletedType":"setAdmin","reviewers":[{"type":"setByPrev"}],"ccs":[{"type":"select","users":[],"roles":[""],"depts":[""],"positions":[""],"userRange":"all"}]},{"type":"end","ccs":[]}]' - field: createdBy range: 'admin' - field: createdDate diff --git a/test/data/auditcl.yaml b/test/data/auditcl.yaml index 33477f38a2..f646bdaee0 100644 --- a/test/data/auditcl.yaml +++ b/test/data/auditcl.yaml @@ -45,13 +45,6 @@ fields: postfix: "" loop: 0 format: "" - - field: parent - note: "父流程ID" - range: 0 - prefix: "" - postfix: "" - loop: 0 - format: "" - field: assignedTo note: "指派给" from: common.user.v1.yaml @@ -67,13 +60,6 @@ fields: postfix: "" loop: 0 format: "" - - field: subStatus - note: "子状态" - range: 0 - prefix: "" - postfix: "" - loop: 0 - format: "" - field: createdBy note: "由谁创建" from: common.user.v1.yaml @@ -84,12 +70,9 @@ fields: format: "" - field: createdDate note: "创建日期" - from: common.date.v1.yaml - use: dateA - prefix: "" - postfix: "" - loop: 0 - format: "" + range: "(M)-(w)" + type: timestamp + format: "YY/MM/DD" - field: editedBy note: "由谁编辑" from: common.user.v1.yaml @@ -100,12 +83,9 @@ fields: format: "" - field: editedDate note: "编辑日期" - from: common.date.v1.yaml - use: dateA - prefix: "" - postfix: "" - loop: 0 - format: "" + range: "(M)-(w)" + type: timestamp + format: "YY/MM/DD" - field: assignedBy note: "由谁指派" from: common.user.v1.yaml @@ -116,12 +96,9 @@ fields: format: "" - field: assignedDate note: "指派日期" - from: common.date.v1.yaml - use: dateA - prefix: "" - postfix: "" - loop: 0 - format: "" + range: "(M)-(w)" + type: timestamp + format: "YY/MM/DD" - field: deleted note: "是否删除" range: 0 diff --git a/test/data/deliverable.yaml b/test/data/deliverable.yaml index ee40d120cc..2c72f0fcb1 100644 --- a/test/data/deliverable.yaml +++ b/test/data/deliverable.yaml @@ -13,22 +13,22 @@ fields: - field: name2 range: 1-1000 - field: module - range: project,execution - - field: method - range: create,close - - field: model + range: 1-100 + - field: workflowGroup + range: 1-100 + - field: status + range: enabled,disabled + - field: activity + range: 1-100 + - field: trimmable + range: 0,1 + - field: trimRule fields: - - field: model1 - range: product,project - postfix: "_" - - field: model2 - range: waterfall,scrum - postfix: "_" - - field: model3 - range: mix,request,design,dev,qa,release,review - - field: type - range: doc,file - - field: files - range: 1-10 + - field: name1 + range: '裁剪准则' + - field: name2 + range: 1-100 - field: desc - range: '交付物描述' \ No newline at end of file + range: '交付物描述' + - field: template + prefix: "[]" diff --git a/test/data/nc.yaml b/test/data/nc.yaml index 7b13f4ee46..b14a584cc7 100644 --- a/test/data/nc.yaml +++ b/test/data/nc.yaml @@ -4,18 +4,11 @@ author: automated export version: "1.0" fields: - field: id - note: "" + note: "ID" + range: 1-10000 prefix: "" postfix: "" loop: 0 - loopfix: "" - format: "" - - field: program - note: "" - prefix: "" - postfix: "" - loop: 0 - loopfix: "" format: "" - field: auditplan note: "" @@ -67,12 +60,10 @@ fields: loopfix: "" format: "" - field: deadline - note: "" - prefix: "" - postfix: "" - loop: 0 - loopfix: "" - format: "" + note: "截至日期" + range: "(M)-(w)" + type: timestamp + format: "YY-MM-DD" - field: resolvedBy note: "" prefix: "" @@ -88,12 +79,10 @@ fields: loopfix: "" format: "" - field: resolvedDate - note: "" - prefix: "" - postfix: "" - loop: 0 - loopfix: "" - format: "" + note: "解决日期" + range: "(M)-(w)" + type: timestamp + format: "YY-MM-DD" - field: closedBy note: "" prefix: "" @@ -102,12 +91,10 @@ fields: loopfix: "" format: "" - field: closedDate - note: "" - prefix: "" - postfix: "" - loop: 0 - loopfix: "" - format: "" + note: "关闭日期" + range: "(M)-(w)" + type: timestamp + format: "YY-MM-DD" - field: parent note: "" prefix: "" @@ -130,12 +117,10 @@ fields: loopfix: "" format: "" - field: createdDate - note: "" - prefix: "" - postfix: "" - loop: 0 - loopfix: "" - format: "" + note: "创建日期" + range: "(M)-(w)" + type: timestamp + format: "YY-MM-DD" - field: editedBy note: "" prefix: "" @@ -144,12 +129,10 @@ fields: loopfix: "" format: "" - field: editedDate - note: "" - prefix: "" - postfix: "" - loop: 0 - loopfix: "" - format: "" + note: "编辑日期" + range: "(M)-(w)" + type: timestamp + format: "YY-MM-DD" - field: deleted note: "" prefix: "" diff --git a/test/data/object.yaml b/test/data/object.yaml index c441d71be6..8891b78b6a 100644 --- a/test/data/object.yaml +++ b/test/data/object.yaml @@ -59,13 +59,6 @@ fields: postfix: "" loop: 0 format: "" - - field: range - note: "范围" - range: all - prefix: "" - postfix: "" - loop: 0 - format: "" - field: data note: "数据" range: 1-10000 @@ -73,18 +66,6 @@ fields: postfix: "" loop: 0 format: "" - - field: storyEst - range: "" - - field: taskEst - range: "" - - field: requestEst - range: "" - - field: testEst - range: "" - - field: devEst - range: "" - - field: designEst - range: "" - field: end range: "(M)-(w)" type: timestamp diff --git a/test/data/opportunity.yaml b/test/data/opportunity.yaml new file mode 100644 index 0000000000..5e81c71886 --- /dev/null +++ b/test/data/opportunity.yaml @@ -0,0 +1,124 @@ +title: table zt_flow_opportunity +desc: "项目机会" +author: automated export +version: "1.0" +fields: + - field: id + note: "ID" + range: 1-10000 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: project + note: "所属项目" + range: 1-100 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: execution + note: "所属执行" + range: 101-700 + - field: name + note: "机会名称" + range: 1-10000 + prefix: "机会名称" + postfix: "" + loop: 0 + format: "" + - field: source + note: "来源" + range: business,team,logistic,manage,sourcing,outsourcing,customer,others + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: strategy + note: "策略" + range: monitor,create,utilize,enhance,accept + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: status + note: "状态" + range: active,closed,hangup,canceled + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: impact + note: "影响程度" + range: 1-5 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: chance + note: "发生概率" + range: 1-5 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: ratio + note: "风险系数" + range: 1-25 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: pri + note: "优先级" + range: 1-3 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: identifiedDate + note: "识别日期" + range: "(-1M)-(+1w):60" + type: timestamp + format: "YYYY-MM-DD" + - field: prevention + note: "处理措施" + range: 1-10000 + prefix: "这是处理措施" + postfix: "" + loop: 0 + format: "" + - field: plannedClosedDate + note: "计划关闭日期" + range: "(-1M)-(+1w):60" + type: timestamp + format: "YYYY-MM-DD" + - field: actualClosedDate + note: "实际关闭日期" + range: "(-1M)-(+1w):60" + type: timestamp + format: "YYYY-MM-DD" + - field: createdBy + note: "由谁创建" + range: admin,user1 + - field: createdDate + note: "创建日期" + range: "(-1M)-(+1w):60" + type: timestamp + format: "YYYY-MM-DD" + - field: editedBy + note: "由谁编辑" + from: common.user.v1.yaml + use: user + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: editedDate + note: "编辑日期" + range: "(-1M)-(+1w):60" + type: timestamp + format: "YYYY-MM-DD" + - field: deleted + note: "是否删除" + range: 0 diff --git a/test/data/process.yaml b/test/data/process.yaml index 47b8709242..a444d3111b 100644 --- a/test/data/process.yaml +++ b/test/data/process.yaml @@ -17,9 +17,16 @@ fields: postfix: "" loop: 0 format: "" - - field: type + - field: workflowGroup + note: "流程" + range: 1-10 + prefix: "" + postfix: "" + loop: 0 + format: "" + - field: module note: "分类" - range: project,engineering,support + range: 1-10000 prefix: "" postfix: "" loop: 0 diff --git a/test/data/programoutput.yaml b/test/data/programoutput.yaml index cbed51c393..85ac41ef28 100644 --- a/test/data/programoutput.yaml +++ b/test/data/programoutput.yaml @@ -10,7 +10,7 @@ fields: postfix: "" loop: 0 format: "" - - field: program + - field: project note: "所属项目" range: 1-10000 prefix: "" @@ -74,22 +74,6 @@ fields: postfix: "" loop: 0 format: "" - - field: createdBy - note: "创建者" - from: common.user.v1.yaml - use: user - prefix: "" - postfix: "" - loop: 0 - format: "" - - field: createdDate - note: "创建日期" - from: common.date.v1.yaml - use: dateB - prefix: "" - postfix: "" - loop: 0 - format: "" - field: deleted note: "是否删除" range: 0 diff --git a/test/data/projectchange.yaml b/test/data/projectchange.yaml new file mode 100644 index 0000000000..be9e1faa36 --- /dev/null +++ b/test/data/projectchange.yaml @@ -0,0 +1,28 @@ +--- +title: table zt_projectchange +author: automated export +version: "1.0" +fields: + - field: id + range: 1-1000 + - field: project + note: "所属项目" + range: 1-100 + - field: name + note: "项目变更名称" + fields: + - field: name1 + range: "项目变更" + - field: name2 + range: 1-1000 + - field: urgency + range: "[normal,major,critical]" + - field: type + range: "[story,design,stage,case]" + - field: status + range: "[wait,reviewing,pass,reject,reverting]" + - field: owner + range: "[admin,dev1,test1]{30},[]{20}" + - field: owner + range: "[admin,dev1,test1]{30},[]{20}" +... diff --git a/test/data/review.yaml b/test/data/review.yaml index f1179a120b..32f3342177 100644 --- a/test/data/review.yaml +++ b/test/data/review.yaml @@ -40,7 +40,7 @@ fields: format: "" - field: status note: "评审状态" - range: draft,wait,reviewing,pass,fail,auditing,done + range: draft,wait,reviewing,pass,fail,done prefix: "" postfix: "" loop: 0 @@ -53,14 +53,6 @@ fields: postfix: "" loop: 0 format: "" - - field: auditedBy - note: "由谁审计" - from: common.user.v1.yaml - use: user - prefix: "" - postfix: "" - loop: 0 - format: "" - field: createdBy note: "由谁创建" from: common.user.v1.yaml @@ -87,14 +79,6 @@ fields: postfix: "" loop: 0 format: "" - - field: lastAuditedBy - note: "最后审计者" - from: common.user.v1.yaml - use: user - prefix: "" - postfix: "" - loop: 0 - format: "" - field: lastEditedBy note: "最后编辑者" from: common.user.v1.yaml @@ -110,13 +94,6 @@ fields: postfix: "" loop: 0 format: "" - - field: auditResult - note: "审计结果" - range: pass,fail,needfix - prefix: "" - postfix: "" - loop: 0 - format: "" - field: deleted note: "是否删除" range: 0 diff --git a/test/data/reviewcl.yaml b/test/data/reviewcl.yaml index d29195df28..0900769b3a 100644 --- a/test/data/reviewcl.yaml +++ b/test/data/reviewcl.yaml @@ -22,7 +22,7 @@ fields: format: "" - field: object note: "检查清单标题" - range: PP,QAP,CMP,ITP,URS,SRS,HLDS,DDS,Code,ITTC,STP,STTC,UM + range: 1-10000 prefix: "" postfix: "" loop: 0 diff --git a/test/data/risk.yaml b/test/data/risk.yaml index f384bee036..4a6fcd7f80 100644 --- a/test/data/risk.yaml +++ b/test/data/risk.yaml @@ -78,7 +78,7 @@ fields: format: "" - field: pri note: "优先级" - range: high,middle,low + range: 1,2,3 prefix: "" postfix: "" loop: 0 diff --git a/www/js/fingerprint/specs/lib/jasmine-1.3.1/jasmine.js b/www/js/fingerprint/specs/lib/jasmine-1.3.1/jasmine.js index 6b3459b913..b7b462a956 100644 --- a/www/js/fingerprint/specs/lib/jasmine-1.3.1/jasmine.js +++ b/www/js/fingerprint/specs/lib/jasmine-1.3.1/jasmine.js @@ -1100,7 +1100,7 @@ jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { type: isSuite ? 'suite' : 'spec', children: [] }; - + if (isSuite) { var children = suiteOrSpec.children(); for (var i = 0; i < children.length; i++) { @@ -1929,7 +1929,7 @@ jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!obj.hasOwnProperty(property)) continue; if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && obj.__lookupGetter__(property) !== null) : false); } }; @@ -2061,7 +2061,7 @@ jasmine.Queue.prototype.next_ = function() { while (goAgain) { goAgain = false; - + if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { var calledSynchronously = true; var completedSynchronously = false; @@ -2099,7 +2099,7 @@ jasmine.Queue.prototype.next_ = function() { if (completedSynchronously) { onComplete(); } - + } else { self.running = false; if (self.onComplete) { @@ -2597,4 +2597,4 @@ jasmine.version_= { "minor": 3, "build": 1, "revision": 1354556913 -}; +}; \ No newline at end of file diff --git a/www/js/fingerprint/specs/lib/phantom-jasmine/console-runner.js b/www/js/fingerprint/specs/lib/phantom-jasmine/console-runner.js index e197595940..9ebf531563 100644 --- a/www/js/fingerprint/specs/lib/phantom-jasmine/console-runner.js +++ b/www/js/fingerprint/specs/lib/phantom-jasmine/console-runner.js @@ -1,9 +1,9 @@ /** - Jasmine Reporter that outputs test results to the browser console. + Jasmine Reporter that outputs test results to the browser console. Useful for running in a headless environment such as PhantomJs, ZombieJs etc. Usage: - // From your html file that loads jasmine: + // From your html file that loads jasmine: jasmine.getEnv().addReporter(new jasmine.ConsoleReporter()); jasmine.getEnv().execute(); */ @@ -23,7 +23,7 @@ var color_code = this.color_map[color]; return "\033[" + color_code + "m" + text + "\033[0m"; } - + var ConsoleReporter = function() { if (!console || !console.log) { throw "console isn't present!"; } this.status = this.statuses.stopped; @@ -100,5 +100,4 @@ }; jasmine.ConsoleReporter = ConsoleReporter; -})(jasmine, console); - +})(jasmine, console); \ No newline at end of file diff --git a/www/theme/default/images/main/featureswitcher.png b/www/theme/default/images/main/featureswitcher.png new file mode 100644 index 0000000000..38bbfd85f4 Binary files /dev/null and b/www/theme/default/images/main/featureswitcher.png differ