견적서 자동 채움 보강 + 시행일자 picker 형식 정정
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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, // 추가 필드
|
||||
|
||||
+29
-17
@@ -80,6 +80,7 @@ export default function EstimateTemplate1Page() {
|
||||
|
||||
// 헤더 필드
|
||||
const [executor, setExecutor] = useState<string>(""); // 시행일자 (YYYY-MM-DD)
|
||||
const executorDateRef = useRef<HTMLInputElement>(null);
|
||||
const [recipient, setRecipient] = useState<string>(""); // 수신처 (customer_objid)
|
||||
const [estimateNo, setEstimateNo] = useState<string>("");
|
||||
const [contactPerson, setContactPerson] = useState<string>("");
|
||||
@@ -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() {
|
||||
<tr>
|
||||
<td className="label">시행일자</td>
|
||||
<td>
|
||||
<input
|
||||
type="date"
|
||||
value={executor}
|
||||
onChange={e => setExecutor(e.target.value)}
|
||||
readOnly={readOnly}
|
||||
style={{ width: 150 }}
|
||||
/>
|
||||
{/* 표시는 YYYY-MM-DD (text), 클릭 시 숨겨진 type=date의 showPicker 트리거 */}
|
||||
<div style={{ position: "relative", display: "inline-block", width: 150 }}>
|
||||
<input
|
||||
type="text"
|
||||
value={executor}
|
||||
readOnly
|
||||
placeholder="YYYY-MM-DD"
|
||||
onClick={() => { if (!readOnly) executorDateRef.current?.showPicker?.(); }}
|
||||
style={{ width: "100%", cursor: readOnly ? "default" : "pointer" }}
|
||||
/>
|
||||
<input
|
||||
ref={executorDateRef}
|
||||
type="date"
|
||||
value={executor}
|
||||
onChange={e => setExecutor(e.target.value)}
|
||||
tabIndex={-1}
|
||||
aria-hidden="true"
|
||||
style={{ position: "absolute", left: 0, top: 0, width: 1, height: 1, opacity: 0, pointerEvents: "none" }}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td rowSpan={4} style={{ border: "none" }}></td>
|
||||
<td rowSpan={4} style={{ textAlign: "center", border: "none", verticalAlign: "middle", padding: 0 }}>
|
||||
|
||||
+25
-11
@@ -109,6 +109,7 @@ export default function EstimateTemplate2Page() {
|
||||
|
||||
// 헤더
|
||||
const [executorDate, setExecutorDate] = useState<string>("");
|
||||
const executorDateRef = React.useRef<HTMLInputElement>(null);
|
||||
const [recipient, setRecipient] = useState<string>("");
|
||||
const [partName, setPartName] = useState<string>("");
|
||||
const [partObjid, setPartObjid] = useState<string>("");
|
||||
@@ -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() {
|
||||
<div style={{ textAlign: "left", fontSize: "10pt", lineHeight: 2 }}>
|
||||
<div>
|
||||
<strong>시행일자 :</strong>{" "}
|
||||
<input
|
||||
type="date"
|
||||
value={executorDate}
|
||||
onChange={e => setExecutorDate(e.target.value)}
|
||||
readOnly={readOnly}
|
||||
style={{ width: 200, borderBottom: "1px solid #999", padding: "2px 5px" }}
|
||||
/>
|
||||
{/* 표시는 YYYY-MM-DD (text), 클릭 시 숨겨진 type=date의 showPicker 트리거 */}
|
||||
<span style={{ position: "relative", display: "inline-block", width: 200 }}>
|
||||
<input
|
||||
type="text"
|
||||
value={executorDate}
|
||||
readOnly
|
||||
placeholder="YYYY-MM-DD"
|
||||
onClick={() => { if (!readOnly) executorDateRef.current?.showPicker?.(); }}
|
||||
style={{ width: "100%", borderBottom: "1px solid #999", padding: "2px 5px", cursor: readOnly ? "default" : "pointer" }}
|
||||
/>
|
||||
<input
|
||||
ref={executorDateRef}
|
||||
type="date"
|
||||
value={executorDate}
|
||||
onChange={e => setExecutorDate(e.target.value)}
|
||||
tabIndex={-1}
|
||||
aria-hidden="true"
|
||||
style={{ position: "absolute", left: 0, top: 0, width: 1, height: 1, opacity: 0, pointerEvents: "none" }}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>수 신 처 :</strong>{" "}
|
||||
|
||||
Reference in New Issue
Block a user