사용약품 저장기능
This commit is contained in:
11
sql/alter_medical_divi_list.sql
Normal file
11
sql/alter_medical_divi_list.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- =====================================================
|
||||||
|
-- medical_divi_list 테이블 엔진 & 캐릭터셋 변경
|
||||||
|
-- MyISAM → InnoDB (FK 지원을 위해)
|
||||||
|
-- utf8mb3 → utf8mb4 (medical_divi_product와 일치시키기 위해)
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
-- 1) 엔진 변경: MyISAM → InnoDB
|
||||||
|
ALTER TABLE `medical_divi_list` ENGINE = InnoDB;
|
||||||
|
|
||||||
|
-- 2) 캐릭터셋 변경: utf8mb3 → utf8mb4
|
||||||
|
ALTER TABLE `medical_divi_list` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||||
24
sql/create_medical_divi_product.sql
Normal file
24
sql/create_medical_divi_product.sql
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
-- =====================================================
|
||||||
|
-- medical_divi_product : 용량/출력(Depth4) 카테고리별 약품 매핑 테이블
|
||||||
|
-- 참조: MU_TREATMENT_PROCEDURE_PRODUCT
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
CREATE TABLE `medical_divi_product` (
|
||||||
|
`pid` INT(11) NOT NULL AUTO_INCREMENT COMMENT '고유 식별자',
|
||||||
|
`store_pid` INT(11) NOT NULL DEFAULT 1 COMMENT '병원(지점) 식별자',
|
||||||
|
`divi_pid` INT(11) NOT NULL COMMENT '진료유형 카테고리 pid (medical_divi_list.pid, Depth4 기준)',
|
||||||
|
`product_name` VARCHAR(200) NOT NULL COMMENT '약품/제품 명칭',
|
||||||
|
`product_code` VARCHAR(100) DEFAULT NULL COMMENT '약품 코드 (재고관리용)',
|
||||||
|
`volume` DECIMAL(10,2) DEFAULT 0 COMMENT '제품 1개당 용량',
|
||||||
|
`use_volume` DECIMAL(10,2) DEFAULT 0 COMMENT '1회 사용량',
|
||||||
|
`unit_cd` VARCHAR(50) DEFAULT NULL COMMENT '단위 코드 (UNIT_CD 공통코드)',
|
||||||
|
`unit_nm` VARCHAR(100) DEFAULT NULL COMMENT '단위 명칭',
|
||||||
|
`price` INT(11) DEFAULT 0 COMMENT '입고 단가',
|
||||||
|
`order_number` INT(11) DEFAULT 0 COMMENT '정렬 순서',
|
||||||
|
`list_use` CHAR(1) DEFAULT 'y' COMMENT '사용여부 (y/n)',
|
||||||
|
`reg_date` DATETIME DEFAULT CURRENT_TIMESTAMP() COMMENT '등록일시',
|
||||||
|
`up_date` DATETIME DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP() COMMENT '수정일시',
|
||||||
|
PRIMARY KEY (`pid`),
|
||||||
|
KEY `idx_divi_pid` (`divi_pid`),
|
||||||
|
CONSTRAINT `fk_mdp_divi_pid` FOREIGN KEY (`divi_pid`) REFERENCES `medical_divi_list` (`pid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='진료유형 카테고리별 약품/제품 매핑 (재고관리)';
|
||||||
@@ -15,6 +15,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
import com.madeu.constants.Constants;
|
import com.madeu.constants.Constants;
|
||||||
import com.madeu.crm.settings.medicalcategory.dto.MedicalCategoryDTO;
|
import com.madeu.crm.settings.medicalcategory.dto.MedicalCategoryDTO;
|
||||||
import com.madeu.crm.settings.medicalcategory.service.MedicalCategoryService;
|
import com.madeu.crm.settings.medicalcategory.service.MedicalCategoryService;
|
||||||
|
import com.madeu.crm.settings.medicalcategory.service.MedicalDiviProductService;
|
||||||
import com.madeu.init.ManagerDraftAction;
|
import com.madeu.init.ManagerDraftAction;
|
||||||
import com.madeu.util.HttpUtil;
|
import com.madeu.util.HttpUtil;
|
||||||
|
|
||||||
@@ -30,6 +31,9 @@ public class MedicalCategoryController extends ManagerDraftAction {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MedicalCategoryService medicalCategoryService;
|
private MedicalCategoryService medicalCategoryService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MedicalDiviProductService diviProductService;
|
||||||
|
|
||||||
// ==================== 뷰 반환 메서드 ====================
|
// ==================== 뷰 반환 메서드 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,4 +292,127 @@ public class MedicalCategoryController extends ManagerDraftAction {
|
|||||||
log.debug("MedicalCategoryController updateBatchMedicalCategory END");
|
log.debug("MedicalCategoryController updateBatchMedicalCategory END");
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 약품 관련 API ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 리스트 조회
|
||||||
|
*/
|
||||||
|
@PostMapping("/getDiviProductList.do")
|
||||||
|
public HashMap<String, Object> getDiviProductList(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
log.debug("MedicalCategoryController getDiviProductList START");
|
||||||
|
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = diviProductService.getDiviProductList(paramMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("getDiviProductList : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController getDiviProductList END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 등록
|
||||||
|
*/
|
||||||
|
@PostMapping("/putDiviProduct.do")
|
||||||
|
public HashMap<String, Object> putDiviProduct(@RequestBody HashMap<String, Object> paramMap,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
log.debug("MedicalCategoryController putDiviProduct START");
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = diviProductService.insertDiviProduct(paramMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("putDiviProduct : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController putDiviProduct END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 삭제
|
||||||
|
*/
|
||||||
|
@PostMapping("/delDiviProduct.do")
|
||||||
|
public HashMap<String, Object> delDiviProduct(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
log.debug("MedicalCategoryController delDiviProduct START");
|
||||||
|
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = diviProductService.deleteDiviProduct(paramMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("delDiviProduct : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController delDiviProduct END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 수정
|
||||||
|
*/
|
||||||
|
@PostMapping("/modDiviProduct.do")
|
||||||
|
public HashMap<String, Object> modDiviProduct(@RequestBody HashMap<String, Object> paramMap,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
log.debug("MedicalCategoryController modDiviProduct START");
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = diviProductService.updateDiviProduct(paramMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("modDiviProduct : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController modDiviProduct END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 일괄 저장 (등록/수정/삭제)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@PostMapping("/saveDiviProductBatch.do")
|
||||||
|
public HashMap<String, Object> saveDiviProductBatch(@RequestBody List<HashMap<String, Object>> list,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
log.debug("MedicalCategoryController saveDiviProductBatch START");
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = diviProductService.saveDiviProductBatch(list);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("saveDiviProductBatch : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController saveDiviProductBatch END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 제품(약품) 검색 (MU_PRODUCT 기반)
|
||||||
|
*/
|
||||||
|
@PostMapping("/searchProductList.do")
|
||||||
|
public HashMap<String, Object> searchProductList(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
log.debug("MedicalCategoryController searchProductList START");
|
||||||
|
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = diviProductService.searchProductList(paramMap);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("searchProductList : ", e);
|
||||||
|
map.put("msgCode", Constants.FAIL);
|
||||||
|
map.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
log.debug("MedicalCategoryController searchProductList END");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.madeu.crm.settings.medicalcategory.mapper;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface MedicalDiviProductMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 카테고리(divi_pid)에 연결된 약품 리스트 조회
|
||||||
|
*
|
||||||
|
* @param paramMap (diviPid, storePid)
|
||||||
|
* @return 약품 리스트
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> getDiviProductList(HashMap<String, Object> paramMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 등록
|
||||||
|
*
|
||||||
|
* @param paramMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int insertDiviProduct(HashMap<String, Object> paramMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 수정
|
||||||
|
*
|
||||||
|
* @param paramMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int updateDiviProduct(HashMap<String, Object> paramMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 삭제 (논리 삭제)
|
||||||
|
*
|
||||||
|
* @param paramMap (pid)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int deleteDiviProduct(HashMap<String, Object> paramMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 제품(약품) 목록 검색 (MU_PRODUCT 기준)
|
||||||
|
*
|
||||||
|
* @param paramMap (keyword)
|
||||||
|
* @return 제품 리스트
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> searchProductList(HashMap<String, Object> paramMap);
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package com.madeu.crm.settings.medicalcategory.service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.madeu.constants.Constants;
|
||||||
|
import com.madeu.crm.settings.medicalcategory.mapper.MedicalDiviProductMapper;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class MedicalDiviProductService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MedicalDiviProductMapper diviProductMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 리스트 조회
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> getDiviProductList(HashMap<String, Object> paramMap) {
|
||||||
|
HashMap<String, Object> returnMap = new HashMap<>();
|
||||||
|
List<Map<String, Object>> rows = diviProductMapper.getDiviProductList(paramMap);
|
||||||
|
returnMap.put("msgCode", Constants.OK);
|
||||||
|
returnMap.put("rows", rows);
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 등록
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> insertDiviProduct(HashMap<String, Object> paramMap) throws Exception {
|
||||||
|
HashMap<String, Object> returnMap = new HashMap<>();
|
||||||
|
|
||||||
|
int result = diviProductMapper.insertDiviProduct(paramMap);
|
||||||
|
if (result > 0) {
|
||||||
|
returnMap.put("msgCode", Constants.OK);
|
||||||
|
returnMap.put("msgDesc", "약품이 등록되었습니다.");
|
||||||
|
} else {
|
||||||
|
returnMap.put("msgCode", Constants.FAIL);
|
||||||
|
returnMap.put("msgDesc", "등록에 실패했습니다.");
|
||||||
|
}
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 수정
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> updateDiviProduct(HashMap<String, Object> paramMap) throws Exception {
|
||||||
|
HashMap<String, Object> returnMap = new HashMap<>();
|
||||||
|
|
||||||
|
int result = diviProductMapper.updateDiviProduct(paramMap);
|
||||||
|
if (result > 0) {
|
||||||
|
returnMap.put("msgCode", Constants.OK);
|
||||||
|
returnMap.put("msgDesc", "약품 정보가 수정되었습니다.");
|
||||||
|
} else {
|
||||||
|
returnMap.put("msgCode", Constants.FAIL);
|
||||||
|
returnMap.put("msgDesc", "수정에 실패했습니다.");
|
||||||
|
}
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 삭제 (논리 삭제)
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> deleteDiviProduct(HashMap<String, Object> paramMap) throws Exception {
|
||||||
|
HashMap<String, Object> returnMap = new HashMap<>();
|
||||||
|
|
||||||
|
int result = diviProductMapper.deleteDiviProduct(paramMap);
|
||||||
|
if (result > 0) {
|
||||||
|
returnMap.put("msgCode", Constants.OK);
|
||||||
|
returnMap.put("msgDesc", "약품이 삭제되었습니다.");
|
||||||
|
} else {
|
||||||
|
returnMap.put("msgCode", Constants.FAIL);
|
||||||
|
returnMap.put("msgDesc", "삭제에 실패했습니다.");
|
||||||
|
}
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 약품 일괄 저장 (등록/수정/삭제)
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> saveDiviProductBatch(List<HashMap<String, Object>> list) throws Exception {
|
||||||
|
HashMap<String, Object> returnMap = new HashMap<>();
|
||||||
|
int successCnt = 0;
|
||||||
|
|
||||||
|
for (HashMap<String, Object> item : list) {
|
||||||
|
String action = (String) item.getOrDefault("_action", "");
|
||||||
|
if ("insert".equals(action)) {
|
||||||
|
diviProductMapper.insertDiviProduct(item);
|
||||||
|
successCnt++;
|
||||||
|
} else if ("update".equals(action)) {
|
||||||
|
diviProductMapper.updateDiviProduct(item);
|
||||||
|
successCnt++;
|
||||||
|
} else if ("delete".equals(action)) {
|
||||||
|
diviProductMapper.deleteDiviProduct(item);
|
||||||
|
successCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnMap.put("msgCode", Constants.OK);
|
||||||
|
returnMap.put("msgDesc", successCnt + "건이 처리되었습니다.");
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 제품(약품) 목록 검색
|
||||||
|
*/
|
||||||
|
public HashMap<String, Object> searchProductList(HashMap<String, Object> paramMap) {
|
||||||
|
HashMap<String, Object> returnMap = new HashMap<>();
|
||||||
|
List<Map<String, Object>> rows = diviProductMapper.searchProductList(paramMap);
|
||||||
|
returnMap.put("msgCode", Constants.OK);
|
||||||
|
returnMap.put("rows", rows);
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,8 +33,10 @@
|
|||||||
DATE_FORMAT(a.reg_date, '%Y-%m-%d %H:%i:%s') AS reg_date,
|
DATE_FORMAT(a.reg_date, '%Y-%m-%d %H:%i:%s') AS reg_date,
|
||||||
DATE_FORMAT(a.up_date, '%Y-%m-%d %H:%i:%s') AS up_date,
|
DATE_FORMAT(a.up_date, '%Y-%m-%d %H:%i:%s') AS up_date,
|
||||||
a.store_pid,
|
a.store_pid,
|
||||||
a.npay_use
|
a.npay_use,
|
||||||
|
ucl.code_nm AS kind_unit_nm
|
||||||
FROM medical_divi_list a
|
FROM medical_divi_list a
|
||||||
|
LEFT JOIN crm_code_list ucl ON ucl.grp_cd = 'UNIT_CD' AND ucl.code_cd = a.kind_unit AND ucl.store_pid = a.store_pid AND ucl.list_use = 'y'
|
||||||
WHERE a.list_use = 'y'
|
WHERE a.list_use = 'y'
|
||||||
<if test='storePid != null and storePid != ""'>
|
<if test='storePid != null and storePid != ""'>
|
||||||
AND a.store_pid = #{storePid}
|
AND a.store_pid = #{storePid}
|
||||||
@@ -79,9 +81,11 @@
|
|||||||
DATE_FORMAT(a.up_date, '%Y-%m-%d %H:%i:%s') AS up_date,
|
DATE_FORMAT(a.up_date, '%Y-%m-%d %H:%i:%s') AS up_date,
|
||||||
a.store_pid,
|
a.store_pid,
|
||||||
a.npay_use,
|
a.npay_use,
|
||||||
p.divi_name AS parent_name
|
p.divi_name AS parent_name,
|
||||||
|
ucl.code_nm AS kind_unit_nm
|
||||||
FROM medical_divi_list a
|
FROM medical_divi_list a
|
||||||
LEFT JOIN medical_divi_list p ON a.divi_parent = p.pid
|
LEFT JOIN medical_divi_list p ON a.divi_parent = p.pid
|
||||||
|
LEFT JOIN crm_code_list ucl ON ucl.grp_cd = 'UNIT_CD' AND ucl.code_cd = a.kind_unit AND ucl.store_pid = a.store_pid AND ucl.list_use = 'y'
|
||||||
WHERE a.pid = #{pid}
|
WHERE a.pid = #{pid}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
|
||||||
|
<mapper namespace="com.madeu.crm.settings.medicalcategory.mapper.MedicalDiviProductMapper">
|
||||||
|
|
||||||
|
<!-- 1. 특정 카테고리(divi_pid)에 연결된 약품 리스트 조회 -->
|
||||||
|
<select id="getDiviProductList" parameterType="java.util.HashMap" resultType="java.util.HashMap">
|
||||||
|
SELECT a.pid,
|
||||||
|
a.store_pid,
|
||||||
|
a.divi_pid,
|
||||||
|
a.product_name,
|
||||||
|
a.product_code,
|
||||||
|
a.volume,
|
||||||
|
a.use_volume,
|
||||||
|
a.unit_cd,
|
||||||
|
IFNULL(ucl.code_nm, a.unit_nm) AS unit_nm,
|
||||||
|
a.price,
|
||||||
|
a.order_number,
|
||||||
|
a.list_use,
|
||||||
|
IFNULL(mc.COMPANY_NAME, '') AS company_name,
|
||||||
|
IFNULL(mp.VOLUME, 0) AS stock_volume,
|
||||||
|
IFNULL(mp.UNIT_NAME, '') AS stock_unit_nm,
|
||||||
|
IFNULL((SELECT FORMAT(SUM(IFNULL(mss.QUANTITY,0)),1)
|
||||||
|
FROM MU_STOCK_SUM mss
|
||||||
|
WHERE mss.MU_PRODUCT_ID = a.product_code
|
||||||
|
AND mss.USE_YN = 'Y'), '0') AS stock_quantity,
|
||||||
|
DATE_FORMAT(a.reg_date, '%Y-%m-%d %H:%i:%s') AS reg_date,
|
||||||
|
DATE_FORMAT(a.up_date, '%Y-%m-%d %H:%i:%s') AS up_date
|
||||||
|
FROM medical_divi_product a
|
||||||
|
LEFT JOIN crm_code_list ucl ON ucl.grp_cd = 'UNIT_CD' AND ucl.code_cd = a.unit_cd AND ucl.store_pid = a.store_pid AND ucl.list_use = 'y'
|
||||||
|
LEFT JOIN MU_PRODUCT mp ON mp.MU_PRODUCT_ID = a.product_code AND mp.USE_YN = 'Y'
|
||||||
|
LEFT JOIN MU_COMPANY_PRODUCT mcp ON mcp.MU_PRODUCT_ID = a.product_code
|
||||||
|
LEFT JOIN MU_COMPANY mc ON mc.MU_COMPANY_ID = mcp.MU_COMPANY_ID
|
||||||
|
WHERE a.list_use = 'y'
|
||||||
|
AND a.divi_pid = #{diviPid}
|
||||||
|
<if test='storePid != null and storePid != ""'>
|
||||||
|
AND a.store_pid = #{storePid}
|
||||||
|
</if>
|
||||||
|
GROUP BY a.pid
|
||||||
|
ORDER BY a.order_number ASC, a.pid ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 2. 약품 등록 -->
|
||||||
|
<insert id="insertDiviProduct" parameterType="java.util.HashMap" useGeneratedKeys="true" keyProperty="pid">
|
||||||
|
INSERT INTO medical_divi_product (
|
||||||
|
store_pid,
|
||||||
|
divi_pid,
|
||||||
|
product_name,
|
||||||
|
product_code,
|
||||||
|
volume,
|
||||||
|
use_volume,
|
||||||
|
unit_cd,
|
||||||
|
unit_nm,
|
||||||
|
price,
|
||||||
|
order_number,
|
||||||
|
list_use,
|
||||||
|
reg_date,
|
||||||
|
up_date
|
||||||
|
) VALUES (
|
||||||
|
#{storePid},
|
||||||
|
#{diviPid},
|
||||||
|
#{productName},
|
||||||
|
#{productCode, jdbcType=VARCHAR},
|
||||||
|
IFNULL(#{volume}, 0),
|
||||||
|
IFNULL(#{useVolume}, 0),
|
||||||
|
#{unitCd, jdbcType=VARCHAR},
|
||||||
|
#{unitNm, jdbcType=VARCHAR},
|
||||||
|
IFNULL(#{price}, 0),
|
||||||
|
IFNULL(#{orderNumber}, 0),
|
||||||
|
'y',
|
||||||
|
NOW(),
|
||||||
|
NOW()
|
||||||
|
)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 3. 약품 수정 -->
|
||||||
|
<update id="updateDiviProduct" parameterType="java.util.HashMap">
|
||||||
|
UPDATE medical_divi_product
|
||||||
|
<set>
|
||||||
|
<if test='productName != null and productName != ""'>
|
||||||
|
product_name = #{productName},
|
||||||
|
</if>
|
||||||
|
<if test='productCode != null'>
|
||||||
|
product_code = #{productCode},
|
||||||
|
</if>
|
||||||
|
<if test='volume != null'>
|
||||||
|
volume = #{volume},
|
||||||
|
</if>
|
||||||
|
<if test='useVolume != null'>
|
||||||
|
use_volume = #{useVolume},
|
||||||
|
</if>
|
||||||
|
<if test='unitCd != null'>
|
||||||
|
unit_cd = #{unitCd},
|
||||||
|
</if>
|
||||||
|
<if test='unitNm != null'>
|
||||||
|
unit_nm = #{unitNm},
|
||||||
|
</if>
|
||||||
|
<if test='price != null'>
|
||||||
|
price = #{price},
|
||||||
|
</if>
|
||||||
|
<if test='orderNumber != null'>
|
||||||
|
order_number = #{orderNumber},
|
||||||
|
</if>
|
||||||
|
up_date = NOW()
|
||||||
|
</set>
|
||||||
|
WHERE pid = #{pid}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 4. 약품 삭제 (논리 삭제) -->
|
||||||
|
<update id="deleteDiviProduct" parameterType="java.util.HashMap">
|
||||||
|
UPDATE medical_divi_product
|
||||||
|
SET list_use = 'n', up_date = NOW()
|
||||||
|
WHERE pid = #{pid}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 5. 제품(약품) 목록 검색 (MU_PRODUCT 기준) -->
|
||||||
|
<select id="searchProductList" parameterType="java.util.HashMap" resultType="java.util.HashMap">
|
||||||
|
SELECT MP.MU_PRODUCT_ID AS "muProductId"
|
||||||
|
,MP.PRODUCT_NAME AS "productName"
|
||||||
|
,MP.PRODUCT_CODE AS "productCode"
|
||||||
|
,IFNULL(MC.COMPANY_NAME,'') AS "companyName"
|
||||||
|
,IFNULL(MCI.CATEGORY_ITEM_NAME,'') AS "treatmentName"
|
||||||
|
,MP.VOLUME AS "volume"
|
||||||
|
,MP.UNIT_CODE AS "unitCode"
|
||||||
|
,MP.UNIT_NAME AS "unitName"
|
||||||
|
,FORMAT(IFNULL(MSS.PRICE,0),0) AS "price"
|
||||||
|
,CASE
|
||||||
|
WHEN FLOOR(SUM(IFNULL(MSS.QUANTITY,0))) = SUM(IFNULL(MSS.QUANTITY,0))
|
||||||
|
THEN FORMAT(SUM(IFNULL(MSS.QUANTITY,0)),0)
|
||||||
|
ELSE TRIM(TRAILING '0' FROM CAST(FORMAT(SUM(IFNULL(MSS.QUANTITY,0)), 2) AS CHAR))
|
||||||
|
END AS "quantity"
|
||||||
|
FROM MU_PRODUCT AS MP
|
||||||
|
LEFT JOIN MU_COMPANY_PRODUCT AS MCP
|
||||||
|
ON MP.MU_PRODUCT_ID = MCP.MU_PRODUCT_ID
|
||||||
|
LEFT JOIN MU_STOCK_SUM MSS
|
||||||
|
ON MP.MU_PRODUCT_ID = MSS.MU_PRODUCT_ID AND MSS.USE_YN='Y'
|
||||||
|
LEFT JOIN MU_CATEGORY_ITEM AS MCI
|
||||||
|
ON MCI.MU_CATEGORY_ITEM_ID = MP.MU_TREATMENT_ID AND MCI.USE_YN = 'Y'
|
||||||
|
LEFT JOIN MU_COMPANY AS MC
|
||||||
|
ON MC.MU_COMPANY_ID = MCP.MU_COMPANY_ID
|
||||||
|
WHERE MP.USE_YN = 'Y'
|
||||||
|
<if test='keyword != null and keyword != ""'>
|
||||||
|
AND MP.PRODUCT_NAME LIKE CONCAT('%', TRIM(#{keyword}), '%')
|
||||||
|
</if>
|
||||||
|
GROUP BY MP.MU_PRODUCT_ID
|
||||||
|
ORDER BY MCI.CATEGORY_ITEM_NAME ASC, MP.PRODUCT_NAME ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
1
src/main/resources/static/image/web/settings_36px.svg
Normal file
1
src/main/resources/static/image/web/settings_36px.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="40px" viewBox="0 -960 960 960" width="40px" fill="#1f1f1f"><path d="m382-80-18.67-126.67q-17-6.33-34.83-16.66-17.83-10.34-32.17-21.67L178-192.33 79.33-365l106.34-78.67q-1.67-8.33-2-18.16-.34-9.84-.34-18.17 0-8.33.34-18.17.33-9.83 2-18.16L79.33-595 178-767.67 296.33-715q14.34-11.33 32.34-21.67 18-10.33 34.66-16L382-880h196l18.67 126.67q17 6.33 35.16 16.33 18.17 10 31.84 22L782-767.67 880.67-595l-106.34 77.33q1.67 9 2 18.84.34 9.83.34 18.83 0 9-.34 18.5Q776-452 774-443l106.33 78-98.66 172.67-118-52.67q-14.34 11.33-32 22-17.67 10.67-35 16.33L578-80H382Zm55.33-66.67h85l14-110q32.34-8 60.84-24.5T649-321l103.67 44.33 39.66-70.66L701-415q4.33-16 6.67-32.17Q710-463.33 710-480q0-16.67-2-32.83-2-16.17-7-32.17l91.33-67.67-39.66-70.66L649-638.67q-22.67-25-50.83-41.83-28.17-16.83-61.84-22.83l-13.66-110h-85l-14 110q-33 7.33-61.5 23.83T311-639l-103.67-44.33-39.66 70.66L259-545.33Q254.67-529 252.33-513 250-497 250-480q0 16.67 2.33 32.67 2.34 16 6.67 32.33l-91.33 67.67 39.66 70.66L311-321.33q23.33 23.66 51.83 40.16 28.5 16.5 60.84 24.5l13.66 110Zm43.34-200q55.33 0 94.33-39T614-480q0-55.33-39-94.33t-94.33-39q-55.67 0-94.5 39-38.84 39-38.84 94.33t38.84 94.33q38.83 39 94.5 39ZM480-480Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -598,7 +598,8 @@ $(document).ready(function () {
|
|||||||
let height = 300; // Depth 2, 3, 5는 기본 300px
|
let height = 300; // Depth 2, 3, 5는 기본 300px
|
||||||
|
|
||||||
if (diviDept == 4) {
|
if (diviDept == 4) {
|
||||||
height = 500; // Depth 4 (용량/출력)는 입력 필드가 많아 500px
|
width = 780; // 약품 관리 그리드 포함으로 넓힘
|
||||||
|
height = 700; // Depth 4 (용량/출력 + 약품 관리)
|
||||||
}
|
}
|
||||||
|
|
||||||
let left = (screen.width - width) / 2;
|
let left = (screen.width - width) / 2;
|
||||||
@@ -651,7 +652,13 @@ $(document).ready(function () {
|
|||||||
{ title: "깊이", field: "divi_dept", width: "4%", hozAlign: "center", editor: "number", editorParams: { min: 1, max: 5, step: 1 } },
|
{ title: "깊이", field: "divi_dept", width: "4%", hozAlign: "center", editor: "number", editorParams: { min: 1, max: 5, step: 1 } },
|
||||||
{ title: "단가", field: "kind_cost", width: "10%", formatter: costFormatter, hozAlign: "right", editor: "number" },
|
{ title: "단가", field: "kind_cost", width: "10%", formatter: costFormatter, hozAlign: "right", editor: "number" },
|
||||||
{ title: "할인가", field: "dc_cost", width: "10%", formatter: costFormatter, hozAlign: "right", editor: "number" },
|
{ title: "할인가", field: "dc_cost", width: "10%", formatter: costFormatter, hozAlign: "right", editor: "number" },
|
||||||
{ title: "단위", field: "kind_unit", width: "8%", hozAlign: "center", editor: "input" },
|
{
|
||||||
|
title: "단위", field: "kind_unit_nm", width: "8%", hozAlign: "center",
|
||||||
|
formatter: function (cell) {
|
||||||
|
var data = cell.getRow().getData();
|
||||||
|
return data.kind_unit_nm || data.kind_unit || '';
|
||||||
|
}
|
||||||
|
},
|
||||||
{ title: "사용여부", field: "list_use", width: "6%", formatter: ynFormatter, hozAlign: "center", editor: "list", editorParams: { values: { "y": "Y", "n": "N" } } },
|
{ title: "사용여부", field: "list_use", width: "6%", formatter: ynFormatter, hozAlign: "center", editor: "list", editorParams: { values: { "y": "Y", "n": "N" } } },
|
||||||
{ title: "면세여부", field: "tax_free", width: "6%", formatter: ynFormatter, hozAlign: "center", editor: "list", editorParams: { values: { "y": "Y", "n": "N" } } }
|
{ title: "면세여부", field: "tax_free", width: "6%", formatter: ynFormatter, hozAlign: "center", editor: "list", editorParams: { values: { "y": "Y", "n": "N" } } }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 카테고리 Depth 4 팝업 스크립트
|
* 카테고리 Depth 4 팝업 스크립트 (약품 관리 그리드 포함)
|
||||||
*/
|
*/
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
@@ -9,10 +9,16 @@ $(document).ready(function () {
|
|||||||
const diviParent = params.get('diviParent');
|
const diviParent = params.get('diviParent');
|
||||||
const parentName = params.get('parentName');
|
const parentName = params.get('parentName');
|
||||||
|
|
||||||
initForm();
|
var productTable = null; // Tabulator 인스턴스
|
||||||
|
var searchResultTable = null; // 검색결과 Tabulator 인스턴스
|
||||||
|
var unitCodeCache = []; // 단위 코드 캐시
|
||||||
|
|
||||||
|
loadUnitCodes(null, function () {
|
||||||
|
initForm();
|
||||||
|
});
|
||||||
bindEvents();
|
bindEvents();
|
||||||
|
|
||||||
// 금액 포맷팅 함수
|
// ====== 금액 포맷팅 ======
|
||||||
function formatNumber(num) {
|
function formatNumber(num) {
|
||||||
if (!num) return '0';
|
if (!num) return '0';
|
||||||
return num.toString().replace(/[^0-9]/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
return num.toString().replace(/[^0-9]/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||||
@@ -23,12 +29,61 @@ $(document).ready(function () {
|
|||||||
return parseFloat(str.toString().replace(/,/g, '')) || 0;
|
return parseFloat(str.toString().replace(/,/g, '')) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====== 공통코드 단위 로드 ======
|
||||||
|
function loadUnitCodes(selectedVal, callback) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/code/getCodeList.do',
|
||||||
|
type: 'POST',
|
||||||
|
data: { grpCd: 'UNIT_CD', storePid: '1' },
|
||||||
|
success: function (res) {
|
||||||
|
if (res.msgCode === '0' && res.rows) {
|
||||||
|
unitCodeCache = res.rows;
|
||||||
|
|
||||||
|
// 메인 단위 셀렉트 세팅
|
||||||
|
var $sel = $('#kindUnit');
|
||||||
|
$sel.find('option:not(:first)').remove();
|
||||||
|
|
||||||
|
$.each(res.rows, function (i, item) {
|
||||||
|
$sel.append('<option value="' + item.code_cd + '">' + item.code_nm + '</option>');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedVal) {
|
||||||
|
$sel.val(selectedVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof callback === 'function') callback();
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
console.error('단위 코드 목록 로드 실패');
|
||||||
|
if (typeof callback === 'function') callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 단위코드 → 단위명 변환 헬퍼
|
||||||
|
function getUnitName(unitCd) {
|
||||||
|
if (!unitCd) return '';
|
||||||
|
for (var i = 0; i < unitCodeCache.length; i++) {
|
||||||
|
if (unitCodeCache[i].code_cd === unitCd) return unitCodeCache[i].code_nm;
|
||||||
|
}
|
||||||
|
return unitCd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 폼 초기화 ======
|
||||||
function initForm() {
|
function initForm() {
|
||||||
if (mode === 'add') {
|
if (mode === 'add') {
|
||||||
$("#popTitle").text('용량/출력 신규 등록');
|
$("#popTitle").text('용량/출력 신규 등록');
|
||||||
$("#pid").val('');
|
$("#pid").val('');
|
||||||
$("#diviParent").val(diviParent);
|
$("#diviParent").val(diviParent);
|
||||||
$("#btn_delete").hide();
|
$("#btn_delete").hide();
|
||||||
|
$("#productSection").show();
|
||||||
|
$("#btn_add_product").prop('disabled', true).css('opacity', '0.5');
|
||||||
|
initProductGrid();
|
||||||
|
// 저장 전 안내 placeholder
|
||||||
|
if (productTable) {
|
||||||
|
productTable.options.placeholder = "카테고리 저장 후 약품을 추가할 수 있습니다.";
|
||||||
|
productTable.setData([]);
|
||||||
|
}
|
||||||
|
|
||||||
if (diviParent !== '0' && parentName) {
|
if (diviParent !== '0' && parentName) {
|
||||||
$("#parentNameRow").show();
|
$("#parentNameRow").show();
|
||||||
@@ -38,10 +93,13 @@ $(document).ready(function () {
|
|||||||
$("#popTitle").text('용량/출력 정보 수정');
|
$("#popTitle").text('용량/출력 정보 수정');
|
||||||
$("#pid").val(pid);
|
$("#pid").val(pid);
|
||||||
$("#btn_delete").show();
|
$("#btn_delete").show();
|
||||||
|
$("#productSection").show();
|
||||||
loadDetail(pid);
|
loadDetail(pid);
|
||||||
|
initProductGrid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====== 상세 정보 로드 ======
|
||||||
function loadDetail(id) {
|
function loadDetail(id) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/settings/medicalCategory/getMedicalCategory.do',
|
url: '/settings/medicalCategory/getMedicalCategory.do',
|
||||||
@@ -55,13 +113,12 @@ $(document).ready(function () {
|
|||||||
$("#diviSort").val(data.divi_sort);
|
$("#diviSort").val(data.divi_sort);
|
||||||
$("#diviColor").val(data.divi_color || '#000000');
|
$("#diviColor").val(data.divi_color || '#000000');
|
||||||
|
|
||||||
// 단가/제품 정보 (복구)
|
|
||||||
$("#kindCost").val(formatNumber(data.kind_cost));
|
$("#kindCost").val(formatNumber(data.kind_cost));
|
||||||
$("#dcCost").val(formatNumber(data.dc_cost));
|
$("#dcCost").val(formatNumber(data.dc_cost));
|
||||||
$("#kindUnit").val(data.kind_unit || '');
|
|
||||||
$("#kindUnitVol").val(data.kind_unit_vol || 0);
|
$("#kindUnitVol").val(data.kind_unit_vol || 0);
|
||||||
|
|
||||||
// 상위 카테고리 명칭 표시 (Edit 모드)
|
loadUnitCodes(data.kind_unit || '');
|
||||||
|
|
||||||
if (data.parent_name) {
|
if (data.parent_name) {
|
||||||
$("#parentNameRow").show();
|
$("#parentNameRow").show();
|
||||||
$("#parentNameTxt").text(data.parent_name);
|
$("#parentNameTxt").text(data.parent_name);
|
||||||
@@ -81,6 +138,274 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====== 약품 그리드 초기화 ======
|
||||||
|
function initProductGrid() {
|
||||||
|
// 단위코드 셀렉트용 values 생성 (배열: 정렬 보장)
|
||||||
|
var unitValuesMap = {};
|
||||||
|
unitValuesMap[''] = '선택';
|
||||||
|
|
||||||
|
// code_cd 오름차순 정렬
|
||||||
|
var sortedUnits = unitCodeCache.slice().sort(function (a, b) {
|
||||||
|
return (a.code_cd || '').localeCompare(b.code_cd || '');
|
||||||
|
});
|
||||||
|
|
||||||
|
var unitSelectValues = [{ label: '선택', value: '' }];
|
||||||
|
for (var i = 0; i < sortedUnits.length; i++) {
|
||||||
|
unitSelectValues.push({ label: sortedUnits[i].code_nm, value: sortedUnits[i].code_cd });
|
||||||
|
unitValuesMap[sortedUnits[i].code_cd] = sortedUnits[i].code_nm;
|
||||||
|
}
|
||||||
|
|
||||||
|
productTable = new Tabulator("#productGrid", {
|
||||||
|
layout: "fitColumns",
|
||||||
|
placeholder: "등록된 약품이 없습니다.",
|
||||||
|
height: "200px",
|
||||||
|
columnDefaults: {
|
||||||
|
headerHozAlign: "center",
|
||||||
|
headerSort: false,
|
||||||
|
tooltip: true
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{ title: "거래처", field: "company_name", width: 90, hozAlign: "center" },
|
||||||
|
{ title: "약품명", field: "product_name", minWidth: 120 },
|
||||||
|
{
|
||||||
|
title: "재고", field: "stock_quantity", width: 65, hozAlign: "right"
|
||||||
|
},
|
||||||
|
{ title: "재고단위", field: "stock_unit_nm", width: 70, hozAlign: "center" },
|
||||||
|
{
|
||||||
|
title: "사용량", field: "use_volume", width: 70, hozAlign: "right",
|
||||||
|
editor: "number",
|
||||||
|
editorParams: { step: 0.1, min: 0 },
|
||||||
|
formatter: function (cell) {
|
||||||
|
var v = cell.getValue();
|
||||||
|
return (v && Number(v) > 0) ? Number(v).toFixed(1) : '0';
|
||||||
|
},
|
||||||
|
cssClass: "editable-cell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "사용단위", field: "unit_cd", width: 80, hozAlign: "center",
|
||||||
|
editor: "list",
|
||||||
|
editorParams: { values: unitSelectValues, defaultValue: '' },
|
||||||
|
formatter: function (cell) {
|
||||||
|
var val = cell.getValue();
|
||||||
|
if (!val || val === '') return '선택';
|
||||||
|
if (unitValuesMap[val]) return unitValuesMap[val];
|
||||||
|
// CRM 코드에 없으면 unit_nm(단위명) 표시
|
||||||
|
var rowData = cell.getRow().getData();
|
||||||
|
return rowData.unit_nm || '선택';
|
||||||
|
},
|
||||||
|
cssClass: "editable-cell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "삭제", width: 45, hozAlign: "center",
|
||||||
|
formatter: function () {
|
||||||
|
return '<span style="color:#ff4444; cursor:pointer; font-weight:600;">✕</span>';
|
||||||
|
},
|
||||||
|
cellClick: function (e, cell) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var data = cell.getRow().getData();
|
||||||
|
if (!confirm("'" + data.product_name + "' 약품을 삭제하시겠습니까?")) return;
|
||||||
|
deleteProduct(data.pid, cell.getRow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 셀 수정 시 자동 저장
|
||||||
|
productTable.on("cellEdited", function (cell) {
|
||||||
|
var data = cell.getRow().getData();
|
||||||
|
var field = cell.getField();
|
||||||
|
|
||||||
|
var updateObj = { pid: data.pid };
|
||||||
|
|
||||||
|
if (field === 'use_volume') {
|
||||||
|
updateObj.useVolume = parseFloat(data.use_volume || 0);
|
||||||
|
} else if (field === 'unit_cd') {
|
||||||
|
updateObj.unitCd = data.unit_cd || '';
|
||||||
|
updateObj.unitNm = getUnitName(data.unit_cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/medicalCategory/modDiviProduct.do',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(updateObj),
|
||||||
|
success: function (res) {
|
||||||
|
if (res.msgCode !== '0') {
|
||||||
|
alert(res.msgDesc || "수정에 실패했습니다.");
|
||||||
|
loadProductList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert("수정 중 오류가 발생했습니다.");
|
||||||
|
loadProductList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 그리드 데이터 로드
|
||||||
|
loadProductList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 약품 리스트 로드 ======
|
||||||
|
function loadProductList() {
|
||||||
|
if (!pid) return;
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/medicalCategory/getDiviProductList.do',
|
||||||
|
type: 'POST',
|
||||||
|
data: { diviPid: pid, storePid: '1' },
|
||||||
|
success: function (res) {
|
||||||
|
if (res.msgCode === '0' && productTable) {
|
||||||
|
productTable.setData(res.rows || []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
console.error('약품 리스트 로드 실패');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 약품 등록 (검색 팝업에서 선택 시 호출) ======
|
||||||
|
function selectAndAddProduct(productData) {
|
||||||
|
if (!productData) return;
|
||||||
|
|
||||||
|
var submitObj = {
|
||||||
|
storePid: $("#storePid").val(),
|
||||||
|
diviPid: pid,
|
||||||
|
productName: productData.productName || '',
|
||||||
|
productCode: productData.muProductId || '',
|
||||||
|
volume: parseFloat(productData.volume || '0'),
|
||||||
|
useVolume: 0,
|
||||||
|
unitCd: null,
|
||||||
|
unitNm: null,
|
||||||
|
price: parseInt((productData.price || '0').toString().replace(/,/g, ''), 10),
|
||||||
|
orderNumber: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/medicalCategory/putDiviProduct.do',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(submitObj),
|
||||||
|
success: function (res) {
|
||||||
|
if (res.msgCode === '0') {
|
||||||
|
loadProductList();
|
||||||
|
closeProductSearchPopup();
|
||||||
|
} else {
|
||||||
|
alert(res.msgDesc || "등록에 실패했습니다.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert("약품 등록 중 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 약품 삭제 ======
|
||||||
|
function deleteProduct(productPid, row) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/medicalCategory/delDiviProduct.do',
|
||||||
|
type: 'POST',
|
||||||
|
data: { pid: productPid },
|
||||||
|
success: function (res) {
|
||||||
|
if (res.msgCode === '0') {
|
||||||
|
loadProductList();
|
||||||
|
} else {
|
||||||
|
alert(res.msgDesc || "삭제에 실패했습니다.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert("약품 삭제 중 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 제품 검색 팝업 열기 ======
|
||||||
|
function openProductSearchPopup() {
|
||||||
|
$("#searchProductKeyword").val('');
|
||||||
|
$("#productSearchOverlay").addClass('active');
|
||||||
|
initSearchResultGrid();
|
||||||
|
// 팝업 열릴 때 전체 목록 바로 조회
|
||||||
|
searchProducts();
|
||||||
|
setTimeout(function () {
|
||||||
|
$("#searchProductKeyword").focus();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 제품 검색 팝업 닫기 ======
|
||||||
|
function closeProductSearchPopup() {
|
||||||
|
$("#productSearchOverlay").removeClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 검색 결과 그리드 초기화 ======
|
||||||
|
function initSearchResultGrid() {
|
||||||
|
if (searchResultTable) return; // 이미 초기화됨
|
||||||
|
|
||||||
|
searchResultTable = new Tabulator("#searchResultGrid", {
|
||||||
|
layout: "fitColumns",
|
||||||
|
placeholder: "검색 결과가 없습니다.",
|
||||||
|
height: "300px",
|
||||||
|
columnDefaults: {
|
||||||
|
headerHozAlign: "center",
|
||||||
|
headerSort: true,
|
||||||
|
tooltip: true
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{ title: "No", formatter: "rownum", width: 35, hozAlign: "center", headerSort: false },
|
||||||
|
{ title: "재고구분", field: "treatmentName", width: 90, hozAlign: "center" },
|
||||||
|
{ title: "거래처", field: "companyName", width: 100 },
|
||||||
|
{
|
||||||
|
title: "제품명", field: "productName", minWidth: 140,
|
||||||
|
formatter: function (cell) {
|
||||||
|
return '<span style="color:#3985EA; cursor:pointer; font-weight:600;">' + (cell.getValue() || '') + '</span>';
|
||||||
|
},
|
||||||
|
cellClick: function (e, cell) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var data = cell.getRow().getData();
|
||||||
|
if (confirm("'" + data.productName + "' 제품을 약품으로 추가하시겠습니까?")) {
|
||||||
|
selectAndAddProduct(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "용량", field: "volume", width: 60, hozAlign: "right",
|
||||||
|
formatter: function (cell) {
|
||||||
|
var v = cell.getValue();
|
||||||
|
return (v && Number(v) > 0) ? Number(v).toFixed(1) : '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: "단위", field: "unitName", width: 55, hozAlign: "center" },
|
||||||
|
{
|
||||||
|
title: "단가(원)", field: "price", width: 80, hozAlign: "right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "재고", field: "quantity", width: 60, hozAlign: "right"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 제품 검색 실행 ======
|
||||||
|
function searchProducts() {
|
||||||
|
var keyword = $.trim($("#searchProductKeyword").val());
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/settings/medicalCategory/searchProductList.do',
|
||||||
|
type: 'POST',
|
||||||
|
data: { keyword: keyword },
|
||||||
|
success: function (res) {
|
||||||
|
if (res.msgCode === '0' && searchResultTable) {
|
||||||
|
searchResultTable.setData(res.rows || []);
|
||||||
|
} else {
|
||||||
|
if (searchResultTable) searchResultTable.setData([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert("제품 검색 중 오류가 발생했습니다.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 이벤트 바인딩 ======
|
||||||
function bindEvents() {
|
function bindEvents() {
|
||||||
$("#btn_close").on("click", function () {
|
$("#btn_close").on("click", function () {
|
||||||
window.close();
|
window.close();
|
||||||
@@ -94,12 +419,41 @@ $(document).ready(function () {
|
|||||||
deleteCategory();
|
deleteCategory();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 금액 콤마 자동 입력 이벤트
|
// 금액 콤마 자동 입력
|
||||||
$("#kindCost, #dcCost").on("keyup", function () {
|
$("#kindCost, #dcCost").on("keyup", function () {
|
||||||
$(this).val(formatNumber($(this).val()));
|
$(this).val(formatNumber($(this).val()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 약품 추가 (검색 팝업 열기)
|
||||||
|
$("#btn_add_product").on("click", function () {
|
||||||
|
openProductSearchPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 검색 팝업 닫기
|
||||||
|
$("#btn_close_search").on("click", function () {
|
||||||
|
closeProductSearchPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 오버레이 클릭 시 닫기
|
||||||
|
$("#productSearchOverlay").on("click", function (e) {
|
||||||
|
if (e.target === this) closeProductSearchPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 검색 버튼
|
||||||
|
$("#btn_search_product").on("click", function () {
|
||||||
|
searchProducts();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 검색 Enter 키
|
||||||
|
$("#searchProductKeyword").on("keydown", function (e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
searchProducts();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====== 카테고리 저장 ======
|
||||||
function saveCategory() {
|
function saveCategory() {
|
||||||
const dName = $("#diviName").val().trim();
|
const dName = $("#diviName").val().trim();
|
||||||
if (!dName) {
|
if (!dName) {
|
||||||
@@ -112,12 +466,10 @@ $(document).ready(function () {
|
|||||||
pid: $("#pid").val() || null,
|
pid: $("#pid").val() || null,
|
||||||
storePid: $("#storePid").val(),
|
storePid: $("#storePid").val(),
|
||||||
diviName: dName,
|
diviName: dName,
|
||||||
diviDept: $("#diviDept").val(), // hidden = 4
|
diviDept: $("#diviDept").val(),
|
||||||
diviParent: $("#diviParent").val(),
|
diviParent: $("#diviParent").val(),
|
||||||
diviSort: parseInt($("#diviSort").val() || "0", 10),
|
diviSort: parseInt($("#diviSort").val() || "0", 10),
|
||||||
diviColor: $("#diviColor").val(),
|
diviColor: $("#diviColor").val(),
|
||||||
|
|
||||||
// 단가/제품 정보 (복구 및 포맷팅 처리)
|
|
||||||
kindCost: unformatNumber($("#kindCost").val()),
|
kindCost: unformatNumber($("#kindCost").val()),
|
||||||
dcCost: unformatNumber($("#dcCost").val()),
|
dcCost: unformatNumber($("#dcCost").val()),
|
||||||
kindUnit: $("#kindUnit").val(),
|
kindUnit: $("#kindUnit").val(),
|
||||||
@@ -137,7 +489,16 @@ $(document).ready(function () {
|
|||||||
if (window.opener && typeof window.opener.loadData === 'function') {
|
if (window.opener && typeof window.opener.loadData === 'function') {
|
||||||
window.opener.loadData(true);
|
window.opener.loadData(true);
|
||||||
}
|
}
|
||||||
window.close();
|
// 신규 등록 후 수정 모드로 전환하여 약품 관리 가능하게
|
||||||
|
if (mode === 'add' && res.pid) {
|
||||||
|
window.location.href = window.location.pathname +
|
||||||
|
'?mode=edit&pid=' + res.pid +
|
||||||
|
'&diviDept=' + diviDept +
|
||||||
|
'&diviParent=' + diviParent +
|
||||||
|
'&parentName=' + encodeURIComponent(parentName || '');
|
||||||
|
} else {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
@@ -146,6 +507,7 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====== 카테고리 삭제 ======
|
||||||
function deleteCategory() {
|
function deleteCategory() {
|
||||||
const pidVal = $("#pid").val();
|
const pidVal = $("#pid").val();
|
||||||
if (!pidVal) return;
|
if (!pidVal) return;
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>진료유형 정보</title>
|
<title>진료유형 정보</title>
|
||||||
<link rel="stylesheet" th:href="@{/css/common.css}">
|
<link rel="stylesheet" th:href="@{/css/common.css}">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/tabulator-tables@5.6.1/dist/css/tabulator.min.css">
|
||||||
<script th:src="@{/js/web/jquery.min.js}"></script>
|
<script th:src="@{/js/web/jquery.min.js}"></script>
|
||||||
|
<script src="https://unpkg.com/tabulator-tables@5.6.1/dist/js/tabulator.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.pop_wrap {
|
.pop_wrap {
|
||||||
max-width: 600px;
|
max-width: 750px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +139,148 @@
|
|||||||
padding: 8px 0 4px 0;
|
padding: 8px 0 4px 0;
|
||||||
border-bottom: 1px solid #e9ecf0;
|
border-bottom: 1px solid #e9ecf0;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title .btn_sm {
|
||||||
|
padding: 3px 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
background: #3985EA;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title .btn_sm:hover {
|
||||||
|
background: #2c6fd1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 약품 그리드 영역 */
|
||||||
|
#productGridWrap {
|
||||||
|
margin-top: 8px;
|
||||||
|
border: 1px solid #e9ecf0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#productGrid {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 편집 가능한 셀 스타일 */
|
||||||
|
.editable-cell {
|
||||||
|
background-color: #f0f7ff !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editable-cell:hover {
|
||||||
|
background-color: #e0efff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-product-msg {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 제품 검색 레이어팝업 ===== */
|
||||||
|
.search-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
z-index: 9999;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-overlay.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-popup {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
|
||||||
|
width: 680px;
|
||||||
|
max-height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 14px 18px;
|
||||||
|
background: #3985EA;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-popup-header h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-popup-header .popup-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-popup-body {
|
||||||
|
padding: 14px 18px;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-row input {
|
||||||
|
flex: 1;
|
||||||
|
height: 34px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-row button {
|
||||||
|
padding: 0 18px;
|
||||||
|
height: 34px;
|
||||||
|
background: #3985EA;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-row button:hover {
|
||||||
|
background: #2c6fd1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchResultGrid {
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -176,7 +320,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- 용량/출력 정보 (Depth 4 전용) 복구 -->
|
<!-- 용량/출력 정보 (Depth 4 전용) -->
|
||||||
<div class="section-title">용량/출력 정보</div>
|
<div class="section-title">용량/출력 정보</div>
|
||||||
<table class="board_write">
|
<table class="board_write">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
@@ -202,19 +346,43 @@
|
|||||||
<input type="number" id="kindUnitVol" class="w120" value="0" step="0.1" placeholder="용량">
|
<input type="number" id="kindUnitVol" class="w120" value="0" step="0.1" placeholder="용량">
|
||||||
<select id="kindUnit" style="width: 100px; margin-left: 5px;">
|
<select id="kindUnit" style="width: 100px; margin-left: 5px;">
|
||||||
<option value="">선택</option>
|
<option value="">선택</option>
|
||||||
<!-- 추후 공통코드 연동을 통해 데이터 바인딩 -->
|
|
||||||
<option value="CC">CC</option>
|
|
||||||
<option value="ML">ML</option>
|
|
||||||
<option value="샷">샷</option>
|
|
||||||
<option value="회">회</option>
|
|
||||||
<option value="V">V</option>
|
|
||||||
<option value="줄">줄</option>
|
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- 약품 관리 (재고관리용) - 수정 모드에서만 표시 -->
|
||||||
|
<div id="productSection" style="display:none;">
|
||||||
|
<div class="section-title">
|
||||||
|
<span>사용 약품 관리 (재고관리)</span>
|
||||||
|
<button type="button" class="btn_sm" id="btn_add_product">+ 약품 추가 (검색)</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 약품 그리드 -->
|
||||||
|
<div id="productGridWrap">
|
||||||
|
<div id="productGrid"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ===== 제품 검색 레이어 팝업 ===== -->
|
||||||
|
<div id="productSearchOverlay" class="search-overlay">
|
||||||
|
<div class="search-popup">
|
||||||
|
<div class="search-popup-header">
|
||||||
|
<h4>약품(제품) 검색</h4>
|
||||||
|
<button type="button" class="popup-close" id="btn_close_search">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="search-popup-body">
|
||||||
|
<div class="search-input-row">
|
||||||
|
<input type="text" id="searchProductKeyword" placeholder="제품명을 입력하세요">
|
||||||
|
<button type="button" id="btn_search_product">검색</button>
|
||||||
|
</div>
|
||||||
|
<div id="searchResultGrid"></div>
|
||||||
|
<p style="margin-top:8px; font-size:11px; color:#999;">* 제품명을 클릭하면 약품이 자동 추가됩니다.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pop_btn_area">
|
<div class="pop_btn_area">
|
||||||
<button type="button" class="btn_blue" id="btn_save">저장</button>
|
<button type="button" class="btn_blue" id="btn_save">저장</button>
|
||||||
<button type="button" class="btn_red" id="btn_delete" style="display:none;">삭제</button>
|
<button type="button" class="btn_red" id="btn_delete" style="display:none;">삭제</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user