优化Excel导入与年度预估表生成逻辑

- 客户/产品销售明细写入逻辑优化,支持动态插入产品行,合并单元格处理更严谨
- 销售明细导入支持“英文品名”列动态索引,兼容多种表格格式
- 启用产品年度预估表自动生成,支持“產品資料”批量导入
- 客户/产品查找新增特殊字符转义,查找更稳定
- “成長率”列标题改为“達成率”
- 项目及程序集版本号提升
This commit is contained in:
earo.lau 2026-01-01 12:50:55 +08:00
parent f839573712
commit 1d0d183d8a
4 changed files with 280 additions and 123 deletions

View File

@ -59,7 +59,7 @@ namespace KellyReport_D
private void initForecastBtn_Click(object sender, EventArgs e)
{
WorkBookUtils.GenForecast(this.initForecastBtn);
MessageBox.Show("生成年度估表完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
MessageBox.Show("生成年度估表完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void importSalesBtn_Click(object sender, EventArgs e)
@ -68,21 +68,27 @@ namespace KellyReport_D
if (workBook == null)
{
MessageBox.Show("未选择文件或文件打开失败,请重试。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show("未選擇文件或者文件無法打開,請重試。", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
String originLabel = this.importSalesBtn.Text;
this.importSalesBtn.Text = Resources.GENERATING;
try
{
WorkBookUtils.ImportSalesDetailFromWorkBook(workBook);
MessageBox.Show("導入銷售表完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"导入过程中发生错误:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
FileLogger.Error($"導入銷售表错误!", ex);
MessageBox.Show($"導入銷售表過程中發生錯誤:{ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
workBook.Close(false); // 关闭工作簿
this.importSalesBtn.Text = originLabel;
}
}
}

View File

@ -34,7 +34,7 @@
<PublishUrl>publish\</PublishUrl>
<InstallUrl />
<TargetCulture>zh-chs</TargetCulture>
<ApplicationVersion>1.0.0.7</ApplicationVersion>
<ApplicationVersion>1.0.0.9</ApplicationVersion>
<AutoIncrementApplicationRevision>true</AutoIncrementApplicationRevision>
<UpdateEnabled>true</UpdateEnabled>
<UpdateInterval>7</UpdateInterval>

View File

@ -34,5 +34,5 @@ using System.Security;
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.6")]
[assembly: AssemblyFileVersion("1.0.0.8")]

View File

@ -375,27 +375,46 @@ namespace KellyReport_D.Utils
}).ToList();
Range salesNameCell = userSheet.Cells[clientSalesRowIndex, 1];
int curRowCount = 1;
int clientSalesRowCount = productSalesList.Count + 1;
int curRowCount = productSalesList.Count + 1;
if (salesNameCell.MergeCells)
{
Range mergedCell = salesNameCell.MergeArea;
curRowCount = mergedCell.Rows.Count;
}
// 添加行数
if (curRowCount < clientSalesRowCount)
{
for (var i = 0; i < clientSalesRowCount - curRowCount; i++)
foreach (var productSales in productSalesList)
{
userSheet.Rows[clientSalesRowIndex + curRowCount - 1].Insert(XlInsertShiftDirection.xlShiftDown);
Range productNameRows = userSheet.Range[
userSheet.Cells[mergedCell.Row, 11],
userSheet.Cells[mergedCell.Row + curRowCount - 1, 11]
];
var targetProductName = productNameRows.Find(What: productSales.ProductNameEN, LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (targetProductName == null)
{
// 原有客户,新增产品行
userSheet.Rows[clientSalesRowIndex + curRowCount - 1].Insert(XlInsertShiftDirection.xlShiftDown);
curRowCount++;
summaryRowIndex++;
}
}
}
else
{
foreach (var productSales in productSalesList)
{
// 新的客户行
userSheet.Rows[clientSalesRowIndex + 1].Insert(XlInsertShiftDirection.xlShiftDown);
summaryRowIndex++;
}
}
salesNameCell.Value2 = clientSalesDetail.SalesName;
int totalRowIndex = clientSalesRowIndex + clientSalesRowCount - 1;
userSheet.Range[userSheet.Cells[clientSalesRowIndex, 1], userSheet.Cells[totalRowIndex, 1]].Merge();
int totalRowIndex = clientSalesRowIndex + curRowCount - 1;
Range clientNameRange = userSheet.Range[userSheet.Cells[clientSalesRowIndex, 1], userSheet.Cells[totalRowIndex, 1]];
clientNameRange.Merge();
WriteSalesDetailToSheet(userSheet, clientSalesRowIndex, yearColIndex, clientContact, productSalesList);
WriteSalesDetailToSheet(userSheet, clientNameRange, clientSalesRowIndex, yearColIndex, clientContact, productSalesList);
// 客户销量汇总
var totalSales = new List<ImportContext.GroupedSalesDetailModel>()
{
@ -437,7 +456,7 @@ namespace KellyReport_D.Utils
}
};
WriteSalesDetailToSheet(userSheet, totalRowIndex, yearColIndex, clientContact, totalSales);
WriteSalesDetailToSheet(userSheet, clientNameRange, totalRowIndex, yearColIndex, clientContact, totalSales);
}
// 计算总结
@ -459,11 +478,11 @@ namespace KellyReport_D.Utils
userSheet.Application.ActiveWindow.FreezePanes = true;
}
private static int WriteSalesDetailToSheet(Worksheet userSheet, int rowIndex, int yearColIndex, ClientContact clientContact, IEnumerable<ImportContext.GroupedSalesDetailModel> salesDetailList)
private static int WriteSalesDetailToSheet(Worksheet userSheet, Range clientNameRange, int rowIndex, int yearColIndex, ClientContact clientContact, IEnumerable<ImportContext.GroupedSalesDetailModel> salesDetailList)
{
Range productNamesCellRange = userSheet.Range[
userSheet.Cells[rowIndex, 11],
userSheet.Cells[rowIndex + salesDetailList.Count(), 11]
userSheet.Cells[clientNameRange.Row, 11],
userSheet.Cells[clientNameRange.Row + clientNameRange.Rows.Count - 1, 11]
];
foreach (var productSalesDetail in salesDetailList)
@ -472,7 +491,6 @@ namespace KellyReport_D.Utils
// 如果存在相同产品名称,则写入对应行
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)
{
@ -516,6 +534,7 @@ namespace KellyReport_D.Utils
userSheet.Cells[currentRowIndex, 9].Value2 = clientContact.PurchaserPhone;
userSheet.Cells[currentRowIndex, 10].Value2 = clientContact.Address;
userSheet.Cells[currentRowIndex, 11].Value2 = productSalesDetail.ProductNameEN;
userSheet.Cells[currentRowIndex, 11].NumberFormat = "@";
// 计算每月的数量和金额
int monthCol = yearColIndex;
@ -640,8 +659,8 @@ namespace KellyReport_D.Utils
userSheet.Cells[3, startCol + 37].Value2 = "總金額";
// 设置所有已用单元格居中
Range usedRange = userSheet.UsedRange;
usedRange.HorizontalAlignment = XlHAlign.xlHAlignCenter;
Range headerRange = userSheet.Rows["1:3"];
headerRange.HorizontalAlignment = XlHAlign.xlHAlignCenter;
return startCol;
}
@ -721,6 +740,8 @@ namespace KellyReport_D.Utils
{
int rowCount = rawSalesDetailData.GetLength(0);
int colCount = rawSalesDetailData.GetLength(1);
int colProductNameENIndex = rawSalesDetailData[2, 8].ToString() == "英文品名" ? 8 : 9;
for (int i = 3; i <= rowCount; i++)
{
@ -733,13 +754,13 @@ namespace KellyReport_D.Utils
InvoiceDate = ResolveExcelDate(rawSalesDetailData[i, 5]),
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() ?? "",
ProductNameEN = rawSalesDetailData[i, colProductNameENIndex]?.ToString() ?? "",
Quantity = rawSalesDetailData[i, 9] != null ? Convert.ToDecimal(rawSalesDetailData[i, colProductNameENIndex + 1]) : (decimal?)null,
PriceWithTax = rawSalesDetailData[i, 10] != null ? Convert.ToDecimal(rawSalesDetailData[i, colProductNameENIndex + 2]) : (decimal?)null,
AmountWithoutTax = rawSalesDetailData[i, 11] != null ? Convert.ToDecimal(rawSalesDetailData[i, colProductNameENIndex + 3]) : (decimal?)null,
Tax = rawSalesDetailData[i, 12] != null ? Convert.ToDecimal(rawSalesDetailData[i, colProductNameENIndex + 4]) : (decimal?)null,
TotalAmount = rawSalesDetailData[i, 13] != null ? Convert.ToDecimal(rawSalesDetailData[i, colProductNameENIndex + 5]) : (decimal?)null,
Remark = rawSalesDetailData[i, colProductNameENIndex + 6]?.ToString() ?? "",
};
salesDatailList.Add(item);
}
@ -789,7 +810,7 @@ namespace KellyReport_D.Utils
// 获取「銷售年度預估」工作表
ReadSalesForecastData(context);
// 获取「產品年度預估」工作表
// GenProductForecastData(context);
GenProductForecastData(context);
GenDepartmentSummary(context);
GenBussenessSheet(context);
@ -965,6 +986,8 @@ namespace KellyReport_D.Utils
var app = context.Application;
var clientContactSheet = app.Worksheets["客戶資料"] as Worksheet;
clientContactSheet.Activate();
var clientContactRange = clientContactSheet.UsedRange;
IList<ClientContact> clientContactList = new List<ClientContact>();
@ -1086,7 +1109,7 @@ namespace KellyReport_D.Utils
productSheet.Cells[2, insertCol + 1].Value2 = "金額(含稅)";
productSheet.Cells[2, insertCol + 2].Value2 = "金額(未稅)";
productSheet.Cells[2, insertCol + 3].Value2 = "平均單價";
productSheet.Cells[2, insertCol + 4].Value2 = "率";
productSheet.Cells[2, insertCol + 4].Value2 = "成率";
var growthRange = productSheet.Range[
productSheet.Cells[2, insertCol + 4],
productSheet.Cells[2, insertCol + 5]
@ -1147,6 +1170,7 @@ namespace KellyReport_D.Utils
{
productSheet.Rows[totalRowCount].Insert();
productSheet.Cells[totalRowCount, 1].Value2 = curProductName;
productSheet.Cells[totalRowCount, 1].NumberFormat = "@";
productRange = productSheet.Cells[totalRowCount, 1];
totalRowCount += 1;
@ -1183,6 +1207,19 @@ namespace KellyReport_D.Utils
FormatSheetStyle(productSheet);
}
private static string EscapeExcelFindValue(string input)
{
if (string.IsNullOrEmpty(input))
{
return input;
}
return input
.Replace("~", "~~")
.Replace("*", "~*")
.Replace("?", "~?");
}
public static void GenClientSummarySheet(ImportContext context)
{
var app = context.Application;
@ -1191,9 +1228,15 @@ namespace KellyReport_D.Utils
Worksheet clientSheet = CreateSheetIfNotExisted(app, sheetName);
clientSheet.Activate();
// 准备首行标题
clientSheet.Cells[2, 1].Value2 = "客戶名稱";
clientSheet.Cells[2, 2].Value2 = "產品名稱";
// 按年写入数据
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)
@ -1205,9 +1248,11 @@ namespace KellyReport_D.Utils
Details = grouped.ToList()
})
.ToList();
// 目标年份列
if (turnOverRange == null)
{
int insertCol = 2;
int insertCol = 3;
Range insertRange = clientSheet.Columns[$"{GetColumnLetter(insertCol)}:{GetColumnLetter(insertCol + 5)}"];
insertRange.EntireColumn.Insert(XlInsertShiftDirection.xlShiftToRight);
@ -1253,16 +1298,17 @@ namespace KellyReport_D.Utils
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
);
var clientProductSalesGrouped = clientSalesGroup.Details.GroupBy(x => x.ProductNameEN).Select(grouped => new ImportContext.GroupedSalesDetailModel()
{
ClientName = curClientName,
ProductNameEN = grouped.Key,
Quantity = grouped.Sum(x => x.Quantity ?? 0),
TotalAmount = grouped.Sum(x => x.TotalAmount ?? 0),
Details = grouped.ToList()
}).ToList();
// 查找是否已经存在的客户,存在则直接写入, 否则创建新行
Range clientRange = clientSheet.UsedRange.Find(What: EscapeExcelFindValue(curClientName));
if (clientRange == null)
{
@ -1273,18 +1319,97 @@ namespace KellyReport_D.Utils
totalRowCount += 1;
}
int curRowIndex = clientRange.Row;
int clientTotalRowIndex = clientRange.Row;
// 处理产品名称行以及总销售行
if (clientRange.MergeCells)
{
Range clientSalesRows = clientRange.MergeArea;
int endRowIndex = clientSalesRows.End[XlDirection.xlDown].Row - 1;
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}";
foreach (var clientProductSales in clientProductSalesGrouped)
{
Range clientProductNameCells = clientSheet.Range[
clientSheet.Cells[clientSalesRows.Row, 2],
clientSheet.Cells[endRowIndex, 2]
];
clientSheet.Cells[curRowIndex, startColIndex + 4].formula = $"={quantityColLetter}{curRowIndex}/${prevQuantityColLetter}{curRowIndex}";
clientSheet.Cells[curRowIndex, startColIndex + 5].formula = $"={amountColLetter}{curRowIndex}/${prevAmountColLetter}{curRowIndex}";
var targetProduct = clientProductNameCells.Find(What: clientProductSales.ProductNameEN);
if (targetProduct == null)
{
clientSheet.Rows[endRowIndex].Insert(XlInsertShiftDirection.xlShiftDown);
clientSheet.Cells[endRowIndex, 2].Value2 = clientProductSales.ProductNameEN;
clientSheet.Cells[endRowIndex, 2].NumberFormat = "@";
clientSheet.Cells[curRowIndex, startColIndex + 4].NumberFormat = "0.00%";
clientSheet.Cells[curRowIndex, startColIndex + 5].NumberFormat = "0.00%";
endRowIndex++;
totalRowCount++;
}
}
clientTotalRowIndex = endRowIndex;
}
else
{
for (int i = 0; i < clientProductSalesGrouped.Count(); i++)
{
var clientProductSales = clientProductSalesGrouped[i];
clientSheet.Cells[clientRange.Row + i, 2].Value2 = clientProductSales.ProductNameEN;
clientSheet.Cells[clientRange.Row + i, 2].NumberFormat = "@";
clientSheet.Rows[clientRange.Row + i + 1].Insert();
clientTotalRowIndex++;
totalRowCount++;
}
}
clientSheet.Cells[clientTotalRowIndex, 2].Value2 = "總銷售額";
clientSheet.Range[
clientSheet.Cells[clientRange.Row, 1],
clientSheet.Cells[clientTotalRowIndex, 1]
].Merge();
// 重新选择合并后单元格
clientRange = clientSheet.Cells[clientRange.Row, 1];
foreach (var clientProductSales in clientProductSalesGrouped)
{
Range clientProductNameCells = clientSheet.Range[
clientSheet.Cells[clientRange.Row, 2],
clientSheet.Cells[clientTotalRowIndex, 2]
];
var targetProduct = clientProductNameCells.Find(What: clientProductSales.ProductNameEN);
if (targetProduct == null)
{
FileLogger.Error($"无法找到客户 {curClientName} 的产品行 {clientProductSales.ProductNameEN},请检查数据完整性。");
MessageBox.Show($"无法找到客户 {curClientName} 的产品行 {clientProductSales.ProductNameEN},请检查数据完整性。", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
continue;
}
int productRowIndex = targetProduct.Row;
clientSheet.Cells[productRowIndex, startColIndex].Value2 = clientProductSales.Quantity;
clientSheet.Cells[productRowIndex, startColIndex + 1].Value2 = clientProductSales.TotalAmount;
clientSheet.Cells[productRowIndex, startColIndex + 2].formula = $"={amountColLetter}{productRowIndex}/1.13";
clientSheet.Cells[productRowIndex, startColIndex + 3].formula = $"={amountColLetter}{productRowIndex}/${quantityColLetter}{productRowIndex}";
clientSheet.Cells[productRowIndex, startColIndex + 4].formula = $"={quantityColLetter}{productRowIndex}/${prevQuantityColLetter}{productRowIndex}";
clientSheet.Cells[productRowIndex, startColIndex + 5].formula = $"={amountColLetter}{productRowIndex}/${prevAmountColLetter}{productRowIndex}";
clientSheet.Cells[productRowIndex, startColIndex + 4].NumberFormat = "0.00%";
clientSheet.Cells[productRowIndex, startColIndex + 5].NumberFormat = "0.00%";
}
// 填写总销售额行
int totalRowIndex = clientRange.Row + clientProductSalesGrouped.Count();
clientSheet.Cells[clientTotalRowIndex, startColIndex].Value2 = clientSalesGroup.Quantity;
clientSheet.Cells[clientTotalRowIndex, startColIndex + 1].Value2 = clientSalesGroup.TotalAmount;
clientSheet.Cells[clientTotalRowIndex, startColIndex + 2].formula = $"={amountColLetter}{clientTotalRowIndex}/1.13";
clientSheet.Cells[clientTotalRowIndex, startColIndex + 3].formula = $"={amountColLetter}{clientTotalRowIndex}/${quantityColLetter}{clientTotalRowIndex}";
clientSheet.Cells[clientTotalRowIndex, startColIndex + 4].formula = $"={quantityColLetter}{clientTotalRowIndex}/${prevQuantityColLetter}{clientTotalRowIndex}";
clientSheet.Cells[clientTotalRowIndex, startColIndex + 5].formula = $"={amountColLetter}{clientTotalRowIndex}/${prevAmountColLetter}{clientTotalRowIndex}";
clientSheet.Cells[clientTotalRowIndex, startColIndex + 4].NumberFormat = "0.00%";
clientSheet.Cells[clientTotalRowIndex, startColIndex + 5].NumberFormat = "0.00%";
}
clientSheet.Cells[totalRowCount, 1].Value2 = "總計";
@ -1411,84 +1536,109 @@ namespace KellyReport_D.Utils
FormatSheetStyle(deptSheet);
}
/* public static void GenProductForecastData(GenForecaseContext context)
public static void ReadProductData(GenForecaseContext context)
{
var app = context.Application;
var productDataSheet = app.Worksheets["產品資料"] as Worksheet;
productDataSheet.Activate();
var productRange = productDataSheet.UsedRange;
IList<ProductForecastInput> productList = new List<ProductForecastInput>();
// 第2行开始
int rowIndex = 2;
while (rowIndex <= productRange.Rows.Count)
{
productList.Add(new ProductForecastInput() { Name = productDataSheet.Cells[rowIndex, 1].Value2.ToString() });
rowIndex++;
}
context.ProductForecastList = productList;
}
public static void GenProductForecastData(GenForecaseContext context)
{
var app = context.Application;
ReadProductData(context);
string sheetName = $"產品年度預估";
Worksheet productSheet = CreateSheetIfNotExisted(app, sheetName);
productSheet.Activate();
var yearList = context.SalesForecastList.Select(x => x.Year).Distinct().ToList();
string yearNumber = yearList[0].ToString();
int yearNumber = yearList[0];
// 准备标题行
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;
int estimateColIndex = 2;
if (estimateRange != null)
{
insertCol = estimateRange.Column;
estimateColIndex = estimateRange.Column;
}
else
{
Range insertRange = productSheet.Columns[$"{GetColumnLetter(estimateColIndex)}:{GetColumnLetter(estimateColIndex + 5)}"];
insertRange.EntireColumn.Insert(XlInsertShiftDirection.xlShiftToRight);
insertRange = productSheet.Columns[$"{GetColumnLetter(estimateColIndex)}:{GetColumnLetter(estimateColIndex + 5)}"];
insertRange.ClearFormats();
}
*//*
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[1, estimateColIndex].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 = "成長率";
productSheet.Cells[2, estimateColIndex].Value2 = "數量";
productSheet.Cells[2, estimateColIndex + 1].Value2 = "金額(含稅)";
productSheet.Cells[2, estimateColIndex + 2].Value2 = "金額(未稅)";
productSheet.Cells[2, estimateColIndex + 3].Value2 = "平均單價";
productSheet.Cells[2, estimateColIndex + 4].Value2 = "成長率";
var growthRange = productSheet.Range[
productSheet.Cells[2, insertCol + 4],
productSheet.Cells[2, insertCol + 5]
productSheet.Cells[2, estimateColIndex + 4],
productSheet.Cells[2, estimateColIndex + 5]
];
growthRange.Merge();
estimateRange = productSheet.Range[
productSheet.Cells[1, insertCol],
productSheet.Cells[1, insertCol + 5]
productSheet.Cells[1, estimateColIndex],
productSheet.Cells[1, estimateColIndex + 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 = productSheet.UsedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
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;
int totalRowCount = productSheet.UsedRange.Rows.Count + 1;
if (totalRange != null)
{
totalRowCount = totalRange.Row;
}
int startColIndex = turnOverRange.Column;
int startColIndex = estimateColIndex;
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)
Range lastYearEstimateRange = productSheet.UsedRange.Find(What: $"{yearNumber - 1}-Turnover", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (lastYearEstimateRange != null)
{
var curProductName = productSalesSummary.ProductNameEN;
// look for the product row
// if not exists then create a new row
Range productRange = clientSheet.UsedRange.Find(
prevQuantityColLetter = GetColumnLetter(lastYearEstimateRange.Column);
prevAmountColLetter = GetColumnLetter(lastYearEstimateRange.Column + 1);
prevAmountNoTaxColLetter = GetColumnLetter(lastYearEstimateRange.Column + 2);
}
for (int i = 0; i < context.ProductForecastList.Count; i++)
{
ProductForecastInput productData = context.ProductForecastList[i];
var curProductName = productData.Name;
// 查找产品列,如已存在,无需处理
// 否则创建新的产品行,并初始化预算
Range productRange = productSheet.UsedRange.Find(
What: curProductName,
LookIn: XlFindLookIn.xlValues,
LookAt: XlLookAt.xlWhole,
@ -1499,44 +1649,45 @@ namespace KellyReport_D.Utils
if (productRange == null)
{
clientSheet.Rows[totalRowCount].Insert();
clientSheet.Cells[totalRowCount, 1].Value2 = curProductName;
productRange = clientSheet.Cells[totalRowCount, 1];
// 创建新的产品预算行
productSheet.Rows[totalRowCount].Insert();
productSheet.Cells[totalRowCount, 1].Value2 = curProductName;
productRange = productSheet.Cells[totalRowCount, 1];
int curRowIndex = totalRowCount;
productSheet.Cells[curRowIndex, startColIndex].Value2 = 0;
productSheet.Cells[curRowIndex, startColIndex + 1].Value2 = 0;
productSheet.Cells[curRowIndex, startColIndex + 2].formula = $"={amountColLetter}{curRowIndex}/1.13";
productSheet.Cells[curRowIndex, startColIndex + 3].formula = $"={amountColLetter}{curRowIndex}/${quantityColLetter}{curRowIndex}";
productSheet.Cells[curRowIndex, startColIndex + 4].formula = $"={quantityColLetter}{curRowIndex}/${prevQuantityColLetter}{curRowIndex}";
productSheet.Cells[curRowIndex, startColIndex + 5].formula = $"={amountColLetter}{curRowIndex}/${prevAmountColLetter}{curRowIndex}";
productSheet.Cells[curRowIndex, startColIndex + 4].NumberFormat = "0.00%";
productSheet.Cells[curRowIndex, startColIndex + 5].NumberFormat = "0.00%";
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})";
productSheet.Cells[totalRowCount, 1].Value2 = "總計";
productSheet.Cells[totalRowCount, startColIndex].formula = $"=SUM({quantityColLetter}5:{quantityColLetter}{totalRowCount - 1})";
productSheet.Cells[totalRowCount, startColIndex + 1].formula = $"=SUM({amountColLetter}5:{amountColLetter}{totalRowCount - 1})";
productSheet.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}";
productSheet.Cells[totalRowCount + 1, 1].Value2 = "備註";
productSheet.Cells[totalRowCount + 1, startColIndex].formula = $"={quantityColLetter}{totalRowCount}/{prevQuantityColLetter}{totalRowCount}";
productSheet.Cells[totalRowCount + 1, startColIndex + 1].formula = $"={amountColLetter}{totalRowCount}/{prevAmountColLetter}{totalRowCount}";
productSheet.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);
productSheet.Cells[totalRowCount + 1, startColIndex].NumberFormat = "0.00%";
productSheet.Cells[totalRowCount + 1, startColIndex + 1].NumberFormat = "0.00%";
productSheet.Cells[totalRowCount + 1, startColIndex + 2].NumberFormat = "0.00%";
FormatSheetStyle(productSheet);
}
*/
public static void UpdateDeptartmentSummary(ImportContext context)
{