From 905b51b865c06008fe02306072bfc6a5015df6d0 Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 15 May 2026 12:19:15 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[1423]=202=EC=B0=A8=20user=20=ED=8C=90?= =?UTF-8?q?=EB=A7=A4=EC=A0=90=20select=20=ED=95=AD=EC=83=81=20enable=20+?= =?UTF-8?q?=20=E8=A8=AD=E8=A8=88=E4=BE=9D=E9=A0=BC=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20=EB=B3=B8=EC=9D=B8=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/management/StuffDetail.jsx | 167 ++++++++++++---------- src/locales/ja.json | 1 + src/locales/ko.json | 1 + 3 files changed, 97 insertions(+), 72 deletions(-) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index b1328188..68129345 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -23,6 +23,7 @@ import { QcastContext } from '@/app/QcastProvider' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useSwal } from '@/hooks/useSwal' import { sanitizeIntegerInputEvent } from '@/util/input-utils' +import { logger } from '@/util/logger' import { CalculatorInput } from '@/components/common/input/CalcInput' import Image from 'next/image' @@ -1008,93 +1009,115 @@ export default function StuffDetail() { //팝업에서 넘어온 설계의뢰 정보로 바꾸기 const setPlanReqInfo = (info) => { - form.setValue('planReqNo', info.planReqNo) - - form.setValue('objectStatusId', info.building) - setSelectObjectStatusId(info.building) - - form.setValue('objectName', info.title) - form.setValue('zipNo', info.zipNo) - form.setValue('address', info.address2) - - prefCodeList.map((row) => { - if (row.prefName == info.address1) { - setPrefValue(row.prefId) - form.setValue('prefId', row.prefId) - form.setValue('prefName', info.address1) - } + // [PLANREQ-DEBUG 2026-05-15] import 흐름 진단 + logger.debug('[PLANREQ-DEBUG] setPlanReqInfo entry', { + session: { storeId: session?.storeId, storeLvl: session?.storeLvl }, + info: { planReqNo: info?.planReqNo, saleStoreId: info?.saleStoreId, saleStoreLevel: info?.saleStoreLevel, saleStoreName: info?.saleStoreName }, + saleStoreList: saleStoreList.map((s) => s.saleStoreId), + otherSaleStoreList: otherSaleStoreList.map((o) => o.saleStoreId), }) - //설계의뢰 팝업에선 WL_안붙어서 옴 - if (info.windSpeed !== '') { - form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) - } else { - form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) + // [PLANREQ-MATCH 2026-05-15] all-or-nothing: saleStore 매핑 검증 후에만 모든 필드 적용 + const applyFields = () => { + form.setValue('planReqNo', info.planReqNo) + form.setValue('objectStatusId', info.building) + setSelectObjectStatusId(info.building) + form.setValue('objectName', info.title) + form.setValue('zipNo', info.zipNo) + form.setValue('address', info.address2) + prefCodeList.map((row) => { + if (row.prefName == info.address1) { + setPrefValue(row.prefId) + form.setValue('prefId', row.prefId) + form.setValue('prefName', info.address1) + } + }) + //설계의뢰 팝업에선 WL_안붙어서 옴 + if (info.windSpeed !== '') { + form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) + } else { + form.setValue('standardWindSpeedId', `WL_${info.windSpeed}`) + } + form.setValue('verticalSnowCover', info.verticalSnowCover) + form.setValue('surfaceType', info.surfaceType) + if (info.surfaceType === 'Ⅱ') { + form.setValue('saltAreaFlg', true) + } else { + form.setValue('saltAreaFlg', false) + } + const installHeight = info.installHeight ? info.installHeight.split('.')[0] : '' + form.setValue('installHeight', installHeight) + form.setValue('remarks', info.remarks) } - form.setValue('verticalSnowCover', info.verticalSnowCover) - form.setValue('surfaceType', info.surfaceType) - - if (info.surfaceType === 'Ⅱ') { - form.setValue('saltAreaFlg', true) - } else { - form.setValue('saltAreaFlg', false) - } - - let installHeight = info.installHeight ? info.installHeight.split('.')[0] : '' - - form.setValue('installHeight', installHeight) - form.setValue('remarks', info.remarks) if (info.saleStoreLevel === '1') { + // 1차 ID: 매핑 실패 사실상 없음 (조회 자체가 권한 필터) → 즉시 적용 + applyFields() setSelOptions(info.saleStoreId) form.setValue('saleStoreId', info.saleStoreId) form.setValue('saleStoreName', info.saleStoreName) form.setValue('saleStoreLevel', info.saleStoreLevel) - } else { - // [PLANREQ-MATCH 2026-05-15] 2차 user: 매핑 실패 시 planReqNo 클리어 + 본인 store 로 초기화 → 2차 select disable 자동 해제 - if (session?.storeLvl === '2') { - const matched = otherSaleStoreList.some((o) => o.saleStoreId === info.saleStoreId) - if (!matched) { - form.setValue('planReqNo', '') - setOtherSelOptions(session?.storeId) - form.setValue('otherSaleStoreId', session?.storeId) - form.setValue('otherSaleStoreName', '') - form.setValue('otherSaleStoreLevel', session?.storeLvl) - return - } + return + } + + // info.saleStoreLevel !== '1' (2차 ID) + if (session?.storeLvl === '2') { + const matched = otherSaleStoreList.some((o) => o.saleStoreId === info.saleStoreId) + logger.debug('[PLANREQ-DEBUG] 2차 user match check', { matched, target: info.saleStoreId }) + if (!matched) { + // 매핑 실패 — 아무것도 적용 안 함 + 사용자에게 알림 + swalFire({ + title: getMessage('stuff.detail.planReq.message.notMatch'), + type: 'alert', + icon: 'warning', + }) + return } + applyFields() setOtherSelOptions(info.saleStoreId) form.setValue('otherSaleStoreId', info.saleStoreId) form.setValue('otherSaleStoreName', info.saleStoreName) form.setValue('otherSaleStoreLevel', info.saleStoreLevel) - get({ url: `/api/object/saleStore/${info.saleStoreId}/firstAgent` }).then((res) => { - if (res?.firstAgentId) { - const firstAgent = { saleStoreId: res.firstAgentId, saleStoreName: res.firstAgentName } - setSaleStoreList((prev) => { - const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) - return exists ? prev : [...prev, firstAgent] - }) - setShowSaleStoreList((prev) => { - const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) - return exists ? prev : [...prev, firstAgent] - }) - setSelOptions(res.firstAgentId) - form.setValue('saleStoreId', res.firstAgentId) - form.setValue('saleStoreName', res.firstAgentName) - form.setValue('saleStoreLevel', '1') - } else { - setSelOptions('') - form.setValue('saleStoreId', '') - form.setValue('saleStoreName', '') - form.setValue('saleStoreLevel', '') - } - }).catch(() => { - setSelOptions('') - form.setValue('saleStoreId', '') - form.setValue('saleStoreName', '') - form.setValue('saleStoreLevel', '') - }) + return } + + // T01 / 1차 user + 2차 ID: firstAgent 검증 후에만 적용 (실패 시 무반영) + get({ url: `/api/object/saleStore/${info.saleStoreId}/firstAgent` }).then((res) => { + logger.debug('[PLANREQ-DEBUG] firstAgent result', { firstAgentId: res?.firstAgentId }) + if (!res?.firstAgentId) { + swalFire({ + title: getMessage('stuff.detail.planReq.message.notMatch'), + type: 'alert', + icon: 'warning', + }) + return + } + applyFields() + setOtherSelOptions(info.saleStoreId) + form.setValue('otherSaleStoreId', info.saleStoreId) + form.setValue('otherSaleStoreName', info.saleStoreName) + form.setValue('otherSaleStoreLevel', info.saleStoreLevel) + const firstAgent = { saleStoreId: res.firstAgentId, saleStoreName: res.firstAgentName } + setSaleStoreList((prev) => { + const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) + return exists ? prev : [...prev, firstAgent] + }) + setShowSaleStoreList((prev) => { + const exists = prev.some((s) => s.saleStoreId === res.firstAgentId) + return exists ? prev : [...prev, firstAgent] + }) + setSelOptions(res.firstAgentId) + form.setValue('saleStoreId', res.firstAgentId) + form.setValue('saleStoreName', res.firstAgentName) + form.setValue('saleStoreLevel', '1') + }).catch(() => { + // 매핑 실패 — 아무것도 적용 안 함 + 사용자에게 알림 + swalFire({ + title: getMessage('stuff.detail.planReq.message.notMatch'), + type: 'alert', + icon: 'warning', + }) + }) } //풍속선택 팝업에서 넘어온 바람정보 diff --git a/src/locales/ja.json b/src/locales/ja.json index ec6d7331..2bc8f273 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -782,6 +782,7 @@ "stuff.detail.tempSave.message2": "担当者名は全角20文字(半角40文字)以下で入力してください.", "stuff.detail.tempSave.message3": "二次販売店を選択してください。", "stuff.detail.confirm.message1": "販売店情報を変更すると、設計依頼文書番号が削除されます。変更しますか?", + "stuff.detail.planReq.message.notMatch": "設計依頼の販売店情報が一致しないため、インポートできません。", "stuff.detail.delete.message1": "仕様が確定したものは削除できません。", "stuff.detail.planList.title": "プランリスト", "stuff.detail.planList.cnt": "全体", diff --git a/src/locales/ko.json b/src/locales/ko.json index 84c95a6b..5d9a5474 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -782,6 +782,7 @@ "stuff.detail.tempSave.message2": "담당자이름은 전각20자(반각40자) 이하로 입력해 주십시오.", "stuff.detail.tempSave.message3": "2차 판매점을 선택해주세요.", "stuff.detail.confirm.message1": "판매점 정보를 변경하면 설계의뢰 문서번호가 삭제됩니다. 변경하시겠습니까?", + "stuff.detail.planReq.message.notMatch": "설계의뢰의 판매점 정보가 일치하지 않아 가져올 수 없습니다.", "stuff.detail.delete.message1": "사양이 확정된 물건은 삭제할 수 없습니다.", "stuff.detail.planList.title": "플랜목록", "stuff.detail.planList.cnt": "전체", -- 2.47.2 From 116953ee2f3760382a286062ea6d1dff10e52b1f Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 15 May 2026 12:28:32 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=86=8C=EC=8A=A4=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/skeleton-utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index e46053c4..2329cc92 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -1994,11 +1994,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver let movedLines = [] // 조건에 맞는 라인들만 필터링 - const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index) + const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index); // [ASI-FIX 2026-05-15] 세미콜론 없으면 다음 줄 `(` 가 함수호출로 묶여 .filter(...)(...) → "is not a function" → SK 빌드 실패 + 확장선 누락 // logger.log('', sortRoofLines, sortWallLines, sortWallBaseLines); - (sortWallLines.length === sortWallBaseLines.length && sortWallBaseLines.length > 3) && + ;(sortWallLines.length === sortWallBaseLines.length && sortWallBaseLines.length > 3) && sortWallLines.forEach((wallLine, index) => { const roofLine = sortRoofLines[index] -- 2.47.2