'use client' import { useEffect, useState, useContext } from 'react' import { useRecoilValue } from 'recoil' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { useMessage } from '@/hooks/useMessage' import SingleDatePicker from '../common/datepicker/SingleDatePicker' import EstimateFileUploader from './EstimateFileUploader' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty, queryStringFormatter } from '@/util/common-utils' import dayjs from 'dayjs' import { useCommonCode } from '@/hooks/common/useCommonCode' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { SessionContext } from '@/app/SessionProvider' import Select, { components } from 'react-select' import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils' import ProductFeaturesPop from './popup/ProductFeaturesPop' import { v4 as uuidv4 } from 'uuid' import { useSearchParams } from 'next/navigation' import { usePlan } from '@/hooks/usePlan' import { usePopup } from '@/hooks/usePopup' import { useSwal } from '@/hooks/useSwal' import { QcastContext } from '@/app/QcastProvider' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import {normalizeDigits, normalizeDecimal} from '@/util/input-utils' export default function Estimate({}) { const [uniqueData, setUniqueData] = useState([]) const [handlePricingFlag, setHandlePricingFlag] = useState(false) const [specialNoteFirstFlg, setSpecialNoteFirstFlg] = useState(false) const [itemChangeYn, setItemChangeYn] = useState(false) const { session } = useContext(SessionContext) const [objectNo, setObjectNo] = useState('') //물건번호 const [planNo, setPlanNo] = useState('') //플랜번호 const { swalFire } = useSwal() const [files, setFiles] = useState([]) // 보내는 첨부파일 const [originFiles, setOriginFiles] = useState([]) //기존 첨부파일 const [showPriceCd, setShowPriceCd] = useState('') const [showContentCode, setShowContentCode] = useState('ATTR001') const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) //견적특이사항 팝업 const [showProductFeatureData, setShowProductFeatureData] = useState([]) //팝업에 보여줄 견적특이사항 데이터 const [selection, setSelection] = useState(new Set()) //견적특이사항 접고 펼치기 const [hidden, setHidden] = useState(true) //아이템 자동완성 리스트 const [originDisplayItemList, setOriginDisplayItemList] = useState([]) const [displayItemList, setDisplayItemList] = useState([]) //공통코드 const { findCommonCode } = useCommonCode() const [honorificCodeList, setHonorificCodeList] = useState([]) //경칭 공통코드 const [storePriceList, setStorePriceList] = useState([]) //가격표시 option const [cableItemList, setCableItemList] = useState([]) //케이블 리스트 const [cableItem, setCableItem] = useState('') //케이블 선택값 const [cableDbItem, setCableDbItem] = useState('') //케이블 선택값 const [startDate, setStartDate] = useState(new Date()) const singleDatePickerProps = { startDate, setStartDate, } /** * objectNo 셋팅 * url로 넘어온 objectNo을 리코일에 세팅 */ const searchParams = useSearchParams() const objectRecoil = useRecoilValue(floorPlanObjectState) const currentObjectNo = searchParams.get('objectNo') const currentPid = searchParams.get('pid') //견적서 상세데이터 const { estimateContextState, setEstimateContextState, addItem, handleEstimateFileDownload } = useEstimateController(currentPid, false) const { selectedPlan } = usePlan() //견적특이사항 List const [specialNoteList, setSpecialNoteList] = useState([]) const [popShowSpecialNoteList, setPopShowSpecialNoteList] = useState([]) const { setIsGlobalLoading } = useContext(QcastContext) const globalLocaleState = useRecoilValue(globalLocaleStore) const { get, post, promisePost } = useAxios(globalLocaleState) const { getMessage } = useMessage() const { closeAll } = usePopup() const { setSelectedMenu } = useCanvasMenu() //새로 추가한 첨부파일 props const fileUploadProps = { uploadFiles: files, setUploadFiles: setFiles, } const initEstimate = (currPid = currentPid) => { // console.log('🚀 ~ initEstimate ~ currPid:', currPid) closeAll() setObjectNo(objectRecoil.floorPlanObjectNo) setPlanNo(currPid) // 공통코드 const code1 = findCommonCode(200800) if (code1 != null) { setHonorificCodeList(code1) } // 케이블제품 공통코드 const code2 = findCommonCode(117900) if (code2 != null) { code2.map((item) => { item.value = item.clRefChr1 item.label = item.clRefChr2 }) // console.log(code2) setCableItemList(code2) } //아이템 자동완성 목록 가져오기 const param = { saleStoreId: session.storeId, } const apiUrl = `/api/display-item/item-list?${queryStringFormatter(param)}` post({ url: apiUrl, data: param }).then((res) => { if (res.length > 0) { let tempList = [] let updatedRes = [] if (estimateContextState?.itemList.length > 0) { tempList = estimateContextState.itemList.filter((item) => !res.some((resItem) => resItem.itemId === item.itemId)) updatedRes = [...res, ...tempList] } else { updatedRes = [...res] } const groupByItemGroup = (items) => { const grouped = items.reduce((acc, item) => { const group = item.itemGroup || '기타'; if (!acc[group]) { acc[group] = { label: group, options: [] }; } acc[group].options.push({ value: item.itemId, label: `${item.itemNo} - ${item.itemName}`, ...item }); return acc; }, {}); return Object.values(grouped); }; const groupedItems = groupByItemGroup(res); setOriginDisplayItemList(groupedItems) setDisplayItemList(updatedRes) } }) //견적특이사항 API호출 //여러개 선택하면 구분자로 (、) //제품영역 팝업용 let url = '/api/estimate/special-note-list' get({ url: url }).then((res) => { if (isNotEmptyArray(res)) { setPopShowSpecialNoteList(res) } }) } const groupStyles = { groupHeading: (provided) => ({ ...provided, fontSize: '14px', fontWeight: 'bold', color: '#333', backgroundColor: '#f5f5f5', padding: '8px 12px', marginBottom: '4px', borderBottom: '2px solid #ddd' }) }; useEffect(() => { // console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan) if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid) }, [selectedPlan]) useEffect(() => { setSelectedMenu('estimate') initEstimate() }, []) useEffect(() => { //견적특이사항 API호출 //여러개 선택하면 구분자로 (、) //체크용 if (estimateContextState.resetFlag === 'Y') { let url = `/api/estimate/special-note-title-list` get({ url: url }).then((res) => { if (isNotEmptyArray(res)) { if (estimateContextState?.estimateOption) { res.map((row) => { let estimateOption = estimateContextState?.estimateOption?.split('、') row.check = false estimateOption.map((row2) => { if (row.pkgYn === '0') { // if (row2 === row.code) { // row.check = true // } if (row.code.split('、').includes(row2)) { row.check = true } } else { if (row.code.includes(row2)) { row.check = true return } } }) if (row.code === 'ATTR003') { //row.check = true } if (row.code === 'ATTR007') { //row.check = true } if (estimateContextState.estimateType === 'YJOD') { if (row.code === 'ATTR002') { row.check = true } } }) setSpecialNoteList(res) setSpecialNoteFirstFlg(true) } } }) } if (!specialNoteFirstFlg) { let url = `/api/estimate/special-note-title-list` get({ url: url }).then((res) => { if (isNotEmptyArray(res)) { if (estimateContextState?.estimateOption) { res.map((row) => { let estimateOption = estimateContextState?.estimateOption?.split('、') row.check = false estimateOption.map((row2) => { if (row.pkgYn === '0') { // if (row2 === row.code) { // row.check = true // } if (row.code.split('、').includes(row2)) { row.check = true } } else { if (row.code.includes(row2)) { row.check = true return } } }) if (row.code === 'ATTR003') { //row.check = true } if (row.code === 'ATTR007') { //row.check = true } if (estimateContextState.estimateType === 'YJOD') { if (row.code === 'ATTR002') { row.check = true } } }) setSpecialNoteList(res) setSpecialNoteFirstFlg(true) } } }) } }, [estimateContextState?.estimateOption]) //API데이터로 견적일 셋팅 let begin = 1 useEffect(() => { if (begin === 1) { setStartDate(estimateContextState?.estimateDate) begin++ } }, [estimateContextState?.estimateDate]) //견적일 set useEffect(() => { let estimateDate if (startDate) { estimateDate = dayjs(startDate).format('YYYY-MM-DD') if (begin === 1) { setEstimateContextState({ estimateDate: estimateDate }) } } else { setEstimateContextState({ estimateDate: '' }) } }, [startDate]) useEffect(() => { //선택된 견적특이사항 setEstimateContextState if (isNotEmptyArray(specialNoteList)) { const liveCheckedData = specialNoteList.filter((row) => row.check === true) const data = [] for (let ele of liveCheckedData) { data.push(ele.code) } const newData = data.join('、') //저장용 보내기 setEstimateContextState({ estimateOption: newData, specialNoteList: specialNoteList, uniqueData: uniqueData }) } }, [specialNoteList]) // 견적특이사항 remark 보여주기 const settingShowContent = (code) => { setShowContentCode(code) } // 추가한 첨부파일 estimateContextState에 넣기 useEffect(() => { if (isNotEmptyArray(files)) { let fileList = [] files.map((row) => { fileList.push(row.data) setEstimateContextState({ fileList: row.data, newFileList: fileList }) }) } else { setEstimateContextState({ fileList: [], newFileList: [] }) } }, [files]) //상세에서 내려온 첨부파일 set 만들기 useEffect(() => { if (isNotEmptyArray(estimateContextState.fileList)) { //드래그영역 비워주기 setFiles([]) setOriginFiles(estimateContextState.fileList) } else { if (estimateContextState?.resetFlag === 'Y') { originReset() } else { if (originFiles.length > 0) { if (isEmptyArray(files)) { if (originFiles[0].planNo !== estimateContextState.planNo) { setOriginFiles([]) } else { let file file = originFiles.filter((item) => item.delFlg === '0') setEstimateContextState({ originFiles: file, }) setOriginFiles(file) } } } } } }, [estimateContextState?.fileList]) const originReset = () => { setOriginFiles([]) estimateContextState.resetFlag = 'N' } // 삭제누른 첨부파일 복원 const returnOriginFile = (no) => { originFiles.map((file) => { if (file.no === no) { file.delFlg = '0' } }) setOriginFiles((prev) => { return [...prev] }) setEstimateContextState({ originFiles: originFiles, }) } // 기존첨부파일 삭제 (플래그값 추가?) 저장할때 플래그값에 따라 진짜 삭제 const deleteOriginFile = (no) => { originFiles.map((file) => { if (file.no === no) { file.delFlg = '1' } }) setOriginFiles((prev) => { return [...prev] }) setEstimateContextState({ originFiles: originFiles, }) swalFire({ text: getMessage('estimate.detail.alert.delFile'), type: 'alert', }) } //가격표시 option 목록 최초세팅 && 주문분류 변경시 useEffect(() => { if (estimateContextState.estimateType !== '') { const param = { saleStoreId: estimateContextState.sapSaleStoreId, sapSalesStoreCd: estimateContextState.sapSalesStoreCd, docTpCd: estimateContextState?.estimateType, } const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` get({ url: apiUrl }).then((res) => { if (isNotEmptyArray(res?.data)) { setStorePriceList(res.data) } setItemChangeYn(true) }) if (estimateContextState.estimateType === 'YJSS') { if (specialNoteList.length > 0) { specialNoteList.map((item) => { if (item.code === 'ATTR002') { item.check = false } }) } //YJSS UNIT_PIRCE로 프라이싱 실행 if (handlePricingFlag) { handlePricing('UNIT_PRICE') } } else { if (specialNoteList.length > 0) { specialNoteList.map((item) => { if (item.code === 'ATTR002') { item.check = true } }) } //YJOD로 돌아가도 UNIT_PRICE로 프라이싱 실행해서 정가로 셋팅 if (handlePricingFlag) { handlePricing('UNIT_PRICE') } } } }, [estimateContextState?.estimateType]) useEffect(() => { if (estimateContextState?.priceCd) { setShowPriceCd(estimateContextState.priceCd) } }, [estimateContextState?.priceCd]) //가격 표시 option 변경 이벤트 const onChangeStorePriceList = (priceCd) => { //프라이싱 했을때 priceCd setEstimateContextState //화면에 보여지는 값은 showPriceCd로 관리 setShowPriceCd(priceCd) } const makeUniqueSpecialNoteCd = (itemList) => { let pushData = [] let uniquSet = new Set() itemList.forEach((item) => { if (item.delFlg === '0') { if (item.specialNoteCd) { let splitData = item.specialNoteCd.split('、') splitData.forEach((note) => { if (!uniquSet.has(note)) { uniquSet.add(note) pushData.push(note) } }) setSpecialNoteFirstFlg(false) } } }) setUniqueData(pushData) specialNoteList.map((item) => { if (item.pkgYn === '1') { let flg = '0' let codes = item.code for (let i = 0; i < pushData.length; i++) { if (codes.indexOf(pushData[i]) > -1) { flg = '1' } } if (flg === '1') { item.check = true } else { item.check = false } } else { let codes = item.code.split('、') let flg = '0' if (codes.length > 1) { for (let i = 0; i < pushData.length; i++) { if (codes.indexOf(pushData[i]) > -1) { flg = '1' } } if (flg === '1') { item.check = true } else { item.check = false } } } }) setEstimateContextState({ specialNoteList: specialNoteList, uniqueData: uniqueData, }) } //Pricing 버튼클릭시 confirm 노출 const handlePricingBtn = (showPriceCd) => { swalFire({ text: getMessage('estimate.detail.showPrice.pricingBtn.confirm'), type: 'confirm', icon: 'warning', confirmFn: () => { handlePricing(showPriceCd) setEstimateContextState({ pricingFlag:true }) }, }) } //Pricing 버튼 const handlePricing = async (showPriceCd) => { const param = { saleStoreId: estimateContextState.sapSaleStoreId, sapSalesStoreCd: estimateContextState.sapSalesStoreCd, docTpCd: estimateContextState.estimateType, secSapSalesStoreCd: estimateContextState.secSapSalesStoreCd?.length > 0 && showPriceCd === 'QSP_PRICE' ? estimateContextState.secSapSalesStoreCd : '', priceCd: showPriceCd, itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null), } if (param.itemIdList.length > 0) { let pass = true param.itemIdList.map((item) => { if (item.itemId === '') { pass = false } }) if (!pass) { //Pricing이 누락된 아이템이 있습니다. 제품 선택 후 Pricing을 진행해주세요. return swalFire({ text: getMessage('estimate.detail.showPrice.pricingBtn.noItemId'), type: 'alert', icon: 'warning', }) } } setIsGlobalLoading(true) await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => { let updateList = [] if (res) { if (res.status === 200) { const data = res.data //기존itemList랑 프라이싱결과랑 비교해서 단가만 업뎃 서로 갯수가 안맞을 수 있음 없는 itemId면 unitPrice 0으로 //itemId로 비교해서 salePrice만 업데이트 if (data.result.code === 200) { setEstimateContextState({ pkgAsp: data?.data?.pkgUnitPrice, }) //주택PKG단가 체인지 이벤트 발생시키기 onChangePkgAsp(data.data.pkgUnitPrice) if (isNotEmptyArray(data.data2)) { estimateContextState.itemList.map((item) => { let checkYn = false for (let i = 0; i < data.data2.length; i++) { if (data.data2[i]) { if (data.data2[i].itemId === item.itemId) { updateList.push({ ...item, openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0', unitOpenFlg: (showPriceCd === 'QSP_PRICE' && item.openFlg === '1') ? '1' : '0', salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice, saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(), }) checkYn = true break } } } if (!checkYn) { updateList.push({ ...item, salePrice: '0', saleTotPrice: '0' }) } }) setEstimateContextState({ priceCd: showPriceCd, itemList: updateList, }) setItemChangeYn(true) } } } } setIsGlobalLoading(false) }) } const getAbledItems = (items) => { return items.filter((items) => items.dispCableFlg !== '1' && items.paDispOrder === null) } const onChangeSelectAll = (e) => { if (e.target.checked) { const allCheckedSelection = new Set(getAbledItems(estimateContextState.itemList).map((item) => item.dispOrder)) setSelection(allCheckedSelection) } else { setSelection(new Set()) } } const isSelectedAll = () => { return selection.size === getAbledItems(estimateContextState.itemList).length } //아이템row 체크박스 컨트롤 const onChangeSelect = (dispOrder) => { const newSelection = new Set(selection) if (newSelection.has(dispOrder)) { newSelection.delete(dispOrder) } else { newSelection.add(dispOrder) } setSelection(newSelection) } //주택PKG input 변경 const onChangePkgAsp = (value) => { if (estimateContextState.estimateType === 'YJSS') { let newValue = (value || '0').replace(/[^0-9.]/g, '') if (newValue.length > 1) { newValue = newValue.replace(/(^0+)/, '') if (newValue.length === 0) { newValue = '0' } } const parts = newValue.split('.') if (parts.length > 2) { newValue = parts[0] + '.' + parts.slice(1).join('') } if (parts[1] && parts[1].length > 2) { newValue = parts[0] + '.' + parts[1].substring(0, 2) } let pkgAsp = normalizeDecimal(newValue || '0') //현재 PKG용량값 가져오기 let totVolKw = estimateContextState.totVolKw * 1000 // let pkgTotPrice = parseFloat(pkgAsp?.replaceAll(',', '')) * totVolKw * 1000 const pkgAspNumber = Number(normalizeDecimal(pkgAsp)) const pkgTotPrice = pkgAspNumber * totVolKw * 1000 setEstimateContextState({ pkgAsp: pkgAsp, pkgTotPrice: pkgTotPrice.toFixed(2), }) //아이템들 중 조건에 맞는애들 뽑아서 상단 공급가액 부가세 총액 수정 setItemChangeYn(true) } } // 수량 변경 const onChangeAmount = (value, dispOrder, index) => { //itemChangeFlg = 1, partAdd = 0 셋팅 let amount = Number(normalizeDigits(value)) if (isNaN(amount)) { amount = '0' } else { amount = amount.toLocaleString() } let updateList = [] let updates = {} updates.amount = amount updates.itemChangeFlg = '1' updates.partAdd = '0' updates.saleTotPrice = (Number(amount.replaceAll(',', '')) * estimateContextState.itemList[index].salePrice.replaceAll(',', '')).toLocaleString() updates.showSaleTotPrice = ( Number(amount.replaceAll(',', '')) * estimateContextState.itemList[index].salePrice?.replaceAll(',', '') ).toLocaleString() updateList = estimateContextState.itemList.map((item) => { if (item.dispOrder === dispOrder) { return { ...item, ...updates } } else if (item.paDispOrder === dispOrder) { return { ...item, ...updates, amount: (item.bomAmount * amount?.replaceAll(',', '')).toLocaleString(), saleTotPrice: '0' } } else { return item } }) setEstimateContextState({ itemList: updateList, }) setItemChangeYn(true) } // 단가 변경 const onChangeSalePrice = (value, dispOrder, index) => { //itemChangeFlg, partAdd 받아온 그대로 let salePrice = Number(normalizeDecimal(value)) if (isNaN(salePrice)) { salePrice = 0 } else { salePrice = salePrice.toLocaleString() } let updateList = [] let updates = {} updates.salePrice = salePrice updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * estimateContextState.itemList[index].amount.replaceAll(',', '')).toLocaleString() updates.showSalePrice = updates.salePrice updates.showSaleTotPrice = updates.saleTotPrice updateList = estimateContextState.itemList.map((item) => { if (item.dispOrder === dispOrder) { return { ...item, ...updates } } else { return item } }) setEstimateContextState({ itemList: updateList, }) setItemChangeYn(true) } /* 케이블 select 변경시 */ const onChangeDisplayCableItem = (value, itemList) => { itemList.map((item, index) => { if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12' && item.itemTpCd !== 'S13') { if (value !== '') { onChangeDisplayItem(value, item.dispOrder, index, true) } } }) setCableItem(value) } /* 케이블 select 변경시 */ const onChangeDisplayDoubleCableItem = (value, itemList) => { itemList.map((item, index) => { if (item.dispCableFlg === '1' && (item.itemTpCd === 'M12' || item.itemTpCd === 'S13')) { if (value !== '') { onChangeDisplayItem(value, item.dispOrder, index, true) } } }) setCableDbItem(value) } // 아이템 자동완성 검색시 아이템 추가/변경시 const onChangeDisplayItem = (itemId, dispOrder, index, flag) => { const param = { itemId: itemId, coldZoneFlg: estimateContextState?.coldRegionFlg, saltAffectedFlg: estimateContextState?.saltAreaFlg, } const apiUrl = `/api/display-item/item-detail?${queryStringFormatter(param)}` let updateList = [] let updates = {} get({ url: apiUrl }).then((res) => { updates.objectNo = objectNo updates.planNo = planNo updates.itemId = res.itemId updates.itemNo = res.itemNo updates.itemName = res.itemName updates.itemChangeFlg = '1' //무조건 1 updates.partAdd = '1' //무조건1 NEW updates.fileUploadFlg = res.fileUploadFlg updates.unit = res.unit updates.unitPrice = res.salePrice //unitPrice도 salePrice로 updates.moduleFlg = res.moduleFlg updates.pkgMaterialFlg = res.pkgMaterialFlg updates.pnowW = res.pnowW updates.salePrice = res.salePrice updates.specification = res.specification updates.unit = res.unit updates.specialNoteCd = res.spnAttrCds updates.itemGroup = res.itemGroup updates.delFlg = '0' // 삭제플래그 0 updates.saleTotPrice = (res.salePrice * estimateContextState.itemList[index].amount).toString() updates.amount = flag ? estimateContextState.itemList[index].amount : '' updates.openFlg = res.openFlg if (estimateContextState.estimateType === 'YJSS') { if (res.pkgMaterialFlg === '0') { updates.showSalePrice = '0' updates.showSaleTotPrice = '0' } } else { if (res.openFlg === '1') { updates.showSalePrice = '0' updates.showSaleTotPrice = '0' } } //104671 let bomList = res.itemBomList updateList = estimateContextState.itemList.map((item) => { if (item.dispOrder === dispOrder) { if (item?.addFlg) { return { ...item, ...updates, saleTotPrice: '' } } else { if (estimateContextState.estimateType === 'YJSS') { if (updates.pkgMaterialFlg === '1') { return { ...item, ...updates, showSalePrice: updates.salePrice } } else { return { ...item, ...updates, salePrice: '', saleTotPrice: '' } } } else { return { ...item, ...updates } } } } else if (item.paDispOrder === dispOrder) { return { ...item, delFlg: '1' } } else { return item } }) //paDispOrder if (bomList) { bomList.map((bomItem, index) => { let maxItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder)) if (maxItemDispOrder == dispOrder) { bomItem.dispOrder = (index + 1 + maxItemDispOrder).toString() bomItem.paDispOrder = dispOrder bomItem.salePrice = '0' bomItem.saleTotPrice = '0' bomItem.unitPrice = '0' bomItem.showSalePrice = '0' } else { bomItem.dispOrder = (index + 1 + Number(dispOrder)).toString() bomItem.paDispOrder = dispOrder bomItem.salePrice = '0' bomItem.saleTotPrice = '0' bomItem.unitPrice = '0' bomItem.showSalePrice = '0' } bomItem.delFlg = '0' bomItem.objectNo = objectNo bomItem.planNo = planNo bomItem.addFlg = true //봄 추가시도 addFlg bomItem.openFlg = '0' //봄 컴포넌트는 무조건 0으로 }) updateList = updateList.filter((item) => item.delFlg === '0') setEstimateContextState({ itemList: [...updateList, ...bomList], }) } else { setEstimateContextState({ itemList: updateList, }) } setItemChangeYn(true) }) } //제품 삭제 const removeItem = () => { const array = [...selection] if (isEmptyArray(array)) { return swalFire({ text: getMessage('estimate.detail.alert.selectDelItem'), type: 'alert', icon: 'warning', }) } let delList = [] estimateContextState.itemList.filter((row) => { array.map((row2) => { if (row2 === row.dispOrder) { delList.push({ ...row }) } if (row2 === row.paDispOrder) { delList.push({ ...row }) } }) }) const updateList = estimateContextState.itemList.map((item) => { const isDeleted = delList.some((row) => item.delFlg === '1' || item.dispOrder === row.dispOrder) return { ...item, delFlg: isDeleted ? '1' : '0', } }) let delCnt = 0 updateList.map((item) => { if (item.delFlg === '1') { delCnt++ } }) if (delCnt === updateList.length) { return swalFire({ text: getMessage('estimate.detail.save.requiredItem'), type: 'alert', icon: 'warning', }) } setEstimateContextState({ itemList: updateList, }) setSelection(new Set()) setItemChangeYn(true) } useEffect(() => { if (estimateContextState.resetFlag === 'Y') { makeUniqueSpecialNoteCd(estimateContextState.itemList) } if (itemChangeYn) { let totals = { totAmount: 0, totVolKw: 0, supplyPrice: 0, vatPrice: 0, totPrice: 0, addSupplyPrice: 0, pkgTotPrice: 0, } const calculateYJODTotals = (itemList) => { itemList.sort((a, b) => a.dispOrder - b.dispOrder) makeUniqueSpecialNoteCd(itemList) itemList.forEach((item) => { delete item.showSalePrice delete item.showSaleTotPrice if (item.delFlg === '0') { let amount = Number(normalizeDigits(item.amount)) || 0 let price if (amount === 0) { price = 0 } else { price = Number(item.saleTotPrice?.replaceAll(',', '')) || 0 } if (item.moduleFlg === '1') { const volKw = (item.pnowW * amount) / 1000 totals.totVolKw += volKw } totals.supplyPrice += price totals.totAmount += amount if (item.paDispOrder) { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (item.openFlg === '1') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } } }) totals.vatPrice = totals.supplyPrice * 0.1 totals.totPrice = totals.supplyPrice + totals.vatPrice } const calculateYJSSTotals = (itemList) => { itemList.sort((a, b) => a.dispOrder - b.dispOrder) makeUniqueSpecialNoteCd(itemList) itemList.forEach((item) => { if (item.delFlg === '0') { let amount = Number(normalizeDigits(item.amount)) || 0 let salePrice if (item.moduleFlg === '1') { const volKw = (item.pnowW * amount) / 1000 totals.totVolKw += volKw } if (amount === 0) { salePrice = 0 } else { salePrice = Number(item.salePrice?.replaceAll(',', '')) || 0 } totals.totAmount += amount if (item.pkgMaterialFlg === '1') { const saleTotPrice = amount * salePrice totals.addSupplyPrice += saleTotPrice } if (!item.paDispOrder) { if (item.pkgMaterialFlg === '0') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } } else { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (item.openFlg === '1') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } } }) //let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0 const pkgAsp = Number(normalizeDecimal(estimateContextState.pkgAsp)) totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000 totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice totals.vatPrice = totals.supplyPrice * 0.1 totals.totPrice = totals.supplyPrice + totals.vatPrice } if (estimateContextState.estimateType === 'YJOD') { calculateYJODTotals(estimateContextState.itemList) setEstimateContextState({ totAmount: totals.totAmount, totVolKw: totals.totVolKw.toFixed(2), supplyPrice: totals.supplyPrice.toFixed(0), //소수첫자리에서 반올림 vatPrice: totals.vatPrice.toFixed(0), //소수첫자리에서 반올림 totPrice: totals.totPrice.toFixed(0), //소수첫자리에서 반올림 }) } else if (estimateContextState.estimateType === 'YJSS') { calculateYJSSTotals(estimateContextState.itemList) setEstimateContextState({ pkgTotPrice: totals.pkgTotPrice, totAmount: totals.totAmount, totVolKw: totals.totVolKw.toFixed(2), supplyPrice: totals.supplyPrice.toFixed(0), //소수첫자리에서 반올림 vatPrice: totals.vatPrice.toFixed(0), //소수첫자리에서 반올림 totPrice: totals.totPrice.toFixed(0), //소수첫자리에서 반올림 }) } setItemChangeYn(false) } else { let totals = { totAmount: 0, totVolKw: 0, supplyPrice: 0, vatPrice: 0, totPrice: 0, addSupplyPrice: 0, pkgTotPrice: 0, } estimateContextState.itemList.sort((a, b) => a.dispOrder - b.dispOrder) makeUniqueSpecialNoteCd(estimateContextState.itemList) if (estimateContextState.estimateType === 'YJSS') { estimateContextState.itemList.forEach((item) => { if (estimateContextState.estimateType === 'YJSS' && !item.paDispOrder && item.pkgMaterialFlg === '0') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (estimateContextState.estimateType === 'YJSS' && item.openFlg === '1') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (estimateContextState.estimateType === 'YJSS' && item.paDispOrder) { item.showSalePrice = '0' item.showSaleTotPrice = '0' } }) let dispCableFlgCnt = 0 estimateContextState.itemList.forEach((item) => { if (item.delFlg === '0') { let amount = Number(normalizeDigits(item.amount)) || 0 let salePrice if (item.moduleFlg === '1') { const volKw = (item.pnowW * amount) / 1000 totals.totVolKw += volKw } if (amount === 0) { salePrice = 0 } else { salePrice = Number(item.salePrice?.replaceAll(',', '')) || 0 } totals.totAmount += amount if (item.pkgMaterialFlg === '1') { const saleTotPrice = amount * salePrice totals.addSupplyPrice += saleTotPrice } if (!item.paDispOrder) { if (item.pkgMaterialFlg === '0') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } } else { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (item.openFlg === '1') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (item.dispCableFlg === '1') { dispCableFlgCnt++ if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') { setCableDbItem(item.itemId) }else{ setCableItem(item.itemId) } } } }) if (dispCableFlgCnt === 0) { setCableItem('100038') setCableDbItem('100037') } // let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0 const pkgAsp = Number(normalizeDecimal(estimateContextState.pkgAsp)) totals.pkgTotPrice = pkgAsp * totals.totVolKw * 1000 totals.supplyPrice = totals.addSupplyPrice + totals.pkgTotPrice totals.vatPrice = totals.supplyPrice * 0.1 totals.totPrice = totals.supplyPrice + totals.vatPrice setEstimateContextState({ pkgTotPrice: totals.pkgTotPrice, totAmount: totals.totAmount, totVolKw: totals.totVolKw.toFixed(2), supplyPrice: totals.supplyPrice.toFixed(0), //소수첫자리에서 반올림 vatPrice: totals.vatPrice.toFixed(0), //소수첫자리에서 반올림 totPrice: totals.totPrice.toFixed(0), //소수첫자리에서 반올림 }) } else { estimateContextState.itemList.forEach((item) => { if (estimateContextState.estimateType === 'YJOD' && item.openFlg === '1') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (estimateContextState.estimateType === 'YJOD' && item.paDispOrder) { item.showSalePrice = '0' item.showSaleTotPrice = '0' } }) let dispCableFlgCnt = 0 estimateContextState.itemList.forEach((item) => { delete item.showSalePrice delete item.showSaleTotPrice if (item.delFlg === '0') { let amount = Number(normalizeDigits(item.amount)) || 0 let price if (amount === 0) { price = 0 } else { price = Number(item.saleTotPrice?.replaceAll(',', '')) || 0 } if (item.moduleFlg === '1') { const volKw = (item.pnowW * amount) / 1000 totals.totVolKw += volKw } totals.supplyPrice += price totals.totAmount += amount if (item.paDispOrder) { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (item.openFlg === '1') { item.showSalePrice = '0' item.showSaleTotPrice = '0' } if (item.dispCableFlg === '1') { dispCableFlgCnt++ if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') { setCableDbItem(item.itemId) }else{ setCableItem(item.itemId) } } } }) if (dispCableFlgCnt === 0) { setCableItem('100038') setCableDbItem('100037') } totals.vatPrice = totals.supplyPrice * 0.1 totals.totPrice = totals.supplyPrice + totals.vatPrice setEstimateContextState({ totAmount: totals.totAmount, totVolKw: totals.totVolKw.toFixed(2), supplyPrice: totals.supplyPrice.toFixed(0), //소수첫자리에서 반올림 vatPrice: totals.vatPrice.toFixed(0), //소수첫자리에서 반올림 totPrice: totals.totPrice.toFixed(0), //소수첫자리에서 반올림 }) } } }, [itemChangeYn, estimateContextState.itemList]) //안건명 인풋 변경 const handleBlurObjectName = (e) => { setEstimateContextState({ objectName: e.target.value }) } //담당자 인풋 변경 const handleBlurCharger = (e) => { setEstimateContextState({ charger: e.target.value }) } //비고 인풋 변경 const handleBlurRemarks = (e) => { setEstimateContextState({ remarks: e.target.value }) } useEffect(() => { setSpecialNoteFirstFlg(false) }, [estimateContextState.resetFlag]) useEffect(() => { if (estimateContextState?.itemList.length > 0) { const tempList = estimateContextState.itemList.filter((item) => !displayItemList.some((resItem) => resItem.itemId === item.itemId)) if (tempList.length > 0) { setDisplayItemList((prevList) => [...prevList, ...tempList]) } } }, [estimateContextState]) useEffect(() => { if (cableItemList.lenght > 0 || estimateContextState?.itemList.length > 0) { let dispCableFlgCnt = 0 estimateContextState.itemList.map((item) => { if (item.dispCableFlg === '1') { dispCableFlgCnt++ cableItemList.map((cable) => { if (item.itemId === cable.clRefChr1) { cable.clRefChr3 = item.itemName } }) } }) if (dispCableFlgCnt === 0) { setCableItem('100038') setCableDbItem('100037') } } }, [estimateContextState?.itemList, cableItemList]) const [agencyCustList, setAgencyCustList] = useState([]) useEffect(() => { // 952 - 2차점 특가 sapSalesStoreCd if (estimateContextState?.sapSalesStoreCd && session?.storeLvl === '1') { const param = { sapSalesStoreCd: estimateContextState.sapSalesStoreCd, } const apiUrl = `api/estimate/agency-cust-list?${queryStringFormatter(param)}` get({ url: apiUrl }).then((res) => { if (isNotEmptyArray(res?.data)) { setAgencyCustList(res?.data) } }) } }, [estimateContextState?.sapSalesStoreCd]) return (
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
{getMessage('estimate.detail.objectNo')}
{currentObjectNo} (Plan No: {currentPid})
{getMessage('estimate.detail.docNo')}
{estimateContextState.docNo}
{getMessage('estimate.detail.drawingEstimateCreateDate')}
{estimateContextState?.drawingEstimateCreateDate ? `${dayjs(estimateContextState.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
{getMessage('estimate.detail.lastEditDatetime')}
{estimateContextState?.lastEditDatetime ? `${dayjs(estimateContextState.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}
{/* 물건번호, 견적서번호, 등록일, 변경일시 끝 */} {/* 기본정보 시작 */}

{getMessage('estimate.detail.header.title')}

{/* 1차 판매점명 */} {/* 견적일 */} {/* 2차 판매점명 */} {/* 담당자 */} {/* 안건명 */} {/* 물건정보에서 입력한 메모 */} {/* 주문분류 */} {/* 지붕재・사양시공 최대4개*/} {/* 비고 */}
{getMessage('estimate.detail.saleStoreId')} {estimateContextState?.firstSaleStoreName} {getMessage('estimate.detail.estimateDate')} *
{getMessage('estimate.detail.otherSaleStoreId')} {session?.storeLvl === '1' && estimateContextState?.saleStoreLevel === '1' ? getMessage('estimate.detail.noOtherSaleStoreId') : estimateContextState?.agencySaleStoreName} {getMessage('estimate.detail.receiveUser')} *
{getMessage('estimate.detail.objectName')} *
{getMessage('estimate.detail.objectRemarks')} {estimateContextState?.objectRemarks}
{getMessage('estimate.detail.estimateType')} *
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
0 && storePriceList[0].pkgRank !== null && storePriceList[0].pkgRank !== '') || estimateContextState?.estimateType === 'YJSS' ? '' : 'none', }} > { //주문분류 setHandlePricingFlag(true) setEstimateContextState({ estimateType: e.target.value, setEstimateContextState }) }} />
{ setHandlePricingFlag(true) setEstimateContextState({ estimateType: e.target.value }) }} />
{session?.storeLvl === '100000' && agencyCustList.length > 0 ? ( //일시적으로 1 => 100000로 정리
{getMessage('estimate.detail.roofCns')} {estimateContextState?.roofMaterialIdMulti?.split('、').map((row, index) => { //지붕재 let roofList = row let roofListLength = estimateContextState?.roofMaterialIdMulti?.split('、').length let style = 'mb5' if (roofListLength == index + 1) { style = '' } //사양시공 let constructSpecificationMulti = estimateContextState?.constructSpecificationMulti?.split('、') return (
{constructSpecificationMulti ? (
) : null}
) })}
{getMessage('estimate.detail.remarks')}
{/* 파일첨부 시작 */}

{getMessage('estimate.detail.header.fileList1')}

{ setEstimateContextState({ fileFlg: e.target.checked ? '1' : '0', }) if (e.target.checked) { if (specialNoteList.length > 0) { specialNoteList.map((item) => { if (item.code === 'ATTR019') { item.check = true } }) } } else { if (specialNoteList.length > 0) { specialNoteList.map((item) => { if (item.code === 'ATTR019') { item.check = false } }) } } }} />
{estimateContextState?.northArrangement === '1' && (
{getMessage('estimate.detail.dragFileGuide')}
)}
{getMessage('estimate.detail.header.fileList1')}
{/* 첨부파일 목록 시작 */} {originFiles.length > 0 && (
{getMessage('estimate.detail.header.fileList2')}
    {originFiles.map((originFile) => { return (
  • handleEstimateFileDownload(originFile)} > {originFile.faileName}
    {originFile.faileName}
  • ) })}
)} {/* 첨부파일 목록 끝 */} {/* 파일첨부 끝 */} {/* 견적특이사항 시작 */}
{/*
*/} {/*

{getMessage('estimate.detail.header.specialEstimate')}

*/} {/*
*/} {/* */} {/* */} {/*
*/} {/*
*/}
{/* 견적 특이사항 코드영역시작 */}
{/* SpecialNoteList반복문 */} {specialNoteList.length > 0 && specialNoteList.map((row) => { return (
{ setSpecialNoteList((specialNote) => specialNote.map((temp) => (temp.code === row.code ? { ...temp, check: !temp.check } : temp)), ) }} />
{ settingShowContent(row.code) }} > {row.codeNm}
) })}
{/* 견적특이사항 선택한 내용 영역시작 */}
{specialNoteList.map((row) => { if (row.code === showContentCode) { let showcontent = popShowSpecialNoteList.find((item) => { return item.code === showContentCode }) if (isObjectNotEmpty(showcontent)) { return (
{showcontent.codeNm}
) } else { let pushData = [] popShowSpecialNoteList.map((item) => { let option = showContentCode.split('、') option.map((item2) => { if (item.code === item2) { pushData.push(item) } }) }) //제품에 있는 특이사항만 보여주기 제품에 특이사항이 없으면 전부 let filterData = pushData.filter((item) => uniqueData.includes(item.code)) if (filterData.length > 0) { return filterData.map((item) => (
{item.codeNm}
)) } else { return pushData.map((item) => (
{item.codeNm}
)) } } } })}
{/* 견적특이사항 선택한 내용 영역끝 */}
{/* 견적 특이사항 코드영역 끝 */} {/* 견적특이사항 영역끝 */} {/* 제품정보 시작 */}

{getMessage('estimate.detail.header.specialEstimateProductInfo')}

{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}
{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}
{convertNumberToPriceDecimalToFixed(estimateContextState?.supplyPrice, 0)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}
{convertNumberToPriceDecimalToFixed(estimateContextState?.vatPrice, 0)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}
{convertNumberToPriceDecimalToFixed(estimateContextState?.totPrice, 0)}
{/* YJOD면 아래영역 숨김 */}
{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice')} *
{ onChangePkgAsp(e.target.value) }} />
{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')} {convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)} {getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')} {convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}
{/* 제품정보 끝 */} {/* 가격표시영역시작 */}
{getMessage('estimate.detail.header.showPrice')}
{session?.storeLvl === '1' ? ( ) : ( )}
{getMessage('estimate.detail.header.singleCable')}
{getMessage('estimate.detail.header.doubleCable')}
  • {getMessage('estimate.detail.showPrice.description1')}
  • {getMessage('estimate.detail.showPrice.description2')}
  • {getMessage('estimate.detail.showPrice.description3')}
  • {getMessage('estimate.detail.showPrice.description4')}
{/* 가격표시영역끝 */} {/* html테이블시작 */}
{estimateContextState?.itemList.length > 0 && estimateContextState.itemList.map((item, index) => { if (item.delFlg === '0') { return ( ) } else { return null } })}
{getMessage('estimate.detail.itemTableHeader.dispOrder')} {getMessage('estimate.detail.itemTableHeader.itemId')} {getMessage('estimate.detail.itemTableHeader.itemNo')} {getMessage('estimate.detail.itemTableHeader.amount')} {getMessage('estimate.detail.itemTableHeader.unit')} {getMessage('estimate.detail.itemTableHeader.salePrice')} {getMessage('estimate.detail.itemTableHeader.saleTotPrice')}
onChangeSelect(item.dispOrder)} checked={!!selection.has(item.dispOrder)} />
{item?.dispOrder}
{item.dispCableFlg !== '1' ? ( x.clRefChr3 + ' (' + x.clRefChr1 + ')'} getOptionValue={(x) => x.clRefChr1} components={{ SingleValue: ({ children, ...props }) => { return {(item.itemTpCd === 'M12' || item.itemTpCd === 'S13')? item.itemName : props.data.clRefChr3} }, }} isClearable={false} isDisabled={true} value={cableItemList.filter(function (option) { return (item.itemTpCd === 'M12' || item.itemTpCd === 'S13' )? item.itemId : option.clRefChr1 === item.itemId })} /> )}
{item?.itemChangeFlg === '1' && (
)}
{item?.itemNo}
{item?.fileUploadFlg === '1' && } {item?.specialNoteCd && ( )}
{ onChangeAmount(e.target.value, item.dispOrder, index) }} maxLength={6} />
{item.unit}
{ onChangeSalePrice(e.target.value, item.dispOrder, index) }} maxLength={12} />
{item.openFlg === '1' && (
)}
{item?.openFlg === '1' ? 'OPEN' : convertNumberToPriceDecimal( item?.showSaleTotPrice === '0' ? null : item?.amount === '' ? null : item?.saleTotPrice === '0' ? null : item?.saleTotPrice?.replaceAll(',', ''), )}
{/* html테이블끝 */}
{/* 기본정보끝 */}
{productFeaturesPopupOpen && ( )}
) }