견적서 상세

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 [originFiles, setOriginFiles] = useState([]) //
const [showPriceCd, setShowPriceCd] = useState('')
const [showContentCode, setShowContentCode] = useState('ATTR001')
const [productFeaturesPopupOpen, setProductFeaturesPopupOpen] = useState(false) //
@ -46,8 +48,6 @@ export default function Estimate({ params }) {
const [storePriceList, setStorePriceList] = useState([]) // option
const [tempPriceCd, setTempPriceCd] = useState('')
const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = {
startDate,
@ -182,7 +182,7 @@ export default function Estimate({ params }) {
})
}
// option
// option &&
useEffect(() => {
if (state.estimateType !== '') {
const param = {
@ -200,23 +200,35 @@ export default function Estimate({ params }) {
}
}, [state?.estimateType])
// option
useEffect(() => {
if (tempPriceCd !== '') {
const param = {
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)
}
})
if (state?.priceCd) {
setShowPriceCd(state.priceCd)
}
}, [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
const handlePricing = async (priceCd) => {
@ -224,24 +236,55 @@ export default function Estimate({ params }) {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: state.estimateType,
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
itemIdList: state.itemList, //
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'))
}
}
// console.log('::', param)
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)) {
// ............
//..
//itemList itemId unitPrice 0
//itemId
setState({
priceCd: session.storeLvl === '1' ? tempPriceCd : priceCd,
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)
}
}
}
@ -319,13 +362,24 @@ export default function Estimate({ params }) {
})
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 {
...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,
})
@ -336,7 +390,7 @@ export default function Estimate({ params }) {
useEffect(() => {
if (itemChangeYn) {
console.log('아이템에 뭔가 변화가 일어났어', itemChangeYn)
// console.log(' ', itemChangeYn)
console.log('아이템상태가져오기::::::::::', state.itemList)
}
@ -778,8 +832,9 @@ export default function Estimate({ params }) {
key={uuidv4()}
className="select-light"
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>)}
</select>
@ -790,9 +845,10 @@ export default function Estimate({ params }) {
)}
</div>
<button
type="button"
className="btn-origin grey ml5"
onClick={() => {
handlePricing(state.priceCd)
handlePricing(state?.priceCd)
}}
>
{getMessage('estimate.detail.showPrice.pricingBtn')}
@ -818,7 +874,15 @@ export default function Estimate({ params }) {
</li>
</ul>
<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>
{getMessage('estimate.detail.showPrice.addItem')}
</button>
@ -862,108 +926,114 @@ export default function Estimate({ params }) {
</thead>
<tbody>
{state?.itemList.length > 0 &&
state.itemList.map((item, index) => {
return (
<tr key={uuidv4()}>
<td className="al-c">
<div className="d-check-box light no-text">
<input
type="checkbox"
id={item?.dispOrder}
onChange={() => onChangeSelect(item.dispOrder)}
checked={selection.has(item.dispOrder) ? true : false}
/>
<label htmlFor={item?.dispOrder}></label>
</div>
</td>
<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>
state.itemList.map((item) => {
if (item.delFlg === '0') {
return (
<>
<tr key={uuidv4()}>
<td className="al-c">
<div className="d-check-box light no-text">
<input
type="checkbox"
id={item?.dispOrder}
onChange={() => onChangeSelect(item.dispOrder)}
checked={selection.has(item.dispOrder) ? true : false}
/>
<label htmlFor={item?.dispOrder}></label>
</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)
</td>
<td className="al-r">{item?.dispOrder}</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?.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>
<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)
}}
/>
</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={convertNumberToPriceDecimal(item?.salePrice)}
onChange={(e) => {
//onChangeDisplayItem
//itemChangeFlg, partAdd
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>
)
/>
</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={convertNumberToPriceDecimal(item?.salePrice)}
disabled={state?.estimateType === 'YJSS' ? (item.pkgMaterialFlg === '1' ? false : true) : false}
onChange={(e) => {
//onChangeDisplayItem
//itemChangeFlg, partAdd
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>
</>
)
} else {
return null
}
})}
</tbody>
</table>

View File

@ -59,6 +59,12 @@ export const useEstimateController = (planNo) => {
try {
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
if (isObjectNotEmpty(res)) {
if (res.itemList.length > 0) {
res.itemList.map((item) => {
item.delFlg = '0'
})
}
setState(res)
}
})
@ -133,27 +139,26 @@ export const useEstimateController = (planNo) => {
const handleEstimateSubmit = async () => {
//0. 필수체크
let flag = true
console.log('::담긴 estimateData:::', estimateData)
// console.log('첨부파일:::::', estimateData.fileList)
//첨부파일을 첨부안했는데
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
if (estimateData.fileList.length < 1) {
if (estimateData.itemList.length > 1) {
estimateData.itemList.map((row) => {
if (row.fileUploadFlg === '1') {
if (estimateData.fileFlg === '0') {
alert(getMessage('estimate.detail.save.requiredMsg'))
flag = false
}
}
})
}
}
// if (estimateData.fileList.length < 1) {
// if (estimateData.itemList.length > 1) {
// estimateData.itemList.map((row) => {
// if (row.fileUploadFlg === '1') {
// if (estimateData.fileFlg === '0') {
// alert(getMessage('estimate.detail.save.requiredMsg'))
// flag = false
// }
// }
// })
// }
// }
if (flag) {
//1. 첨부파일 저장
//1. 첨부파일 저장시작
const formData = new FormData()
console.log('첨부파일:!!!', estimateData.fileList)
formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo)
@ -161,7 +166,23 @@ export const useEstimateController = (planNo) => {
formData.append('userId', estimateData.userId)
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. 상세데이터 저장
return
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.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing",
"estimate.detail.showPrice.pricingBtn.noItemId": "Pricingが欠落しているアイテムがあります。 Pricingを進めてください.",
"estimate.detail.showPrice.description1": "製品価格 OPEN",
"estimate.detail.showPrice.description2": "追加, 変更資材",
"estimate.detail.showPrice.description3": "添付必須",
@ -879,6 +880,7 @@
"estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
"simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日",

View File

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