1077 lines
44 KiB
JavaScript
1077 lines
44 KiB
JavaScript
function CreateReceiptEditApp() {
|
|
const loadMarker = document.getElementById('receiptEditApp');
|
|
if (loadMarker) {
|
|
DoCreateReceiptEditApp();
|
|
}
|
|
}
|
|
|
|
function DoCreateReceiptEditApp() {
|
|
|
|
window.__vms = window.__vms || {}
|
|
|
|
if (window.__vms.receiptEditApp)
|
|
return;
|
|
|
|
window.__vms.receiptEditApp = new Vue({
|
|
el: '#receiptEditApp',
|
|
mixins: [DateMixin, ToastMixin, ApiMixin],
|
|
data: {
|
|
mode: 'view', // view, add, edit
|
|
編號: 0,
|
|
originalMainData: null,
|
|
originalLaborData: null,
|
|
originalTravelData: null,
|
|
mainData: {
|
|
編號: 0,
|
|
表單編號: '',
|
|
活動日期: '',
|
|
身分別: '',
|
|
講師_非講師編號: '',
|
|
領款人姓名: '',
|
|
身分證字號: '',
|
|
憑證類別: '',
|
|
計畫代號: '',
|
|
計畫名稱: '',
|
|
活動地點: '',
|
|
活動類別: '',
|
|
活動名稱事由: '',
|
|
製單人員: '',
|
|
付款方式: '',
|
|
作廢: 0,
|
|
列印次數: 0,
|
|
已關帳: false,
|
|
DB_APPNO: 1,
|
|
DB_CRDAT: '',
|
|
DB_CRUSR: '',
|
|
DB_CRUSR_NAME: '',
|
|
DB_TRDAT: '',
|
|
DB_TRUSR: '',
|
|
DB_TRUSR_NAME: ''
|
|
},
|
|
laborData: {
|
|
編號: '',
|
|
領據編號: 0,
|
|
費用別: '',
|
|
單價: 0,
|
|
數量: 0,
|
|
單位: '',
|
|
扣繳類別: '',
|
|
合計金額: 0,
|
|
應扣繳稅額: 0,
|
|
服務費: 0,
|
|
作廢: 0,
|
|
DB_APPNO: 1
|
|
},
|
|
travelData: {
|
|
編號: '',
|
|
領據編號: 0,
|
|
票價資訊: '',
|
|
費用別: '',
|
|
起點: '',
|
|
訖點: '',
|
|
捷運公車: 0,
|
|
火車: 0,
|
|
計程車: 0,
|
|
高鐵飛機: 0,
|
|
自行開車: 0,
|
|
過路費停車費: 0,
|
|
住宿費: 0,
|
|
合計: 0,
|
|
作廢: 0,
|
|
DB_APPNO: 1
|
|
},
|
|
dropdown: {
|
|
身分別: [],
|
|
憑證類別: [],
|
|
付款方式: [],
|
|
勞務費_費用別: [],
|
|
差旅費_費用別: [],
|
|
扣繳類別: [],
|
|
單位別: []
|
|
},
|
|
recipientList: [],
|
|
showRecipientDropdown: false,
|
|
planList: [],
|
|
showPlanDropdown: false,
|
|
originStations: [],
|
|
showOriginDropdown: false,
|
|
destStations: [],
|
|
showDestDropdown: false,
|
|
stations: [],
|
|
showActivityLocationPhrase: false,
|
|
activityLocationPhrases: [],
|
|
showActivityCategoryPhrase: false,
|
|
activityCategoryPhrases: [],
|
|
showTicketPricePhrase: false,
|
|
ticketPricePhrases: [],
|
|
loading: false,
|
|
variables: {
|
|
領據執行業務所得課稅門檻: 0,
|
|
領據薪資課稅門檻: 0,
|
|
領據基本工資: 0,
|
|
領據執行業務所得稅率: 0,
|
|
領據薪資稅率: 0
|
|
},
|
|
// 花花: 上下筆導航用
|
|
navIdList: [],
|
|
navCurrentIndex: -1,
|
|
navCurrentPage: 1,
|
|
navPageSize: 20,
|
|
navTotalPages: 0,
|
|
navSearchParams: {}
|
|
},
|
|
created() {
|
|
// 從 AJAX 取得下拉選單資料
|
|
this.loading = true;
|
|
this.loadVariables();
|
|
this.loadDropdownLists();
|
|
this.loadPhraseLists();
|
|
this.loading = false;
|
|
this.mainData.製單人員 = V製單人員;
|
|
this.編號 = 0;
|
|
if (document.getElementById('ctl00_ContentPlaceHolder1_hid編號').value !== '') {
|
|
this.編號 = parseInt(document.getElementById('ctl00_ContentPlaceHolder1_hid編號').value) || 0;
|
|
}
|
|
|
|
// 花花: 初始化上下筆導航狀態(從查詢畫面傳來的 HiddenField 讀取)
|
|
this._initNavigation();
|
|
},
|
|
mounted() {
|
|
if (this.編號 !== 0) {
|
|
this.mode = 'view';
|
|
this.loadData(this.編號)
|
|
} else {
|
|
this.mode = 'add';
|
|
this.reset();
|
|
this.backupData();
|
|
this.setDefault();
|
|
}
|
|
},
|
|
watch: {
|
|
'laborData.單價'() {
|
|
this.calculateLaborTotal();
|
|
},
|
|
'laborData.數量'() {
|
|
this.calculateLaborTotal();
|
|
},
|
|
'laborData.扣繳類別'() {
|
|
this.calculateTax();
|
|
},
|
|
'mainData.身分別'() {
|
|
if (this.mode === 'add' || this.mode === 'edit') {
|
|
this.mainData.講師_非講師編號 = '';
|
|
this.mainData.領款人姓名 = '';
|
|
this.mainData.身分證字號 = '';
|
|
this.recipientList = [];
|
|
}
|
|
},
|
|
'travelData.捷運公車'() { this.calculateTravelTotal(); },
|
|
'travelData.火車'() { this.calculateTravelTotal(); },
|
|
'travelData.計程車'() { this.calculateTravelTotal(); },
|
|
'travelData.高鐵飛機'() { this.calculateTravelTotal(); },
|
|
'travelData.自行開車'() { this.calculateTravelTotal(); },
|
|
'travelData.過路費停車費'() { this.calculateTravelTotal(); },
|
|
'travelData.住宿費'() { this.calculateTravelTotal(); }
|
|
},
|
|
computed: {
|
|
isViewMode() {
|
|
return this.mode === 'view';
|
|
},
|
|
isEditMode() {
|
|
return this.mode === 'edit' || this.mode === 'add';
|
|
},
|
|
canAddNew() {
|
|
return this.mode === 'view';
|
|
},
|
|
canEdit() {
|
|
return this.mode === 'view' && this.編號 !== 0;
|
|
},
|
|
canCancel() {
|
|
return this.mode === 'add' || this.mode === 'edit';
|
|
},
|
|
canSave() {
|
|
return this.mode === 'add' || this.mode === 'edit';
|
|
},
|
|
canReturnToList() {
|
|
return this.mode === 'view';
|
|
},
|
|
closed() {
|
|
return this.mainData.已關帳;
|
|
},
|
|
// 花花: 上下筆導航可用性(僅 view 模式可用)
|
|
hasPrev() {
|
|
if (this.mode !== 'view') return false;
|
|
if (this.navIdList.length === 0 || this.navCurrentIndex < 0) return false;
|
|
return this.navCurrentIndex > 0 || this.navCurrentPage > 1;
|
|
},
|
|
hasNext() {
|
|
if (this.mode !== 'view') return false;
|
|
if (this.navIdList.length === 0 || this.navCurrentIndex < 0) return false;
|
|
return this.navCurrentIndex < this.navIdList.length - 1 || this.navCurrentPage < this.navTotalPages;
|
|
},
|
|
laborWarnings() {
|
|
const warnings = [];
|
|
const 扣繳類別 = this.laborData.扣繳類別 || '';
|
|
const 有金額 = (this.laborData.單價 || 0) > 0 || (this.laborData.數量 || 0) > 0 || (this.laborData.合計金額 || 0) > 0;
|
|
|
|
// 費用別警告
|
|
if (['演講費', '撰稿費'].indexOf(this.laborData.費用別) >= 0 && 扣繳類別 === '50') {
|
|
warnings.push('扣繳類別只能是執行業務所得');
|
|
}
|
|
|
|
// 有金額但費用別未填
|
|
if (有金額 && !this.laborData.費用別) {
|
|
warnings.push('勞務費已輸入金額,請選擇費用別');
|
|
}
|
|
|
|
// 有金額但單位未填
|
|
if (有金額 && !this.laborData.單位) {
|
|
warnings.push('勞務費已輸入金額,請選擇單位');
|
|
}
|
|
|
|
// 合計金額警告
|
|
if (扣繳類別 === '50' && this.laborData.合計金額 >= this.variables.領據基本工資) {
|
|
warnings.push(`薪資不得超過基本工資${this.variables.領據基本工資}(含),超過請拆成不同日期的領據`);
|
|
}
|
|
if ((扣繳類別.startsWith('9A') || 扣繳類別.startsWith('9B')) && this.laborData.合計金額 >= this.variables.領據執行業務所得課稅門檻) {
|
|
warnings.push(`執行業務所得不得超過${this.variables.領據執行業務所得課稅門檻}(含),超過請拆成不同日期的領據`);
|
|
}
|
|
|
|
return warnings;
|
|
}
|
|
},
|
|
methods: {
|
|
async loadVariables() {
|
|
try {
|
|
const result = await this.callApi('領據處理', 'GetVariables', {});
|
|
if (result.Data) {
|
|
this.variables = result.Data;
|
|
}
|
|
} catch (error) {
|
|
console.error('載入片語清單失敗:', error);
|
|
}
|
|
},
|
|
|
|
async loadDropdownLists() {
|
|
this.dropdown.身分別 = await this.loadDropdownList("身分別");
|
|
this.dropdown.憑證類別 = await this.loadDropdownList("憑證類別");
|
|
this.dropdown.付款方式 = await this.loadDropdownList("付款方式");
|
|
this.dropdown.勞務費_費用別 = await this.loadDropdownList("勞務費_費用別");
|
|
this.dropdown.差旅費_費用別 = await this.loadDropdownList("差旅費_費用別");
|
|
this.dropdown.扣繳類別 = await this.loadDropdownList("扣繳類別");
|
|
this.dropdown.單位別 = await this.loadDropdownList("單位別");
|
|
},
|
|
|
|
async loadDropdownList(key) {
|
|
try {
|
|
const result = await this.callApi('領據處理', 'GetDropdownLists', { key });
|
|
return result.Data || [];
|
|
} catch (error) {
|
|
console.error('載入下拉選單失敗:', error);
|
|
return [];
|
|
}
|
|
},
|
|
|
|
async loadPhraseLists() {
|
|
try {
|
|
const result = await this.callApi('領據處理', 'GetPhraseLists', {});
|
|
if (result.Data) {
|
|
this.activityLocationPhrases = result.Data.activityLocation || [];
|
|
this.activityCategoryPhrases = result.Data.activityCategory || [];
|
|
this.ticketPricePhrases = result.Data.ticketPrice || [];
|
|
}
|
|
} catch (error) {
|
|
console.error('載入公用變數失敗:', error);
|
|
}
|
|
},
|
|
|
|
toggleActivityLocationPhrase() {
|
|
if (this.isViewMode) return;
|
|
this.showActivityLocationPhrase = !this.showActivityLocationPhrase;
|
|
this.showActivityCategoryPhrase = false;
|
|
this.showTicketPricePhrase = false;
|
|
},
|
|
|
|
toggleActivityCategoryPhrase() {
|
|
if (this.isViewMode) return;
|
|
this.showActivityCategoryPhrase = !this.showActivityCategoryPhrase;
|
|
this.showActivityLocationPhrase = false;
|
|
this.showTicketPricePhrase = false;
|
|
},
|
|
|
|
toggleTicketPricePhrase() {
|
|
if (this.isViewMode) return;
|
|
this.showTicketPricePhrase = !this.showTicketPricePhrase;
|
|
this.showActivityLocationPhrase = false;
|
|
this.showActivityCategoryPhrase = false;
|
|
},
|
|
|
|
insertPhrase(fieldName, phrase) {
|
|
let input;
|
|
if (fieldName === '活動地點') {
|
|
input = this.$refs.活動地點Input;
|
|
this.showActivityLocationPhrase = false;
|
|
} else if (fieldName === '活動類別') {
|
|
input = this.$refs.活動類別Input;
|
|
this.showActivityCategoryPhrase = false;
|
|
} else if (fieldName === '票價資訊') {
|
|
input = this.$refs.票價資訊Input;
|
|
this.showTicketPricePhrase = false;
|
|
}
|
|
|
|
if (input) {
|
|
const startPos = input.selectionStart || input.value.length;
|
|
const endPos = input.selectionEnd || input.value.length;
|
|
const currentValue = input.value;
|
|
const newValue = currentValue.substring(0, startPos) + phrase + currentValue.substring(endPos);
|
|
|
|
if (fieldName === '活動地點') {
|
|
this.mainData.活動地點 = newValue;
|
|
} else if (fieldName === '活動類別') {
|
|
this.mainData.活動類別 = newValue;
|
|
} else if (fieldName === '票價資訊') {
|
|
this.travelData.票價資訊 = newValue;
|
|
}
|
|
|
|
// 更新游標位置
|
|
this.$nextTick(() => {
|
|
input.focus();
|
|
input.setSelectionRange(startPos + phrase.length, startPos + phrase.length);
|
|
});
|
|
}
|
|
},
|
|
|
|
calculateLaborTotal() {
|
|
const price = parseFloat(this.laborData.單價) || 0;
|
|
const qty = parseFloat(this.laborData.數量) || 0;
|
|
this.laborData.合計金額 = Math.round(price * qty);
|
|
this.calculateTax();
|
|
},
|
|
|
|
async calculateTax() {
|
|
const laborFee = parseInt(this.laborData.合計金額) || 0;
|
|
const category = this.laborData.扣繳類別 || '';
|
|
|
|
if (laborFee > 0 && category) {
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'CalcTax', {
|
|
laborFee,
|
|
category
|
|
});
|
|
this.laborData.應扣繳稅額 = result.Data;
|
|
} catch (error) {
|
|
console.error('計算稅額失敗:', error);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
} else {
|
|
this.laborData.應扣繳稅額 = 0;
|
|
}
|
|
},
|
|
|
|
calculateTravelTotal() {
|
|
this.travelData.合計 =
|
|
(parseInt(this.travelData.捷運公車) || 0) +
|
|
(parseInt(this.travelData.火車) || 0) +
|
|
(parseInt(this.travelData.計程車) || 0) +
|
|
(parseInt(this.travelData.高鐵飛機) || 0) +
|
|
(parseInt(this.travelData.自行開車) || 0) +
|
|
(parseInt(this.travelData.過路費停車費) || 0) +
|
|
(parseInt(this.travelData.住宿費) || 0);
|
|
},
|
|
|
|
searchStation(field) {
|
|
const key = this.travelData[field];
|
|
|
|
if (key && key.length > 0) {
|
|
axios({
|
|
method: 'post',
|
|
url: `${API_TRAIN_FARE}/SelectStation`,
|
|
headers: {
|
|
'Content-Type': 'application/json; charset=utf-8'
|
|
},
|
|
data: JSON.stringify({ key })
|
|
})
|
|
.then(response => {
|
|
const result = response.data.d || response.data;
|
|
if (result.Success) {
|
|
this.stations = result.Data || [];
|
|
} else {
|
|
this.stations = [];
|
|
this.ShowWarning(result.Message);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('查詢車站失敗:', error);
|
|
});
|
|
}
|
|
},
|
|
|
|
async searchRecipient() {
|
|
const key = this.mainData.領款人姓名;
|
|
const identity = this.mainData.身分別;
|
|
const activationDate = this.mainData.活動日期;
|
|
|
|
if (!key || key.length === 0) {
|
|
this.recipientList = [];
|
|
this.showRecipientDropdown = false;
|
|
return;
|
|
}
|
|
|
|
if (!identity) {
|
|
this.ShowWarning('請先選擇身分別');
|
|
this.recipientList = [];
|
|
this.showRecipientDropdown = false;
|
|
return;
|
|
}
|
|
if (!activationDate) {
|
|
this.ShowWarning('請輸入活動日期');
|
|
this.recipientList = [];
|
|
this.showRecipientDropdown = false;
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'SearchRecipient', { key, identity, activationDate });
|
|
this.recipientList = result.Data || [];
|
|
this.showRecipientDropdown = this.recipientList.length > 0;
|
|
} catch (error) {
|
|
console.error('查詢領款人失敗:', error);
|
|
this.recipientList = [];
|
|
this.showRecipientDropdown = false;
|
|
this.ShowWarning(error.message);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
hideRecipientDropdown() {
|
|
setTimeout(() => {
|
|
this.showRecipientDropdown = false;
|
|
this.recipientList = [];
|
|
}, 200);
|
|
},
|
|
|
|
selectRecipient(recipient) {
|
|
this.mainData.講師_非講師編號 = recipient.Value;
|
|
this.mainData.領款人姓名 = recipient.Name;
|
|
this.mainData.身分證字號 = recipient.IdNumber;
|
|
this.mainData.憑證類別 = recipient.VoucherType;
|
|
this.laborData.扣繳類別 = recipient.ConductType;
|
|
this.recipientList = [];
|
|
this.showRecipientDropdown = false;
|
|
},
|
|
|
|
async searchInstructorEvaluation() {
|
|
const date = this.mainData.活動日期;
|
|
const teacherNo = this.mainData.講師_非講師編號;
|
|
const name = this.mainData.領款人姓名;
|
|
|
|
if (!date) {
|
|
this.ShowWarning('請輸入活動日期');
|
|
return;
|
|
}
|
|
if (!teacherNo && !name) {
|
|
this.ShowWarning('請輸入講師編號或領款人姓名');
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'SearchInstructorEvaluation', {
|
|
日期: date,
|
|
講師編號: teacherNo || '',
|
|
姓名: name || ''
|
|
});
|
|
const data = result.Data || [];
|
|
if (data.length === 0) {
|
|
this.ShowWarning('無相符講師評鑑');
|
|
} else {
|
|
const lines = data.map(item => `日期:${this.FormatDate(item.日期)},課程名稱:${item.課程名稱}`);
|
|
this.ShowSuccess(lines.join('\n'));
|
|
}
|
|
} catch (error) {
|
|
console.error('查詢講師評鑑失敗:', error);
|
|
this.ShowError('查詢講師評鑑失敗: ' + (error.message || '請稍後重試'));
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async searchPlan() {
|
|
const key = this.mainData.計畫代號;
|
|
const year = this.mainData.活動日期 ? new Date(this.mainData.活動日期).getFullYear() - 1911 : null;
|
|
|
|
if (year === null) {
|
|
this.ShowWarning("請輸入活動日期");
|
|
return;
|
|
}
|
|
|
|
if (!key || key.length === 0) {
|
|
this.planList = [];
|
|
this.showPlanDropdown = false;
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'SearchPlan', { key, year });
|
|
this.planList = result.Data || [];
|
|
this.showPlanDropdown = this.planList.length > 0;
|
|
} catch (error) {
|
|
console.error('查詢計畫失敗:', error);
|
|
this.planList = [];
|
|
this.showPlanDropdown = false;
|
|
this.ShowWarning(error.message);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
selectPlan(plan) {
|
|
this.mainData.計畫代號 = plan.Code;
|
|
this.mainData.計畫名稱 = plan.Name;
|
|
this.planList = [];
|
|
this.showPlanDropdown = false;
|
|
},
|
|
|
|
hidePlanDropdown() {
|
|
setTimeout(() => {
|
|
this.showPlanDropdown = false;
|
|
this.planList = [];
|
|
}, 200);
|
|
},
|
|
|
|
async searchOriginStation() {
|
|
const key = this.travelData.起點;
|
|
|
|
if (!key || key.length === 0) {
|
|
this.originStations = [];
|
|
this.showOriginDropdown = false;
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('火車票價', 'SelectOriginStation', { key });
|
|
this.originStations = result.Data || [];
|
|
this.showOriginDropdown = this.originStations.length > 0;
|
|
} catch (error) {
|
|
console.error('查詢起點失敗:', error);
|
|
this.originStations = [];
|
|
this.showOriginDropdown = false;
|
|
this.ShowWarning(error.message);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
selectOriginStation(station) {
|
|
this.travelData.起點 = station.Name;
|
|
this.originStations = [];
|
|
this.showOriginDropdown = false;
|
|
},
|
|
|
|
hideOriginDropdown() {
|
|
setTimeout(() => {
|
|
this.showOriginDropdown = false;
|
|
this.originStations = [];
|
|
}, 200);
|
|
this.calculateFare();
|
|
},
|
|
|
|
async searchDestStation() {
|
|
const key = this.travelData.訖點;
|
|
|
|
if (!key || key.length === 0) {
|
|
this.destStations = [];
|
|
this.showDestDropdown = false;
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('火車票價', 'SelectDestStation', { key });
|
|
this.destStations = result.Data || [];
|
|
this.showDestDropdown = this.destStations.length > 0;
|
|
} catch (error) {
|
|
console.error('查詢訖點失敗:', error);
|
|
this.destStations = [];
|
|
this.showDestDropdown = false;
|
|
this.ShowWarning(error.message);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
selectDestStation(station) {
|
|
this.travelData.訖點 = station.Name;
|
|
this.destStations = [];
|
|
this.showDestDropdown = false;
|
|
},
|
|
|
|
hideDestDropdown() {
|
|
setTimeout(() => {
|
|
this.showDestDropdown = false;
|
|
this.destStations = [];
|
|
}, 200);
|
|
this.calculateFare();
|
|
},
|
|
|
|
async calculateFare() {
|
|
const 起站 = this.travelData.起點;
|
|
const 迄站 = this.travelData.訖點;
|
|
|
|
if (!起站 || !迄站) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('火車票價', 'SelectFare', { 起站, 迄站 });
|
|
this.travelData.火車 = result.Data;
|
|
this.calculateTravelTotal();
|
|
} catch (error) {
|
|
console.error('查詢票價失敗:', error);
|
|
this.ShowWarning(error.message);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async save() {
|
|
if (!this.validate() || (this.laborWarnings || []).length > 0) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
|
|
try {
|
|
const data = {
|
|
isNew: this.mode === 'add',
|
|
mainItem: this.mainData,
|
|
laborItem: this.laborData,
|
|
travelItem: this.travelData
|
|
};
|
|
|
|
const result = await this.callApiWithErrorMsg('領據處理', 'Save', data, '儲存');
|
|
this.ShowSuccess(result.Message);
|
|
// 新增存檔更新編號變數
|
|
if (this.mode === 'add') {
|
|
this.編號 = result.Data.編號;
|
|
}
|
|
// 返回查看模式
|
|
this.mode = 'view';
|
|
this.loadData(this.編號);
|
|
this.backupData();
|
|
} catch (error) {
|
|
console.error('Save error:', error);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
validate() {
|
|
if (!this.mainData.身分別) {
|
|
this.ShowWarning('請選擇身分別');
|
|
return false;
|
|
}
|
|
if (!this.mainData.領款人姓名) {
|
|
this.ShowWarning('請輸入領款人姓名');
|
|
return false;
|
|
}
|
|
if (!this.mainData.計畫代號) {
|
|
this.ShowWarning('請輸入計畫代號');
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
returnToList() {
|
|
if ((this.mode === 'add' || this.mode === 'edit') && !confirm("是否放棄" + (this.mode === 'add' ? '新增' : '修改')))
|
|
return;
|
|
window.__doPostBack('ctl00$ContentPlaceHolder1$btnTab1', '');
|
|
},
|
|
|
|
backupData() {
|
|
this.originalMainData = JSON.parse(JSON.stringify(this.mainData));
|
|
this.originalLaborData = JSON.parse(JSON.stringify(this.laborData));
|
|
this.originalTravelData = JSON.parse(JSON.stringify(this.travelData));
|
|
},
|
|
|
|
restoreData() {
|
|
if (this.originalMainData) {
|
|
this.mainData = JSON.parse(JSON.stringify(this.originalMainData));
|
|
}
|
|
if (this.originalLaborData) {
|
|
this.laborData = JSON.parse(JSON.stringify(this.originalLaborData));
|
|
}
|
|
if (this.originalTravelData) {
|
|
this.travelData = JSON.parse(JSON.stringify(this.originalTravelData));
|
|
}
|
|
},
|
|
|
|
addNew() {
|
|
if (this.mode !== 'view') {
|
|
return;
|
|
}
|
|
this.mode = 'add';
|
|
this.reset();
|
|
this.backupData();
|
|
this.setDefault();
|
|
},
|
|
|
|
edit() {
|
|
if (this.mode !== 'view') {
|
|
return;
|
|
}
|
|
this.mode = 'edit';
|
|
this.backupData();
|
|
},
|
|
|
|
cancel() {
|
|
if (this.mode === 'view') {
|
|
return;
|
|
}
|
|
if (!confirm("是否放棄" + (this.mode === 'add' ? '新增' : '修改') + ",資料將復原?")) {
|
|
return;
|
|
}
|
|
this.restoreData();
|
|
this.mode = 'view';
|
|
},
|
|
|
|
reset() {
|
|
this.mainData = {
|
|
編號: 0,
|
|
表單編號: '',
|
|
活動日期: '',
|
|
身分別: '',
|
|
講師_非講師編號: '',
|
|
領款人姓名: '',
|
|
身分證字號: '',
|
|
憑證類別: '',
|
|
計畫代號: '',
|
|
計畫名稱: '',
|
|
活動地點: '',
|
|
活動類別: '',
|
|
活動名稱事由: '',
|
|
付款方式: '',
|
|
作廢: 0,
|
|
列印次數: 0,
|
|
DB_APPNO: 1,
|
|
DB_CRDAT: '',
|
|
DB_CRUSR: '',
|
|
DB_CRUSR_NAME: '',
|
|
DB_TRDAT: '',
|
|
DB_TRUSR: '',
|
|
DB_TRUSR_NAME: '',
|
|
};
|
|
this.laborData = {
|
|
編號: '',
|
|
領據編號: 0,
|
|
費用別: '',
|
|
單價: 0,
|
|
數量: 0,
|
|
單位: '',
|
|
扣繳類別: '',
|
|
合計金額: 0,
|
|
應扣繳稅額: 0,
|
|
服務費: 0,
|
|
作廢: 0,
|
|
DB_APPNO: 1
|
|
};
|
|
this.travelData = {
|
|
編號: '',
|
|
領據編號: 0,
|
|
票價資訊: '',
|
|
費用別: '',
|
|
起點: '',
|
|
訖點: '',
|
|
捷運公車: 0,
|
|
火車: 0,
|
|
計程車: 0,
|
|
高鐵飛機: 0,
|
|
自行開車: 0,
|
|
過路費停車費: 0,
|
|
住宿費: 0,
|
|
合計: 0,
|
|
作廢: 0,
|
|
DB_APPNO: 1
|
|
};
|
|
},
|
|
|
|
setDefault()
|
|
{
|
|
this.mainData.DB_CRDAT = this.FormatDate(new Date());
|
|
this.mainData.DB_CRUSR = document.getElementById('ctl00_hidCurrentUser').value;
|
|
this.mainData.DB_CRUSR_NAME = document.getElementById('ctl00_hidCurrentUserName').value;
|
|
},
|
|
|
|
async loadData(no) {
|
|
this.loading = true;
|
|
|
|
try {
|
|
const result = await this.callApiWithErrorMsg('領據處理', 'GetOne', { no }, '載入');
|
|
|
|
const data = result.Data;
|
|
|
|
this.mainData = {
|
|
編號: data.編號,
|
|
表單編號: data.表單編號,
|
|
活動日期: this.FormatDate(data.活動日期),
|
|
身分別: data.身分別,
|
|
講師_非講師編號: data.講師_非講師編號,
|
|
領款人姓名: data.領款人姓名,
|
|
身分證字號: data.身分證字號,
|
|
憑證類別: data.憑證類別,
|
|
計畫代號: data.計畫代號,
|
|
計畫名稱: data.計畫名稱,
|
|
活動地點: data.活動地點,
|
|
活動類別: data.活動類別,
|
|
活動名稱事由: data.活動名稱事由,
|
|
付款方式: data.付款方式,
|
|
作廢: data.作廢,
|
|
列印次數: data.列印次數,
|
|
已關帳: data.已關帳,
|
|
DB_APPNO: data.DB_APPNO,
|
|
DB_CRDAT: this.FormatDate(data.DB_CRDAT),
|
|
DB_CRUSR: data.DB_CRUSR,
|
|
DB_CRUSR_NAME: data.DB_CRUSR_NAME,
|
|
DB_TRDAT: data.DB_TRUSR !== null ? this.FormatDate(data.DB_TRDAT) : '',
|
|
DB_TRUSR: data.DB_TRUSR !== null ? data.DB_TRUSR : '',
|
|
DB_TRUSR_NAME: data.DB_TRUSR != null ? data.DB_TRUSR_NAME: ''
|
|
};
|
|
this.laborData = {
|
|
編號: data.勞務費_編號,
|
|
領據編號: data.編號,
|
|
費用別: data.勞務費_費用別,
|
|
單價: data.勞務費_單價,
|
|
數量: data.勞務費_數量,
|
|
單位: data.勞務費_單位,
|
|
扣繳類別: data.勞務費_扣繳類別,
|
|
合計金額: data.勞務費_合計金額,
|
|
應扣繳稅額: data.勞務費_應扣繳稅額,
|
|
服務費: data.勞務費_服務費,
|
|
作廢: data.勞務費_作廢
|
|
};
|
|
this.travelData = {
|
|
編號: data.差旅費_編號,
|
|
領據編號: data.編號,
|
|
票價資訊: data.差旅費_票價資訊,
|
|
費用別: data.差旅費_費用別,
|
|
起點: data.差旅費_起點,
|
|
訖點: data.差旅費_訖點,
|
|
捷運公車: data.差旅費_捷運公車,
|
|
火車: data.差旅費_火車,
|
|
計程車: data.差旅費_計程車,
|
|
高鐵飛機: data.差旅費_高鐵飛機,
|
|
自行開車: data.差旅費_自行開車,
|
|
過路費停車費: data.差旅費_過路費停車費,
|
|
住宿費: data.差旅費_住宿費,
|
|
合計: data.差旅費_合計,
|
|
作廢: data.差旅費_作廢
|
|
};
|
|
|
|
if (data.main) {
|
|
this.mainData = data.main;
|
|
}
|
|
if (data.labor) {
|
|
this.laborData = data.labor;
|
|
}
|
|
if (data.travel) {
|
|
this.travelData = data.travel;
|
|
}
|
|
this.backupData();
|
|
} catch (error) {
|
|
console.error('LoadData error:', error);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async print() {
|
|
if (this.mainData.作廢 !== 0) {
|
|
this.ShowError('已作廢不可列印');
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApiWithErrorMsg(
|
|
'領據處理',
|
|
'Print',
|
|
{ idList: [this.mainData.編號 ] },
|
|
'列印領據'
|
|
);
|
|
|
|
if (result.Success) {
|
|
// 下載 Excel 檔案
|
|
if (result.Data && result.Data.fileName) {
|
|
const link = document.createElement('a');
|
|
link.href = `data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,${result.Data.excelData}`;
|
|
link.download = result.Data.fileName;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
|
|
this.ShowSuccess(result.Message);
|
|
} else {
|
|
this.ShowWarning(result.Message);
|
|
}
|
|
} else {
|
|
this.ShowError('列印失敗: ' + (error.message || '請稍後重試'));
|
|
}
|
|
} catch (error) {
|
|
this.ShowError('列印失敗: ' + (error.message || '請檢查網路連線'));
|
|
} finally {
|
|
this.loadData(this.編號);
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async voidVoucher() {
|
|
if (this.mainData.作廢 === 0 && !confirm("是否確定作廢?") || this.mainData.作廢 !== 0 && !confirm("是否確定取消作廢?")) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'Void', {
|
|
no: this.mainData.編號
|
|
});
|
|
this.loadData(this.mainData.編號);
|
|
this.ShowSuccess('作廢操作完成');
|
|
} catch (error) {
|
|
console.error('Void error:', error);
|
|
this.ShowError('作廢操作失敗: ' + (error.message || '請檢查網路連線'));
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async voidLaborFee() {
|
|
if (this.laborData.作廢 === 0 && !confirm("是否確定作廢勞務費?") || this.laborData.作廢 !== 0 && !confirm("是否確定取消作廢勞務費?")) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'VoidLaborFee', {
|
|
receiptNo: this.mainData.編號
|
|
});
|
|
this.loadData(this.mainData.編號);
|
|
this.ShowSuccess('勞務費作廢操作完成');
|
|
} catch (error) {
|
|
console.error('VoidLaborFee error:', error);
|
|
this.ShowError('勞務費作廢操作失敗: ' + (error.message || '請檢查網路連線'));
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
async voidTravelFee() {
|
|
if (this.travelData.作廢 === 0 && !confirm("是否確定作廢差旅費?") || this.travelData.作廢 !== 0 && !confirm("是否確定取消作廢差旅費?")) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
try {
|
|
const result = await this.callApi('領據處理', 'VoidTravelFee', {
|
|
receiptNo: this.mainData.編號
|
|
});
|
|
this.loadData(this.mainData.編號);
|
|
this.ShowSuccess('差旅費作廢操作完成');
|
|
} catch (error) {
|
|
console.error('VoidTravelFee error:', error);
|
|
this.ShowError('差旅費作廢操作失敗: ' + (error.message || '請檢查網路連線'));
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
// 花花: 上下筆導航 — 初始化和執行
|
|
_initNavigation() {
|
|
const hidIdList = document.getElementById('ctl00_ContentPlaceHolder1_hidIdList');
|
|
if (hidIdList && hidIdList.value) {
|
|
try {
|
|
this.navIdList = JSON.parse(hidIdList.value);
|
|
} catch(e) {
|
|
this.navIdList = [];
|
|
}
|
|
}
|
|
// 在 idList 中找到目前編號的位置
|
|
this.navCurrentIndex = this.navIdList.indexOf(this.編號);
|
|
this.navCurrentPage = 1 * document.getElementById('ctl00_ContentPlaceHolder1_hidCurrentPage').value || 1;
|
|
this.navPageSize = 1 * document.getElementById('ctl00_ContentPlaceHolder1_hidPageSize').value || 20;
|
|
this.navTotalPages = 1 * document.getElementById('ctl00_ContentPlaceHolder1_hidNavTotalPages').value || 0;
|
|
// 從 HiddenField 讀取搜尋條件副本,供跨頁查詢使用
|
|
this.navSearchParams = {
|
|
活動日期_1: document.getElementById('ctl00_ContentPlaceHolder1_hid活動日期_1').value || null,
|
|
活動日期_2: document.getElementById('ctl00_ContentPlaceHolder1_hid活動日期_2').value || null,
|
|
身分別: document.getElementById('ctl00_ContentPlaceHolder1_hid身分別').value || null,
|
|
講師_非講師編號: null,
|
|
憑證類別: document.getElementById('ctl00_ContentPlaceHolder1_hid憑證類別').value || null,
|
|
計畫代號: document.getElementById('ctl00_ContentPlaceHolder1_hid計畫代號').value || null,
|
|
計畫名稱: document.getElementById('ctl00_ContentPlaceHolder1_hid計畫名稱').value || null,
|
|
活動類別: document.getElementById('ctl00_ContentPlaceHolder1_hid活動類別').value || null,
|
|
付款方式: document.getElementById('ctl00_ContentPlaceHolder1_hid付款方式').value || null,
|
|
領款人姓名: document.getElementById('ctl00_ContentPlaceHolder1_hid付款方式').value || null,
|
|
orderBy: ''
|
|
};
|
|
},
|
|
async goPrev() {
|
|
if (!this.hasPrev) return;
|
|
if (this.navCurrentIndex > 0) {
|
|
// 當頁內上一筆:直接從 navIdList 取
|
|
this.navCurrentIndex--;
|
|
this.編號 = this.navIdList[this.navCurrentIndex];
|
|
this.loadData(this.編號);
|
|
} else {
|
|
// 跨頁上一筆:取上一頁最後一筆
|
|
await this._fetchPageAndNavigate(this.navCurrentPage - 1, 'last');
|
|
}
|
|
},
|
|
async goNext() {
|
|
if (!this.hasNext) return;
|
|
if (this.navCurrentIndex < this.navIdList.length - 1) {
|
|
// 當頁內下一筆:直接從 navIdList 取
|
|
this.navCurrentIndex++;
|
|
this.編號 = this.navIdList[this.navCurrentIndex];
|
|
this.loadData(this.編號);
|
|
} else {
|
|
// 跨頁下一筆:取下一頁第一筆
|
|
await this._fetchPageAndNavigate(this.navCurrentPage + 1, 'first');
|
|
}
|
|
},
|
|
async _fetchPageAndNavigate(pageIndex, position) {
|
|
this.loading = true;
|
|
try {
|
|
const params = { ...this.navSearchParams, pageIndex, pageSize: this.navPageSize };
|
|
const result = await this.callApiWithErrorMsg('領據處理', 'SelectPage', params, '查詢');
|
|
const newList = this.ConvertDatesInObject(result.Data || [], '-');
|
|
if (newList.length === 0) return;
|
|
|
|
this.navIdList = newList.map(item => item.編號);
|
|
this.navCurrentPage = pageIndex;
|
|
this.navTotalPages = Math.ceil(result.TotalCount / this.navPageSize);
|
|
this.navCurrentIndex = position === 'first' ? 0 : this.navIdList.length - 1;
|
|
this.編號 = this.navIdList[this.navCurrentIndex];
|
|
|
|
// 同步更新 hidden fields,避免 postback 後狀態遺失
|
|
document.getElementById('ctl00_ContentPlaceHolder1_hidIdList').value = JSON.stringify(this.navIdList);
|
|
document.getElementById('ctl00_ContentPlaceHolder1_hidNavTotalPages').value = this.navTotalPages;
|
|
document.getElementById('ctl00_ContentPlaceHolder1_hidCurrentPage').value = this.navCurrentPage;
|
|
|
|
this.loadData(this.編號);
|
|
} catch (error) {
|
|
console.error('導航查詢失敗:', error);
|
|
this.ShowError('導航失敗: ' + (error.message || '請稍後重試'));
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
}
|
|
});
|
|
}
|