From f3d433b269f28e99dd352f5b69f8ef864c09e5b0 Mon Sep 17 00:00:00 2001
From: jhjang <jhjang@maprex.co.kr>
Date: 월, 29 11월 2021 14:33:06 +0900
Subject: [PATCH] - api 기본값 설정 기능 완료 - api 중복 필드 설정 기능 완료

---
 src/main/java/kr/wisestone/owl/repository/CustomFieldApiOverlapRepository.java     |   12 +
 src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java  |   90 ++++++++
 src/main/webapp/scripts/components/api/api.service.js                              |   12 +
 src/main/webapp/views/api/apiSettingOverlap.html                                   |   74 ++++--
 src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java           |   14 +
 src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java        |    2 
 src/main/java/kr/wisestone/owl/web/controller/CustomFieldApiOverlapController.java |   48 ++++
 src/main/webapp/scripts/components/utils/autoComplete.controller.js                |   36 +++
 src/main/webapp/scripts/app/api/apiSetting.controller.js                           |  145 +++++++++++-
 src/main/java/kr/wisestone/owl/vo/CustomFieldApiOverlapVo.java                     |   48 ++++
 src/main/webapp/scripts/main.js                                                    |    1 
 src/main/java/kr/wisestone/owl/web/form/CustomFieldApiOverlapForm.java             |   61 +++++
 src/main/webapp/views/api/apiSettingColumn.html                                    |   68 ++++-
 13 files changed, 550 insertions(+), 61 deletions(-)

diff --git a/src/main/java/kr/wisestone/owl/repository/CustomFieldApiOverlapRepository.java b/src/main/java/kr/wisestone/owl/repository/CustomFieldApiOverlapRepository.java
new file mode 100644
index 0000000..cc4b738
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/repository/CustomFieldApiOverlapRepository.java
@@ -0,0 +1,12 @@
+package kr.wisestone.owl.repository;
+
+import kr.wisestone.owl.domain.CustomFieldApiOverlap;
+import kr.wisestone.owl.domain.IssueApiDefault;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface CustomFieldApiOverlapRepository extends JpaRepository<CustomFieldApiOverlap, Long> {
+    List<CustomFieldApiOverlap> findByUserIdAndIssueTypeId(@Param("UserId") Long userId, @Param("IssueTypeId") Long issueTypeId);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java b/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java
new file mode 100644
index 0000000..0a04dec
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/CustomFieldApiOverlapService.java
@@ -0,0 +1,14 @@
+package kr.wisestone.owl.service;
+
+import kr.wisestone.owl.domain.CustomFieldApiOverlap;
+import kr.wisestone.owl.domain.IssueApiDefault;
+import kr.wisestone.owl.web.form.CustomFieldApiOverlapForm;
+import kr.wisestone.owl.web.form.IssueApiDefaultForm;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Map;
+
+public interface CustomFieldApiOverlapService extends AbstractService<CustomFieldApiOverlap, Long, JpaRepository<CustomFieldApiOverlap, Long>> {
+    void find(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form);
+    boolean modify(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form);
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java
new file mode 100644
index 0000000..4b3be98
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/service/impl/CustomFieldApiOverlapServiceImpl.java
@@ -0,0 +1,90 @@
+package kr.wisestone.owl.service.impl;
+
+import com.google.common.collect.Lists;
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.domain.*;
+import kr.wisestone.owl.repository.CustomFieldApiOverlapRepository;
+import kr.wisestone.owl.repository.IssueApiDefaultRepository;
+import kr.wisestone.owl.service.*;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.vo.*;
+import kr.wisestone.owl.web.form.CustomFieldApiOverlapForm;
+import kr.wisestone.owl.web.form.IssueApiDefaultForm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class CustomFieldApiOverlapServiceImpl extends AbstractServiceImpl<CustomFieldApiOverlap, Long, JpaRepository<CustomFieldApiOverlap, Long>>
+        implements CustomFieldApiOverlapService {
+
+    static final Logger log = LoggerFactory.getLogger(CustomFieldApiOverlapServiceImpl.class);
+
+    @Autowired
+    private CustomFieldApiOverlapRepository customFieldApiOverlapRepository;
+
+    @Autowired
+    private IssueTypeService issueTypeService;
+
+    @Autowired
+    private CustomFieldService customFieldService;
+
+    @Override
+    protected JpaRepository<CustomFieldApiOverlap, Long> getRepository() {
+        return this.customFieldApiOverlapRepository;
+    }
+
+    @Override
+    @Transactional
+    public void find(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form) {
+        UserVo userVo = this.webAppUtil.getLoginUser();
+        List<CustomFieldApiOverlap> customFieldApiOverlaps = this.customFieldApiOverlapRepository.findByUserIdAndIssueTypeId(userVo.getId(), form.getIssueTypeId());
+        if (customFieldApiOverlaps != null && customFieldApiOverlaps.size() > 0) {
+
+            List<CustomFieldApiOverlapVo> customFieldApiOverlapVos =  Lists.newArrayList();
+            for (CustomFieldApiOverlap customFieldApiOverlap : customFieldApiOverlaps) {
+                CustomFieldApiOverlapVo customFieldApiOverlapVo = ConvertUtil.copyProperties(customFieldApiOverlap, CustomFieldApiOverlapVo.class);
+
+                CustomField customField = customFieldApiOverlap.getCustomField();
+                if (customField != null) {
+                    customFieldApiOverlapVo.setCustomFieldVo(ConvertUtil.copyProperties(customField, CustomFieldVo.class));
+                }
+
+                customFieldApiOverlapVos.add(customFieldApiOverlapVo);
+            }
+
+            resJsonData.put(Constants.RES_KEY_CONTENTS, customFieldApiOverlapVos);
+        }
+    }
+
+    @Override
+    @Transactional
+    public boolean modify(Map<String, Object> resJsonData, CustomFieldApiOverlapForm form) {
+        User user = this.webAppUtil.getLoginUserObject();
+        List<CustomFieldApiOverlap> customFieldApiOverlaps = this.customFieldApiOverlapRepository.findByUserIdAndIssueTypeId(user.getId(), form.getIssueTypeId());
+        if (customFieldApiOverlaps != null && customFieldApiOverlaps.size() > 0) {
+            this.customFieldApiOverlapRepository.deleteAll(customFieldApiOverlaps);
+        }
+
+        List<CustomFieldApiOverlap> customFieldApiOverlapList = Lists.newArrayList();
+        for (Long customFieldId : form.getCustomFieldIds()) {
+            CustomFieldApiOverlap customFieldApiOverlap = ConvertUtil.copyProperties(form, CustomFieldApiOverlap.class);
+            CustomField customField = this.customFieldService.getCustomField(customFieldId);
+            customFieldApiOverlap.setCustomField(customField);
+            customFieldApiOverlap.setUser(user);
+            customFieldApiOverlap.setIssueType(this.issueTypeService.getIssueType(form.getIssueTypeId()));
+
+            customFieldApiOverlapList.add(customFieldApiOverlap);
+        }
+
+        this.customFieldApiOverlapRepository.saveAll(customFieldApiOverlapList);
+
+        return  true;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java b/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java
index 4fb83bc..8df1a36 100644
--- a/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java
+++ b/src/main/java/kr/wisestone/owl/service/impl/IssueApiDefaultServiceImpl.java
@@ -57,7 +57,7 @@
             }
 
             Priority priority = issueApiDefault.getPriority();
-            if (project != null) {
+            if (priority != null) {
                 issueApiDefaultVo.setPriorityId(priority.getId());
             }
 
diff --git a/src/main/java/kr/wisestone/owl/vo/CustomFieldApiOverlapVo.java b/src/main/java/kr/wisestone/owl/vo/CustomFieldApiOverlapVo.java
new file mode 100644
index 0000000..ff4eb9c
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/vo/CustomFieldApiOverlapVo.java
@@ -0,0 +1,48 @@
+package kr.wisestone.owl.vo;
+
+import kr.wisestone.owl.domain.CustomField;
+
+import java.util.Date;
+
+public class CustomFieldApiOverlapVo extends BaseVo {
+
+    private Long id;
+    private Long userId;
+    private Long issueTypeId;
+    private CustomFieldVo customFieldVo;
+
+    public CustomFieldApiOverlapVo() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public CustomFieldVo getCustomFieldVo() {
+        return customFieldVo;
+    }
+
+    public void setCustomFieldVo(CustomFieldVo customFieldVo) {
+        this.customFieldVo = customFieldVo;
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/controller/CustomFieldApiOverlapController.java b/src/main/java/kr/wisestone/owl/web/controller/CustomFieldApiOverlapController.java
new file mode 100644
index 0000000..439dd08
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/controller/CustomFieldApiOverlapController.java
@@ -0,0 +1,48 @@
+package kr.wisestone.owl.web.controller;
+
+import kr.wisestone.owl.constant.Constants;
+import kr.wisestone.owl.service.CustomFieldApiOverlapService;
+import kr.wisestone.owl.service.IssueApiDefaultService;
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.web.form.CustomFieldApiOverlapForm;
+import kr.wisestone.owl.web.form.IssueApiDefaultForm;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+public class CustomFieldApiOverlapController extends BaseController {
+
+    @Autowired
+    private CustomFieldApiOverlapService customFieldApiOverlapService;
+
+    //  �씠�뒋 ���엯蹂� 湲곕낯媛� 媛��졇�삤湲�
+    @RequestMapping(value = "customFieldApiOverlap/find", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> find(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.customFieldApiOverlapService.find(resJsonData, CustomFieldApiOverlapForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+
+    //  �씠�뒋 ���엯蹂� 湲곕낯媛� 媛��졇�삤湲�
+    @RequestMapping(value = "customFieldApiOverlap/modify", produces = MediaType.APPLICATION_JSON_VALUE)
+    public
+    @ResponseBody
+    Map<String, Object> modify(@RequestBody Map<String, Map<String, Object>> params) {
+        Map<String, Object> resJsonData = new HashMap<>();
+
+        this.customFieldApiOverlapService.modify(resJsonData, CustomFieldApiOverlapForm.make(params.get(Constants.REQ_KEY_CONTENT)));
+
+        return this.setSuccessMessage(resJsonData);
+    }
+}
diff --git a/src/main/java/kr/wisestone/owl/web/form/CustomFieldApiOverlapForm.java b/src/main/java/kr/wisestone/owl/web/form/CustomFieldApiOverlapForm.java
new file mode 100644
index 0000000..95f3920
--- /dev/null
+++ b/src/main/java/kr/wisestone/owl/web/form/CustomFieldApiOverlapForm.java
@@ -0,0 +1,61 @@
+package kr.wisestone.owl.web.form;
+
+import kr.wisestone.owl.util.ConvertUtil;
+import kr.wisestone.owl.util.MapUtil;
+import org.hibernate.annotations.DynamicInsert;
+
+import java.util.List;
+import java.util.Map;
+
+@DynamicInsert
+public class CustomFieldApiOverlapForm {
+    private Long id;
+    private Long userId;
+    private Long issueTypeId;
+    private List<Long> customFieldIds;
+
+    public CustomFieldApiOverlapForm(){}
+
+    public static CustomFieldApiOverlapForm make(Map<String, Object> params) {
+        CustomFieldApiOverlapForm form = ConvertUtil.convertMapToClass(params, CustomFieldApiOverlapForm.class);
+
+        if (MapUtil.getLongs(params, "customFieldIds") != null) {
+            form.setCustomFieldIds(MapUtil.getLongs(params, "customFieldIds"));
+        }
+
+        return form;
+    }
+
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getIssueTypeId() {
+        return issueTypeId;
+    }
+
+    public void setIssueTypeId(Long issueTypeId) {
+        this.issueTypeId = issueTypeId;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public List<Long> getCustomFieldIds() {
+        return customFieldIds;
+    }
+
+    public void setCustomFieldIds(List<Long> customFieldIds) {
+        this.customFieldIds = customFieldIds;
+    }
+}
diff --git a/src/main/webapp/scripts/app/api/apiSetting.controller.js b/src/main/webapp/scripts/app/api/apiSetting.controller.js
index a96bc8c..31a58c5 100644
--- a/src/main/webapp/scripts/app/api/apiSetting.controller.js
+++ b/src/main/webapp/scripts/app/api/apiSetting.controller.js
@@ -7,8 +7,8 @@
         'app', 'angular'
     ],
     function (app, angular) {
-        app.controller('apiSettingController', ['$scope', '$rootScope', '$log', '$resourceProvider','$uibModal', 'SweetAlert', '$timeout', '$filter', '$injector', '$controller', 'Api', 'Priority', 'Severity',
-            function ($scope, $rootScope, $log, $resourceProvider, $uibModal, SweetAlert, $timeout, $filter, $injector, $controller, Api, Priority, Severity) {
+        app.controller('apiSettingController', ['$scope', '$rootScope', '$log', '$resourceProvider','$uibModal', 'SweetAlert', '$timeout', '$filter', '$injector', '$controller', 'Api', 'Priority', 'Severity', 'IssueType',
+            function ($scope, $rootScope, $log, $resourceProvider, $uibModal, SweetAlert, $timeout, $filter, $injector, $controller, Api, Priority, Severity, IssueType) {
 
                 $scope.fn = {
                     changeTab : changeTab,
@@ -19,7 +19,15 @@
                     initForm : initForm,
                     getPriorities : getPriorities,
                     getSeverities : getSeverities,
-                    start : start
+                    start : start,
+                    getIssueTypes : getIssueTypes,
+                    onChangeIssueType : onChangeIssueType,
+                    reset : reset,
+                    resetOverlap : resetOverlap,
+                    formSubmitOverlap : formSubmitOverlap,
+                    removeCustomField : removeCustomField,
+                    getOverlapList : getOverlapList,
+                    onChangeIssueTypeOverlap : onChangeIssueTypeOverlap
                 };
 
                 $scope.vm = {
@@ -34,8 +42,10 @@
                             description : "",
                             priorityId : "",
                             severityId : "",
+                            customFields : [],
                             // projectName : "",
-                        }
+                        },
+                        customFieldId : "",
                     },
 
                     autoCompletePage : {
@@ -48,7 +58,9 @@
                         //     totalPage : 0
                         // }
                     },
+                    issueTypeId : "",
                     issueTypeName : "",
+                    customFieldName : "",
                     issueTypeData : null            // �씠�뒋 �쑀�삎 媛앹껜
                 };
 
@@ -61,22 +73,75 @@
                     $scope.vm.form.issueApiDefault.severityId = "";
                 }
 
-                // �씠�뒋 �쑀�삎 �겢由��떆
-                $scope.$on("onClickIssueType", function (evnet, args) {
-                    if (args != null && args.length > 0) {
+                function reset() {
+                    $scope.fn.initForm();
+                    $scope.fn.formSubmit();
+                }
 
+                function resetOverlap() {
+                    $scope.vm.form.customFieldIds = [];
+                    $scope.fn.form.formSubmitOverlap();
+                }
+
+                function  getOverlapList() {
+                    let conditions = {
+                        issueTypeId: $scope.vm.issueTypeId
+                    }
+
+                    Api.findApiOverlap($resourceProvider.getContent(
+                        conditions, $resourceProvider.getPageContent(0, 1000))).then(function (result) {
+                        if (result.data.message.status === "success") {
+
+                            let customFieldApiOverlaps = result.data.data;
+                            $scope.vm.form.customFields = [];
+
+                            customFieldApiOverlaps.forEach(function (customFieldApiOverlap) {
+                                $scope.vm.form.customFields.push(customFieldApiOverlap.customFieldVo);
+                            });
+                        }
+                        else {
+                            SweetAlert.swal($filter("translate")("issue.failedToIssueTypeListLookup"), result.data.message.message, "error"); // "�씠�뒋 �쑀�삎 紐⑸줉 議고쉶 �떎�뙣"
+                        }
+                    });
+                }
+
+                function getIssueTypes() {
+                    var conditions = {
+                    };
+
+                    IssueType.find($resourceProvider.getContent(
+                        conditions, $resourceProvider.getPageContent(0, 1000))).then(function (result) {
+                        if (result.data.message.status === "success") {
+                            $scope.vm.issueTypes = result.data.data;
+                            if ($scope.vm.issueTypes != null && $scope.vm.issueTypes.length > 0) {
+                                $scope.vm.issueTypeId = $scope.vm.issueTypes[0].id.toString();
+                                $scope.fn.onChangeIssueType();
+                            }
+                        }
+                        else {
+                            SweetAlert.swal($filter("translate")("issue.failedToIssueTypeListLookup"), result.data.message.message, "error"); // "�씠�뒋 �쑀�삎 紐⑸줉 議고쉶 �떎�뙣"
+                        }
+                    });
+                }
+
+                function onChangeIssueTypeOverlap() {
+                    $scope.fn.getOverlapList();
+                }
+
+                function onChangeIssueType() {
+                    if ($scope.vm.issueTypeId != null) {
                         let conditions = {
-                            issueTypeId: args[0].id
+                            issueTypeId: $scope.vm.issueTypeId
                         }
 
                         Api.findApiDefault($resourceProvider.getContent(
-                            conditions, $resourceProvider.getPageContent(0, 0))).then(function (result) {
+                            conditions, $resourceProvider.getPageContent(0, 1000))).then(function (result) {
                             $scope.fn.initForm();
                             if (result.data.message.status === "success") {
                                 if (angular.isDefined(result.data.data)) {
                                     $scope.vm.form.issueApiDefault = result.data.data;
-                                    $scope.vm.form.issueApiDefault.priorityId = result.data.data.priorityId.toString();
-                                    $scope.vm.form.issueApiDefault.severityId = result.data.data.severityId.toString();
+                                    $scope.vm.form.issueApiDefault.priorityId = result.data.data.priorityId != null ? result.data.data.priorityId.toString() : "";
+                                    $scope.vm.form.issueApiDefault.severityId = result.data.data.severityId != null ? result.data.data.severityId.toString() : "";
 
                                     // if (angular.isDefined(result.data.data.projectVo)) {
                                     //     $scope.vm.projects = [];
@@ -88,7 +153,8 @@
                             }
                         });
                     }
-                });
+
+                }
 
                 //  �뤌 泥댄겕
                 function formCheck(formInvalid) {
@@ -100,11 +166,11 @@
                 }
 
                 function formSubmit() {
-                    if ($scope.vm.issueTypes == null || $scope.vm.issueTypes.length === 0)
+                    if ($scope.vm.issueTypeId == null)
                         return;
 
                     let condition = {
-                        issueTypeId : $scope.vm.issueTypes[0].id,
+                        issueTypeId : $scope.vm.issueTypeId,
                         title : $scope.vm.form.issueApiDefault.title,
                         // projectId : $scope.vm.projects != null && $scope.vm.projects.length > 0 ? $scope.vm.projects[0].id : null,
                         priorityId : $scope.vm.form.issueApiDefault.priorityId,
@@ -116,10 +182,45 @@
                         $resourceProvider.getPageContent(0, 1))).then(function (result) {
 
                         if (result.data.message.status === "success") {
-                            SweetAlert.swal($filter("translate")("api.successToApiIssueDefault"), result.data.message.message, "success"); // "api �넗�겙 �깮�꽦 �꽦怨�"
+                            SweetAlert.swal($filter("translate")("api.successToApiIssueDefault"), result.data.message.message, "success"); // "api 湲곕낯媛� �꽕�젙 �꽦怨�"
                         }
                         else {
-                            SweetAlert.swal($filter("translate")("api.failedToApiIssueDefault"), result.data.message.message, "error"); // "api �넗�겙 �깮�꽦 �떎�뙣"
+                            SweetAlert.swal($filter("translate")("api.failedToApiIssueDefault"), result.data.message.message, "error"); // "api 湲곕낯媛� �꽕�젙 �떎�뙣"
+                        }
+                    });
+                }
+
+                // �궗�슜�옄 �젙�쓽 �븘�뱶 �꽑�깮 �빆紐� �궘�젣
+                function removeCustomField(index) {
+                    $scope.vm.form.customFields.splice(index, 1);
+                }
+
+                // 以묐났 �빆紐� �쟾�넚
+                function formSubmitOverlap() {
+                    if ($scope.vm.issueTypeId === null)
+                        return;
+
+                    let condition = {
+                        issueTypeId : $scope.vm.issueTypeId,
+                        customFieldIds : (function () {
+                            var ids = [];
+
+                            angular.forEach($scope.vm.form.customFields, function (customField) {
+                                ids.push(customField.id);
+                            });
+
+                            return ids;
+                        })(),
+                    }
+
+                    Api.modifyApiOverlap($resourceProvider.getContent(condition,
+                        $resourceProvider.getPageContent(0, 1))).then(function (result) {
+
+                        if (result.data.message.status === "success") {
+                            SweetAlert.swal($filter("translate")("api.successToApiIssueOverlap"), result.data.message.message, "success"); // "api 以묐났媛� �꽕�젙 �꽦怨�"
+                        }
+                        else {
+                            SweetAlert.swal($filter("translate")("api.failedToApiIssueOverlap"), result.data.message.message, "error"); // "api 以묐났媛� �꽕�젙 �떎�뙣"
                         }
                     });
                 }
@@ -160,6 +261,15 @@
                     });
                 }
 
+                $rootScope.$on("changeColumnSettingTab", function (event, args){
+                    $scope.fn.start();
+                });
+
+
+                $rootScope.$on("changeOverlapSettingTab", function (event, args){
+                    $scope.fn.getOverlapList();
+                });
+
 
                 function changeTab(tab) {
                     $scope.vm.tab = tab;
@@ -175,8 +285,9 @@
                     $scope.fn.initForm();
                     $scope.fn.getSeverities();
                     $scope.fn.getPriorities();
+                    $scope.fn.getIssueTypes();
                 }
-
                 $scope.fn.start();
+
             }]);
     });
diff --git a/src/main/webapp/scripts/components/api/api.service.js b/src/main/webapp/scripts/components/api/api.service.js
index 9dd5537..6b797a9 100644
--- a/src/main/webapp/scripts/components/api/api.service.js
+++ b/src/main/webapp/scripts/components/api/api.service.js
@@ -34,6 +34,18 @@
                     $log.debug("�씠�뒋 �쑀�삎 珥덇린媛� �닔�젙 寃곌낵 : ", response);
                     return response;
                 });
+            },
+            findApiOverlap : function (conditions) {
+                return $http.post("customFieldApiOverlap/find", conditions).then(function (response) {
+                    $log.debug("�씠�뒋 �쑀�삎 以묐났媛� 議고쉶 寃곌낵 : ", response);
+                    return response;
+                });
+            },
+            modifyApiOverlap : function (conditions) {
+                return $http.post("customFieldApiOverlap/modify", conditions).then(function (response) {
+                    $log.debug("�씠�뒋 �쑀�삎 以묐났媛� �닔�젙 寃곌낵 : ", response);
+                    return response;
+                });
             }
         }
     }
diff --git a/src/main/webapp/scripts/components/utils/autoComplete.controller.js b/src/main/webapp/scripts/components/utils/autoComplete.controller.js
index cdb0fce..67227b9 100644
--- a/src/main/webapp/scripts/components/utils/autoComplete.controller.js
+++ b/src/main/webapp/scripts/components/utils/autoComplete.controller.js
@@ -23,6 +23,7 @@
                 var IspField = $injector.get("IspField");
                 var HostingField = $injector.get("HostingField");
                 var IssueType = $injector.get("IssueType");
+                var CustomField = $injector.get("CustomField");
                 var $log = $injector.get("$log");
                 var SweetAlert = $injector.get("SweetAlert");
 
@@ -38,6 +39,7 @@
                 $scope.fn.getIssueHostingFieldList = getIssueHostingFieldList;    //  �샇�뒪�똿 紐⑸줉 �젙蹂대�� 議고쉶�븳�떎.
                 $scope.fn.getPartnerList = getPartnerList;          // �뾽泥�/isp/�샇�뒪�똿 紐⑸줉 議고쉶
                 $scope.fn.getIssueTypeList = getIssueTypeList;          // �씠�뒋 �쑀�삎 紐⑸줉 議고쉶
+                $scope.fn.getCustomFieldList = getCustomFieldList;          // �궗�슜�옄 �젙�쓽 �븘�뱶 紐⑸줉 議고쉶
 
                 function getUserList(query, excludeList, page, callBack) {
                     var conditions = {
@@ -403,6 +405,40 @@
                     return deferred.promise;
                 }
 
+                function getCustomFieldList(query, excludeList, page, callBack) {
+                    var conditions = {
+                        name : query,
+                        excludeIds : (function () {
+                            var excludeIds = [];
+
+                            angular.forEach(excludeList, function (exclude) {
+                                excludeIds.push(exclude.id);
+                            });
+
+                            return excludeIds;
+                        })()
+                    };
+
+                    var deferred = $q.defer();
+
+                    CustomField.find($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") {
+
+                            if ($rootScope.isDefined(callBack)) {
+                                callBack(result);
+                            }
+
+                            deferred.resolve(result.data.data);
+                        }
+                        else {
+                            SweetAlert.swal($filter("translate")("issue.failedToIssueTypeListLookup"), result.data.message.message, "error"); // "�씠�뒋 �쑀�삎 紐⑸줉 議고쉶 �떎�뙣"
+                        }
+                    });
+
+                    return deferred.promise;
+                }
+
                 function getIssueTypeList(query, excludeList, page, callBack) {
                     var conditions = {
                         name : query,
diff --git a/src/main/webapp/scripts/main.js b/src/main/webapp/scripts/main.js
index 4e22f59..da87d90 100644
--- a/src/main/webapp/scripts/main.js
+++ b/src/main/webapp/scripts/main.js
@@ -523,6 +523,7 @@
     'authService',
     'userInviteService',
     'projectService',
+    'customFieldService',
     'loginRoute',
     'projectRoute',
     'issueStatusRoute',
diff --git a/src/main/webapp/views/api/apiSettingColumn.html b/src/main/webapp/views/api/apiSettingColumn.html
index 6bcf4c8..b89e0fa 100644
--- a/src/main/webapp/views/api/apiSettingColumn.html
+++ b/src/main/webapp/views/api/apiSettingColumn.html
@@ -1,21 +1,25 @@
 <div class="row">
     <div class="col-md-4">
+
         <div class="form-group mb10">
-            <label class="issue-label"> <span translate="issue.issueType">�씠�뒋 �쑀�삎</span>
-                <code class="highlighter-rouge">*</code></label>
-            <js-autocomplete-single data-input-name="issueType"
-                                    selected-model="vm.issueTypes"
-                                    search="vm.issueTypeName"
-                                    source="fn.getIssueTypeList(vm.issueTypeName, vm.issueTypes, vm.autoCompletePage.issueType.page, fn.getIssueTypeCallback)"
-                                    page="vm.autoCompletePage.issueType.page"
-                                    total-page="vm.autoCompletePage.issueType.totalPage"
-                                    input-disabled="false"
-                                    required
-                                    broad-cast="onClickIssueType"
-                                    translation-texts="{ empty : 'common.emptyIssueType' }"
-                                    extra-settings="{ displayProp : 'name' , idProp : 'id', imageable : false, imagePathProp : '',
-                                            type : '', maxlength : 200, autoResize : false, stopRemoveBodyEvent : true }"></js-autocomplete-single>
+            <label for="issueTypeForm" class="issue-label">
+                <span translate="issue.issueType">�씠�뒋 �쑀�삎</span>
+            </label>
+            <select id="issueTypeForm"
+                    name="issueType"
+                    class="form-control input-sm issue-select-label"
+                    ng-model="vm.issueTypeId"
+                    ng-style="{ 'color' : fn.getOptionColor(vm.issueTypes, vm.issueTypeId) }"
+                    ng-change="fn.onChangeIssueType()"
+                    required>
+                <option ng-repeat="issueType in vm.issueTypes"
+                        ng-style="{ 'color' : issueType.color, 'font-weight': 600 }"
+                        value="{{issueType.id}}"
+                        translate="{{issueType.name}}">
+                </option>
+            </select>
         </div>
+
     </div>
 
 </div>
@@ -23,8 +27,29 @@
 <div class="element-box">
     <form role="form" name="apiSettingColumnForm">
         <div class="form-group mb10">
-            <label for="issueAddForm1" class="issue-label"><span translate="issue.issueTitle">�씪媛� �젣紐�</span> </label>
-            <input id="issueAddForm1"
+            <label for="customFieldSelectForm" class="issue-label">
+                <span translate="issue.issueType">�씠�뒋 �쑀�삎</span>
+            </label>
+            <select id="customFieldSelectForm"
+                    name="issueType"
+                    class="form-control input-sm issue-select-label"
+                    ng-model="vm.issueTypeId"
+                    ng-style="{ 'color' : fn.getOptionColor(vm.issueTypes, vm.issueTypeId) }"
+                    ng-change="fn.onChangeIssueType()"
+                    required>
+                <option ng-repeat="issueType in vm.issueTypes"
+                        ng-style="{ 'color' : issueType.color, 'font-weight': 600 }"
+                        value="{{issueType.id}}"
+                        translate="{{issueType.name}}">
+                </option>
+            </select>
+        </div>
+
+
+
+        <div class="form-group mb10">
+            <label for="issueTitle" class="issue-label"><span translate="issue.issueTitle">�씪媛� �젣紐�</span> </label>
+            <input id="issueTitle"
                    class="form-control input-sm"
                    ng-model="vm.form.issueApiDefault.title"
                    name="title"
@@ -58,10 +83,10 @@
 
                     <div class="col-md-4">
                         <div class="form-group mb10">
-                            <label for="issueAddForm2" class="issue-label">
+                            <label for="issue-priority" class="issue-label">
                                 <span translate="common.priority">�슦�꽑 �닚�쐞</span>
                             </label>
-                            <select id="issueAddForm2"
+                            <select id="issue-priority"
                                     name="priority"
                                     class="form-control input-sm issue-select-label"
                                     ng-model="vm.form.issueApiDefault.priorityId"
@@ -77,9 +102,9 @@
                     </div>
                     <div class="col-md-4">
                         <div class="form-group mb10">
-                            <label for="issueAddForm3" class="issue-label"> <span
+                            <label for="issue-severity" class="issue-label"> <span
                                     translate="common.importance">以묒슂�룄</span> </label>
-                            <select id="issueAddForm3"
+                            <select id="issue-severity"
                                     name="severity"
                                     class="form-control input-sm issue-select-label"
                                     ng-model="vm.form.issueApiDefault.severityId"
@@ -113,6 +138,9 @@
 
     </form>
     <div class="modal-footer buttons-on-right">
+        <button type="button" class="btn btn-md btn-secondary bold"
+                ng-click="fn.reset()"><span translate="common.reset">珥덇린�솕</span>
+        </button>
         <button type="button" class="btn btn-md btn-primary bold"
                 js-short-cut
                 js-short-cut-action="(fn.formCheck(apiSettingColumnForm.$invalid) || $root.spinner) ? null : fn.formSubmit()"
diff --git a/src/main/webapp/views/api/apiSettingOverlap.html b/src/main/webapp/views/api/apiSettingOverlap.html
index 6929f53..99d5598 100644
--- a/src/main/webapp/views/api/apiSettingOverlap.html
+++ b/src/main/webapp/views/api/apiSettingOverlap.html
@@ -2,35 +2,63 @@
 
 <div class="row">
     <div class="col-sm-5">
+
         <div class="element-wrapper">
-            <label for="issueAddForm4" class="issue-label"> <span
-                    translate="issue.issueType">�씠�뒋 ���엯</span>
-            </label>
-            <select id="issueAddForm4"
-                    name="issueType"
-                    class="form-control input-sm issue-select-label"
-                    ng-model="vm.form.issueTypeId"
-                    ng-change="fn.getIssueTypeCustomFields()"
-                    ng-style="{ 'color' : fn.getOptionColor(vm.issueTypes, vm.form.issueTypeId) }"
-                    required>
-                <option ng-style="{ 'color' : '#353535' }" value="">�솃�럹�씠吏� 蹂�議� 媛먯�
-                </option>
-                <option ng-style="{ 'color' : '#353535' }" value="">寃쎌쑀吏� �깘吏�
-                </option>
-                <option ng-repeat="issueType in vm.issueTypes"
-                        ng-style="{ 'color' : issueType.color, 'font-weight': 600 }"
-                        value="{{issueType.id}}">�뿈&nbsp;{{issueType.name}}
-                </option>
-            </select>
+            <div class="form-group mb10">
+                <label for="issueTypeForm" class="issue-label">
+                    <span translate="issue.issueType">�씠�뒋 �쑀�삎</span>
+                </label>
+                <select id="issueTypeForm"
+                        name="issueType"
+                        class="form-control input-sm issue-select-label"
+                        ng-model="vm.issueTypeId"
+                        ng-style="{ 'color' : fn.getOptionColor(vm.issueTypes, vm.issueTypeId) }"
+                        ng-change="fn.onChangeIssueTypeOverlap()"
+                        required>
+                    <option ng-repeat="issueType in vm.issueTypes"
+                            ng-style="{ 'color' : issueType.color, 'font-weight': 600 }"
+                            value="{{issueType.id}}"
+                            translate="{{issueType.name}}">
+                    </option>
+                </select>
+            </div>
         </div>
+
     </div>
 
 
 </div>
 
 <div class="element-box">
-    <p class="btn btn-secondary">
-        <span >寃쎌쑀吏�</span>
-        <span>횞</span>
-    </p>
+    <form role="form" name="apiSettingOverlapForm">
+        <div class="form-group">
+            <label><span translate="common.customField">�궗�슜�옄 �젙�쓽 �븘�뱶</span> </label>
+            <js-autocomplete-multi data-input-name="customField"
+                                   selected-model="vm.form.customFields"
+                                   search="vm.customFieldName"
+                                   source="fn.getCustomFieldList(vm.customFieldName, vm.form.customFields)"
+                                   input-disabled="false"
+                                   translation-texts="{ count : 'common.few', empty : 'common.emptyCustomField' }"
+                                   required
+                                   extra-settings="{ displayProp : 'name' , idProp : 'id', imageable : false, maxlength : 100, autoResize : true }"></js-autocomplete-multi>
+
+            <div class="select3-selection__choicediv mt-10">
+                <span class="select3-selection__choice" ng-repeat="customField in vm.form.customFields">
+                    <span>{{customField.name}}</span>
+                    <span class="select3-selection__choice__remove" ng-click="fn.removeCustomField($index)">횞</span>
+                </span>
+            </div>
+        </div>
+
+        <div class="modal-footer buttons-on-right">
+            <button type="button" class="btn btn-md btn-secondary bold"
+                    ng-click="fn.resetOverlap()"><span translate="common.reset">珥덇린�솕</span>
+            </button>
+            <button type="button" class="btn btn-md btn-primary bold"
+                    js-short-cut
+                    js-short-cut-action="(fn.formCheck(apiSettingOverlapForm.$invalid) || $root.spinner) ? null : fn.formSubmitOverlap()"
+                    ng-click="fn.formSubmitOverlap()"><span translate="common.saved">�깮�꽦</span>
+            </button>
+      </div>
+    </form>
 </div>

--
Gitblit v1.8.0