using Education.Forms.領據系統; using Education.Report; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Linq; using System.Web; using System.Web.Script.Services; using System.Web.Services; using WebLib; namespace Education.Services { [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] [ScriptService] public class 領據處理 : ServiceBase { [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SelectPage( int pageIndex = 1, int pageSize = 20, DateTime? 活動日期_1 = null, DateTime? 活動日期_2 = null, string 身分別 = null, string 講師_非講師編號 = null, string 憑證類別 = null, string 計畫代號 = null, string 計畫名稱 = null, string 活動類別 = null, string 付款方式 = null, string 領款人姓名 = null, string orderBy = "") { try { DAC_領據 dac = new DAC_領據(); int startRowIndex = (pageIndex - 1) * pageSize; var data = dac.SelectPageWithRelations( noInputReturnAll: true, startRowIndex: startRowIndex, maximumRows: pageSize, 活動日期_1: 活動日期_1, 活動日期_2: 活動日期_2, 身分別: 身分別, 講師_非講師編號: 講師_非講師編號, 憑證類別: 憑證類別, 計畫代號: 計畫代號, 計畫名稱: 計畫名稱, 活動類別: 活動類別, 付款方式: 付款方式, 領款人姓名: 領款人姓名, orderBy: orderBy ); int totalCount = dac.SelectCount( noInputReturnAll: true, 活動日期_1: 活動日期_1, 活動日期_2: 活動日期_2, 身分別: 身分別, 講師_非講師編號: 講師_非講師編號, 憑證類別: 憑證類別, 計畫代號: 計畫代號, 計畫名稱: 計畫名稱, 活動類別: 活動類別, 付款方式: 付款方式, 領款人姓名: 領款人姓名 ); return ServiceResponse.CreateSuccess("查詢成功", data, totalCount); } catch (Exception ex) { return ServiceResponse.CreateError($"查詢失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse GetOne(int no) { try { using (var conn = DAC.NewConnection()) { DAC_領據 dacMain = new DAC_領據(conn); DAC_勞務費 dacLabor = new DAC_勞務費(conn); DAC_差旅費 dacTravel = new DAC_差旅費(conn); DAC_BPSN dacBPSN = new DAC_BPSN(conn); var mainRec = dacMain.SelectOne(no).FirstOrDefault(); if (mainRec == null) { return ServiceResponse.CreateError("沒有此筆資料"); } mainRec.DB_CRUSR_NAME = dacBPSN.Get員工姓名(mainRec.DB_CRUSR); mainRec.DB_TRUSR_NAME = dacBPSN.Get員工姓名(mainRec.DB_TRUSR); var existingLabor = dacLabor.SelectOne(no).FirstOrDefault(); if (existingLabor != null) { mainRec.勞務費_編號 = existingLabor.編號; mainRec.勞務費_費用別 = existingLabor.費用別; mainRec.勞務費_單價 = existingLabor.單價; mainRec.勞務費_數量 = existingLabor.數量; mainRec.勞務費_單位 = existingLabor.單位; mainRec.勞務費_扣繳類別 = existingLabor.扣繳類別; mainRec.勞務費_合計金額 = existingLabor.合計金額; mainRec.勞務費_應扣繳稅額 = existingLabor.應扣繳稅額; mainRec.勞務費_服務費 = existingLabor.服務費; mainRec.勞務費_作廢 = existingLabor.作廢; } var existingTravel = dacTravel.SelectOne(no).FirstOrDefault(); if (existingTravel != null) { mainRec.差旅費_編號 = existingTravel.編號; mainRec.差旅費_票價資訊 = existingTravel.票價資訊; mainRec.差旅費_費用別 = existingTravel.費用別; mainRec.差旅費_起點 = existingTravel.起點; mainRec.差旅費_訖點 = existingTravel.訖點; mainRec.差旅費_捷運公車 = existingTravel.捷運公車; mainRec.差旅費_火車 = existingTravel.火車; mainRec.差旅費_計程車 = existingTravel.計程車; mainRec.差旅費_高鐵飛機 = existingTravel.高鐵飛機; mainRec.差旅費_自行開車 = existingTravel.自行開車; mainRec.差旅費_過路費停車費 = existingTravel.過路費停車費; mainRec.差旅費_住宿費 = existingTravel.住宿費; mainRec.差旅費_合計 = existingTravel.合計; mainRec.差旅費_作廢 = existingTravel.作廢; } return ServiceResponse.CreateSuccess("OK", data: mainRec); } } catch (Exception ex) { return ServiceResponse.CreateError($"取得資料失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse Save(bool isNew, 領據Item mainItem, 勞務費Item laborItem, 差旅費Item travelItem) { try { using (var ts = DAC.NewTransactionScope()) { using (var conn = DAC.NewConnection()) { DAC_領據 dacMain = new DAC_領據(conn); DAC_勞務費 dacLabor = new DAC_勞務費(conn); DAC_差旅費 dacTravel = new DAC_差旅費(conn); // 後端驗證:勞務費有金額但費用別或單位未填 bool hasLaborAmount = laborItem != null && ((laborItem.單價 ?? 0) > 0 || (laborItem.數量 ?? 0) > 0 || (laborItem.合計金額 ?? 0) > 0); if (hasLaborAmount) { if (string.IsNullOrEmpty(laborItem.費用別)) return ServiceResponse.CreateError("勞務費已輸入金額,請選擇費用別"); if (string.IsNullOrEmpty(laborItem.單位)) return ServiceResponse.CreateError("勞務費已輸入金額,請選擇單位"); } if (isNew) { mainItem = dacMain.InsertOne(mainItem); if (laborItem != null && !dacLabor.IsEmpty(laborItem)) { laborItem.領據編號 = mainItem.編號; dacLabor.InsertOne(laborItem); } if (travelItem != null && !dacTravel.IsEmpty(travelItem)) { travelItem.領據編號 = mainItem.編號; dacTravel.InsertOne(travelItem); } ts.Complete(); return ServiceResponse.CreateSuccess("新增成功", mainItem); } else { dacMain.UpdateOne(mainItem); if (laborItem != null && laborItem.領據編號 > 0) { var existingLabor = dacLabor.SelectOne(mainItem.編號); if (existingLabor.Count > 0) { laborItem.DB_APPNO = existingLabor.FirstOrDefault().DB_APPNO; dacLabor.UpdateOne(laborItem); } else if (!dacLabor.IsEmpty(laborItem)) { dacLabor.InsertOne(laborItem); } } if (travelItem != null && travelItem.領據編號 > 0) { var existingTravel = dacTravel.SelectOne(mainItem.編號); if (existingTravel.Count > 0) { travelItem.DB_APPNO = existingTravel.FirstOrDefault().DB_APPNO; dacTravel.UpdateOne(travelItem); } else if (!dacTravel.IsEmpty(travelItem)) { dacTravel.InsertOne(travelItem); } } ts.Complete(); return ServiceResponse.CreateSuccess("修改成功"); } } } } catch (Exception ex) { return ServiceResponse.CreateError($"儲存失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse Delete(int no, int dbAppNo) { try { using (var ts = DAC.NewTransactionScope()) { using (var conn = DAC.NewConnection()) { DAC_領據 dacMain = new DAC_領據(conn); DAC_勞務費 dacLabor = new DAC_勞務費(conn); DAC_差旅費 dacTravel = new DAC_差旅費(conn); var existingLabor = dacLabor.SelectOne(no); if (existingLabor.Count > 0) { dacLabor.DeleteOne(no, existingLabor[0].DB_APPNO); } var existingTravel = dacTravel.SelectOne(no); if (existingTravel.Count > 0) { dacTravel.DeleteOne(no, existingTravel[0].DB_APPNO); } dacMain.DeleteOne(no, dbAppNo); ts.Complete(); return ServiceResponse.CreateSuccess("刪除成功"); } } } catch (Exception ex) { return ServiceResponse.CreateError($"刪除失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse Void(int no) { try { using (var conn = DAC.NewConnection()) { DAC_領據 dacMain = new DAC_領據(conn); DAC_勞務費 dacLabor = new DAC_勞務費(conn); DAC_差旅費 dacTravel = new DAC_差旅費(conn); DAC_組織人員 dacOrgnization = new DAC_組織人員(conn); var mainItem = dacMain.SelectOne(no).FirstOrDefault(); if (mainItem == null) { return ServiceResponse.CreateError("找不到指定的領據"); } mainItem.作廢 = (mainItem.作廢 == 1) ? 0 : 1; // 取消作廢只有總管理處財務部可以執行 if (mainItem.作廢 == 0 && !dacOrgnization.Is人員屬於組織(PublicVariable.財務部ID, CurrentUserId)) { return ServiceResponse.CreateError("財務部人員才可以取消作廢"); } var laborItem = dacLabor.SelectOne(no).FirstOrDefault(); var travelItem = dacTravel.SelectOne(no).FirstOrDefault(); dacMain.UpdateOne(mainItem); if (laborItem != null) { laborItem.作廢 = mainItem.作廢; dacLabor.UpdateOne(laborItem); } if (travelItem != null) { travelItem.作廢 = mainItem.作廢; dacTravel.UpdateOne(travelItem); } string action = mainItem.作廢 == 1 ? "作廢" : "取消作廢"; return ServiceResponse.CreateSuccess($"{action}成功"); } } catch (Exception ex) { return ServiceResponse.CreateError($"作廢操作失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse VoidLaborFee(int receiptNo) { try { using (var conn = DAC.NewConnection()) { DAC_勞務費 dacLabor = new DAC_勞務費(conn); DAC_組織人員 dacOrgnization = new DAC_組織人員(conn); var item = dacLabor.SelectOne(receiptNo).FirstOrDefault(); if (item == null) { return ServiceResponse.CreateError("找不到指定的勞務費資料"); } item.作廢 = (item.作廢 == 1) ? 0 : 1; // 取消作廢只有總管理處財務部可以執行 if (item.作廢 == 0 && !dacOrgnization.Is人員屬於組織(PublicVariable.財務部ID, CurrentUserId)) { return ServiceResponse.CreateError("財務部人員才可以取消作廢"); } dacLabor.UpdateOne(item); string action = item.作廢 == 1 ? "作廢" : "取消作廢"; return ServiceResponse.CreateSuccess($"勞務費{action}成功"); } } catch (Exception ex) { return ServiceResponse.CreateError($"勞務費作廢操作失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse VoidTravelFee(int receiptNo) { try { using (var conn = DAC.NewConnection()) { DAC_差旅費 dacTravel = new DAC_差旅費(conn); DAC_組織人員 dacOrgnization = new DAC_組織人員(conn); var item = dacTravel.SelectOne(receiptNo).FirstOrDefault(); if (item == null) { return ServiceResponse.CreateError("找不到指定的差旅費資料"); } item.作廢 = (item.作廢 == 1) ? 0 : 1; // 取消作廢只有總管理處財務部可以執行 if (item.作廢 == 0 && !dacOrgnization.Is人員屬於組織(PublicVariable.財務部ID, CurrentUserId)) { return ServiceResponse.CreateError("財務部人員才可以取消作廢"); } dacTravel.UpdateOne(item); string action = item.作廢 == 1 ? "作廢" : "取消作廢"; return ServiceResponse.CreateSuccess($"差旅費{action}成功"); } } catch (Exception ex) { return ServiceResponse.CreateError($"差旅費作廢操作失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse CalcTax(int laborFee, string category) { try { int tax = 0; if (category != null) { if ((category.StartsWith("9A") || category.StartsWith("9B")) && laborFee > PublicVariable.領據執行業務所得課稅門檻) { tax = (int)Math.Round(laborFee * PublicVariable.領據執行業務所得稅率, MidpointRounding.AwayFromZero); } else if (category.StartsWith("50") && laborFee >= PublicVariable.領據薪資課稅門檻) { tax = (int)Math.Round(laborFee * PublicVariable.領據薪資稅率, MidpointRounding.AwayFromZero); } } return ServiceResponse.CreateSuccess("計算成功", tax); } catch (Exception ex) { return ServiceResponse.CreateError($"計算稅額失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SearchRecipient(string key, string identity, DateTime? activationDate) { try { if (string.IsNullOrEmpty(key)) { return ServiceResponse.CreateSuccess("查詢成功", new List()); } var result = new List(); using (var conn = DAC.NewConnection()) { if (identity == "講師") { DAC_講師 dacInstructor = new DAC_講師(conn); DAC_付款設定 dacPaymentSetting = new DAC_付款設定(conn); var instructors = dacInstructor.SelectByKeyword(key); foreach (var item in instructors) { var paymentSetting = dacPaymentSetting.SelectRecentlyActivated(item.付款設定講師編號, activationDate); result.Add(new { Name = item.姓名, IdNumber = item.身份證字號, Value = item.編號.ToString(), VoucherType = paymentSetting?.憑證類別, ConductType = paymentSetting?.扣繳類別, }); } } else if (identity == "非講師") { DAC_非講師 dacNonInstructor = new DAC_非講師(conn); DAC_付款設定 dacPaymentSetting = new DAC_付款設定(conn); var nonInstructors = dacNonInstructor.SelectByKeyword(key); foreach (var item in nonInstructors) { var paymentSetting = dacPaymentSetting.SelectRecentlyActivated(item.編號, activationDate); result.Add(new { Name = item.姓名, IdNumber = item.身分證字號, Value = item.編號, VoucherType = paymentSetting?.憑證類別, ConductType = paymentSetting?.扣繳類別, }); } } } if (result.Count > 0) return ServiceResponse.CreateSuccess("查詢成功", result); return ServiceResponse.CreateError($"無符合資料"); } catch (Exception ex) { return ServiceResponse.CreateError($"搜尋失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SearchInstructorEvaluation(DateTime? 日期, string 講師編號, string 姓名) { try { if (日期 == null) { return ServiceResponse.CreateError("請輸入活動日期"); } using (var conn = DAC.NewConnection()) { int? teacherNo = null; // 有編號就查編號,沒有編號查姓名 if (!string.IsNullOrEmpty(講師編號) && int.TryParse(講師編號.Trim(), out int parsedNo)) { teacherNo = parsedNo; } else if (!string.IsNullOrEmpty(姓名)) { DAC_講師 dac講師 = new DAC_講師(conn); var teacher = dac講師.Select(false, 姓名.Trim(), null, null) .FirstOrDefault(t => t.姓名 == 姓名.Trim()); if (teacher != null) { teacherNo = teacher.編號; } } if (teacherNo == null) { return ServiceResponse.CreateSuccess("查詢成功", new List()); } DAC_講師外訓滿意度 dacEval = new DAC_講師外訓滿意度(conn); var evals = dacEval.Select(false, null, teacherNo, 日期, 日期, null, null, null, null, null); var result = evals.Select(e => new { e.日期, e.課程名稱 }).ToList(); return ServiceResponse.CreateSuccess("查詢成功", result); } } catch (Exception ex) { return ServiceResponse.CreateError($"查詢講師評鑑失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SearchPlan(string key, int year = 0) { try { if (string.IsNullOrEmpty(key)) { return ServiceResponse.CreateSuccess("查詢成功", new List()); } key = key.ToUpper(); var result = new List(); DAC_BPAA dac = new DAC_BPAA(); DataTable dtPlans, dtPlansAdditional = null; if (year > 0) { dtPlans = dac.GetBpaaWithTYPlusNone(year); if (year == 115) { dtPlansAdditional = dac.GetBpaaWithTYPlusNone(114); } } else { dtPlans = dac.GetBpaaWithTYPlusNone(); } if (dtPlans != null && dtPlans.Rows.Count > 0) { if (dtPlansAdditional != null) { foreach (DataRow row in dtPlansAdditional.Select("BPPYN = '114AG114003'")) { dtPlans.ImportRow(row); } } foreach (DataRow row in dtPlans.Rows) { string planCode = row["BPPYN"].ToString(); string planName = row["BPPYM"].ToString(); if (planCode.Contains(key) || planName.Contains(key)) { result.Add(new { Name = planName, Code = planCode, Value = planCode }); } } } if (result.Count > 0) return ServiceResponse.CreateSuccess("查詢成功", result); return ServiceResponse.CreateError($"無符合資料"); } catch (Exception ex) { return ServiceResponse.CreateError($"搜尋計畫失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse GetDropdownLists(string key) { try { NameValueList result = null; switch (key) { case "身分別": result = 身分別.InputSelectList; break; case "憑證類別": result = 憑證類別.InputSelectList; break; case "付款方式": result = 付款方式.InputSelectList; break; case "扣繳類別": result = 扣繳類別.InputSelectList; break; case "勞務費_費用別": result = 勞務費_費用別.InputSelectList; break; case "差旅費_費用別": result = 差旅費_費用別.InputSelectList; break; case "單位別": result = 單位別.InputSelectList; break; } if (result != null) return ServiceResponse.CreateSuccess("取得下拉選單資料成功", result); return ServiceResponse.CreateError($"取得下拉選單資料失敗:找不到對應的下拉選單"); } catch (Exception ex) { return ServiceResponse.CreateError($"取得下拉選單資料失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse GetSearchDropdownLists(string key) { try { NameValueList result = null; switch (key) { case "身分別": result = 身分別.SearchSelectList; break; case "憑證類別": result = 憑證類別.SearchSelectList; break; case "付款方式": result = 付款方式.SearchSelectList; break; case "扣繳類別": result = 扣繳類別.SearchSelectList; break; case "勞務費_費用別": result = 勞務費_費用別.SearchSelectList; break; case "差旅費_費用別": result = 差旅費_費用別.SearchSelectList; break; case "單位別": result = 單位別.SearchSelectList; break; } if (result != null) return ServiceResponse.CreateSuccess("取得下拉選單資料成功", result); return ServiceResponse.CreateError($"取得下拉選單資料失敗:找不到對應的下拉選單"); } catch (Exception ex) { return ServiceResponse.CreateError($"取得查詢下拉選單資料失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse GetPhraseLists() { try { using (var conn = DAC.NewConnection()) { var daoPhrase = new DAC_片語(conn); var result = new { activityLocation = daoPhrase.SelectBy適用欄位("活動地點").Select(p => p.片語內容).ToList(), activityCategory = daoPhrase.SelectBy適用欄位("活動類別").Select(p => p.片語內容).ToList(), ticketPrice = daoPhrase.SelectBy適用欄位("票價資訊").Select(p => p.片語內容).ToList() }; return ServiceResponse.CreateSuccess("取得片語清單成功", result); } } catch (Exception ex) { return ServiceResponse.CreateError($"取得片語清單失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse GetClosingMonth() { try { // 返回預設值,實際實作應該從 BWEX 表查詢 // BSNUM = '領據關帳年月', BSITM = ' 1', BSNAM = 關帳年月 (YYYY-MM 格式) var result = new { currentClosingMonth = new DAC_BWEX().ReadSetting("領據關帳年月", 1) }; return ServiceResponse.CreateSuccess("取得關帳年月成功", result); } catch (Exception ex) { return ServiceResponse.CreateError($"取得關帳年月失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse UpdateClosingMonth(string closingMonth) { try { if (string.IsNullOrEmpty(closingMonth)) { return ServiceResponse.CreateError("關帳年月不能為空"); } // 驗證格式 YYYY-MM if (!System.Text.RegularExpressions.Regex.IsMatch(closingMonth, @"^\d{4}-\d{2}$")) { return ServiceResponse.CreateError("關帳年月格式不正確,請使用 YYYY-MM 格式"); } // 實作應該在此處更新 BWEX 表 PublicVariable.領據關帳年月 = closingMonth; return ServiceResponse.CreateSuccess("關帳年月已成功更新"); } catch (Exception ex) { return ServiceResponse.CreateError($"更新關帳年月失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse Print(List idList) { try { if (idList == null || idList.Count == 0) { return ServiceResponse.CreateError("未指定要列印的領據"); } try { using (var ts = DAC.NewTransactionScope()) { using (var conn = DAC.NewConnection()) { DAC_領據 dacMain = new DAC_領據(conn); DAC_勞務費 dacLabor = new DAC_勞務費(conn); DAC_差旅費 dacTravel = new DAC_差旅費(conn); DAC_講師 dac講師 = new DAC_講師(conn); DAC_非講師 dac非講師 = new DAC_非講師(conn); // 收集所有要列印的領據資料 List printDataList = new List(); // 無資料清單 List noDataList = new List(); foreach (var id in idList) { var mainRec = dacMain.SelectOne(id).FirstOrDefault(); if (mainRec == null) { continue; } if (mainRec.作廢 != 0) { noDataList.Add(mainRec.表單編號); continue; } // 取得講師非講師資料 講師 講師 = null; 非講師Item 非講師 = null; if (mainRec.身分別 == "講師") { 講師 = dac講師.SelectOne(DAC.GetInt32(mainRec.講師_非講師編號)).FirstOrDefault(); } else { 非講師 = dac非講師.SelectOne(mainRec.講師_非講師編號).FirstOrDefault(); } // 取得勞務費資料 var laborItem = dacLabor.SelectOne(id).Where(p => p.作廢 != 1).FirstOrDefault(); if (laborItem != null) { // 若勞務費編號為空,需要給勞務費自動編號 if (string.IsNullOrEmpty(laborItem.編號) && !laborItem.IsEmpty) { laborItem.編號 = dacLabor.Next單號(mainRec.活動日期, "勞務費", "編號", "'T'CCCMM<9999>", ""); dacLabor.UpdateOne(laborItem); } } // 取得差旅費資料 var travelItem = dacTravel.SelectOne(id).Where(p => p.作廢 != 1).FirstOrDefault(); if (travelItem != null && travelItem.合計.HasValue && travelItem.合計 > 0) { // 若差旅費編號為空,需要給差旅費自動編號 if (string.IsNullOrEmpty(travelItem.編號) && !travelItem.IsEmpty) { travelItem.編號 = dacTravel.Next單號(mainRec.活動日期, "差旅費", "編號", "'E'CCCMM<9999>", ""); dacTravel.UpdateOne(travelItem); } } if (laborItem != null && !laborItem.IsEmpty || travelItem != null && !travelItem.IsEmpty) { if (mainRec.憑證類別 == 憑證類別.憑證類別_領據) { mainRec.列印次數 = mainRec.列印次數.HasValue ? mainRec.列印次數.Value + 1 : 1; dacMain.UpdateOne(mainRec); printDataList.Add(new PrintData { Main = mainRec, Labor = laborItem, Travel = travelItem, 講師 = 講師, 非講師 = 非講師 }); } else { noDataList.Add($"{mainRec.表單編號} 憑證類別不是領據"); } } else { noDataList.Add($"{mainRec.表單編號} 無資料或已作廢"); } } // 更新單據欄位 ts.Complete(); if (printDataList.Count == 0) { return ServiceResponse.CreateSuccess($"{string.Join("、", noDataList.ToArray())}"); } // 生成 Excel 檔案 Report.領據列印 report = new Report.領據列印(); System.IO.MemoryStream ms = report.DoReport(printDataList); // 轉換為 Base64 編碼 string excelBase64 = Convert.ToBase64String(ms.ToArray()); // 生成檔案名稱 string fileName = string.Format("領據{0}_{1:yyyyMMddHHmmss}.xlsx" , (printDataList.Count > 0 ? string.Format("_{0}_{1}", printDataList[0].Main.領款人姓名, printDataList[0].Main.表單編號) : String.Empty) , PublicVariable.TaiwanNow); return ServiceResponse.CreateSuccess("列印成功", new { fileName = fileName, excelData = excelBase64, count = printDataList.Count }); } } } catch (Exception innerEx) { return ServiceResponse.CreateError(string.Format("列印處理失敗:{0}", innerEx.Message)); } } catch (Exception ex) { return ServiceResponse.CreateError(string.Format("列印失敗:{0}", ex.Message)); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse Export( DateTime? 活動日期_1 = null, DateTime? 活動日期_2 = null, string 身分別 = null, string 講師_非講師編號 = null, string 憑證類別 = null, string 計畫代號 = null, string 計畫名稱 = null, string 活動類別 = null, string 付款方式 = null, string 領款人姓名 = null, string orderBy = "") { try { using (var conn = DAC.NewConnection()) { DAC_領據 dacMain = new DAC_領據(conn); var data = dacMain.SelectPageWithRelations( noInputReturnAll: true, startRowIndex: 0, maximumRows: Int32.MaxValue, 活動日期_1: 活動日期_1, 活動日期_2: 活動日期_2, 身分別: 身分別, 講師_非講師編號: 講師_非講師編號, 憑證類別: 憑證類別, 計畫代號: 計畫代號, 計畫名稱: 計畫名稱, 活動類別: 活動類別, 付款方式: 付款方式, 領款人姓名: 領款人姓名, orderBy: orderBy ); // 生成 Excel 檔案 Report.領據匯出 report = new Report.領據匯出(); report.data = data; System.IO.MemoryStream ms = report.DoReport(); // 轉換為 Base64 編碼 string excelBase64 = Convert.ToBase64String(ms.ToArray()); // 生成檔案名稱 string fileName = string.Format("領據匯出_{0:yyyyMMdd_HHmmss}.xlsx", PublicVariable.TaiwanNow); return ServiceResponse.CreateSuccess("匯出成功", new { fileName = fileName, excelData = excelBase64 }); } } catch (Exception ex) { return ServiceResponse.CreateError(string.Format("匯出失敗:{0}", ex.Message)); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse GetVariables() { return ServiceResponse.CreateSuccess("OK", data: new { 領據執行業務所得課稅門檻 = PublicVariable.領據執行業務所得課稅門檻, 領據薪資課稅門檻 = PublicVariable.領據薪資課稅門檻, 領據基本工資 = PublicVariable.領據基本工資, 領據執行業務所得稅率 = PublicVariable.領據執行業務所得稅率, 領據薪資稅率 = PublicVariable.領據薪資稅率 }); } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SearchSummaryTable( string 起日, string 迄日, string 身分別, string 付款方式, string 輸出方式) { try { // 解析日期參數 DateTime? startDate = null; DateTime? endDate = null; if (!string.IsNullOrEmpty(起日)) { if (DateTime.TryParse(起日, out var parsedStartDate)) { startDate = parsedStartDate; } } if (!string.IsNullOrEmpty(迄日)) { if (DateTime.TryParse(迄日, out var parsedEndDate)) { endDate = parsedEndDate; } } // 驗證輸出方式 if (string.IsNullOrEmpty(輸出方式) || (輸出方式 != "DATA" && 輸出方式 != "EXPORT")) { return ServiceResponse.CreateError("輸出方式無效"); } using (var conn = DAC.NewConnection()) { var dao領據 = new DAC_領據(conn); var summaryData = dao領據.SelectSummary(true, 0, int.MaxValue, startDate, endDate, 身分別, 付款方式); if (輸出方式 == "DATA") { return ServiceResponse.CreateSuccess("查詢成功", summaryData); } else { var report = new 講師個人費用彙整表(); report.起日 = startDate; report.迄日 = endDate; report.身分別 = 身分別; report.付款方式 = 付款方式; report.data = summaryData; var ms = report.DoReport(); // 轉換為 Base64 編碼 string excelBase64 = Convert.ToBase64String(ms.ToArray()); var excelResult = new { excelData = excelBase64, fileName = $"講師個人費用彙整表_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx" }; return ServiceResponse.CreateSuccess("匯出成功", excelResult); } } } catch (Exception ex) { return ServiceResponse.CreateError($"查詢失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SearchDetailTable( string 起日, string 迄日, string 身分別, string 憑證類別, string 輸出方式) { try { // 解析日期參數 DateTime? startDate = null; DateTime? endDate = null; if (!string.IsNullOrEmpty(起日)) { if (DateTime.TryParse(起日, out var parsedStartDate)) { startDate = parsedStartDate; } } if (!string.IsNullOrEmpty(迄日)) { if (DateTime.TryParse(迄日, out var parsedEndDate)) { endDate = parsedEndDate; } } // 驗證輸出方式 if (string.IsNullOrEmpty(輸出方式) || (輸出方式 != "DATA" && 輸出方式 != "EXPORT_SUMMARY" && 輸出方式 != "EXPORT_DETAIL")) { return ServiceResponse.CreateError("輸出方式無效"); } using (var conn = DAC.NewConnection()) { var dao領據 = new DAC_領據(conn); if (輸出方式 == "EXPORT_SUMMARY") { var detailData = dao領據.SelectDetail(true, 0, int.MaxValue, startDate, endDate, 身分別, 憑證類別); var report = new 扣繳彙整表(); report.起日 = startDate; report.迄日 = endDate; report.身分別 = 身分別; report.憑證類別 = 憑證類別; report.data = detailData; var ms = report.DoReport(); // 轉換為 Base64 編碼 string excelBase64 = Convert.ToBase64String(ms.ToArray()); var excelResult = new { excelData = excelBase64, fileName = $"扣繳彙整表_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx" }; return ServiceResponse.CreateSuccess("匯出成功", excelResult); } else if (輸出方式 == "EXPORT_DETAIL") { var detailData = dao領據.SelectDetail(true, 0, int.MaxValue, startDate, endDate, 身分別, 憑證類別); var reportData = new List<扣繳明細Item>(); foreach (var d in detailData) { reportData.AddRange(d.明細); } var report = new 扣繳明細表(); report.起日 = startDate; report.迄日 = endDate; report.身分別 = 身分別; report.憑證類別 = 憑證類別; report.data = reportData; var ms = report.DoReport(); // 轉換為 Base64 編碼 string excelBase64 = Convert.ToBase64String(ms.ToArray()); var excelResult = new { excelData = excelBase64, fileName = $"扣繳明細表_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx" }; return ServiceResponse.CreateSuccess("匯出成功", excelResult); } else { var detailData = dao領據.SelectDetail(true, 0, int.MaxValue, startDate, endDate, 身分別, 憑證類別); return ServiceResponse.CreateSuccess("查詢成功", detailData); } } } catch (Exception ex) { return ServiceResponse.CreateError($"查詢失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse SendEmail( string 起日, string 迄日, string 身分別, string 憑證類別, string 輸出方式, List 講師非講師編號, string 寄件人郵箱, string 郵件主旨, string 郵件內文) { try { // 解析日期參數 DateTime? startDate = null; DateTime? endDate = null; if (!string.IsNullOrEmpty(起日)) { if (DateTime.TryParse(起日, out var parsedStartDate)) { startDate = parsedStartDate; } } if (!string.IsNullOrEmpty(迄日)) { if (DateTime.TryParse(迄日, out var parsedEndDate)) { endDate = parsedEndDate; } } // 驗證輸出方式 if (string.IsNullOrEmpty(輸出方式) || (輸出方式 != "PREVIEW" && 輸出方式 != "SENDMAIL")) { return ServiceResponse.CreateError("輸出方式無效"); } // 驗證輸入參數 if (講師非講師編號 == null || 講師非講師編號.Count == 0) { return ServiceResponse.CreateError("請選擇至少一個收件者"); } if (string.IsNullOrEmpty(寄件人郵箱)) { return ServiceResponse.CreateError("請輸入寄件人郵箱"); } if (string.IsNullOrEmpty(郵件主旨)) { return ServiceResponse.CreateError("請輸入郵件主旨"); } if (string.IsNullOrEmpty(郵件內文)) { return ServiceResponse.CreateError("請輸入郵件內文"); } using (var conn = DAC.NewConnection()) { var dao領據 = new DAC_領據(conn); var detailData = dao領據.SelectDetail(true, 0, int.MaxValue, startDate, endDate, 身分別, 憑證類別); var previewData = new List<扣繳匯總郵件Item>(); foreach (var d in detailData) { if (講師非講師編號.FirstOrDefault(x => x == d.講師_非講師編號) == null) continue; var p = new 扣繳匯總郵件Item(); p.收件人姓名 = d.領款人姓名; p.收件人郵箱 = d.電子郵件; p.主旨 = 郵件主旨; p.內文 = 郵件內文; p.明細 = d.明細; p.講師勞務費合計 = p.明細.Sum(x => x.講師勞務費); p.應扣繳稅額合計 = p.明細.Sum(x => x.應扣繳稅額); p.服務費合計 = p.明細.Sum(x => x.服務費); previewData.Add(p); } if (輸出方式 == "PREVIEW") { return ServiceResponse.CreateSuccess("預覽成功", previewData); } else { var 發送郵件 = new 發送郵件(); 發送郵件.Context = this.Context; var successCount = 0; var failCount = 0; var failMessages = new List(); foreach (var d in previewData) { var sendResult = 發送郵件.發送扣繳明細表郵件(寄件人郵箱, startDate, endDate, d, out string message); if (!sendResult) { failCount++; failMessages.Add(message); } else { successCount++; } } if (failCount == 0) return ServiceResponse.CreateSuccess($"郵件已成功發送給 {successCount} 位收件者"); else return ServiceResponse.CreateSuccess($"郵件已成功發送給 {successCount} 位收件者,但有 {failCount} 封發送失敗"); } } } catch (Exception ex) { return ServiceResponse.CreateError($"處理失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse ValidateImportFile(string filePath) { try { if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { return ServiceResponse.CreateError("檔案不存在"); } // 驗證副檔名 string extension = System.IO.Path.GetExtension(filePath).ToLower(); if (extension != ".xls" && extension != ".xlsx") { return ServiceResponse.CreateError("只允許上傳 .xls 或 .xlsx 檔案"); } try { // 驗證欄位格式 var importer = new 領據匯入(); List errors = importer.Validate(filePath, typeof(領據匯入Item), out int rowCount); if (errors.Count > 0) { // 清除臨時檔案 if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); return ServiceResponse.CreateSuccess("檔案驗證失敗", new { Success = false, Errors = errors, RowCount = 0 }); } // 驗證欄位內容 List contentErrors = importer.ValidateContent(filePath); if (contentErrors.Count > 0) { // 清除臨時檔案 if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); return ServiceResponse.CreateSuccess("檔案驗證失敗", new { Success = false, Errors = contentErrors, RowCount = 0 }); } // 驗證重複資料 List duplicateErrors; List duplicateWarnings; using (var conn = DAC.NewConnection()) { importer.ValidateDuplicates(filePath, conn, out duplicateErrors, out duplicateWarnings); } if (duplicateErrors.Count > 0) { // 與資料庫重複超過 5 筆,不允許匯入 if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); return ServiceResponse.CreateSuccess("檔案驗證失敗", new { Success = false, Errors = duplicateErrors, RowCount = 0 }); } // 儲存檔案路徑到 Session,以便匯入時使用 HttpContext.Current.Session["ImportFilePath"] = filePath; return ServiceResponse.CreateSuccess("驗證成功", new { Success = true, Errors = new List(), Warnings = duplicateWarnings, RowCount = rowCount }); } catch (Exception ex) { // 清除臨時檔案 if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); return ServiceResponse.CreateError($"驗證過程中發生錯誤:{ex.Message}"); } } catch (Exception ex) { return ServiceResponse.CreateError($"驗證失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse ImportReceiptData() { try { // 從 Session 中取得檔案路徑 string filePath = HttpContext.Current?.Session?["ImportFilePath"]?.ToString(); if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) { return ServiceResponse.CreateError("檔案不存在或已過期,請重新上傳"); } try { var importer = new 領據匯入(); using (var conn = DAC.NewConnection()) { importer.Connection = conn; // 讀取資料列表 var items = importer.ReadRows<領據匯入Item>(filePath, 1); if (items.Count == 0) { return ServiceResponse.CreateError("沒有可匯入的資料"); } // 逐筆插入資料 int successCount = 0; int failCount = 0; List failedRows = new List(); for (int i = 0; i < items.Count; i++) { try { importer.InsertOne<領據匯入Item>(items[i]); successCount++; } catch (Exception ex) { failCount++; string errorMsg = importer.ImportRowErrorMessage<領據匯入Item>(items[i], ex.Message); failedRows.Add($"第 {i + 2} 列:{errorMsg}"); } } // 清除 Session HttpContext.Current.Session["ImportFilePath"] = null; // 刪除臨時檔案 if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); return ServiceResponse.CreateSuccess("匯入完成", new { successCount = successCount, failCount = failCount, failedRows = failedRows, totalCount = items.Count }); } } catch (Exception ex) { // 清除臨時檔案 if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath); return ServiceResponse.CreateError($"匯入過程中發生錯誤:{ex.Message}"); } } catch (Exception ex) { return ServiceResponse.CreateError($"匯入失敗:{ex.Message}"); } } [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public ServiceResponse DownloadImportTemplate() { try { var report = new 領據匯出(); report.範本 = true; // 查詢活動類別片語清單,填入儲存格註釋 using (var conn = DAC.NewConnection()) { var daoPhrase = new DAC_片語(conn); var phrases = daoPhrase.SelectBy適用欄位("活動類別"); if (phrases.Count > 0) { var 活動類別Comment = string.Join("\n", phrases.Select(p => p.片語內容).ToArray()); var col = report.Columns.FirstOrDefault(x => x.Key == "活動類別"); if (col != null) col.Comment = 活動類別Comment; } // 查詢單位別下拉選單,填入儲存格註釋(排除「請選擇」) var 單位別List = 單位別.InputSelectList; if (單位別List != null && 單位別List.Count > 0) { var 單位Comment = string.Join("\n", 單位別List.Where(x => !string.IsNullOrEmpty(x.Value.ToString())).Select(x => x.Name).ToArray()); var col = report.Columns.FirstOrDefault(x => x.Key == "單位"); if (col != null) col.Comment = 單位Comment; } } var ms = report.DoReport(); // 轉換為 Base64 編碼 string excelBase64 = Convert.ToBase64String(ms.ToArray()); return ServiceResponse.CreateSuccess("下載成功", new { excelData = excelBase64, fileName = "領據資料匯入格式.xlsx" }); } catch (Exception ex) { return ServiceResponse.CreateError($"下載失敗:{ex.Message}"); } } } [Serializable] public class 領據處理Dropdowns { public NameValueList 身分別 { get; set; } public NameValueList 憑證類別 { get; set; } public NameValueList 付款方式 { get; set; } public NameValueList 扣繳類別 { get; set; } public NameValueList 勞務費_費用別 { get; set; } public NameValueList 差旅費_費用別 { get; set; } public NameValueList 單位別 { get; set; } } public class PrintData { public 領據Item Main { get; set; } public 講師 講師 { get; set; } public 非講師Item 非講師 { get; set; } public 勞務費Item Labor { get; set; } public 差旅費Item Travel { get; set; } } }