List Report kısmındaki Gruplar çalıştırıldı
This commit is contained in:
parent
5970221681
commit
75b459d79e
8 changed files with 405 additions and 66 deletions
|
|
@ -399,6 +399,16 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
|
|||
parameters.Add("@param" + i, input.Filters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var cascadeParameterCount = lookup.CascadeParentFields?
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Length ?? 0;
|
||||
|
||||
for (int i = input.Filters?.Length ?? 0; i < cascadeParameterCount; i++)
|
||||
{
|
||||
parameters.Add("@param" + i, null!);
|
||||
}
|
||||
|
||||
var lookupQuery = defaultValueHelper.GetDefaultValue(lookup.LookupQuery);
|
||||
return await dynamicDataRepository.QueryAsync(lookupQuery, connectionString, parameters);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public class DynamicFormReport : XtraReport
|
|||
Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
|
||||
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
|
||||
Landscape = false;
|
||||
Font = new DXFont("Arial", 8.25F);
|
||||
Font = new DXFont("Arial", 8F);
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
|
|
@ -206,7 +206,7 @@ public class DynamicFormReport : XtraReport
|
|||
{
|
||||
Text = DynamicReportImageHelper.Localize(localizer, group.Caption, group.Caption),
|
||||
BoundsF = new System.Drawing.RectangleF(0, y, pageWidth, 22),
|
||||
Font = new DXFont("Arial", 9F, DXFontStyle.Bold),
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
|
||||
Padding = new PaddingInfo(5, 5, 2, 2),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class DynamicGridReport : XtraReport
|
|||
private const int A4PortraitWidth = 827;
|
||||
private const int HorizontalMargin = 50;
|
||||
private const int LandscapeColumnThreshold = 5;
|
||||
private const float GroupIndentWidth = 18F;
|
||||
private static readonly System.Drawing.Color TableBorderColor = System.Drawing.Color.FromArgb(226, 232, 240);
|
||||
private static IServiceScopeFactory _scopeFactory;
|
||||
|
||||
|
|
@ -122,21 +123,38 @@ public class DynamicGridReport : XtraReport
|
|||
.Take(8)
|
||||
.ToList();
|
||||
}
|
||||
var groupedFields = DynamicReportGridStateHelper.GetGroupedFields(
|
||||
availableFields,
|
||||
gridState?.CustomizationData,
|
||||
queryParams.Group);
|
||||
var groupedFieldNames = groupedFields
|
||||
.Select(x => x.FieldName)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var reportFields = groupedFieldNames.Count == 0
|
||||
? visibleFields
|
||||
: visibleFields
|
||||
.Where(x => !groupedFieldNames.Contains(x.FieldName))
|
||||
.ToList();
|
||||
if (reportFields.Count == 0)
|
||||
{
|
||||
reportFields = visibleFields;
|
||||
}
|
||||
|
||||
var reportColumns = visibleFields
|
||||
.Select(field =>
|
||||
{
|
||||
var selectField = selectFieldsByName[field.FieldName];
|
||||
editorTypesByField.TryGetValue(field.FieldName, out var editorType);
|
||||
return CreateReportColumn(
|
||||
field,
|
||||
selectField,
|
||||
defaultValueHelper,
|
||||
editorType,
|
||||
DynamicReportImageHelper.IsImageEditor(editorType),
|
||||
editorType == PlatformConsts.EditorTypes.dxHtmlEditor);
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
||||
var groupColumns = CreateReportColumns(
|
||||
groupedFields,
|
||||
selectFieldsByName,
|
||||
editorTypesByField,
|
||||
defaultValueHelper);
|
||||
var reportColumns = CreateReportColumns(
|
||||
reportFields,
|
||||
selectFieldsByName,
|
||||
editorTypesByField,
|
||||
defaultValueHelper);
|
||||
|
||||
var allColumns = groupColumns
|
||||
.Concat(reportColumns)
|
||||
.GroupBy(x => x.BindingName, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(x => x.First())
|
||||
.ToList();
|
||||
|
||||
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
||||
|
|
@ -145,7 +163,7 @@ public class DynamicGridReport : XtraReport
|
|||
dataSourceType));
|
||||
selectQuery = DynamicReportSqlBuilder.ApplyLookupTextColumns(
|
||||
selectQuery,
|
||||
reportColumns,
|
||||
allColumns,
|
||||
defaultValueHelper,
|
||||
dataSourceType);
|
||||
var imageBaseUrl = DynamicReportImageHelper.GetImageBaseUrl(configuration);
|
||||
|
|
@ -161,10 +179,33 @@ public class DynamicGridReport : XtraReport
|
|||
selectQuery,
|
||||
connectionString,
|
||||
dataSourceType);
|
||||
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
|
||||
BuildLayout(listForm, reportColumns, groupColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
|
||||
RequestParameters = false;
|
||||
}
|
||||
|
||||
private static List<ReportColumn> CreateReportColumns(
|
||||
IEnumerable<ListFormField> fields,
|
||||
IReadOnlyDictionary<string, SelectField> selectFieldsByName,
|
||||
IReadOnlyDictionary<string, string> editorTypesByField,
|
||||
DefaultValueHelper defaultValueHelper)
|
||||
{
|
||||
return fields
|
||||
.Select(field =>
|
||||
{
|
||||
var selectField = selectFieldsByName[field.FieldName];
|
||||
editorTypesByField.TryGetValue(field.FieldName, out var editorType);
|
||||
return CreateReportColumn(
|
||||
field,
|
||||
selectField,
|
||||
defaultValueHelper,
|
||||
editorType,
|
||||
DynamicReportImageHelper.IsImageEditor(editorType),
|
||||
editorType == PlatformConsts.EditorTypes.dxHtmlEditor);
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields, bool isSubReport)
|
||||
{
|
||||
|
|
@ -174,7 +215,7 @@ public class DynamicGridReport : XtraReport
|
|||
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
|
||||
Landscape = !isSubReport &&
|
||||
(visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth);
|
||||
Font = new DXFont("Arial", 8.25F);
|
||||
Font = new DXFont("Arial", 8F);
|
||||
}
|
||||
|
||||
private static System.Drawing.Printing.Margins CreateReportMargins(
|
||||
|
|
@ -248,6 +289,7 @@ public class DynamicGridReport : XtraReport
|
|||
private void BuildLayout(
|
||||
ListForm listForm,
|
||||
IReadOnlyList<ReportColumn> fields,
|
||||
IReadOnlyList<ReportColumn> groupFields,
|
||||
IStringLocalizer<PlatformResource> localizer,
|
||||
string imageBaseUrl,
|
||||
string currentUserName,
|
||||
|
|
@ -256,6 +298,7 @@ public class DynamicGridReport : XtraReport
|
|||
{
|
||||
Bands.Clear();
|
||||
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
||||
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand
|
||||
|
|
@ -270,10 +313,15 @@ public class DynamicGridReport : XtraReport
|
|||
: 24;
|
||||
var detail = new DetailBand { HeightF = detailHeight };
|
||||
var reportFooter = new ReportFooterBand { HeightF = HasAnySummary(fields) ? 28 : 0 };
|
||||
var groupHeaderBands = CreateGroupHeaderBands(groupFields, localizer, pageWidth);
|
||||
|
||||
Bands.AddRange([topMargin, reportHeader, pageHeader, detail, reportFooter, bottomMargin]);
|
||||
Bands.AddRange([topMargin, reportHeader, pageHeader]);
|
||||
foreach (var groupHeaderBand in groupHeaderBands.AsEnumerable().Reverse())
|
||||
{
|
||||
Bands.Add(groupHeaderBand);
|
||||
}
|
||||
Bands.AddRange([detail, reportFooter, bottomMargin]);
|
||||
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
reportHeader.Controls.Add(new XRLabel
|
||||
{
|
||||
Text = DynamicReportImageHelper.GetLocalizedReportTitle(listForm, localizer),
|
||||
|
|
@ -302,21 +350,24 @@ public class DynamicGridReport : XtraReport
|
|||
};
|
||||
bottomMargin.Controls.Add(pageInfo);
|
||||
|
||||
var widths = CalculateWidths(fields, pageWidth);
|
||||
var detailIndent = GetGroupDetailIndent(groupFields.Count, pageWidth);
|
||||
var tableWidth = pageWidth - detailIndent;
|
||||
var detailWidths = CalculateWidths(fields, tableWidth);
|
||||
var headerWidths = CreateIndentedHeaderWidths(detailWidths, detailIndent);
|
||||
var headerTable = CreateTable(pageWidth, 28);
|
||||
var headerRow = new XRTableRow { HeightF = 28 };
|
||||
var detailTable = CreateTable(pageWidth, detailHeight);
|
||||
var detailTable = CreateTable(tableWidth, detailHeight, detailIndent);
|
||||
detailTable.CanGrow = true;
|
||||
var detailRow = new XRTableRow { HeightF = detailHeight, CanGrow = true };
|
||||
var footerTable = CreateTable(pageWidth, 24);
|
||||
var footerTable = CreateTable(tableWidth, 24, detailIndent);
|
||||
var footerRow = new XRTableRow { HeightF = 24 };
|
||||
|
||||
for (var i = 0; i < fields.Count; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
headerRow.Cells.Add(CreateHeaderCell(field, widths[i], localizer));
|
||||
detailRow.Cells.Add(CreateDetailCell(field, widths[i], imageBaseUrl, detailHeight));
|
||||
footerRow.Cells.Add(CreateFooterCell(field, widths[i]));
|
||||
headerRow.Cells.Add(CreateHeaderCell(field, headerWidths[i], localizer));
|
||||
detailRow.Cells.Add(CreateDetailCell(field, detailWidths[i], imageBaseUrl, detailHeight));
|
||||
footerRow.Cells.Add(CreateFooterCell(field, detailWidths[i]));
|
||||
}
|
||||
|
||||
headerTable.Rows.Add(headerRow);
|
||||
|
|
@ -331,11 +382,99 @@ public class DynamicGridReport : XtraReport
|
|||
}
|
||||
}
|
||||
|
||||
private static XRTable CreateTable(float width, float height)
|
||||
private static List<GroupHeaderBand> CreateGroupHeaderBands(
|
||||
IReadOnlyList<ReportColumn> groupFields,
|
||||
IStringLocalizer<PlatformResource> localizer,
|
||||
float pageWidth)
|
||||
{
|
||||
var bands = new List<GroupHeaderBand>();
|
||||
for (var i = 0; i < groupFields.Count; i++)
|
||||
{
|
||||
var groupField = groupFields[i];
|
||||
var level = groupFields.Count - i - 1;
|
||||
var band = new GroupHeaderBand
|
||||
{
|
||||
HeightF = 26,
|
||||
KeepTogether = true,
|
||||
RepeatEveryPage = true,
|
||||
Level = level,
|
||||
BackColor = System.Drawing.Color.FromArgb(248, 250, 252)
|
||||
};
|
||||
band.GroupFields.Add(new GroupField(groupField.BindingName, XRColumnSortOrder.Ascending));
|
||||
|
||||
var indent = GetGroupHeaderIndent(i, pageWidth);
|
||||
var caption = DynamicReportImageHelper.GetLocalizedFieldCaption(groupField.Field, localizer);
|
||||
var availableWidth = pageWidth - indent;
|
||||
var captionWidth = Math.Clamp(caption.Length * 6F + 28F, 90F, Math.Min(220F, availableWidth / 2));
|
||||
|
||||
var captionLabel = new XRLabel
|
||||
{
|
||||
Text = $"{caption}:",
|
||||
BoundsF = new System.Drawing.RectangleF(indent, 3, captionWidth, 20),
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
Padding = new PaddingInfo(6, 2, 0, 0),
|
||||
TextAlignment = TextAlignment.MiddleLeft,
|
||||
BackColor = System.Drawing.Color.FromArgb(226, 232, 240),
|
||||
Borders = BorderSide.Left | BorderSide.Top | BorderSide.Bottom,
|
||||
BorderColor = TableBorderColor,
|
||||
BorderWidth = 0.25F
|
||||
};
|
||||
var valueLabel = new XRLabel
|
||||
{
|
||||
BoundsF = new System.Drawing.RectangleF(indent + captionWidth, 3, availableWidth - captionWidth, 20),
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
Padding = new PaddingInfo(6, 6, 0, 0),
|
||||
TextAlignment = TextAlignment.MiddleLeft,
|
||||
BackColor = System.Drawing.Color.FromArgb(241, 245, 249),
|
||||
Borders = BorderSide.All,
|
||||
BorderColor = TableBorderColor,
|
||||
BorderWidth = 0.25F,
|
||||
CanGrow = true,
|
||||
Multiline = true
|
||||
};
|
||||
valueLabel.ExpressionBindings.Add(new ExpressionBinding(
|
||||
"BeforePrint",
|
||||
"Text",
|
||||
DynamicReportImageHelper.CreateFormatValueExpression(
|
||||
$"[{groupField.BindingName}]",
|
||||
groupField.Field,
|
||||
groupField.EditorType)));
|
||||
|
||||
band.Controls.AddRange([captionLabel, valueLabel]);
|
||||
bands.Add(band);
|
||||
}
|
||||
|
||||
return bands;
|
||||
}
|
||||
|
||||
private static float GetGroupHeaderIndent(int groupIndex, float pageWidth)
|
||||
{
|
||||
return Math.Min(groupIndex * GroupIndentWidth, pageWidth * 0.25F);
|
||||
}
|
||||
|
||||
private static float GetGroupDetailIndent(int groupCount, float pageWidth)
|
||||
{
|
||||
return Math.Min(groupCount * GroupIndentWidth, pageWidth * 0.25F);
|
||||
}
|
||||
|
||||
private static float[] CreateIndentedHeaderWidths(float[] detailWidths, float detailIndent)
|
||||
{
|
||||
if (detailWidths.Length == 0 || detailIndent <= 0)
|
||||
{
|
||||
return detailWidths;
|
||||
}
|
||||
|
||||
var headerWidths = detailWidths.ToArray();
|
||||
headerWidths[0] += detailIndent;
|
||||
|
||||
return headerWidths;
|
||||
}
|
||||
|
||||
private static XRTable CreateTable(float width, float height, float left = 0)
|
||||
{
|
||||
return new XRTable
|
||||
{
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, width, height),
|
||||
BoundsF = new System.Drawing.RectangleF(left, 0, width, height),
|
||||
Borders = BorderSide.All,
|
||||
BorderColor = TableBorderColor,
|
||||
BorderWidth = 0.25F
|
||||
|
|
@ -387,7 +526,7 @@ public class DynamicGridReport : XtraReport
|
|||
{
|
||||
WidthF = width,
|
||||
Weight = width,
|
||||
Font = new DXFont("Segoe UI Emoji", 8F),
|
||||
Font = new DXFont("Arial", 8F),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = DynamicReportImageHelper.ToTextAlignment(field.Alignment, field.SourceDbType),
|
||||
Multiline = true,
|
||||
|
|
@ -475,18 +614,6 @@ public class DynamicGridReport : XtraReport
|
|||
};
|
||||
}
|
||||
|
||||
private static float[] CalculateWidths(IReadOnlyList<ListFormField> fields, float pageWidth)
|
||||
{
|
||||
var raw = fields.Select(x => (float)Math.Max(x.Width ?? 90, 60)).ToArray();
|
||||
var total = raw.Sum();
|
||||
if (total <= 0)
|
||||
{
|
||||
return fields.Select(_ => pageWidth / fields.Count).ToArray();
|
||||
}
|
||||
|
||||
return raw.Select(x => pageWidth * x / total).ToArray();
|
||||
}
|
||||
|
||||
private static float[] CalculateWidths(IReadOnlyList<ReportColumn> fields, float pageWidth)
|
||||
{
|
||||
var raw = fields.Select(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field)).ToArray();
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public partial class DynamicReport : XtraReport
|
|||
HorizontalMargin,
|
||||
VerticalMargin,
|
||||
VerticalMargin);
|
||||
Font = new DXFont("Arial", 8.25F);
|
||||
Font = new DXFont("Arial", 8F);
|
||||
|
||||
TopMargin.HeightF = Margins.Top;
|
||||
BottomMargin.HeightF = Margins.Bottom;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Sozsoft.Platform;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
|
|
@ -32,6 +34,51 @@ internal static class DynamicReportGridStateHelper
|
|||
.ToList();
|
||||
}
|
||||
|
||||
public static List<ListFormField> GetGroupedFields(
|
||||
IReadOnlyList<ListFormField> availableFields,
|
||||
string gridStateJson,
|
||||
string groupParameter)
|
||||
{
|
||||
var groupedFields = new List<ListFormField>();
|
||||
var groupedFieldNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var field in GetGroupFieldsFromParameter(availableFields, groupParameter))
|
||||
{
|
||||
AddGroupedField(field);
|
||||
}
|
||||
|
||||
if (TryGetColumnStates(gridStateJson, out var statesByField))
|
||||
{
|
||||
foreach (var field in availableFields.Where(field =>
|
||||
statesByField.TryGetValue(field.FieldName, out var state) &&
|
||||
state.GroupIndex.HasValue)
|
||||
.OrderBy(field => statesByField[field.FieldName].GroupIndex)
|
||||
.ThenBy(field => statesByField[field.FieldName].VisibleIndex))
|
||||
{
|
||||
AddGroupedField(field);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var field in availableFields
|
||||
.Select(field => new { Field = field, GroupIndex = GetConfiguredGroupIndex(field) })
|
||||
.Where(x => x.GroupIndex.HasValue)
|
||||
.OrderBy(x => x.GroupIndex)
|
||||
.Select(x => x.Field))
|
||||
{
|
||||
AddGroupedField(field);
|
||||
}
|
||||
|
||||
return groupedFields;
|
||||
|
||||
void AddGroupedField(ListFormField field)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(field?.FieldName) && groupedFieldNames.Add(field.FieldName))
|
||||
{
|
||||
groupedFields.Add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetStateStorageKey(ListForm listForm)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(listForm.StateStoringJson))
|
||||
|
|
@ -100,10 +147,14 @@ internal static class DynamicReportGridStateHelper
|
|||
visibleIndexProperty.TryGetInt32(out var value)
|
||||
? value
|
||||
: states.Count;
|
||||
var groupIndex = TryGetProperty(column, "groupIndex", out var groupIndexProperty) &&
|
||||
groupIndexProperty.TryGetInt32(out var groupValue)
|
||||
? groupValue
|
||||
: (int?)null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fieldName))
|
||||
{
|
||||
states.Add(new GridColumnState(fieldName, visible, visibleIndex));
|
||||
states.Add(new GridColumnState(fieldName, visible, visibleIndex, groupIndex));
|
||||
}
|
||||
|
||||
if (TryGetProperty(column, "columns", out var childColumns) &&
|
||||
|
|
@ -131,6 +182,55 @@ internal static class DynamicReportGridStateHelper
|
|||
return null;
|
||||
}
|
||||
|
||||
private static List<ListFormField> GetGroupFieldsFromParameter(
|
||||
IReadOnlyList<ListFormField> availableFields,
|
||||
string groupParameter)
|
||||
{
|
||||
var result = new List<ListFormField>();
|
||||
if (string.IsNullOrWhiteSpace(groupParameter))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var fieldsByName = availableFields
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName))
|
||||
.ToDictionary(x => x.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var group in groupParameter.Split(
|
||||
PlatformConsts.MultiValueDelimiter,
|
||||
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
var parts = group.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
if (parts.Length > 0 && fieldsByName.TryGetValue(parts[0], out var field))
|
||||
{
|
||||
result.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int? GetConfiguredGroupIndex(ListFormField field)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(field.GroupingJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var grouping = JsonSerializer.Deserialize<ColumnGroupingDto>(field.GroupingJson, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
return grouping?.GroupIndex;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetProperty(JsonElement element, string name, out JsonElement value)
|
||||
{
|
||||
if (element.ValueKind == JsonValueKind.Object)
|
||||
|
|
@ -149,5 +249,5 @@ internal static class DynamicReportGridStateHelper
|
|||
return false;
|
||||
}
|
||||
|
||||
private sealed record GridColumnState(string FieldName, bool Visible, int VisibleIndex);
|
||||
private sealed record GridColumnState(string FieldName, bool Visible, int VisibleIndex, int? GroupIndex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
|
@ -52,7 +53,7 @@ internal static class DynamicReportSqlBuilder
|
|||
|
||||
var lookupSelectParts = columns
|
||||
.Select(column => CreateLookupSelectPart(column, defaultValueHelper, dataSourceType))
|
||||
.Where(part => !string.IsNullOrWhiteSpace(part))
|
||||
.Where(part => part != null && !string.IsNullOrWhiteSpace(part.SelectSql))
|
||||
.ToList();
|
||||
|
||||
if (lookupSelectParts.Count == 0)
|
||||
|
|
@ -68,10 +69,19 @@ internal static class DynamicReportSqlBuilder
|
|||
normalizedSql = OrderByRegex.Replace(normalizedSql, string.Empty).Trim();
|
||||
}
|
||||
|
||||
return $"SELECT {sourceAlias}.*, {string.Join(", ", lookupSelectParts)} FROM ({normalizedSql}) AS {sourceAlias}{orderBy}";
|
||||
var commonTableExpressions = lookupSelectParts
|
||||
.Select(part => part.CommonTableExpression)
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
var withClause = commonTableExpressions.Count > 0
|
||||
? $"WITH {string.Join(", ", commonTableExpressions)} "
|
||||
: string.Empty;
|
||||
|
||||
return $"{withClause}SELECT {sourceAlias}.*, {string.Join(", ", lookupSelectParts.Select(x => x.SelectSql))} FROM ({normalizedSql}) AS {sourceAlias}{orderBy}";
|
||||
}
|
||||
|
||||
private static string CreateLookupSelectPart(
|
||||
private static LookupSelectPart CreateLookupSelectPart(
|
||||
IReportLookupColumn column,
|
||||
DefaultValueHelper defaultValueHelper,
|
||||
DataSourceTypeEnum dataSourceType)
|
||||
|
|
@ -89,7 +99,7 @@ internal static class DynamicReportSqlBuilder
|
|||
return null;
|
||||
}
|
||||
|
||||
private static string CreateStaticLookupSelectPart(
|
||||
private static LookupSelectPart CreateStaticLookupSelectPart(
|
||||
IReportLookupColumn column,
|
||||
DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
|
|
@ -102,10 +112,11 @@ internal static class DynamicReportSqlBuilder
|
|||
var cases = column.StaticLookupValues
|
||||
.Select(item => $"WHEN {DynamicReportImageHelper.ToSqlLiteral(item.Key, dataSourceType)} THEN {DynamicReportImageHelper.ToSqlLiteral(item.Value, dataSourceType)}");
|
||||
|
||||
return $"CASE {sourceField} {string.Join(" ", cases)} ELSE CAST({sourceField} AS VARCHAR(4000)) END AS {QuoteSqlIdentifier(column.BindingName)}";
|
||||
return new LookupSelectPart(
|
||||
$"CASE {sourceField} {string.Join(" ", cases)} ELSE CAST({sourceField} AS VARCHAR(4000)) END AS {QuoteSqlIdentifier(column.BindingName)}");
|
||||
}
|
||||
|
||||
private static string CreateQueryLookupSelectPart(
|
||||
private static LookupSelectPart CreateQueryLookupSelectPart(
|
||||
IReportLookupColumn column,
|
||||
DefaultValueHelper defaultValueHelper,
|
||||
DataSourceTypeEnum dataSourceType)
|
||||
|
|
@ -121,6 +132,7 @@ internal static class DynamicReportSqlBuilder
|
|||
return CreateQueryMultiLookupSelectPart(column, lookupQuery, dataSourceType);
|
||||
}
|
||||
|
||||
var (commonTableExpression, lookupSelectQuery) = SplitCommonTableExpression(lookupQuery);
|
||||
var lookupAlias = QuoteSqlIdentifier(LookupAlias);
|
||||
var valueExpr = QuoteSqlIdentifier(column.Lookup.ValueExpr ?? "Key");
|
||||
var displayExpr = QuoteSqlIdentifier(column.Lookup.DisplayExpr ?? "Name");
|
||||
|
|
@ -128,13 +140,13 @@ internal static class DynamicReportSqlBuilder
|
|||
var resultAlias = QuoteSqlIdentifier(column.BindingName);
|
||||
|
||||
var scalarQuery = dataSourceType == DataSourceTypeEnum.Mssql
|
||||
? $"SELECT TOP 1 {lookupAlias}.{displayExpr} FROM ({lookupQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField}"
|
||||
: $"SELECT {lookupAlias}.{displayExpr} FROM ({lookupQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField} LIMIT 1";
|
||||
? $"SELECT TOP 1 {lookupAlias}.{displayExpr} FROM ({lookupSelectQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField}"
|
||||
: $"SELECT {lookupAlias}.{displayExpr} FROM ({lookupSelectQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField} LIMIT 1";
|
||||
|
||||
return $"({scalarQuery}) AS {resultAlias}";
|
||||
return new LookupSelectPart($"({scalarQuery}) AS {resultAlias}", commonTableExpression);
|
||||
}
|
||||
|
||||
private static string CreateStaticMultiLookupSelectPart(
|
||||
private static LookupSelectPart CreateStaticMultiLookupSelectPart(
|
||||
IReportLookupColumn column,
|
||||
DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
|
|
@ -147,27 +159,28 @@ internal static class DynamicReportSqlBuilder
|
|||
|
||||
if (dataSourceType == DataSourceTypeEnum.Mssql)
|
||||
{
|
||||
return $"""
|
||||
return new LookupSelectPart($"""
|
||||
(SELECT STRING_AGG(CAST({LookupAliasForValues()}.DisplayValue AS NVARCHAR(MAX)), N', ')
|
||||
FROM (VALUES {values}) AS {LookupAliasForValues()}(ValueKey, DisplayValue)
|
||||
INNER JOIN STRING_SPLIT(CAST({sourceField} AS NVARCHAR(MAX)), N'{PlatformConsts.MultiValueDelimiter}') AS SplitValues
|
||||
ON CAST({LookupAliasForValues()}.ValueKey AS NVARCHAR(4000)) = LTRIM(RTRIM(SplitValues.value))) AS {resultAlias}
|
||||
""".ReplaceLineEndings(" ").Trim();
|
||||
""".ReplaceLineEndings(" ").Trim());
|
||||
}
|
||||
|
||||
return $"""
|
||||
return new LookupSelectPart($"""
|
||||
(SELECT string_agg(CAST({LookupAliasForValues()}."DisplayValue" AS TEXT), ', ')
|
||||
FROM (VALUES {values}) AS {LookupAliasForValues()}("ValueKey", "DisplayValue")
|
||||
INNER JOIN unnest(string_to_array(CAST({sourceField} AS TEXT), '{PlatformConsts.MultiValueDelimiter}')) AS SplitValues(value)
|
||||
ON CAST({LookupAliasForValues()}."ValueKey" AS TEXT) = btrim(SplitValues.value)) AS {resultAlias}
|
||||
""".ReplaceLineEndings(" ").Trim();
|
||||
""".ReplaceLineEndings(" ").Trim());
|
||||
}
|
||||
|
||||
private static string CreateQueryMultiLookupSelectPart(
|
||||
private static LookupSelectPart CreateQueryMultiLookupSelectPart(
|
||||
IReportLookupColumn column,
|
||||
string lookupQuery,
|
||||
DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
var (commonTableExpression, lookupSelectQuery) = SplitCommonTableExpression(lookupQuery);
|
||||
var lookupAlias = QuoteSqlIdentifier(LookupAlias);
|
||||
var valueExpr = QuoteSqlIdentifier(column.Lookup.ValueExpr ?? "Key");
|
||||
var displayExpr = QuoteSqlIdentifier(column.Lookup.DisplayExpr ?? "Name");
|
||||
|
|
@ -177,18 +190,92 @@ internal static class DynamicReportSqlBuilder
|
|||
var scalarQuery = dataSourceType == DataSourceTypeEnum.Mssql
|
||||
? $"""
|
||||
SELECT STRING_AGG(CAST({lookupAlias}.{displayExpr} AS NVARCHAR(MAX)), N', ')
|
||||
FROM ({lookupQuery}) AS {lookupAlias}
|
||||
FROM ({lookupSelectQuery}) AS {lookupAlias}
|
||||
INNER JOIN STRING_SPLIT(CAST({sourceField} AS NVARCHAR(MAX)), N'{PlatformConsts.MultiValueDelimiter}') AS SplitValues
|
||||
ON CAST({lookupAlias}.{valueExpr} AS NVARCHAR(4000)) = LTRIM(RTRIM(SplitValues.value))
|
||||
""".ReplaceLineEndings(" ").Trim()
|
||||
: $"""
|
||||
SELECT string_agg(CAST({lookupAlias}.{displayExpr} AS TEXT), ', ')
|
||||
FROM ({lookupQuery}) AS {lookupAlias}
|
||||
FROM ({lookupSelectQuery}) AS {lookupAlias}
|
||||
INNER JOIN unnest(string_to_array(CAST({sourceField} AS TEXT), '{PlatformConsts.MultiValueDelimiter}')) AS SplitValues(value)
|
||||
ON CAST({lookupAlias}.{valueExpr} AS TEXT) = btrim(SplitValues.value)
|
||||
""".ReplaceLineEndings(" ").Trim();
|
||||
|
||||
return $"({scalarQuery}) AS {resultAlias}";
|
||||
return new LookupSelectPart($"({scalarQuery}) AS {resultAlias}", commonTableExpression);
|
||||
}
|
||||
|
||||
private static (string CommonTableExpression, string SelectQuery) SplitCommonTableExpression(string query)
|
||||
{
|
||||
var normalized = query.Trim().TrimStart(';').TrimStart();
|
||||
if (!normalized.StartsWith("WITH ", System.StringComparison.OrdinalIgnoreCase) &&
|
||||
!normalized.StartsWith("WITH\t", System.StringComparison.OrdinalIgnoreCase) &&
|
||||
!normalized.StartsWith("WITH\r", System.StringComparison.OrdinalIgnoreCase) &&
|
||||
!normalized.StartsWith("WITH\n", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return (null, normalized);
|
||||
}
|
||||
|
||||
var depth = 0;
|
||||
var quote = '\0';
|
||||
for (var index = 4; index < normalized.Length; index++)
|
||||
{
|
||||
var current = normalized[index];
|
||||
if (quote != '\0')
|
||||
{
|
||||
if (current == quote)
|
||||
{
|
||||
if (index + 1 < normalized.Length && normalized[index + 1] == quote)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
quote = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current is '\'' or '"')
|
||||
{
|
||||
quote = current;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current == '(')
|
||||
{
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current == ')')
|
||||
{
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (depth == 0 && IsKeywordAt(normalized, index, "SELECT"))
|
||||
{
|
||||
return (normalized[4..index].Trim(), normalized[index..].Trim());
|
||||
}
|
||||
}
|
||||
|
||||
return (null, normalized);
|
||||
}
|
||||
|
||||
private static bool IsKeywordAt(string value, int index, string keyword)
|
||||
{
|
||||
if (index + keyword.Length > value.Length ||
|
||||
!value.AsSpan(index, keyword.Length).Equals(keyword, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasBoundaryBefore = index == 0 || !char.IsLetterOrDigit(value[index - 1]) && value[index - 1] != '_';
|
||||
var after = index + keyword.Length;
|
||||
var hasBoundaryAfter = after == value.Length || !char.IsLetterOrDigit(value[after]) && value[after] != '_';
|
||||
return hasBoundaryBefore && hasBoundaryAfter;
|
||||
}
|
||||
|
||||
private static string LookupAliasForValues() => "LookupValues";
|
||||
|
|
@ -227,4 +314,6 @@ internal static class DynamicReportSqlBuilder
|
|||
return $"\"{identifier.Replace("\"", "\"\"")}\"";
|
||||
}
|
||||
|
||||
private sealed record LookupSelectPart(string SelectSql, string CommonTableExpression = null);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ public class DynamicTreeReport : XtraReport
|
|||
var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
|
||||
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
|
||||
Landscape = visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth;
|
||||
Font = new DXFont("Arial", 8.25F);
|
||||
Font = new DXFont("Arial", 8F);
|
||||
}
|
||||
|
||||
private static System.Drawing.Printing.Margins CreateReportMargins(
|
||||
|
|
@ -550,7 +550,7 @@ public class DynamicTreeReport : XtraReport
|
|||
{
|
||||
WidthF = width,
|
||||
Weight = width,
|
||||
Font = new DXFont("Segoe UI Emoji", 8F),
|
||||
Font = new DXFont("Arial", 8F),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = isTreeColumn
|
||||
? TextAlignment.MiddleLeft
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
{
|
||||
"commit": "771ea95",
|
||||
"commit": "e7670c6",
|
||||
"releases": [
|
||||
{
|
||||
"version": "1.1.05",
|
||||
"buildDate": "2026-06-27",
|
||||
"commit": "352cf047b0af1b8aad53b632a41161efcc6cecdf",
|
||||
"changeLog": [
|
||||
"- Ai için Ollama kurulumu yapıldı. Ayrıca Gemma LLM ile çalıştırıldı.",
|
||||
"- DynamicReport eklendi. ReportDesigner ile çalışan bir rapor tasarlandı.",
|
||||
"- DynamicGridReport eklendi. Grid üzerinde otomatik rapor alınabiliyor.",
|
||||
"- DynamicTreeReport eklendi. Tree üzerinden otomatik rapor alınabiliyor.",
|
||||
"- DynamicFormReport eklendi. Single ekranlarda otomatik rapor eklendi.",
|
||||
"- Demo sayfası menüden ayrıldı."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.04",
|
||||
"buildDate": "2026-06-04",
|
||||
|
|
|
|||
Loading…
Reference in a new issue