견적서 상세

This commit is contained in:
basssy 2024-11-15 17:36:50 +09:00
parent 18e3ebe7f5
commit 889ced564d
4 changed files with 243 additions and 148 deletions

View File

@ -28,6 +28,8 @@ export default function Estimate({ params }) {
const [files, setFiles] = useState([]) // const [files, setFiles] = useState([]) //
const [originFiles, setOriginFiles] = useState([]) // const [originFiles, setOriginFiles] = useState([]) //
const [showPriceCd, setShowPriceCd] = useState('')
const [showContentCode, setShowContentCode] = useState('ATTR001') const [showContentCode, setShowContentCode] = useState('ATTR001')
const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) // const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) //
@ -46,8 +48,6 @@ export default function Estimate({ params }) {
const [storePriceList, setStorePriceList] = useState([]) // option const [storePriceList, setStorePriceList] = useState([]) // option
const [tempPriceCd, setTempPriceCd] = useState('')
const [startDate, setStartDate] = useState(new Date()) const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = { const singleDatePickerProps = {
startDate, startDate,
@ -182,7 +182,7 @@ export default function Estimate({ params }) {
}) })
} }
// option // option &&
useEffect(() => { useEffect(() => {
if (state.estimateType !== '') { if (state.estimateType !== '') {
const param = { const param = {
@ -200,23 +200,35 @@ export default function Estimate({ params }) {
} }
}, [state?.estimateType]) }, [state?.estimateType])
// option
useEffect(() => { useEffect(() => {
if (tempPriceCd !== '') { if (state?.priceCd) {
const param = { setShowPriceCd(state.priceCd)
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: tempPriceCd,
}
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data)
}
})
} }
}, [tempPriceCd]) }, [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 //Pricing
const handlePricing = async (priceCd) => { const handlePricing = async (priceCd) => {
@ -224,24 +236,55 @@ export default function Estimate({ params }) {
saleStoreId: session.storeId, saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd, sapSalesStoreCd: session.custCd,
docTpCd: state.estimateType, docTpCd: state.estimateType,
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd, priceCd: priceCd,
itemIdList: state.itemList, // //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'))
}
} }
// console.log('::', param)
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => { await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
let updateList = []
if (res) { if (res) {
if (res.status === 200) { if (res.status === 200) {
const data = res.data const data = res.data
//itemList itemId unitPrice 0
//itemId
if (data.result.code === 200) { if (data.result.code === 200) {
if (isNotEmptyArray(data.data2)) { if (isNotEmptyArray(data.data2)) {
// ............ state.itemList.map((item) => {
//.. let checkYn = false
//itemList itemId unitPrice 0 data.data2.map((item2) => {
//itemId if (item2.itemId === item.itemId) {
setState({ updateList.push({ ...item, unitPrice: item2.unitPrice })
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd, checkYn = true
}
})
if (!checkYn) {
updateList.push({ ...item, unitPrice: '0' })
}
}) })
setState({
priceCd: priceCd,
itemList: updateList,
})
setItemChangeYn(true)
} }
} }
} }
@ -319,13 +362,24 @@ export default function Estimate({ params }) {
}) })
const updateList = state.itemList.map((item) => { const updateList = state.itemList.map((item) => {
const isDeleted = delList.some((row) => item.dispOrder === row.dispOrder) const isDeleted = delList.some((row) => item.delFlg === '1' || item.dispOrder === row.dispOrder)
return { return {
...item, ...item,
delFlg: isDeleted ? '1' : '0', 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({ setState({
itemList: updateList, itemList: updateList,
}) })
@ -336,7 +390,7 @@ export default function Estimate({ params }) {
useEffect(() => { useEffect(() => {
if (itemChangeYn) { if (itemChangeYn) {
console.log('아이템에 뭔가 변화가 일어났어', itemChangeYn) // console.log(' ', itemChangeYn)
console.log('아이템상태가져오기::::::::::', state.itemList) console.log('아이템상태가져오기::::::::::', state.itemList)
} }
@ -778,8 +832,9 @@ export default function Estimate({ params }) {
key={uuidv4()} key={uuidv4()}
className="select-light" className="select-light"
onChange={(e) => { onChange={(e) => {
setTempPriceCd(e.target.value) onChangeStorePriceList(e.target.value)
}} }}
value={showPriceCd}
> >
{storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)} {storePriceList.length > 0 && storePriceList.map((row) => <option value={row.priceCd}>{row.priceNm}</option>)}
</select> </select>
@ -790,9 +845,10 @@ export default function Estimate({ params }) {
)} )}
</div> </div>
<button <button
type="button"
className="btn-origin grey ml5" className="btn-origin grey ml5"
onClick={() => { onClick={() => {
handlePricing(state.priceCd) handlePricing(state?.priceCd)
}} }}
> >
{getMessage('estimate.detail.showPrice.pricingBtn')} {getMessage('estimate.detail.showPrice.pricingBtn')}
@ -818,7 +874,15 @@ export default function Estimate({ params }) {
</li> </li>
</ul> </ul>
<div className="product-edit-btn"> <div className="product-edit-btn">
<button className="btn-origin navy mr5" type="button" onClick={addItem}> {/* <button className="btn-origin navy mr5" type="button" onClick={addItem}> */}
<button
className="btn-origin navy mr5"
type="button"
onClick={() => {
addItem()
setItemChangeYn(true)
}}
>
<span className="plus"></span> <span className="plus"></span>
{getMessage('estimate.detail.showPrice.addItem')} {getMessage('estimate.detail.showPrice.addItem')}
</button> </button>
@ -862,108 +926,114 @@ export default function Estimate({ params }) {
</thead> </thead>
<tbody> <tbody>
{state?.itemList.length > 0 && {state?.itemList.length > 0 &&
state.itemList.map((item, index) => { state.itemList.map((item) => {
return ( if (item.delFlg === '0') {
<tr key={uuidv4()}> return (
<td className="al-c"> <>
<div className="d-check-box light no-text"> <tr key={uuidv4()}>
<input <td className="al-c">
type="checkbox" <div className="d-check-box light no-text">
id={item?.dispOrder} <input
onChange={() => onChangeSelect(item.dispOrder)} type="checkbox"
checked={selection.has(item.dispOrder) ? true : false} id={item?.dispOrder}
/> onChange={() => onChangeSelect(item.dispOrder)}
<label htmlFor={item?.dispOrder}></label> checked={selection.has(item.dispOrder) ? true : false}
</div> />
</td> <label htmlFor={item?.dispOrder}></label>
<td className="al-r">{item?.dispOrder * 100}</td>
<td>
<div className="form-flex-wrap">
<div className="select-wrap mr5">
<Select
id="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={displayItemList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
onChangeDisplayItem(e.itemId, item.dispOrder)
}
}}
getOptionLabel={(x) => x.itemName}
getOptionValue={(x) => x.itemId}
isClearable={true}
isDisabled={false}
value={displayItemList.filter(function (option) {
return option.itemId === item.itemId
})}
/>
</div>
{/* {item?.partAdd === '1' && ( */}
{item?.itemChangeFlg === '1' && (
<div className="btn-area">
<span className="tb_ico change_check"></span>
</div> </div>
)} </td>
</div> <td className="al-r">{item?.dispOrder}</td>
</td> <td>
<td> <div className="form-flex-wrap">
<div className="form-flex-wrap"> <div className="select-wrap mr5">
<div className="name">{item?.itemNo}</div> <Select
<div className="icon-wrap"> id="long-value-select1"
{item?.fileUploadFlg === '1' && <span className="tb_ico file_check"></span>} instanceId="long-value-select1"
{item?.specialNoteCd && ( className="react-select-custom"
<button classNamePrefix="custom"
type="button" placeholder="Select"
className="grid-tip" options={displayItemList}
onClick={() => { onChange={(e) => {
setProductFeaturesPopupOpen(true) if (isObjectNotEmpty(e)) {
setShowProductFeatureData(item?.specialNoteCd) onChangeDisplayItem(e.itemId, item.dispOrder)
}
}}
getOptionLabel={(x) => x.itemName}
getOptionValue={(x) => x.itemId}
isClearable={true}
isDisabled={false}
value={displayItemList.filter(function (option) {
return option.itemId === 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"
defaultValue={convertNumberToPriceDecimal(item?.amount)}
onChange={(e) => {
//onChangeDisplayItem
//itemChangeFlg = 1, partAdd = 0
console.log('수량변경::::::::', e.target.value)
}} }}
></button> />
)} </div>
</div> </td>
</div> <td>{item.unit}</td>
</td> <td>
<td> <div className="form-flex-wrap">
<div className="input-wrap" style={{ width: '100%' }}> <div className="input-wrap mr5">
<input <input
type="text" type="text"
className="input-light al-r" className="input-light al-r"
defaultValue={convertNumberToPriceDecimal(item?.amount)} value={convertNumberToPriceDecimal(item?.salePrice)}
onChange={(e) => { disabled={state?.estimateType === 'YJSS' ? (item.pkgMaterialFlg === '1' ? false : true) : false}
//onChangeDisplayItem onChange={(e) => {
//itemChangeFlg = 1, partAdd = 0 //onChangeDisplayItem
console.log('수량변경::::::::', e.target.value) //itemChangeFlg, partAdd
}} console.log('단가변경:::::::', e.target.value)
/> }}
</div> />
</td> </div>
<td>{item.unit}</td> {/* <div className="btn-area">
<td> <span className="tb_ico open_check">OPEN아이콘 처리</span>
<div className="form-flex-wrap"> </div> */}
<div className="input-wrap mr5"> </div>
<input </td>
type="text" <td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice)}</td>
className="input-light al-r" </tr>
value={convertNumberToPriceDecimal(item?.salePrice)} </>
onChange={(e) => { )
//onChangeDisplayItem } else {
//itemChangeFlg, partAdd return null
console.log('단가변경:::::::', e.target.value) }
}}
/>
</div>
{/* <div className="btn-area">
<span className="tb_ico open_check">OPEN아이콘 처리</span>
</div> */}
</div>
</td>
<td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice)}</td>
</tr>
)
})} })}
</tbody> </tbody>
</table> </table>

View File

@ -59,6 +59,12 @@ export const useEstimateController = (planNo) => {
try { try {
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => { await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
if (isObjectNotEmpty(res)) { if (isObjectNotEmpty(res)) {
if (res.itemList.length > 0) {
res.itemList.map((item) => {
item.delFlg = '0'
})
}
setState(res) setState(res)
} }
}) })
@ -133,27 +139,26 @@ export const useEstimateController = (planNo) => {
const handleEstimateSubmit = async () => { const handleEstimateSubmit = async () => {
//0. 필수체크 //0. 필수체크
let flag = true let flag = true
console.log('::담긴 estimateData:::', estimateData)
// console.log('첨부파일:::::', estimateData.fileList) // console.log('첨부파일:::::', estimateData.fileList)
//첨부파일을 첨부안했는데 //첨부파일을 첨부안했는데
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
if (estimateData.fileList.length < 1) { // if (estimateData.fileList.length < 1) {
if (estimateData.itemList.length > 1) { // if (estimateData.itemList.length > 1) {
estimateData.itemList.map((row) => { // estimateData.itemList.map((row) => {
if (row.fileUploadFlg === '1') { // if (row.fileUploadFlg === '1') {
if (estimateData.fileFlg === '0') { // if (estimateData.fileFlg === '0') {
alert(getMessage('estimate.detail.save.requiredMsg')) // alert(getMessage('estimate.detail.save.requiredMsg'))
flag = false // flag = false
} // }
} // }
}) // })
} // }
} // }
if (flag) { if (flag) {
//1. 첨부파일 저장 //1. 첨부파일 저장시작
const formData = new FormData() const formData = new FormData()
console.log('첨부파일:!!!', estimateData.fileList)
formData.append('file', estimateData.fileList) formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo) formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo) formData.append('planNo', estimateData.planNo)
@ -161,7 +166,23 @@ export const useEstimateController = (planNo) => {
formData.append('userId', estimateData.userId) formData.append('userId', estimateData.userId)
await post({ url: '/api/file/fileUpload', data: formData }) await post({ url: '/api/file/fileUpload', data: formData })
//첨부파일저장끝
//제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기
estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '')
let delCnt = 0
estimateData.itemList.map((item) => {
if (item.delFlg === '1') {
delCnt++
}
})
if (delCnt === estimateData.itemList.length) {
return alert(getMessage('estimate.detail.save.requiredItem'))
}
console.log('최종 정보::;', estimateData)
console.log('최종 남은 아이템정보:::', estimateData.itemList)
//2. 상세데이터 저장 //2. 상세데이터 저장
return return
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {

View File

@ -844,6 +844,7 @@
"estimate.detail.header.showPrice": "価格表示", "estimate.detail.header.showPrice": "価格表示",
"estimate.detail.header.unitPrice": "定価", "estimate.detail.header.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください.",
"estimate.detail.showPrice.description1": "製品価格 OPEN", "estimate.detail.showPrice.description1": "製品価格 OPEN",
"estimate.detail.showPrice.description2": "追加, 変更資材", "estimate.detail.showPrice.description2": "追加, 変更資材",
"estimate.detail.showPrice.description3": "添付必須", "estimate.detail.showPrice.description3": "添付必須",
@ -879,6 +880,7 @@
"estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.", "estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
"simulator.title.sub1": "物件番号", "simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日", "simulator.title.sub2": "作成日",

View File

@ -849,11 +849,12 @@
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액", "estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액",
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈량 * 수량)÷100", "estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈량 * 수량)÷100",
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)", "estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
"estimate.detail.header.showPrice": "가격표시", "estimate.detail.header.showPrice": "가격표시",
"estimate.detail.header.unitPrice": "정가", "estimate.detail.header.unitPrice": "정가",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricing이 누락된 아이템이 있습니다. Pricing을 진행해주세요.",
"estimate.detail.showPrice.description1": "제품 가격 OPEN", "estimate.detail.showPrice.description1": "제품 가격 OPEN",
"estimate.detail.showPrice.description2": "추가, 변경 자재", "estimate.detail.showPrice.description2": "추가, 변경 자재",
"estimate.detail.showPrice.description3": "첨부필수", "estimate.detail.showPrice.description3": "첨부필수",
@ -889,6 +890,7 @@
"estimate.detail.productFeaturesPopup.close": "닫기", "estimate.detail.productFeaturesPopup.close": "닫기",
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.", "estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.", "estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
"estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.",
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
"simulator.title.sub1": "물건번호", "simulator.title.sub1": "물건번호",
"simulator.title.sub2": "작성일", "simulator.title.sub2": "작성일",