From 0afa8b03cfc2dfc17550475a4e117854515eb0a7 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 11 May 2026 15:07:25 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=AC=EC=A0=81=EC=84=9C=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=B1=84=EC=9B=80=20=EB=B3=B4=EA=B0=95=20+=20?= =?UTF-8?q?=EC=8B=9C=ED=96=89=EC=9D=BC=EC=9E=90=20picker=20=ED=98=95?= =?UTF-8?q?=EC=8B=9D=20=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) getById 응답 구조 fix - salesEstimateService.getById는 { ...contract_mgmt 헤더, items } 평면 객체 반환 - 페이지가 contractInfo.header.customer_objid로 잘못 접근해 고객사·환율·통화 자동 채움이 안 됐던 버그 - contractInfo.customer_objid 직접 접근으로 수정 (template1·template2 동일) 2) /auth/me 응답에 cellPhone/tel 추가 - AuthService.getUserInfo는 이미 두 필드 반환하나 authController가 응답에서 누락 - 견적서 연락처가 wace MailUtil 패턴(cell_phone 우선 → tel 폴백)으로 자동 채워지도록 노출 3) 시행일자 picker 표시 형식 정정 - input[type="date"]는 한국 Chrome이 "YYYY. MM. DD." 강제 (CSS 변경 불가) - text input + hidden type=date 조합: 표시는 YYYY-MM-DD, 클릭 시 hidden picker showPicker() 트리거 - template1 executor + template2 executor_date 모두 적용 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/controllers/authController.ts | 2 + .../template1/pop/[contractObjid]/page.tsx | 46 ++++++++++++------- .../template2/pop/[contractObjid]/page.tsx | 36 ++++++++++----- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/backend-node/src/controllers/authController.ts b/backend-node/src/controllers/authController.ts index ad66a7c2..b841af44 100644 --- a/backend-node/src/controllers/authController.ts +++ b/backend-node/src/controllers/authController.ts @@ -376,6 +376,8 @@ export class AuthController { userType: userInfo.userType || dbUserInfo.userType || "USER", // JWT 토큰 우선 userTypeName: dbUserInfo.userTypeName || "일반사용자", email: dbUserInfo.email || "", + tel: dbUserInfo.tel || "", + cellPhone: dbUserInfo.cellPhone || "", photo: dbUserInfo.photo, locale: dbUserInfo.locale || "KR", // locale 정보 추가 deptCode: dbUserInfo.deptCode, // 추가 필드 diff --git a/frontend/app/(main)/COMPANY_16/sales/estimate/template1/pop/[contractObjid]/page.tsx b/frontend/app/(main)/COMPANY_16/sales/estimate/template1/pop/[contractObjid]/page.tsx index 0390d071..e61ddd6e 100644 --- a/frontend/app/(main)/COMPANY_16/sales/estimate/template1/pop/[contractObjid]/page.tsx +++ b/frontend/app/(main)/COMPANY_16/sales/estimate/template1/pop/[contractObjid]/page.tsx @@ -80,6 +80,7 @@ export default function EstimateTemplate1Page() { // 헤더 필드 const [executor, setExecutor] = useState(""); // 시행일자 (YYYY-MM-DD) + const executorDateRef = useRef(null); const [recipient, setRecipient] = useState(""); // 수신처 (customer_objid) const [estimateNo, setEstimateNo] = useState(""); const [contactPerson, setContactPerson] = useState(""); @@ -161,15 +162,12 @@ export default function EstimateTemplate1Page() { } } - if (contractInfo?.header) { - const h = contractInfo.header; - if (!cancel) { - setExchangeRate(parseFloat(h.exchange_rate || "1") || 1); - // 통화명: comm_code lookup이 필요한데 detail이 raw코드만 줄 수도 있음 → 일단 코드 사용 - setCurrencyName(h.contract_currency_name || h.contract_currency || "KRW"); - // 수신처는 customer_objid (예: 'C_RPS001') - if (h.customer_objid) setRecipient(h.customer_objid); - } + // getById 응답: { ...contract_mgmt 헤더 필드들, items: [...] } (평면 구조) + if (contractInfo && !cancel) { + setExchangeRate(parseFloat(contractInfo.exchange_rate || "1") || 1); + setCurrencyName(contractInfo.contract_currency_name || contractInfo.contract_currency || "KRW"); + // 수신처는 customer_objid (예: 'C_RPS001') — 견적요청의 고객사를 견적서에 자동 채움 + if (contractInfo.customer_objid) setRecipient(contractInfo.customer_objid); } // 2) 기존 견적 차수 수정 (templateObjid 지정) — 우선 @@ -241,7 +239,8 @@ export default function EstimateTemplate1Page() { if (!user) return; if (templateObjidParam) return; // 기존 견적서 수정 모드는 건드리지 않음 setManagerName(prev => prev || `${user.deptName ?? ""} ${user.userName ?? ""}`.trim()); - setManagerContact(prev => prev || user.tel ?? ""); + // wace MailUtil 패턴: cell_phone 우선, 없으면 tel + setManagerContact(prev => prev || user.cellPhone || user.tel || ""); }, [user, templateObjidParam]); // ─── 저장 (wace fn_save 1:1) ───────────────────────────────── @@ -511,13 +510,26 @@ export default function EstimateTemplate1Page() { 시행일자 - setExecutor(e.target.value)} - readOnly={readOnly} - style={{ width: 150 }} - /> + {/* 표시는 YYYY-MM-DD (text), 클릭 시 숨겨진 type=date의 showPicker 트리거 */} +
+ { if (!readOnly) executorDateRef.current?.showPicker?.(); }} + style={{ width: "100%", cursor: readOnly ? "default" : "pointer" }} + /> + setExecutor(e.target.value)} + tabIndex={-1} + aria-hidden="true" + style={{ position: "absolute", left: 0, top: 0, width: 1, height: 1, opacity: 0, pointerEvents: "none" }} + /> +
diff --git a/frontend/app/(main)/COMPANY_16/sales/estimate/template2/pop/[contractObjid]/page.tsx b/frontend/app/(main)/COMPANY_16/sales/estimate/template2/pop/[contractObjid]/page.tsx index 489ec521..749ff195 100644 --- a/frontend/app/(main)/COMPANY_16/sales/estimate/template2/pop/[contractObjid]/page.tsx +++ b/frontend/app/(main)/COMPANY_16/sales/estimate/template2/pop/[contractObjid]/page.tsx @@ -109,6 +109,7 @@ export default function EstimateTemplate2Page() { // 헤더 const [executorDate, setExecutorDate] = useState(""); + const executorDateRef = React.useRef(null); const [recipient, setRecipient] = useState(""); const [partName, setPartName] = useState(""); const [partObjid, setPartObjid] = useState(""); @@ -201,10 +202,10 @@ export default function EstimateTemplate2Page() { console.warn("영업정보 로드 실패", e); } } - if (contractInfo?.header && !cancel) { - const h = contractInfo.header; - setExchangeRate(parseFloat(h.exchange_rate || "1") || 1); - if (h.customer_objid) setRecipient(h.customer_objid); + // getById 응답: { ...contract_mgmt 헤더 필드들, items: [...] } (평면 구조) + if (contractInfo && !cancel) { + setExchangeRate(parseFloat(contractInfo.exchange_rate || "1") || 1); + if (contractInfo.customer_objid) setRecipient(contractInfo.customer_objid); } // 신규: contract_item[0]의 part_name을 품명/Model로 if (contractInfo?.items?.[0] && !cancel) { @@ -454,13 +455,26 @@ export default function EstimateTemplate2Page() {
시행일자 :{" "} - setExecutorDate(e.target.value)} - readOnly={readOnly} - style={{ width: 200, borderBottom: "1px solid #999", padding: "2px 5px" }} - /> + {/* 표시는 YYYY-MM-DD (text), 클릭 시 숨겨진 type=date의 showPicker 트리거 */} + + { if (!readOnly) executorDateRef.current?.showPicker?.(); }} + style={{ width: "100%", borderBottom: "1px solid #999", padding: "2px 5px", cursor: readOnly ? "default" : "pointer" }} + /> + setExecutorDate(e.target.value)} + tabIndex={-1} + aria-hidden="true" + style={{ position: "absolute", left: 0, top: 0, width: 1, height: 1, opacity: 0, pointerEvents: "none" }} + /> +
수 신 처 :{" "}