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]);
|
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);
|
var lookupQuery = defaultValueHelper.GetDefaultValue(lookup.LookupQuery);
|
||||||
return await dynamicDataRepository.QueryAsync(lookupQuery, connectionString, parameters);
|
return await dynamicDataRepository.QueryAsync(lookupQuery, connectionString, parameters);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ public class DynamicFormReport : XtraReport
|
||||||
Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
|
Margins = CreateReportMargins(HorizontalMargin, HorizontalMargin, 35, 35);
|
||||||
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
|
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
|
||||||
Landscape = false;
|
Landscape = false;
|
||||||
Font = new DXFont("Arial", 8.25F);
|
Font = new DXFont("Arial", 8F);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CA1416
|
#pragma warning disable CA1416
|
||||||
|
|
@ -206,7 +206,7 @@ public class DynamicFormReport : XtraReport
|
||||||
{
|
{
|
||||||
Text = DynamicReportImageHelper.Localize(localizer, group.Caption, group.Caption),
|
Text = DynamicReportImageHelper.Localize(localizer, group.Caption, group.Caption),
|
||||||
BoundsF = new System.Drawing.RectangleF(0, y, pageWidth, 22),
|
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),
|
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
|
||||||
Padding = new PaddingInfo(5, 5, 2, 2),
|
Padding = new PaddingInfo(5, 5, 2, 2),
|
||||||
TextAlignment = TextAlignment.MiddleLeft
|
TextAlignment = TextAlignment.MiddleLeft
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ public class DynamicGridReport : XtraReport
|
||||||
private const int A4PortraitWidth = 827;
|
private const int A4PortraitWidth = 827;
|
||||||
private const int HorizontalMargin = 50;
|
private const int HorizontalMargin = 50;
|
||||||
private const int LandscapeColumnThreshold = 5;
|
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 readonly System.Drawing.Color TableBorderColor = System.Drawing.Color.FromArgb(226, 232, 240);
|
||||||
private static IServiceScopeFactory _scopeFactory;
|
private static IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
|
@ -122,21 +123,38 @@ public class DynamicGridReport : XtraReport
|
||||||
.Take(8)
|
.Take(8)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
var groupedFields = DynamicReportGridStateHelper.GetGroupedFields(
|
||||||
var reportColumns = visibleFields
|
availableFields,
|
||||||
.Select(field =>
|
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)
|
||||||
{
|
{
|
||||||
var selectField = selectFieldsByName[field.FieldName];
|
reportFields = visibleFields;
|
||||||
editorTypesByField.TryGetValue(field.FieldName, out var editorType);
|
}
|
||||||
return CreateReportColumn(
|
|
||||||
field,
|
var groupColumns = CreateReportColumns(
|
||||||
selectField,
|
groupedFields,
|
||||||
defaultValueHelper,
|
selectFieldsByName,
|
||||||
editorType,
|
editorTypesByField,
|
||||||
DynamicReportImageHelper.IsImageEditor(editorType),
|
defaultValueHelper);
|
||||||
editorType == PlatformConsts.EditorTypes.dxHtmlEditor);
|
var reportColumns = CreateReportColumns(
|
||||||
})
|
reportFields,
|
||||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
selectFieldsByName,
|
||||||
|
editorTypesByField,
|
||||||
|
defaultValueHelper);
|
||||||
|
|
||||||
|
var allColumns = groupColumns
|
||||||
|
.Concat(reportColumns)
|
||||||
|
.GroupBy(x => x.BindingName, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(x => x.First())
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
||||||
|
|
@ -145,7 +163,7 @@ public class DynamicGridReport : XtraReport
|
||||||
dataSourceType));
|
dataSourceType));
|
||||||
selectQuery = DynamicReportSqlBuilder.ApplyLookupTextColumns(
|
selectQuery = DynamicReportSqlBuilder.ApplyLookupTextColumns(
|
||||||
selectQuery,
|
selectQuery,
|
||||||
reportColumns,
|
allColumns,
|
||||||
defaultValueHelper,
|
defaultValueHelper,
|
||||||
dataSourceType);
|
dataSourceType);
|
||||||
var imageBaseUrl = DynamicReportImageHelper.GetImageBaseUrl(configuration);
|
var imageBaseUrl = DynamicReportImageHelper.GetImageBaseUrl(configuration);
|
||||||
|
|
@ -161,10 +179,33 @@ public class DynamicGridReport : XtraReport
|
||||||
selectQuery,
|
selectQuery,
|
||||||
connectionString,
|
connectionString,
|
||||||
dataSourceType);
|
dataSourceType);
|
||||||
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
|
BuildLayout(listForm, reportColumns, groupColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
|
||||||
RequestParameters = false;
|
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
|
#pragma warning disable CA1416
|
||||||
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields, bool isSubReport)
|
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));
|
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
|
||||||
Landscape = !isSubReport &&
|
Landscape = !isSubReport &&
|
||||||
(visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth);
|
(visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth);
|
||||||
Font = new DXFont("Arial", 8.25F);
|
Font = new DXFont("Arial", 8F);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static System.Drawing.Printing.Margins CreateReportMargins(
|
private static System.Drawing.Printing.Margins CreateReportMargins(
|
||||||
|
|
@ -248,6 +289,7 @@ public class DynamicGridReport : XtraReport
|
||||||
private void BuildLayout(
|
private void BuildLayout(
|
||||||
ListForm listForm,
|
ListForm listForm,
|
||||||
IReadOnlyList<ReportColumn> fields,
|
IReadOnlyList<ReportColumn> fields,
|
||||||
|
IReadOnlyList<ReportColumn> groupFields,
|
||||||
IStringLocalizer<PlatformResource> localizer,
|
IStringLocalizer<PlatformResource> localizer,
|
||||||
string imageBaseUrl,
|
string imageBaseUrl,
|
||||||
string currentUserName,
|
string currentUserName,
|
||||||
|
|
@ -256,6 +298,7 @@ public class DynamicGridReport : XtraReport
|
||||||
{
|
{
|
||||||
Bands.Clear();
|
Bands.Clear();
|
||||||
|
|
||||||
|
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||||
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
||||||
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
||||||
var reportHeader = new ReportHeaderBand
|
var reportHeader = new ReportHeaderBand
|
||||||
|
|
@ -270,10 +313,15 @@ public class DynamicGridReport : XtraReport
|
||||||
: 24;
|
: 24;
|
||||||
var detail = new DetailBand { HeightF = detailHeight };
|
var detail = new DetailBand { HeightF = detailHeight };
|
||||||
var reportFooter = new ReportFooterBand { HeightF = HasAnySummary(fields) ? 28 : 0 };
|
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
|
reportHeader.Controls.Add(new XRLabel
|
||||||
{
|
{
|
||||||
Text = DynamicReportImageHelper.GetLocalizedReportTitle(listForm, localizer),
|
Text = DynamicReportImageHelper.GetLocalizedReportTitle(listForm, localizer),
|
||||||
|
|
@ -302,21 +350,24 @@ public class DynamicGridReport : XtraReport
|
||||||
};
|
};
|
||||||
bottomMargin.Controls.Add(pageInfo);
|
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 headerTable = CreateTable(pageWidth, 28);
|
||||||
var headerRow = new XRTableRow { HeightF = 28 };
|
var headerRow = new XRTableRow { HeightF = 28 };
|
||||||
var detailTable = CreateTable(pageWidth, detailHeight);
|
var detailTable = CreateTable(tableWidth, detailHeight, detailIndent);
|
||||||
detailTable.CanGrow = true;
|
detailTable.CanGrow = true;
|
||||||
var detailRow = new XRTableRow { HeightF = detailHeight, 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 };
|
var footerRow = new XRTableRow { HeightF = 24 };
|
||||||
|
|
||||||
for (var i = 0; i < fields.Count; i++)
|
for (var i = 0; i < fields.Count; i++)
|
||||||
{
|
{
|
||||||
var field = fields[i];
|
var field = fields[i];
|
||||||
headerRow.Cells.Add(CreateHeaderCell(field, widths[i], localizer));
|
headerRow.Cells.Add(CreateHeaderCell(field, headerWidths[i], localizer));
|
||||||
detailRow.Cells.Add(CreateDetailCell(field, widths[i], imageBaseUrl, detailHeight));
|
detailRow.Cells.Add(CreateDetailCell(field, detailWidths[i], imageBaseUrl, detailHeight));
|
||||||
footerRow.Cells.Add(CreateFooterCell(field, widths[i]));
|
footerRow.Cells.Add(CreateFooterCell(field, detailWidths[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
headerTable.Rows.Add(headerRow);
|
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
|
return new XRTable
|
||||||
{
|
{
|
||||||
BoundsF = new System.Drawing.RectangleF(0, 0, width, height),
|
BoundsF = new System.Drawing.RectangleF(left, 0, width, height),
|
||||||
Borders = BorderSide.All,
|
Borders = BorderSide.All,
|
||||||
BorderColor = TableBorderColor,
|
BorderColor = TableBorderColor,
|
||||||
BorderWidth = 0.25F
|
BorderWidth = 0.25F
|
||||||
|
|
@ -387,7 +526,7 @@ public class DynamicGridReport : XtraReport
|
||||||
{
|
{
|
||||||
WidthF = width,
|
WidthF = width,
|
||||||
Weight = width,
|
Weight = width,
|
||||||
Font = new DXFont("Segoe UI Emoji", 8F),
|
Font = new DXFont("Arial", 8F),
|
||||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||||
TextAlignment = DynamicReportImageHelper.ToTextAlignment(field.Alignment, field.SourceDbType),
|
TextAlignment = DynamicReportImageHelper.ToTextAlignment(field.Alignment, field.SourceDbType),
|
||||||
Multiline = true,
|
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)
|
private static float[] CalculateWidths(IReadOnlyList<ReportColumn> fields, float pageWidth)
|
||||||
{
|
{
|
||||||
var raw = fields.Select(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field)).ToArray();
|
var raw = fields.Select(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field)).ToArray();
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public partial class DynamicReport : XtraReport
|
||||||
HorizontalMargin,
|
HorizontalMargin,
|
||||||
VerticalMargin,
|
VerticalMargin,
|
||||||
VerticalMargin);
|
VerticalMargin);
|
||||||
Font = new DXFont("Arial", 8.25F);
|
Font = new DXFont("Arial", 8F);
|
||||||
|
|
||||||
TopMargin.HeightF = Margins.Top;
|
TopMargin.HeightF = Margins.Top;
|
||||||
BottomMargin.HeightF = Margins.Bottom;
|
BottomMargin.HeightF = Margins.Bottom;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Sozsoft.Platform;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
|
using Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
namespace Sozsoft.Reports.PredefinedReports;
|
namespace Sozsoft.Reports.PredefinedReports;
|
||||||
|
|
||||||
|
|
@ -32,6 +34,51 @@ internal static class DynamicReportGridStateHelper
|
||||||
.ToList();
|
.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)
|
private static string GetStateStorageKey(ListForm listForm)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(listForm.StateStoringJson))
|
if (!string.IsNullOrWhiteSpace(listForm.StateStoringJson))
|
||||||
|
|
@ -100,10 +147,14 @@ internal static class DynamicReportGridStateHelper
|
||||||
visibleIndexProperty.TryGetInt32(out var value)
|
visibleIndexProperty.TryGetInt32(out var value)
|
||||||
? value
|
? value
|
||||||
: states.Count;
|
: states.Count;
|
||||||
|
var groupIndex = TryGetProperty(column, "groupIndex", out var groupIndexProperty) &&
|
||||||
|
groupIndexProperty.TryGetInt32(out var groupValue)
|
||||||
|
? groupValue
|
||||||
|
: (int?)null;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(fieldName))
|
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) &&
|
if (TryGetProperty(column, "columns", out var childColumns) &&
|
||||||
|
|
@ -131,6 +182,55 @@ internal static class DynamicReportGridStateHelper
|
||||||
return null;
|
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)
|
private static bool TryGetProperty(JsonElement element, string name, out JsonElement value)
|
||||||
{
|
{
|
||||||
if (element.ValueKind == JsonValueKind.Object)
|
if (element.ValueKind == JsonValueKind.Object)
|
||||||
|
|
@ -149,5 +249,5 @@ internal static class DynamicReportGridStateHelper
|
||||||
return false;
|
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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
@ -52,7 +53,7 @@ internal static class DynamicReportSqlBuilder
|
||||||
|
|
||||||
var lookupSelectParts = columns
|
var lookupSelectParts = columns
|
||||||
.Select(column => CreateLookupSelectPart(column, defaultValueHelper, dataSourceType))
|
.Select(column => CreateLookupSelectPart(column, defaultValueHelper, dataSourceType))
|
||||||
.Where(part => !string.IsNullOrWhiteSpace(part))
|
.Where(part => part != null && !string.IsNullOrWhiteSpace(part.SelectSql))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (lookupSelectParts.Count == 0)
|
if (lookupSelectParts.Count == 0)
|
||||||
|
|
@ -68,10 +69,19 @@ internal static class DynamicReportSqlBuilder
|
||||||
normalizedSql = OrderByRegex.Replace(normalizedSql, string.Empty).Trim();
|
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,
|
IReportLookupColumn column,
|
||||||
DefaultValueHelper defaultValueHelper,
|
DefaultValueHelper defaultValueHelper,
|
||||||
DataSourceTypeEnum dataSourceType)
|
DataSourceTypeEnum dataSourceType)
|
||||||
|
|
@ -89,7 +99,7 @@ internal static class DynamicReportSqlBuilder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CreateStaticLookupSelectPart(
|
private static LookupSelectPart CreateStaticLookupSelectPart(
|
||||||
IReportLookupColumn column,
|
IReportLookupColumn column,
|
||||||
DataSourceTypeEnum dataSourceType)
|
DataSourceTypeEnum dataSourceType)
|
||||||
{
|
{
|
||||||
|
|
@ -102,10 +112,11 @@ internal static class DynamicReportSqlBuilder
|
||||||
var cases = column.StaticLookupValues
|
var cases = column.StaticLookupValues
|
||||||
.Select(item => $"WHEN {DynamicReportImageHelper.ToSqlLiteral(item.Key, dataSourceType)} THEN {DynamicReportImageHelper.ToSqlLiteral(item.Value, dataSourceType)}");
|
.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,
|
IReportLookupColumn column,
|
||||||
DefaultValueHelper defaultValueHelper,
|
DefaultValueHelper defaultValueHelper,
|
||||||
DataSourceTypeEnum dataSourceType)
|
DataSourceTypeEnum dataSourceType)
|
||||||
|
|
@ -121,6 +132,7 @@ internal static class DynamicReportSqlBuilder
|
||||||
return CreateQueryMultiLookupSelectPart(column, lookupQuery, dataSourceType);
|
return CreateQueryMultiLookupSelectPart(column, lookupQuery, dataSourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (commonTableExpression, lookupSelectQuery) = SplitCommonTableExpression(lookupQuery);
|
||||||
var lookupAlias = QuoteSqlIdentifier(LookupAlias);
|
var lookupAlias = QuoteSqlIdentifier(LookupAlias);
|
||||||
var valueExpr = QuoteSqlIdentifier(column.Lookup.ValueExpr ?? "Key");
|
var valueExpr = QuoteSqlIdentifier(column.Lookup.ValueExpr ?? "Key");
|
||||||
var displayExpr = QuoteSqlIdentifier(column.Lookup.DisplayExpr ?? "Name");
|
var displayExpr = QuoteSqlIdentifier(column.Lookup.DisplayExpr ?? "Name");
|
||||||
|
|
@ -128,13 +140,13 @@ internal static class DynamicReportSqlBuilder
|
||||||
var resultAlias = QuoteSqlIdentifier(column.BindingName);
|
var resultAlias = QuoteSqlIdentifier(column.BindingName);
|
||||||
|
|
||||||
var scalarQuery = dataSourceType == DataSourceTypeEnum.Mssql
|
var scalarQuery = dataSourceType == DataSourceTypeEnum.Mssql
|
||||||
? $"SELECT TOP 1 {lookupAlias}.{displayExpr} FROM ({lookupQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField}"
|
? $"SELECT TOP 1 {lookupAlias}.{displayExpr} FROM ({lookupSelectQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField}"
|
||||||
: $"SELECT {lookupAlias}.{displayExpr} FROM ({lookupQuery}) AS {lookupAlias} WHERE {lookupAlias}.{valueExpr} = {sourceField} LIMIT 1";
|
: $"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,
|
IReportLookupColumn column,
|
||||||
DataSourceTypeEnum dataSourceType)
|
DataSourceTypeEnum dataSourceType)
|
||||||
{
|
{
|
||||||
|
|
@ -147,27 +159,28 @@ internal static class DynamicReportSqlBuilder
|
||||||
|
|
||||||
if (dataSourceType == DataSourceTypeEnum.Mssql)
|
if (dataSourceType == DataSourceTypeEnum.Mssql)
|
||||||
{
|
{
|
||||||
return $"""
|
return new LookupSelectPart($"""
|
||||||
(SELECT STRING_AGG(CAST({LookupAliasForValues()}.DisplayValue AS NVARCHAR(MAX)), N', ')
|
(SELECT STRING_AGG(CAST({LookupAliasForValues()}.DisplayValue AS NVARCHAR(MAX)), N', ')
|
||||||
FROM (VALUES {values}) AS {LookupAliasForValues()}(ValueKey, DisplayValue)
|
FROM (VALUES {values}) AS {LookupAliasForValues()}(ValueKey, DisplayValue)
|
||||||
INNER JOIN STRING_SPLIT(CAST({sourceField} AS NVARCHAR(MAX)), N'{PlatformConsts.MultiValueDelimiter}') AS SplitValues
|
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}
|
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), ', ')
|
(SELECT string_agg(CAST({LookupAliasForValues()}."DisplayValue" AS TEXT), ', ')
|
||||||
FROM (VALUES {values}) AS {LookupAliasForValues()}("ValueKey", "DisplayValue")
|
FROM (VALUES {values}) AS {LookupAliasForValues()}("ValueKey", "DisplayValue")
|
||||||
INNER JOIN unnest(string_to_array(CAST({sourceField} AS TEXT), '{PlatformConsts.MultiValueDelimiter}')) AS SplitValues(value)
|
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}
|
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,
|
IReportLookupColumn column,
|
||||||
string lookupQuery,
|
string lookupQuery,
|
||||||
DataSourceTypeEnum dataSourceType)
|
DataSourceTypeEnum dataSourceType)
|
||||||
{
|
{
|
||||||
|
var (commonTableExpression, lookupSelectQuery) = SplitCommonTableExpression(lookupQuery);
|
||||||
var lookupAlias = QuoteSqlIdentifier(LookupAlias);
|
var lookupAlias = QuoteSqlIdentifier(LookupAlias);
|
||||||
var valueExpr = QuoteSqlIdentifier(column.Lookup.ValueExpr ?? "Key");
|
var valueExpr = QuoteSqlIdentifier(column.Lookup.ValueExpr ?? "Key");
|
||||||
var displayExpr = QuoteSqlIdentifier(column.Lookup.DisplayExpr ?? "Name");
|
var displayExpr = QuoteSqlIdentifier(column.Lookup.DisplayExpr ?? "Name");
|
||||||
|
|
@ -177,18 +190,92 @@ internal static class DynamicReportSqlBuilder
|
||||||
var scalarQuery = dataSourceType == DataSourceTypeEnum.Mssql
|
var scalarQuery = dataSourceType == DataSourceTypeEnum.Mssql
|
||||||
? $"""
|
? $"""
|
||||||
SELECT STRING_AGG(CAST({lookupAlias}.{displayExpr} AS NVARCHAR(MAX)), N', ')
|
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
|
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))
|
ON CAST({lookupAlias}.{valueExpr} AS NVARCHAR(4000)) = LTRIM(RTRIM(SplitValues.value))
|
||||||
""".ReplaceLineEndings(" ").Trim()
|
""".ReplaceLineEndings(" ").Trim()
|
||||||
: $"""
|
: $"""
|
||||||
SELECT string_agg(CAST({lookupAlias}.{displayExpr} AS TEXT), ', ')
|
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)
|
INNER JOIN unnest(string_to_array(CAST({sourceField} AS TEXT), '{PlatformConsts.MultiValueDelimiter}')) AS SplitValues(value)
|
||||||
ON CAST({lookupAlias}.{valueExpr} AS TEXT) = btrim(SplitValues.value)
|
ON CAST({lookupAlias}.{valueExpr} AS TEXT) = btrim(SplitValues.value)
|
||||||
""".ReplaceLineEndings(" ").Trim();
|
""".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";
|
private static string LookupAliasForValues() => "LookupValues";
|
||||||
|
|
@ -227,4 +314,6 @@ internal static class DynamicReportSqlBuilder
|
||||||
return $"\"{identifier.Replace("\"", "\"\"")}\"";
|
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 portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
|
||||||
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
|
var requestedContentWidth = visibleFields.Sum(x => x.IsImage ? GetImageColumnWidth(x) : GetPreferredColumnWidth(x.Field));
|
||||||
Landscape = visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth;
|
Landscape = visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth;
|
||||||
Font = new DXFont("Arial", 8.25F);
|
Font = new DXFont("Arial", 8F);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static System.Drawing.Printing.Margins CreateReportMargins(
|
private static System.Drawing.Printing.Margins CreateReportMargins(
|
||||||
|
|
@ -550,7 +550,7 @@ public class DynamicTreeReport : XtraReport
|
||||||
{
|
{
|
||||||
WidthF = width,
|
WidthF = width,
|
||||||
Weight = width,
|
Weight = width,
|
||||||
Font = new DXFont("Segoe UI Emoji", 8F),
|
Font = new DXFont("Arial", 8F),
|
||||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||||
TextAlignment = isTreeColumn
|
TextAlignment = isTreeColumn
|
||||||
? TextAlignment.MiddleLeft
|
? TextAlignment.MiddleLeft
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,19 @@
|
||||||
{
|
{
|
||||||
"commit": "771ea95",
|
"commit": "e7670c6",
|
||||||
"releases": [
|
"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",
|
"version": "1.1.04",
|
||||||
"buildDate": "2026-06-04",
|
"buildDate": "2026-06-04",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue