CargoWise Reporting & Analytics: Custom Reports and Dashboards (2025 Guide)

Jan 19, 2025
cargowisereportinganalyticsdashboards
0

CargoWise reporting and analytics capabilities are essential for gaining insights into freight forwarding operations, making data-driven decisions, and improving business performance. With the right reporting tools and techniques, you can transform raw operational data into actionable intelligence that drives growth and efficiency.

This comprehensive guide covers everything you need to know about CargoWise reporting and analytics, from basic report creation to advanced dashboard design and data visualization. Whether you're a business analyst creating your first report or an experienced developer building complex analytics solutions, this guide will provide the tools and techniques you need.

Understanding CargoWise Reporting

Reporting Capabilities

Core Reporting Features:

  • Standard Reports: Pre-built reports for common business needs
  • Custom Reports: User-defined reports with specific requirements
  • Dashboards: Interactive visualizations and KPI displays
  • Data Exports: Export data in various formats (Excel, PDF, CSV)
  • Scheduled Reports: Automated report generation and distribution
  • Real-time Analytics: Live data updates and monitoring

Report Types:

  • Operational Reports: Daily operations and process monitoring
  • Financial Reports: Revenue, costs, and profitability analysis
  • Performance Reports: KPI tracking and performance metrics
  • Compliance Reports: Regulatory and audit requirements
  • Customer Reports: Customer analysis and relationship management
  • Carrier Reports: Carrier performance and cost analysis

Data Sources and Integration

CargoWise Data Sources:

  • Shipment Data: Core freight forwarding operations
  • Customer Data: Customer information and relationships
  • Financial Data: Billing, invoicing, and payment information
  • Operational Data: Process workflows and status updates
  • External Data: Third-party integrations and APIs
  • Historical Data: Trend analysis and historical comparisons

Custom Report Development

Report Builder Framework

C# Report Builder Implementation:

public class CargoWiseReportBuilder
{
    private readonly ILogger<CargoWiseReportBuilder> _logger;
    private readonly IReportRepository _reportRepository;
    private readonly IDataQueryService _dataQueryService;
    private readonly IReportRenderer _reportRenderer;
    
    public CargoWiseReportBuilder(
        ILogger<CargoWiseReportBuilder> logger,
        IReportRepository reportRepository,
        IDataQueryService dataQueryService,
        IReportRenderer reportRenderer)
    {
        _logger = logger;
        _reportRepository = reportRepository;
        _dataQueryService = dataQueryService;
        _reportRenderer = reportRenderer;
    }
    
    public async Task<ReportResult> CreateReport(CreateReportRequest request)
    {
        var result = new ReportResult
        {
            ReportId = request.ReportId,
            StartTime = DateTime.UtcNow,
            Status = ReportStatus.InProgress
        };
        
        try
        {
            _logger.LogInformation("Creating report: {ReportId}", request.ReportId);
            
            // Validate report configuration
            var validationResult = await ValidateReportConfiguration(request);
            if (!validationResult.IsValid)
            {
                result.Status = ReportStatus.ValidationFailed;
                result.ErrorMessage = validationResult.ErrorMessage;
                return result;
            }
            
            // Build report query
            var query = await BuildReportQuery(request);
            
            // Execute data query
            var dataResult = await _dataQueryService.ExecuteQueryAsync(query);
            
            // Process and transform data
            var processedData = await ProcessReportData(dataResult.Data, request);
            
            // Render report
            var renderedReport = await _reportRenderer.RenderReport(processedData, request);
            
            result.Status = ReportStatus.Completed;
            result.ReportData = renderedReport;
            result.RecordCount = processedData.Count;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            _logger.LogInformation("Report created successfully: {ReportId} with {RecordCount} records in {Duration}", 
                request.ReportId, processedData.Count, result.Duration);
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating report: {ReportId}", request.ReportId);
            
            result.Status = ReportStatus.Failed;
            result.ErrorMessage = ex.Message;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            return result;
        }
    }
    
    private async Task<ReportValidationResult> ValidateReportConfiguration(CreateReportRequest request)
    {
        var result = new ReportValidationResult { IsValid = true };
        
        // Validate required fields
        if (string.IsNullOrEmpty(request.ReportName))
        {
            result.IsValid = false;
            result.ErrorMessage = "Report name is required";
            return result;
        }
        
        if (request.DataSource == null)
        {
            result.IsValid = false;
            result.ErrorMessage = "Data source is required";
            return result;
        }
        
        if (request.Columns == null || !request.Columns.Any())
        {
            result.IsValid = false;
            result.ErrorMessage = "At least one column is required";
            return result;
        }
        
        // Validate data source
        var dataSourceValidation = await ValidateDataSource(request.DataSource);
        if (!dataSourceValidation.IsValid)
        {
            result.IsValid = false;
            result.ErrorMessage = dataSourceValidation.ErrorMessage;
            return result;
        }
        
        // Validate columns
        var columnValidation = await ValidateColumns(request.Columns, request.DataSource);
        if (!columnValidation.IsValid)
        {
            result.IsValid = false;
            result.ErrorMessage = columnValidation.ErrorMessage;
            return result;
        }
        
        return result;
    }
    
    private async Task<ReportQuery> BuildReportQuery(CreateReportRequest request)
    {
        var query = new ReportQuery
        {
            DataSource = request.DataSource,
            Columns = request.Columns,
            Filters = request.Filters,
            GroupBy = request.GroupBy,
            OrderBy = request.OrderBy,
            Limit = request.Limit,
            Offset = request.Offset
        };
        
        // Build SQL query
        query.SqlQuery = await BuildSqlQuery(query);
        
        return query;
    }
    
    private async Task<string> BuildSqlQuery(ReportQuery query)
    {
        var sqlBuilder = new StringBuilder();
        
        // SELECT clause
        sqlBuilder.Append("SELECT ");
        if (query.Columns.Any())
        {
            sqlBuilder.Append(string.Join(", ", query.Columns.Select(c => $"{c.TableAlias}.{c.ColumnName}")));
        }
        else
        {
            sqlBuilder.Append("*");
        }
        
        // FROM clause
        sqlBuilder.Append($" FROM {query.DataSource.TableName} {query.DataSource.TableAlias}");
        
        // JOIN clauses
        if (query.DataSource.Joins != null && query.DataSource.Joins.Any())
        {
            foreach (var join in query.DataSource.Joins)
            {
                sqlBuilder.Append($" {join.JoinType} {join.TableName} {join.TableAlias} ON {join.JoinCondition}");
            }
        }
        
        // WHERE clause
        if (query.Filters != null && query.Filters.Any())
        {
            sqlBuilder.Append(" WHERE ");
            var whereConditions = new List<string>();
            
            foreach (var filter in query.Filters)
            {
                var condition = BuildFilterCondition(filter);
                whereConditions.Add(condition);
            }
            
            sqlBuilder.Append(string.Join(" AND ", whereConditions));
        }
        
        // GROUP BY clause
        if (query.GroupBy != null && query.GroupBy.Any())
        {
            sqlBuilder.Append($" GROUP BY {string.Join(", ", query.GroupBy)}");
        }
        
        // ORDER BY clause
        if (query.OrderBy != null && query.OrderBy.Any())
        {
            sqlBuilder.Append($" ORDER BY {string.Join(", ", query.OrderBy)}");
        }
        
        // LIMIT clause
        if (query.Limit.HasValue)
        {
            sqlBuilder.Append($" LIMIT {query.Limit.Value}");
        }
        
        // OFFSET clause
        if (query.Offset.HasValue)
        {
            sqlBuilder.Append($" OFFSET {query.Offset.Value}");
        }
        
        return sqlBuilder.ToString();
    }
}

Report Configuration

C# Report Configuration:

public class CargoWiseReportConfiguration
{
    public string ReportId { get; set; }
    public string ReportName { get; set; }
    public string Description { get; set; }
    public ReportType Type { get; set; }
    public DataSourceConfiguration DataSource { get; set; }
    public List<ColumnConfiguration> Columns { get; set; }
    public List<FilterConfiguration> Filters { get; set; }
    public List<string> GroupBy { get; set; }
    public List<OrderByConfiguration> OrderBy { get; set; }
    public int? Limit { get; set; }
    public int? Offset { get; set; }
    public ReportFormat Format { get; set; }
    public List<ParameterConfiguration> Parameters { get; set; }
    public ScheduleConfiguration Schedule { get; set; }
}

public class DataSourceConfiguration
{
    public string TableName { get; set; }
    public string TableAlias { get; set; }
    public List<JoinConfiguration> Joins { get; set; }
    public List<CalculatedFieldConfiguration> CalculatedFields { get; set; }
}

public class ColumnConfiguration
{
    public string ColumnName { get; set; }
    public string DisplayName { get; set; }
    public string TableAlias { get; set; }
    public DataType DataType { get; set; }
    public string Format { get; set; }
    public bool IsVisible { get; set; }
    public int SortOrder { get; set; }
    public string Aggregation { get; set; }
}

public class FilterConfiguration
{
    public string ColumnName { get; set; }
    public string TableAlias { get; set; }
    public FilterOperator Operator { get; set; }
    public object Value { get; set; }
    public object SecondValue { get; set; }
    public FilterLogic Logic { get; set; }
}

public class OrderByConfiguration
{
    public string ColumnName { get; set; }
    public string TableAlias { get; set; }
    public SortDirection Direction { get; set; }
    public int SortOrder { get; set; }
}

Report Templates

C# Report Template System:

public class CargoWiseReportTemplateManager
{
    private readonly ILogger<CargoWiseReportTemplateManager> _logger;
    private readonly IReportTemplateRepository _templateRepository;
    private readonly IReportBuilder _reportBuilder;
    
    public CargoWiseReportTemplateManager(
        ILogger<CargoWiseReportTemplateManager> logger,
        IReportTemplateRepository templateRepository,
        IReportBuilder reportBuilder)
    {
        _logger = logger;
        _templateRepository = templateRepository;
        _reportBuilder = reportBuilder;
    }
    
    public async Task<ReportTemplateResult> CreateReportFromTemplate(string templateId, Dictionary<string, object> parameters)
    {
        var result = new ReportTemplateResult
        {
            TemplateId = templateId,
            StartTime = DateTime.UtcNow,
            Status = ReportTemplateStatus.InProgress
        };
        
        try
        {
            _logger.LogInformation("Creating report from template: {TemplateId}", templateId);
            
            // Get template
            var template = await _templateRepository.GetByIdAsync(templateId);
            if (template == null)
            {
                result.Status = ReportTemplateStatus.TemplateNotFound;
                result.ErrorMessage = "Template not found";
                return result;
            }
            
            // Apply parameters to template
            var reportConfiguration = await ApplyParametersToTemplate(template, parameters);
            
            // Create report
            var reportResult = await _reportBuilder.CreateReport(reportConfiguration);
            
            result.Status = ReportTemplateStatus.Completed;
            result.ReportResult = reportResult;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            _logger.LogInformation("Report created from template successfully: {TemplateId} in {Duration}", 
                templateId, result.Duration);
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating report from template: {TemplateId}", templateId);
            
            result.Status = ReportTemplateStatus.Failed;
            result.ErrorMessage = ex.Message;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            return result;
        }
    }
    
    private async Task<CreateReportRequest> ApplyParametersToTemplate(ReportTemplate template, Dictionary<string, object> parameters)
    {
        var configuration = JsonSerializer.Deserialize<CargoWiseReportConfiguration>(template.Configuration);
        
        // Apply parameter values
        if (configuration.Parameters != null)
        {
            foreach (var parameter in configuration.Parameters)
            {
                if (parameters.ContainsKey(parameter.Name))
                {
                    parameter.Value = parameters[parameter.Name];
                }
            }
        }
        
        // Apply parameter values to filters
        if (configuration.Filters != null)
        {
            foreach (var filter in configuration.Filters)
            {
                if (filter.Value is string stringValue && stringValue.StartsWith("@"))
                {
                    var parameterName = stringValue.Substring(1);
                    if (parameters.ContainsKey(parameterName))
                    {
                        filter.Value = parameters[parameterName];
                    }
                }
            }
        }
        
        return new CreateReportRequest
        {
            ReportId = Guid.NewGuid().ToString(),
            ReportName = configuration.ReportName,
            Description = configuration.Description,
            Type = configuration.Type,
            DataSource = configuration.DataSource,
            Columns = configuration.Columns,
            Filters = configuration.Filters,
            GroupBy = configuration.GroupBy,
            OrderBy = configuration.OrderBy,
            Limit = configuration.Limit,
            Offset = configuration.Offset,
            Format = configuration.Format
        };
    }
}

Dashboard Design and Implementation

Dashboard Framework

C# Dashboard Builder:

public class CargoWiseDashboardBuilder
{
    private readonly ILogger<CargoWiseDashboardBuilder> _logger;
    private readonly IDashboardRepository _dashboardRepository;
    private readonly IWidgetService _widgetService;
    private readonly IDataQueryService _dataQueryService;
    
    public CargoWiseDashboardBuilder(
        ILogger<CargoWiseDashboardBuilder> logger,
        IDashboardRepository dashboardRepository,
        IWidgetService widgetService,
        IDataQueryService dataQueryService)
    {
        _logger = logger;
        _dashboardRepository = dashboardRepository;
        _widgetService = widgetService;
        _dataQueryService = dataQueryService;
    }
    
    public async Task<DashboardResult> CreateDashboard(CreateDashboardRequest request)
    {
        var result = new DashboardResult
        {
            DashboardId = request.DashboardId,
            StartTime = DateTime.UtcNow,
            Status = DashboardStatus.InProgress
        };
        
        try
        {
            _logger.LogInformation("Creating dashboard: {DashboardId}", request.DashboardId);
            
            // Validate dashboard configuration
            var validationResult = await ValidateDashboardConfiguration(request);
            if (!validationResult.IsValid)
            {
                result.Status = DashboardStatus.ValidationFailed;
                result.ErrorMessage = validationResult.ErrorMessage;
                return result;
            }
            
            // Create dashboard
            var dashboard = new Dashboard
            {
                Id = request.DashboardId,
                Name = request.Name,
                Description = request.Description,
                Layout = request.Layout,
                Widgets = new List<Widget>(),
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };
            
            // Create widgets
            foreach (var widgetConfig in request.Widgets)
            {
                var widget = await CreateWidget(widgetConfig);
                dashboard.Widgets.Add(widget);
            }
            
            // Save dashboard
            await _dashboardRepository.CreateAsync(dashboard);
            
            result.Status = DashboardStatus.Completed;
            result.Dashboard = dashboard;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            _logger.LogInformation("Dashboard created successfully: {DashboardId} with {WidgetCount} widgets in {Duration}", 
                request.DashboardId, dashboard.Widgets.Count, result.Duration);
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating dashboard: {DashboardId}", request.DashboardId);
            
            result.Status = DashboardStatus.Failed;
            result.ErrorMessage = ex.Message;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            return result;
        }
    }
    
    private async Task<Widget> CreateWidget(WidgetConfiguration config)
    {
        var widget = new Widget
        {
            Id = Guid.NewGuid().ToString(),
            Type = config.Type,
            Title = config.Title,
            Position = config.Position,
            Size = config.Size,
            Configuration = config.Configuration,
            DataSource = config.DataSource,
            RefreshInterval = config.RefreshInterval,
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };
        
        // Initialize widget data
        widget.Data = await _widgetService.InitializeWidgetData(widget);
        
        return widget;
    }
}

Widget Types and Implementation

C# Widget Service:

public class CargoWiseWidgetService
{
    private readonly ILogger<CargoWiseWidgetService> _logger;
    private readonly IDataQueryService _dataQueryService;
    private readonly IChartService _chartService;
    private readonly ITableService _tableService;
    
    public CargoWiseWidgetService(
        ILogger<CargoWiseWidgetService> logger,
        IDataQueryService dataQueryService,
        IChartService chartService,
        ITableService tableService)
    {
        _logger = logger;
        _dataQueryService = dataQueryService;
        _chartService = chartService;
        _tableService = tableService;
    }
    
    public async Task<WidgetData> InitializeWidgetData(Widget widget)
    {
        try
        {
            _logger.LogDebug("Initializing widget data: {WidgetId}", widget.Id);
            
            switch (widget.Type)
            {
                case WidgetType.Chart:
                    return await InitializeChartWidget(widget);
                case WidgetType.Table:
                    return await InitializeTableWidget(widget);
                case WidgetType.KPI:
                    return await InitializeKpiWidget(widget);
                case WidgetType.Gauge:
                    return await InitializeGaugeWidget(widget);
                case WidgetType.Map:
                    return await InitializeMapWidget(widget);
                default:
                    throw new NotSupportedException($"Widget type not supported: {widget.Type}");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error initializing widget data: {WidgetId}", widget.Id);
            throw;
        }
    }
    
    private async Task<WidgetData> InitializeChartWidget(Widget widget)
    {
        var data = await _dataQueryService.ExecuteQueryAsync(widget.DataSource);
        
        var chartData = new ChartData
        {
            Type = widget.Configuration.ChartType,
            Labels = data.Labels,
            Datasets = data.Datasets,
            Options = widget.Configuration.ChartOptions
        };
        
        return new WidgetData
        {
            Type = WidgetDataType.Chart,
            Data = chartData,
            LastUpdated = DateTime.UtcNow
        };
    }
    
    private async Task<WidgetData> InitializeTableWidget(Widget widget)
    {
        var data = await _dataQueryService.ExecuteQueryAsync(widget.DataSource);
        
        var tableData = new TableData
        {
            Columns = data.Columns,
            Rows = data.Rows,
            TotalCount = data.TotalCount,
            PageSize = widget.Configuration.PageSize,
            CurrentPage = 1
        };
        
        return new WidgetData
        {
            Type = WidgetDataType.Table,
            Data = tableData,
            LastUpdated = DateTime.UtcNow
        };
    }
    
    private async Task<WidgetData> InitializeKpiWidget(Widget widget)
    {
        var data = await _dataQueryService.ExecuteQueryAsync(widget.DataSource);
        
        var kpiData = new KpiData
        {
            Value = data.Value,
            PreviousValue = data.PreviousValue,
            Change = data.Change,
            ChangePercentage = data.ChangePercentage,
            Trend = data.Trend,
            Format = widget.Configuration.Format
        };
        
        return new WidgetData
        {
            Type = WidgetDataType.KPI,
            Data = kpiData,
            LastUpdated = DateTime.UtcNow
        };
    }
}

Data Visualization

Chart Types and Configuration

C# Chart Service:

public class CargoWiseChartService
{
    private readonly ILogger<CargoWiseChartService> _logger;
    private readonly IChartRenderer _chartRenderer;
    
    public CargoWiseChartService(ILogger<CargoWiseChartService> logger, IChartRenderer chartRenderer)
    {
        _logger = logger;
        _chartRenderer = chartRenderer;
    }
    
    public async Task<ChartResult> CreateChart(ChartConfiguration config)
    {
        var result = new ChartResult
        {
            ChartId = config.ChartId,
            StartTime = DateTime.UtcNow,
            Status = ChartStatus.InProgress
        };
        
        try
        {
            _logger.LogInformation("Creating chart: {ChartId}", config.ChartId);
            
            // Validate chart configuration
            var validationResult = await ValidateChartConfiguration(config);
            if (!validationResult.IsValid)
            {
                result.Status = ChartStatus.ValidationFailed;
                result.ErrorMessage = validationResult.ErrorMessage;
                return result;
            }
            
            // Process chart data
            var processedData = await ProcessChartData(config);
            
            // Render chart
            var renderedChart = await _chartRenderer.RenderChart(processedData, config);
            
            result.Status = ChartStatus.Completed;
            result.ChartData = renderedChart;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            _logger.LogInformation("Chart created successfully: {ChartId} in {Duration}", 
                config.ChartId, result.Duration);
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating chart: {ChartId}", config.ChartId);
            
            result.Status = ChartStatus.Failed;
            result.ErrorMessage = ex.Message;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            return result;
        }
    }
    
    private async Task<ChartData> ProcessChartData(ChartConfiguration config)
    {
        var chartData = new ChartData
        {
            Type = config.Type,
            Labels = new List<string>(),
            Datasets = new List<ChartDataset>(),
            Options = config.Options
        };
        
        switch (config.Type)
        {
            case ChartType.Line:
                chartData = await ProcessLineChartData(config);
                break;
            case ChartType.Bar:
                chartData = await ProcessBarChartData(config);
                break;
            case ChartType.Pie:
                chartData = await ProcessPieChartData(config);
                break;
            case ChartType.Doughnut:
                chartData = await ProcessDoughnutChartData(config);
                break;
            case ChartType.Area:
                chartData = await ProcessAreaChartData(config);
                break;
            case ChartType.Scatter:
                chartData = await ProcessScatterChartData(config);
                break;
            default:
                throw new NotSupportedException($"Chart type not supported: {config.Type}");
        }
        
        return chartData;
    }
    
    private async Task<ChartData> ProcessLineChartData(ChartConfiguration config)
    {
        var chartData = new ChartData
        {
            Type = ChartType.Line,
            Labels = config.DataSource.Labels,
            Datasets = new List<ChartDataset>()
        };
        
        foreach (var series in config.DataSource.Series)
        {
            var dataset = new ChartDataset
            {
                Label = series.Label,
                Data = series.Data,
                BorderColor = series.Color,
                BackgroundColor = series.Color + "20", // Add transparency
                BorderWidth = 2,
                Fill = false,
                Tension = 0.4
            };
            
            chartData.Datasets.Add(dataset);
        }
        
        return chartData;
    }
    
    private async Task<ChartData> ProcessBarChartData(ChartConfiguration config)
    {
        var chartData = new ChartData
        {
            Type = ChartType.Bar,
            Labels = config.DataSource.Labels,
            Datasets = new List<ChartDataset>()
        };
        
        foreach (var series in config.DataSource.Series)
        {
            var dataset = new ChartDataset
            {
                Label = series.Label,
                Data = series.Data,
                BackgroundColor = series.Color,
                BorderColor = series.Color,
                BorderWidth = 1
            };
            
            chartData.Datasets.Add(dataset);
        }
        
        return chartData;
    }
}

KPI Tracking and Metrics

C# KPI Service:

public class CargoWiseKpiService
{
    private readonly ILogger<CargoWiseKpiService> _logger;
    private readonly IKpiRepository _kpiRepository;
    private readonly IDataQueryService _dataQueryService;
    
    public CargoWiseKpiService(
        ILogger<CargoWiseKpiService> logger,
        IKpiRepository kpiRepository,
        IDataQueryService dataQueryService)
    {
        _logger = logger;
        _kpiRepository = kpiRepository;
        _dataQueryService = dataQueryService;
    }
    
    public async Task<KpiResult> CalculateKpi(string kpiId, KpiConfiguration config)
    {
        var result = new KpiResult
        {
            KpiId = kpiId,
            StartTime = DateTime.UtcNow,
            Status = KpiStatus.InProgress
        };
        
        try
        {
            _logger.LogInformation("Calculating KPI: {KpiId}", kpiId);
            
            // Execute KPI query
            var data = await _dataQueryService.ExecuteQueryAsync(config.DataSource);
            
            // Calculate KPI value
            var kpiValue = await CalculateKpiValue(data, config);
            
            // Calculate trend
            var trend = await CalculateKpiTrend(kpiId, kpiValue, config);
            
            // Calculate change
            var change = await CalculateKpiChange(kpiId, kpiValue, config);
            
            result.Status = KpiStatus.Completed;
            result.Value = kpiValue;
            result.Trend = trend;
            result.Change = change;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            _logger.LogInformation("KPI calculated successfully: {KpiId} = {Value} in {Duration}", 
                kpiId, kpiValue, result.Duration);
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error calculating KPI: {KpiId}", kpiId);
            
            result.Status = KpiStatus.Failed;
            result.ErrorMessage = ex.Message;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            return result;
        }
    }
    
    private async Task<decimal> CalculateKpiValue(QueryResult data, KpiConfiguration config)
    {
        switch (config.CalculationType)
        {
            case KpiCalculationType.Sum:
                return data.Rows.Sum(r => Convert.ToDecimal(r[config.ValueColumn]));
            case KpiCalculationType.Average:
                return data.Rows.Average(r => Convert.ToDecimal(r[config.ValueColumn]));
            case KpiCalculationType.Count:
                return data.Rows.Count;
            case KpiCalculationType.Max:
                return data.Rows.Max(r => Convert.ToDecimal(r[config.ValueColumn]));
            case KpiCalculationType.Min:
                return data.Rows.Min(r => Convert.ToDecimal(r[config.ValueColumn]));
            case KpiCalculationType.Custom:
                return await CalculateCustomKpi(data, config);
            default:
                throw new NotSupportedException($"KPI calculation type not supported: {config.CalculationType}");
        }
    }
    
    private async Task<KpiTrend> CalculateKpiTrend(string kpiId, decimal currentValue, KpiConfiguration config)
    {
        // Get historical values
        var historicalValues = await _kpiRepository.GetHistoricalValuesAsync(kpiId, config.TrendPeriod);
        
        if (historicalValues.Count < 2)
        {
            return KpiTrend.Stable;
        }
        
        // Calculate trend
        var recentValues = historicalValues.TakeLast(7).ToList();
        var olderValues = historicalValues.SkipLast(7).ToList();
        
        if (recentValues.Count < 2 || olderValues.Count < 2)
        {
            return KpiTrend.Stable;
        }
        
        var recentAverage = recentValues.Average();
        var olderAverage = olderValues.Average();
        
        var changePercentage = ((recentAverage - olderAverage) / olderAverage) * 100;
        
        if (changePercentage > 5)
        {
            return KpiTrend.Up;
        }
        else if (changePercentage < -5)
        {
            return KpiTrend.Down;
        }
        else
        {
            return KpiTrend.Stable;
        }
    }
}

Scheduled Reports and Automation

Report Scheduling System

C# Report Scheduler:

public class CargoWiseReportScheduler
{
    private readonly ILogger<CargoWiseReportScheduler> _logger;
    private readonly IReportRepository _reportRepository;
    private readonly IReportBuilder _reportBuilder;
    private readonly IEmailService _emailService;
    private readonly Timer _schedulerTimer;
    
    public CargoWiseReportScheduler(
        ILogger<CargoWiseReportScheduler> logger,
        IReportRepository reportRepository,
        IReportBuilder reportBuilder,
        IEmailService emailService)
    {
        _logger = logger;
        _reportRepository = reportRepository;
        _reportBuilder = reportBuilder;
        _emailService = emailService;
        
        // Set up scheduler timer (check every minute)
        _schedulerTimer = new Timer(ProcessScheduledReports, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
    }
    
    private async void ProcessScheduledReports(object state)
    {
        try
        {
            await ProcessDueReports();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing scheduled reports");
        }
    }
    
    public async Task<ScheduleResult> ScheduleReport(ScheduleReportRequest request)
    {
        var result = new ScheduleResult
        {
            StartTime = DateTime.UtcNow,
            Status = ScheduleStatus.InProgress
        };
        
        try
        {
            _logger.LogInformation("Scheduling report: {ReportId}", request.ReportId);
            
            // Create schedule
            var schedule = new ReportSchedule
            {
                Id = Guid.NewGuid().ToString(),
                ReportId = request.ReportId,
                ScheduleType = request.ScheduleType,
                ScheduleExpression = request.ScheduleExpression,
                NextRunTime = CalculateNextRunTime(request.ScheduleType, request.ScheduleExpression),
                IsActive = true,
                CreatedAt = DateTime.UtcNow,
                UpdatedAt = DateTime.UtcNow
            };
            
            // Save schedule
            await _reportRepository.CreateScheduleAsync(schedule);
            
            result.Status = ScheduleStatus.Scheduled;
            result.ScheduleId = schedule.Id;
            result.NextRunTime = schedule.NextRunTime;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            _logger.LogInformation("Report scheduled successfully: {ReportId} -> {ScheduleId}", 
                request.ReportId, schedule.Id);
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error scheduling report: {ReportId}", request.ReportId);
            
            result.Status = ScheduleStatus.Failed;
            result.ErrorMessage = ex.Message;
            result.EndTime = DateTime.UtcNow;
            result.Duration = result.EndTime - result.StartTime;
            
            return result;
        }
    }
    
    private async Task ProcessDueReports()
    {
        var dueSchedules = await _reportRepository.GetDueSchedulesAsync();
        
        foreach (var schedule in dueSchedules)
        {
            try
            {
                await ProcessScheduledReport(schedule);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing scheduled report: {ScheduleId}", schedule.Id);
            }
        }
    }
    
    private async Task ProcessScheduledReport(ReportSchedule schedule)
    {
        _logger.LogInformation("Processing scheduled report: {ScheduleId}", schedule.Id);
        
        // Get report configuration
        var reportConfig = await _reportRepository.GetReportConfigurationAsync(schedule.ReportId);
        
        // Generate report
        var reportResult = await _reportBuilder.CreateReport(reportConfig);
        
        if (reportResult.Status == ReportStatus.Completed)
        {
            // Send report via email
            await _emailService.SendReportEmailAsync(schedule.EmailRecipients, reportResult);
            
            // Update next run time
            schedule.NextRunTime = CalculateNextRunTime(schedule.ScheduleType, schedule.ScheduleExpression);
            await _reportRepository.UpdateScheduleAsync(schedule);
            
            _logger.LogInformation("Scheduled report processed successfully: {ScheduleId}", schedule.Id);
        }
        else
        {
            _logger.LogError("Scheduled report generation failed: {ScheduleId}, Status: {Status}", 
                schedule.Id, reportResult.Status);
        }
    }
}

Conclusion

CargoWise reporting and analytics provide powerful tools for gaining insights into freight forwarding operations and making data-driven decisions. By implementing the reporting strategies and techniques outlined in this guide, you can create comprehensive reporting solutions that drive business growth and efficiency.

Key Takeaways:

  1. Start with Requirements: Define clear reporting requirements and user needs
  2. Use Templates: Leverage report templates for consistency and efficiency
  3. Design Effective Dashboards: Create intuitive and informative dashboards
  4. Implement Automation: Use scheduled reports for regular insights
  5. Monitor Performance: Track report usage and performance metrics

Next Steps:

  1. Assess Reporting Needs and identify key stakeholders
  2. Design Report Architecture and data models
  3. Create Report Templates for common business needs
  4. Build Interactive Dashboards for real-time insights
  5. Implement Automation for scheduled reporting

For more CargoWise reporting guidance and implementation support, explore our CargoWise Integration Services or contact our team for personalized consulting.

FAQ

Q: How do I create custom reports in CargoWise? A: Use the CargoWise Report Builder to create custom reports by defining data sources, columns, filters, and formatting. You can also use report templates for common business needs.

Q: What types of charts and visualizations are available in CargoWise? A: CargoWise supports various chart types including line charts, bar charts, pie charts, doughnut charts, area charts, and scatter plots, as well as KPI widgets and tables.

Q: How can I schedule reports to run automatically? A: Use the CargoWise Report Scheduler to set up automated report generation with various schedule types including daily, weekly, monthly, and custom intervals.

Q: What data sources can I use for CargoWise reports? A: CargoWise reports can use data from shipment records, customer data, financial information, operational metrics, and external data sources through integrations.

Q: How do I create interactive dashboards in CargoWise? A: Use the CargoWise Dashboard Builder to create interactive dashboards with widgets, charts, KPIs, and real-time data updates. Configure refresh intervals and user permissions.

Related posts