From 5226e6d3bb46c1b91e6b0d2e3b45e1a11a861ab2 Mon Sep 17 00:00:00 2001 From: ysCha Date: Mon, 6 Jul 2026 14:57:04 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Revert=20"[2311]=20=EA=B2=AC=EC=A0=81=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=ED=9B=84=20CONN=5FVOL=5FW(=EC=A0=91?= =?UTF-8?q?=EC=86=8D=EC=9A=A9=EB=9F=89)=20=EB=B0=B1=ED=95=84=20=E2=80=94?= =?UTF-8?q?=20backfillConnVol=20=ED=97=AC=ED=8D=BC=20=EC=B6=94=EC=B6=9C,?= =?UTF-8?q?=20update-conn-vol=20=E2=86=92=20detail=20=EC=9E=AC=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=9C=EC=84=9C=20=EA=B3=A0=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 661da5ec8d8717e5800dcb82cc3b386e2a555572. --- .../estimate/useEstimateController.js | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 264b9732..3f7120d8 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -10,8 +10,6 @@ import { useRouter, useSearchParams } from 'next/navigation' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { QcastContext } from '@/app/QcastProvider' import { useSwal } from '@/hooks/useSwal' -import { POLYGON_TYPE } from '@/common/common' -import { logger } from '@/util/logger' // Constants const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의 @@ -35,7 +33,7 @@ export const useEstimateController = (planNo, flag) => { const { getMessage } = useMessage() - const { promiseGet, post, promisePost, get } = useAxios(globalLocaleState) + const { promiseGet, post, promisePost } = useAxios(globalLocaleState) const [isLoading, setIsLoading] = useState(false) const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext) @@ -388,36 +386,6 @@ export const useEstimateController = (planNo, flag) => { } } - // 견적 상세 저장 후 저장된 plan JSON 에서 회로 payload 를 만들어 CONN_VOL_W(접속용량) 백필을 요청한다. - const backfillConnVol = async (objectNo, planNo) => { - try { - const list = await get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) - const target = (list || []).find((it) => String(it.planNo) === String(planNo)) || (list || [])[0] - const raw = target?.canvasStatus?.replace(/##/g, '"').replace(/∠/g, '∠').replace(/°/g, '°') - const objects = raw ? JSON.parse(raw).objects || [] : [] - - const moduleObjs = objects.filter((o) => o.name === POLYGON_TYPE.MODULE) - const moduleList = moduleObjs.map((m) => ({ circuit: m.circuitNumber, specification: m.moduleInfo?.wpOut })) - if (moduleList.length === 0) return - - // PCS 유닛 번호 = circuit 첫 숫자. distinct 개수 = 유닛 수, 오름차순 = 유닛 index - const unitNumbers = [...new Set(moduleObjs.map((m) => parseInt(String(m.circuitNumber ?? '').match(/\d+/)?.[0], 10)).filter((n) => n > 0))].sort( - (a, b) => a - b - ) - // PCS 데이터는 surfaces[0] 에만 실려 있다. - const surface0 = objects.find((o) => o.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) - const pcsList = (surface0?.pcses || []).filter((pcs) => pcs?.pcsItemId) - const circuitItemList = unitNumbers.map((unit) => ({ itemId: pcsList[unit - 1]?.pcsItemId })) - - await promisePost({ - url: `${ESTIMATE_API_ENDPOINT}/update-conn-vol`, - data: { objectNo, planNo, circuitItemList, roofSurfaceList: [{ moduleList }] }, - }) - } catch (e) { - logger.log('[UPDATE-CONN-VOL] 백필 호출 실패', e?.message) - } - } - const realSave = async (fileList) => { setEstimateContextState({pricingFlag: false}) @@ -483,11 +451,9 @@ export const useEstimateController = (planNo, flag) => { //2. 상세데이터 저장 try { setIsGlobalLoading(true) - await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then(async (res) => { + await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { if (res.status === 201) { estimateData.newFileList = [] - // 인증용량이 CONN_VOL_W 로 계산되므로, /detail 재조회(fetchSetting) 전에 백필을 먼저 끝낸다. - await backfillConnVol(estimateData.objectNo, estimateData.planNo) swalFire({ text: getMessage('estimate.detail.save.alertMsg'), type: 'alert' }) fetchSetting(estimateData.objectNo, estimateData.planNo) } -- 2.47.2 From 8817e72ddb18ba4b0c3013b6d60272a4ef17fa4e Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 7 Jul 2026 08:37:33 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[=ED=9A=8C=EB=A1=9C=ED=95=A0=EB=8B=B9]=20?= =?UTF-8?q?=ED=9A=8C=EB=A1=9C=20=EB=AF=B8=ED=95=A0=EB=8B=B9=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=9E=88=EC=9D=84=20=EB=95=8C=20=EC=BA=A1=EC=B2=98?= =?UTF-8?q?=20=EC=A0=84=EC=97=90=20=EC=B0=A8=EB=8B=A8=20=E2=80=94=20onAppl?= =?UTF-8?q?y=20=EC=A7=84=EC=9E=85=20=EC=8B=9C=20not.allocation.exist.modul?= =?UTF-8?q?e=20=EC=84=A0=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modal/circuitTrestle/CircuitTrestleSetting.jsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx index 333bc0e7..17a7d63d 100644 --- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx +++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx @@ -685,6 +685,18 @@ export default function CircuitTrestleSetting({ id }) { // 회로할당(승압설정) 저장 버튼 클릭 시 const onApply = async () => { + // [NOT-ALLOC-GUARD 2026-07-03] 회로 미할당 모듈이 있으면 캡처(capture) 전에 차단. + // apply() 는 검증+가대설치가 묶여 있어 캡처 뒤에 실행되므로, 여기서 먼저 막지 않으면 이미지가 먼저 생성된다. + const notAllocationModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE && !obj.circuit) + if (notAllocationModules.length > 0) { + swalFire({ + text: getMessage('not.allocation.exist.module'), + type: 'alert', + icon: 'warning', + }) + return + } + setAllModuleSurfaceIsComplete(false) setIsGlobalLoading(true) -- 2.47.2 From cf0ff417fc7cff8d6a597aa465a6730012068d02 Mon Sep 17 00:00:00 2001 From: ysCha Date: Tue, 7 Jul 2026 13:41:37 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[2311]=20=EA=B2=AC=EC=A0=81=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=ED=9B=84=20CONN=5FVOL=5FW(=EC=A0=91=EC=86=8D?= =?UTF-8?q?=EC=9A=A9=EB=9F=89)=20=EB=B0=B1=ED=95=84=20=E2=80=94=20circuitI?= =?UTF-8?q?temList=20=EB=B9=88=20{}=20=EC=A0=9C=EA=B1=B0,=20PCS=20?= =?UTF-8?q?=EC=8B=A4=EB=8C=80=EC=88=98=20=EA=B8=B0=EC=A4=80=20=EB=A7=A4?= =?UTF-8?q?=ED=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../estimate/useEstimateController.js | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 3f7120d8..ce177683 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -10,6 +10,8 @@ import { useRouter, useSearchParams } from 'next/navigation' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' import { QcastContext } from '@/app/QcastProvider' import { useSwal } from '@/hooks/useSwal' +import { POLYGON_TYPE } from '@/common/common' +import { logger } from '@/util/logger' // Constants const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의 @@ -33,7 +35,7 @@ export const useEstimateController = (planNo, flag) => { const { getMessage } = useMessage() - const { promiseGet, post, promisePost } = useAxios(globalLocaleState) + const { promiseGet, post, promisePost, get } = useAxios(globalLocaleState) const [isLoading, setIsLoading] = useState(false) const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext) @@ -386,6 +388,44 @@ export const useEstimateController = (planNo, flag) => { } } + // 견적 저장 직후 CONN_VOL_W(접속용량) 백필 → 인증용량 = Σ min(CONN_VOL_W_unit, PCS정격_unit) 계산용. + // 견적서 상세 화면엔 라이브 캔버스가 없으므로, 서버에 저장된 plan JSON(by-object)을 받아 파싱한다. + const backfillConnVol = async (objectNo, planNo) => { + try { + const list = await get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) + const target = (list || []).find((it) => String(it.planNo) === String(planNo)) || (list || [])[0] + const raw = target?.canvasStatus?.replace(/##/g, '"').replace(/∠/g, '∠').replace(/°/g, '°') + const objects = raw ? JSON.parse(raw).objects || [] : [] + + const moduleObjs = objects.filter((o) => o.name === POLYGON_TYPE.MODULE) + const moduleList = moduleObjs.map((m) => ({ circuit: m.circuitNumber, specification: m.moduleInfo?.wpOut })) + + // PCS 데이터는 surfaces[0] 에만 실려 있다. + // circuitItemList 개수 = 실제 PCS 대수(빈 {} 금지). 라벨 첫 숫자로 유닛 수를 세면 + // 단일 PCS+회로 여러 개일 때 pcsList[unit-1] 이 undefined → 빈 {} 가 생겨 서버가 정격을 못 찾아 회로가 탈락한다. + const surface0 = objects.find((o) => o.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) + const pcsList = (surface0?.pcses || []).filter((pcs) => pcs?.pcsItemId) + const circuitItemList = pcsList.map((pcs) => ({ itemId: pcs.pcsItemId })) + + logger.log('[UPDATE-CONN-VOL] enter', { + planCount: (list || []).length, + moduleCount: moduleObjs.length, + pcsCount: pcsList.length, + objectNo, + planNo, + }) + if (moduleList.length === 0) { + logger.log('[UPDATE-CONN-VOL] skip — plan JSON 에 모듈 없음') + return + } + const connVolData = { objectNo, planNo, circuitItemList, roofSurfaceList: [{ moduleList }] } + logger.log('[UPDATE-CONN-VOL] payload', JSON.stringify(connVolData)) + await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/update-conn-vol`, data: connVolData }) + } catch (e) { + logger.log('[UPDATE-CONN-VOL] 백필 호출 실패', e?.message) + } + } + const realSave = async (fileList) => { setEstimateContextState({pricingFlag: false}) @@ -451,9 +491,12 @@ export const useEstimateController = (planNo, flag) => { //2. 상세데이터 저장 try { setIsGlobalLoading(true) - await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { + await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then(async (res) => { + logger.log('[UPDATE-CONN-VOL] save-estimate 응답', res.status) if (res.status === 201) { estimateData.newFileList = [] + // 인증용량이 CONN_VOL_W 로 계산되므로 /detail 재조회(fetchSetting) 전에 백필을 끝낸다. + await backfillConnVol(estimateData.objectNo, estimateData.planNo) swalFire({ text: getMessage('estimate.detail.save.alertMsg'), type: 'alert' }) fetchSetting(estimateData.objectNo, estimateData.planNo) } -- 2.47.2