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