mirror of
https://git.hmsn.ink/kospo/svcm/oa.git
synced 2026-03-20 01:02:19 +09:00
691 lines
21 KiB
Vue
691 lines
21 KiB
Vue
<script setup lang="ts">
|
|
|
|
import {saveTempSlip} from "/@src/service/slipApi.ts";
|
|
import {type iPbAtt, Person, SapPerson} from "/@src/utils/types.ts";
|
|
import {VTabsItem} from "/src/components/app-vuero/VCustomTabs.vue";
|
|
import {getContractDetail} from "/@src/service/contractApi.ts";
|
|
import {deletePrcsFile} from "/@src/service/priceApi.ts";
|
|
|
|
onBeforeMount(async ()=>{
|
|
const result = await getContractDetail(history.state.key)
|
|
// 계약종료는 contract api 결재선 추가해야 됨
|
|
getDetailBeforeList(result)
|
|
})
|
|
const notyf = useNotyf()
|
|
|
|
const affiliationCode = ref()
|
|
const taxCode = ref('')
|
|
const currencyCode = ref('KRW')
|
|
const taxInvoiceCode = ref()
|
|
const accountSubjectCode = ref()
|
|
const params = reactive({
|
|
//샘플 데이터 형식으로 맞춤
|
|
// https://git.hmsn.ink/kospo/svcm/api/-/blob/main/api/slip/%EC%9E%84%EC%8B%9C%EC%A0%84%ED%91%9C%EC%A0%80%EC%9E%A5.http?ref_type=heads
|
|
cateCd: '',
|
|
contNo: '',
|
|
bldat: '',
|
|
budat: '',
|
|
regSdt: '',
|
|
regSdt2: '',
|
|
//waers => currencyCode ref
|
|
bktxt: '', //계약명
|
|
lifnr: '', //계약상대자
|
|
//wrbtr => formattedNumber ref
|
|
//mwskz => taxCode ref
|
|
//gsber => affiliationCode ref
|
|
//bupla => affiliationCode ref
|
|
zterm: '',
|
|
banks: '',
|
|
bankl:'',
|
|
bankn:'',
|
|
hkont: '',
|
|
wrbtrS: '', //공급가액 체크 필요 todo
|
|
kostl: '',
|
|
projk: '',
|
|
trtGubun: '',
|
|
txBillSeq: '',
|
|
slipAtts: [],
|
|
//apprList:[], ==> apprLine 변수로 변경 // 결재선 데이터 키: 값(배열 들어감)
|
|
|
|
compNm: '',
|
|
signDt: '',
|
|
contAmt: 0,
|
|
contStat: '',
|
|
regsabun: '',
|
|
place: '',
|
|
regDt: '',
|
|
reason: '',
|
|
page: 1,
|
|
row: 10,
|
|
flexColumn: [
|
|
//이름은 안넘어오고 있음
|
|
{ key: 'lineclsf', label: '구분', value: {} },
|
|
{ key: 'bname', label: '사원번호', value: {} },
|
|
{ key: 'btext', label: '이름', value: {} },
|
|
{ key: 'abscd', label: '근태관리', value: {} },
|
|
{ key: 'actions', label: '동작' },
|
|
],
|
|
modalColumn: [],
|
|
})
|
|
const pbAtts = ref<iPbAtt[]>(params.slipAtts)
|
|
const formattedNumber = ref(0)
|
|
const apprLine = ref<SapPerson[]>([])
|
|
|
|
const getDetailBeforeList = (item) => {
|
|
console.log("item before",item)
|
|
params.contNo = item.contNo
|
|
params.place = item.compNm// !!!!!!업체명 bupla
|
|
params.bktxt = item.title //!!!!!!계약명
|
|
params.lifnr = item.bizNo //!!!!!계약상대자
|
|
}
|
|
|
|
function onInput(event) {
|
|
let onlyNumber = event.target.value.replace(/[^0-9]/g, '')
|
|
formattedNumber.value = onlyNumber ? Number(onlyNumber).toLocaleString() : ''
|
|
}
|
|
|
|
const selectCostCode = ref('코스트센터') //todo
|
|
const selectCostCodeOptions = [
|
|
{ text: '코스트센터', key: 'kostl' },
|
|
{ text: 'WBS', key: 'projk'}
|
|
]
|
|
const router = useRouter()
|
|
const cancel = () => {
|
|
router.push('/app/contractManagement')
|
|
}
|
|
|
|
const loading = ref(false)
|
|
const createChit = async () => {
|
|
let res = null
|
|
try{
|
|
loading.value = true
|
|
const createParams = {
|
|
contNo : params.contNo, //"CONT-0000000005",
|
|
bldat : '20250602',//params.regSdt.replace("-",""), // "20250501",
|
|
budat : '20250610',//params.regSdt2.replace("-",""), // "20250502",
|
|
waers : "KRW", //currencyCode.value, //"KRW",
|
|
bktxt : params.bktxt,//"전표 생성 테스트1",
|
|
lifnr : params.lifnr, //999-99-99999",
|
|
wrbtr : "1203", //formattedNumber.value, //!!!!!수정필요 "1203",
|
|
mwskz : "V4", //taxCode.value, //"V4",
|
|
gsber : '1000', //affiliationCode.value, //무슨 값인지 모름 gsber bukrs bupla 같다고 함 1000
|
|
bupla : '1000', //affiliationCode.value, //1000
|
|
zterm : 'PF00', //params.zterm, //!!!!!!어떤 값인지 모름 PF00
|
|
banks : 'KR', //params.banks , //어떤 값인지 모름 KR
|
|
bankl : '012', //params.bankl, //012
|
|
bankn : '3510876657453', //params.bankn, //3510876657453
|
|
hkont : '5366010', //params.hkont,//G/L 계정 5366010
|
|
wrbtrS : formattedNumber.value, //1102
|
|
kostl : "12330", //!!!!!!12330
|
|
projk : selectCostCode.value, //빈값
|
|
trtGubun : '11', //params.trtGubun,//!!!!!!어떤 값인지 모름 11
|
|
txBillSeq : '202503231', //params.txBillSeq,//!!!!!!어떤 값인지 모름 202503231
|
|
slipAtts: pbAtts.value.map(req => ({
|
|
logiFnm: req.logiFnm,
|
|
data: req.data})),//첨부파일 데이터,
|
|
zwf0011t : {
|
|
wkftx : "결재 테스트11", //!!!!!!어떤 값인지 모름
|
|
apprs : [{
|
|
label: '결재',
|
|
value: apprLine.value.map((req) => ({
|
|
lineclsf : req.lineclsf,
|
|
bname : req.bname,
|
|
abscd : req.abscd
|
|
}))
|
|
}]
|
|
}
|
|
|
|
// waers : currencyCode.value, //"KRW",
|
|
// bktxt : params.bktxt,//"전표 생성 테스트1",
|
|
// lifnr : params.lifnr, //999-99-99999",
|
|
// wrbtr : formattedNumber.value, //수정필요 "1203",
|
|
// mwskz : taxCode.value, //"V4",
|
|
// gsber : affiliationCode.value, //무슨 값인지 모름 gsber bukrs bupla 같다고 함 1000
|
|
// bupla : affiliationCode.value, //1000
|
|
// zterm : params.zterm, //어떤 값인지 모름 PF00
|
|
// banks : params.banks , //어떤 값인지 모름 KR
|
|
// bankl : params.bankl, //012
|
|
// bankn : params.bankn, //3510876657453
|
|
// hkont : params.hkont,//G/L 계정 5366010
|
|
// wrbtrS : formattedNumber.value, //1102
|
|
// kostl : "12330", //12330
|
|
// projk : selectCostCode.value, //빈값
|
|
// trtGubun : params.trtGubun,//어떤 값인지 모름 11
|
|
// txBillSeq : params.txBillSeq,//어떤 값인지 모름 202503231
|
|
}
|
|
notyf.dismissAll()
|
|
res = await saveTempSlip(createParams)
|
|
if(res.request.status == '200'){
|
|
notyf.primary('등록 되었습니다.')
|
|
router.push({path: '/app/priceManagement'})
|
|
}
|
|
}catch(e){
|
|
notyf.error(e.message)
|
|
}finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const selectUser = ref<SapPerson[]>([])
|
|
|
|
const isDuplicate = (person: SapPerson) =>{
|
|
return apprLine.value.some((p) => p.bname === person.bname)}
|
|
|
|
watch(
|
|
selectUser,
|
|
(newPersons) => {
|
|
if (Array.isArray(newPersons) && newPersons.length > 0) {
|
|
const filtered = newPersons.filter((p) => !isDuplicate(p))
|
|
apprLine.value = [...apprLine.value, ...filtered]
|
|
selectUser.value = []
|
|
}
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
const onFileClick = () => {
|
|
const input = <any> document.querySelector('.file-input')
|
|
input.click()
|
|
}
|
|
|
|
const onFileChange = (e: any) => {
|
|
Array.from(e.target.files).forEach((file: any) => {
|
|
const reader = new FileReader()
|
|
reader.onload = () => {
|
|
const result = <string>reader.result
|
|
const pbAtt = <iPbAtt>{ // todo 여기 첨부파일로 변경 필요
|
|
prcsNo: params.prcsNo,
|
|
logiFnm: file.name,
|
|
size: file.size,
|
|
data: result.split(',')[1],
|
|
}
|
|
notyf.dismissAll()
|
|
if(pbAtt.logiFnm.includes('.pdf')) { //todo 여러개 올릴 때 이슈 발생 확인 필요
|
|
pbAtts.value.push(pbAtt)
|
|
}else{
|
|
notyf.error("pdf 파일만 업로드 가능합니다.")
|
|
}
|
|
}
|
|
|
|
reader.readAsDataURL(file)
|
|
})
|
|
}
|
|
|
|
const onFilDelete = async (prcsNo: string, index: number, fileOrd: number) => {
|
|
notyf.dismissAll()
|
|
const confirmed = confirm('삭제하시겠습니까?')
|
|
if (!prcsNo) {
|
|
if (confirmed){
|
|
pbAtts.value.splice(index, 1)
|
|
}
|
|
} else {
|
|
if (confirmed) {
|
|
await deletePrcsFile(prcsNo, fileOrd).then((res: string) => {
|
|
notyf.success(res)
|
|
pbAtts.value.splice(index, 1)
|
|
}).catch((err) => {
|
|
notyf.error('삭제 중 오류가 발생했습니다.')
|
|
console.error(err)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
const onPrcsFileDownload = async (prcsNo: string, fileOrd: number, logiFnm: string) => {
|
|
const link = document.createElement('a')
|
|
link.href = `https://svcm.hmsn.ink/api/prcs/${prcsNo}/${fileOrd}` //todo
|
|
link.setAttribute('download', logiFnm)
|
|
link.setAttribute('target', '_blank')
|
|
document.body.appendChild(link)
|
|
link.click()
|
|
}
|
|
|
|
const onPriceDelete = (index: number) => {
|
|
|
|
if(apprLine.value.length-1 !== params.flexColumn.length
|
|
|| (params.flexColumn.length == 8 && apprLine.value.length-1 == params.flexColumn.length))
|
|
{
|
|
apprLine.value.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
const moveUp = (index: number) => {
|
|
if (index <= 0) return
|
|
let temp = apprLine.value[index]
|
|
apprLine.value[index] = apprLine.value[index - 1]
|
|
apprLine.value[index - 1] = temp
|
|
}
|
|
|
|
const moveDown = (index: number) => {
|
|
if (index >= apprLine.value.length - 1) return
|
|
let temp = apprLine.value[index]
|
|
apprLine.value[index] = apprLine.value[index + 1]
|
|
apprLine.value[index + 1] = temp
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="page-content is-navbar-lg">
|
|
<div class="datatable-wrapper">
|
|
<div class="table-container">
|
|
<table class="table datatable-table is-fullwidth">
|
|
<colgroup>
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr>
|
|
<td>소속</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VCodeSelect
|
|
v-model="affiliationCode"
|
|
cd_grp=11 >
|
|
</VCodeSelect>
|
|
</VField>
|
|
</td>
|
|
<td colspan="7" />
|
|
</tr>
|
|
<tr>
|
|
<td>계약명</td>
|
|
<td colspan="9">
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
v-model="params.bktxt"
|
|
class="input custom-text-filter"
|
|
placeholder="계약명"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>계약상대자</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
v-model="params.lifnr"
|
|
class="input custom-text-filter"
|
|
placeholder="사업자번호"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
v-model="params.place"
|
|
class="input custom-text-filter"
|
|
placeholder="업체명"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
<td>
|
|
<VButton color="danger">
|
|
구매처 확인
|
|
</VButton>
|
|
</td>
|
|
<td>
|
|
<VButton color="success">
|
|
정상
|
|
</VButton>
|
|
</td>
|
|
<td colspan="2" />
|
|
</tr>
|
|
<tr>
|
|
<td>공급가액</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
:value="formattedNumber"
|
|
placeholder="금액"
|
|
@input="onInput"
|
|
class="input custom-text-filter"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
<td style="color: black">(부가세 별도)</td>
|
|
<td style="background-color: var(--primary); text-align: center">
|
|
<span>세금코드</span>
|
|
</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VCodeSelect v-model="taxCode" cd_grp="12">
|
|
<template #code="{ item }">
|
|
{{ item.cd }}
|
|
</template>
|
|
</VCodeSelect>
|
|
</VField>
|
|
</td>
|
|
<td style="background-color: var(--primary); text-align: center">통화</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VCodeSelect v-model="currencyCode" cd_grp="13">
|
|
<template #code="{ item }">
|
|
{{ item.cd }}
|
|
</template>
|
|
</VCodeSelect>
|
|
</VField>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>증빙일</td>
|
|
<td colspan="2">
|
|
<div>
|
|
<VDatePicker
|
|
v-model="params.regSdt"
|
|
color="green"
|
|
trim-weeks
|
|
>
|
|
<template #default="{ inputValue, inputEvents }">
|
|
<VField>
|
|
<VControl icon="lucide:calendar">
|
|
<input
|
|
class="input v-input"
|
|
type="text"
|
|
:value="inputValue"
|
|
placeholder="증빙일"
|
|
v-on="inputEvents"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</template>
|
|
</VDatePicker>
|
|
</div>
|
|
</td>
|
|
<td />
|
|
<td style="background-color: var(--primary); text-align: center">
|
|
<span>전기일</span>
|
|
</td>
|
|
<td colspan="2">
|
|
<div>
|
|
<VDatePicker
|
|
v-model="params.regSdt2"
|
|
color="green"
|
|
trim-weeks
|
|
>
|
|
<template #default="{ inputValue, inputEvents }">
|
|
<VField>
|
|
<VControl icon="lucide:calendar">
|
|
<input
|
|
class="input v-input"
|
|
type="text"
|
|
:value="inputValue"
|
|
placeholder="전기일"
|
|
v-on="inputEvents"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</template>
|
|
</VDatePicker>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>계좌관리</td>
|
|
<td>
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
v-model="params.bankl"
|
|
class="input custom-text-filter"
|
|
placeholder="계약번호"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
v-model="params.bankn"
|
|
class="input custom-text-filter"
|
|
placeholder="계좌번호"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
<td>
|
|
<VButton color="info">
|
|
계좌조회
|
|
</VButton>
|
|
</td>
|
|
<td>
|
|
<VButton color="success">
|
|
정상
|
|
</VButton>
|
|
</td>
|
|
<td></td>
|
|
<td style="background-color: var(--primary); text-align: center">
|
|
<span>세금계산서</span>
|
|
</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VCodeSelect
|
|
v-model="taxInvoiceCode"
|
|
cd_grp=12 >
|
|
</VCodeSelect>
|
|
</VField>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>예산관리</td>
|
|
<td>
|
|
<VButton
|
|
disabled
|
|
color="primary">
|
|
G/L계정
|
|
</VButton>
|
|
</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VCodeSelect
|
|
v-model="accountSubjectCode"
|
|
cd_grp=12 >
|
|
</VCodeSelect>
|
|
</VField>
|
|
</td>
|
|
<td>
|
|
<VField>
|
|
<VSelect v-model="selectCostCode">
|
|
<option v-for="opt in selectCostCodeOptions" :key="opt.key">
|
|
{{ opt.text }}
|
|
</option>
|
|
</VSelect>
|
|
</VField>
|
|
</td>
|
|
<td colspan="2">
|
|
<VField>
|
|
<VControl>
|
|
<input
|
|
v-model="params.bktxt"
|
|
class="input custom-text-filter"
|
|
placeholder="코드"
|
|
>
|
|
</VControl>
|
|
</VField>
|
|
</td>
|
|
<td colspan="4"></td>
|
|
</tr>
|
|
<tr>
|
|
<td>첨부파일</td>
|
|
<td colspan="1">
|
|
<VField>
|
|
<div class="form-label">
|
|
<div class="file has-name is-fullwidth">
|
|
<input
|
|
class="file-input hide"
|
|
type="file"
|
|
multiple
|
|
@change="(e) => onFileChange(e)"
|
|
>
|
|
<VLabel>
|
|
<VButton icon="fas fa-plus" color="info" class="file-trigger" @click="onFileClick" >
|
|
<span>파일업로드</span>
|
|
</VButton>
|
|
</VLabel>
|
|
</div>
|
|
</div>
|
|
<VControl>
|
|
<div
|
|
v-for="(f, i) in pbAtts"
|
|
:key="f.logiFnm"
|
|
class="content estimate-file-wrapper"
|
|
>
|
|
<div class="estimate-file-name">
|
|
{{ f.logiFnm }}{{" ("}}{{Math.ceil(f.size / 1024)}}kb{{")"}}
|
|
</div>
|
|
<div>
|
|
<i class="fa fa-trash estimate-file-delete" @click="onFilDelete(f.bizNo, i, f.fileOrd)" />
|
|
</div>
|
|
</div>
|
|
</VControl>
|
|
</VField>
|
|
<div class="column is-12">
|
|
<VField>
|
|
<VControl>
|
|
<div
|
|
v-for="f in params.slipAtts"
|
|
:key="f.logiFnm"
|
|
class="content estimate-file-wrapper"
|
|
>
|
|
<div class="estimate-file-name">
|
|
{{ f.logiFnm }}{{" ("}}{{Math.ceil(f.size / 1024)}}kb{{")"}}
|
|
</div>
|
|
<span>
|
|
<i class="fa fa-download estimate-file-download" @click="onPrcsFileDownload(f.prcsNo, f.fileOrd, f.logiFnm)" />
|
|
<i class="fa fa-trash estimate-file-delete" @click="onFilDelete(f.prcsNo, i, f.fileOrd)" />
|
|
</span>
|
|
</div>
|
|
</VControl>
|
|
</VField>
|
|
</div>
|
|
</td>
|
|
<td colspan="5">
|
|
<div>('준공보고서' 또는 '검수보고서' 등)</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="bottom-button">
|
|
<VButton @click="createChit">전 표 생 성</VButton>
|
|
<VButton @click="cancel">취 소</VButton>
|
|
</div>
|
|
<div class="column is-12">
|
|
<VUserSap v-model="selectUser" placeholder="사번이나 이름으로 검색해주세요"/>
|
|
</div>
|
|
<div>
|
|
<ComVFlexTable
|
|
:data="apprLine"
|
|
:columns="params.flexColumn"
|
|
:compact="true"
|
|
>
|
|
<template #body-cell="{row, column, index, value}">
|
|
<span v-if="column.key=='lineclsf'" class="column">
|
|
<VField class="pr-1">
|
|
<VCodeSelect
|
|
cd_grp=9
|
|
v-model="row.lineclsf"
|
|
/>
|
|
</VField>
|
|
</span>
|
|
<span v-else-if="column.key=='wkfst'" class="column">
|
|
<VField class="pr-1">
|
|
<VCodeSelect
|
|
cd_grp=8
|
|
v-model="row.wkfst"
|
|
/>
|
|
</VField>
|
|
</span>
|
|
<span v-else-if="column.key === 'actions'" class="flex gap-1">
|
|
<VCustomButton v-if="index != 1" @click="moveUp(index)" icon="lucide:arrow-up" />
|
|
<VCustomButton @click="moveDown(index)" icon="lucide:arrow-down" />
|
|
<VCustomButton @click="onPriceDelete(index)">{{"삭제"}}</VCustomButton>
|
|
</span>
|
|
</template>
|
|
</ComVFlexTable>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.table tbody td {
|
|
color: var(--smoke-white);
|
|
|
|
}
|
|
|
|
.datatable-table {
|
|
td{
|
|
font-family: var(--font),serif;
|
|
vertical-align: middle;
|
|
padding: 4px 20px;
|
|
border-bottom: 1px solid var(--fade-grey);
|
|
}
|
|
|
|
td:nth-child(1) {
|
|
background-color: var(--primary);
|
|
text-align: center;
|
|
}
|
|
|
|
tr:nth-child(3) {
|
|
text-align: center;
|
|
}
|
|
|
|
tr td button{
|
|
width: 100%;
|
|
}
|
|
|
|
}
|
|
|
|
.button.v-button {
|
|
padding: 0px 0px;
|
|
}
|
|
|
|
.bottom-button {
|
|
text-align: center;
|
|
button {
|
|
background-color: cornflowerblue;
|
|
margin: 10px;
|
|
font-weight: bold;
|
|
border-color: var(--primary);
|
|
color: white;
|
|
padding: 0.5rem 1rem !important;
|
|
}
|
|
}
|
|
button:nth-child(2) {
|
|
background-color: #AB9A6c;
|
|
}
|
|
button:nth-child(3) {
|
|
background-color: silver;
|
|
}
|
|
|
|
.field {
|
|
margin: 0px 0px;
|
|
}
|
|
|
|
|
|
.disabled-button {
|
|
//opacity: 0.5;
|
|
background-color: #ccc;
|
|
cursor: not-allowed;
|
|
//transition: all 0.2s;
|
|
}
|
|
|
|
</style>
|