2025-10-01 10:55:09 +09:00
|
|
|
import { query, queryOne, transaction } from "../database/db";
|
2025-09-02 11:30:19 +09:00
|
|
|
import { logger } from "../utils/logger";
|
|
|
|
|
|
|
|
|
|
export interface CodeCategory {
|
|
|
|
|
category_code: string;
|
|
|
|
|
category_name: string;
|
|
|
|
|
category_name_eng?: string | null;
|
|
|
|
|
description?: string | null;
|
|
|
|
|
sort_order: number;
|
|
|
|
|
is_active: string;
|
2025-10-28 10:07:07 +09:00
|
|
|
company_code: string; // 추가
|
2025-09-02 11:30:19 +09:00
|
|
|
created_date?: Date | null;
|
|
|
|
|
created_by?: string | null;
|
|
|
|
|
updated_date?: Date | null;
|
|
|
|
|
updated_by?: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CodeInfo {
|
|
|
|
|
code_category: string;
|
|
|
|
|
code_value: string;
|
|
|
|
|
code_name: string;
|
|
|
|
|
code_name_eng?: string | null;
|
|
|
|
|
description?: string | null;
|
|
|
|
|
sort_order: number;
|
|
|
|
|
is_active: string;
|
2025-10-28 10:07:07 +09:00
|
|
|
company_code: string; // 추가
|
2025-09-02 11:30:19 +09:00
|
|
|
created_date?: Date | null;
|
|
|
|
|
created_by?: string | null;
|
|
|
|
|
updated_date?: Date | null;
|
|
|
|
|
updated_by?: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface GetCategoriesParams {
|
|
|
|
|
search?: string;
|
|
|
|
|
isActive?: boolean;
|
|
|
|
|
page?: number;
|
|
|
|
|
size?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface GetCodesParams {
|
|
|
|
|
search?: string;
|
|
|
|
|
isActive?: boolean;
|
2025-09-03 18:23:23 +09:00
|
|
|
page?: number;
|
|
|
|
|
size?: number;
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateCategoryData {
|
|
|
|
|
categoryCode: string;
|
|
|
|
|
categoryName: string;
|
|
|
|
|
categoryNameEng?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
sortOrder?: number;
|
2025-09-02 18:25:44 +09:00
|
|
|
isActive?: string;
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateCodeData {
|
|
|
|
|
codeValue: string;
|
|
|
|
|
codeName: string;
|
|
|
|
|
codeNameEng?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
sortOrder?: number;
|
2025-09-02 18:25:44 +09:00
|
|
|
isActive?: string;
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class CommonCodeService {
|
|
|
|
|
/**
|
|
|
|
|
* 카테고리 목록 조회
|
|
|
|
|
*/
|
2025-11-11 15:25:07 +09:00
|
|
|
async getCategories(params: GetCategoriesParams, userCompanyCode?: string, menuObjid?: number) {
|
2025-09-02 11:30:19 +09:00
|
|
|
try {
|
|
|
|
|
const { search, isActive, page = 1, size = 20 } = params;
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const whereConditions: string[] = [];
|
|
|
|
|
const values: any[] = [];
|
|
|
|
|
let paramIndex = 1;
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-11-11 15:25:07 +09:00
|
|
|
// 메뉴별 필터링 (형제 메뉴 포함)
|
|
|
|
|
if (menuObjid) {
|
|
|
|
|
const { getSiblingMenuObjids } = await import('./menuService');
|
|
|
|
|
const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
|
|
|
|
|
whereConditions.push(`menu_objid = ANY($${paramIndex})`);
|
|
|
|
|
values.push(siblingMenuObjids);
|
|
|
|
|
paramIndex++;
|
|
|
|
|
logger.info(`메뉴별 코드 카테고리 필터링: ${menuObjid}, 형제 메뉴: ${siblingMenuObjids.join(', ')}`);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
// 회사별 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (userCompanyCode && userCompanyCode !== "*") {
|
|
|
|
|
whereConditions.push(`company_code = $${paramIndex}`);
|
|
|
|
|
values.push(userCompanyCode);
|
|
|
|
|
paramIndex++;
|
|
|
|
|
logger.info(`회사별 코드 카테고리 필터링: ${userCompanyCode}`);
|
|
|
|
|
} else if (userCompanyCode === "*") {
|
|
|
|
|
// 최고 관리자는 모든 데이터 조회 가능
|
|
|
|
|
logger.info(`최고 관리자: 모든 코드 카테고리 조회`);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
if (search) {
|
2025-10-01 10:55:09 +09:00
|
|
|
whereConditions.push(
|
|
|
|
|
`(category_name ILIKE $${paramIndex} OR category_code ILIKE $${paramIndex})`
|
|
|
|
|
);
|
|
|
|
|
values.push(`%${search}%`);
|
|
|
|
|
paramIndex++;
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isActive !== undefined) {
|
2025-10-01 10:55:09 +09:00
|
|
|
whereConditions.push(`is_active = $${paramIndex++}`);
|
|
|
|
|
values.push(isActive ? "Y" : "N");
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const whereClause =
|
|
|
|
|
whereConditions.length > 0
|
|
|
|
|
? `WHERE ${whereConditions.join(" AND ")}`
|
|
|
|
|
: "";
|
|
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
const offset = (page - 1) * size;
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// 카테고리 조회
|
|
|
|
|
const categories = await query<CodeCategory>(
|
|
|
|
|
`SELECT * FROM code_category
|
|
|
|
|
${whereClause}
|
|
|
|
|
ORDER BY sort_order ASC, category_code ASC
|
|
|
|
|
LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
|
|
|
|
|
[...values, size, offset]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 전체 개수 조회
|
|
|
|
|
const countResult = await queryOne<{ count: string }>(
|
|
|
|
|
`SELECT COUNT(*) as count FROM code_category ${whereClause}`,
|
|
|
|
|
values
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const total = parseInt(countResult?.count || "0");
|
2025-09-02 11:30:19 +09:00
|
|
|
|
|
|
|
|
logger.info(
|
2025-10-28 10:07:07 +09:00
|
|
|
`카테고리 조회 완료: ${categories.length}개, 전체: ${total}개 (회사: ${userCompanyCode || "전체"})`
|
2025-09-02 11:30:19 +09:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
data: categories,
|
|
|
|
|
total,
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error("카테고리 조회 중 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 카테고리별 코드 목록 조회
|
|
|
|
|
*/
|
2025-10-28 10:07:07 +09:00
|
|
|
async getCodes(
|
|
|
|
|
categoryCode: string,
|
|
|
|
|
params: GetCodesParams,
|
2025-11-11 15:25:07 +09:00
|
|
|
userCompanyCode?: string,
|
|
|
|
|
menuObjid?: number
|
2025-10-28 10:07:07 +09:00
|
|
|
) {
|
2025-09-02 11:30:19 +09:00
|
|
|
try {
|
2025-09-03 18:23:23 +09:00
|
|
|
const { search, isActive, page = 1, size = 20 } = params;
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-11-11 16:28:17 +09:00
|
|
|
logger.info(`🔍 [getCodes] 코드 조회 시작:`, {
|
|
|
|
|
categoryCode,
|
|
|
|
|
menuObjid,
|
|
|
|
|
hasMenuObjid: !!menuObjid,
|
|
|
|
|
userCompanyCode,
|
|
|
|
|
search,
|
|
|
|
|
isActive,
|
|
|
|
|
page,
|
|
|
|
|
size,
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const whereConditions: string[] = ["code_category = $1"];
|
|
|
|
|
const values: any[] = [categoryCode];
|
|
|
|
|
let paramIndex = 2;
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-11-11 15:25:07 +09:00
|
|
|
// 메뉴별 필터링 (형제 메뉴 포함)
|
|
|
|
|
if (menuObjid) {
|
|
|
|
|
const { getSiblingMenuObjids } = await import('./menuService');
|
|
|
|
|
const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
|
|
|
|
|
whereConditions.push(`menu_objid = ANY($${paramIndex})`);
|
|
|
|
|
values.push(siblingMenuObjids);
|
|
|
|
|
paramIndex++;
|
2025-11-11 16:28:17 +09:00
|
|
|
logger.info(`📋 [getCodes] 메뉴별 코드 필터링:`, {
|
|
|
|
|
menuObjid,
|
|
|
|
|
siblingMenuObjids,
|
|
|
|
|
siblingCount: siblingMenuObjids.length,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
logger.warn(`⚠️ [getCodes] menuObjid 없음 - 전역 코드 조회`);
|
2025-11-11 15:25:07 +09:00
|
|
|
}
|
|
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
// 회사별 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (userCompanyCode && userCompanyCode !== "*") {
|
|
|
|
|
whereConditions.push(`company_code = $${paramIndex}`);
|
|
|
|
|
values.push(userCompanyCode);
|
|
|
|
|
paramIndex++;
|
|
|
|
|
logger.info(`회사별 코드 필터링: ${userCompanyCode}`);
|
|
|
|
|
} else if (userCompanyCode === "*") {
|
|
|
|
|
logger.info(`최고 관리자: 모든 코드 조회`);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 11:30:19 +09:00
|
|
|
if (search) {
|
2025-10-01 10:55:09 +09:00
|
|
|
whereConditions.push(
|
|
|
|
|
`(code_name ILIKE $${paramIndex} OR code_value ILIKE $${paramIndex})`
|
|
|
|
|
);
|
|
|
|
|
values.push(`%${search}%`);
|
|
|
|
|
paramIndex++;
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isActive !== undefined) {
|
2025-10-01 10:55:09 +09:00
|
|
|
whereConditions.push(`is_active = $${paramIndex++}`);
|
|
|
|
|
values.push(isActive ? "Y" : "N");
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const whereClause = `WHERE ${whereConditions.join(" AND ")}`;
|
|
|
|
|
|
2025-09-03 18:23:23 +09:00
|
|
|
const offset = (page - 1) * size;
|
|
|
|
|
|
2025-11-11 16:28:17 +09:00
|
|
|
logger.info(`📝 [getCodes] 실행할 쿼리:`, {
|
|
|
|
|
whereClause,
|
|
|
|
|
values,
|
|
|
|
|
whereConditions,
|
|
|
|
|
paramIndex,
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// 코드 조회
|
|
|
|
|
const codes = await query<CodeInfo>(
|
|
|
|
|
`SELECT * FROM code_info
|
|
|
|
|
${whereClause}
|
|
|
|
|
ORDER BY sort_order ASC, code_value ASC
|
|
|
|
|
LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
|
|
|
|
|
[...values, size, offset]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 전체 개수 조회
|
|
|
|
|
const countResult = await queryOne<{ count: string }>(
|
|
|
|
|
`SELECT COUNT(*) as count FROM code_info ${whereClause}`,
|
|
|
|
|
values
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const total = parseInt(countResult?.count || "0");
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-09-03 18:23:23 +09:00
|
|
|
logger.info(
|
2025-11-11 16:28:17 +09:00
|
|
|
`✅ [getCodes] 코드 조회 완료: ${categoryCode} - ${codes.length}개, 전체: ${total}개 (회사: ${userCompanyCode || "전체"}, menuObjid: ${menuObjid || "없음"})`
|
2025-09-03 18:23:23 +09:00
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-11-11 16:28:17 +09:00
|
|
|
logger.info(`📊 [getCodes] 조회된 코드 상세:`, {
|
|
|
|
|
categoryCode,
|
|
|
|
|
menuObjid,
|
|
|
|
|
codes: codes.map((c) => ({
|
|
|
|
|
code_value: c.code_value,
|
|
|
|
|
code_name: c.code_name,
|
|
|
|
|
menu_objid: c.menu_objid,
|
|
|
|
|
company_code: c.company_code,
|
|
|
|
|
})),
|
|
|
|
|
});
|
|
|
|
|
|
2025-09-03 18:23:23 +09:00
|
|
|
return { data: codes, total };
|
2025-09-02 11:30:19 +09:00
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`코드 조회 중 오류 (${categoryCode}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 카테고리 생성
|
|
|
|
|
*/
|
2025-10-28 10:07:07 +09:00
|
|
|
async createCategory(
|
|
|
|
|
data: CreateCategoryData,
|
|
|
|
|
createdBy: string,
|
2025-11-11 15:25:07 +09:00
|
|
|
companyCode: string,
|
|
|
|
|
menuObjid: number
|
2025-10-28 10:07:07 +09:00
|
|
|
) {
|
2025-09-02 11:30:19 +09:00
|
|
|
try {
|
2025-10-01 10:55:09 +09:00
|
|
|
const category = await queryOne<CodeCategory>(
|
|
|
|
|
`INSERT INTO code_category
|
|
|
|
|
(category_code, category_name, category_name_eng, description, sort_order,
|
2025-11-11 15:25:07 +09:00
|
|
|
is_active, menu_objid, company_code, created_by, updated_by, created_date, updated_date)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5, 'Y', $6, $7, $8, $9, NOW(), NOW())
|
2025-10-01 10:55:09 +09:00
|
|
|
RETURNING *`,
|
|
|
|
|
[
|
|
|
|
|
data.categoryCode,
|
|
|
|
|
data.categoryName,
|
|
|
|
|
data.categoryNameEng || null,
|
|
|
|
|
data.description || null,
|
|
|
|
|
data.sortOrder || 0,
|
2025-11-11 15:25:07 +09:00
|
|
|
menuObjid,
|
2025-10-28 10:07:07 +09:00
|
|
|
companyCode,
|
2025-10-01 10:55:09 +09:00
|
|
|
createdBy,
|
|
|
|
|
createdBy,
|
|
|
|
|
]
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(
|
2025-11-11 15:25:07 +09:00
|
|
|
`카테고리 생성 완료: ${data.categoryCode} (메뉴: ${menuObjid}, 회사: ${companyCode})`
|
2025-10-28 10:07:07 +09:00
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
return category;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error("카테고리 생성 중 오류:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 카테고리 수정
|
|
|
|
|
*/
|
|
|
|
|
async updateCategory(
|
|
|
|
|
categoryCode: string,
|
|
|
|
|
data: Partial<CreateCategoryData>,
|
2025-10-28 10:07:07 +09:00
|
|
|
updatedBy: string,
|
|
|
|
|
companyCode?: string
|
2025-09-02 11:30:19 +09:00
|
|
|
) {
|
|
|
|
|
try {
|
2025-09-02 18:25:44 +09:00
|
|
|
// 디버깅: 받은 데이터 로그
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(`카테고리 수정 데이터:`, { categoryCode, data, companyCode });
|
2025-10-01 10:58:11 +09:00
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// 동적 UPDATE 쿼리 생성
|
2025-10-01 10:58:11 +09:00
|
|
|
const updateFields: string[] = [
|
|
|
|
|
"updated_by = $1",
|
|
|
|
|
"updated_date = NOW()",
|
|
|
|
|
];
|
2025-10-01 10:55:09 +09:00
|
|
|
const values: any[] = [updatedBy];
|
|
|
|
|
let paramIndex = 2;
|
|
|
|
|
|
|
|
|
|
if (data.categoryName !== undefined) {
|
|
|
|
|
updateFields.push(`category_name = $${paramIndex++}`);
|
|
|
|
|
values.push(data.categoryName);
|
|
|
|
|
}
|
|
|
|
|
if (data.categoryNameEng !== undefined) {
|
|
|
|
|
updateFields.push(`category_name_eng = $${paramIndex++}`);
|
|
|
|
|
values.push(data.categoryNameEng);
|
|
|
|
|
}
|
|
|
|
|
if (data.description !== undefined) {
|
|
|
|
|
updateFields.push(`description = $${paramIndex++}`);
|
|
|
|
|
values.push(data.description);
|
|
|
|
|
}
|
|
|
|
|
if (data.sortOrder !== undefined) {
|
|
|
|
|
updateFields.push(`sort_order = $${paramIndex++}`);
|
|
|
|
|
values.push(data.sortOrder);
|
|
|
|
|
}
|
|
|
|
|
if (data.isActive !== undefined) {
|
|
|
|
|
const activeValue =
|
|
|
|
|
typeof data.isActive === "boolean"
|
|
|
|
|
? data.isActive
|
|
|
|
|
? "Y"
|
|
|
|
|
: "N"
|
|
|
|
|
: data.isActive;
|
|
|
|
|
updateFields.push(`is_active = $${paramIndex++}`);
|
|
|
|
|
values.push(activeValue);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
// WHERE 절 구성
|
|
|
|
|
let whereClause = `WHERE category_code = $${paramIndex}`;
|
|
|
|
|
values.push(categoryCode);
|
|
|
|
|
|
|
|
|
|
// 회사 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (companyCode && companyCode !== "*") {
|
|
|
|
|
paramIndex++;
|
|
|
|
|
whereClause += ` AND company_code = $${paramIndex}`;
|
|
|
|
|
values.push(companyCode);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const category = await queryOne<CodeCategory>(
|
|
|
|
|
`UPDATE code_category
|
|
|
|
|
SET ${updateFields.join(", ")}
|
2025-10-28 10:07:07 +09:00
|
|
|
${whereClause}
|
2025-10-01 10:55:09 +09:00
|
|
|
RETURNING *`,
|
2025-10-28 10:07:07 +09:00
|
|
|
values
|
2025-10-01 10:55:09 +09:00
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(
|
|
|
|
|
`카테고리 수정 완료: ${categoryCode} (회사: ${companyCode || "전체"})`
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
return category;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`카테고리 수정 중 오류 (${categoryCode}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 카테고리 삭제
|
|
|
|
|
*/
|
2025-10-28 10:07:07 +09:00
|
|
|
async deleteCategory(categoryCode: string, companyCode?: string) {
|
2025-09-02 11:30:19 +09:00
|
|
|
try {
|
2025-10-28 10:07:07 +09:00
|
|
|
let sql = `DELETE FROM code_category WHERE category_code = $1`;
|
|
|
|
|
const values: any[] = [categoryCode];
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
// 회사 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (companyCode && companyCode !== "*") {
|
|
|
|
|
sql += ` AND company_code = $2`;
|
|
|
|
|
values.push(companyCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await query(sql, values);
|
|
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
`카테고리 삭제 완료: ${categoryCode} (회사: ${companyCode || "전체"})`
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`카테고리 삭제 중 오류 (${categoryCode}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 코드 생성
|
|
|
|
|
*/
|
|
|
|
|
async createCode(
|
|
|
|
|
categoryCode: string,
|
|
|
|
|
data: CreateCodeData,
|
2025-10-28 10:07:07 +09:00
|
|
|
createdBy: string,
|
2025-11-11 15:25:07 +09:00
|
|
|
companyCode: string,
|
|
|
|
|
menuObjid: number
|
2025-09-02 11:30:19 +09:00
|
|
|
) {
|
|
|
|
|
try {
|
2025-10-01 10:55:09 +09:00
|
|
|
const code = await queryOne<CodeInfo>(
|
|
|
|
|
`INSERT INTO code_info
|
|
|
|
|
(code_category, code_value, code_name, code_name_eng, description, sort_order,
|
2025-11-11 15:25:07 +09:00
|
|
|
is_active, menu_objid, company_code, created_by, updated_by, created_date, updated_date)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5, $6, 'Y', $7, $8, $9, $10, NOW(), NOW())
|
2025-10-01 10:55:09 +09:00
|
|
|
RETURNING *`,
|
|
|
|
|
[
|
|
|
|
|
categoryCode,
|
|
|
|
|
data.codeValue,
|
|
|
|
|
data.codeName,
|
|
|
|
|
data.codeNameEng || null,
|
|
|
|
|
data.description || null,
|
|
|
|
|
data.sortOrder || 0,
|
2025-11-11 15:25:07 +09:00
|
|
|
menuObjid,
|
2025-10-28 10:07:07 +09:00
|
|
|
companyCode,
|
2025-10-01 10:55:09 +09:00
|
|
|
createdBy,
|
|
|
|
|
createdBy,
|
|
|
|
|
]
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(
|
2025-11-11 15:25:07 +09:00
|
|
|
`코드 생성 완료: ${categoryCode}.${data.codeValue} (메뉴: ${menuObjid}, 회사: ${companyCode})`
|
2025-10-28 10:07:07 +09:00
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
return code;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(
|
|
|
|
|
`코드 생성 중 오류 (${categoryCode}.${data.codeValue}):`,
|
|
|
|
|
error
|
|
|
|
|
);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 코드 수정
|
|
|
|
|
*/
|
|
|
|
|
async updateCode(
|
|
|
|
|
categoryCode: string,
|
|
|
|
|
codeValue: string,
|
|
|
|
|
data: Partial<CreateCodeData>,
|
2025-10-28 10:07:07 +09:00
|
|
|
updatedBy: string,
|
|
|
|
|
companyCode?: string
|
2025-09-02 11:30:19 +09:00
|
|
|
) {
|
|
|
|
|
try {
|
2025-09-02 18:25:44 +09:00
|
|
|
// 디버깅: 받은 데이터 로그
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(`코드 수정 데이터:`, {
|
|
|
|
|
categoryCode,
|
|
|
|
|
codeValue,
|
|
|
|
|
data,
|
|
|
|
|
companyCode,
|
|
|
|
|
});
|
2025-10-01 10:58:11 +09:00
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// 동적 UPDATE 쿼리 생성
|
2025-10-01 10:58:11 +09:00
|
|
|
const updateFields: string[] = [
|
|
|
|
|
"updated_by = $1",
|
|
|
|
|
"updated_date = NOW()",
|
|
|
|
|
];
|
2025-10-01 10:55:09 +09:00
|
|
|
const values: any[] = [updatedBy];
|
|
|
|
|
let paramIndex = 2;
|
|
|
|
|
|
|
|
|
|
if (data.codeName !== undefined) {
|
|
|
|
|
updateFields.push(`code_name = $${paramIndex++}`);
|
|
|
|
|
values.push(data.codeName);
|
|
|
|
|
}
|
|
|
|
|
if (data.codeNameEng !== undefined) {
|
|
|
|
|
updateFields.push(`code_name_eng = $${paramIndex++}`);
|
|
|
|
|
values.push(data.codeNameEng);
|
|
|
|
|
}
|
|
|
|
|
if (data.description !== undefined) {
|
|
|
|
|
updateFields.push(`description = $${paramIndex++}`);
|
|
|
|
|
values.push(data.description);
|
|
|
|
|
}
|
|
|
|
|
if (data.sortOrder !== undefined) {
|
|
|
|
|
updateFields.push(`sort_order = $${paramIndex++}`);
|
|
|
|
|
values.push(data.sortOrder);
|
|
|
|
|
}
|
|
|
|
|
if (data.isActive !== undefined) {
|
|
|
|
|
const activeValue =
|
|
|
|
|
typeof data.isActive === "boolean"
|
|
|
|
|
? data.isActive
|
|
|
|
|
? "Y"
|
|
|
|
|
: "N"
|
|
|
|
|
: data.isActive;
|
|
|
|
|
updateFields.push(`is_active = $${paramIndex++}`);
|
|
|
|
|
values.push(activeValue);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
// WHERE 절 구성
|
|
|
|
|
let whereClause = `WHERE code_category = $${paramIndex++} AND code_value = $${paramIndex}`;
|
|
|
|
|
values.push(categoryCode, codeValue);
|
|
|
|
|
|
|
|
|
|
// 회사 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (companyCode && companyCode !== "*") {
|
|
|
|
|
paramIndex++;
|
|
|
|
|
whereClause += ` AND company_code = $${paramIndex}`;
|
|
|
|
|
values.push(companyCode);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const code = await queryOne<CodeInfo>(
|
|
|
|
|
`UPDATE code_info
|
|
|
|
|
SET ${updateFields.join(", ")}
|
2025-10-28 10:07:07 +09:00
|
|
|
${whereClause}
|
2025-10-01 10:55:09 +09:00
|
|
|
RETURNING *`,
|
2025-10-28 10:07:07 +09:00
|
|
|
values
|
2025-10-01 10:55:09 +09:00
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(
|
|
|
|
|
`코드 수정 완료: ${categoryCode}.${codeValue} (회사: ${companyCode || "전체"})`
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
return code;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`코드 수정 중 오류 (${categoryCode}.${codeValue}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 코드 삭제
|
|
|
|
|
*/
|
2025-10-28 10:07:07 +09:00
|
|
|
async deleteCode(
|
|
|
|
|
categoryCode: string,
|
|
|
|
|
codeValue: string,
|
|
|
|
|
companyCode?: string
|
|
|
|
|
) {
|
2025-09-02 11:30:19 +09:00
|
|
|
try {
|
2025-10-28 10:07:07 +09:00
|
|
|
let sql = `DELETE FROM code_info WHERE code_category = $1 AND code_value = $2`;
|
|
|
|
|
const values: any[] = [categoryCode, codeValue];
|
2025-09-02 11:30:19 +09:00
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
// 회사 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (companyCode && companyCode !== "*") {
|
|
|
|
|
sql += ` AND company_code = $3`;
|
|
|
|
|
values.push(companyCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await query(sql, values);
|
|
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
`코드 삭제 완료: ${categoryCode}.${codeValue} (회사: ${companyCode || "전체"})`
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`코드 삭제 중 오류 (${categoryCode}.${codeValue}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 카테고리별 옵션 조회 (화면관리용)
|
|
|
|
|
*/
|
2025-10-28 10:07:07 +09:00
|
|
|
async getCodeOptions(categoryCode: string, userCompanyCode?: string) {
|
2025-09-02 11:30:19 +09:00
|
|
|
try {
|
2025-10-28 10:07:07 +09:00
|
|
|
let sql = `SELECT code_value, code_name, code_name_eng, sort_order
|
|
|
|
|
FROM code_info
|
|
|
|
|
WHERE code_category = $1 AND is_active = 'Y'`;
|
|
|
|
|
const values: any[] = [categoryCode];
|
|
|
|
|
|
|
|
|
|
// 회사별 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (userCompanyCode && userCompanyCode !== "*") {
|
|
|
|
|
sql += ` AND company_code = $2`;
|
|
|
|
|
values.push(userCompanyCode);
|
|
|
|
|
logger.info(`회사별 코드 옵션 필터링: ${userCompanyCode}`);
|
|
|
|
|
} else if (userCompanyCode === "*") {
|
|
|
|
|
logger.info(`최고 관리자: 모든 코드 옵션 조회`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sql += ` ORDER BY sort_order ASC, code_value ASC`;
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const codes = await query<{
|
|
|
|
|
code_value: string;
|
|
|
|
|
code_name: string;
|
|
|
|
|
code_name_eng: string | null;
|
|
|
|
|
sort_order: number;
|
2025-10-28 10:07:07 +09:00
|
|
|
}>(sql, values);
|
2025-09-02 11:30:19 +09:00
|
|
|
|
|
|
|
|
const options = codes.map((code) => ({
|
|
|
|
|
value: code.code_value,
|
|
|
|
|
label: code.code_name,
|
|
|
|
|
labelEng: code.code_name_eng,
|
|
|
|
|
}));
|
|
|
|
|
|
2025-10-28 10:07:07 +09:00
|
|
|
logger.info(
|
|
|
|
|
`코드 옵션 조회 완료: ${categoryCode} - ${options.length}개 (회사: ${userCompanyCode || "전체"})`
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
return options;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`코드 옵션 조회 중 오류 (${categoryCode}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 코드 순서 변경
|
|
|
|
|
*/
|
|
|
|
|
async reorderCodes(
|
|
|
|
|
categoryCode: string,
|
|
|
|
|
codes: Array<{ codeValue: string; sortOrder: number }>,
|
|
|
|
|
updatedBy: string
|
|
|
|
|
) {
|
|
|
|
|
try {
|
2025-09-02 13:57:53 +09:00
|
|
|
// 먼저 존재하는 코드들을 확인
|
2025-10-01 10:55:09 +09:00
|
|
|
const codeValues = codes.map((c) => c.codeValue);
|
|
|
|
|
const placeholders = codeValues.map((_, i) => `$${i + 2}`).join(", ");
|
2025-10-01 10:58:11 +09:00
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
const existingCodes = await query<{ code_value: string }>(
|
|
|
|
|
`SELECT code_value FROM code_info
|
|
|
|
|
WHERE code_category = $1 AND code_value IN (${placeholders})`,
|
|
|
|
|
[categoryCode, ...codeValues]
|
|
|
|
|
);
|
2025-09-02 13:57:53 +09:00
|
|
|
|
|
|
|
|
const existingCodeValues = existingCodes.map((c) => c.code_value);
|
|
|
|
|
const validCodes = codes.filter((c) =>
|
|
|
|
|
existingCodeValues.includes(c.codeValue)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (validCodes.length === 0) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`카테고리 ${categoryCode}에 순서를 변경할 유효한 코드가 없습니다.`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// 트랜잭션으로 업데이트
|
|
|
|
|
await transaction(async (client) => {
|
|
|
|
|
for (const { codeValue, sortOrder } of validCodes) {
|
|
|
|
|
await client.query(
|
|
|
|
|
`UPDATE code_info
|
|
|
|
|
SET sort_order = $1, updated_by = $2, updated_date = NOW()
|
|
|
|
|
WHERE code_category = $3 AND code_value = $4`,
|
|
|
|
|
[sortOrder, updatedBy, categoryCode, codeValue]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-09-02 13:57:53 +09:00
|
|
|
|
|
|
|
|
const skippedCodes = codes.filter(
|
|
|
|
|
(c) => !existingCodeValues.includes(c.codeValue)
|
|
|
|
|
);
|
|
|
|
|
if (skippedCodes.length > 0) {
|
|
|
|
|
logger.warn(
|
|
|
|
|
`코드 순서 변경 시 존재하지 않는 코드들을 건너뜀: ${skippedCodes.map((c) => c.codeValue).join(", ")}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
`코드 순서 변경 완료: ${categoryCode} - ${validCodes.length}개 (전체 ${codes.length}개 중)`
|
|
|
|
|
);
|
2025-09-02 11:30:19 +09:00
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`코드 순서 변경 중 오류 (${categoryCode}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-03 11:20:43 +09:00
|
|
|
|
|
|
|
|
/**
|
2025-10-28 11:54:44 +09:00
|
|
|
* 카테고리 중복 검사 (회사별)
|
2025-09-03 11:20:43 +09:00
|
|
|
*/
|
|
|
|
|
async checkCategoryDuplicate(
|
2025-09-03 16:44:36 +09:00
|
|
|
field: "categoryCode" | "categoryName" | "categoryNameEng",
|
2025-09-03 11:20:43 +09:00
|
|
|
value: string,
|
2025-10-28 11:54:44 +09:00
|
|
|
excludeCategoryCode?: string,
|
|
|
|
|
userCompanyCode?: string
|
2025-09-03 11:20:43 +09:00
|
|
|
): Promise<{ isDuplicate: boolean; message: string }> {
|
|
|
|
|
try {
|
|
|
|
|
if (!value || !value.trim()) {
|
|
|
|
|
return {
|
|
|
|
|
isDuplicate: false,
|
2025-09-03 16:44:36 +09:00
|
|
|
message: "값을 입력해주세요.",
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const trimmedValue = value.trim();
|
|
|
|
|
let whereCondition: any = {};
|
|
|
|
|
|
|
|
|
|
// 필드별 검색 조건 설정
|
|
|
|
|
switch (field) {
|
2025-09-03 16:44:36 +09:00
|
|
|
case "categoryCode":
|
2025-09-03 11:20:43 +09:00
|
|
|
whereCondition.category_code = trimmedValue;
|
|
|
|
|
break;
|
2025-09-03 16:44:36 +09:00
|
|
|
case "categoryName":
|
2025-09-03 11:20:43 +09:00
|
|
|
whereCondition.category_name = trimmedValue;
|
|
|
|
|
break;
|
2025-09-03 16:44:36 +09:00
|
|
|
case "categoryNameEng":
|
2025-09-03 11:20:43 +09:00
|
|
|
whereCondition.category_name_eng = trimmedValue;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// SQL 쿼리 생성
|
|
|
|
|
let sql = "";
|
|
|
|
|
const values: any[] = [];
|
|
|
|
|
let paramIndex = 1;
|
|
|
|
|
|
|
|
|
|
switch (field) {
|
|
|
|
|
case "categoryCode":
|
|
|
|
|
sql = `SELECT category_code FROM code_category WHERE category_code = $${paramIndex++}`;
|
|
|
|
|
values.push(trimmedValue);
|
|
|
|
|
break;
|
|
|
|
|
case "categoryName":
|
|
|
|
|
sql = `SELECT category_code FROM code_category WHERE category_name = $${paramIndex++}`;
|
|
|
|
|
values.push(trimmedValue);
|
|
|
|
|
break;
|
|
|
|
|
case "categoryNameEng":
|
|
|
|
|
sql = `SELECT category_code FROM code_category WHERE category_name_eng = $${paramIndex++}`;
|
|
|
|
|
values.push(trimmedValue);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 11:54:44 +09:00
|
|
|
// 회사별 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (userCompanyCode && userCompanyCode !== "*") {
|
|
|
|
|
sql += ` AND company_code = $${paramIndex++}`;
|
|
|
|
|
values.push(userCompanyCode);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 11:20:43 +09:00
|
|
|
// 수정 시 자기 자신 제외
|
|
|
|
|
if (excludeCategoryCode) {
|
2025-10-01 10:55:09 +09:00
|
|
|
sql += ` AND category_code != $${paramIndex++}`;
|
|
|
|
|
values.push(excludeCategoryCode);
|
2025-09-03 11:20:43 +09:00
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
sql += ` LIMIT 1`;
|
|
|
|
|
|
|
|
|
|
const existingCategory = await queryOne<{ category_code: string }>(
|
|
|
|
|
sql,
|
|
|
|
|
values
|
|
|
|
|
);
|
2025-09-03 11:20:43 +09:00
|
|
|
|
|
|
|
|
const isDuplicate = !!existingCategory;
|
|
|
|
|
const fieldNames = {
|
2025-09-03 16:44:36 +09:00
|
|
|
categoryCode: "카테고리 코드",
|
|
|
|
|
categoryName: "카테고리명",
|
|
|
|
|
categoryNameEng: "카테고리 영문명",
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
|
2025-10-28 11:54:44 +09:00
|
|
|
logger.info(
|
|
|
|
|
`카테고리 중복 검사: ${field}=${value}, 회사=${userCompanyCode}, 중복=${isDuplicate}`
|
|
|
|
|
);
|
|
|
|
|
|
2025-09-03 11:20:43 +09:00
|
|
|
return {
|
|
|
|
|
isDuplicate,
|
2025-09-03 16:44:36 +09:00
|
|
|
message: isDuplicate
|
2025-09-03 11:20:43 +09:00
|
|
|
? `이미 사용 중인 ${fieldNames[field]}입니다.`
|
2025-09-03 16:44:36 +09:00
|
|
|
: `사용 가능한 ${fieldNames[field]}입니다.`,
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`카테고리 중복 검사 중 오류 (${field}: ${value}):`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-10-28 11:54:44 +09:00
|
|
|
* 코드 중복 검사 (회사별)
|
2025-09-03 11:20:43 +09:00
|
|
|
*/
|
|
|
|
|
async checkCodeDuplicate(
|
|
|
|
|
categoryCode: string,
|
2025-09-03 16:44:36 +09:00
|
|
|
field: "codeValue" | "codeName" | "codeNameEng",
|
2025-09-03 11:20:43 +09:00
|
|
|
value: string,
|
2025-10-28 11:54:44 +09:00
|
|
|
excludeCodeValue?: string,
|
|
|
|
|
userCompanyCode?: string
|
2025-09-03 11:20:43 +09:00
|
|
|
): Promise<{ isDuplicate: boolean; message: string }> {
|
|
|
|
|
try {
|
|
|
|
|
if (!value || !value.trim()) {
|
|
|
|
|
return {
|
|
|
|
|
isDuplicate: false,
|
2025-09-03 16:44:36 +09:00
|
|
|
message: "값을 입력해주세요.",
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const trimmedValue = value.trim();
|
|
|
|
|
let whereCondition: any = {
|
2025-09-03 16:44:36 +09:00
|
|
|
code_category: categoryCode,
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 필드별 검색 조건 설정
|
|
|
|
|
switch (field) {
|
2025-09-03 16:44:36 +09:00
|
|
|
case "codeValue":
|
2025-09-03 11:20:43 +09:00
|
|
|
whereCondition.code_value = trimmedValue;
|
|
|
|
|
break;
|
2025-09-03 16:44:36 +09:00
|
|
|
case "codeName":
|
2025-09-03 11:20:43 +09:00
|
|
|
whereCondition.code_name = trimmedValue;
|
|
|
|
|
break;
|
2025-09-03 16:44:36 +09:00
|
|
|
case "codeNameEng":
|
2025-09-03 11:20:43 +09:00
|
|
|
whereCondition.code_name_eng = trimmedValue;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
// SQL 쿼리 생성
|
2025-10-01 10:58:11 +09:00
|
|
|
let sql =
|
|
|
|
|
"SELECT code_value FROM code_info WHERE code_category = $1 AND ";
|
2025-10-01 10:55:09 +09:00
|
|
|
const values: any[] = [categoryCode];
|
|
|
|
|
let paramIndex = 2;
|
|
|
|
|
|
|
|
|
|
switch (field) {
|
|
|
|
|
case "codeValue":
|
|
|
|
|
sql += `code_value = $${paramIndex++}`;
|
|
|
|
|
values.push(trimmedValue);
|
|
|
|
|
break;
|
|
|
|
|
case "codeName":
|
|
|
|
|
sql += `code_name = $${paramIndex++}`;
|
|
|
|
|
values.push(trimmedValue);
|
|
|
|
|
break;
|
|
|
|
|
case "codeNameEng":
|
|
|
|
|
sql += `code_name_eng = $${paramIndex++}`;
|
|
|
|
|
values.push(trimmedValue);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 11:54:44 +09:00
|
|
|
// 회사별 필터링 (최고 관리자가 아닌 경우)
|
|
|
|
|
if (userCompanyCode && userCompanyCode !== "*") {
|
|
|
|
|
sql += ` AND company_code = $${paramIndex++}`;
|
|
|
|
|
values.push(userCompanyCode);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 11:20:43 +09:00
|
|
|
// 수정 시 자기 자신 제외
|
|
|
|
|
if (excludeCodeValue) {
|
2025-10-01 10:55:09 +09:00
|
|
|
sql += ` AND code_value != $${paramIndex++}`;
|
|
|
|
|
values.push(excludeCodeValue);
|
2025-09-03 11:20:43 +09:00
|
|
|
}
|
|
|
|
|
|
2025-10-01 10:55:09 +09:00
|
|
|
sql += ` LIMIT 1`;
|
|
|
|
|
|
|
|
|
|
const existingCode = await queryOne<{ code_value: string }>(sql, values);
|
2025-09-03 11:20:43 +09:00
|
|
|
|
|
|
|
|
const isDuplicate = !!existingCode;
|
|
|
|
|
const fieldNames = {
|
2025-09-03 16:44:36 +09:00
|
|
|
codeValue: "코드값",
|
|
|
|
|
codeName: "코드명",
|
|
|
|
|
codeNameEng: "코드 영문명",
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
|
2025-10-28 11:54:44 +09:00
|
|
|
logger.info(
|
|
|
|
|
`코드 중복 검사: ${categoryCode}.${field}=${value}, 회사=${userCompanyCode}, 중복=${isDuplicate}`
|
|
|
|
|
);
|
|
|
|
|
|
2025-09-03 11:20:43 +09:00
|
|
|
return {
|
|
|
|
|
isDuplicate,
|
2025-09-03 16:44:36 +09:00
|
|
|
message: isDuplicate
|
2025-09-03 11:20:43 +09:00
|
|
|
? `이미 사용 중인 ${fieldNames[field]}입니다.`
|
2025-09-03 16:44:36 +09:00
|
|
|
: `사용 가능한 ${fieldNames[field]}입니다.`,
|
2025-09-03 11:20:43 +09:00
|
|
|
};
|
|
|
|
|
} catch (error) {
|
2025-09-03 16:44:36 +09:00
|
|
|
logger.error(
|
|
|
|
|
`코드 중복 검사 중 오류 (${categoryCode}, ${field}: ${value}):`,
|
|
|
|
|
error
|
|
|
|
|
);
|
2025-09-03 11:20:43 +09:00
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-02 11:30:19 +09:00
|
|
|
}
|