From f557cd3c7c1d981e23ed6b8220c8d8c4906a7e27 Mon Sep 17 00:00:00 2001
From: 이민희 <mhlee@maprex.co.kr>
Date: 화, 07 12월 2021 19:30:47 +0900
Subject: [PATCH] - 사용자 필드 기능 수정 - 이슈 추가/수정시 워크플로우에 설정된 담당부서로 변경되도록 수정 * DB 초기화 필요

---
 src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java         |   27 ++++
 src/main/java/kr/wisestone/owl/vo/IssueVo.java                            |    9 +
 src/main/java/kr/wisestone/owl/service/impl/DepartmentServiceImpl.java    |   29 ++++
 src/main/resources/migration/V1_12__Alter_Table.sql                       |    7 +
 src/main/webapp/views/issue/issueAdd.html                                 |   63 +++++++++-
 src/main/java/kr/wisestone/owl/web/controller/DepartmentController.java   |   11 +
 src/main/webapp/scripts/app/customField/customFieldModify.controller.js   |    4 
 src/main/webapp/scripts/app/issue/issueAdd.controller.js                  |   33 +++++
 src/main/webapp/scripts/components/userWorkspace/userWorkspace.service.js |    6 +
 src/main/java/kr/wisestone/owl/domain/CustomField.java                    |    9 +
 src/main/resources/mybatis/query-template/project-template.xml            |    8 
 src/main/webapp/scripts/components/utils/autoComplete.controller.js       |   15 +
 src/main/webapp/views/customField/customFieldAdd.html                     |    1 
 src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java              |   14 ++
 src/main/java/kr/wisestone/owl/web/condition/DepartmentCondition.java     |   11 +
 src/main/java/kr/wisestone/owl/service/DepartmentService.java             |    2 
 src/main/webapp/i18n/ko/global.json                                       |    1 
 src/main/webapp/views/issue/issueModify.html                              |   75 +++++++++---
 src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java                      |    9 +
 src/main/webapp/scripts/app/customField/customFieldAdd.controller.js      |    5 
 20 files changed, 296 insertions(+), 43 deletions(-)

diff --git a/src/main/java/kr/wisestone/owl/domain/CustomField.java b/src/main/java/kr/wisestone/owl/domain/CustomField.java
index 22884c5..309dd06 100644
--- a/src/main/java/kr/wisestone/owl/domain/CustomField.java
+++ b/src/main/java/kr/wisestone/owl/domain/CustomField.java
@@ -20,6 +20,7 @@
     private Long id;
     private String name;
     private String useFlag;
+    private String requiredData;
 
     @Enumerated(EnumType.STRING)
     private CustomFieldType customFieldType;
@@ -118,4 +119,12 @@
     public void setUseFlag(String useFlag) {
         this.useFlag = useFlag;
     }
+
+    public String getRequiredData() {
+        return requiredData;
+    }
+
+    public void setRequiredData(String requiredData) {
+        this.requiredData = requiredData;
+    }
 }
diff --git a/src/main/java/kr/wisestone/owl/service/DepartmentService.java b/src/main/java/kr/wisestone/owl/service/DepartmentService.java
index 8b813da..4386624 100644
--- a/src/main/java/kr/wisestone/owl/service/DepartmentService.java
+++ b/src/main/java/kr/wisestone/owl/service/DepartmentService.java
@@ -37,5 +37,7 @@
 
     void findProjectDepartment(Map<String, Object> resJsonData, DepartmentCondition departmentCondition);
 
+    void findWorkflowDepartment(Map<String, Object> resJsonData, DepartmentCondition departmentCondition);
+
     List<Map<String, Object>> findProjectDepartment(Project project);
 }
diff --git a/src/main/java/kr/wisestone/owl/service/impl/DepartmentServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/DepartmentServiceImpl.java
index 587a349..344147e 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/DepartmentServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/DepartmentServiceImpl.java
@@ -2,6 +2,7 @@
 
 import kr.wisestone.owl.domain.*;
 import kr.wisestone.owl.mapper.DepartmentMapper;
+import kr.wisestone.owl.repository.WorkflowDepartmentRepository;
 import kr.wisestone.owl.service.*;
 import kr.wisestone.owl.util.CommonUtil;
 import kr.wisestone.owl.web.condition.DepartmentCondition;
@@ -55,6 +56,12 @@
 
     @Autowired
     private ProjectRoleService projectRoleService;
+
+    @Autowired
+    private WorkflowDepartmentRepository workflowDepartmentRepository;
+
+    @Autowired
+    private IssueTypeService issueTypeService;
 
     @Autowired
     private WorkspaceService workspaceService;
@@ -156,6 +163,28 @@
         resJsonData.put(Constants.RES_KEY_CONTENTS, departmentVos);
     }
 
+    //  �썙�겕�뵆濡쒖슦�뿉 �냽�빐�엳�뒗 遺��꽌 紐⑸줉 議고쉶
+    @Override
+    @Transactional(readOnly = true)
+    public void findWorkflowDepartment(Map<String, Object> resJsonData, DepartmentCondition departmentCondition) {
+
+        IssueType issueType = this.issueTypeService.getIssueType(departmentCondition.getIssueTypeId());
+        Long workflowId = issueType.getWorkflow().getId();
+        List<WorkflowDepartment> workflowDepartmentList = this.workflowDepartmentRepository.findByWorkflowId(workflowId);
+        List<DepartmentVo> departmentVos = Lists.newArrayList();
+
+        //List<Long> workflowDepartmentIds = Lists.newArrayList();
+        if(workflowDepartmentList != null && workflowDepartmentList.size()>0){
+            for(WorkflowDepartment workflowDepartment : workflowDepartmentList){
+                DepartmentVo departmentVo = ConvertUtil.copyProperties(workflowDepartment.getDepartment(), DepartmentVo.class);
+                departmentVo.setByName(departmentVo.getDepartmentName());
+                //workflowDepartmentIds.add(workflowDepartment.getDepartment().getId());
+                departmentVos.add(departmentVo);
+            }
+            resJsonData.put(Constants.RES_KEY_CONTENTS, departmentVos);
+        }
+    }
+
 
     // 遺��꽌 紐⑸줉�쓣 �뿊��濡� �떎�슫濡쒕뱶 �븳�떎.
     @Override
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
index 1c33b93..7c58174 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -19,6 +19,7 @@
 import kr.wisestone.owl.mapper.ProjectMapper;
 import kr.wisestone.owl.repository.IssueRepository;
 import kr.wisestone.owl.repository.UserDepartmentRepository;
+import kr.wisestone.owl.repository.WorkflowDepartmentRepository;
 import kr.wisestone.owl.service.*;
 import kr.wisestone.owl.util.*;
 import kr.wisestone.owl.util.DateUtil;
@@ -167,6 +168,9 @@
 
     @Autowired
     private UserDepartmentRepository userDepartmentRepository;
+
+    @Autowired
+    private WorkflowDepartmentRepository workflowDepartmentRepository;
 
     @Override
     protected JpaRepository<Issue, Long> getRepository() {
@@ -677,11 +681,28 @@
         issueCondition.setMyDepartmentIds(myDepartmentIds);
     }
 
+    void SetWorkflowDepartment(List<IssueVo> issueVos){
+        for(IssueVo issueVo : issueVos){
+            Long issueTypeId = issueVo.getIssueTypeId();
+            IssueType issueType = this.issueTypeService.getIssueType(issueTypeId);
+            Long workflowId = issueType.getWorkflow().getId();
+            List<WorkflowDepartment> workflowDepartmentList = this.workflowDepartmentRepository.findByWorkflowId(workflowId);
+            List<Long> workflowDepartmentIds = Lists.newArrayList();
+            if(workflowDepartmentList != null && workflowDepartmentList.size()>0){
+                for(WorkflowDepartment workflowDepartment : workflowDepartmentList){
+                    workflowDepartmentIds.add(workflowDepartment.getDepartment().getId());
+                }
+            }
+            if(issueVo.getIssueTypeId().equals(issueTypeId)){
+                issueVo.setWorkflowDepartmentIds(workflowDepartmentIds);
+            }
+        }
+    }
+
     //  �씠�뒋 紐⑸줉�쓣 議고쉶�븳�떎.
     @Override
     @Transactional(readOnly = true)
-    public List<IssueVo> findIssue(Map<String, Object> resJsonData,
-                                   IssueCondition issueCondition, Pageable pageable) {
+    public List<IssueVo> findIssue(Map<String, Object> resJsonData, IssueCondition issueCondition, Pageable pageable) {
 
         //  寃��깋 議곌굔�쓣 留뚮뱺�떎
         if (!this.makeIssueSearchCondition(issueCondition, Lists.newArrayList("01", "02", "03"), pageable)) {
@@ -723,6 +744,7 @@
             totalCount = this.issueMapper.countByDepartment(issueCondition);
         }
 
+
         //  �뒠�떇 �쟾 - 0.8, 0.9, 0.9, 0.9, 0.9
         /*StopWatch serviceStart = new StopWatch();
         serviceStart.start();*/
@@ -738,6 +760,7 @@
         this.setMapToIssueVo(results, issueVos, issueCondition, user);
 
         this.setCountDownIssues(results, issueVos);
+        this.SetWorkflowDepartment(issueVos); //�썙�겕�뵆濡쒖슦�뿉 �꽕�젙�븳 �떞�떦遺��꽌 媛��졇�삤湲�
 
         resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos);
         resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
diff --git a/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java b/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java
index 0a3055f..2e9f48c 100644
--- a/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java
+++ b/src/main/java/kr/wisestone/owl/vo/CustomFieldVo.java
@@ -16,6 +16,7 @@
     private List<CustomFieldValueVo> customFieldValueVos = Lists.newArrayList();
     private Boolean modifyPermissionCheck = Boolean.TRUE;   //  �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 紐⑤뱺 �궗�엺�뱾�씠 �닔�젙, �궘�젣�븷 �닔 �엳�뼱�꽌 湲곕낯 媛믪씠 True
     private Boolean useCustomFieldValue = Boolean.FALSE;    //  �씠�뒋�뿉�꽌 �궗�슜�옄 �젙�쓽 �븘�뱶 �샃�뀡 媛믪쓣 �궗�슜�븯怨� �엳�쑝硫� true, �븘�땺寃쎌슦�뒗 false
+    private String requiredData;
 
     public CustomFieldVo(){}
 
@@ -75,4 +76,12 @@
     public void setUseCustomFieldValue(Boolean useCustomFieldValue) {
         this.useCustomFieldValue = useCustomFieldValue;
     }
+
+    public String getRequiredData() {
+        return requiredData;
+    }
+
+    public void setRequiredData(String requiredData) {
+        this.requiredData = requiredData;
+    }
 }
diff --git a/src/main/java/kr/wisestone/owl/vo/IssueVo.java b/src/main/java/kr/wisestone/owl/vo/IssueVo.java
index c7eb5ee..7a64cb4 100644
--- a/src/main/java/kr/wisestone/owl/vo/IssueVo.java
+++ b/src/main/java/kr/wisestone/owl/vo/IssueVo.java
@@ -55,6 +55,7 @@
     private IssueVo parentIssueVo;
     private int downIssueCount;
     private int downIssueAllCount;
+    private List<Long> workflowDepartmentIds; // �썙�겕�뵆濡쒖슦�뿉 �꽕�젙�븳 �떞�떦遺��꽌 ID
 
     private List<IssueCompanyVo> issueCompanyVos;
     private List<IssueIspVo> issueIspVos;
@@ -475,4 +476,12 @@
     public void setDownIssueAllCount(int downIssueAllCount) {
         this.downIssueAllCount = downIssueAllCount;
     }
+
+    public List<Long> getWorkflowDepartmentIds() {
+        return workflowDepartmentIds;
+    }
+
+    public void setWorkflowDepartmentIds(List<Long> workflowDepartmentIds) {
+        this.workflowDepartmentIds = workflowDepartmentIds;
+    }
 }
diff --git a/src/main/java/kr/wisestone/owl/web/condition/DepartmentCondition.java b/src/main/java/kr/wisestone/owl/web/condition/DepartmentCondition.java
index 815b458..87bfed4 100644
--- a/src/main/java/kr/wisestone/owl/web/condition/DepartmentCondition.java
+++ b/src/main/java/kr/wisestone/owl/web/condition/DepartmentCondition.java
@@ -18,8 +18,9 @@
     private Long projectId;
 
     private List<Long> excludeIds = Lists.newArrayList();
-
     private List<Long> departmentIds = Lists.newArrayList();
+
+    private Long issueTypeId;
 
     private Integer Page;
     private Integer PageSize;
@@ -105,4 +106,12 @@
     public void setExcludeIds(List<Long> excludeIds) {
         this.excludeIds = excludeIds;
     }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
 }
diff --git a/src/main/java/kr/wisestone/owl/web/controller/DepartmentController.java b/src/main/java/kr/wisestone/owl/web/controller/DepartmentController.java
index bdea59d..945f430 100644
--- a/src/main/java/kr/wisestone/owl/web/controller/DepartmentController.java
+++ b/src/main/java/kr/wisestone/owl/web/controller/DepartmentController.java
@@ -102,6 +102,17 @@
         return this.setSuccessMessage(resJsonData);
     }
 
+    //  �썙�겕�뵆濡쒖슦�뿉 �냽�빐�엳�뒗 遺��꽌 紐⑸줉 議고쉶
+    @RequestMapping(value = "/department/findWorkflowDepartment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> findWorkflowDepartment(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.departmentService.findWorkflowDepartment(resJsonData, DepartmentCondition.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
 
     // 遺��꽌 �뿊�� �떎�슫濡쒕뱶
     @RequestMapping(value = "/department/downloadExcel", method = RequestMethod.POST)
diff --git a/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java b/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java
index 8e3935f..4f9b04b 100644
--- a/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java
+++ b/src/main/java/kr/wisestone/owl/web/form/CustomFieldForm.java
@@ -19,6 +19,7 @@
     private Long workspaceId;
     private List<Long> removeIds = Lists.newArrayList();
     private String useFlag;
+    private String requiredData;
 
     public CustomFieldForm(){}
 
@@ -33,6 +34,11 @@
             form.setOptions(MapUtil.getStrings(params, "options"));
         }
 
+        if (MapUtil.getBoolean(params, "requiredData")) {
+            form.setRequiredData("Y");
+        }else {
+            form.setRequiredData("N");
+        }
         return form;
     }
 
@@ -103,4 +109,12 @@
     public void setUseFlag(String useFlag) {
         this.useFlag = useFlag;
     }
+
+    public String getRequiredData() {
+        return requiredData;
+    }
+
+    public void setRequiredData(String requiredData) {
+        this.requiredData = requiredData;
+    }
 }
diff --git a/src/main/resources/migration/V1_12__Alter_Table.sql b/src/main/resources/migration/V1_12__Alter_Table.sql
new file mode 100644
index 0000000..cbce5f8
--- /dev/null
+++ b/src/main/resources/migration/V1_12__Alter_Table.sql
@@ -0,0 +1,7 @@
+-- �븘�닔 �뜲�씠�꽣 �뿬遺�
+ALTER TABLE `custom_field` ADD COLUMN  `required_data` VARCHAR(1) NOT NULL DEFAULT 'N';
+
+-- �룄硫붿씤 �빆紐� 異붽�
+ALTER TABLE `company_field` ADD COLUMN  `url` VARCHAR(255) NULL;
+ALTER TABLE `isp_field` ADD COLUMN  `url` VARCHAR(255) NULL;
+ALTER TABLE `hosting_field` ADD COLUMN  `url` VARCHAR(255) NULL;
diff --git a/src/main/resources/mybatis/query-template/project-template.xml b/src/main/resources/mybatis/query-template/project-template.xml
index 100ae59..0c14c0a 100644
--- a/src/main/resources/mybatis/query-template/project-template.xml
+++ b/src/main/resources/mybatis/query-template/project-template.xml
@@ -331,7 +331,7 @@
 
 
     <!--    �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬�븯怨� �엳�뒗 吏꾪뻾以묒씤 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎  -->
-    <!--<select id="findByWorkspaceIdAndIncludeProjectAll" resultType="java.util.HashMap"
+    <select id="findByWorkspaceIdAndIncludeProjectAll" resultType="java.util.HashMap"
             parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
         SELECT
         DISTINCT p.id as id,
@@ -367,9 +367,9 @@
                 </foreach>
             </when>
         </choose>
-    </select>-->
+    </select>
 
-    <select id="findByWorkspaceIdAndIncludeProjectAll" resultType="java.util.HashMap"
+    <!--<select id="findByWorkspaceIdAndIncludeProjectAll" resultType="java.util.HashMap"
             parameterType="kr.wisestone.owl.web.condition.ProjectCondition">
         SELECT
         DISTINCT p.id as id,
@@ -406,7 +406,7 @@
                 </foreach>
             </when>
         </choose>
-    </select>
+    </select>-->
 
     <!--    �빐�떦 �뾽臾� 怨듦컙�뿉�꽌 李몄뿬�븯怨� �엳�뒗 吏꾪뻾以묒씤 �봽濡쒖젥�듃瑜� 議고쉶�븳�떎  -->
     <!--<select id="findByWorkspaceIdAndIncludeProject" resultType="java.util.HashMap"
diff --git a/src/main/webapp/i18n/ko/global.json b/src/main/webapp/i18n/ko/global.json
index 70c9614..8061624 100644
--- a/src/main/webapp/i18n/ko/global.json
+++ b/src/main/webapp/i18n/ko/global.json
@@ -207,6 +207,7 @@
         "initializedIssueStatus": "�씠�뒋 �쑀�삎�쓣 蹂�寃쏀븷 寃쎌슦 �궗�슜�옄 �젙�쓽 �븘�뱶, �씠�뒋 �긽�깭媛� 珥덇린�솕 �맗�땲�떎.",
         "clickToSelectDate": "�겢由��븯硫� �궇吏쒕�� �꽑�깮�븷 �닔 �엳�뒿�땲�떎.",
         "notIssueTypeCustomFields": "�씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶媛� �뾾�뒿�땲�떎.",
+        "pleaseEnterIssueTypeCustomFields": "�빐�떦 �궗�슜�옄�젙�쓽�븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.",
         "attachOnlyFiles": "hwp, jpg, png, txt, pptx, ppt, pdf, xlsx, xls, docx, zip, gif, jpeg, doc, tif, bmp, wmv, avi, mp4, mkv, mov �뙆�씪留� 泥⑤��븷 �닔 �엳�뒿�땲�떎.",
         "registerExcelIssues": "�뿊��濡� �씠�뒋 �벑濡앺븯湲�",
         "videoExample": "�룞�쁺�긽 �삁�젣",
diff --git a/src/main/webapp/scripts/app/customField/customFieldAdd.controller.js b/src/main/webapp/scripts/app/customField/customFieldAdd.controller.js
index 41ffb40..65c1549 100644
--- a/src/main/webapp/scripts/app/customField/customFieldAdd.controller.js
+++ b/src/main/webapp/scripts/app/customField/customFieldAdd.controller.js
@@ -30,7 +30,8 @@
                         ipAdress : "",
                         email : "",
                         site : "",
-                        tel : ""
+                        tel : "",
+                        requiredData : ""
                     }
                 };
 
@@ -106,7 +107,6 @@
                             return true;
                         }
                     }
-
                     return false;
                 }
 
@@ -116,7 +116,6 @@
 
                     var content = angular.copy($scope.vm.form);
                     content.name = $rootScope.preventXss(content.name);
-
 
                     if ($scope.vm.form.customFieldType === 'MULTI_SELECT'|| $scope.vm.form.customFieldType === "SINGLE_SELECT") {
                         var convertDefaultValues = "";
diff --git a/src/main/webapp/scripts/app/customField/customFieldModify.controller.js b/src/main/webapp/scripts/app/customField/customFieldModify.controller.js
index 0cf5e1f..a0ff0e9 100644
--- a/src/main/webapp/scripts/app/customField/customFieldModify.controller.js
+++ b/src/main/webapp/scripts/app/customField/customFieldModify.controller.js
@@ -39,7 +39,8 @@
                         ipAdress : "",
                         email : "",
                         site : "",
-                        tel : ""
+                        tel : "",
+                        requiredData: ""
                     },
                     origin : {
                         options : []    //  �샃�뀡 媛� 蹂�寃� �뿬遺� �솗�씤�쓣 �쐞�빐 �꽌踰꾩뿉�꽌 �궡�젮�삱 �븣 �썝蹂� 媛믪쓣 �뵲濡� 愿�由ы븳�떎.
@@ -217,6 +218,7 @@
                                 $scope.vm.form.customFieldType = result.data.data.customFieldType;
                                 $scope.vm.form.defaultValue = result.data.data.defaultValue;
                                 $scope.vm.form.useCustomFieldValue = result.data.data.useCustomFieldValue;
+                                $scope.vm.form.requiredData = result.data.data.requiredData;
 
                                 angular.forEach(result.data.data.customFieldValueVos, function (customFieldValueVo) {
                                     $scope.vm.form.options.push(customFieldValueVo.value);
diff --git a/src/main/webapp/scripts/app/issue/issueAdd.controller.js b/src/main/webapp/scripts/app/issue/issueAdd.controller.js
index 5dbac8d..151503e 100644
--- a/src/main/webapp/scripts/app/issue/issueAdd.controller.js
+++ b/src/main/webapp/scripts/app/issue/issueAdd.controller.js
@@ -58,7 +58,8 @@
                         attachedFiles : [], //  �꽟癒몃끂�듃濡� �뙆�씪 �뾽濡쒕뱶瑜� �븷 寃쎌슦 �꽌踰꾩뿉�꽌 pk瑜� �뵲怨� issue id�� �뿰�룞 �옉�뾽�씠 �븘�슂�븯�떎.
                         startCompleteDateRange : "", //  �떆�옉�씪 ~ 醫낅즺�씪
                         detectingDateRange : "", //  �깘吏��씪
-                        issueCustomFields : []  //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶
+                        issueCustomFields : [],  //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶
+                        requiredDatas : []  // �궗�슜�옄 �젙�쓽 �븘�뱶 �븘�닔 �뜲�씠�꽣 泥댄겕
                     },
                     infiniteAdd : false,    //  �뿰�냽 �깮�꽦
                     projectName : "",   //  �봽濡쒖젥�듃 紐� 寃��깋
@@ -558,6 +559,30 @@
                         startCompleteDateRange : $scope.vm.form.startCompleteDateRange,
                         detectingDateRange : $scope.vm.form.detectingDateRange,
 
+                        requiredDatas : (function () {    //  �궗�슜�옄 �젙�쓽 �븘�뱶 �븘�닔 �뜲�씠�꽣 泥댄겕
+                            var requiredDatas = [];
+
+                            angular.forEach($scope.vm.form.requiredDatas, function (issueCustomField) {
+                                var useValues = [];
+
+                                if (angular.isArray(issueCustomField.useValues)) {
+                                    angular.forEach(issueCustomField.useValues, function (useValue) {
+                                        useValues.push(useValue.value);
+                                    });
+                                }
+                                else {
+                                    useValues.push(issueCustomField.useValues);
+                                }
+
+                                //  useValues 瑜� 諛곗뿴濡� 蹂��솚�븳�떎.
+                                var temp = angular.copy(issueCustomField);
+                                temp.useValues = useValues;
+                                issueCustomFields.push(temp);
+                            });
+
+                            return issueCustomFields;
+                        })(),
+
                         issueCustomFields : (function () {    //  �씠�뒋�뿉�꽌 �궗�슜�릺�뒗 �궗�슜�옄 �젙�쓽 �븘�뱶
                             var issueCustomFields = [];
 
@@ -686,6 +711,7 @@
                 //  �씠�뒋 �쑀�삎�뿉 �뿰寃곕맂 �궗�슜�옄 �젙�쓽 �븘�뱶
                 function getIssueTypeCustomFields() {
                     $scope.vm.form.issueCustomFields = [];
+                    $scope.vm.form.requiredDatas = [];
                     //  �씠�뒋 ���엯 �븘�씠�뵒�굹 �봽濡쒖젥�듃 �븘�씠�뵒媛� �뾾�쑝硫� �넻�떊�쓣 �븯吏� �븡�뒗�떎.
                     if (!$rootScope.isDefined($scope.vm.form.issueTypeId) || $scope.vm.form.projects.length < 1) {
                         return;
@@ -701,6 +727,7 @@
                         if (result.data.message.status === "success") {
 
                             $scope.vm.form.issueCustomFields = [];
+                            $scope.vm.form.requiredDatas = [];
                             angular.forEach(result.data.data, function (issueTypeCustomField) {
                                 switch (issueTypeCustomField.customFieldVo.customFieldType) {
                                     case "INPUT" :
@@ -728,8 +755,10 @@
                                         });
                                         break;
                                 }
-
                                 $scope.vm.form.issueCustomFields.push(issueTypeCustomField);
+                                if(issueTypeCustomField.customFieldVo.requiredData === "Y") {
+                                    $scope.vm.form.requiredDatas.push(issueTypeCustomField.customFieldVo.requiredData);
+                                }
                             });
                         }
                         else {
diff --git a/src/main/webapp/scripts/components/userWorkspace/userWorkspace.service.js b/src/main/webapp/scripts/components/userWorkspace/userWorkspace.service.js
index 11f727f..1f573a5 100644
--- a/src/main/webapp/scripts/components/userWorkspace/userWorkspace.service.js
+++ b/src/main/webapp/scripts/components/userWorkspace/userWorkspace.service.js
@@ -75,6 +75,12 @@
                     return response;
                 });
             },
+            findWorkflowDepartment : function (conditions) {
+                return $http.post("department/findWorkflowDepartment", conditions).then(function (response) {
+                    $log.debug("�썙�겕�뵆濡쒖슦�뿉 �냽�빐�엳�뒗 遺��꽌 紐⑸줉 議고쉶 : ", response);
+                    return response;
+                });
+            },
             
             //�궗�슜�옄 愿�由�
             find : function (conditions) {
diff --git a/src/main/webapp/scripts/components/utils/autoComplete.controller.js b/src/main/webapp/scripts/components/utils/autoComplete.controller.js
index 8be4e38..6402006 100644
--- a/src/main/webapp/scripts/components/utils/autoComplete.controller.js
+++ b/src/main/webapp/scripts/components/utils/autoComplete.controller.js
@@ -312,6 +312,7 @@
 
                 function getIssueDepartmentList(query, excludeList, page, callBack) {
                     var conditions = {
+                        issueTypeId : $scope.vm.form.issueTypeId,
                         departmentName : query,
                         userId : $rootScope.user.id,
                         projectId : (function () {
@@ -339,15 +340,21 @@
                     var deferred = $q.defer();
 
 
-                    //�봽濡쒖젥�듃�뿉 �냽�빐�엳�뒗 �떞�떦遺��꽌 李얘린
-                    UserWorkspace.findProjectDepartment($resourceProvider.getContent(         //  �럹�씠吏� �뾽�뜲�씠�듃媛� �븘�슂�븳 而댄룷�꼳�듃 �씪寃쎌슦, page �뾽�뜲�씠�듃媛� �엳�쓣 寃쎌슦 湲곕낯 10媛쒖뵫 媛��졇�삤怨� �븘�땺寃쎌슦 25媛쒖뵫 媛��졇�삩�떎.
+                    //�썙�겕�뵆濡쒖슦�뿉 �냽�빐�엳�뒗 �떞�떦遺��꽌 李얘린
+                    UserWorkspace.findWorkflowDepartment($resourceProvider.getContent(         //  �럹�씠吏� �뾽�뜲�씠�듃媛� �븘�슂�븳 而댄룷�꼳�듃 �씪寃쎌슦, page �뾽�뜲�씠�듃媛� �엳�쓣 寃쎌슦 湲곕낯 10媛쒖뵫 媛��졇�삤怨� �븘�땺寃쎌슦 25媛쒖뵫 媛��졇�삩�떎.
                         conditions, $resourceProvider.getPageContent($rootScope.isDefined(page) ? page : 0, $rootScope.isDefined(page) ? 10 : 25))).then(function (result) {
                         if (result.data.message.status === "success") {
+                            const departments = result.data.data;
+                            var filterDepartments = departments.filter(function(item, idx){
+                                return departments.findIndex(function(item2, idx2){
+                                    return item.departmentName === item2.departmentName
+                                }) == idx;
+                            });
+
                             if ($rootScope.isDefined(callBack)) {
                                 callBack(result);
                             }
-
-                            deferred.resolve(result.data.data);
+                            deferred.resolve(filterDepartments);
                         }
                         else {
                             SweetAlert.swal($filter("translate")("common.failedToProjectListLookUp"), result.data.message.message, "error"); // "�봽濡쒖젥�듃 紐⑸줉 議고쉶 �떎�뙣"
diff --git a/src/main/webapp/views/customField/customFieldAdd.html b/src/main/webapp/views/customField/customFieldAdd.html
index 0610908..1f28b1d 100644
--- a/src/main/webapp/views/customField/customFieldAdd.html
+++ b/src/main/webapp/views/customField/customFieldAdd.html
@@ -34,6 +34,7 @@
             <div class="form-group">
                 <label for="customFieldAddForm2"><span translate="customField.fieldType">�븘�뱶 �쑀�삎</span> <code
                         class="highlighter-rouge">*</code></label>
+                &nbsp;&nbsp; <label> <input type="checkbox" ng-model="vm.form.requiredData"> �븘�닔 �뜲�씠�꽣 </label>
                 <select id="customFieldAddForm2" class="form-control" ng-model="vm.form.customFieldType"
                         ng-change="fn.changeFieldType()">
                     <option value="INPUT" translate="common.stringField">臾몄옄�뿴 �븘�뱶</option>
diff --git a/src/main/webapp/views/issue/issueAdd.html b/src/main/webapp/views/issue/issueAdd.html
index d34b3c0..f1ac85a 100644
--- a/src/main/webapp/views/issue/issueAdd.html
+++ b/src/main/webapp/views/issue/issueAdd.html
@@ -236,62 +236,99 @@
                                 <!-- 湲곕낯 �엯�젰 -->
                                 <div ng-switch-when="INPUT">
                                     <input type="text" class="form-control input-sm"
+                                           name="input"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.input.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄�젙�쓽�븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="NUMBER">
                                     <input type="text" class="form-control input-sm"
+                                           name="number"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.number.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="DATETIME">
                                     <input type="text" class="form-control input-sm"
+                                           name="dateTime"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.dateTime.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="IP_ADDRESS">
                                     <input type="text" class="form-control input-sm"
+                                           name="ipAddress"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.ipAddress.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="SITE">
                                     <input type="text" class="form-control input-sm"
+                                           name="site"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.site.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="TEL">
                                     <input type="text" class="form-control input-sm"
+                                           name="tel"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.tel.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <!-- �떒�씪 ���젆�듃 -->
                                 <div ng-switch-when="SINGLE_SELECT">
                                     <select class="form-control input-sm issue-select-label"
-                                            ng-required="issueCustomField.fieldOption == '01'"
+                                            name="singleSelect"
+                                            ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'"
                                             ng-model="issueCustomField.useValues">
                                         <option value="" value="" translate="common.choose">�꽑�깮�븯�꽭�슂.</option>
                                         <option ng-repeat="customFieldValueVo in issueCustomField.customFieldVo.customFieldValueVos"
@@ -300,16 +337,28 @@
                                             {{customFieldValueVo.value}}
                                         </option>
                                     </select>
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.singleSelect.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <!-- 硫��떚 ���젆�듃 -->
                                 <div ng-switch-when="MULTI_SELECT">
                                     <ng-dropdown-multiselect class="multiSelect cursor"
+                                                             name="multiSelect"
+                                                             ng-required="issueCustomField.customFieldVo.requiredData == 'Y'"
                                                              data-input-name=""
                                                              modal-form-auto-scroll
                                                              selected-model="issueCustomField.useValues"
                                                              extra-settings="{ 'idProp' : 'value', 'externalIdProp' : 'value', 'displayProp' : 'value', 'stringTypeOption' : 'true', stopRemoveBodyEvent : true }"
                                                              options="issueCustomField.customFieldVo.customFieldValueVos"></ng-dropdown-multiselect>
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.multiSelect.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
                             </div>
                         </div>
diff --git a/src/main/webapp/views/issue/issueModify.html b/src/main/webapp/views/issue/issueModify.html
index f356cc6..e737e65 100644
--- a/src/main/webapp/views/issue/issueModify.html
+++ b/src/main/webapp/views/issue/issueModify.html
@@ -212,18 +212,6 @@
                 </div>
             </div>
 
-            <div class="row">
-                <div class="col-sm-4">
-                    <select id="relationIssueType"
-                            name="relationIssueType"
-                            class="form-control input-sm"
-                            ng-options="option.name for option in vm.relationIssueTypes track by option.id"
-                            ng-model="vm.relationIssueType"
-                            required>
-                    </select>
-                </div>
-            </div>
-
             <hr>
             <div class="row">
                 <div class="col-lg-4 fontcolor_green">
@@ -248,62 +236,99 @@
                                 <!-- 湲곕낯 �엯�젰 -->
                                 <div ng-switch-when="INPUT">
                                     <input type="text" class="form-control input-sm"
+                                           name="input"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.input.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄�젙�쓽�븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="NUMBER">
                                     <input type="text" class="form-control input-sm"
+                                           name="number"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.number.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="DATETIME">
                                     <input type="text" class="form-control input-sm"
+                                           name="dateTime"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.dateTime.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="IP_ADDRESS">
                                     <input type="text" class="form-control input-sm"
+                                           name="ipAddress"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.ipAddress.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="SITE">
                                     <input type="text" class="form-control input-sm"
+                                           name="site"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.site.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <div ng-switch-when="TEL">
                                     <input type="text" class="form-control input-sm"
+                                           name="tel"
                                            ng-model="issueCustomField.useValues"
                                            maxlength="100"
                                            autocomplete="off"
                                            kr-input
-                                           ng-required="issueCustomField.fieldOption == '01'">
+                                           ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'">
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.tel.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <!-- �떒�씪 ���젆�듃 -->
                                 <div ng-switch-when="SINGLE_SELECT">
                                     <select class="form-control input-sm issue-select-label"
-                                            ng-required="issueCustomField.fieldOption == '01'"
+                                            name="singleSelect"
+                                            ng-required="issueCustomField.fieldOption == '01' || issueCustomField.customFieldVo.requiredData == 'Y'"
                                             ng-model="issueCustomField.useValues">
                                         <option value="" value="" translate="common.choose">�꽑�깮�븯�꽭�슂.</option>
                                         <option ng-repeat="customFieldValueVo in issueCustomField.customFieldVo.customFieldValueVos"
@@ -312,16 +337,28 @@
                                             {{customFieldValueVo.value}}
                                         </option>
                                     </select>
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.singleSelect.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
 
                                 <!-- 硫��떚 ���젆�듃 -->
                                 <div ng-switch-when="MULTI_SELECT">
                                     <ng-dropdown-multiselect class="multiSelect cursor"
+                                                             name="multiSelect"
+                                                             ng-required="issueCustomField.customFieldVo.requiredData == 'Y'"
                                                              data-input-name=""
                                                              modal-form-auto-scroll
                                                              selected-model="issueCustomField.useValues"
                                                              extra-settings="{ 'idProp' : 'value', 'externalIdProp' : 'value', 'displayProp' : 'value', 'stringTypeOption' : 'true', stopRemoveBodyEvent : true }"
                                                              options="issueCustomField.customFieldVo.customFieldValueVos"></ng-dropdown-multiselect>
+                                    <small class="help-block form-text text-danger"
+                                           ng-show="issueCustomField.customFieldVo.requiredData == 'Y'"
+                                           ng-if="issueAddForm.multiSelect.$error.required"
+                                           translate="issue.pleaseEnterIssueTypeCustomFields">�빐�떦 �궗�슜�옄 �젙�쓽 �븘�뱶�뒗 �븘�닔 �엯�젰 媛� �엯�땲�떎.
+                                    </small>
                                 </div>
                             </div>
                         </div>

--
Gitblit v1.8.0