Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions ClosedXML.Report/ClosedXML.Report.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
<PackageId>ClosedXML.Report</PackageId>
<Configurations>Debug;Release</Configurations>
<PackageProjectUrl>https://github.com/ClosedXML/ClosedXML.Report</PackageProjectUrl>
<RepositoryUrl>https://github.com/ClosedXML/ClosedXML.Report</RepositoryUrl>
<Authors>Alexey Rozhkov, Alexey Pankratev</Authors>
<RepositoryUrl>https://github.com/eagleoriginal/ClosedXML.Report</RepositoryUrl>
<Authors>IOrlov, Alexey Rozhkov, Alexey Pankratev</Authors>
<Copyright>MIT</Copyright>
<Product>ClosedXML.Report</Product>
<PackageReleaseNotes>See https://github.com/ClosedXML/ClosedXML.Report/releases/tag/$(productVersion)</PackageReleaseNotes>
<Description>ClosedXML.Report is a tool for report generation and data analysis in .NET applications through the use of Microsoft Excel. ClosedXML.Report is a .NET-library for report generation Microsoft Excel without requiring Excel to be installed on the machine that's running the code.</Description>
<Product>CustomEa.ClosedXML.Report</Product>
<PackageReleaseNotes>See https://github.com/eagleoriginal/ClosedXML.Report/releases/tag/$(productVersion)</PackageReleaseNotes>
<Description>Not Intended for Common Use!!!
Pacakge with Custom Changes in original ClosedXML.Report.
ClosedXML.Report is a tool for report generation and data analysis in .NET applications through the use of Microsoft Excel. ClosedXML.Report is a .NET-library for report generation Microsoft Excel without requiring Excel to be installed on the machine that's running the code.</Description>
<PackageTags>ClosedXML Reporting Excel</PackageTags>
<Company>ClosedXML</Company>
<PackageIconUrl>https://github.com/ClosedXML/ClosedXML.Report/raw/develop/Resources/favicon-01.png</PackageIconUrl>
<Company>CustomEa.ClosedXML</Company>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>

<!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
Expand All @@ -30,6 +31,8 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>ClosedXML.Report.snk</AssemblyOriginatorKeyFile>
<Version>0.2.13-Beta8</Version>
<PackageIcon>favicon-01.png</PackageIcon>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)'=='Release'">
Expand All @@ -38,6 +41,13 @@

<ItemGroup>
<PackageReference Include="ClosedXML" Version="0.105.*" />
<None Include="..\Resources\favicon-01.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="morelinq" Version="4.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.0.2" />
Expand Down
24 changes: 22 additions & 2 deletions ClosedXML.Report/Excel/Subtotal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,16 @@ private MoveData[] ScanRange(int groupBy)

var val = row.Cell(groupBy).GetString();
var isSummaryRow = row.IsSummary();

if (string.IsNullOrEmpty(val) && !isSummaryRow)
var isLeftGroupEndLine = false;
if (false == isSummaryRow)
{
isLeftGroupEndLine = _groups.SingleOrDefault(gr => gr.Column == groupBy - 1 &&
gr.SummaryRow == null &&
gr.Range.RangeAddress.LastAddress.RowNumber == row.RowNumber()) != null;
}

if (this._summaryAbove && // TODO: Without _summaryAbove clarification all empty values fall into RangeType.HeaderRow category and as result no groups created
string.IsNullOrEmpty(val) && !isSummaryRow)
{
if (groupStart > 0)
{
Expand All @@ -364,6 +372,18 @@ private MoveData[] ScanRange(int groupBy)
continue;
}

if (isLeftGroupEndLine)
{
var localGroupStart = groupStart == 0
? row.RangeAddress.Relative(_range.RangeAddress).FirstAddress.RowNumber
: groupStart;

groups.Add(CreateMoveTask(groupBy, prevVal, _range.Cell(localGroupStart, 1), row.LastCell(), RangeType.DataRange));
prevVal = null;
groupStart = 0;
continue;
}

if (val != prevVal)
{
if (groupStart > 0)
Expand Down
2 changes: 2 additions & 0 deletions ClosedXML.Report/Excel/SubtotalSummaryFunc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public virtual int FuncNum
}
}

public object DefaultValueForEmptySource { get; set; }

public Func<Type, Delegate> GetCalculateDelegate;

internal object Calculate(IDataSource dataSource)
Expand Down
8 changes: 7 additions & 1 deletion ClosedXML.Report/Excel/XlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,13 @@ public static void ReplaceCFFormulaeToA1(this IXLWorksheet worksheet)
{
foreach (var format in worksheet.ConditionalFormats)
{
var target = format.Ranges.OrderBy(x=>x.RangeAddress.FirstAddress.RowNumber)
format.Ranges.RemoveAll(range => false == range.RangeAddress.IsValid);
var validRanges = format.Ranges.Where(range => range.RangeAddress.IsValid).ToList();
if (false == validRanges.Any())
continue;

var target =
validRanges.OrderBy(x=>x.RangeAddress.FirstAddress.RowNumber)
.ThenBy(x=> x.RangeAddress.FirstAddress.ColumnNumber)
.First().FirstCell();
foreach (var v in format.Values.Where(v => v.Value.Value.StartsWith("&=")).ToList())
Expand Down
63 changes: 59 additions & 4 deletions ClosedXML.Report/Options/GroupTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ OPTION PARAMS OBJECTS RNG Priority
"\PageBreaks"
"\TotalLabel"
"\GrandLabel"
"\DisableSubtotalLine"

"SummaryAbove" Range rD Normal

Expand All @@ -30,6 +31,8 @@ OPTION PARAMS OBJECTS RNG Priority
using ClosedXML.Excel;
using ClosedXML.Report.Excel;
using ClosedXML.Report.Utils;
using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Spreadsheet;
using MoreLinq;

namespace ClosedXML.Report.Options
Expand All @@ -40,6 +43,7 @@ public class GroupTag : SortTag

public bool PageBreaks => Parameters.ContainsKey("pagebreaks");
public bool DisableSubtotals => Parameters.ContainsKey("disablesubtotals");
public bool DisableSubtotalLine => Parameters.ContainsKey("disablesubtotalline");
public bool Collapse => Parameters.ContainsKey("collapse");
public bool DisableOutLine => Parameters.ContainsKey("disableoutline");
public bool OutLine => !Parameters.ContainsKey("disableoutline");
Expand Down Expand Up @@ -110,11 +114,48 @@ private void Process(ProcessingContext context, GroupTag[] groups, bool summaryA
var level = 0;
var rows = root.RowCount() - 1;
var columns = root.ColumnCount();
if (rows <= 0 || columns <= 0)
if (columns <= 0)
{
return;
}

var r = root.Offset(0, 0, rows, columns);
// Empty Total grand for report
if (rows <= 0)
{
if (disableGrandTotal)
return;

var r2= root.Offset(0, 0, 1, columns);
using (var subtotal = new Subtotal(r2, summaryAbove, groups, context.Evaluator))
{
if (TotalLabel != null) subtotal.TotalLabel = TotalLabel;
if (GrandLabel != null) subtotal.GrandLabel = GrandLabel;
if (!disableGrandTotal)
{
var total = subtotal.AddGrandTotal(summaries);
total.SummaryRow.Cell(2).Value = total.SummaryRow.Cell(1).Value;
total.SummaryRow.Cell(1).Value = Blank.Value;
level++;
}

foreach (var subGroup in subtotal.Groups.OrderBy(x => x.Column).Reverse())
{
FormatHeaderFooter(subGroup, groupRow);

GroupRender(subGroup, new GroupTag { Column = 1, Level = 1 });
}

r2.Rows().ForEach(r => r.WorksheetRow().OutlineLevel = 0);
}

// Rem DoDeleteSpecialRow
root.LastRow().Delete(XLShiftDeletedCells.ShiftCellsUp);

return;
}

var r = root.Offset(0, 0, rows, columns);

using (var subtotal = new Subtotal(r, summaryAbove, groups, context.Evaluator))
{
if (TotalLabel != null) subtotal.TotalLabel = TotalLabel;
Expand All @@ -129,9 +170,18 @@ private void Process(ProcessingContext context, GroupTag[] groups, bool summaryA

foreach (var g in groups.OrderBy(x => x.Column))
{
// Todo: New Feature Group Without Subtotal. Only Merge.
if (g.DisableSubtotalLine)
{
subtotal.ScanForGroups(g.Column);
g.Level = ++level;

continue;
}

Func<string, string> labFormat = null;
if (!string.IsNullOrEmpty(g.LabelFormat))
labFormat = title => string.Format(LabelFormat, title);
labFormat = title => string.Format(g.LabelFormat, title);

if (g.MergeLabels == MergeMode.Merge2 && summaries.Length == 0)
subtotal.ScanForGroups(g.Column);
Expand Down Expand Up @@ -218,7 +268,12 @@ protected virtual void GroupRender(SubtotalGroup subGroup, GroupTag grData)
var rng = subGroup.Range.Column(subGroup.Column);
if (subGroup.Range.RowCount() > 1)
{
int cellIdx = _maxLevel - subGroup.Level + 1;
// TODO: Wrong Style apply for merged cells if on right has grouped total
// But in first cell i expect already cell with value and style
// Plus with DisableSubtotalLine feature this became totally wrong
int cellIdx = 1;
//int cellIdx = _maxLevel - subGroup.Level + 1; // TODO: Comment for future investigation

var style = rng.Cell(cellIdx).Style;
rng.Merge();
rng.Style = style;
Expand Down
21 changes: 20 additions & 1 deletion ClosedXML.Report/Options/SummaryFuncTag.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using ClosedXML.Excel;
using ClosedXML.Report.Excel;
using ClosedXML.Report.Utils;
using DocumentFormat.OpenXml.Math;
using DocumentFormat.OpenXml.Spreadsheet;

namespace ClosedXML.Report.Options
{
Expand Down Expand Up @@ -32,8 +35,16 @@ public override void Execute(ProcessingContext context)
summRow = context.Range.LastRow();
calculatedRange = context.Range.Offset(0, summ.Column - 1, context.Range.RowCount() - 1, 1);
}
var items = summ.DataSource.GetAll();

if (summ.FuncNum == 0)
if (items == null || items.Length == 0)
{
if (summ.DefaultValueForEmptySource != null)
{
summRow.Cell(summ.Column).Value = XLCellValueConverter.FromObject(summ.DefaultValueForEmptySource);
}
}
else if (summ.FuncNum == 0)
{
var value = summ.Calculate((IDataSource)context.Value);
summRow.Cell(summ.Column).Value = XLCellValueConverter.FromObject(value);
Expand Down Expand Up @@ -62,6 +73,14 @@ private SubtotalSummaryFunc GetFunc(ProcessingContext context)
//return XLDynamicExpressionParser.ParseLambda(new[] {par}, null, GetParameter("Over"));
};
func.DataSource = DataSource;

if (HasParameter("Default"))
{
var dlg = context.Evaluator.ParseExpression(GetParameter("Default"), new ParameterExpression[] {});

func.DefaultValueForEmptySource = dlg.DynamicInvoke();
}

return func;
}
}
Expand Down
9 changes: 8 additions & 1 deletion ClosedXML.Report/RangeInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ string EvalString(string str)
{
var grownRange = rng.GrowToMergedRanges();
var items = nr.RangeData as object[] ?? nr.RangeData.Cast<object>().ToArray();
// Comment this section
// Revision: a38eda3a3a7092812c8358a949d4e489635501bf
// Author: Aleksei <pankraty@gmail.com>
// Date: 27.07.2023 23:54:54
// Message:
// Forcibly remove a range representing a table if its data source is empty (#251) (#323)
/*if (!items.Any())

if (!items.Any() && grownRange.IsOptionsRowEmpty())
{
Expand All @@ -187,7 +194,7 @@ string EvalString(string str)
// row would thus delete only first cell, not full (empty) options row.
grownRange.Delete(XLShiftDeletedCells.ShiftCellsUp);
continue;
}
}*/

// Range template generates output into a new temporary sheet, as not to affect other things
// and then copies it to the range in the original sheet.
Expand Down
40 changes: 37 additions & 3 deletions tests/ClosedXML.Report.Tests/GroupTagTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using ClosedXML.Report.Tests.TestModels;
using LinqToDB;
using Xunit;
Expand Down Expand Up @@ -36,7 +40,7 @@ public void Simple(string templateFile)
{
using (var db = new DbDemos())
{
var cust = db.customers.LoadWith(x=>x.Orders.First().Items).OrderBy(c => c.CustNo).First(x=>x.CustNo == 1356);
var cust = db.customers.LoadWith(x => x.Orders.First().Items).OrderBy(c => c.CustNo).First(x => x.CustNo == 1356);
cust.Logo = Resource.toms_diving_center;
tpl.AddVariable("MoreOrders", cust.Orders.Take(5));
tpl.AddVariable(cust);
Expand All @@ -50,6 +54,29 @@ public void Simple(string templateFile)
});
}

[Xunit.Fact]
public void Simple_EmptyResult()
{
string templateFile = "GroupTagTests_Simple_Empty.xlsx";
XlTemplateTest(templateFile,
tpl =>
{
using (var db = new DbDemos())
{
var cust = db.customers.LoadWith(x => x.Orders.First().Items).OrderBy(c => c.CustNo).First(x => x.CustNo == 1356);
cust.Orders.Clear();
cust.Logo = Resource.toms_diving_center;
tpl.AddVariable("MoreOrders", cust.Orders.Take(0));
tpl.AddVariable(cust);
}
tpl.AddVariable("Tax", 13);
},
wb =>
{
CompareWithGauge(wb, templateFile);
});
}

[Theory,
InlineData("GroupTagTests_SummaryAbove.xlsx"),
InlineData("GroupTagTests_MergeLabels.xlsx"),
Expand Down Expand Up @@ -77,6 +104,7 @@ public void EmptyDataSource(string templateFile)
InlineData("GroupTagTests_MultiRanges.xlsx"),
InlineData("GroupTagTests_FormulasWithTagsInGroupRow.xlsx", Skip = "Formulas with tags got broken after upgrading to ClosedXML 0.100"),
InlineData("GroupTagTests_TotalLabel.xlsx"),
InlineData("GroupTagTests_DisableSubTotals_MergeLabels.xlsx")
]
public void Customers(string templateFile)
{
Expand All @@ -86,6 +114,7 @@ public void Customers(string templateFile)
using (var db = new DbDemos())
{
var orders = db.orders.LoadWith(x => x.Customer).ToList();
//orders.ForEach(order => order.Customer.Company = order.Customer.Company == "Adventure Undersea"? "": order.Customer.Company);
tpl.AddVariable("Orders", orders);
}
},
Expand All @@ -102,7 +131,12 @@ public void WithHeader()
tpl =>
{
using (var db = new DbDemos())
tpl.AddVariable("Orders", db.orders.LoadWith(x => x.Customer).OrderBy(c => c.OrderNo).ToArray());
{
db.orders.LoadWith(x => x.Customer);
var orders = db.orders.LoadWith(x => x.Customer).ToList();
//orders.ForEach(order => order.Customer.Company = order.Customer.Company == "Adventure Undersea" ? "" : "");
tpl.AddVariable("Orders", orders.OrderBy(c => c.OrderNo).ToArray());
}
},
wb =>
{
Expand Down
Binary file not shown.
Binary file added tests/Gauges/GroupTagTests_Simple_Empty.xlsx
Binary file not shown.
Binary file not shown.
Binary file added tests/Templates/GroupTagTests_Simple_Empty.xlsx
Binary file not shown.