using KellyReport_D.Properties; using System.Windows.Forms; using Microsoft.Office.Interop.Excel; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using KellyReport_D.Model; using System.Windows.Input; namespace KellyReport_D.Utils { internal static class WorkBookUtils { public static void importSalesDetailFromWorkBook(Workbook workbook) { var importContext = new ImportContext() { Application = Globals.ThisAddIn.Application, ImportWorkbook = workbook }; // 获取「客户资料」工作表 readClientContact(importContext); // 读取「销售明细」工作表 ReadSalesDetails(importContext); foreach (var userGroupedSalesDetail in importContext.GroupedSalesDetailBySalesName) { var userSheet = GenUserSalesDetailSheet(importContext, userGroupedSalesDetail); PrepareTotalHeader(userSheet); WriteSalesDetailsData(importContext, userGroupedSalesDetail, userSheet); } } public static void WriteSalesDetailsData( ImportContext context, ImportContext.GroupedSalesDetailModel userGroupedSalesDetail, Worksheet userSheet ) { int rowIndex = 4; // 从第四行开始写入数据 var clientGroupedSalesDetail = context.GetGroupedSalesDetailByClientName(userGroupedSalesDetail.Details); foreach (var salesDetail in clientGroupedSalesDetail) { ClientContact clientContact = context.ClientContactList.FirstOrDefault(x => x.Name == salesDetail.ClientName) ?? new ClientContact { Name = salesDetail.ClientName, }; userSheet.Cells[rowIndex, 1].Value2 = salesDetail.SalesName; userSheet.Cells[rowIndex, 2].Value2 = clientContact.Region; userSheet.Cells[rowIndex, 3].Value2 = clientContact.Industry; userSheet.Cells[rowIndex, 4].Value2 = clientContact.AnnualCapacity; userSheet.Cells[rowIndex, 5].Value2 = clientContact.Name; userSheet.Cells[rowIndex, 6].Value2 = clientContact.PurchasingManager; userSheet.Cells[rowIndex, 7].Value2 = clientContact.PurchasingManagerPhone; userSheet.Cells[rowIndex, 8].Value2 = clientContact.Purchaser; userSheet.Cells[rowIndex, 9].Value2 = clientContact.PurchaserPhone; userSheet.Cells[rowIndex, 10].Value2 = clientContact.Address; userSheet.Cells[rowIndex, 11].Value2 = salesDetail.ProductNameEN; rowIndex++; } } public static void PrepareTotalHeader(Worksheet userSheet) { // 合并A1:L1并设置标题 userSheet.Range["A1", "L1"].Merge(); userSheet.Range["A1"].Value2 = "客戶資料"; // 合并并设置各列标题 userSheet.Range["A2", "A3"].Merge(); userSheet.Range["A2"].Value2 = "業務"; userSheet.Range["B2", "B3"].Merge(); userSheet.Range["B2"].Value2 = "地區"; userSheet.Range["C2", "C3"].Merge(); userSheet.Range["C2"].Value2 = "行業"; userSheet.Range["D2", "D3"].Merge(); userSheet.Range["D2"].Value2 = "產能/年"; userSheet.Range["E2", "E3"].Merge(); userSheet.Range["E2"].Value2 = "名稱"; userSheet.Range["F2", "F3"].Merge(); userSheet.Range["F2"].Value2 = "採購主管"; userSheet.Range["G2", "G3"].Merge(); userSheet.Range["G2"].Value2 = "電話"; userSheet.Range["H2", "H3"].Merge(); userSheet.Range["H2"].Value2 = "採購"; userSheet.Range["I2", "I3"].Merge(); userSheet.Range["I2"].Value2 = "電話"; userSheet.Range["J2", "J3"].Merge(); userSheet.Range["J2"].Value2 = "地址"; userSheet.Range["K2", "K3"].Merge(); userSheet.Range["K2"].Value2 = "產品"; userSheet.Range["L2", "L3"].Merge(); userSheet.Range["L2"].Value2 = "需求/月"; // 计算起始列(L列后面,列号13) int startCol = 13; // 合并年度销售表标题 userSheet.Range[userSheet.Cells[1, startCol], userSheet.Cells[1, startCol + 37]].Merge(); userSheet.Cells[1, startCol].Value2 = "年度销售表"; // 设置月份表头 for (int i = 0; i < 12; i++) { int monthCol = startCol + i * 3; userSheet.Range[userSheet.Cells[2, monthCol], userSheet.Cells[2, monthCol + 2]].Merge(); userSheet.Cells[2, monthCol].Value2 = $"{i + 1}月"; userSheet.Cells[3, monthCol].Value2 = "單價"; userSheet.Cells[3, monthCol + 1].Value2 = "數量"; userSheet.Cells[3, monthCol + 2].Value2 = "金額"; } // 合并Total表头 userSheet.Range[userSheet.Cells[2, startCol + 36], userSheet.Cells[2, startCol + 37]].Merge(); userSheet.Cells[2, startCol + 36].Value2 = "Total"; userSheet.Cells[3, startCol + 36].Value2 = "總數量"; userSheet.Cells[3, startCol + 37].Value2 = "總金額"; // 设置所有已用单元格居中 Range usedRange = userSheet.UsedRange; usedRange.HorizontalAlignment = XlHAlign.xlHAlignCenter; } private static Worksheet GenUserSalesDetailSheet( Model.ImportContext context, Model.ImportContext.GroupedSalesDetailModel groupedSalesDetail ) { var app = context.Application; var salesName = groupedSalesDetail.SalesName ?? "UNKONW"; string sheetName = $"JQ Total({salesName.ToUpper()})"; Worksheet userSheet = null; try { // 新建工作表并命名 userSheet = app.Worksheets.Add(); userSheet.Name = sheetName; } catch { // 已存在则获取 userSheet = app.Worksheets[sheetName] as Worksheet; try { Range usedRange = userSheet.UsedRange; if (usedRange != null && usedRange.Cells.Count > 1) { usedRange.Clear(); } } catch { // 无 UsedRange 可忽略 } } return userSheet; } private static void ReadSalesDetails(ImportContext context) { var dataRange = context.ImportWorkbook.Sheets[1].UsedRange; IList salesDatailList = new List(); if (dataRange.Value2 is object[,] rawSalesDetailData) { int rowCount = rawSalesDetailData.GetLength(0); int colCount = rawSalesDetailData.GetLength(1); for (int i = 3; i <= rowCount; i++) { SalesDetail item = new SalesDetail { SalesName = rawSalesDetailData[i, 1]?.ToString() ?? "", Month = rawSalesDetailData[i, 2]?.ToString() ?? "", InvoiceDate = rawSalesDetailData[i, 3] is double invoiceDate ? DateTime.FromOADate(invoiceDate) : (DateTime?)null, OrderNumber = rawSalesDetailData[i, 4]?.ToString() ?? "", DeliveryDate = rawSalesDetailData[i, 5] is double deliveryDate ? DateTime.FromOADate(deliveryDate) : (DateTime?)null, InvoiceNumber = rawSalesDetailData[i, 6]?.ToString() ?? "", ClientName = rawSalesDetailData[i, 7]?.ToString() ?? "", ProductNameEN = rawSalesDetailData[i, 8]?.ToString() ?? "", Quantity = rawSalesDetailData[i, 9] != null ? Convert.ToDecimal(rawSalesDetailData[i, 9]) : (decimal?)null, PriceWithTax = rawSalesDetailData[i, 10] != null ? Convert.ToDecimal(rawSalesDetailData[i, 10]) : (decimal?)null, AmountWithoutTax = rawSalesDetailData[i, 11] != null ? Convert.ToDecimal(rawSalesDetailData[i, 11]) : (decimal?)null, Tax = rawSalesDetailData[i, 12] != null ? Convert.ToDecimal(rawSalesDetailData[i, 12]) : (decimal?)null, TotalAmount = rawSalesDetailData[i, 13] != null ? Convert.ToDecimal(rawSalesDetailData[i, 13]) : (decimal?)null, Remark = rawSalesDetailData[i, 14]?.ToString() ?? "", }; salesDatailList.Add(item); } } context.SalesDetails = salesDatailList; } public static Workbook SelectAndOpenExcelFile() { var app = Globals.ThisAddIn.Application; var curWorkbook = app.ActiveWorkbook; using (OpenFileDialog dialog = new OpenFileDialog()) { dialog.Filter = "Excel 文件 (*.xlsx)|*.xlsx"; dialog.Title = "请选择要打开的 Excel 文件"; if (dialog.ShowDialog() == DialogResult.OK) { string filePath = dialog.FileName; Workbook workbook = app.Workbooks.Open(filePath); curWorkbook.Activate(); return workbook; } } return null; } public static async void GenForecast(System.Windows.Forms.Button button) { String originLabel = button.Text; if (button != null) { button.Text = Resources.GENERATING; } var app = Globals.ThisAddIn.Application; var context = new GenForecaseContext { Application = app }; try { // 获取「銷售年度預估」工作表 readSalesForecastData(context); // 获取「產品年度預估」工作表 readProductForecastData(context); GenBussenessSheet(context); } catch (Exception ex) { Console.WriteLine("Error generating forecast:" + ex.Message); MessageBox.Show("分析预估表失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } button.Text = originLabel; } public static void GenBussenessSheet(GenForecaseContext context) { var app = context.Application; // 假设 app 是 Excel.Application Worksheet sheet = null; string sheetName = "業務月達成率"; try { // 新增工作表 sheet = app.Worksheets.Add(); sheet.Name = sheetName; } catch { // 已存在则获取 sheet = app.Worksheets[sheetName] as Worksheet; try { Range usedRange = sheet.UsedRange; if (usedRange != null && usedRange.Cells.Count > 1) { usedRange.Clear(); } } catch { // 无 UsedRange 可忽略 } } // 合并A1:A2并填入年份 try { var monthData = context.MonthData; var yearValue = monthData[0]; Range yearRange = sheet.Range["A1", "A2"]; yearRange.Merge(); yearRange.Value2 = yearValue.ToString(); yearRange.NumberFormat = "@"; // 文本格式 // 填入月份 for (int i = 1; i < monthData.Length; i++) { sheet.Cells[i + 2, 1] = monthData[i]; } // B列开始填 salesName 及相关数据 int col = 2; // B列 foreach (var salesForcastData in context.SalesForecastList) { int rowIndex = 1; // Excel 行号从1开始 int startCol = col; int endCol = col + 4; // 合并并填入 salesName Range salesNameRange = sheet.Range[ sheet.Cells[rowIndex, startCol], sheet.Cells[rowIndex, startCol + 1] ]; salesNameRange.Merge(); salesNameRange.Value2 = salesForcastData.Name; // 合并并填入“完成%” Range finishRange = sheet.Range[ sheet.Cells[rowIndex, startCol + 2], sheet.Cells[rowIndex, startCol + 2] ]; finishRange.Merge(); finishRange.Value2 = "完成%"; // 合并并填入“累積達成率” Range accRange = sheet.Range[ sheet.Cells[rowIndex, startCol + 3], sheet.Cells[rowIndex, startCol + 3] ]; accRange.Merge(); accRange.Value2 = "累積達成率"; // 第二行填入 Y-forecast 和 turnover rowIndex++; sheet.Cells[rowIndex, startCol] = "Y-forecast"; sheet.Cells[rowIndex, startCol + 1] = "turnover"; // 填入每月数据 rowIndex++; foreach (var forcastMonth in salesForcastData.MonthlyForecast) { var cell = sheet.Cells[rowIndex, startCol]; cell.Value2 = forcastMonth; cell.NumberFormat = "#,##0"; rowIndex++; } col = endCol; } // 自适应所有已用列宽 Range usedRange2 = sheet.UsedRange; if (usedRange2 != null) { usedRange2.Columns.AutoFit(); } } catch (Exception ex) { MessageBox.Show("写入业绩月达成率时发生错误:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private static void readClientContact(ImportContext context) { var app = context.Application; var clientContactSheet = app.Worksheets["客戶資料"] as Worksheet; var clientContactRange = clientContactSheet.UsedRange; IList clientContactList = new List(); if (clientContactRange.Value2 is object[,] rawContactData) { int rowCount = rawContactData.GetLength(0); int colCount = rawContactData.GetLength(1); // 模板第二行开始是数据 for (int i = 2; i <= rowCount; i++) { var input = new ClientContact { Region = rawContactData[i, 1]?.ToString() ?? "", Industry = rawContactData[i, 2]?.ToString() ?? "", AnnualCapacity = rawContactData[i, 3]?.ToString() ?? "", Name = rawContactData[i, 4]?.ToString() ?? "", PurchasingManager = rawContactData[i, 5]?.ToString() ?? "", PurchasingManagerPhone = rawContactData[i, 6]?.ToString() ?? "", Purchaser = rawContactData[i, 7]?.ToString() ?? "", PurchaserPhone = rawContactData[i, 8]?.ToString() ?? "", Address = rawContactData[i, 9]?.ToString() ?? "" }; clientContactList.Add(input); } } context.ClientContactList = clientContactList; } private static void readProductForecastData(GenForecaseContext context) { var app = context.Application; var productForecastSheet = app.Worksheets["產品年度預估"] as Worksheet; var productRange = productForecastSheet.UsedRange; IList productForecastList = new List(); if (productRange.Value2 is object[,] rawProductData) { int rowCount = rawProductData.GetLength(0); int colCount = rawProductData.GetLength(1); if (rawProductData[1, 2] == null) { throw new MissingMemberException("「產品年度預估」工作表的(年份)不能为空,请检查数据格式。"); } var year = Convert.ToInt32(rawProductData[1, 2]); // 模板第三行开始是数据 for (int i = 3; i <= rowCount; i++) { var input = new ProductForecastInput { Year = year, Name = rawProductData[i, 1]?.ToString() ?? "", Quantity = rawProductData[i, 2] != null ? Convert.ToDecimal(rawProductData[i, 2]) : 0, AmountTax = rawProductData[i, 3] != null ? Convert.ToDecimal(rawProductData[i, 3]) : 0, Amount = rawProductData[i, 4] != null ? Convert.ToDecimal(rawProductData[i, 4]) : 0, PriceAvg = rawProductData[i, 5] != null ? Convert.ToDecimal(rawProductData[i, 5]) : 0, }; productForecastList.Add(input); } } context.ProductForecastList = productForecastList; } private static void readSalesForecastData(GenForecaseContext context) { var app = context.Application; var salesForecastSheet = app.Worksheets["銷售年度預估"] as Worksheet; var salesRange = salesForecastSheet.UsedRange; IList salesForecastInputList = new List(); string[] monthData = new string[13]; if (salesRange.Value2 is object[,] rawSalesData) { int rowCount = rawSalesData.GetLength(0); int colCount = rawSalesData.GetLength(1); if (rawSalesData[1, 2] == null) { throw new MissingMemberException("「銷售年度預估」工作表的(年份)不能为空,请检查数据格式。"); } var year = Convert.ToInt32(rawSalesData[1, 2]); monthData[0] = year.ToString(); for (int i = 1; i <= 12; i++) { monthData[i] = rawSalesData[2, i + 1].ToString(); } // 模板第三行开始是数据 for (int i = 3; i <= rowCount; i++) { var input = new SalesForecastInput { Year = year, Name = rawSalesData[i, 1]?.ToString() ?? "", January = rawSalesData[i, 2] != null ? Convert.ToDecimal(rawSalesData[i, 2]) : 0, February = rawSalesData[i, 3] != null ? Convert.ToDecimal(rawSalesData[i, 3]) : 0, March = rawSalesData[i, 4] != null ? Convert.ToDecimal(rawSalesData[i, 4]) : 0, April = rawSalesData[i, 5] != null ? Convert.ToDecimal(rawSalesData[i, 5]) : 0, May = rawSalesData[i, 6] != null ? Convert.ToDecimal(rawSalesData[i, 6]) : 0, June = rawSalesData[i, 7] != null ? Convert.ToDecimal(rawSalesData[i, 7]) : 0, July = rawSalesData[i, 8] != null ? Convert.ToDecimal(rawSalesData[i, 8]) : 0, August = rawSalesData[i, 9] != null ? Convert.ToDecimal(rawSalesData[i, 9]) : 0, September = rawSalesData[i, 10] != null ? Convert.ToDecimal(rawSalesData[i, 10]) : 0, October = rawSalesData[i, 11] != null ? Convert.ToDecimal(rawSalesData[i, 11]) : 0, November = rawSalesData[i, 12] != null ? Convert.ToDecimal(rawSalesData[i, 12]) : 0, December = rawSalesData[i, 13] != null ? Convert.ToDecimal(rawSalesData[i, 13]) : 0, Total = rawSalesData[i, 14] != null ? Convert.ToDecimal(rawSalesData[i, 14]) : 0, }; salesForecastInputList.Add(input); } } context.SalesForecastList = salesForecastInputList; context.MonthData = monthData; } } }