Devexpress Grid Raporu otomatik oluşturma
This commit is contained in:
parent
3cff822775
commit
6884812484
7 changed files with 594 additions and 38 deletions
|
|
@ -1586,6 +1586,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
AuthName = listFormName,
|
||||
Url=$"/admin/reports/TemplateReport/view?id=@Id",
|
||||
IsVisible = true,
|
||||
VisibleExpression = "((e)=> e.row.data.Description !== 'Liste Raporları')"
|
||||
},
|
||||
new() {
|
||||
Hint = "App.Platform.Design",
|
||||
|
|
@ -1594,6 +1595,16 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
|||
AuthName = listFormName + ".Update",
|
||||
Url=$"/admin/reports/TemplateReport/design?id=@Id",
|
||||
IsVisible = true,
|
||||
VisibleExpression = "((e)=> e.row.data.Description !== 'Liste Raporları')"
|
||||
},
|
||||
new() {
|
||||
Hint = "App.Platform.View",
|
||||
Text ="App.Platform.View",
|
||||
UrlTarget="_blank",
|
||||
AuthName = listFormName,
|
||||
Url=$"/admin/reports/DynamicListFormReport/view?listFormCode=@Name",
|
||||
IsVisible = true,
|
||||
VisibleExpression = "((e)=> e.row.data.Description !== 'Liste Raporları')"
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1373,8 +1373,8 @@
|
|||
"icon": "✍️"
|
||||
},
|
||||
{
|
||||
"name": "Sözleşmeler",
|
||||
"description": "Tedarikçiler ile ilgili sözleşme raporları",
|
||||
"name": "Listeler",
|
||||
"description": "Tüm listelerin raporları",
|
||||
"icon": "📜"
|
||||
}
|
||||
],
|
||||
|
|
@ -1384,7 +1384,14 @@
|
|||
"description": "Standard Taahhütname",
|
||||
"categoryName": "Taahhütnameler",
|
||||
"htmlContent": "<h1>Taahhütname</h1><p>Bu belge, tarafların belirli yükümlülüklerini ve taahhütlerini belirtir.</p>",
|
||||
"status": "Aktif"
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "App.Definitions.AiBot",
|
||||
"description": "Liste Raporları",
|
||||
"categoryName": "Listeler",
|
||||
"htmlContent": "<p>Liste Raporları.</p>",
|
||||
"status": "active"
|
||||
}
|
||||
],
|
||||
"WorkHours": [
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
|
||||
using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
|
||||
using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Sozsoft.Platform.Controllers;
|
||||
|
||||
[Route("DXXRDV")]
|
||||
[Authorize]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public class CustomWebDocumentViewerController : WebDocumentViewerController
|
||||
{
|
||||
public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService)
|
||||
public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService)
|
||||
: base(controllerService)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,480 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using DevExpress.DataAccess.ConnectionParameters;
|
||||
using DevExpress.DataAccess.Sql;
|
||||
using DevExpress.Drawing;
|
||||
using DevExpress.XtraPrinting;
|
||||
using DevExpress.XtraReports.UI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Sozsoft.Platform.DynamicData;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Enums;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Sozsoft.Platform.Localization;
|
||||
using Sozsoft.Platform.Queries;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
public class DynamicListFormReport : XtraReport
|
||||
{
|
||||
public const string ReportName = nameof(DynamicListFormReport);
|
||||
private const string QueryName = "DynamicList";
|
||||
private const int A4PortraitWidth = 827;
|
||||
private const int HorizontalMargin = 35;
|
||||
private const int LandscapeColumnThreshold = 5;
|
||||
private static IServiceProvider _serviceProvider;
|
||||
|
||||
public static void SetServiceProvider(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public static DynamicListFormReport Create(IDictionary<string, string> parameters)
|
||||
{
|
||||
var report = new DynamicListFormReport();
|
||||
report.Build(parameters);
|
||||
return report;
|
||||
}
|
||||
|
||||
public DynamicListFormReport()
|
||||
{
|
||||
}
|
||||
|
||||
private void Build(IDictionary<string, string> parameters)
|
||||
{
|
||||
if (_serviceProvider == null)
|
||||
{
|
||||
throw new InvalidOperationException("DynamicListFormReport service provider is not configured.");
|
||||
}
|
||||
|
||||
if (!parameters.TryGetValue("listFormCode", out var listFormCode) || string.IsNullOrWhiteSpace(listFormCode))
|
||||
{
|
||||
throw new ArgumentException("listFormCode parameter is required.");
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var authManager = scope.ServiceProvider.GetRequiredService<IListFormAuthorizationManager>();
|
||||
var listFormManager = scope.ServiceProvider.GetRequiredService<IListFormManager>();
|
||||
var listFormFieldManager = scope.ServiceProvider.GetRequiredService<IListFormFieldManager>();
|
||||
var customizationManager = scope.ServiceProvider.GetRequiredService<IListFormCustomizationManager>();
|
||||
var dynamicDataManager = scope.ServiceProvider.GetRequiredService<IDynamicDataManager>();
|
||||
var selectQueryManager = scope.ServiceProvider.GetRequiredService<ISelectQueryManager>();
|
||||
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
|
||||
|
||||
if (!authManager.CanAccess(listFormCode).GetAwaiter().GetResult())
|
||||
{
|
||||
throw new UnauthorizedAccessException($"No read permission for list form '{listFormCode}'.");
|
||||
}
|
||||
|
||||
var listForm = listFormManager.GetUserListForm(listFormCode).GetAwaiter().GetResult();
|
||||
var fields = listFormFieldManager.GetUserListFormFields(listFormCode).GetAwaiter().GetResult()
|
||||
.Where(x => x.CanRead)
|
||||
.OrderBy(x => x.ListOrderNo ?? int.MaxValue)
|
||||
.ThenBy(x => x.CaptionName)
|
||||
.ToList();
|
||||
var customizations = customizationManager.GetUsersServerCustomizations(listFormCode).GetAwaiter().GetResult();
|
||||
var (_, connectionString, dataSourceType) = dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode).GetAwaiter().GetResult();
|
||||
|
||||
var queryParameters = ToStringValues(parameters);
|
||||
var queryParams = new QueryParameters
|
||||
{
|
||||
ListFormCode = listFormCode,
|
||||
Filter = GetParameter(parameters, "filter"),
|
||||
Sort = GetParameter(parameters, "sort"),
|
||||
Group = GetParameter(parameters, "group"),
|
||||
GroupSummary = GetParameter(parameters, "groupSummary"),
|
||||
TotalSummary = GetParameter(parameters, "totalSummary"),
|
||||
Take = 0,
|
||||
Skip = 0
|
||||
};
|
||||
|
||||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
||||
|
||||
var visibleFields = fields
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
|
||||
.Where(x => x.IsActive != false && x.Visible == true)
|
||||
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.FieldName))
|
||||
.ToList();
|
||||
|
||||
if (visibleFields.Count == 0)
|
||||
{
|
||||
visibleFields = fields
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
|
||||
.Where(x => x.IsActive != false)
|
||||
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.FieldName))
|
||||
.Take(8)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var reportColumns = visibleFields
|
||||
.Select(field =>
|
||||
{
|
||||
var selectField = selectQueryManager.SelectFields.First(s => s.FieldName == field.FieldName);
|
||||
return new ReportColumn(field, GetReportFieldName(selectField));
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
||||
.ToList();
|
||||
|
||||
ConfigurePage(reportColumns);
|
||||
ConfigureDataSource(
|
||||
ApplyQueryParameters(selectQueryManager.SelectQuery, selectQueryManager.SelectQueryParameters),
|
||||
connectionString,
|
||||
dataSourceType);
|
||||
BuildLayout(listForm, reportColumns, localizer);
|
||||
RequestParameters = false;
|
||||
}
|
||||
|
||||
private void ConfigurePage(IReadOnlyCollection<ReportColumn> visibleFields)
|
||||
{
|
||||
Margins = new System.Drawing.Printing.Margins(HorizontalMargin, HorizontalMargin, 35, 35);
|
||||
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4;
|
||||
var portraitContentWidth = A4PortraitWidth - Margins.Left - Margins.Right;
|
||||
var requestedContentWidth = visibleFields.Sum(x => Math.Max(x.Field.Width ?? 90, 60));
|
||||
Landscape = visibleFields.Count >= LandscapeColumnThreshold || requestedContentWidth > portraitContentWidth;
|
||||
Font = new DXFont("Arial", 8.25F);
|
||||
}
|
||||
|
||||
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
|
||||
dataSource.Queries.Add(new CustomSqlQuery(QueryName, selectQuery));
|
||||
ComponentStorage.Add(dataSource);
|
||||
DataSource = dataSource;
|
||||
DataMember = QueryName;
|
||||
}
|
||||
|
||||
private static string ToDevExpressConnectionString(string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionString) ||
|
||||
connectionString.Contains("XpoProvider=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
var provider = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => "MSSqlServer",
|
||||
DataSourceTypeEnum.Postgresql => "Postgres",
|
||||
_ => null
|
||||
};
|
||||
|
||||
return string.IsNullOrWhiteSpace(provider)
|
||||
? connectionString
|
||||
: $"XpoProvider={provider};{connectionString}";
|
||||
}
|
||||
|
||||
private static string ApplyQueryParameters(string sql, Dictionary<string, object> parameters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sql) || parameters == null || parameters.Count == 0)
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters.OrderByDescending(x => x.Key.Length))
|
||||
{
|
||||
var key = parameter.Key.StartsWith("@") ? parameter.Key : "@" + parameter.Key;
|
||||
sql = Regex.Replace(
|
||||
sql,
|
||||
Regex.Escape(key) + @"\b",
|
||||
ToSqlLiteral(parameter.Value),
|
||||
RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static string ToSqlLiteral(object value)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return value switch
|
||||
{
|
||||
string text => $"'{text.Replace("'", "''")}'",
|
||||
Guid guid => $"'{guid}'",
|
||||
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
|
||||
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
|
||||
bool boolean => boolean ? "1" : "0",
|
||||
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
|
||||
=> Convert.ToString(value, CultureInfo.InvariantCulture),
|
||||
_ => $"'{value.ToString()?.Replace("'", "''")}'"
|
||||
};
|
||||
}
|
||||
|
||||
private void BuildLayout(
|
||||
ListForm listForm,
|
||||
IReadOnlyList<ReportColumn> fields,
|
||||
IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
Bands.Clear();
|
||||
|
||||
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
||||
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand { HeightF = 42 };
|
||||
var pageHeader = new PageHeaderBand { HeightF = 28 };
|
||||
var detail = new DetailBand { HeightF = 24 };
|
||||
var reportFooter = new ReportFooterBand { HeightF = HasAnySummary(fields) ? 28 : 0 };
|
||||
|
||||
Bands.AddRange([topMargin, reportHeader, pageHeader, detail, reportFooter, bottomMargin]);
|
||||
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
reportHeader.Controls.Add(new XRLabel
|
||||
{
|
||||
Text = GetLocalizedReportTitle(listForm, localizer),
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth, 30),
|
||||
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
});
|
||||
|
||||
var generatedAt = new XRPageInfo
|
||||
{
|
||||
PageInfo = PageInfo.DateTime,
|
||||
TextFormatString = "{0:dd.MM.yyyy HH:mm}",
|
||||
BoundsF = new System.Drawing.RectangleF(0, 30, pageWidth / 2, 12),
|
||||
Font = new DXFont("Arial", 7.5F),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
};
|
||||
reportHeader.Controls.Add(generatedAt);
|
||||
|
||||
var pageInfo = new XRPageInfo
|
||||
{
|
||||
TextFormatString = "Sayfa {0} / {1}",
|
||||
BoundsF = new System.Drawing.RectangleF(pageWidth / 2, 0, pageWidth / 2, 18),
|
||||
Font = new DXFont("Arial", 8F),
|
||||
TextAlignment = TextAlignment.MiddleRight
|
||||
};
|
||||
bottomMargin.Controls.Add(pageInfo);
|
||||
|
||||
var widths = CalculateWidths(fields, pageWidth);
|
||||
var headerTable = CreateTable(pageWidth, 28);
|
||||
var headerRow = new XRTableRow();
|
||||
var detailTable = CreateTable(pageWidth, 24);
|
||||
var detailRow = new XRTableRow();
|
||||
var footerTable = CreateTable(pageWidth, 24);
|
||||
var footerRow = new XRTableRow();
|
||||
|
||||
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]));
|
||||
footerRow.Cells.Add(CreateFooterCell(field, widths[i]));
|
||||
}
|
||||
|
||||
headerTable.Rows.Add(headerRow);
|
||||
detailTable.Rows.Add(detailRow);
|
||||
footerTable.Rows.Add(footerRow);
|
||||
|
||||
pageHeader.Controls.Add(headerTable);
|
||||
detail.Controls.Add(detailTable);
|
||||
if (HasAnySummary(fields))
|
||||
{
|
||||
reportFooter.Controls.Add(footerTable);
|
||||
}
|
||||
}
|
||||
|
||||
private static XRTable CreateTable(float width, float height)
|
||||
{
|
||||
return new XRTable
|
||||
{
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, width, height),
|
||||
Borders = BorderSide.All,
|
||||
BorderWidth = 0.5F
|
||||
};
|
||||
}
|
||||
|
||||
private static XRTableCell CreateHeaderCell(
|
||||
ReportColumn column,
|
||||
float width,
|
||||
IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var field = column.Field;
|
||||
return new XRTableCell
|
||||
{
|
||||
Text = GetLocalizedFieldCaption(field, localizer),
|
||||
WidthF = width,
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = TextAlignment.MiddleCenter,
|
||||
Multiline = true
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetLocalizedReportTitle(ListForm listForm, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var title = string.IsNullOrWhiteSpace(listForm.Title) ? listForm.Name : listForm.Title;
|
||||
return Localize(localizer, title, listForm.ListFormCode);
|
||||
}
|
||||
|
||||
private static string GetLocalizedFieldCaption(ListFormField field, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
|
||||
return Localize(localizer, caption, field.FieldName);
|
||||
}
|
||||
|
||||
private static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
var localized = localizer[key];
|
||||
return localized.ResourceNotFound || string.IsNullOrWhiteSpace(localized.Value)
|
||||
? key
|
||||
: localized.Value;
|
||||
}
|
||||
|
||||
private static XRTableCell CreateDetailCell(ReportColumn column, float width)
|
||||
{
|
||||
var field = column.Field;
|
||||
var cell = new XRTableCell
|
||||
{
|
||||
WidthF = width,
|
||||
Font = new DXFont("Arial", 8F),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = ToTextAlignment(field.Alignment, field.SourceDbType),
|
||||
Multiline = false,
|
||||
CanGrow = false
|
||||
};
|
||||
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"[{column.BindingName}]"));
|
||||
return cell;
|
||||
}
|
||||
|
||||
private static XRTableCell CreateFooterCell(ReportColumn column, float width)
|
||||
{
|
||||
var field = column.Field;
|
||||
var summaryType = GetSummaryType(field.TotalSummaryJson);
|
||||
var cell = new XRTableCell
|
||||
{
|
||||
WidthF = width,
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = ToTextAlignment(field.Alignment, field.SourceDbType)
|
||||
};
|
||||
|
||||
if (summaryType == null)
|
||||
{
|
||||
return cell;
|
||||
}
|
||||
|
||||
cell.ExpressionBindings.Add(new ExpressionBinding("BeforePrint", "Text", $"sumSum([{column.BindingName}])"));
|
||||
cell.Summary = new XRSummary
|
||||
{
|
||||
Running = SummaryRunning.Report,
|
||||
Func = summaryType.Value,
|
||||
IgnoreNullValues = true
|
||||
};
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
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 => (float)Math.Max(x.Field.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 bool HasAnySummary(IEnumerable<ReportColumn> fields)
|
||||
{
|
||||
return fields.Any(x => GetSummaryType(x.Field.TotalSummaryJson) != null);
|
||||
}
|
||||
|
||||
private static string GetReportFieldName(SelectField selectField)
|
||||
{
|
||||
if (selectField?.JoinOptions != null && !string.IsNullOrWhiteSpace(selectField.JoinOptions.FieldNameAlias))
|
||||
{
|
||||
return selectField.JoinOptions.FieldNameAlias;
|
||||
}
|
||||
|
||||
return selectField?.FieldName;
|
||||
}
|
||||
|
||||
private sealed record ReportColumn(ListFormField Field, string BindingName);
|
||||
|
||||
private static SummaryFunc? GetSummaryType(string totalSummaryJson)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(totalSummaryJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = totalSummaryJson.ToLowerInvariant();
|
||||
if (value.Contains("\"summarytype\":\"sum\""))
|
||||
{
|
||||
return SummaryFunc.Sum;
|
||||
}
|
||||
if (value.Contains("\"summarytype\":\"avg\""))
|
||||
{
|
||||
return SummaryFunc.Avg;
|
||||
}
|
||||
if (value.Contains("\"summarytype\":\"min\""))
|
||||
{
|
||||
return SummaryFunc.Min;
|
||||
}
|
||||
if (value.Contains("\"summarytype\":\"max\""))
|
||||
{
|
||||
return SummaryFunc.Max;
|
||||
}
|
||||
if (value.Contains("\"summarytype\":\"count\""))
|
||||
{
|
||||
return SummaryFunc.Count;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TextAlignment ToTextAlignment(string alignment, DbType dbType)
|
||||
{
|
||||
return alignment?.ToLowerInvariant() switch
|
||||
{
|
||||
"center" => TextAlignment.MiddleCenter,
|
||||
"right" => TextAlignment.MiddleRight,
|
||||
"left" => TextAlignment.MiddleLeft,
|
||||
_ => IsNumeric(dbType) ? TextAlignment.MiddleRight : TextAlignment.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsNumeric(DbType dbType)
|
||||
{
|
||||
return dbType is DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32
|
||||
or DbType.Int64 or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64;
|
||||
}
|
||||
|
||||
private static string GetParameter(IDictionary<string, string> parameters, string name)
|
||||
{
|
||||
return parameters.TryGetValue(name, out var value) ? value : null;
|
||||
}
|
||||
|
||||
private static Dictionary<string, StringValues> ToStringValues(IDictionary<string, string> parameters)
|
||||
{
|
||||
return parameters.ToDictionary(x => x.Key, x => new StringValues(x.Value));
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
|
||||
// TemplateReport için ServiceProvider'ı set et
|
||||
Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider);
|
||||
Sozsoft.Reports.PredefinedReports.DynamicListFormReport.SetServiceProvider(serviceProvider);
|
||||
|
||||
if (!Directory.Exists(reportDirectory))
|
||||
{
|
||||
|
|
@ -34,6 +35,7 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
|
||||
// TemplateReport için ServiceProvider'ı set et
|
||||
Sozsoft.Reports.PredefinedReports.TemplateReport.SetServiceProvider(serviceProvider);
|
||||
Sozsoft.Reports.PredefinedReports.DynamicListFormReport.SetServiceProvider(serviceProvider);
|
||||
|
||||
if (!Directory.Exists(reportDirectory))
|
||||
{
|
||||
|
|
@ -65,8 +67,8 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
// Implement your own logic to prohibit URLs that contain spaces or other specific characters.
|
||||
// Return **true** if no validation is required.
|
||||
|
||||
// Query string varsa sadece rapor adını kontrol et
|
||||
string reportName = url.Contains("?") ? url.Split('?')[0] : url;
|
||||
// Query string encoded gelse bile sadece rapor adını kontrol et.
|
||||
var (reportName, _) = ParseReportUrl(url);
|
||||
return Path.GetFileName(reportName) == reportName;
|
||||
}
|
||||
|
||||
|
|
@ -78,26 +80,14 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
// if the parameters are included in the report URL's query string.
|
||||
try
|
||||
{
|
||||
// URL'den rapor adını ve parametreleri ayır
|
||||
string reportName = url;
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>();
|
||||
var (reportName, parameters) = ParseReportUrl(url);
|
||||
|
||||
if (url.Contains("?"))
|
||||
if (reportName == Sozsoft.Reports.PredefinedReports.DynamicListFormReport.ReportName)
|
||||
{
|
||||
var parts = url.Split('?');
|
||||
reportName = parts[0];
|
||||
|
||||
// Query string parametrelerini parse et
|
||||
var queryString = parts[1];
|
||||
var paramPairs = queryString.Split('&');
|
||||
foreach (var pair in paramPairs)
|
||||
{
|
||||
var keyValue = pair.Split('=');
|
||||
if (keyValue.Length == 2)
|
||||
{
|
||||
parameters[keyValue[0]] = Uri.UnescapeDataString(keyValue[1]);
|
||||
}
|
||||
}
|
||||
var report = Sozsoft.Reports.PredefinedReports.DynamicListFormReport.Create(parameters);
|
||||
using MemoryStream ms = new MemoryStream();
|
||||
report.SaveLayoutToXml(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
// Önce Reports klasöründen yüklemeyi dene
|
||||
|
|
@ -177,6 +167,51 @@ public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extens
|
|||
throw new FaultException(new FaultReason(string.Format("Could not find report '{0}'.", url)), new FaultCode("Server"), "GetData");
|
||||
}
|
||||
|
||||
private static (string ReportName, Dictionary<string, string> Parameters) ParseReportUrl(string url)
|
||||
{
|
||||
var decodedUrl = DecodeRepeatedly(url);
|
||||
var reportName = decodedUrl;
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
var queryIndex = decodedUrl.IndexOf('?');
|
||||
if (queryIndex < 0)
|
||||
{
|
||||
return (reportName, parameters);
|
||||
}
|
||||
|
||||
reportName = decodedUrl[..queryIndex];
|
||||
var queryString = decodedUrl[(queryIndex + 1)..];
|
||||
var paramPairs = queryString.Split('&', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var pair in paramPairs)
|
||||
{
|
||||
var keyValue = pair.Split('=', 2);
|
||||
if (keyValue.Length == 2 && !string.IsNullOrWhiteSpace(keyValue[0]))
|
||||
{
|
||||
parameters[keyValue[0]] = keyValue[1];
|
||||
}
|
||||
}
|
||||
|
||||
return (reportName, parameters);
|
||||
}
|
||||
|
||||
private static string DecodeRepeatedly(string value)
|
||||
{
|
||||
var decoded = value;
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var next = Uri.UnescapeDataString(decoded);
|
||||
if (next == decoded)
|
||||
{
|
||||
return decoded;
|
||||
}
|
||||
|
||||
decoded = next;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
public override Dictionary<string, string> GetUrls()
|
||||
{
|
||||
// Returns a dictionary that contains the report names (URLs) and display names.
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ export const ROUTES_ENUM = {
|
|||
dynamicServicesEdit: '/admin/developerkit/dynamic-services/edit/:id',
|
||||
},
|
||||
reports: {
|
||||
generator: '/admin/reports/generator',
|
||||
view: '/admin/reports/:id',
|
||||
view: '/admin/reports/:id/view',
|
||||
design: '/admin/reports/:id/design',
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import React, { useMemo, lazy, Suspense, useEffect } from 'react'
|
||||
import React, { useMemo, useEffect, useState } from 'react'
|
||||
import { Container } from '@/components/shared'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { useParams, useLocation } from 'react-router-dom'
|
||||
import { RequestOptions } from 'devexpress-reporting-react/dx-report-viewer'
|
||||
import ReportViewer, {
|
||||
RequestOptions,
|
||||
} from 'devexpress-reporting-react/dx-report-viewer'
|
||||
import { ajaxSetup } from '@devexpress/analytics-core/analytics-utils-native'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
|
||||
// Lazy load
|
||||
const ReportViewer = lazy(() =>
|
||||
import('devexpress-reporting-react/dx-report-viewer')
|
||||
)
|
||||
import { useStoreState } from '@/store'
|
||||
|
||||
const loadViewerCss = () => {
|
||||
const styles = [
|
||||
|
|
@ -31,11 +30,33 @@ const DevexpressReportViewer: React.FC = () => {
|
|||
const { translate } = useLocalization()
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const location = useLocation()
|
||||
const token = useStoreState((state) => state.auth.session.token)
|
||||
const [isRequestAuthReady, setIsRequestAuthReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
loadViewerCss()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setIsRequestAuthReady(false)
|
||||
const authorization = token ? `Bearer ${token}` : undefined
|
||||
|
||||
const ajaxHeaders = ajaxSetup.ajaxSettings.headers ?? {}
|
||||
|
||||
if (authorization) {
|
||||
ajaxSetup.ajaxSettings.headers = {
|
||||
...ajaxHeaders,
|
||||
Authorization: authorization,
|
||||
}
|
||||
setIsRequestAuthReady(true)
|
||||
return
|
||||
}
|
||||
|
||||
const { Authorization: _ajaxAuthorization, ...nextAjaxHeaders } = ajaxHeaders
|
||||
ajaxSetup.ajaxSettings.headers = nextAjaxHeaders
|
||||
setIsRequestAuthReady(true)
|
||||
}, [token])
|
||||
|
||||
const reportUrlWithParams = useMemo(() => {
|
||||
if (!id) return ''
|
||||
return location.search ? `${id}${location.search}` : id
|
||||
|
|
@ -51,14 +72,14 @@ const DevexpressReportViewer: React.FC = () => {
|
|||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
|
||||
<Suspense fallback={<div>Rapor yükleniyor...</div>}>
|
||||
{isRequestAuthReady && (
|
||||
<ReportViewer reportUrl={reportUrlWithParams}>
|
||||
<RequestOptions
|
||||
host={`${import.meta.env.VITE_API_URL}/`}
|
||||
invokeAction="DXXRDV"
|
||||
host={import.meta.env.VITE_API_URL}
|
||||
invokeAction="/DXXRDV"
|
||||
/>
|
||||
</ReportViewer>
|
||||
</Suspense>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue