diff --git a/JQSalesSummaryPanel.Designer.cs b/JQSalesSummaryPanel.Designer.cs
index 491deca..e324326 100644
--- a/JQSalesSummaryPanel.Designer.cs
+++ b/JQSalesSummaryPanel.Designer.cs
@@ -117,6 +117,7 @@
this.initForecastBtn.TabIndex = 2;
this.initForecastBtn.Text = "button1";
this.initForecastBtn.UseVisualStyleBackColor = true;
+ this.initForecastBtn.Click += new System.EventHandler(this.initForecastBtn_Click);
//
// flowLayoutPanel2
//
diff --git a/JQSalesSummaryPanel.cs b/JQSalesSummaryPanel.cs
index 7274fc9..8c30393 100644
--- a/JQSalesSummaryPanel.cs
+++ b/JQSalesSummaryPanel.cs
@@ -1,4 +1,5 @@
using KellyReport_D.Properties;
+using KellyReport_D.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -53,5 +54,11 @@ namespace KellyReport_D
);
step2DescLab.Size = new Size(width, textSize.Height + padding);
}
+
+ private void initForecastBtn_Click(object sender, EventArgs e)
+ {
+ WorkBookUtils.GenForecast(this.initForecastBtn);
+
+ }
}
}
diff --git a/KellyReport_D.csproj b/KellyReport_D.csproj
index aefbbb3..575efa8 100644
--- a/KellyReport_D.csproj
+++ b/KellyReport_D.csproj
@@ -218,9 +218,14 @@
JQSalesSummaryPanel.cs
+
+
+
+
Code
+
JQSalesSummaryPanel.cs
diff --git a/Model/ClientContact.cs b/Model/ClientContact.cs
new file mode 100644
index 0000000..c392924
--- /dev/null
+++ b/Model/ClientContact.cs
@@ -0,0 +1,47 @@
+public class ClientContact
+{
+ ///
+ /// 地区
+ ///
+ public string Region { get; set; }
+
+ ///
+ /// 行业
+ ///
+ public string Industry { get; set; }
+
+ ///
+ /// 产能/年
+ ///
+ public string AnnualCapacity { get; set; }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 采购主管
+ ///
+ public string PurchasingManager { get; set; }
+
+ ///
+ /// 采购主管电话
+ ///
+ public string PurchasingManagerPhone { get; set; }
+
+ ///
+ /// 采购
+ ///
+ public string Purchaser { get; set; }
+
+ ///
+ /// 采购电话
+ ///
+ public string PurchaserPhone { get; set; }
+
+ ///
+ /// 地址
+ ///
+ public string Address { get; set; }
+}
diff --git a/Model/GenForecaseContext.cs b/Model/GenForecaseContext.cs
new file mode 100644
index 0000000..0ebb906
--- /dev/null
+++ b/Model/GenForecaseContext.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace KellyReport_D.Model
+{
+ internal class GenForecaseContext
+ {
+ public Microsoft.Office.Interop.Excel.Application Application { get; set; }
+
+ public string[] MonthData { get; set; }
+ public IList SalesForecastList { get; set; }
+ public IList ProductForecastList { get; set; }
+ public IList ClientContactList { get; set; }
+ }
+}
diff --git a/Model/ProductForecastInput.cs b/Model/ProductForecastInput.cs
new file mode 100644
index 0000000..b5b1bc1
--- /dev/null
+++ b/Model/ProductForecastInput.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace KellyReport_D.Model
+{
+ internal class ProductForecastInput
+ {
+ ///
+ /// 年份
+ ///
+ public int Year { get; set; }
+
+ ///
+ /// 产品名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 数量
+ ///
+ public decimal Quantity { get; set; }
+ ///
+ /// 金额(含税)
+ ///
+ public decimal AmountTax { get; set; }
+ ///
+ /// 金额(未税)
+ ///
+ public decimal Amount { get; set; }
+ ///
+ /// 平均单价
+ ///
+ public decimal PriceAvg { get; set; }
+ }
+}
diff --git a/Model/SalesForecastInput.cs b/Model/SalesForecastInput.cs
new file mode 100644
index 0000000..587fc47
--- /dev/null
+++ b/Model/SalesForecastInput.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace KellyReport_D.Model
+{
+ internal class SalesForecastInput
+ {
+ ///
+ /// 年度
+ ///
+ public int Year { get; set; }
+
+ ///
+ /// 销售名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 总计
+ ///
+ public decimal Total { get; set; }
+
+ ///
+ /// 一月
+ ///
+ public decimal January { get; set; }
+
+ ///
+ /// 二月
+ ///
+ public decimal February { get; set; }
+
+ ///
+ /// 三月
+ ///
+ public decimal March { get; set; }
+
+ ///
+ /// 四月
+ ///
+ public decimal April { get; set; }
+
+ ///
+ /// 五月
+ ///
+ public decimal May { get; set; }
+
+ ///
+ /// 六月
+ ///
+ public decimal June { get; set; }
+
+ ///
+ /// 七月
+ ///
+ public decimal July { get; set; }
+
+ ///
+ /// 八月
+ ///
+ public decimal August { get; set; }
+
+ ///
+ /// 九月
+ ///
+ public decimal September { get; set; }
+
+ ///
+ /// 十月
+ ///
+ public decimal October { get; set; }
+
+ ///
+ /// 十一月
+ ///
+ public decimal November { get; set; }
+
+ ///
+ /// 十二月
+ ///
+ public decimal December { get; set; }
+
+ public List MonthlyForecast
+ {
+ get
+ {
+ return new List
+ {
+ January, February, March, April, May, June,
+ July, August, September, October, November, December
+ };
+ }
+ }
+ }
+}
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
index 84ae4f9..c644985 100644
--- a/Properties/Resources.Designer.cs
+++ b/Properties/Resources.Designer.cs
@@ -69,6 +69,15 @@ namespace KellyReport_D.Properties {
}
}
+ ///
+ /// 查找类似 正在生成... 的本地化字符串。
+ ///
+ public static string GENERATING {
+ get {
+ return ResourceManager.GetString("GENERATING", resourceCulture);
+ }
+ }
+
///
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
///
diff --git a/Properties/Resources.resx b/Properties/Resources.resx
index 2f425e5..57e4eaa 100644
--- a/Properties/Resources.resx
+++ b/Properties/Resources.resx
@@ -120,6 +120,9 @@
JQ销售年度预估表
+
+ 正在生成...
+
..\Resources\icon_32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/Utils/WorkBookUtils.cs b/Utils/WorkBookUtils.cs
new file mode 100644
index 0000000..3e7e3b8
--- /dev/null
+++ b/Utils/WorkBookUtils.cs
@@ -0,0 +1,287 @@
+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;
+
+namespace KellyReport_D.Utils
+{
+ internal static class WorkBookUtils
+ {
+ 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);
+ // 获取「客户资料」工作表
+ readClientContact(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(GenForecaseContext 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;
+ }
+ }
+}