'use client' import { useEffect, useState, useContext } from 'react' import { useRecoilValue } from 'recoil' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { useMessage } from '@/hooks/useMessage' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import SingleDatePicker from '../common/datepicker/SingleDatePicker' import EstimateFileUploader from './EstimateFileUploader' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' import { 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 } from '@/util/common-utils' import ProductFeaturesPop from './popup/ProductFeaturesPop' import { v4 as uuidv4 } from 'uuid' export default function Estimate({ params }) { const fixedKey = 'itemKey' const [itemChangeYn, setItemChangeYn] = useState(false) const { session } = useContext(SessionContext) const [objectNo, setObjectNo] = useState('') //물건번호 const [planNo, setPlanNo] = useState('') //플랜번호 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(false) //아이템 자동완성 리스트 const [displayItemList, setDisplayItemList] = useState([]) //공통코드 const { findCommonCode } = useCommonCode() const [honorificCodeList, setHonorificCodeList] = useState([]) //경칭 공통코드 const [storePriceList, setStorePriceList] = useState([]) //가격표시 option const [startDate, setStartDate] = useState(new Date()) const singleDatePickerProps = { startDate, setStartDate, } const objectRecoil = useRecoilValue(floorPlanObjectState) //견적서 상세데이터 const { state, setState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid) //견적특이사항 List const [specialNoteList, setSpecialNoteList] = useState([]) const globalLocaleState = useRecoilValue(globalLocaleStore) const { get, promisePost } = useAxios(globalLocaleState) const { getMessage } = useMessage() const { setMenuNumber } = useCanvasMenu() //새로 추가한 첨부파일 props const fileUploadProps = { uploadFiles: files, setUploadFiles: setFiles, } useEffect(() => { setMenuNumber(5) setObjectNo(objectRecoil.floorPlanObjectNo) setPlanNo(params.pid) // 공통코드 const code1 = findCommonCode(200800) if (code1 != null) { setHonorificCodeList(code1) } //아이템 자동완성 목록 가져오기 const param = { saleStoreId: session.storeId, } const apiUrl = `/api/display-item/item-list?${queryStringFormatter(param)}` get({ url: apiUrl }).then((res) => { if (res.length > 0) { setDisplayItemList(res) } }) }, []) useEffect(() => { //견적특이사항 API호출 //여러개 선택하면 구분자로 (、) let url = `/api/estimate/special-note-list` get({ url: url }).then((res) => { if (isNotEmptyArray(res)) { if (state?.estimateOption) { res.map((row) => { let estimateOption = state?.estimateOption?.split('、') row.text = false estimateOption.map((row2) => { if (row2 === row.code) { row.text = true } }) }) setSpecialNoteList(res) } } }) }, [state?.estimateOption]) //견적일 set useEffect(() => { let estimateDate = dayjs(startDate).format('YYYY-MM-DD') setState({ estimateDate: estimateDate }) }, [startDate]) useEffect(() => { //선택된 견적특이사항 setState if (isNotEmptyArray(specialNoteList)) { const liveCheckedData = specialNoteList.filter((row) => row.text === true) const data = [] for (let ele of liveCheckedData) { data.push(ele.code) } const newData = data.join('、') setState({ estimateOption: newData }) } }, [specialNoteList]) // 견적특이사항 remark 보여주기 const settingShowContent = (code, event) => { setShowContentCode(code) event.stopPropagation() } // 추가한 첨부파일 state에 넣기 useEffect(() => { if (isNotEmptyArray(files)) { files.map((row) => { setState({ fileList: row.data }) }) } else { setState({ fileList: [] }) } }, [files]) //상세에서 내려온 첨부파일 set 만들기 useEffect(() => { if (isNotEmptyArray(state.fileList)) { setOriginFiles(state.fileList) } }, [state?.fileList]) // 기존첨부파일 삭제 const deleteOriginFile = async (objectNo, no) => { const delParams = { userId: session.userId, objectNo: objectNo, no: no, } await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => { if (res.status === 204) { setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no)) setState({ fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no), }) } }) } //가격표시 option 목록 최초세팅 && 주문분류 변경시 useEffect(() => { if (state.estimateType !== '') { const param = { saleStoreId: session.storeId, sapSalesStoreCd: session.custCd, docTpCd: state?.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) } }, [state?.estimateType]) useEffect(() => { if (state?.priceCd) { setShowPriceCd(state.priceCd) } }, [state?.priceCd]) //가격 표시 option 변경 이벤트 const onChangeStorePriceList = (priceCd) => { const param = { saleStoreId: session.storeId, sapSalesStoreCd: session.custCd, docTpCd: priceCd, } //가격표시 바꾸만헀을때는 tempPriceCd에 바꾼값 관리 불필요? //프라이싱 했을때 priceCd setState //화면에 보여지는 값은 showPriceCd로 관리 setShowPriceCd(priceCd) //setState({ // tempPriceCd: priceCd, //}) const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` get({ url: apiUrl }).then((res) => { if (isNotEmptyArray(res?.data)) { setStorePriceList(res.data) } }) } //Pricing 버튼 const handlePricing = async (priceCd) => { const param = { saleStoreId: session.storeId, sapSalesStoreCd: session.custCd, docTpCd: state.estimateType, priceCd: priceCd, //itemIdList: state.itemList, //아이템 최초정보로 호출 delFlg 0인거만.. itemIdList: state.itemList.filter((item) => item.delFlg === '0'), } if (param.itemIdList.length > 0) { let pass = true param.itemIdList.map((item) => { if (item.itemId === '') { pass = false } }) if (!pass) { //Pricing이 누락된 아이템이 있습니다. Pricing을 진행해주세요. return alert(getMessage('estimate.detail.showPrice.pricingBtn.noItemId')) } } 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로 비교해서 단가정보만 업데이트 if (data.result.code === 200) { if (isNotEmptyArray(data.data2)) { state.itemList.map((item) => { let checkYn = false data.data2.map((item2) => { if (item2.itemId === item.itemId) { updateList.push({ ...item, unitPrice: item2.unitPrice }) checkYn = true } }) if (!checkYn) { updateList.push({ ...item, unitPrice: '0' }) } }) setState({ priceCd: priceCd, itemList: updateList, }) setItemChangeYn(true) } } } } }) } //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 (state.estimateType === 'YJSS') { let pkgAsp = Number(value.replace(/[^0-9]/g, '').replaceAll(',', '')) if (isNaN(pkgAsp)) { pkgAsp = 0 } else { pkgAsp = pkgAsp.toLocaleString() } //현재 PKG용량값 가져오기 let totVolKw = state.totVolKw * 1000 let pkgTotPrice = pkgAsp * totVolKw setState({ pkgAsp: pkgAsp, pkgTotPrice: pkgTotPrice.toFixed(3), }) } } // 수량 변경 const onChangeAmount = (value, dispOrder, index) => { //itemChangeFlg = 1, partAdd = 0 셋팅 let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', '')) 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(',', '')) * state.itemList[index].salePrice.replaceAll(',', '')).toLocaleString() updateList = state.itemList.map((item) => { if (item.dispOrder === dispOrder) { return { ...item, ...updates } } else { return item } }) setState({ itemList: updateList, }) setItemChangeYn(true) } // 단가 변경 const onChangeSalePrice = (value, dispOrder, index) => { //itemChangeFlg, partAdd 받아온 그대로 let salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', '')) if (isNaN(salePrice)) { salePrice = 0 } else { salePrice = salePrice.toLocaleString() } let updateList = [] let updates = {} updates.salePrice = salePrice updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * state.itemList[index].amount.replaceAll(',', '')).toLocaleString() updateList = state.itemList.map((item) => { if (item.dispOrder === dispOrder) { return { ...item, ...updates } } else { return item } }) setState({ itemList: updateList, }) setItemChangeYn(true) } // 아이템 자동완성 검색시 아이템 변경 const onChangeDisplayItem = (itemId, dispOrder, index) => { const param = { itemId: itemId, } const apiUrl = `/api/display-item/item-detail?${queryStringFormatter(param)}` let updateList = [] let updates = {} get({ url: apiUrl }).then((res) => { // console.log('아이템상세정보:::::::', 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 * state.itemList[index].amount updates.saleTotPrice = '0' //추가때는 수량을 안받아서 합계를 무조건 0으로 //104671 let bomList = res.itemBomList updateList = state.itemList.map((item) => { if (item.dispOrder === dispOrder) { return { ...item, ...updates } } else if (item.paDispOrder === dispOrder) { return { ...item, delFlg: '1' } } else { return item } }) //paDispOrder if (bomList) { bomList.map((bomItem, index) => { let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) bomItem.dispOrder = index + 1 + newItemDispOrder bomItem.delFlg = '0' bomItem.objectNo = objectNo bomItem.planNo = planNo }) setState({ itemList: [...updateList, ...bomList], }) } else { setState({ itemList: updateList, }) } setItemChangeYn(true) }) } //제품 삭제 const removeItem = () => { const array = [...selection] let delList = [] state.itemList.filter((row) => { array.map((row2) => { if (row2 === row.dispOrder) { delList.push({ ...row }) } if (row2 === row.paDispOrder) { delList.push({ ...row }) } }) }) const updateList = state.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 alert(getMessage('estimate.detail.save.requiredItem')) } setState({ itemList: updateList, }) setSelection(new Set()) setItemChangeYn(true) } useEffect(() => { if (itemChangeYn) { let totAmount = 0 let totVolKw = 0 let supplyPrice = 0 let vatPrice = 0 let totPrice = 0 let addPkgPrice = 0 if (state.estimateType === 'YJOD') { state.itemList.map((item) => { if (item.delFlg === '0') { const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', '')) const price = Number(item.saleTotPrice.replaceAll(',', '')) if (item.moduleFlg === '1') { //용량(Kw)은 모듈플래그 1만 합산 const volKw = (item.pnowW * amount) / 1000 totVolKw += volKw } // const price totAmount += amount supplyPrice += price } }) vatPrice = supplyPrice * 0.1 totPrice = supplyPrice + vatPrice setState({ totAmount: totAmount, totVolKw: totVolKw.toFixed(3), supplyPrice: supplyPrice.toFixed(3), vatPrice: vatPrice.toFixed(3), totPrice: totPrice.toFixed(3), }) } else { //YJSS state.itemList.map((item) => { if (item.delFlg === '0') { const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', '')) const price = Number(item.saleTotPrice.replaceAll(',', '')) const salePrice = Number(item.salePrice.replaceAll(',', '')) if (item.moduleFlg === '1') { //용량(Kw)은 모듈플래그 1만 합산 const volKw = (item.pnowW * amount) / 1000 totVolKw += volKw } if (item.pkgMaterialFlg === '1') { const pkgPrice = amount * salePrice //YJSS는 PKG제외상품들만 모아서 수량 * 단가를 공급가액에 추가로 더해줌 addPkgPrice += pkgPrice } // const price totAmount += amount supplyPrice += price } }) vatPrice = supplyPrice * 0.1 totPrice = supplyPrice + vatPrice setState({ totAmount: totAmount, totVolKw: totVolKw.toFixed(3), supplyPrice: supplyPrice.toFixed(3), vatPrice: vatPrice.toFixed(3), totPrice: totPrice.toFixed(3), }) } setItemChangeYn(false) } }, [itemChangeYn, state.itemList]) return (
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
{getMessage('estimate.detail.objectNo')}
{objectNo} (Plan No: {planNo})
{getMessage('estimate.detail.docNo')}
{state.docNo}
{getMessage('estimate.detail.drawingEstimateCreateDate')}
{state?.drawingEstimateCreateDate ? `${dayjs(state.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''}
{getMessage('estimate.detail.lastEditDatetime')}
{state?.lastEditDatetime ? `${dayjs(state.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}
{/* 물건번호, 견적서번호, 등록일, 변경일시 끝 */} {/* 기본정보 시작 */}

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

{/* 1차 판매점명 */} {/* 견적일 */} {/* 2차 판매점명 */} {/* 담당자 */} {/* 안건명 */} {/* 물건정보에서 입력한 메모 */} {/* 주문분류 */} {/* 지붕재・사양시공 최대4개*/} {/* 비고 */}
{getMessage('estimate.detail.saleStoreId')} {state?.firstSaleStoreName} {getMessage('estimate.detail.estimateDate')} *
{getMessage('estimate.detail.otherSaleStoreId')} {state?.agencySaleStoreName} {getMessage('estimate.detail.receiveUser')} *
{ //담당자 charger setState({ charger: e.target.value }) }} />
{getMessage('estimate.detail.objectName')} *
{ //안건명 objectName setState({ objectName: e.target.value }) }} />
{getMessage('estimate.detail.objectRemarks')} {state?.objectRemarks}
{getMessage('estimate.detail.estimateType')} *
{ //주문분류 setState({ estimateType: e.target.value }) }} />
{ setState({ estimateType: e.target.value }) }} />
{getMessage('estimate.detail.roofCns')} {state?.roofMaterialIdMulti?.split('、').map((row, index) => { //지붕재 let roofList = row let roofListLength = state?.roofMaterialIdMulti?.split('、').length let style = 'mb5' if (roofListLength == index + 1) { style = '' } //사양시공 let constructSpecificationMulti = state?.constructSpecificationMulti?.split('、') return ( <>
) })}
{getMessage('estimate.detail.remarks')}
{ //비고 setState({ remarks: e.target.value }) }} />
{/* 파일첨부 시작 */}

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

{ setState({ fileFlg: e.target.checked ? '1' : '0', }) }} />
{getMessage('estimate.detail.header.fileList1')}
{/* 첨부파일 목록 시작 */}
{getMessage('estimate.detail.header.fileList2')}
    {originFiles.length > 0 && originFiles.map((originFile) => { return (
  • handleEstimateFileDownload(originFile)}> {originFile.faileName}
  • ) })}
{/* 첨부파일 목록 끝 */} {/* 파일첨부 끝 */} {/* 견적특이사항 시작 */}

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

{getMessage('estimate.detail.header.specialEstimateProductInfo')}
{/* 견적 특이사항 코드영역시작 */}
{/* SpecialNoteList반복문 */} {specialNoteList.length > 0 && specialNoteList.map((row) => { return (
{ settingShowContent(row.code, event) }} >
{ setSpecialNoteList((specialNote) => specialNote.map((temp) => (temp.code === row.code ? { ...temp, text: !temp.text } : temp)), ) settingShowContent(row.code, event) }} />
) })}
{/* 견적특이사항 선택한 내용 영역시작 */}
{specialNoteList.map((row) => { if (row.code === showContentCode) { return (
{row.codeNm}
) } })}
{/* 견적특이사항 선택한 내용 영역끝 */}
{/* 견적 특이사항 코드영역 끝 */} {/* 견적특이사항 영역끝 */} {/* 제품정보 시작 */}

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

{getMessage('estimate.detail.sepcialEstimateProductInfo.totAmount')}
{convertNumberToPriceDecimal(state?.totAmount)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}
{convertNumberToPriceDecimal(state?.totVolKw)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}
{convertNumberToPriceDecimal(state?.supplyPrice)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}
{convertNumberToPriceDecimal(state?.vatPrice)}
{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}
{convertNumberToPriceDecimal(state?.totPrice)}
{/* YJOD면 아래영역 숨김 */}
{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice')} *
{ onChangePkgAsp(e.target.value) }} />
{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')} {convertNumberToPriceDecimal(state?.totVolKw)} {getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')} {convertNumberToPriceDecimal(state?.pkgTotPrice)}
{/* 제품정보 끝 */} {/* 가격표시영역시작 */}
{getMessage('estimate.detail.header.showPrice')}
{session?.storeLvl === '1' ? ( ) : ( )}
  • {getMessage('estimate.detail.showPrice.description1')}
  • {getMessage('estimate.detail.showPrice.description2')}
  • {getMessage('estimate.detail.showPrice.description3')}
  • {getMessage('estimate.detail.showPrice.description4')}
{/*
{/* 가격표시영역끝 */} {/* html테이블시작 */}
{state?.itemList.length > 0 && state.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?.itemNo}
{item?.fileUploadFlg === '1' && } {item?.specialNoteCd && ( )}
{ onChangeAmount(e.target.value, item.dispOrder, index) }} />
{item.unit}
{ onChangeSalePrice(e.target.value, item.dispOrder, index) }} />
{/*
OPEN아이콘 처리
*/}
{convertNumberToPriceDecimal(item?.saleTotPrice.replaceAll(',', ''))}
{/* html테이블끝 */}
{/* 기본정보끝 */}
{productFeaturesPopupOpen && ( )}
) }