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

1749 lines
85 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 KellyReport_D.Model;
using System.Runtime.InteropServices;
namespace KellyReport_D.Utils
{
internal static class WorkBookUtils
{
public static void ImportSalesDetailFromWorkBook(Workbook workbook)
{
var importContext = new ImportContext()
{
Application = Globals.ThisAddIn.Application,
ImportWorkbook = workbook,
SalesWorksheets = new List<Worksheet>(),
};
// 获取「客户资料」工作表
ReadClientContact(importContext);
// 读取「销售明细」工作表
ReadSalesDetails(importContext);
foreach (var userGroupedSalesDetail in importContext.GroupedSalesDetailBySalesName)
{
var userSheet = GenUserSalesDetailSheet(importContext, userGroupedSalesDetail);
var yearColIndex = PrepareTotalHeader(userSheet, importContext);
WriteSalesDetailsData(importContext, userGroupedSalesDetail, userSheet, yearColIndex);
importContext.SalesWorksheets.Add(userSheet);
}
GenSalesTotal(importContext);
// 更新业务预估表
UpdateEstimatedTable(importContext);
UpdateDeptartmentSummary(importContext);
// 产品年度销量统计
GenProductSummarySheet(importContext);
// 生成客户年度销量统计
GenClientSummarySheet(importContext);
}
private static void UpdateEstimatedTable(ImportContext importContext)
{
var app = importContext.Application;
Worksheet bussenessWorksheet = null;
foreach (Worksheet item in app.Worksheets)
{
if (item.Name == "業務月達成率")
{
bussenessWorksheet = item;
break;
}
}
if (bussenessWorksheet == null)
{
throw new EntryPointNotFoundException("未找到「業務月達成率」工作表,请先生成预估表。");
}
bussenessWorksheet.Activate();
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[yearRowIndex, i].Value2;
if (string.IsNullOrWhiteSpace(salesName))
{
continue;
}
var salesDetails = salesName.ToUpper() == "TOTAL" ?
importContext.SalesDetails :
importContext.GroupedSalesDetailBySalesName.FirstOrDefault(x => x.SalesName.ToUpper() == salesName.ToUpper())?.Details;
if (salesDetails == null)
continue;
salesDetails.GroupBy(x => x.Month)
.Select(grouped => new
{
Month = grouped.Key,
TotalAmount = grouped.Sum(x => x.TotalAmount ?? 0),
})
.ToList()
.ForEach(monthlySales =>
{
int startRowIndex = yearRowIndex + 2;
for (int rowIndex = startRowIndex; rowIndex < startRowIndex + 12; rowIndex++)
{
var monthCell = bussenessWorksheet.Cells[rowIndex, 1];
if (monthCell.Value2 != null && monthCell.Value2.ToString() == monthlySales.Month)
{
var forecsastColLetter = GetColumnLetter(i);
var turnoverCell = bussenessWorksheet.Cells[rowIndex, i + 1];
var turnoverColLetter = GetColumnLetter(i + 1);
turnoverCell.Value2 = monthlySales.TotalAmount;
turnoverCell.NumberFormat = "#,##0";
var completionCell = bussenessWorksheet.Cells[rowIndex, i + 2];
completionCell.formula = $"={turnoverColLetter}{rowIndex}/{forecsastColLetter}{rowIndex}";
completionCell.NumberFormat = "0.00%";
var accCompletionCell = bussenessWorksheet.Cells[rowIndex, i + 3];
accCompletionCell.formula = $"=SUM(${turnoverColLetter}${startRowIndex}:{turnoverColLetter}{rowIndex})/SUM(${forecsastColLetter}${startRowIndex}:{forecsastColLetter}{rowIndex})";
accCompletionCell.NumberFormat = "0.00%";
break;
}
}
});
}
}
private static void UpdateSalesDetailSummary(Worksheet userWorkSheet, int summaryRowStartIndex)
{
int lastColumnIndex = userWorkSheet.UsedRange.Columns.Count;
int summaryRowIndex = summaryRowStartIndex;
int sumColStart = 13; // L列是第12列
do
{
userWorkSheet.Cells[summaryRowIndex, 11].Value2 = "總銷量及金額(未稅)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
string quantityColLetter = GetColumnLetter(quantityColIndex);
userWorkSheet.Cells[summaryRowIndex, quantityColIndex].formula = $"=(SUM({quantityColLetter}4:{quantityColLetter}{summaryRowIndex - 1})/2)";
int amountColIndex = sumColStart + i * 3 + 2;
string amountColLetter = GetColumnLetter(amountColIndex);
userWorkSheet.Cells[summaryRowIndex, amountColIndex].formula = $"=((SUM({amountColLetter}4:{amountColLetter}{summaryRowIndex - 1})/2) - {amountColLetter}{summaryRowIndex + 2})/1.13";
}
string totalQuantityColLetter = GetColumnLetter(sumColStart + 36);
string totalAmountColLetter = GetColumnLetter(sumColStart + 37);
userWorkSheet.Cells[summaryRowIndex, sumColStart + 36].formula = $"=(SUM({totalQuantityColLetter}4:{totalAmountColLetter}{summaryRowIndex - 1})/2)";
userWorkSheet.Cells[summaryRowIndex, sumColStart + 37].formula = $"=((SUM({totalAmountColLetter}4:{totalAmountColLetter}{summaryRowIndex - 1})/2) - {totalAmountColLetter}{summaryRowIndex + 2})/1.13";
summaryRowIndex++;
userWorkSheet.Cells[summaryRowIndex, 11].Value2 = "總銷量及金額(含稅)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
string quantityColLetter = GetColumnLetter(quantityColIndex);
userWorkSheet.Cells[summaryRowIndex, quantityColIndex].formula = $"=(SUM({quantityColLetter}4:{quantityColLetter}{summaryRowIndex - 1})/2)";
int amountColIndex = sumColStart + i * 3 + 2;
string amountColLetter = GetColumnLetter(amountColIndex);
userWorkSheet.Cells[summaryRowIndex, amountColIndex].formula = $"=(SUM({amountColLetter}4:{amountColLetter}{summaryRowIndex - 1})/2)";
}
userWorkSheet.Cells[summaryRowIndex, sumColStart + 36].formula = $"=(SUM({totalQuantityColLetter}4:{totalQuantityColLetter}{summaryRowIndex - 1})/2)";
userWorkSheet.Cells[summaryRowIndex, sumColStart + 37].formula = $"=(SUM({totalAmountColLetter}4:{totalAmountColLetter}{summaryRowIndex - 1})/2)";
summaryRowIndex++;
userWorkSheet.Cells[summaryRowIndex, 11].Value2 = "總銷量及金額(外銷)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
userWorkSheet.Cells[summaryRowIndex, quantityColIndex].Value2 = 0;
int amountColIndex = sumColStart + i * 3 + 2;
userWorkSheet.Cells[summaryRowIndex, amountColIndex].Value2 = 0;
}
userWorkSheet.Cells[summaryRowIndex, sumColStart + 36].Value2 = 0;
userWorkSheet.Cells[summaryRowIndex, sumColStart + 37].Value2 = 0;
summaryRowIndex++;
userWorkSheet.Cells[summaryRowIndex, 11].Value2 = "總銷量及金額(內外銷)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
string quantityColLetter = GetColumnLetter(quantityColIndex);
userWorkSheet.Cells[summaryRowIndex, quantityColIndex].formula = $"={quantityColLetter}{summaryRowIndex - 3}+{quantityColLetter}{summaryRowIndex - 1}";
int amountColIndex = sumColStart + i * 3 + 2;
string amountColLetter = GetColumnLetter(amountColIndex);
userWorkSheet.Cells[summaryRowIndex, amountColIndex].formula = $"={amountColLetter}{summaryRowIndex - 3}+{amountColLetter}{summaryRowIndex - 1}";
}
userWorkSheet.Cells[summaryRowIndex, sumColStart + 36].formula = $"={totalQuantityColLetter}{summaryRowIndex - 3}+{totalQuantityColLetter}{summaryRowIndex - 1}";
userWorkSheet.Cells[summaryRowIndex, sumColStart + 37].formula = $"={totalAmountColLetter}{summaryRowIndex - 3}+{totalAmountColLetter}{summaryRowIndex - 1}";
// 下一年的index
sumColStart += 38;
summaryRowIndex = summaryRowStartIndex;
} while (sumColStart < lastColumnIndex);
}
private static void GenSalesTotal(ImportContext context)
{
var app = context.Application;
var totalWorksheet = CreateSheetIfNotExisted(app, "JQ Total(Total)");
PrepareTotalHeader(totalWorksheet, context);
// 删除现有数据
int totalRowIndex = 4;
int lastRowIndex = totalWorksheet.UsedRange.Rows.Count;
if (lastRowIndex > totalRowIndex)
{
try
{
totalWorksheet.Application.DisplayAlerts = false;
var deleteRows = totalWorksheet.Rows[$"{totalRowIndex}:{lastRowIndex}"];
deleteRows.UnMerge();
deleteRows.EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp);
}
catch (Exception ex)
{
FileLogger.Error("删除Total(All)旧数据时发生错误", ex);
}
finally
{
totalWorksheet.Application.DisplayAlerts = true;
}
}
foreach (var userSheet in context.SalesWorksheets)
{
Range usedRange = userSheet.UsedRange;
int rowIndexStart = 4;
int rowIndexEnd = usedRange.Rows.Count - 5;
int rowCount = rowIndexEnd - rowIndexStart;
if (rowCount > 1)
{
Range sourceRange = userSheet.Range[
userSheet.Cells[rowIndexStart, 1],
userSheet.Cells[rowIndexEnd, usedRange.Columns.Count]
];
int totalRowIndexEnd = totalRowIndex + rowCount;
Range targetRange = totalWorksheet.Range[
totalWorksheet.Cells[totalRowIndex, 1],
totalWorksheet.Cells[totalRowIndexEnd, usedRange.Columns.Count]
];
sourceRange.Copy(targetRange);
totalRowIndex = totalRowIndexEnd + 1;
}
}
totalWorksheet.Cells[totalRowIndex, 1].Value = "總結";
var sumRange = totalWorksheet.Range[
totalWorksheet.Cells[totalRowIndex, 1],
totalWorksheet.Cells[totalRowIndex + 3, 5]
];
sumRange.Merge();
UpdateSalesDetailSummary(totalWorksheet, totalRowIndex);
// 自适应所有已用列宽
FormatSheetStyle(totalWorksheet);
totalWorksheet.Cells[4, 12].Select();
totalWorksheet.Application.ActiveWindow.FreezePanes = true;
}
private static void FormatSheetStyle(Worksheet worksheet)
{
worksheet.Activate();
// 自适应所有已用列宽
Range usedRange2 = worksheet.UsedRange;
if (usedRange2 != null)
{
usedRange2.Columns.AutoFit();
usedRange2.Borders.LineStyle = XlLineStyle.xlContinuous;
usedRange2.Borders.Weight = XlBorderWeight.xlThin;
}
}
// 列号转字母方法
private static string GetColumnLetter(int col)
{
string colLetter = "";
while (col > 0)
{
int mod = (col - 1) % 26;
colLetter = (char)(65 + mod) + colLetter;
col = (col - mod - 1) / 26;
}
return colLetter;
}
public static int FindRowBySalesAndClient(Worksheet userSheet, string salesName, string clientName)
{
Range salesRows = userSheet.Columns[1];
Range found = salesRows.Find(What: salesName, LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (found == null)
return 0;
string firstAddress = found?.Address;
Range cur = found;
int result = 0;
do
{
var rIndex = cur.Row;
Range eCell = userSheet.Cells[rIndex, 5];
var rawValue = eCell.Value2;
if (rawValue != null && string.Equals(rawValue.ToString().Trim(), clientName, StringComparison.OrdinalIgnoreCase))
{
result = cur.Row;
break;
}
cur = salesRows.FindNext(cur);
} while (cur != null && cur.Address != firstAddress);
return result;
}
public static void WriteSalesDetailsData(
ImportContext context,
ImportContext.GroupedSalesDetailModel userGroupedSalesDetail,
Worksheet userSheet,
int yearColIndex)
{
var detailSummaryRow = userSheet.UsedRange.Find(What: "總結", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int summaryRowIndex = userSheet.UsedRange.Rows.Count; // 从第四行开始写入数据
bool hasSummary = detailSummaryRow != null;
if (hasSummary)
{
summaryRowIndex = detailSummaryRow.Row;
}
var clientSalesList = context.GetGroupedSalesDetailByClientName(userGroupedSalesDetail.Details);
foreach (var clientSalesDetail in clientSalesList)
{
var clientSalesRowIndex = FindRowBySalesAndClient(userSheet, clientSalesDetail.SalesName, clientSalesDetail.ClientName);
if (clientSalesRowIndex == 0)
{
clientSalesRowIndex = summaryRowIndex;
if (hasSummary)
{
userSheet.Rows[clientSalesRowIndex].Insert(XlInsertShiftDirection.xlShiftDown);
summaryRowIndex++;
}
else
{
clientSalesRowIndex++;
summaryRowIndex++;
}
}
ClientContact clientContact = context.ClientContactList.FirstOrDefault(x => x.Name == clientSalesDetail.ClientName) ?? new ClientContact
{
Name = clientSalesDetail.ClientName,
};
var productSalesList = clientSalesDetail.Details
.GroupBy(x => x.ProductNameEN)
.Select(grouped => new ImportContext.GroupedSalesDetailModel
{
SalesName = clientSalesDetail.SalesName,
ClientName = clientSalesDetail.ClientName,
ProductNameEN = grouped.Key,
TotalAmount = grouped.Sum(x => x.TotalAmount ?? 0),
Quantity = grouped.Sum(x => x.Quantity ?? 0),
Details = grouped.ToList()
}).ToList();
Range salesNameCell = userSheet.Cells[clientSalesRowIndex, 1];
int curRowCount = productSalesList.Count + 1;
if (salesNameCell.MergeCells)
{
Range mergedCell = salesNameCell.MergeArea;
curRowCount = mergedCell.Rows.Count;
foreach (var productSales in productSalesList)
{
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 + curRowCount - 1;
Range clientNameRange = userSheet.Range[userSheet.Cells[clientSalesRowIndex, 1], userSheet.Cells[totalRowIndex, 1]];
clientNameRange.Merge();
WriteSalesDetailToSheet(userSheet, clientNameRange, clientSalesRowIndex, yearColIndex, clientContact, productSalesList);
// 客户销量汇总
var totalSales = new List<ImportContext.GroupedSalesDetailModel>()
{
new ImportContext.GroupedSalesDetailModel
{
SalesName = clientSalesDetail.SalesName,
ClientName = clientSalesDetail.ClientName,
ProductNameEN = "總銷售額",
TotalAmount = clientSalesDetail.TotalAmount,
Quantity = clientSalesDetail.Quantity,
Details = clientSalesDetail.Details.Aggregate(
new List<SalesDetail>(),
(accDetail, curDetail) => {
var monthSales = accDetail.FirstOrDefault(x => x.Month == curDetail.Month);
if (monthSales == null)
{
monthSales = new SalesDetail
{
Month = curDetail.Month,
SalesName = curDetail.SalesName,
ProductNameEN = "總銷售額",
Quantity = 0,
Tax = 0,
TotalAmount = 0,
PriceWithTax = 0,
AmountWithoutTax = 0
};
accDetail.Add(monthSales);
}
monthSales.Quantity += curDetail.Quantity;
monthSales.Tax += curDetail.Tax;
monthSales.TotalAmount += curDetail.TotalAmount;
//monthSales.PriceWithTax += curDetail.PriceWithTax;
monthSales.AmountWithoutTax += curDetail.AmountWithoutTax;
return accDetail;
})
}
};
WriteSalesDetailToSheet(userSheet, clientNameRange, totalRowIndex, yearColIndex, clientContact, totalSales);
}
// 计算总结
if (!hasSummary)
{
summaryRowIndex++;
}
userSheet.Cells[summaryRowIndex, 1].Value = "總結";
var sumRange = userSheet.Range[
userSheet.Cells[summaryRowIndex, 1],
userSheet.Cells[summaryRowIndex + 3, 5]
];
sumRange.Merge();
UpdateSalesDetailSummary(userSheet, summaryRowIndex);
FormatSheetStyle(userSheet);
userSheet.Cells[4, 12].Select();
userSheet.Application.ActiveWindow.FreezePanes = true;
}
private static int WriteSalesDetailToSheet(Worksheet userSheet, Range clientNameRange, int rowIndex, int yearColIndex, ClientContact clientContact, IEnumerable<ImportContext.GroupedSalesDetailModel> salesDetailList)
{
Range productNamesCellRange = userSheet.Range[
userSheet.Cells[clientNameRange.Row, 11],
userSheet.Cells[clientNameRange.Row + clientNameRange.Rows.Count - 1, 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[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;
userSheet.Cells[currentRowIndex, 11].NumberFormat = "@";
// 计算每月的数量和金额
int monthCol = yearColIndex;
for (int i = 1; i < 13; i++)
{
int monthCounter = 0;
var monthDetail = productSalesDetail.Details.Where(x => x.Month == $"{i}月")
.Aggregate(new SalesDetail()
{
Month = $"{i}月",
Quantity = 0,
TotalAmount = 0,
PriceWithTax = 0,
AmountWithoutTax = 0,
Tax = 0
}, (accSalesModel, curSalesModel) =>
{
accSalesModel.Quantity += (curSalesModel.Quantity ?? 0);
accSalesModel.TotalAmount += (curSalesModel.TotalAmount ?? 0);
accSalesModel.PriceWithTax += (curSalesModel.PriceWithTax ?? 0);
monthCounter++;
return accSalesModel;
});
if (monthCounter > 1)
{
monthDetail.PriceWithTax = monthDetail.PriceWithTax / monthCounter;
}
if (monthDetail == null)
{
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[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[currentRowIndex, 49].Value = productSalesDetail.Quantity;
userSheet.Cells[currentRowIndex, 50].Value = productSalesDetail.TotalAmount;
rowIndex++;
}
return rowIndex;
}
public static int PrepareTotalHeader(Worksheet userSheet, ImportContext importContext)
{
// 合并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 year = importContext.YearList[0];
string yearTitle = $"{year}销售表";
int startCol = 13;
Range yearCol = userSheet.Rows[1].Find(What: yearTitle, LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (yearCol != null)
{
// 已经存在年度销售数据,不需处理
return yearCol.Column;
}
for (int i = 0; i < 38; i++)
{
userSheet.Columns[13].EntireColumn.Insert(XlInsertShiftDirection.xlShiftToRight);
}
// 合并年度销售表标题
userSheet.Range[userSheet.Cells[1, startCol], userSheet.Cells[1, startCol + 37]].Merge();
userSheet.Cells[1, startCol].Value2 = yearTitle;
// 设置月份表头
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 headerRange = userSheet.Rows["1:3"];
headerRange.HorizontalAlignment = XlHAlign.xlHAlignCenter;
return startCol;
}
private static Worksheet GenUserSalesDetailSheet(
ImportContext context,
ImportContext.GroupedSalesDetailModel groupedSalesDetail
)
{
var app = context.Application;
var salesName = groupedSalesDetail.SalesName ?? "UNKONW";
string sheetName = $"JQ Total({salesName.ToUpper()})";
Worksheet userSheet = CreateSheetIfNotExisted(app, sheetName);
return userSheet;
}
private static Worksheet CreateSheetIfNotExisted(Microsoft.Office.Interop.Excel.Application app, string sheetName)
{
Worksheet userSheet = null;
foreach (Worksheet item in app.Worksheets)
{
if (item.Name == sheetName)
{
userSheet = item;
break;
}
}
;
if (userSheet == null)
{
try
{
// 新建工作表并命名
userSheet = app.Worksheets.Add();
userSheet.Name = sheetName;
}
catch
{
throw new FormatException("创建或获取用户工作表失败,请检查是否存在同名工作表。");
}
}
return userSheet;
}
public static DateTime? ResolveExcelDate(object rawValue)
{
try
{
if (rawValue is double dateValue)
{
return DateTime.FromOADate(dateValue);
}
else if (rawValue != null)
{
return DateTime.Parse(rawValue.ToString());
}
}
catch (Exception ex)
{
FileLogger.Error($"解析日期错误 '{rawValue}'", ex);
//throw new FormatException($"日期格式错误 '{rawValue}'");
}
return null;
}
private static void ReadSalesDetails(ImportContext context)
{
var dataRange = context.ImportWorkbook.Sheets[1].UsedRange;
IList<SalesDetail> salesDatailList = new List<SalesDetail>();
if (dataRange.Value2 is object[,] rawSalesDetailData)
{
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++)
{
SalesDetail item = new SalesDetail
{
SalesName = rawSalesDetailData[i, 1]?.ToString() ?? "UNKNOWN",
Month = rawSalesDetailData[i, 2]?.ToString() ?? "",
DeliveryDate = ResolveExcelDate(rawSalesDetailData[i, 3]),
OrderNumber = rawSalesDetailData[i, 4]?.ToString() ?? "",
InvoiceDate = ResolveExcelDate(rawSalesDetailData[i, 5]),
InvoiceNumber = rawSalesDetailData[i, 6]?.ToString() ?? "",
ClientName = rawSalesDetailData[i, 7]?.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);
}
}
context.SalesDetails = salesDatailList;
if (context.YearList.Count > 1)
{
throw new Exception("抱歉,匯入的銷售資料格式有誤,每次只能處理同一個自然年的資料。");
}
}
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 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);
// 获取「產品年度預估」工作表
GenProductForecastData(context);
GenDepartmentSummary(context);
GenBussenessSheet(context);
}
catch (Exception ex)
{
FileLogger.Error("生成销售年度预估表失败", ex);
MessageBox.Show("分析预估表失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
button.Text = originLabel;
}
public static bool FindBussenessYearRow(Worksheet sheet, string targetYear, ref int yearRowIndex)
{
Range usedRange = sheet.UsedRange;
int totalRows = usedRange?.Rows?.Count ?? 0;
Boolean updateCurrentBook = false;
while (yearRowIndex < totalRows - 1)
{
object rawYearValue = sheet.Cells[yearRowIndex, 1]?.Value2;
if (rawYearValue == null)
{
throw new Exception($"更新業務月達成率年份异常");
}
string yearValue = "";
if (rawYearValue is double d)
{
yearValue = (rawYearValue).ToString();
}
if (yearValue == targetYear)
{
updateCurrentBook = true;
break;
}
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 = "";
if (updateCurrentBook)
{
rowRanges = $"{yearRowIndex}:{yearRowIndex + 14}";
Range updateRows = sheet.Rows[rowRanges];
updateRows.Clear();
}
else
{
yearRowIndex = 1;
rowRanges = "1:15";
Range newRows = sheet.Rows[rowRanges];
newRows.Insert(XlInsertShiftDirection.xlShiftDown);
}
// 插入后重新获取引用并清除新行格式
Range targetRows = sheet.Rows[rowRanges];
targetRows.ClearFormats();
}
catch (Exception ex)
{
FileLogger.Error($"更新{context.MonthData[0]}年業務月達成率失败", ex);
throw new Exception($"更新{context.MonthData[0]}年業務月達成率失败", ex);
}
// 合并A1:A2并填入年份
try
{
var monthData = context.MonthData;
var yearValue = monthData[0];
Range yearRange = sheet.Range[$"A{yearRowIndex}", $"A{yearRowIndex + 1}"];
yearRange.Merge();
yearRange.Value2 = yearValue.ToString();
yearRange.NumberFormat = "@"; // 文本格式
int startRowIndex = yearRowIndex;
// 填入月份
for (int i = 0; i < 12; i++)
{
int rowOffset = startRowIndex + 2;
sheet.Cells[i + rowOffset, 1] = $"{i + 1}月";
}
sheet.Cells[startRowIndex + 14, 1].value2 = "Total";
// B列开始填 salesName 及相关数据
int col = 2; // B列
foreach (var salesForcastData in context.SalesForecastList)
{
int rowIndex = startRowIndex; // 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 + 1, startCol + 2]
];
finishRange.Merge();
finishRange.Value2 = "完成%";
// 合并并填入“累積達成率”
Range accRange = sheet.Range[
sheet.Cells[rowIndex, startCol + 3],
sheet.Cells[rowIndex + 1, 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++;
}
var forecastColLetter = GetColumnLetter(col);
var turnoverColLetter = GetColumnLetter(col + 1);
sheet.Cells[rowIndex, col].formula = $"=SUM({forecastColLetter}{startRowIndex}:{forecastColLetter}{rowIndex - 1})";
sheet.Cells[rowIndex, col + 1].formula = $"=SUM({turnoverColLetter}{startRowIndex}:{turnoverColLetter}{rowIndex - 1})";
sheet.Cells[rowIndex, col + 2].formula = $"=SUM({turnoverColLetter}{rowIndex}/{forecastColLetter}{rowIndex})";
sheet.Cells[rowIndex, col + 2].NumberFormat = "0.00%";
col = endCol;
}
// 自适应所有已用列宽
FormatSheetStyle(sheet);
}
catch (Exception ex)
{
FileLogger.Error($"写入{context.MonthData[0]}年業務月達成率失败", 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;
clientContactSheet.Activate();
var clientContactRange = clientContactSheet.UsedRange;
IList<ClientContact> clientContactList = new List<ClientContact>();
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 ReadSalesForecastData(GenForecaseContext context)
{
var app = context.Application;
var salesForecastSheet = app.Worksheets["銷售年度預估"] as Worksheet;
var salesRange = salesForecastSheet.UsedRange;
IList<SalesForecastInput> salesForecastInputList = new List<SalesForecastInput>();
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);
// 只读取第一段的数据
if (input.Name == "total")
break;
}
}
context.SalesForecastList = salesForecastInputList;
context.MonthData = monthData;
}
public static void GenProductSummarySheet(ImportContext context)
{
var app = context.Application;
string sheetName = $"產品年度預估";
Worksheet productSheet = CreateSheetIfNotExisted(app, sheetName);
productSheet.Activate();
context.YearList.ForEach(yearNumber =>
{
Range usedRange = productSheet.UsedRange;
Range turnOverRange = usedRange.Find(What: $"{yearNumber}-Turnover", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (turnOverRange == null)
{
Range estimateRange = 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}-Turnover";
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();
turnOverRange = productSheet.Range[
productSheet.Cells[1, insertCol],
productSheet.Cells[1, insertCol + 5]
];
turnOverRange.Merge();
}
usedRange = productSheet.UsedRange;
var productSalesList = context.SalesDetails
.Where(x => x.InvoiceDate.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);
int totalRowCount = productSheet.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 = productSheet.UsedRange.Find(
What: curProductName,
LookIn: XlFindLookIn.xlValues,
LookAt: XlLookAt.xlWhole,
SearchOrder: XlSearchOrder.xlByRows,
SearchDirection: XlSearchDirection.xlNext,
MatchCase: false
);
if (productRange == null)
{
productSheet.Rows[totalRowCount].Insert();
productSheet.Cells[totalRowCount, 1].Value2 = curProductName;
productSheet.Cells[totalRowCount, 1].NumberFormat = "@";
productRange = productSheet.Cells[totalRowCount, 1];
totalRowCount += 1;
}
int curRowIndex = productRange.Row;
productSheet.Cells[curRowIndex, startColIndex].Value2 = productSalesSummary.Quantity;
productSheet.Cells[curRowIndex, startColIndex + 1].Value2 = productSalesSummary.TotalAmount;
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%";
}
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})";
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}";
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);
}
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;
string sheetName = $"客戶年度預估";
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)
.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 = 3;
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;
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)
{
clientSheet.Rows[totalRowCount].Insert();
clientSheet.Cells[totalRowCount, 1].Value2 = curClientName;
clientRange = clientSheet.Cells[totalRowCount, 1];
totalRowCount += 1;
}
int clientTotalRowIndex = clientRange.Row;
// 处理产品名称行以及总销售行
if (clientRange.MergeCells)
{
Range clientSalesRows = clientRange.MergeArea;
int endRowIndex = clientSalesRows.End[XlDirection.xlDown].Row - 1;
foreach (var clientProductSales in clientProductSalesGrouped)
{
Range clientProductNameCells = clientSheet.Range[
clientSheet.Cells[clientSalesRows.Row, 2],
clientSheet.Cells[endRowIndex, 2]
];
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 = "@";
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 = "總計";
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)
{
var app = context.Application;
string sheetName = $"部门统计";
Worksheet deptSheet = CreateSheetIfNotExisted(app, sheetName);
deptSheet.Activate();
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();
Range initUsedRange = deptSheet.UsedRange;
Range totalRange = initUsedRange.Find(What: $"Total", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int totalRowIndex = initUsedRange.Rows.Count + 1;
if (totalRange != null)
{
totalRowIndex = totalRange.Row;
}
yearList.ForEach(yearNumber =>
{
Range curYearRange = deptSheet.UsedRange.Find(What: $"{yearNumber}", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int startCol = deptSheet.UsedRange.Column + 1;
if (curYearRange != null)
{
startCol = curYearRange.Column;
}
var startColLetter = GetColumnLetter(startCol);
var endColLetter = GetColumnLetter(startCol + 3);
if (curYearRange == null)
{
deptSheet.Range[$"{startColLetter}:{endColLetter}"]
.EntireColumn
.Insert(XlInsertShiftDirection.xlShiftToRight);
}
Range prevYearRange = deptSheet.UsedRange.Find(What: $"{yearNumber - 1}", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int prevStartCol = 0;
string prevForecastColLetter = "";
string prevTurnoverColLetter = "";
if (prevYearRange != null)
{
prevStartCol = prevYearRange.Column;
prevForecastColLetter = GetColumnLetter(prevStartCol);
prevTurnoverColLetter = GetColumnLetter(prevStartCol + 1);
}
deptSheet.Cells[1, startCol].Value2 = $"{yearNumber}";
deptSheet.Range[deptSheet.Cells[1, startCol], deptSheet.Cells[1, startCol + 3]].Merge();
deptSheet.Cells[2, startCol].Value2 = "Forecast";
deptSheet.Cells[2, startCol + 1].Value2 = "Turnover";
deptSheet.Cells[2, startCol + 2].Value2 = "成長率";
deptSheet.Cells[2, startCol + 3].Value2 = "達成率";
var forcastColLetter = startColLetter;
var turnoverColLetter = GetColumnLetter(startCol + 1);
foreach (var salesForecast in context.SalesForecastList.Where(x => x.Name.ToUpper() != "TOTAL"))
{
var salesName = salesForecast.Name;
Range curUsedRange = deptSheet.UsedRange;
Range salesNameRange = curUsedRange.Find(What: $"{salesName}", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (salesNameRange == null)
{
deptSheet.Rows[totalRowIndex].Insert();
deptSheet.Cells[curUsedRange.Rows.Count + 1, 1].Value2 = salesName;
salesNameRange = deptSheet.Cells[curUsedRange.Rows.Count + 1, 1];
totalRowIndex++;
}
deptSheet.Cells[salesNameRange.Row, startCol].Value2 = salesForecast.Total;
if (prevYearRange != null)
{
deptSheet.Cells[salesNameRange.Row, startCol + 2].formula = $"=SUM({forcastColLetter}{salesNameRange.Row} - {prevTurnoverColLetter}{salesNameRange.Row})/{forcastColLetter}{salesNameRange.Row}";
deptSheet.Cells[salesNameRange.Row, startCol + 2].NumberFormat = "0.00%";
}
deptSheet.Cells[salesNameRange.Row, startCol + 3].formula = $"={turnoverColLetter}{salesNameRange.Row}/{forcastColLetter}{salesNameRange.Row}";
deptSheet.Cells[salesNameRange.Row, startCol + 3].NumberFormat = "0.00%";
}
deptSheet.Cells[totalRowIndex, 1].Value2 = "Total";
deptSheet.Cells[totalRowIndex, startCol].formula = $"=SUM({forcastColLetter}3:{forcastColLetter}{totalRowIndex - 1})";
deptSheet.Cells[totalRowIndex, startCol + 1].formula = $"=SUM({turnoverColLetter}3:{turnoverColLetter}{totalRowIndex - 1})";
if (prevYearRange != null)
{
deptSheet.Cells[totalRowIndex, startCol + 2].formula = $"=SUM({forcastColLetter}{totalRowIndex} - {prevTurnoverColLetter}{totalRowIndex})/{forcastColLetter}{totalRowIndex}";
deptSheet.Cells[totalRowIndex, startCol + 2].NumberFormat = "0.00%";
}
deptSheet.Cells[totalRowIndex, startCol + 3].formula = $"={turnoverColLetter}{totalRowIndex}/{forcastColLetter}{totalRowIndex}";
deptSheet.Cells[totalRowIndex, startCol + 3].NumberFormat = "0.00%";
});
FormatSheetStyle(deptSheet);
}
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();
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 estimateColIndex = 2;
if (estimateRange != null)
{
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();
}
productSheet.Cells[1, estimateColIndex].Value2 = $"{yearNumber}-Estimate";
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, estimateColIndex + 4],
productSheet.Cells[2, estimateColIndex + 5]
];
growthRange.Merge();
estimateRange = productSheet.Range[
productSheet.Cells[1, estimateColIndex],
productSheet.Cells[1, estimateColIndex + 5]
];
estimateRange.Merge();
// 当前的汇总行
Range totalRange = productSheet.UsedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int totalRowCount = productSheet.UsedRange.Rows.Count + 1;
if (totalRange != null)
{
totalRowCount = totalRange.Row;
}
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);
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)
{
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,
SearchOrder: XlSearchOrder.xlByRows,
SearchDirection: XlSearchDirection.xlNext,
MatchCase: false
);
if (productRange == null)
{
// 创建新的产品预算行
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;
}
}
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})";
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}";
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)
{
var app = context.Application;
string sheetName = $"部门统计";
Worksheet deptSheet = CreateSheetIfNotExisted(app, sheetName);
deptSheet.Activate();
context.YearList.ForEach(yearoNumber =>
{
Range yearRange = deptSheet.UsedRange.Find(What: $"{yearoNumber}", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (yearRange == null)
{
FileLogger.Info($"Cannot find year column in department summary: {yearoNumber}");
return;
}
var startCol = yearRange.Column;
var startColLetter = GetColumnLetter(startCol);
var turnoverColLetter = GetColumnLetter(startCol + 1);
Range totalRange = deptSheet.UsedRange.Find(What: $"Total", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int totalRowIndex = deptSheet.UsedRange.Rows.Count + 1;
if (totalRange != null)
{
totalRowIndex = totalRange.Row;
}
var salesDetails = context.SalesDetails
.Where(x => x.InvoiceDate.HasValue && x.InvoiceDate.Value.Year == yearoNumber && x.SalesName.ToUpper() != "TOTAL")
.GroupBy(x => x.SalesName)
.Select(grouped => new
{
SalesName = grouped.Key,
Turnover = grouped.Sum(x => x.TotalAmount ?? 0)
})
.ToList();
foreach (var salesDetail in salesDetails)
{
var salesName = salesDetail.SalesName;
Range curUsedRange = deptSheet.UsedRange;
Range salesNameRange = curUsedRange.Find(What: $"{salesName}", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
if (salesNameRange != null)
{
deptSheet.Cells[salesNameRange.Row, startCol + 1].Value2 = salesDetail.Turnover;
}
}
deptSheet.Cells[totalRowIndex, startCol + 1].formula = $"=SUM({turnoverColLetter}3:{turnoverColLetter}{totalRowIndex - 1})";
});
}
}
}