diff --git a/KellyReport_D.csproj b/KellyReport_D.csproj
index 32343c0..7b147fc 100644
--- a/KellyReport_D.csproj
+++ b/KellyReport_D.csproj
@@ -34,7 +34,7 @@
publish\
zh-chs
- 1.0.0.4
+ 1.0.0.7
true
true
7
@@ -227,6 +227,7 @@
Code
+
JQSalesSummaryPanel.cs
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 9489fc0..54a36a3 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -34,5 +34,5 @@ using System.Security;
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.6")]
diff --git a/Ribbon1.cs b/Ribbon1.cs
index ba7c104..c0219a1 100644
--- a/Ribbon1.cs
+++ b/Ribbon1.cs
@@ -3,7 +3,9 @@ using Microsoft.Office.Tools;
using Microsoft.Office.Tools.Ribbon;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
+using System.Reflection;
using System.Text;
namespace KellyReport_D
@@ -12,6 +14,20 @@ namespace KellyReport_D
{
private CustomTaskPane jqSalesSummaryPanel;
+ private string GetAppVersion()
+ {
+ var asm = Assembly.GetExecutingAssembly();
+ try
+ {
+ var fvi = FileVersionInfo.GetVersionInfo(asm.Location);
+ if (!string.IsNullOrWhiteSpace(fvi.FileVersion))
+ return fvi.FileVersion;
+ }
+ catch { }
+
+ return asm.GetName().Version.ToString();
+ }
+
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
this.button1.Label = Resources.APP_NAME;
@@ -23,7 +39,7 @@ namespace KellyReport_D
if (jqSalesSummaryPanel == null)
{
var panel = new JQSalesSummaryPanel();
- jqSalesSummaryPanel = Globals.ThisAddIn.CustomTaskPanes.Add(panel, Resources.APP_NAME);
+ jqSalesSummaryPanel = Globals.ThisAddIn.CustomTaskPanes.Add(panel, $"{Resources.APP_NAME} v{GetAppVersion()}");
jqSalesSummaryPanel.Width = 600;
}
diff --git a/Utils/FileLogger.cs b/Utils/FileLogger.cs
new file mode 100644
index 0000000..3ccb624
--- /dev/null
+++ b/Utils/FileLogger.cs
@@ -0,0 +1,47 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace KellyReport_D.Utils
+{
+ internal static class FileLogger
+ {
+ private static readonly object _sync = new object();
+ private static string LogDirectory =>
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "KellyReport", "logs");
+
+ private static string GetLogFilePath()
+ {
+ string fileName = $"kellyreport_{DateTime.Now:yyyyMMdd}.log";
+ return Path.Combine(LogDirectory, fileName);
+ }
+
+ private static void WriteInternal(string level, string message, Exception ex = null)
+ {
+ try
+ {
+ lock (_sync)
+ {
+ Directory.CreateDirectory(LogDirectory);
+ var path = GetLogFilePath();
+ using (var sw = new StreamWriter(path, true, Encoding.UTF8))
+ {
+ sw.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] {message}");
+ if (ex != null)
+ {
+ sw.WriteLine(ex.ToString());
+ }
+ }
+ }
+ }
+ catch
+ {
+ // 日志写入不应影响主流程,忽略任何异常
+ }
+ }
+
+ public static void Info(string message) => WriteInternal("INFO", message);
+ public static void Debug(string message) => WriteInternal("DEBUG", message);
+ public static void Error(string message, Exception ex = null) => WriteInternal("ERROR", message, ex);
+ }
+}
diff --git a/Utils/WorkBookUtils.cs b/Utils/WorkBookUtils.cs
index 84a7a22..84a62c9 100644
--- a/Utils/WorkBookUtils.cs
+++ b/Utils/WorkBookUtils.cs
@@ -5,8 +5,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using KellyReport_D.Model;
+using System.Runtime.InteropServices;
namespace KellyReport_D.Utils
{
@@ -40,7 +40,10 @@ namespace KellyReport_D.Utils
UpdateEstimatedTable(importContext);
UpdateDeptartmentSummary(importContext);
+ // 产品年度销量统计
GenProductSummarySheet(importContext);
+ // 生成客户年度销量统计
+ GenClientSummarySheet(importContext);
}
private static void UpdateEstimatedTable(ImportContext importContext)
@@ -66,9 +69,12 @@ namespace KellyReport_D.Utils
Range usedRange = bussenessWorksheet.UsedRange;
int maxColIndex = usedRange.Columns.Count;
+ int yearRowIndex = 1;
+ FindBussenessYearRow(bussenessWorksheet, importContext.YearList[0].ToString(), ref yearRowIndex);
+
for (int i = 2; i < maxColIndex; i += 4)
{
- String salesName = bussenessWorksheet.Cells[1, i].Value2;
+ String salesName = bussenessWorksheet.Cells[yearRowIndex, i].Value2;
if (string.IsNullOrWhiteSpace(salesName))
{
continue;
@@ -90,7 +96,7 @@ namespace KellyReport_D.Utils
.ToList()
.ForEach(monthlySales =>
{
- int startRowIndex = 3;
+ int startRowIndex = yearRowIndex + 2;
for (int rowIndex = startRowIndex; rowIndex < startRowIndex + 12; rowIndex++)
{
var monthCell = bussenessWorksheet.Cells[rowIndex, 1];
@@ -214,7 +220,7 @@ namespace KellyReport_D.Utils
}
catch (Exception ex)
{
- Console.Error.WriteLine("delete error %s", ex.Message);
+ FileLogger.Error("删除Total(All)旧数据时发生错误", ex);
}
finally
{
@@ -381,7 +387,7 @@ namespace KellyReport_D.Utils
{
for (var i = 0; i < clientSalesRowCount - curRowCount; i++)
{
- userSheet.Rows[clientSalesRowIndex + curRowCount].Insert(XlInsertShiftDirection.xlShiftDown);
+ userSheet.Rows[clientSalesRowIndex + curRowCount - 1].Insert(XlInsertShiftDirection.xlShiftDown);
summaryRowIndex++;
}
}
@@ -455,19 +461,61 @@ namespace KellyReport_D.Utils
private static int WriteSalesDetailToSheet(Worksheet userSheet, int rowIndex, int yearColIndex, ClientContact clientContact, IEnumerable salesDetailList)
{
+ Range productNamesCellRange = userSheet.Range[
+ userSheet.Cells[rowIndex, 11],
+ userSheet.Cells[rowIndex + salesDetailList.Count(), 11]
+ ];
+
foreach (var productSalesDetail in salesDetailList)
{
+ int currentRowIndex = rowIndex;
+ // 如果存在相同产品名称,则写入对应行
+ if (productSalesDetail.ProductNameEN != "總銷售額")
+ {
+
+ var targetProductCell = productNamesCellRange.Find(What: productSalesDetail.ProductNameEN, LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
+ if (targetProductCell != null)
+ {
+ currentRowIndex = targetProductCell.Row;
+ }
+ else
+ {
+ Range blankCell = null;
+ try
+ {
+
+ blankCell = productNamesCellRange.SpecialCells(XlCellType.xlCellTypeBlanks);
+ }
+ catch (COMException)
+ {
+ // 没有空白单元格
+ //productNamesCellRange.Insert(XlInsertShiftDirection.xlShiftDown);
+ MessageBox.Show($"写入数据时发生错误,可能是因为产品名称过多导致,错误数据:{productSalesDetail.SalesName}-{productSalesDetail.ClientName}-{productSalesDetail.ProductNameEN}。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ rowIndex++;
+
+ continue;
+ //userSheet.Rows[rowIndex + salesDetailList.Count() - 1].Insert(XlInsertShiftDirection.xlShiftDown);
+ //blankCell = userSheet.Cells[rowIndex + salesDetailList.Count() - 1, 11];
+ }
+
+ if (blankCell != null)
+ {
+ currentRowIndex = blankCell.Row;
+ }
+ }
+ }
+
//userSheet.Cells[rowIndex, 1].Value2 = productSalesDetail.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 = productSalesDetail.ProductNameEN;
+ userSheet.Cells[currentRowIndex, 2].Value2 = clientContact.Region;
+ userSheet.Cells[currentRowIndex, 3].Value2 = clientContact.Industry;
+ userSheet.Cells[currentRowIndex, 4].Value2 = clientContact.AnnualCapacity;
+ userSheet.Cells[currentRowIndex, 5].Value2 = clientContact.Name;
+ userSheet.Cells[currentRowIndex, 6].Value2 = clientContact.PurchasingManager;
+ userSheet.Cells[currentRowIndex, 7].Value2 = clientContact.PurchasingManagerPhone;
+ userSheet.Cells[currentRowIndex, 8].Value2 = clientContact.Purchaser;
+ userSheet.Cells[currentRowIndex, 9].Value2 = clientContact.PurchaserPhone;
+ userSheet.Cells[currentRowIndex, 10].Value2 = clientContact.Address;
+ userSheet.Cells[currentRowIndex, 11].Value2 = productSalesDetail.ProductNameEN;
// 计算每月的数量和金额
int monthCol = yearColIndex;
@@ -500,19 +548,19 @@ namespace KellyReport_D.Utils
if (monthDetail == null)
{
- userSheet.Cells[rowIndex, monthCol + (i - 1) * 3].Value2 = 0; // 單價
- userSheet.Cells[rowIndex, monthCol + (i - 1) * 3 + 1].Value2 = 0; // 數量
- userSheet.Cells[rowIndex, monthCol + (i - 1) * 3 + 2].Value2 = 0; // 金額
+ userSheet.Cells[currentRowIndex, monthCol + (i - 1) * 3].Value2 = 0; // 單價
+ userSheet.Cells[currentRowIndex, monthCol + (i - 1) * 3 + 1].Value2 = 0; // 數量
+ userSheet.Cells[currentRowIndex, monthCol + (i - 1) * 3 + 2].Value2 = 0; // 金額
}
else
{
- userSheet.Cells[rowIndex, monthCol + (i - 1) * 3].Value2 = monthDetail.PriceWithTax ?? 0; // 單價
- userSheet.Cells[rowIndex, monthCol + (i - 1) * 3 + 1].Value2 = monthDetail.Quantity ?? 0; // 數量
- userSheet.Cells[rowIndex, monthCol + (i - 1) * 3 + 2].Value2 = monthDetail.TotalAmount ?? 0; // 总额
+ userSheet.Cells[currentRowIndex, monthCol + (i - 1) * 3].Value2 = monthDetail.PriceWithTax ?? 0; // 單價
+ userSheet.Cells[currentRowIndex, monthCol + (i - 1) * 3 + 1].Value2 = monthDetail.Quantity ?? 0; // 數量
+ userSheet.Cells[currentRowIndex, monthCol + (i - 1) * 3 + 2].Value2 = monthDetail.TotalAmount ?? 0; // 总额
}
}
- userSheet.Cells[rowIndex, 49].Value = productSalesDetail.Quantity;
- userSheet.Cells[rowIndex, 50].Value = productSalesDetail.TotalAmount;
+ userSheet.Cells[currentRowIndex, 49].Value = productSalesDetail.Quantity;
+ userSheet.Cells[currentRowIndex, 50].Value = productSalesDetail.TotalAmount;
rowIndex++;
}
@@ -656,7 +704,7 @@ namespace KellyReport_D.Utils
}
catch (Exception ex)
{
- Console.Error.WriteLine("parse date error:" + ex.Message);
+ FileLogger.Error($"解析日期错误 '{rawValue}'", ex);
//throw new FormatException($"日期格式错误 '{rawValue}'");
}
@@ -741,28 +789,25 @@ namespace KellyReport_D.Utils
// 获取「銷售年度預估」工作表
ReadSalesForecastData(context);
// 获取「產品年度預估」工作表
- //ReadProductForecastData(context);
+ // GenProductForecastData(context);
GenDepartmentSummary(context);
GenBussenessSheet(context);
}
catch (Exception ex)
{
- Console.WriteLine("Error generating forecast:" + ex.Message);
+ FileLogger.Error("生成销售年度预估表失败", ex);
+
MessageBox.Show("分析预估表失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
button.Text = originLabel;
}
- public static void GenBussenessSheet(GenForecaseContext context)
+ public static bool FindBussenessYearRow(Worksheet sheet, string targetYear, ref int yearRowIndex)
{
- var app = context.Application;
- Worksheet sheet = CreateSheetIfNotExisted(app, "業務月達成率");
-
Range usedRange = sheet.UsedRange;
int totalRows = usedRange?.Rows?.Count ?? 0;
- int yearRowIndex = 1;
Boolean updateCurrentBook = false;
while (yearRowIndex < totalRows - 1)
{
@@ -777,7 +822,7 @@ namespace KellyReport_D.Utils
yearValue = (rawYearValue).ToString();
}
- if (yearValue == context.MonthData[0])
+ if (yearValue == targetYear)
{
updateCurrentBook = true;
break;
@@ -786,6 +831,19 @@ namespace KellyReport_D.Utils
yearRowIndex += 15;
}
+ return updateCurrentBook;
+ }
+
+ public static void GenBussenessSheet(GenForecaseContext context)
+ {
+ var app = context.Application;
+ Worksheet sheet = CreateSheetIfNotExisted(app, "業務月達成率");
+
+ Range usedRange = sheet.UsedRange;
+ int totalRows = usedRange?.Rows?.Count ?? 0;
+ int yearRowIndex = 1;
+ Boolean updateCurrentBook = FindBussenessYearRow(sheet, context.MonthData[0], ref yearRowIndex);
+
try
{
string rowRanges = "";
@@ -808,6 +866,7 @@ namespace KellyReport_D.Utils
}
catch (Exception ex)
{
+ FileLogger.Error($"更新{context.MonthData[0]}年業務月達成率失败", ex);
throw new Exception($"更新{context.MonthData[0]}年業務月達成率失败", ex);
}
@@ -894,6 +953,8 @@ namespace KellyReport_D.Utils
}
catch (Exception ex)
{
+ FileLogger.Error($"写入{context.MonthData[0]}年業務月達成率失败", ex);
+
MessageBox.Show("写入业绩月达成率时发生错误:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
@@ -934,44 +995,6 @@ namespace KellyReport_D.Utils
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;
@@ -1076,6 +1099,7 @@ namespace KellyReport_D.Utils
];
turnOverRange.Merge();
}
+ usedRange = productSheet.UsedRange;
var productSalesList = context.SalesDetails
.Where(x => x.InvoiceDate.HasValue && x.InvoiceDate.Value.Year == yearNumber)
@@ -1089,9 +1113,9 @@ namespace KellyReport_D.Utils
})
.ToList();
- Range totalRange = usedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
+ Range totalRange = productSheet.UsedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
- int totalRowCount = usedRange.Rows.Count + 1;
+ int totalRowCount = productSheet.UsedRange.Rows.Count + 1;
if (totalRange != null)
{
totalRowCount = totalRange.Row;
@@ -1110,7 +1134,7 @@ namespace KellyReport_D.Utils
var curProductName = productSalesSummary.ProductNameEN;
// look for the product row
// if not exists then create a new row
- Range productRange = usedRange.Find(
+ Range productRange = productSheet.UsedRange.Find(
What: curProductName,
LookIn: XlFindLookIn.xlValues,
LookAt: XlLookAt.xlWhole,
@@ -1159,6 +1183,127 @@ namespace KellyReport_D.Utils
FormatSheetStyle(productSheet);
}
+ public static void GenClientSummarySheet(ImportContext context)
+ {
+ var app = context.Application;
+
+ string sheetName = $"客戶年度預估";
+ Worksheet clientSheet = CreateSheetIfNotExisted(app, sheetName);
+ clientSheet.Activate();
+
+ context.YearList.ForEach(yearNumber =>
+ {
+ Range turnOverRange = clientSheet.UsedRange.Find(What: $"{yearNumber}-Turnover", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
+ var clientSalesList = context.SalesDetails
+ .Where(x => x.InvoiceDate.HasValue && x.InvoiceDate.Value.Year == yearNumber)
+ .GroupBy(x => x.ClientName)
+ .Select(grouped => new ImportContext.GroupedSalesDetailModel()
+ {
+ ClientName = grouped.Key,
+ Quantity = grouped.Sum(x => x.Quantity ?? 0),
+ TotalAmount = grouped.Sum(x => x.TotalAmount ?? 0),
+ Details = grouped.ToList()
+ })
+ .ToList();
+ if (turnOverRange == null)
+ {
+ int insertCol = 2;
+
+ Range insertRange = clientSheet.Columns[$"{GetColumnLetter(insertCol)}:{GetColumnLetter(insertCol + 5)}"];
+ insertRange.EntireColumn.Insert(XlInsertShiftDirection.xlShiftToRight);
+ insertRange = clientSheet.Columns[$"{GetColumnLetter(insertCol)}:{GetColumnLetter(insertCol + 5)}"];
+ insertRange.ClearFormats();
+
+ clientSheet.Cells[1, insertCol].Value2 = $"{yearNumber}-Turnover";
+
+ clientSheet.Cells[2, insertCol].Value2 = "數量";
+ clientSheet.Cells[2, insertCol + 1].Value2 = "金額(含稅)";
+ clientSheet.Cells[2, insertCol + 2].Value2 = "金額(未稅)";
+ clientSheet.Cells[2, insertCol + 3].Value2 = "平均單價";
+ clientSheet.Cells[2, insertCol + 4].Value2 = "成長率";
+ var growthRange = clientSheet.Range[
+ clientSheet.Cells[2, insertCol + 4],
+ clientSheet.Cells[2, insertCol + 5]
+ ];
+ growthRange.Merge();
+
+ turnOverRange = clientSheet.Range[
+ clientSheet.Cells[1, insertCol],
+ clientSheet.Cells[1, insertCol + 5]
+ ];
+ turnOverRange.Merge();
+ }
+
+ Range totalRange = clientSheet.UsedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
+
+ int totalRowCount = clientSheet.UsedRange.Rows.Count + 1;
+ if (totalRange != null)
+ {
+ totalRowCount = totalRange.Row;
+ }
+
+ int startColIndex = turnOverRange.Column;
+ var quantityColLetter = GetColumnLetter(startColIndex);
+ var amountColLetter = GetColumnLetter(startColIndex + 1);
+ var amountNoTaxColLetter = GetColumnLetter(startColIndex + 2);
+ var prevQuantityColLetter = GetColumnLetter(startColIndex + 6);
+ var prevAmountColLetter = GetColumnLetter(startColIndex + 7);
+ var prevAmountNoTaxColLetter = GetColumnLetter(startColIndex + 8);
+
+ foreach (var clientSalesGroup in clientSalesList)
+ {
+ var curClientName = clientSalesGroup.ClientName;
+ // look for the Client row
+ // if not exists then create a new row
+ Range clientRange = clientSheet.UsedRange.Find(
+ What: curClientName,
+ LookIn: XlFindLookIn.xlValues,
+ LookAt: XlLookAt.xlWhole,
+ SearchOrder: XlSearchOrder.xlByRows,
+ SearchDirection: XlSearchDirection.xlNext,
+ MatchCase: false
+ );
+
+ if (clientRange == null)
+ {
+ clientSheet.Rows[totalRowCount].Insert();
+ clientSheet.Cells[totalRowCount, 1].Value2 = curClientName;
+ clientRange = clientSheet.Cells[totalRowCount, 1];
+
+ totalRowCount += 1;
+ }
+
+ int curRowIndex = clientRange.Row;
+
+ clientSheet.Cells[curRowIndex, startColIndex].Value2 = clientSalesGroup.Quantity;
+ clientSheet.Cells[curRowIndex, startColIndex + 1].Value2 = clientSalesGroup.TotalAmount;
+ clientSheet.Cells[curRowIndex, startColIndex + 2].formula = $"={amountColLetter}{curRowIndex}/1.13";
+ clientSheet.Cells[curRowIndex, startColIndex + 3].formula = $"={amountColLetter}{curRowIndex}/${quantityColLetter}{curRowIndex}";
+
+ clientSheet.Cells[curRowIndex, startColIndex + 4].formula = $"={quantityColLetter}{curRowIndex}/${prevQuantityColLetter}{curRowIndex}";
+ clientSheet.Cells[curRowIndex, startColIndex + 5].formula = $"={amountColLetter}{curRowIndex}/${prevAmountColLetter}{curRowIndex}";
+
+ clientSheet.Cells[curRowIndex, startColIndex + 4].NumberFormat = "0.00%";
+ clientSheet.Cells[curRowIndex, startColIndex + 5].NumberFormat = "0.00%";
+ }
+
+ clientSheet.Cells[totalRowCount, 1].Value2 = "總計";
+ clientSheet.Cells[totalRowCount, startColIndex].formula = $"=SUM({quantityColLetter}5:{quantityColLetter}{totalRowCount - 1})";
+ clientSheet.Cells[totalRowCount, startColIndex + 1].formula = $"=SUM({amountColLetter}5:{amountColLetter}{totalRowCount - 1})";
+ clientSheet.Cells[totalRowCount, startColIndex + 2].formula = $"=SUM({amountNoTaxColLetter}5:{amountNoTaxColLetter}{totalRowCount - 1})";
+
+ clientSheet.Cells[totalRowCount + 1, 1].Value2 = "備註";
+ clientSheet.Cells[totalRowCount + 1, startColIndex].formula = $"={quantityColLetter}{totalRowCount}/{prevQuantityColLetter}{totalRowCount}";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 1].formula = $"={amountColLetter}{totalRowCount}/{prevAmountColLetter}{totalRowCount}";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 2].formula = $"={amountNoTaxColLetter}{totalRowCount}/{prevAmountNoTaxColLetter}{totalRowCount}";
+
+ clientSheet.Cells[totalRowCount + 1, startColIndex].NumberFormat = "0.00%";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 1].NumberFormat = "0.00%";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 2].NumberFormat = "0.00%";
+ });
+
+ FormatSheetStyle(clientSheet);
+ }
public static void GenDepartmentSummary(GenForecaseContext context)
{
@@ -1171,10 +1316,7 @@ namespace KellyReport_D.Utils
deptSheet.Cells[1, 1].Value2 = "鈞全";
deptSheet.Range[deptSheet.Cells[1, 1], deptSheet.Cells[2, 1]].Merge();
- var yearList = context.SalesForecastList
- .Select(x => x.Year)
- .Distinct()
- .ToList();
+ var yearList = context.SalesForecastList.Select(x => x.Year).Distinct().ToList();
Range initUsedRange = deptSheet.UsedRange;
@@ -1269,6 +1411,133 @@ namespace KellyReport_D.Utils
FormatSheetStyle(deptSheet);
}
+/* public static void GenProductForecastData(GenForecaseContext context)
+ {
+ var app = context.Application;
+
+ string sheetName = $"產品年度預估";
+ Worksheet productSheet = CreateSheetIfNotExisted(app, sheetName);
+ productSheet.Activate();
+
+ var yearList = context.SalesForecastList.Select(x => x.Year).Distinct().ToList();
+ string yearNumber = yearList[0].ToString();
+
+ Range estimateRange = productSheet.UsedRange.Find(What: $"{yearNumber}-Estimate", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
+
+ int insertCol = 2;
+ if (estimateRange != null)
+ {
+ insertCol = estimateRange.Column;
+ }
+
+ *//*
+ Range insertRange = productSheet.Columns[$"{GetColumnLetter(insertCol)}:{GetColumnLetter(insertCol + 5)}"];
+ insertRange.EntireColumn.Insert(XlInsertShiftDirection.xlShiftToRight);
+ insertRange = productSheet.Columns[$"{GetColumnLetter(insertCol)}:{GetColumnLetter(insertCol + 5)}"];
+ insertRange.ClearFormats();
+ *//*
+ productSheet.Cells[1, insertCol].Value2 = $"{yearNumber}-Estimate";
+
+ productSheet.Cells[2, insertCol].Value2 = "數量";
+ productSheet.Cells[2, insertCol + 1].Value2 = "金額(含稅)";
+ productSheet.Cells[2, insertCol + 2].Value2 = "金額(未稅)";
+ productSheet.Cells[2, insertCol + 3].Value2 = "平均單價";
+ productSheet.Cells[2, insertCol + 4].Value2 = "成長率";
+ var growthRange = productSheet.Range[
+ productSheet.Cells[2, insertCol + 4],
+ productSheet.Cells[2, insertCol + 5]
+ ];
+ growthRange.Merge();
+
+ estimateRange = productSheet.Range[
+ productSheet.Cells[1, insertCol],
+ productSheet.Cells[1, insertCol + 5]
+ ];
+ estimateRange.Merge();
+
+ var productSalesList = context.SalesForecastList
+ .Where(x => x..HasValue && x.InvoiceDate.Value.Year == yearNumber)
+ .GroupBy(x => x.ProductNameEN)
+ .Select(grouped => new ImportContext.GroupedSalesDetailModel()
+ {
+ ProductNameEN = grouped.Key,
+ Quantity = grouped.Sum(x => x.Quantity ?? 0),
+ TotalAmount = grouped.Sum(x => x.TotalAmount ?? 0),
+ Details = grouped.ToList()
+ })
+ .ToList();
+
+ Range totalRange = clientSheet.UsedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
+
+ int totalRowCount = clientSheet.UsedRange.Rows.Count + 1;
+ if (totalRange != null)
+ {
+ totalRowCount = totalRange.Row;
+ }
+
+ int startColIndex = turnOverRange.Column;
+ var quantityColLetter = GetColumnLetter(startColIndex);
+ var amountColLetter = GetColumnLetter(startColIndex + 1);
+ var amountNoTaxColLetter = GetColumnLetter(startColIndex + 2);
+ var prevQuantityColLetter = GetColumnLetter(startColIndex + 6);
+ var prevAmountColLetter = GetColumnLetter(startColIndex + 7);
+ var prevAmountNoTaxColLetter = GetColumnLetter(startColIndex + 8);
+
+ foreach (var productSalesSummary in productSalesList)
+ {
+ var curProductName = productSalesSummary.ProductNameEN;
+ // look for the product row
+ // if not exists then create a new row
+ Range productRange = clientSheet.UsedRange.Find(
+ What: curProductName,
+ LookIn: XlFindLookIn.xlValues,
+ LookAt: XlLookAt.xlWhole,
+ SearchOrder: XlSearchOrder.xlByRows,
+ SearchDirection: XlSearchDirection.xlNext,
+ MatchCase: false
+ );
+
+ if (productRange == null)
+ {
+ clientSheet.Rows[totalRowCount].Insert();
+ clientSheet.Cells[totalRowCount, 1].Value2 = curProductName;
+ productRange = clientSheet.Cells[totalRowCount, 1];
+
+ totalRowCount += 1;
+ }
+
+ int curRowIndex = productRange.Row;
+
+ clientSheet.Cells[curRowIndex, startColIndex].Value2 = productSalesSummary.Quantity;
+ clientSheet.Cells[curRowIndex, startColIndex + 1].Value2 = productSalesSummary.TotalAmount;
+ clientSheet.Cells[curRowIndex, startColIndex + 2].formula = $"={amountColLetter}{curRowIndex}/1.13";
+ clientSheet.Cells[curRowIndex, startColIndex + 3].formula = $"={amountColLetter}{curRowIndex}/${quantityColLetter}{curRowIndex}";
+
+ clientSheet.Cells[curRowIndex, startColIndex + 4].formula = $"={quantityColLetter}{curRowIndex}/${prevQuantityColLetter}{curRowIndex}";
+ clientSheet.Cells[curRowIndex, startColIndex + 5].formula = $"={amountColLetter}{curRowIndex}/${prevAmountColLetter}{curRowIndex}";
+
+ clientSheet.Cells[curRowIndex, startColIndex + 4].NumberFormat = "0.00%";
+ clientSheet.Cells[curRowIndex, startColIndex + 5].NumberFormat = "0.00%";
+ }
+
+ clientSheet.Cells[totalRowCount, 1].Value2 = "總計";
+ clientSheet.Cells[totalRowCount, startColIndex].formula = $"=SUM({quantityColLetter}5:{quantityColLetter}{totalRowCount - 1})";
+ clientSheet.Cells[totalRowCount, startColIndex + 1].formula = $"=SUM({amountColLetter}5:{amountColLetter}{totalRowCount - 1})";
+ clientSheet.Cells[totalRowCount, startColIndex + 2].formula = $"=SUM({amountNoTaxColLetter}5:{amountNoTaxColLetter}{totalRowCount - 1})";
+
+ clientSheet.Cells[totalRowCount + 1, 1].Value2 = "備註";
+ clientSheet.Cells[totalRowCount + 1, startColIndex].formula = $"={quantityColLetter}{totalRowCount}/{prevQuantityColLetter}{totalRowCount}";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 1].formula = $"={amountColLetter}{totalRowCount}/{prevAmountColLetter}{totalRowCount}";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 2].formula = $"={amountNoTaxColLetter}{totalRowCount}/{prevAmountNoTaxColLetter}{totalRowCount}";
+
+ clientSheet.Cells[totalRowCount + 1, startColIndex].NumberFormat = "0.00%";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 1].NumberFormat = "0.00%";
+ clientSheet.Cells[totalRowCount + 1, startColIndex + 2].NumberFormat = "0.00%";
+ });
+ FormatSheetStyle(clientSheet);
+ }
+*/
+
public static void UpdateDeptartmentSummary(ImportContext context)
{
var app = context.Application;
@@ -1283,7 +1552,7 @@ namespace KellyReport_D.Utils
if (yearRange == null)
{
- Console.WriteLine("Cannot find year column in department summary: {0}", yearoNumber);
+ FileLogger.Info($"Cannot find year column in department summary: {yearoNumber}");
return;
}