diff --git a/Controllers/ApiController.cs b/Controllers/ApiController.cs index 4a8fc399..a6581c89 100644 --- a/Controllers/ApiController.cs +++ b/Controllers/ApiController.cs @@ -13,6 +13,12 @@ using System.Data; using System; using AutoMapper; using Org.BouncyCastle.Asn1.X509; +using NPOI.XSSF.UserModel; +using NPOI.HPSF; +using NPOI.SS.UserModel; +using NPOI.HSSF.UserModel; +using System.Text; +using System.Web; namespace QuotationMaker.Controllers { @@ -21,6 +27,10 @@ namespace QuotationMaker.Controllers { private readonly IHttpContextAccessor _httpContextAccessor; + public ISheet sheet; + public FileStream fileStream; + public IWorkbook workbook = null; //新建IWorkbook對象 + DbConn dbConn = new DbConn(); SqlConnection conn = new SqlConnection(GlobalClass.appsettings("ConnectionStrings:SQLConnectionString")); SqlConnection elabConn = new SqlConnection(GlobalClass.appsettings("ConnectionStrings:ElabConnectionString")); @@ -32,6 +42,144 @@ namespace QuotationMaker.Controllers } + [Route("exportXlsx")] + public ActionResult ExportXlsx(IFormCollection obj) { + normalResult ret = new normalResult(); + + authToken token = new authToken(this._httpContextAccessor); + if (token.user_isLogin == false) + { + HttpContext.Response.Cookies.Delete("token_key"); + ret.ret = "no"; + ret.err_code = "99999"; + ret.message = "非登入狀態!"; + return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8"); + } + + string quotation_uid = obj["quotation_uid"].ToString(); + string quotation_version = obj["quotation_version"].ToString(); + + quotation objData = conn.QueryFirstOrDefault("select * from quotation where quotation_isdel = 'N' and quotation_uid = @quotation_uid and quotation_version = @quotation_version ", new { quotation_uid = quotation_uid, quotation_version = quotation_version }); + + if (objData == null) { + ret.ret = "no"; + ret.err_code = "00009"; + ret.message = "無此quotation_uid 與 quotation_version資料!"; + return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8"); + } + + quotationDetail objDetail = new quotationDetail(objData); + + string excelName = ""; + string ourCompany_name = ""; + + if (objDetail.dept_uid == "bremen") { + excelName = "bremen_temp.xlsx"; + ourCompany_name = "不來梅股份有限公司"; + } + + if (objDetail.dept_uid == "journeys") { + excelName = "journeys_temp.xlsx"; + ourCompany_name = "奇異之旅媒體股份有限公司"; + } + + string excelFullPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/logo/" + excelName); + + FileStream fileStream = new FileStream(excelFullPath, FileMode.Open, FileAccess.ReadWrite); + + workbook = new XSSFWorkbook(fileStream); + + sheet = workbook.GetSheetAt(0); //獲取第一個工作表 + + XSSFRow row; + ICell cell = null; + + row = (XSSFRow)sheet.GetRow(1); + //估價單名稱與報價日期 + row.Cells[0].SetCellValue(row.Cells[0].StringCellValue.Replace("{quotation_date}", objDetail.quotation_date)); + row.Cells[3].SetCellValue(row.Cells[3].StringCellValue.Replace("{quotation_name}", objDetail.quotation_name)); + + //估價單收件人 公司名稱 承辦人 + row = (XSSFRow)sheet.GetRow(2); + row.Cells[0].SetCellValue(row.Cells[0].StringCellValue.Replace("{contactPerson_name}", objDetail.contactPerson.contactPerson_name)); + row.Cells[2].SetCellValue(row.Cells[2].StringCellValue.Replace("{company_name}", objDetail.company.company_name)); + row.Cells[3].SetCellValue(row.Cells[3].StringCellValue.Replace("{user_name}", objDetail.user.user_name)); + + row = (XSSFRow)sheet.GetRow(3); + row.Cells[0].SetCellValue(row.Cells[0].StringCellValue.Replace("{contactPerson_email}", objDetail.contactPerson.contactPerson_email)); + row.Cells[2].SetCellValue(row.Cells[2].StringCellValue.Replace("{company_address}", objDetail.company.company_address)); + row.Cells[3].SetCellValue(row.Cells[3].StringCellValue.Replace("{user_email}", objDetail.user.user_email)); + + row = (XSSFRow)sheet.GetRow(4); + row.Cells[0].SetCellValue(row.Cells[0].StringCellValue.Replace("{company_serialNo}", objDetail.company.company_serialNo)); + row.Cells[2].SetCellValue(row.Cells[2].StringCellValue.Replace("{contactPerson_tel}", objDetail.contactPerson.contactPerson_tel)); + + //估價單契約有效期限 + UpperConvert upcov = new UpperConvert(); + string startDateStr = upcov.dateToUpper(DateTime.Parse(objDetail.quotation_expStart)); + string endDateStr = upcov.dateToUpper(DateTime.Parse(objDetail.quotation_expEnd)); + row = (XSSFRow)sheet.GetRow(5); + row.Cells[0].SetCellValue(row.Cells[0].StringCellValue.Replace("{date_range}", startDateStr + " 至 " + endDateStr)); + + //估價單總價項目 + row = (XSSFRow)sheet.GetRow(11); + row.Cells[6].SetCellValue(row.Cells[6].StringCellValue.Replace("{quotation_noTaxTotal}", objDetail.quotation_noTaxTotal.ToString("###,###"))); + row = (XSSFRow)sheet.GetRow(12); + + string quotation_specTotal = ""; + + if (objDetail.quotation_specTotal > 0) { + quotation_specTotal = objDetail.quotation_specTotal.ToString("###,###"); + } + + row.Cells[6].SetCellValue(row.Cells[6].StringCellValue.Replace("{quotation_specTotal}", quotation_specTotal)); + + row = (XSSFRow)sheet.GetRow(13); + row.Cells[6].SetCellValue(row.Cells[6].StringCellValue.Replace("{quotation_tax}", objDetail.quotation_tax.ToString("###,###"))); + + row = (XSSFRow)sheet.GetRow(14); + row.Cells[6].SetCellValue(row.Cells[6].StringCellValue.Replace("{quotation_grandTotal}", objDetail.quotation_grandTotal.ToString("###,###"))); + + //服務協議 + string services_aggrement = objDetail.quotation_sa; + services_aggrement = services_aggrement.Replace("##客戶公司##", objDetail.company.company_name + " "); + services_aggrement = services_aggrement.Replace("##我們公司##", ourCompany_name + " "); + row = (XSSFRow)sheet.GetRow(16); + row.Cells[0].SetCellValue(row.Cells[0].StringCellValue.Replace("{quotation_sa}", services_aggrement)); + + + //付款方式與發票 + string quotation_grandTotal = ChtNumConverter.ToChtNum((long)objDetail.quotation_grandTotal); + string quotation_grandTotalStr = "本專案費用總計新台幣"+ quotation_grandTotal + "圓整 (即NTD" + objDetail.quotation_grandTotal.ToString("###,###") + "含稅)"; + row = (XSSFRow)sheet.GetRow(20); + row.Cells[0].SetCellValue(quotation_grandTotalStr); + + MemoryStream ms = new MemoryStream(); + workbook.Write(ms); + ms.Flush(); + + var arrBites = ms.ToArray(); + + MemoryStream newStream = new MemoryStream(arrBites); + + string downloadName = objDetail.quotation_name + " 報價單_" + DateTime.Now.ToString("yyyy-MM-dd"); + + string agent = Request.Headers["User-Agent"].ToString(); + + if (agent.Contains("Macintosh")) + { + downloadName = HttpUtility.UrlEncode(downloadName, Encoding.UTF8); + } + + return File(newStream, "application/vnd.ms-excel", downloadName + ".xlsx"); + + + + + + return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8"); + } + [Route("saveas")] public ActionResult SaveAs(IFormCollection obj) { saveasResult ret = new saveasResult(); diff --git a/Modals/GlobalClass.cs b/Modals/GlobalClass.cs index 17765473..85dfc6b1 100644 --- a/Modals/GlobalClass.cs +++ b/Modals/GlobalClass.cs @@ -221,3 +221,197 @@ public static class GlobalClass } } } + +/// +/// 日期转换为中文大写 +/// +public class UpperConvert +{ + public UpperConvert() + { + // + // TODO: 在此处添加构造函数逻辑 + // + } + //把数字转换为大写 + public string numtoUpper(int num) + { + String str = num.ToString(); + string rstr = ""; + int n; + for (int i = 0; i < str.Length; i++) + { + n = Convert.ToInt16(str[i].ToString());//char转数字,转换为字符串,再转数字 + switch (n) + { + case 0: rstr = rstr + "〇"; break; + case 1: rstr = rstr + "一"; break; + case 2: rstr = rstr + "二"; break; + case 3: rstr = rstr + "三"; break; + case 4: rstr = rstr + "四"; break; + case 5: rstr = rstr + "五"; break; + case 6: rstr = rstr + "六"; break; + case 7: rstr = rstr + "七"; break; + case 8: rstr = rstr + "八"; break; + default: rstr = rstr + "九"; break; + } + + } + return rstr; + } + //月转化为大写 + public string monthtoUpper(int month) + { + if (month < 10) + { + return numtoUpper(month); + } + else + if (month == 10) { return "十"; } + + else + { + return "十" + numtoUpper(month - 10); + } + } + //日转化为大写 + public string daytoUpper(int day) + { + if (day < 20) + { + return monthtoUpper(day); + } + else + { + String str = day.ToString(); + if (str[1] == '0') + { + return numtoUpper(Convert.ToInt16(str[0].ToString())) + "十"; + } + else + { + return numtoUpper(Convert.ToInt16(str[0].ToString())) + "十" + + numtoUpper(Convert.ToInt16(str[1].ToString())); + } + } + } + //日期转换为大写 + public string dateToUpper(System.DateTime date) + { + int year = date.Year; + int month = date.Month; + int day = date.Day; + return numtoUpper(year) + "年" + monthtoUpper(month) + "月" + daytoUpper(day) + "日"; + + } +} + +public class ChtNumConverter +{ + public static string ChtNums = "零壹貳參肆伍陸柒捌玖"; + public static Dictionary ChtUnits = new Dictionary{ + {"拾", 10}, + {"佰", 100}, + {"千", 1000}, + {"萬", 10000}, + {"億", 100000000}, + {"兆", 1000000000000} + }; + // 解析中文數字 + public static long ParseChtNum(string chtNumString) + { + var isNegative = false; + if (chtNumString.StartsWith("負")) + { + chtNumString = chtNumString.Substring(1); + isNegative = true; + } + long num = 0; + // 處理千百十範圍的四位數 + Func Parse4Digits = (s) => + { + long lastDigit = 0; + long subNum = 0; + foreach (var rawChar in s) + { + var c = rawChar.ToString().Replace("〇", "零"); + if (ChtNums.Contains(c)) + { + lastDigit = (long)ChtNums.IndexOf(c); + } + else if (ChtUnits.ContainsKey(c)) + { + if (c == "十" && lastDigit == 0) lastDigit = 1; + long unit = ChtUnits[c]; + subNum += lastDigit * unit; + lastDigit = 0; + } + else + { + throw new ArgumentException($"包含無法解析的中文數字:{c}"); + } + } + subNum += lastDigit; + return subNum; + }; + // 以兆億萬分割四位值個別解析 + foreach (var splitUnit in "兆億萬".ToArray()) + { + var pos = chtNumString.IndexOf(splitUnit); + if (pos == -1) continue; + var subNumString = chtNumString.Substring(0, pos); + chtNumString = chtNumString.Substring(pos + 1); + num += Parse4Digits(subNumString) * ChtUnits[splitUnit.ToString()]; + } + num += Parse4Digits(chtNumString); + return isNegative ? -num : num; + } + // 轉換為中文數字 + public static string ToChtNum(long n) + { + var negtive = n < 0; + if (negtive) n = -n; + if (n >= 10000 * ChtUnits["兆"]) + throw new ArgumentException("數字超出可轉換範圍"); + var unitChars = "千佰拾".ToArray(); + // 處理 0000 ~ 9999 範圍數字 + Func Conv4Digits = (subNum) => + { + var sb = new StringBuilder(); + foreach (var c in unitChars) + { + if (subNum >= ChtUnits[c.ToString()]) + { + var digit = subNum / ChtUnits[c.ToString()]; + subNum = subNum % ChtUnits[c.ToString()]; + sb.Append($"{ChtNums[(int)digit]}{c}"); + } + else sb.Append("零"); + } + sb.Append(ChtNums[(int)subNum]); + return sb.ToString(); + }; + var numString = new StringBuilder(); + var forceRun = false; + foreach (var splitUnit in "兆億萬".ToArray()) + { + var unit = ChtUnits[splitUnit.ToString()]; + if (n < unit) + { + if (forceRun) numString.Append("零"); + continue; + } + forceRun = true; + var subNum = n / unit; + n = n % unit; + if (subNum > 0) + numString.Append(Conv4Digits(subNum).TrimEnd('零') + splitUnit); + else numString.Append("零"); + } + numString.Append(Conv4Digits(n)); + var t = Regex.Replace(numString.ToString(), "[零]+", "零"); + if (t.Length > 1) t = t.Trim('零'); + t = Regex.Replace(t, "^壹拾", "拾"); + return (negtive ? "負" : string.Empty) + t; + } +} diff --git a/Modals/resultClass.cs b/Modals/resultClass.cs index 81c12069..afb52752 100644 --- a/Modals/resultClass.cs +++ b/Modals/resultClass.cs @@ -4,6 +4,12 @@ using Dapper; using static DbTableClass; public class resultClass { + public class normalResult + { + public string ret = "no"; + public string err_code = "0000"; + public string message = ""; + } public class saveasResult { public string ret = "no"; @@ -172,6 +178,9 @@ public class resultClass public List quotationMainItemDetails = new List(); public List payments = new List(); public List invoices = new List(); + public user user = new user(); + public company company = new company(); + public contactPerson contactPerson = new contactPerson(); public quotationDetail() { } @@ -192,7 +201,9 @@ public class resultClass foreach (quotationMainItem qItem in quotationMainItems) { quotationMainItemDetails.Add(new quotationMainItemDetail(qItem)); } - + contactPerson = conn.QueryFirstOrDefault("select * from contactPerson where contactPerson_uid = @contactPerson_uid ", new { contactPerson_uid = this.contactPerson_uid }); + company = conn.QueryFirstOrDefault("select * from company where company_uid = @company_uid", new { company_uid = this.company_uid} ); + user = conn.QueryFirstOrDefault("select * from users where user_uid = @user_uid", new { user_uid = this.quotation_create_uid}); payments = conn.Query("select * from payment where payment_version = @payment_version and quotation_uid = @quotation_uid ", new { payment_version = this.quotation_version, quotation_uid = this.quotation_uid }).ToList(); invoices = conn.Query("select * from invoice where invoice_version = @invoice_version and quotation_uid = @quotation_uid ", new { invoice_version = this.quotation_version, quotation_uid = this.quotation_uid }).ToList(); } diff --git a/Views/Shared/_LooperLayout.cshtml b/Views/Shared/_LooperLayout.cshtml index f4bcae8d..dca5da26 100644 --- a/Views/Shared/_LooperLayout.cshtml +++ b/Views/Shared/_LooperLayout.cshtml @@ -250,6 +250,7 @@ + @RenderSection("Script", required: false) diff --git a/wwwroot/assets/javascript/custom/projectlist.js b/wwwroot/assets/javascript/custom/projectlist.js index d453c2f6..1ef75492 100644 --- a/wwwroot/assets/javascript/custom/projectlist.js +++ b/wwwroot/assets/javascript/custom/projectlist.js @@ -1910,6 +1910,15 @@ function buttonQuotationClick(obj, view) { quotationRowPos = quotationTable.fnGetPosition($('#' + uid)[0]); + if (type == "excel") { + var formData = { + quotation_uid: uid, + quotation_version: version + } + + $.redirect('/Api/exportXlsx', formData); + } + if (type == "history") { var formData = { method: 'get', @@ -2281,9 +2290,10 @@ function loadQuotationTable() { render: function render(data, type, row, meta) { var ret = ''; - ret += ''; - ret += ''; - ret += ''; + ret += ' '; + ret += ' '; + ret += ' '; + ret += ''; return ret; } } diff --git a/wwwroot/logo/bremen.png b/wwwroot/logo/bremen.png new file mode 100644 index 00000000..898cda1c Binary files /dev/null and b/wwwroot/logo/bremen.png differ diff --git a/wwwroot/logo/bremen.xlsx b/wwwroot/logo/bremen.xlsx new file mode 100644 index 00000000..d930e01a Binary files /dev/null and b/wwwroot/logo/bremen.xlsx differ diff --git a/wwwroot/logo/bremen_temp.xlsx b/wwwroot/logo/bremen_temp.xlsx new file mode 100644 index 00000000..c37458f9 Binary files /dev/null and b/wwwroot/logo/bremen_temp.xlsx differ diff --git a/wwwroot/logo/journeys.png b/wwwroot/logo/journeys.png new file mode 100644 index 00000000..d9465f4e Binary files /dev/null and b/wwwroot/logo/journeys.png differ diff --git a/wwwroot/logo/journeys.xlsx b/wwwroot/logo/journeys.xlsx new file mode 100644 index 00000000..dca52671 Binary files /dev/null and b/wwwroot/logo/journeys.xlsx differ diff --git a/wwwroot/logo/journeys_temp.xlsx b/wwwroot/logo/journeys_temp.xlsx new file mode 100644 index 00000000..32d02f48 Binary files /dev/null and b/wwwroot/logo/journeys_temp.xlsx differ