Add Customer Contact Routes and Enhance User Management
- Introduced new routes for customer contact management, allowing for the retrieval of customer contact information. - Updated the user management functionality to include validation for the hire date, ensuring proper date format and handling of null values. - Enhanced the save user functionality to accommodate the new hire date field, maintaining existing values when not provided. (TASK: ERP-XXX)
This commit is contained in:
@@ -4122,6 +4122,7 @@ interface UserWithDeptRequest {
|
||||
position_code?: string;
|
||||
position_name?: string;
|
||||
end_date?: string | null;
|
||||
hire_date?: string | null; // 입사일 (등록시각 regdate 와 분리)
|
||||
};
|
||||
mainDept?: {
|
||||
dept_code: string;
|
||||
@@ -4169,6 +4170,24 @@ export const saveUserWithDept = async (
|
||||
return;
|
||||
}
|
||||
|
||||
// 입사일(hire_date) 형식 검증 — 값이 들어온 경우에만 YYYY-MM-DD 유효 날짜인지 확인
|
||||
let hireDate: string | null | undefined = undefined;
|
||||
if (userInfo.hire_date !== undefined && userInfo.hire_date !== null && userInfo.hire_date !== "") {
|
||||
const raw = String(userInfo.hire_date).substring(0, 10);
|
||||
const d = new Date(`${raw}T00:00:00+09:00`);
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(raw) || isNaN(d.getTime())) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "입사일 형식이 올바르지 않습니다.",
|
||||
error: { code: "INVALID_HIRE_DATE" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
hireDate = raw;
|
||||
} else if (userInfo.hire_date === null || userInfo.hire_date === "") {
|
||||
hireDate = null; // 명시적으로 비운 경우
|
||||
}
|
||||
|
||||
// 트랜잭션 시작
|
||||
await client.query("BEGIN");
|
||||
|
||||
@@ -4234,6 +4253,8 @@ export const saveUserWithDept = async (
|
||||
position_code: userInfo.position_code,
|
||||
position_name: positionName,
|
||||
end_date: userInfo.end_date !== undefined ? (userInfo.end_date ? `${userInfo.end_date.substring(0, 10)}T00:00:00+09:00` : null) : undefined,
|
||||
// 입사일: 페이로드에 hire_date 키가 온 경우에만 갱신(누락 시 기존값 유지)
|
||||
hire_date: userInfo.hire_date !== undefined ? hireDate ?? null : undefined,
|
||||
company_code: companyCode !== "*" ? companyCode : undefined,
|
||||
};
|
||||
|
||||
@@ -4265,8 +4286,8 @@ export const saveUserWithDept = async (
|
||||
email, tel, cell_phone, sabun,
|
||||
user_type, user_type_name, status, locale,
|
||||
dept_code, dept_name, position_code, position_name,
|
||||
company_code, end_date, regdate
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, NOW())`,
|
||||
company_code, end_date, hire_date, regdate
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, COALESCE($19::date, CURRENT_DATE), NOW())`,
|
||||
[
|
||||
userInfo.user_id,
|
||||
userInfo.user_name,
|
||||
@@ -4286,6 +4307,8 @@ export const saveUserWithDept = async (
|
||||
positionName,
|
||||
companyCode !== "*" ? companyCode : null,
|
||||
userInfo.end_date ? `${userInfo.end_date.substring(0, 10)}T00:00:00+09:00` : null,
|
||||
// 입사일: 값 있으면 그 날짜, 없으면 COALESCE 로 CURRENT_DATE(오늘) 기본값
|
||||
hireDate ?? null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
50
backend-node/src/controllers/customerContactController.ts
Normal file
50
backend-node/src/controllers/customerContactController.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 거래처 담당자(customer_contact) 조회 컨트롤러
|
||||
*
|
||||
* 수주관리 등에서 거래처 선택 시 담당자 리스트/메인담당자 조회용.
|
||||
* 조회 전용 (DB 스키마 변경 없음).
|
||||
*/
|
||||
|
||||
import { Response } from "express";
|
||||
import { AuthenticatedRequest } from "../types/auth";
|
||||
import * as customerContactService from "../services/customerContactService";
|
||||
|
||||
/**
|
||||
* GET /api/customer-contacts/:customerId
|
||||
* 거래처별 담당자 리스트 조회
|
||||
* - :customerId 는 customer_mng.customer_code 또는 customer_mng.id
|
||||
* - company_code 는 인증 토큰에서 추출 (멀티테넌트 격리)
|
||||
*/
|
||||
export async function getContactsByCustomer(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const { customerId } = req.params;
|
||||
|
||||
if (!customerId) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "거래처 식별자가 필요합니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const contacts = await customerContactService.getContactsByCustomer(
|
||||
companyCode,
|
||||
customerId,
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: contacts,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("거래처 담당자 조회 실패:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "거래처 담당자 조회에 실패했습니다.",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ interface InspectionRow {
|
||||
apply_process: string | null;
|
||||
classification: string | null;
|
||||
pass_criteria: string | null;
|
||||
upper_limit: string | null;
|
||||
lower_limit: string | null;
|
||||
is_required: string | null;
|
||||
is_active: string | null;
|
||||
manager_id: string | null;
|
||||
@@ -98,7 +100,7 @@ export async function getGroupedList(req: AuthenticatedRequest, res: Response) {
|
||||
SELECT
|
||||
id, item_code, item_name, inspection_type, inspection_standard, inspection_standard_id,
|
||||
inspection_item_name, inspection_method, apply_process, classification,
|
||||
pass_criteria, is_required, is_active, manager_id, memo,
|
||||
pass_criteria, upper_limit, lower_limit, is_required, is_active, manager_id, memo,
|
||||
sort_order, change_record, created_date, updated_date
|
||||
FROM item_inspection_info
|
||||
WHERE company_code = $1
|
||||
|
||||
@@ -3644,6 +3644,8 @@ accepted_results AS (
|
||||
'started_at', started_at,
|
||||
'completed_at', completed_at,
|
||||
'equipment_code', equipment_code,
|
||||
'defect_detail', defect_detail,
|
||||
'result_note', result_note,
|
||||
'batch_id', batch_id
|
||||
) ORDER BY seq
|
||||
) AS accepted_results
|
||||
|
||||
@@ -167,6 +167,20 @@ export async function create(req: AuthenticatedRequest, res: Response) {
|
||||
|
||||
await client.query("BEGIN");
|
||||
|
||||
// 담당자(처리자) 한글명 조회 — inventory_history에 manager_id/manager_name 동반 기록용
|
||||
// (CLAUDE.md "사용자 식별 표시 필수": DB에는 user_id 저장, 표시는 user_name)
|
||||
let managerName = userId;
|
||||
try {
|
||||
const mgrRes = await client.query(
|
||||
`SELECT COALESCE(NULLIF(user_name, ''), user_id) AS user_name
|
||||
FROM user_info WHERE user_id = $1 AND company_code = $2 LIMIT 1`,
|
||||
[userId, companyCode],
|
||||
);
|
||||
if (mgrRes.rows[0]?.user_name) managerName = mgrRes.rows[0].user_name;
|
||||
} catch {
|
||||
/* user_info 조회 실패 시 userId fallback 유지 */
|
||||
}
|
||||
|
||||
inboundType = await resolveCategoryCode(client, "inbound_mng", "inbound_type", inboundType);
|
||||
|
||||
// 1. 헤더 — 같은 (company_code, inbound_number) 헤더가 있으면 reuse, 없으면 INSERT (멱등성)
|
||||
@@ -329,8 +343,8 @@ export async function create(req: AuthenticatedRequest, res: Response) {
|
||||
`INSERT INTO inventory_history (
|
||||
id, company_code, item_code, warehouse_code, location_code,
|
||||
transaction_type, transaction_date, quantity, balance_qty, remark,
|
||||
writer, created_date
|
||||
) VALUES (gen_random_uuid()::text, $1, $2, $3, $4, '입고', NOW(), $5, $6, $7, $8, NOW())`,
|
||||
writer, manager_id, manager_name, created_date
|
||||
) VALUES (gen_random_uuid()::text, $1, $2, $3, $4, '입고', NOW(), $5, $6, $7, $8, $9, $10, NOW())`,
|
||||
[
|
||||
companyCode,
|
||||
itemCode,
|
||||
@@ -340,6 +354,8 @@ export async function create(req: AuthenticatedRequest, res: Response) {
|
||||
afterQty,
|
||||
resolvedItemInboundType || "입고",
|
||||
userId,
|
||||
userId,
|
||||
managerName,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,8 +121,9 @@ async function getNormalizedOrders(
|
||||
dueDate: r.due_date || "",
|
||||
orderQty: Number(r.order_qty || 0),
|
||||
shipQty: Number(r.ship_qty || 0),
|
||||
// balance_qty가 NULL/0이면 orderQty - shipQty fallback (수주 등록 시 채워지지 않은 데이터 보정)
|
||||
balanceQty: Number(r.balance_qty) || (Number(r.order_qty || 0) - Number(r.ship_qty || 0)),
|
||||
// 잔량은 항상 (수주량 - 출하수량) 파생값으로 산출.
|
||||
// 저장된 balance_qty는 비정규화 필드라 오염될 수 있어 집계에 신뢰하지 않는다.
|
||||
balanceQty: Math.max(0, Number(r.order_qty || 0) - Number(r.ship_qty || 0)),
|
||||
}));
|
||||
} else {
|
||||
// 마스터 기준 → 거래처 JOIN
|
||||
@@ -166,8 +167,9 @@ async function getNormalizedOrders(
|
||||
dueDate: r.due_date || "",
|
||||
orderQty: Number(r.order_qty || 0),
|
||||
shipQty: Number(r.ship_qty || 0),
|
||||
// balance_qty가 NULL/0이면 orderQty - shipQty fallback (수주 등록 시 채워지지 않은 데이터 보정)
|
||||
balanceQty: Number(r.balance_qty) || (Number(r.order_qty || 0) - Number(r.ship_qty || 0)),
|
||||
// 잔량은 항상 (수주량 - 출하수량) 파생값으로 산출.
|
||||
// 저장된 balance_qty는 비정규화 필드라 오염될 수 있어 집계에 신뢰하지 않는다.
|
||||
balanceQty: Math.max(0, Number(r.order_qty || 0) - Number(r.ship_qty || 0)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -411,38 +413,9 @@ export async function updatePlan(req: AuthenticatedRequest, res: Response) {
|
||||
finalParams
|
||||
);
|
||||
|
||||
// plan_qty가 변경되었고 마스터(sales_order_mng) 기반 출하계획이면 master 재계산 + 자동 전이
|
||||
if (qtyDelta !== 0 && masterId) {
|
||||
// sales_order_mng의 ship_qty/balance_qty를 delta만큼 보정
|
||||
await client.query(
|
||||
`UPDATE sales_order_mng
|
||||
SET ship_qty = COALESCE(ship_qty, 0) + $1,
|
||||
balance_qty = COALESCE(order_qty, 0) - (COALESCE(ship_qty, 0) + $1),
|
||||
updated_date = NOW()
|
||||
WHERE id = $2 AND company_code = $3`,
|
||||
[qtyDelta, masterId, companyCode]
|
||||
);
|
||||
|
||||
// 자동 상태 전이 (TASK:ERP-047) — 양방향
|
||||
// balance_qty <= 0 AND status='CONFIRMED' → 'COMPLETED'
|
||||
await client.query(
|
||||
`UPDATE sales_order_mng
|
||||
SET status = 'COMPLETED', updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2
|
||||
AND status = 'CONFIRMED'
|
||||
AND COALESCE(balance_qty, 0) <= 0`,
|
||||
[masterId, companyCode]
|
||||
);
|
||||
// balance_qty > 0 AND status='COMPLETED' → 'CONFIRMED'
|
||||
await client.query(
|
||||
`UPDATE sales_order_mng
|
||||
SET status = 'CONFIRMED', updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2
|
||||
AND status = 'COMPLETED'
|
||||
AND COALESCE(balance_qty, 0) > 0`,
|
||||
[masterId, companyCode]
|
||||
);
|
||||
}
|
||||
// TASK:ERP-053-A — 출하계획 plan_qty 수정 시 sales_order_mng.ship_qty/balance_qty 가산 보정 제거.
|
||||
// ship_qty는 "실제 출고 누적"으로 단일화되었으므로 계획수량 변경은 ship_qty에 영향 주지 않는다.
|
||||
// 계획수량 변경은 shipment_plan.plan_qty 에만 반영. 수주 상태 전이는 실제 출고 시점에서 처리.
|
||||
|
||||
await client.query("COMMIT");
|
||||
|
||||
@@ -507,9 +480,9 @@ export async function getAggregate(req: AuthenticatedRequest, res: Response) {
|
||||
const result: Record<string, any> = {};
|
||||
|
||||
for (const [partCode, partOrders] of partCodeMap) {
|
||||
// 총수주잔량: 선택된 수주들의 balance_qty 합
|
||||
// 총수주잔량: 선택된 수주들의 (수주량 - 출하수량) 합 (파생값 일관 사용)
|
||||
const totalBalance = partOrders.reduce(
|
||||
(s, o) => s + (o.balanceQty > 0 ? o.balanceQty : o.orderQty - o.shipQty),
|
||||
(s, o) => s + Math.max(0, o.orderQty - o.shipQty),
|
||||
0
|
||||
);
|
||||
|
||||
@@ -723,7 +696,7 @@ export async function batchSave(req: AuthenticatedRequest, res: Response) {
|
||||
// 디테일 소스: detail_id로 저장
|
||||
const detailCheck = await client.query(
|
||||
`SELECT d.id, d.order_no, d.part_code, d.qty, d.ship_qty, d.balance_qty,
|
||||
m.id AS master_id
|
||||
m.id AS master_id, m.order_qty AS m_order_qty
|
||||
FROM sales_order_detail d
|
||||
LEFT JOIN sales_order_mng m
|
||||
ON d.order_no = m.order_no AND d.company_code = m.company_code
|
||||
@@ -736,13 +709,25 @@ export async function batchSave(req: AuthenticatedRequest, res: Response) {
|
||||
}
|
||||
|
||||
const detail = detailCheck.rows[0];
|
||||
const qty = Number(detail.qty || 0);
|
||||
const shipQty = Number(detail.ship_qty || 0);
|
||||
const balanceQty = detail.balance_qty
|
||||
? Number(detail.balance_qty)
|
||||
: qty - shipQty;
|
||||
// 수주량: detail.qty 우선, 없으면 마스터 order_qty 폴백
|
||||
// (getNormalizedOrders와 동일 규칙 — '0'/공란 함정 제거)
|
||||
const baseQty = Number(detail.qty) || Number(detail.m_order_qty) || 0;
|
||||
|
||||
if (balanceQty > 0 && planQty > balanceQty) {
|
||||
// TASK:ERP-053-A — ship_qty는 "실제 출고 누적"으로 단일화.
|
||||
// 출하계획 생성 단계에서는 ship_qty/balance_qty를 가산하지 않는다.
|
||||
// (실제 출고는 outboundController.ts:288 에서만 ship_qty 반영)
|
||||
// 과다 출하계획 가드: 미출하량 = 수주량 - 실제출고(ship_qty) - 기존 계획수량 합.
|
||||
const prevShip = Number(detail.ship_qty) || 0;
|
||||
const planSumRes = await client.query(
|
||||
`SELECT COALESCE(SUM(plan_qty), 0) AS plan_sum
|
||||
FROM shipment_plan
|
||||
WHERE detail_id = $1 AND company_code = $2`,
|
||||
[sourceId, companyCode]
|
||||
);
|
||||
const existingPlanSum = Number(planSumRes.rows[0]?.plan_sum || 0);
|
||||
const balanceQty = baseQty - prevShip - existingPlanSum; // 이번 계획 전 잔여 가능량
|
||||
|
||||
if (baseQty > 0 && planQty > balanceQty) {
|
||||
throw new Error(
|
||||
`수주번호 ${detail.order_no}: 출하계획량(${planQty})이 미출하량(${balanceQty})을 초과합니다`
|
||||
);
|
||||
@@ -758,16 +743,8 @@ export async function batchSave(req: AuthenticatedRequest, res: Response) {
|
||||
);
|
||||
savedPlans.push(insertRes.rows[0]);
|
||||
|
||||
// detail ship_qty 업데이트
|
||||
await client.query(
|
||||
`UPDATE sales_order_detail
|
||||
SET ship_qty = (COALESCE(NULLIF(ship_qty,'')::numeric, 0) + $1)::text,
|
||||
balance_qty = (COALESCE(NULLIF(qty,'')::numeric, 0)
|
||||
- COALESCE(NULLIF(ship_qty,'')::numeric, 0) - $1)::text,
|
||||
updated_date = NOW()
|
||||
WHERE id = $2 AND company_code = $3`,
|
||||
[planQty, sourceId, companyCode]
|
||||
);
|
||||
// TASK:ERP-053-A — 출하계획 생성 시 sales_order_detail.ship_qty 가산 제거.
|
||||
// 계획수량은 shipment_plan.plan_qty 로만 관리. ship_qty는 실제 출고에서만 반영.
|
||||
} else {
|
||||
// 마스터 소스: sales_order_id로 저장
|
||||
const masterId = Number(sourceId);
|
||||
@@ -783,9 +760,21 @@ export async function batchSave(req: AuthenticatedRequest, res: Response) {
|
||||
}
|
||||
|
||||
const master = masterCheck.rows[0];
|
||||
const balanceQty = Number(master.balance_qty || 0);
|
||||
const orderQty = Number(master.order_qty || 0);
|
||||
|
||||
if (balanceQty > 0 && planQty > balanceQty) {
|
||||
// TASK:ERP-053-A — ship_qty는 "실제 출고 누적"으로 단일화.
|
||||
// 과다 출하계획 가드: 미출하량 = 수주량 - 실제출고(ship_qty) - 기존 계획수량 합.
|
||||
const prevShip = Number(master.ship_qty || 0);
|
||||
const planSumRes = await client.query(
|
||||
`SELECT COALESCE(SUM(plan_qty), 0) AS plan_sum
|
||||
FROM shipment_plan
|
||||
WHERE sales_order_id = $1 AND company_code = $2`,
|
||||
[masterId, companyCode]
|
||||
);
|
||||
const existingPlanSum = Number(planSumRes.rows[0]?.plan_sum || 0);
|
||||
const balanceQty = orderQty - prevShip - existingPlanSum;
|
||||
|
||||
if (orderQty > 0 && planQty > balanceQty) {
|
||||
throw new Error(
|
||||
`수주번호 ${master.order_no}: 출하계획량(${planQty})이 미출하량(${balanceQty})을 초과합니다`
|
||||
);
|
||||
@@ -801,27 +790,10 @@ export async function batchSave(req: AuthenticatedRequest, res: Response) {
|
||||
);
|
||||
savedPlans.push(insertRes.rows[0]);
|
||||
|
||||
// 마스터 ship_qty 업데이트
|
||||
await client.query(
|
||||
`UPDATE sales_order_mng
|
||||
SET ship_qty = COALESCE(ship_qty, 0) + $1,
|
||||
balance_qty = COALESCE(order_qty, 0) - COALESCE(ship_qty, 0) - $1,
|
||||
updated_date = NOW()
|
||||
WHERE id = $2 AND company_code = $3`,
|
||||
[planQty, masterId, companyCode]
|
||||
);
|
||||
|
||||
// 자동 상태 전이 (TASK:ERP-047)
|
||||
// balance_qty <= 0 AND status='CONFIRMED' → 'COMPLETED'
|
||||
// 같은 트랜잭션 내에서 처리
|
||||
await client.query(
|
||||
`UPDATE sales_order_mng
|
||||
SET status = 'COMPLETED', updated_date = NOW()
|
||||
WHERE id = $1 AND company_code = $2
|
||||
AND status = 'CONFIRMED'
|
||||
AND COALESCE(balance_qty, 0) <= 0`,
|
||||
[masterId, companyCode]
|
||||
);
|
||||
// TASK:ERP-053-A — 출하계획 생성 시 sales_order_mng.ship_qty/balance_qty 가산 제거.
|
||||
// 계획수량은 shipment_plan.plan_qty 로만 관리. ship_qty는 실제 출고에서만 반영.
|
||||
// 자동 상태 전이(TASK:ERP-047 COMPLETED)는 실제 출고 시점(outboundController)에서 처리되어야 하므로
|
||||
// 출하계획 생성 단계의 stale balance_qty 기반 전이는 제거(중복 가산 해소 후 무의미).
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,18 +82,26 @@ export const getCategoryValues = async (req: AuthenticatedRequest, res: Response
|
||||
filterCompanyCode,
|
||||
});
|
||||
|
||||
const values = await tableCategoryValueService.getCategoryValues(
|
||||
tableName,
|
||||
columnName,
|
||||
effectiveCompanyCode,
|
||||
includeInactive,
|
||||
menuObjid,
|
||||
topLevelOnly
|
||||
);
|
||||
const [values, useHierarchy] = await Promise.all([
|
||||
tableCategoryValueService.getCategoryValues(
|
||||
tableName,
|
||||
columnName,
|
||||
effectiveCompanyCode,
|
||||
includeInactive,
|
||||
menuObjid,
|
||||
topLevelOnly
|
||||
),
|
||||
tableCategoryValueService.getUseHierarchy(
|
||||
tableName,
|
||||
columnName,
|
||||
effectiveCompanyCode
|
||||
),
|
||||
]);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: values,
|
||||
useHierarchy,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`카테고리 값 조회 실패: ${error.message}`);
|
||||
@@ -105,6 +113,66 @@ export const getCategoryValues = async (req: AuthenticatedRequest, res: Response
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 카테고리 컬럼 use_hierarchy 플래그 업데이트
|
||||
*
|
||||
* PUT /api/table-categories/:tableName/:columnName/hierarchy-flag
|
||||
*
|
||||
* Body: { useHierarchy: boolean }
|
||||
*/
|
||||
export const updateUseHierarchy = async (
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
) => {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const userId = req.user!.userId;
|
||||
const { tableName, columnName } = req.params;
|
||||
const { useHierarchy } = req.body;
|
||||
|
||||
if (typeof useHierarchy !== "boolean") {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "useHierarchy는 boolean 값이어야 합니다",
|
||||
});
|
||||
}
|
||||
|
||||
if (companyCode === "*") {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "최고관리자(*) 계정에서는 use_hierarchy 설정을 변경할 수 없습니다",
|
||||
});
|
||||
}
|
||||
|
||||
logger.info("use_hierarchy 업데이트 요청", {
|
||||
tableName,
|
||||
columnName,
|
||||
companyCode,
|
||||
useHierarchy,
|
||||
});
|
||||
|
||||
const updated = await tableCategoryValueService.updateUseHierarchy(
|
||||
tableName,
|
||||
columnName,
|
||||
companyCode,
|
||||
useHierarchy,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: { useHierarchy: updated },
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(`use_hierarchy 업데이트 실패: ${error.message}`);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || "use_hierarchy 업데이트 중 오류가 발생했습니다",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 카테고리 값 추가 (메뉴 스코프)
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user