KellyReport_D/Utils/WorkBookUtils.cs

1149 lines
54 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 System.Threading.Tasks;
using KellyReport_D.Model;
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);
PrepareTotalHeader(userSheet);
WriteSalesDetailsData(importContext, userGroupedSalesDetail, userSheet);
importContext.SalesWorksheets.Add(userSheet);
}
GenSalesTotal(importContext);
// 更新业务预估表
UpdateEstimatedTable(importContext);
UpdateDeptartmentSummary(importContext);
GenProductSummarySheet(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;
for (int i = 2; i < maxColIndex; i += 4)
{
String salesName = bussenessWorksheet.Cells[1, 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 = 3;
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 GenSalesTotal(ImportContext context)
{
var app = context.Application;
var totalWorksheet = CreateSheetIfNotExisted(app, "JQ Total(Total)");
PrepareTotalHeader(totalWorksheet);
int totalRowIndex = 4;
foreach (var userSheet in context.SalesWorksheets)
{
Range usedRange = userSheet.UsedRange;
int rowIndexStart = 4;
int rowIndexEnd = usedRange.Rows.Count;
int rowCount = rowIndexEnd - rowIndexStart + 1;
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;
}
}
totalWorksheet.Cells[totalRowIndex, 1].Value = "總結";
var sumRange = totalWorksheet.Range[
totalWorksheet.Cells[totalRowIndex, 1],
totalWorksheet.Cells[totalRowIndex + 4, 5]
];
sumRange.Merge();
int sumColStart = 13; // L列是第12列
totalWorksheet.Cells[totalRowIndex, 11].Value2 = "總銷量及金額(未稅)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
string quantityColLetter = GetColumnLetter(quantityColIndex);
totalWorksheet.Cells[totalRowIndex, quantityColIndex].formula = $"=(SUM({quantityColLetter}4:{quantityColLetter}{totalRowIndex - 1})/2)";
int amountColIndex = sumColStart + i * 3 + 2;
string amountColLetter = GetColumnLetter(amountColIndex);
totalWorksheet.Cells[totalRowIndex, amountColIndex].formula = $"=((SUM({amountColLetter}4:{amountColLetter}{totalRowIndex - 1})/2) - {amountColLetter}{totalRowIndex + 2})/1.13";
}
string totalQuantityColLetter = GetColumnLetter(49);
string totalAmountColLetter = GetColumnLetter(50);
totalWorksheet.Cells[totalRowIndex, 49].formula = $"=(SUM({totalQuantityColLetter}4:{totalAmountColLetter}{totalRowIndex - 1})/2)";
totalWorksheet.Cells[totalRowIndex, 50].formula = $"=((SUM({totalAmountColLetter}4:{totalAmountColLetter}{totalRowIndex - 1})/2) - {totalAmountColLetter}{totalRowIndex + 2})/1.13";
totalRowIndex++;
totalWorksheet.Cells[totalRowIndex, 11].Value2 = "總銷量及金額(含稅)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
string quantityColLetter = GetColumnLetter(quantityColIndex);
totalWorksheet.Cells[totalRowIndex, quantityColIndex].formula = $"=(SUM({quantityColLetter}4:{quantityColLetter}{totalRowIndex - 1})/2)";
int amountColIndex = sumColStart + i * 3 + 2;
string amountColLetter = GetColumnLetter(amountColIndex);
totalWorksheet.Cells[totalRowIndex, amountColIndex].formula = $"=(SUM({amountColLetter}4:{amountColLetter}{totalRowIndex - 1})/2)";
}
totalWorksheet.Cells[totalRowIndex, 49].formula = $"=(SUM({totalQuantityColLetter}4:{totalQuantityColLetter}{totalRowIndex - 1})/2)";
totalWorksheet.Cells[totalRowIndex, 50].formula = $"=(SUM({totalAmountColLetter}4:{totalAmountColLetter}{totalRowIndex - 1})/2)";
totalRowIndex++;
totalWorksheet.Cells[totalRowIndex, 11].Value2 = "總銷量及金額(外銷)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
totalWorksheet.Cells[totalRowIndex, quantityColIndex].Value2 = 0;
int amountColIndex = sumColStart + i * 3 + 2;
totalWorksheet.Cells[totalRowIndex, amountColIndex].Value2 = 0;
}
totalWorksheet.Cells[totalRowIndex, 49].Value2 = 0;
totalWorksheet.Cells[totalRowIndex, 50].Value2 = 0;
totalRowIndex++;
totalWorksheet.Cells[totalRowIndex, 11].Value2 = "總銷量及金額(內外銷)";
for (int i = 0; i < 12; i++)
{
int quantityColIndex = sumColStart + i * 3 + 1;
string quantityColLetter = GetColumnLetter(quantityColIndex);
totalWorksheet.Cells[totalRowIndex, quantityColIndex].formula = $"={quantityColLetter}{totalRowIndex - 3}+{quantityColLetter}{totalRowIndex - 1}";
int amountColIndex = sumColStart + i * 3 + 2;
string amountColLetter = GetColumnLetter(amountColIndex);
totalWorksheet.Cells[totalRowIndex, amountColIndex].formula = $"={amountColLetter}{totalRowIndex - 3}+{amountColLetter}{totalRowIndex - 1}";
}
totalWorksheet.Cells[totalRowIndex, 49].formula = $"={totalQuantityColLetter}{totalRowIndex - 3}+{totalQuantityColLetter}{totalRowIndex - 1}";
totalWorksheet.Cells[totalRowIndex, 50].formula = $"={totalAmountColLetter}{totalRowIndex - 3}+{totalAmountColLetter}{totalRowIndex - 1}";
// 自适应所有已用列宽
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 void WriteSalesDetailsData(
ImportContext context,
ImportContext.GroupedSalesDetailModel userGroupedSalesDetail,
Worksheet userSheet
)
{
int rowIndex = 4; // 从第四行开始写入数据
var clientSalesList = context.GetGroupedSalesDetailByClientName(userGroupedSalesDetail.Details);
foreach (var clientSalesDetail in clientSalesList)
{
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()
});
rowIndex = WriteSalesDetailToSheet(userSheet, rowIndex, 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;
})
}
};
rowIndex = WriteSalesDetailToSheet(userSheet, rowIndex, clientContact, totalSales);
}
FormatSheetStyle(userSheet);
userSheet.Cells[4, 12].Select();
userSheet.Application.ActiveWindow.FreezePanes = true;
}
private static int WriteSalesDetailToSheet(Worksheet userSheet, int rowIndex, ClientContact clientContact, IEnumerable<ImportContext.GroupedSalesDetailModel> salesDetailList)
{
foreach (var productSalesDetail in salesDetailList)
{
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;
// 计算每月的数量和金额
int monthCol = 13; // L列是第12列
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[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; // 金額
}
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[rowIndex, 49].Value = productSalesDetail.Quantity;
userSheet.Cells[rowIndex, 50].Value = productSalesDetail.TotalAmount;
rowIndex++;
}
return rowIndex;
}
public static void PrepareTotalHeader(Worksheet userSheet)
{
// 合并A1:L1并设置标题
userSheet.Range["A1", "L1"].Merge();
userSheet.Range["A1"].Value2 = "客戶資料";
// 合并并设置各列标题
userSheet.Range["A2", "A3"].Merge();
userSheet.Range["A2"].Value2 = "業務";
userSheet.Range["B2", "B3"].Merge();
userSheet.Range["B2"].Value2 = "地區";
userSheet.Range["C2", "C3"].Merge();
userSheet.Range["C2"].Value2 = "行業";
userSheet.Range["D2", "D3"].Merge();
userSheet.Range["D2"].Value2 = "產能/年";
userSheet.Range["E2", "E3"].Merge();
userSheet.Range["E2"].Value2 = "名稱";
userSheet.Range["F2", "F3"].Merge();
userSheet.Range["F2"].Value2 = "採購主管";
userSheet.Range["G2", "G3"].Merge();
userSheet.Range["G2"].Value2 = "電話";
userSheet.Range["H2", "H3"].Merge();
userSheet.Range["H2"].Value2 = "採購";
userSheet.Range["I2", "I3"].Merge();
userSheet.Range["I2"].Value2 = "電話";
userSheet.Range["J2", "J3"].Merge();
userSheet.Range["J2"].Value2 = "地址";
userSheet.Range["K2", "K3"].Merge();
userSheet.Range["K2"].Value2 = "產品";
userSheet.Range["L2", "L3"].Merge();
userSheet.Range["L2"].Value2 = "需求/月";
// 计算起始列L列后面列号13
int startCol = 13;
// 合并年度销售表标题
userSheet.Range[userSheet.Cells[1, startCol], userSheet.Cells[1, startCol + 37]].Merge();
userSheet.Cells[1, startCol].Value2 = "年度销售表";
// 设置月份表头
for (int i = 0; i < 12; i++)
{
int monthCol = startCol + i * 3;
userSheet.Range[userSheet.Cells[2, monthCol], userSheet.Cells[2, monthCol + 2]].Merge();
userSheet.Cells[2, monthCol].Value2 = $"{i + 1}月";
userSheet.Cells[3, monthCol].Value2 = "單價";
userSheet.Cells[3, monthCol + 1].Value2 = "數量";
userSheet.Cells[3, monthCol + 2].Value2 = "金額";
}
// 合并Total表头
userSheet.Range[userSheet.Cells[2, startCol + 36], userSheet.Cells[2, startCol + 37]].Merge();
userSheet.Cells[2, startCol + 36].Value2 = "Total";
userSheet.Cells[3, startCol + 36].Value2 = "總數量";
userSheet.Cells[3, startCol + 37].Value2 = "總金額";
// 设置所有已用单元格居中
Range usedRange = userSheet.UsedRange;
usedRange.HorizontalAlignment = XlHAlign.xlHAlignCenter;
}
private static Worksheet GenUserSalesDetailSheet(
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);
Range usedRange = userSheet.UsedRange;
if (usedRange != null && usedRange.Cells.Count > 1)
{
usedRange.Clear();
}
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;
}
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);
for (int i = 3; i <= rowCount; i++)
{
SalesDetail item = new SalesDetail
{
SalesName = rawSalesDetailData[i, 1]?.ToString() ?? "UNKNOWN",
Month = rawSalesDetailData[i, 2]?.ToString() ?? "",
DeliveryDate = rawSalesDetailData[i, 3] is double invoiceDate ? DateTime.FromOADate(invoiceDate) : (DateTime?)null,
OrderNumber = rawSalesDetailData[i, 4]?.ToString() ?? "",
InvoiceDate = rawSalesDetailData[i, 5] != null ? DateTime.Parse(rawSalesDetailData[i, 5]?.ToString()) : (DateTime?)null,
InvoiceNumber = rawSalesDetailData[i, 6]?.ToString() ?? "",
ClientName = rawSalesDetailData[i, 7]?.ToString() ?? "",
ProductNameEN = rawSalesDetailData[i, 8]?.ToString() ?? "",
Quantity = rawSalesDetailData[i, 9] != null ? Convert.ToDecimal(rawSalesDetailData[i, 9]) : (decimal?)null,
PriceWithTax = rawSalesDetailData[i, 10] != null ? Convert.ToDecimal(rawSalesDetailData[i, 10]) : (decimal?)null,
AmountWithoutTax = rawSalesDetailData[i, 11] != null ? Convert.ToDecimal(rawSalesDetailData[i, 11]) : (decimal?)null,
Tax = rawSalesDetailData[i, 12] != null ? Convert.ToDecimal(rawSalesDetailData[i, 12]) : (decimal?)null,
TotalAmount = rawSalesDetailData[i, 13] != null ? Convert.ToDecimal(rawSalesDetailData[i, 13]) : (decimal?)null,
Remark = rawSalesDetailData[i, 14]?.ToString() ?? "",
};
salesDatailList.Add(item);
}
}
context.SalesDetails = salesDatailList;
}
public static Workbook SelectAndOpenExcelFile()
{
var app = Globals.ThisAddIn.Application;
var curWorkbook = app.ActiveWorkbook;
using (OpenFileDialog dialog = new OpenFileDialog())
{
dialog.Filter = "Excel 文件 (*.xlsx)|*.xlsx";
dialog.Title = "请选择要打开的 Excel 文件";
if (dialog.ShowDialog() == DialogResult.OK)
{
string filePath = dialog.FileName;
Workbook workbook = app.Workbooks.Open(filePath);
curWorkbook.Activate();
return workbook;
}
}
return null;
}
public static void GenForecast(System.Windows.Forms.Button button)
{
String originLabel = button.Text;
if (button != null)
{
button.Text = Resources.GENERATING;
}
var app = Globals.ThisAddIn.Application;
var context = new GenForecaseContext
{
Application = app
};
try
{
// 获取「銷售年度預估」工作表
ReadSalesForecastData(context);
// 获取「產品年度預估」工作表
//ReadProductForecastData(context);
GenDepartmentSummary(context);
GenBussenessSheet(context);
}
catch (Exception ex)
{
Console.WriteLine("Error generating forecast:" + ex.Message);
MessageBox.Show("分析预估表失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
button.Text = originLabel;
}
public static void GenBussenessSheet(GenForecaseContext context)
{
var app = context.Application;
// 假设 app 是 Excel.Application
Worksheet sheet = null;
string sheetName = "業務月達成率";
try
{
// 新增工作表
sheet = app.Worksheets.Add();
sheet.Name = sheetName;
}
catch
{
// 已存在则获取
sheet = app.Worksheets[sheetName] as Worksheet;
try
{
Range usedRange = sheet.UsedRange;
if (usedRange != null && usedRange.Cells.Count > 1)
{
usedRange.Clear();
}
}
catch
{
// 无 UsedRange 可忽略
}
}
// 合并A1:A2并填入年份
try
{
var monthData = context.MonthData;
var yearValue = monthData[0];
Range yearRange = sheet.Range["A1", "A2"];
yearRange.Merge();
yearRange.Value2 = yearValue.ToString();
yearRange.NumberFormat = "@"; // 文本格式
int startRowIndex = 1;
// 填入月份
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)
{
MessageBox.Show("写入业绩月达成率时发生错误:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static void ReadClientContact(ImportContext context)
{
var app = context.Application;
var clientContactSheet = app.Worksheets["客戶資料"] as Worksheet;
var clientContactRange = clientContactSheet.UsedRange;
IList<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 ReadProductForecastData(GenForecaseContext context)
{
var app = context.Application;
var productForecastSheet = app.Worksheets["產品年度預估"] as Worksheet;
var productRange = productForecastSheet.UsedRange;
IList<ProductForecastInput> productForecastList = new List<ProductForecastInput>();
if (productRange.Value2 is object[,] rawProductData)
{
int rowCount = rawProductData.GetLength(0);
int colCount = rawProductData.GetLength(1);
if (rawProductData[1, 2] == null)
{
throw new MissingMemberException("「產品年度預估」工作表的(年份)不能为空,请检查数据格式。");
}
var year = Convert.ToInt32(rawProductData[1, 2]);
// 模板第三行开始是数据
for (int i = 3; i <= rowCount; i++)
{
var input = new ProductForecastInput
{
Year = year,
Name = rawProductData[i, 1]?.ToString() ?? "",
Quantity = rawProductData[i, 2] != null ? Convert.ToDecimal(rawProductData[i, 2]) : 0,
AmountTax = rawProductData[i, 3] != null ? Convert.ToDecimal(rawProductData[i, 3]) : 0,
Amount = rawProductData[i, 4] != null ? Convert.ToDecimal(rawProductData[i, 4]) : 0,
PriceAvg = rawProductData[i, 5] != null ? Convert.ToDecimal(rawProductData[i, 5]) : 0,
};
productForecastList.Add(input);
}
}
context.ProductForecastList = productForecastList;
}
private static void ReadSalesForecastData(GenForecaseContext context)
{
var app = context.Application;
var salesForecastSheet = app.Worksheets["銷售年度預估"] as Worksheet;
var salesRange = salesForecastSheet.UsedRange;
IList<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);
}
}
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();
var yearList = context.SalesDetails
.Where(x => x.InvoiceDate.HasValue)
.Select(x => x.InvoiceDate.Value.Year)
.Distinct()
.ToList();
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();
}
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 = usedRange.Find(What: $"總計", LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlWhole, SearchOrder: XlSearchOrder.xlByRows, SearchDirection: XlSearchDirection.xlNext, MatchCase: false);
int totalRowCount = 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 = 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];
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);
}
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);
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.Range[$"{startColLetter}:{endColLetter}"]
.EntireColumn
.Insert(XlInsertShiftDirection.xlShiftToRight);
deptSheet.Cells[1, 2].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 UpdateDeptartmentSummary(ImportContext context)
{
var app = context.Application;
string sheetName = $"部门统计";
Worksheet deptSheet = CreateSheetIfNotExisted(app, sheetName);
deptSheet.Activate();
var yearList = context.SalesDetails.Where(x => x.InvoiceDate.HasValue).Select(x => x.InvoiceDate.Value.Year).Distinct().ToList();
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)
{
Console.WriteLine("Cannot find year column in department summary: {0}", 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})";
});
}
}
}