2025-12-22 13:19:09 +09:00

2196 lines
85 KiB
JavaScript

'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 (
<div className="sub-content estimate">
<div className="sub-content-inner">
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */}
<div className="sub-content-box">
<div className="sub-table-box">
<div className="estimate-list-wrap one">
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
<div className="estimate-name">
{currentObjectNo} (Plan No: {currentPid})
</div>
</div>
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div>
<div className="estimate-name">{estimateContextState.docNo}</div>
</div>
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div>
<div className="estimate-name">
{estimateContextState?.drawingEstimateCreateDate
? `${dayjs(estimateContextState.drawingEstimateCreateDate).format('YYYY.MM.DD')}`
: ''}
</div>
</div>
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div>
<div className="estimate-name">
{estimateContextState?.lastEditDatetime ? `${dayjs(estimateContextState.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}
</div>
</div>
</div>
</div>
</div>
{/* 물건번호, 견적서번호, 등록일, 변경일시 끝 */}
{/* 기본정보 시작 */}
<div className="sub-content-box">
<div className="sub-table-box">
<div className="table-box-title-wrap">
<div className="title-wrap">
<h3>{getMessage('estimate.detail.header.title')}</h3>
</div>
</div>
<div className="common-table bt-able">
<table>
<colgroup>
<col style={{ width: '160px' }} />
<col />
<col style={{ width: '160px' }} />
<col />
</colgroup>
<tbody>
<tr>
{/* 1차 판매점명 */}
<th>{getMessage('estimate.detail.saleStoreId')}</th>
<td>{estimateContextState?.firstSaleStoreName}</td>
{/* 견적일 */}
<th>
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
</th>
<td>
<div className="date-picker" style={{ width: '350px' }}>
<SingleDatePicker {...singleDatePickerProps} />
</div>
</td>
</tr>
<tr>
{/* 2차 판매점명 */}
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
<td>
{session?.storeLvl === '1' && estimateContextState?.saleStoreLevel === '1'
? getMessage('estimate.detail.noOtherSaleStoreId')
: estimateContextState?.agencySaleStoreName}
</td>
{/* 담당자 */}
<th>
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
</th>
<td>
<div className="input-wrap" style={{ width: '350px' }}>
<input
type="text"
className="input-light"
value={estimateContextState?.charger || ''}
onBlur={handleBlurCharger}
onChange={handleBlurCharger}
/>
</div>
</td>
</tr>
<tr>
{/* 안건명 */}
<th>
{getMessage('estimate.detail.objectName')} <span className="important">*</span>
</th>
<td colSpan={3}>
<div className="form-flex-wrap">
<div className="input-wrap mr5" style={{ width: '610px' }}>
<input
type="text"
className="input-light"
value={estimateContextState?.objectName || ''}
onBlur={handleBlurObjectName}
onChange={handleBlurObjectName}
/>
</div>
<div className="select-wrap" style={{ width: '200px' }}>
<Select
id="objectNameOmit"
instanceId="objectNameOmit"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={honorificCodeList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
setEstimateContextState({ objectNameOmit: e.clCodeNm })
} else {
setEstimateContextState({ objectNameOmit: '' })
}
}}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={true}
isSearchable={false}
value={honorificCodeList.filter(function (option) {
return option.clCodeNm === estimateContextState.objectNameOmit
})}
/>
</div>
</div>
</td>
</tr>
<tr>
{/* 물건정보에서 입력한 메모 */}
<th>{getMessage('estimate.detail.objectRemarks')}</th>
<td colSpan={3}>{estimateContextState?.objectRemarks}</td>
</tr>
<tr>
{/* 주문분류 */}
<th>
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
</th>
<td colSpan={3}>
<div className="form-flex-wrap">
<div className="radio-wrap">
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
<div
className="d-check-radio light mr10"
style={{
display:
(isNotEmptyArray(storePriceList) > 0 && storePriceList[0].pkgRank !== null && storePriceList[0].pkgRank !== '') ||
estimateContextState?.estimateType === 'YJSS'
? ''
: 'none',
}}
>
<input
type="radio"
name="estimateType"
id="YJSS"
value={'YJSS'}
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
onChange={(e) => {
//주문분류
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value, setEstimateContextState })
}}
/>
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
onChange={(e) => {
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
</div>
{session?.storeLvl === '100000' && agencyCustList.length > 0 ? ( //일시적으로 1 => 100000로 정리
<div className="form-flex-select ml10">
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
<div className="select-wrap" style={{ width: '400px' }}>
<Select
id="agencyName"
instanceId="agencyName"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={agencyCustList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
setEstimateContextState({ secSapSalesStoreCd: e.sapSalesStoreCd })
} else {
setEstimateContextState({ secSapSalesStoreCd: '' })
}
}}
getOptionLabel={(x) => x.sapSalesStoreNm}
getOptionValue={(x) => x.sapSalesStoreCd}
isClearable={true}
isSearchable={true}
value={agencyCustList.filter(function (option) {
return option.sapSalesStoreCd === estimateContextState.secSapSalesStoreCd
})}
/>
</div>
</div>
) : (
''
)}
</div>
</td>
</tr>
<tr>
{/* 지붕재・사양시공 최대4개*/}
<th>{getMessage('estimate.detail.roofCns')}</th>
<td colSpan={3}>
{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 (
<div className={`form-flex-wrap ${style}`} key={`roof_${index}_${row}`}>
<div className="input-wrap mr5" style={{ width: '610px' }}>
<input type="text" className="input-light" value={roofList || ''} readOnly />
</div>
{constructSpecificationMulti ? (
<div className="input-wrap" style={{ width: '200px' }}>
<input type="text" className="input-light" value={constructSpecificationMulti[index] || ''} readOnly />
</div>
) : null}
</div>
)
})}
</td>
</tr>
<tr>
{/* 비고 */}
<th>{getMessage('estimate.detail.remarks')}</th>
<td colSpan={3}>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={estimateContextState?.remarks || ''}
onBlur={handleBlurRemarks}
onChange={handleBlurRemarks}
/>
</div>
</td>
</tr>
</tbody>
</table>
</div>
{/* 파일첨부 시작 */}
<div className="table-box-title-wrap">
<div className="title-wrap">
<h3>{getMessage('estimate.detail.header.fileList1')}</h3>
<div className="d-check-box light mr5">
<input
type="checkbox"
id="next"
checked={estimateContextState?.fileFlg === '0' || estimateContextState?.fileFlg === null ? false : true}
onChange={(e) => {
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
}
})
}
}
}}
/>
<label htmlFor="next" style={{ color: '#101010' }}>
{getMessage('estimate.detail.fileFlg')}
</label>
</div>
{estimateContextState?.northArrangement === '1' && (
<div className="drag-file-guide">{getMessage('estimate.detail.dragFileGuide')}</div>
)}
</div>
</div>
<div className="common-table mb10">
<table>
<colgroup>
<col style={{ width: '160px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('estimate.detail.header.fileList1')}</th>
<td>
<EstimateFileUploader {...fileUploadProps} />
</td>
</tr>
</tbody>
</table>
</div>
{/* 첨부파일 목록 시작 */}
{originFiles.length > 0 && (
<div className="common-table bt-able">
<table>
<colgroup>
<col style={{ width: '160px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('estimate.detail.header.fileList2')}</th>
<td>
<div className="drag-file-box">
<ul className="file-list">
{originFiles.map((originFile) => {
return (
<li className="file-item" key={originFile.no}>
<div className="file-item-wrap">
<span
style={{ display: originFile.delFlg === '0' ? '' : 'none' }}
onClick={() => handleEstimateFileDownload(originFile)}
>
{originFile.faileName}
<button
type="button"
className="delete"
onClick={(e) => {
deleteOriginFile(originFile.no)
e.stopPropagation()
}}
></button>
</span>
<div className="return-wrap" style={{ display: originFile.delFlg !== '0' ? '' : 'none' }}>
<span className="return">{originFile.faileName}</span>
<button
type="button"
className="return-btn"
onClick={(e) => {
returnOriginFile(originFile.no)
e.stopPropagation()
}}
>
<i className="return-ico"></i>
{getMessage('estimate.detail.fileList2.btn.return')}
</button>
</div>
</div>
</li>
)
})}
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</div>
)}
{/* 첨부파일 목록 끝 */}
{/* 파일첨부 끝 */}
{/* 견적특이사항 시작 */}
<div className="table-box-title-wrap">
{/*<div className="title-wrap">*/}
{/* <h3 className="product">{getMessage('estimate.detail.header.specialEstimate')}</h3>*/}
{/* <div className="estimate-check-btn">*/}
{/* <button className={`estimate-arr-btn down mr5 ${hidden ? '' : 'on'}`} onClick={() => setHidden(false)}></button>*/}
{/* <button className={`estimate-arr-btn up ${hidden ? 'on' : ''}`} onClick={() => setHidden(true)}></button>*/}
{/* </div>*/}
{/*</div>*/}
</div>
{/* 견적 특이사항 코드영역시작 */}
<div className={`estimate-check-wrap ${hidden ? 'hide' : ''}`}>
<div className="estimate-check-inner">
<div className="special-note-check-wrap">
{/* SpecialNoteList반복문 */}
{specialNoteList.length > 0 &&
specialNoteList.map((row) => {
return (
<div key={row.code} className="special-note-check-item">
<div className="special-note-check-box">
<div className="d-check-box light">
<input
type="checkbox"
id={row.code}
checked={!!row.check}
disabled={row.code === 'ATTR001' || row.pkgYn === '1' ? true : false}
onChange={() => {
setSpecialNoteList((specialNote) =>
specialNote.map((temp) => (temp.code === row.code ? { ...temp, check: !temp.check } : temp)),
)
}}
/>
<label htmlFor={row.code}></label>
</div>
<span
className="check-name"
onClick={() => {
settingShowContent(row.code)
}}
>
{row.codeNm}
</span>
</div>
</div>
)
})}
</div>
{/* 견적특이사항 선택한 내용 영역시작 */}
<div className="calculation-estimate">
{specialNoteList.map((row) => {
if (row.code === showContentCode) {
let showcontent = popShowSpecialNoteList.find((item) => {
return item.code === showContentCode
})
if (isObjectNotEmpty(showcontent)) {
return (
<dl key={row.code}>
<dt>{showcontent.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: showcontent.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl>
)
} 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) => (
<dl key={item.code}>
<dt>{item.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: item.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl>
))
} else {
return pushData.map((item) => (
<dl key={item.code}>
<dt>{item.codeNm}</dt>
<dd dangerouslySetInnerHTML={{ __html: item.remarks }} style={{ whiteSpace: 'pre-wrap' }}></dd>
</dl>
))
}
}
}
})}
</div>
{/* 견적특이사항 선택한 내용 영역끝 */}
</div>
</div>
{/* 견적 특이사항 코드영역 끝 */}
{/* 견적특이사항 영역끝 */}
{/* 제품정보 시작 */}
<div className="table-box-title-wrap">
<div className="title-wrap">
<h3>{getMessage('estimate.detail.header.specialEstimateProductInfo')}</h3>
</div>
</div>
<div className="esimate-wrap">
<div className="estimate-list-wrap one">
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}</div>
</div>
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.supplyPrice, 0)}</div>
</div>
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimalToFixed(estimateContextState?.vatPrice, 0)}</div>
</div>
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div>
<div className="estimate-name red">{convertNumberToPriceDecimalToFixed(estimateContextState?.totPrice, 0)}</div>
</div>
</div>
</div>
{/* YJOD면 아래영역 숨김 */}
<div className="common-table bt-able" style={{ display: estimateContextState?.estimateType === 'YJSS' ? '' : 'none' }}>
<table>
<colgroup>
<col style={{ width: '160px' }} />
<col />
<col style={{ width: '160px' }} />
<col />
<col style={{ width: '160px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>
{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice')}
<span className="important">*</span>
</th>
<td>
<div className="input-wrap">
<input
type="text"
className="input-light"
value={estimateContextState?.estimateType === 'YJSS' ? estimateContextState?.pkgAsp : '0'}
onChange={(e) => {
onChangePkgAsp(e.target.value)
}}
/>
</div>
</td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th>
<td>{convertNumberToPriceDecimalToFixed(estimateContextState?.totVolKw, 2)}</td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th>
<td>{convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}</td>
</tr>
</tbody>
</table>
</div>
{/* 제품정보 끝 */}
{/* 가격표시영역시작 */}
<div className="estimate-product-option">
<div className="product-price-wrap">
<div className="product-price-tit">{getMessage('estimate.detail.header.showPrice')}</div>
<div className="select-wrap">
{session?.storeLvl === '1' ? (
<select
className="select-light"
onChange={(e) => {
onChangeStorePriceList(e.target.value)
}}
value={showPriceCd}
>
{storePriceList.length > 0 &&
storePriceList.map((row) => (
<option key={row.priceCd} value={row.priceCd}>
{row.priceNm}
</option>
))}
</select>
) : (
<select key={uuidv4()} className="select-light">
<option value="UNIT_PRICE">{getMessage('estimate.detail.header.unitPrice')}</option>
</select>
)}
</div>
<button
type="button"
className="btn-origin grey ml5"
onClick={(event) => {
setHandlePricingFlag(true)
handlePricingBtn(showPriceCd)
}}
>
{getMessage('estimate.detail.showPrice.pricingBtn')}
</button>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.singleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
onChange={(e) => {
onChangeDisplayCableItem(e.target.value, estimateContextState.itemList)
}}
value={cableItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => {
if(!row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2}
</option>
}
})}
</select>
</div>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.doubleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
onChange={(e) => {
onChangeDisplayDoubleCableItem(e.target.value, estimateContextState.itemList)
}}
value={cableDbItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => {
if(row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2.replace('S','')}
</option>
}
})}
</select>
</div>
</div>
<div className="product-edit-wrap">
<ul className="product-edit-explane">
<li className="explane-item item01">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description1')}
</li>
<li className="explane-item item02">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description2')}
</li>
<li className="explane-item item03">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description3')}
</li>
<li className="explane-item item04">
<span className="ico"></span>
{getMessage('estimate.detail.showPrice.description4')}
</li>
</ul>
<div className="product-edit-btn">
<button
className="btn-origin navy mr5"
type="button"
onClick={() => {
addItem()
}}
>
<span className="plus"></span>
{getMessage('estimate.detail.showPrice.addItem')}
</button>
<button className="btn-origin grey" type="button" onClick={removeItem}>
<span className="minus"></span>
{getMessage('estimate.detail.showPrice.delItem')}
</button>
</div>
</div>
</div>
{/* 가격표시영역끝 */}
{/* html테이블시작 */}
<div className="esimate-table">
<table>
<colgroup>
<col width={50} />
<col width={70} />
<col />
<col width={300} />
<col width={90} />
<col width={80} />
<col width={170} />
<col width={190} />
</colgroup>
<thead>
<tr>
<th>
<div className="d-check-box pop no-text">
<input type="checkbox" id="ch97" checked={isSelectedAll()} onChange={onChangeSelectAll} />
<label htmlFor="ch97"></label>
</div>
</th>
<th>{getMessage('estimate.detail.itemTableHeader.dispOrder')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.itemId')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.itemNo')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.amount')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.unit')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.salePrice')}</th>
<th>{getMessage('estimate.detail.itemTableHeader.saleTotPrice')}</th>
</tr>
</thead>
<tbody>
{estimateContextState?.itemList.length > 0 &&
estimateContextState.itemList.map((item, index) => {
if (item.delFlg === '0') {
return (
<tr key={item?.dispOrder || index}>
<td className="al-c">
<div className="d-check-box light no-text">
<input
type="checkbox"
id={item?.dispOrder}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1X'}
onChange={() => onChangeSelect(item.dispOrder)}
checked={!!selection.has(item.dispOrder)}
/>
<label htmlFor={item?.dispOrder}></label>
</div>
</td>
<td className="al-r">{item?.dispOrder}</td>
<td>
<div className="form-flex-wrap">
<div className="select-wrap mr5">
{item.dispCableFlg !== '1' ? (
<Select
name="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={originDisplayItemList}
styles={groupStyles}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
onChangeDisplayItem(e.itemId, item.dispOrder, index, false)
}
}}
menuPlacement={'auto'}
getOptionLabel={(x) => x.itemName + ' (' + x.itemNo + ')'}
getOptionValue={(x) => x.itemNo}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{props.data.itemName}</components.SingleValue>
},
}}
isClearable={false}
isDisabled={!!item?.paDispOrder}
value={displayItemList.filter(function (option) {
if (item.itemNo === '') {
return false
} else {
return option.itemId === item.itemId
}
})}
/>
) : (
<Select
name="long-value-select11"
instanceId="long-value-select11"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={cableItemList}
menuPlacement={'auto'}
getOptionLabel={(x) => x.clRefChr3 + ' (' + x.clRefChr1 + ')'}
getOptionValue={(x) => x.clRefChr1}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12' || item.itemTpCd === 'S13')? item.itemName : props.data.clRefChr3}</components.SingleValue>
},
}}
isClearable={false}
isDisabled={true}
value={cableItemList.filter(function (option) {
return (item.itemTpCd === 'M12' || item.itemTpCd === 'S13' )? item.itemId : option.clRefChr1 === item.itemId
})}
/>
)}
</div>
{item?.itemChangeFlg === '1' && (
<div className="btn-area">
<span className="tb_ico change_check"></span>
</div>
)}
</div>
</td>
<td>
<div className="form-flex-wrap">
<div className="name">{item?.itemNo}</div>
<div className="icon-wrap">
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>}
{item?.specialNoteCd && (
<button
type="button"
className="grid-tip"
onClick={() => {
setProductFeaturesPopupOpen(true)
setShowProductFeatureData(item?.specialNoteCd)
}}
></button>
)}
</div>
</div>
</td>
<td>
<div className="input-wrap" style={{ width: '100%' }}>
<input
type="text"
className="input-light al-r"
value={convertNumberToPriceDecimal(item?.amount?.replaceAll(',', ''))}
disabled={item.itemId === '' || !!item?.paDispOrder}
onChange={(e) => {
onChangeAmount(e.target.value, item.dispOrder, index)
}}
maxLength={6}
/>
</div>
</td>
<td>{item.unit}</td>
<td>
<div className="form-flex-wrap">
<div className="input-wrap mr5">
<input
type="text"
className="input-light al-r"
value={
item.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))
}
disabled={
item.openFlg === '1'
? true
: estimateContextState?.estimateType === 'YJSS'
? item?.paDispOrder
? true
: item.pkgMaterialFlg !== '1'
: item.itemId === '' || !!item?.paDispOrder
? true
: item.openFlg === '1'
? true
: false
}
onChange={(e) => {
onChangeSalePrice(e.target.value, item.dispOrder, index)
}}
maxLength={12}
/>
</div>
{item.openFlg === '1' && (
<div className="btn-area">
<span className="tb_ico open_check"></span>
</div>
)}
</div>
</td>
<td className="al-r">
{item?.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(
item?.showSaleTotPrice === '0'
? null
: item?.amount === ''
? null
: item?.saleTotPrice === '0'
? null
: item?.saleTotPrice?.replaceAll(',', ''),
)}
</td>
</tr>
)
} else {
return null
}
})}
</tbody>
</table>
</div>
{/* html테이블끝 */}
</div>
</div>
{/* 기본정보끝 */}
</div>
{productFeaturesPopupOpen && (
<ProductFeaturesPop
popShowSpecialNoteList={popShowSpecialNoteList}
showProductFeatureData={showProductFeatureData}
setProductFeaturesPopupOpen={setProductFeaturesPopupOpen}
/>
)}
</div>
)
}