1291 lines
54 KiB
C#
1291 lines
54 KiB
C#
using Zentral.API.Services.Reports;
|
|
using Zentral.Domain.Entities;
|
|
using Zentral.Infrastructure.Data;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Zentral.API.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/reports")]
|
|
public class ReportsController : ControllerBase
|
|
{
|
|
private readonly ReportGeneratorService _reportGenerator;
|
|
private readonly ZentralDbContext _context;
|
|
|
|
public ReportsController(ReportGeneratorService reportGenerator, ZentralDbContext context)
|
|
{
|
|
_reportGenerator = reportGenerator;
|
|
_context = context;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Genera un PDF da un template con i dati forniti
|
|
/// </summary>
|
|
[HttpPost("generate")]
|
|
public async Task<IActionResult> Generate([FromBody] GenerateReportRequest request)
|
|
{
|
|
try
|
|
{
|
|
var pdf = await _reportGenerator.GeneratePdfAsync(request.TemplateId, request.DataContext);
|
|
return File(pdf, "application/pdf", "report.pdf");
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return NotFound(ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest($"Error generating report: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Genera il PDF di un evento usando il template predefinito o specificato
|
|
/// </summary>
|
|
[HttpGet("evento/{eventoId}")]
|
|
public async Task<IActionResult> GenerateEvento(int eventoId, [FromQuery] int? templateId = null)
|
|
{
|
|
try
|
|
{
|
|
var pdf = await _reportGenerator.GenerateEventoPdfAsync(eventoId, templateId);
|
|
return File(pdf, "application/pdf", $"evento_{eventoId}.pdf");
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return NotFound(ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest($"Error generating report: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Debug endpoint to test binding resolution
|
|
/// </summary>
|
|
[HttpPost("debug-binding")]
|
|
public async Task<IActionResult> DebugBinding([FromBody] DebugBindingRequest request)
|
|
{
|
|
var dataContext = await BuildDataContextAsync(request.DataSources);
|
|
|
|
var results = new Dictionary<string, object?>();
|
|
results["dataContextKeys"] = dataContext.Keys.ToList();
|
|
|
|
foreach (var kvp in dataContext)
|
|
{
|
|
var type = kvp.Value?.GetType();
|
|
results[$"dataset_{kvp.Key}_type"] = type?.Name;
|
|
|
|
// Try to get the property
|
|
if (kvp.Value != null && !string.IsNullOrEmpty(request.PropertyName))
|
|
{
|
|
var prop = type?.GetProperty(request.PropertyName,
|
|
System.Reflection.BindingFlags.Public |
|
|
System.Reflection.BindingFlags.Instance |
|
|
System.Reflection.BindingFlags.IgnoreCase);
|
|
|
|
results[$"dataset_{kvp.Key}_property_found"] = prop != null;
|
|
if (prop != null)
|
|
{
|
|
var value = prop.GetValue(kvp.Value);
|
|
results[$"dataset_{kvp.Key}_value"] = value?.ToString();
|
|
results[$"dataset_{kvp.Key}_value_type"] = value?.GetType().Name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Ok(results);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Genera un'anteprima del PDF con dati reali
|
|
/// </summary>
|
|
[HttpPost("preview")]
|
|
public async Task<IActionResult> Preview([FromBody] PreviewReportRequest request)
|
|
{
|
|
try
|
|
{
|
|
var dataContext = await BuildDataContextAsync(request.DataSources);
|
|
var pdf = await _reportGenerator.GeneratePdfAsync(request.TemplateId, dataContext);
|
|
return File(pdf, "application/pdf");
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return NotFound(ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BadRequest($"Error generating preview: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene la lista dei tipi di dataset disponibili (inclusi Virtual Dataset)
|
|
/// </summary>
|
|
[HttpGet("datasets")]
|
|
public async Task<ActionResult<List<DatasetTypeDto>>> GetAvailableDatasets()
|
|
{
|
|
var datasets = new List<DatasetTypeDto>
|
|
{
|
|
// Dataset principali
|
|
new() { Id = "evento", Name = "Evento", Description = "Dati evento completi con cliente, location, ospiti, costi e risorse", Icon = "event", Category = "Principale" },
|
|
new() { Id = "cliente", Name = "Cliente", Description = "Anagrafica clienti completa", Icon = "people", Category = "Principale" },
|
|
new() { Id = "location", Name = "Location", Description = "Sedi e location eventi", Icon = "place", Category = "Principale" },
|
|
new() { Id = "articolo", Name = "Articolo", Description = "Catalogo articoli e materiali", Icon = "inventory", Category = "Principale" },
|
|
new() { Id = "risorsa", Name = "Risorsa", Description = "Staff e personale", Icon = "person", Category = "Principale" },
|
|
|
|
// Dataset di lookup/configurazione
|
|
new() { Id = "tipoEvento", Name = "Tipo Evento", Description = "Tipologie di evento (matrimonio, compleanno, etc.)", Icon = "category", Category = "Configurazione" },
|
|
new() { Id = "tipoOspite", Name = "Tipo Ospite", Description = "Tipologie di ospiti (adulti, bambini, etc.)", Icon = "groups", Category = "Configurazione" },
|
|
new() { Id = "categoria", Name = "Categoria Articoli", Description = "Categorie articoli con coefficienti di calcolo", Icon = "folder", Category = "Configurazione" },
|
|
new() { Id = "tipoRisorsa", Name = "Tipo Risorsa", Description = "Tipologie di risorse (cameriere, cuoco, etc.)", Icon = "badge", Category = "Configurazione" },
|
|
new() { Id = "tipoMateriale", Name = "Tipo Materiale", Description = "Tipologie di materiali", Icon = "category", Category = "Configurazione" },
|
|
|
|
// Dataset lista (per report con elenchi)
|
|
new() { Id = "listaEventi", Name = "Lista Eventi", Description = "Elenco eventi per report multipli", Icon = "list", Category = "Liste" },
|
|
new() { Id = "listaArticoli", Name = "Lista Articoli", Description = "Elenco articoli per catalogo", Icon = "list", Category = "Liste" },
|
|
};
|
|
|
|
// Aggiungi Virtual Dataset dal database
|
|
var virtualDatasets = await _context.VirtualDatasets
|
|
.Where(vd => vd.Attivo)
|
|
.OrderBy(vd => vd.DisplayName)
|
|
.Select(vd => new DatasetTypeDto
|
|
{
|
|
Id = $"virtual:{vd.Nome}", // Prefisso per identificare i virtual dataset
|
|
Name = vd.DisplayName,
|
|
Description = vd.Descrizione ?? "Dataset virtuale personalizzato",
|
|
Icon = vd.Icon,
|
|
Category = vd.Categoria,
|
|
IsVirtual = true
|
|
})
|
|
.ToListAsync();
|
|
|
|
datasets.AddRange(virtualDatasets);
|
|
|
|
return datasets;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene le categorie dei dataset (incluse quelle dei Virtual Dataset)
|
|
/// </summary>
|
|
[HttpGet("datasets/categories")]
|
|
public async Task<ActionResult<List<string>>> GetDatasetCategories()
|
|
{
|
|
var baseCategories = new List<string> { "Principale", "Configurazione", "Liste" };
|
|
|
|
// Aggiungi categorie dai Virtual Dataset
|
|
var virtualCategories = await _context.VirtualDatasets
|
|
.Where(vd => vd.Attivo)
|
|
.Select(vd => vd.Categoria)
|
|
.Distinct()
|
|
.ToListAsync();
|
|
|
|
foreach (var cat in virtualCategories)
|
|
{
|
|
if (!baseCategories.Contains(cat))
|
|
baseCategories.Add(cat);
|
|
}
|
|
|
|
return baseCategories;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene lo schema dei dati per un dataset (inclusi Virtual Dataset)
|
|
/// </summary>
|
|
[HttpGet("schema/{datasetId}")]
|
|
public async Task<ActionResult<DataSchemaDto>> GetSchema(string datasetId)
|
|
{
|
|
// Check se è un Virtual Dataset
|
|
if (datasetId.StartsWith("virtual:"))
|
|
{
|
|
var virtualName = datasetId.Substring("virtual:".Length);
|
|
var schema = await GetVirtualDatasetSchemaAsync(virtualName);
|
|
if (schema == null)
|
|
return NotFound($"Virtual Dataset '{virtualName}' not found");
|
|
return schema;
|
|
}
|
|
|
|
var staticSchema = GetSchemaForDataset(datasetId);
|
|
if (staticSchema == null)
|
|
return NotFound($"Dataset '{datasetId}' not found");
|
|
return staticSchema;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene la lista delle entità disponibili per un dataset (per la preview)
|
|
/// </summary>
|
|
[HttpGet("datasets/{datasetId}/entities")]
|
|
public async Task<ActionResult<List<EntityListItemDto>>> GetEntitiesForDataset(
|
|
string datasetId,
|
|
[FromQuery] string? search = null,
|
|
[FromQuery] int limit = 50,
|
|
[FromQuery] int offset = 0)
|
|
{
|
|
// Virtual Dataset - restituisce le entità del dataset primario
|
|
if (datasetId.StartsWith("virtual:"))
|
|
{
|
|
var virtualName = datasetId.Substring("virtual:".Length);
|
|
return await GetVirtualDatasetEntities(virtualName, search, limit, offset);
|
|
}
|
|
|
|
var entities = datasetId.ToLower() switch
|
|
{
|
|
"evento" => await GetEventiEntities(search, limit, offset),
|
|
"cliente" => await GetClientiEntities(search, limit, offset),
|
|
"location" => await GetLocationEntities(search, limit, offset),
|
|
"articolo" => await GetArticoliEntities(search, limit, offset),
|
|
"risorsa" => await GetRisorseEntities(search, limit, offset),
|
|
"tipoevento" => await GetTipiEventoEntities(search, limit, offset),
|
|
"tipoospite" => await GetTipiOspiteEntities(search, limit, offset),
|
|
"categoria" => await GetCategorieEntities(search, limit, offset),
|
|
"tiporisorsa" => await GetTipiRisorsaEntities(search, limit, offset),
|
|
"tipomateriale" => await GetTipiMaterialeEntities(search, limit, offset),
|
|
_ => new List<EntityListItemDto>()
|
|
};
|
|
|
|
return entities;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Conta le entità disponibili per un dataset
|
|
/// </summary>
|
|
[HttpGet("datasets/{datasetId}/count")]
|
|
public async Task<ActionResult<int>> GetEntityCount(string datasetId, [FromQuery] string? search = null)
|
|
{
|
|
var count = datasetId.ToLower() switch
|
|
{
|
|
"evento" => await CountEventi(search),
|
|
"cliente" => await CountClienti(search),
|
|
"location" => await CountLocation(search),
|
|
"articolo" => await CountArticoli(search),
|
|
"risorsa" => await CountRisorse(search),
|
|
"tipoevento" => await _context.TipiEvento.CountAsync(t => t.Attivo),
|
|
"tipoospite" => await _context.TipiOspite.CountAsync(t => t.Attivo),
|
|
"categoria" => await _context.CodiciCategoria.CountAsync(c => c.Attivo),
|
|
"tiporisorsa" => await _context.TipiRisorsa.CountAsync(t => t.Attivo),
|
|
"tipomateriale" => await _context.TipiMateriale.CountAsync(t => t.Attivo),
|
|
_ => 0
|
|
};
|
|
|
|
return count;
|
|
}
|
|
|
|
#region Entity Queries
|
|
|
|
private async Task<List<EntityListItemDto>> GetEventiEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.Eventi
|
|
.Include(e => e.Cliente)
|
|
.Include(e => e.Location)
|
|
.Include(e => e.TipoEvento)
|
|
.AsQueryable();
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(e =>
|
|
(e.Codice != null && e.Codice.ToLower().Contains(search)) ||
|
|
(e.Cliente != null && e.Cliente.RagioneSociale.ToLower().Contains(search)) ||
|
|
(e.Location != null && e.Location.Nome.ToLower().Contains(search)));
|
|
}
|
|
|
|
return await query
|
|
.OrderByDescending(e => e.DataEvento)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(e => new EntityListItemDto
|
|
{
|
|
Id = e.Id,
|
|
Label = $"{e.Codice ?? $"EVT-{e.Id}"} - {e.DataEvento:dd/MM/yyyy}",
|
|
Description = $"{e.Cliente!.RagioneSociale ?? "N/D"} @ {e.Location!.Nome ?? "N/D"}",
|
|
SecondaryInfo = e.TipoEvento != null ? e.TipoEvento.Descrizione : null,
|
|
Status = e.Stato.ToString()
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<int> CountEventi(string? search)
|
|
{
|
|
var query = _context.Eventi.AsQueryable();
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(e =>
|
|
(e.Codice != null && e.Codice.ToLower().Contains(search)) ||
|
|
(e.Cliente != null && e.Cliente.RagioneSociale.ToLower().Contains(search)));
|
|
}
|
|
return await query.CountAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetClientiEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.Clienti.Where(c => c.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(c =>
|
|
c.RagioneSociale.ToLower().Contains(search) ||
|
|
(c.Citta != null && c.Citta.ToLower().Contains(search)) ||
|
|
(c.Email != null && c.Email.ToLower().Contains(search)));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(c => c.RagioneSociale)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(c => new EntityListItemDto
|
|
{
|
|
Id = c.Id,
|
|
Label = c.RagioneSociale,
|
|
Description = $"{c.Citta ?? "N/D"} - {c.Telefono ?? "N/D"}",
|
|
SecondaryInfo = c.Email
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<int> CountClienti(string? search)
|
|
{
|
|
var query = _context.Clienti.Where(c => c.Attivo);
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(c => c.RagioneSociale.ToLower().Contains(search));
|
|
}
|
|
return await query.CountAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetLocationEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.Location.Where(l => l.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(l =>
|
|
l.Nome.ToLower().Contains(search) ||
|
|
(l.Citta != null && l.Citta.ToLower().Contains(search)));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(l => l.Nome)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(l => new EntityListItemDto
|
|
{
|
|
Id = l.Id,
|
|
Label = l.Nome,
|
|
Description = $"{l.Citta ?? "N/D"} ({l.Provincia ?? "N/D"})",
|
|
SecondaryInfo = l.DistanzaKm.HasValue ? $"{l.DistanzaKm} km" : null
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<int> CountLocation(string? search)
|
|
{
|
|
var query = _context.Location.Where(l => l.Attivo);
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(l => l.Nome.ToLower().Contains(search));
|
|
}
|
|
return await query.CountAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetArticoliEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.Articoli
|
|
.Include(a => a.Categoria)
|
|
.Where(a => a.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(a =>
|
|
a.Codice.ToLower().Contains(search) ||
|
|
a.Descrizione.ToLower().Contains(search));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(a => a.Descrizione)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(a => new EntityListItemDto
|
|
{
|
|
Id = a.Id,
|
|
Label = $"{a.Codice} - {a.Descrizione}",
|
|
Description = $"Disponibile: {a.QtaDisponibile ?? 0}",
|
|
SecondaryInfo = a.Categoria != null ? a.Categoria.Descrizione : null
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<int> CountArticoli(string? search)
|
|
{
|
|
var query = _context.Articoli.Where(a => a.Attivo);
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(a =>
|
|
a.Codice.ToLower().Contains(search) ||
|
|
a.Descrizione.ToLower().Contains(search));
|
|
}
|
|
return await query.CountAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetRisorseEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.Risorse
|
|
.Include(r => r.TipoRisorsa)
|
|
.Where(r => r.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(r =>
|
|
r.Nome.ToLower().Contains(search) ||
|
|
(r.Cognome != null && r.Cognome.ToLower().Contains(search)));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(r => r.Cognome).ThenBy(r => r.Nome)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(r => new EntityListItemDto
|
|
{
|
|
Id = r.Id,
|
|
Label = $"{r.Nome} {r.Cognome ?? ""}".Trim(),
|
|
Description = r.Telefono ?? "",
|
|
SecondaryInfo = r.TipoRisorsa != null ? r.TipoRisorsa.Descrizione : null
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<int> CountRisorse(string? search)
|
|
{
|
|
var query = _context.Risorse.Where(r => r.Attivo);
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(r =>
|
|
r.Nome.ToLower().Contains(search) ||
|
|
(r.Cognome != null && r.Cognome.ToLower().Contains(search)));
|
|
}
|
|
return await query.CountAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetTipiEventoEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.TipiEvento
|
|
.Include(t => t.TipoPasto)
|
|
.Where(t => t.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(t =>
|
|
t.Codice.ToLower().Contains(search) ||
|
|
t.Descrizione.ToLower().Contains(search));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(t => t.Descrizione)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(t => new EntityListItemDto
|
|
{
|
|
Id = t.Id,
|
|
Label = t.Descrizione,
|
|
Description = $"Codice: {t.Codice}",
|
|
SecondaryInfo = t.TipoPasto != null ? t.TipoPasto.Descrizione : null
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetTipiOspiteEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.TipiOspite.Where(t => t.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(t =>
|
|
t.Codice.ToLower().Contains(search) ||
|
|
t.Descrizione.ToLower().Contains(search));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(t => t.Descrizione)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(t => new EntityListItemDto
|
|
{
|
|
Id = t.Id,
|
|
Label = t.Descrizione,
|
|
Description = $"Codice: {t.Codice}"
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetCategorieEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.CodiciCategoria.Where(c => c.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(c =>
|
|
c.Codice.ToLower().Contains(search) ||
|
|
c.Descrizione.ToLower().Contains(search));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(c => c.Descrizione)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(c => new EntityListItemDto
|
|
{
|
|
Id = c.Id,
|
|
Label = c.Descrizione,
|
|
Description = $"Codice: {c.Codice}",
|
|
SecondaryInfo = $"Coeff: A={c.CoeffA:F2}, B={c.CoeffB:F2}, S={c.CoeffS:F2}"
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetTipiRisorsaEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.TipiRisorsa.Where(t => t.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(t =>
|
|
t.Codice.ToLower().Contains(search) ||
|
|
t.Descrizione.ToLower().Contains(search));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(t => t.Descrizione)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(t => new EntityListItemDto
|
|
{
|
|
Id = t.Id,
|
|
Label = t.Descrizione,
|
|
Description = $"Codice: {t.Codice}"
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
private async Task<List<EntityListItemDto>> GetTipiMaterialeEntities(string? search, int limit, int offset)
|
|
{
|
|
var query = _context.TipiMateriale.Where(t => t.Attivo);
|
|
|
|
if (!string.IsNullOrWhiteSpace(search))
|
|
{
|
|
search = search.ToLower();
|
|
query = query.Where(t =>
|
|
t.Codice.ToLower().Contains(search) ||
|
|
t.Descrizione.ToLower().Contains(search));
|
|
}
|
|
|
|
return await query
|
|
.OrderBy(t => t.Descrizione)
|
|
.Skip(offset)
|
|
.Take(limit)
|
|
.Select(t => new EntityListItemDto
|
|
{
|
|
Id = t.Id,
|
|
Label = t.Descrizione,
|
|
Description = $"Codice: {t.Codice}"
|
|
})
|
|
.ToListAsync();
|
|
}
|
|
|
|
#endregion
|
|
|
|
private async Task<Dictionary<string, object>> BuildDataContextAsync(List<DataSourceSelection> dataSources)
|
|
{
|
|
var context = new Dictionary<string, object>();
|
|
|
|
foreach (var ds in dataSources)
|
|
{
|
|
object? data;
|
|
|
|
// Check se è un Virtual Dataset
|
|
if (ds.DatasetId.StartsWith("virtual:"))
|
|
{
|
|
var virtualName = ds.DatasetId.Substring("virtual:".Length);
|
|
data = await LoadVirtualDatasetAsync(virtualName, ds.EntityId);
|
|
}
|
|
else
|
|
{
|
|
data = await LoadEntityDataAsync(ds.DatasetId, ds.EntityId);
|
|
}
|
|
|
|
if (data != null)
|
|
{
|
|
// Usa l'alias se fornito, altrimenti usa l'ID del dataset
|
|
var key = ds.Alias ?? ds.DatasetId;
|
|
// Per i virtual dataset, rimuovi il prefisso "virtual:" dalla chiave
|
|
if (key.StartsWith("virtual:"))
|
|
key = key.Substring("virtual:".Length);
|
|
context[key] = data;
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
private async Task<object?> LoadEntityDataAsync(string datasetId, int entityId)
|
|
{
|
|
return datasetId.ToLower() switch
|
|
{
|
|
"evento" => await _context.Eventi
|
|
.Include(e => e.Cliente)
|
|
.Include(e => e.Location)
|
|
.Include(e => e.TipoEvento).ThenInclude(t => t!.TipoPasto)
|
|
.Include(e => e.DettagliOspiti).ThenInclude(d => d.TipoOspite)
|
|
.Include(e => e.DettagliPrelievo).ThenInclude(d => d.Articolo).ThenInclude(a => a!.Categoria)
|
|
.Include(e => e.DettagliRisorse).ThenInclude(d => d.Risorsa).ThenInclude(r => r!.TipoRisorsa)
|
|
.Include(e => e.Acconti)
|
|
.Include(e => e.AltriCosti)
|
|
.Include(e => e.Degustazioni)
|
|
.FirstOrDefaultAsync(e => e.Id == entityId),
|
|
|
|
"cliente" => await _context.Clienti.FindAsync(entityId),
|
|
"location" => await _context.Location.FindAsync(entityId),
|
|
"articolo" => await _context.Articoli
|
|
.Include(a => a.Categoria)
|
|
.Include(a => a.TipoMateriale)
|
|
.FirstOrDefaultAsync(a => a.Id == entityId),
|
|
"risorsa" => await _context.Risorse
|
|
.Include(r => r.TipoRisorsa)
|
|
.FirstOrDefaultAsync(r => r.Id == entityId),
|
|
"tipoevento" => await _context.TipiEvento
|
|
.Include(t => t.TipoPasto)
|
|
.FirstOrDefaultAsync(t => t.Id == entityId),
|
|
"tipoospite" => await _context.TipiOspite.FindAsync(entityId),
|
|
"categoria" => await _context.CodiciCategoria.FindAsync(entityId),
|
|
"tiporisorsa" => await _context.TipiRisorsa.FindAsync(entityId),
|
|
"tipomateriale" => await _context.TipiMateriale.FindAsync(entityId),
|
|
_ => null
|
|
};
|
|
}
|
|
|
|
#region Virtual Dataset Support
|
|
|
|
/// <summary>
|
|
/// Genera lo schema per un Virtual Dataset basato sulla sua configurazione
|
|
/// </summary>
|
|
private async Task<DataSchemaDto?> GetVirtualDatasetSchemaAsync(string virtualName)
|
|
{
|
|
var virtualDataset = await _context.VirtualDatasets
|
|
.FirstOrDefaultAsync(vd => vd.Nome == virtualName && vd.Attivo);
|
|
|
|
if (virtualDataset == null) return null;
|
|
|
|
var config = System.Text.Json.JsonSerializer.Deserialize<VirtualDatasetConfiguration>(
|
|
virtualDataset.ConfigurationJson,
|
|
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
|
);
|
|
|
|
if (config == null) return null;
|
|
|
|
var fields = new List<DataFieldDto>();
|
|
|
|
// Se ci sono OutputFields definiti, usa quelli
|
|
if (config.OutputFields.Any(f => f.Included))
|
|
{
|
|
foreach (var outputField in config.OutputFields.Where(f => f.Included).OrderBy(f => f.Order))
|
|
{
|
|
var source = config.Sources.FirstOrDefault(s => s.Id == outputField.SourceId);
|
|
if (source == null) continue;
|
|
|
|
// Ottieni lo schema del dataset sorgente per determinare il tipo
|
|
var sourceSchema = GetSchemaForDataset(source.DatasetId);
|
|
var sourceField = sourceSchema?.Fields.FirstOrDefault(f =>
|
|
f.Name.Equals(outputField.FieldName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
fields.Add(new DataFieldDto
|
|
{
|
|
Name = outputField.Alias ?? $"{source.Alias}.{outputField.FieldName}",
|
|
Label = outputField.Label ?? sourceField?.Label ?? outputField.FieldName,
|
|
Type = sourceField?.Type ?? "string",
|
|
Group = outputField.Group ?? source.Alias
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Se non ci sono OutputFields, includi tutti i campi di tutte le sorgenti
|
|
foreach (var source in config.Sources)
|
|
{
|
|
var sourceSchema = GetSchemaForDataset(source.DatasetId);
|
|
if (sourceSchema == null) continue;
|
|
|
|
foreach (var field in sourceSchema.Fields)
|
|
{
|
|
fields.Add(new DataFieldDto
|
|
{
|
|
Name = $"{source.Alias}.{field.Name}",
|
|
Label = $"{source.Alias} - {field.Label}",
|
|
Type = field.Type,
|
|
Group = source.Alias
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return new DataSchemaDto
|
|
{
|
|
EntityType = virtualDataset.DisplayName,
|
|
DatasetId = $"virtual:{virtualDataset.Nome}",
|
|
Fields = fields,
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ottiene le entità disponibili per un Virtual Dataset (basato sul dataset primario)
|
|
/// </summary>
|
|
private async Task<List<EntityListItemDto>> GetVirtualDatasetEntities(string virtualName, string? search, int limit, int offset)
|
|
{
|
|
var virtualDataset = await _context.VirtualDatasets
|
|
.FirstOrDefaultAsync(vd => vd.Nome == virtualName && vd.Attivo);
|
|
|
|
if (virtualDataset == null) return new List<EntityListItemDto>();
|
|
|
|
var config = System.Text.Json.JsonSerializer.Deserialize<VirtualDatasetConfiguration>(
|
|
virtualDataset.ConfigurationJson,
|
|
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
|
);
|
|
|
|
if (config == null) return new List<EntityListItemDto>();
|
|
|
|
// Trova il dataset primario
|
|
var primarySource = config.Sources.FirstOrDefault(s => s.IsPrimary)
|
|
?? config.Sources.FirstOrDefault();
|
|
|
|
if (primarySource == null) return new List<EntityListItemDto>();
|
|
|
|
// Restituisce le entità del dataset primario
|
|
return primarySource.DatasetId.ToLower() switch
|
|
{
|
|
"evento" => await GetEventiEntities(search, limit, offset),
|
|
"cliente" => await GetClientiEntities(search, limit, offset),
|
|
"location" => await GetLocationEntities(search, limit, offset),
|
|
"articolo" => await GetArticoliEntities(search, limit, offset),
|
|
"risorsa" => await GetRisorseEntities(search, limit, offset),
|
|
_ => new List<EntityListItemDto>()
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Carica i dati per un Virtual Dataset applicando JOIN e filtri
|
|
/// </summary>
|
|
private async Task<object?> LoadVirtualDatasetAsync(string virtualName, int primaryEntityId)
|
|
{
|
|
var virtualDataset = await _context.VirtualDatasets
|
|
.FirstOrDefaultAsync(vd => vd.Nome == virtualName && vd.Attivo);
|
|
|
|
if (virtualDataset == null) return null;
|
|
|
|
var config = System.Text.Json.JsonSerializer.Deserialize<VirtualDatasetConfiguration>(
|
|
virtualDataset.ConfigurationJson,
|
|
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
|
);
|
|
|
|
if (config == null) return null;
|
|
|
|
// Carica tutti i dataset sorgente
|
|
var loadedData = new Dictionary<string, object?>();
|
|
|
|
foreach (var source in config.Sources)
|
|
{
|
|
if (source.IsPrimary)
|
|
{
|
|
// Carica l'entità primaria
|
|
loadedData[source.Id] = await LoadEntityDataAsync(source.DatasetId, primaryEntityId);
|
|
}
|
|
}
|
|
|
|
// Applica le relazioni per caricare i dati correlati
|
|
foreach (var relationship in config.Relationships)
|
|
{
|
|
var fromData = loadedData.GetValueOrDefault(relationship.FromSourceId);
|
|
if (fromData == null) continue;
|
|
|
|
var toSource = config.Sources.FirstOrDefault(s => s.Id == relationship.ToSourceId);
|
|
if (toSource == null) continue;
|
|
|
|
// Ottieni il valore della chiave esterna
|
|
var fromKeyValue = GetPropertyValue(fromData, relationship.FromField);
|
|
if (fromKeyValue == null) continue;
|
|
|
|
// Carica l'entità correlata
|
|
if (int.TryParse(fromKeyValue.ToString(), out int relatedId))
|
|
{
|
|
loadedData[relationship.ToSourceId] = await LoadEntityDataAsync(toSource.DatasetId, relatedId);
|
|
}
|
|
}
|
|
|
|
// Costruisci l'oggetto risultante con tutti i dati
|
|
var result = new Dictionary<string, object?>();
|
|
|
|
foreach (var source in config.Sources)
|
|
{
|
|
var data = loadedData.GetValueOrDefault(source.Id);
|
|
if (data != null)
|
|
{
|
|
result[source.Alias] = data;
|
|
}
|
|
}
|
|
|
|
// Se ci sono OutputFields, costruisci un oggetto piatto
|
|
if (config.OutputFields.Any(f => f.Included))
|
|
{
|
|
var flatResult = new Dictionary<string, object?>();
|
|
|
|
foreach (var outputField in config.OutputFields.Where(f => f.Included))
|
|
{
|
|
var source = config.Sources.FirstOrDefault(s => s.Id == outputField.SourceId);
|
|
if (source == null) continue;
|
|
|
|
var sourceData = loadedData.GetValueOrDefault(outputField.SourceId);
|
|
if (sourceData == null) continue;
|
|
|
|
var value = GetPropertyValue(sourceData, outputField.FieldName);
|
|
var fieldName = outputField.Alias ?? $"{source.Alias}_{outputField.FieldName}";
|
|
flatResult[fieldName] = value;
|
|
}
|
|
|
|
return flatResult;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static object? GetPropertyValue(object obj, string propertyName)
|
|
{
|
|
var type = obj.GetType();
|
|
var prop = type.GetProperty(propertyName,
|
|
System.Reflection.BindingFlags.Public |
|
|
System.Reflection.BindingFlags.Instance |
|
|
System.Reflection.BindingFlags.IgnoreCase);
|
|
return prop?.GetValue(obj);
|
|
}
|
|
|
|
#endregion
|
|
|
|
private static DataSchemaDto? GetSchemaForDataset(string datasetId)
|
|
{
|
|
return datasetId.ToLower() switch
|
|
{
|
|
"evento" => GetEventoSchema(),
|
|
"cliente" => GetClienteSchema(),
|
|
"location" => GetLocationSchema(),
|
|
"articolo" => GetArticoloSchema(),
|
|
"risorsa" => GetRisorsaSchema(),
|
|
"tipoevento" => GetTipoEventoSchema(),
|
|
"tipoospite" => GetTipoOspiteSchema(),
|
|
"categoria" => GetCategoriaSchema(),
|
|
"tiporisorsa" => GetTipoRisorsaSchema(),
|
|
"tipomateriale" => GetTipoMaterialeSchema(),
|
|
_ => null
|
|
};
|
|
}
|
|
|
|
#region Schema Definitions
|
|
|
|
private static DataSchemaDto GetEventoSchema() => new()
|
|
{
|
|
EntityType = "Evento",
|
|
DatasetId = "evento",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
// Campi base
|
|
new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice Evento", Group = "Base" },
|
|
new() { Name = "dataEvento", Type = "date", Label = "Data Evento", Group = "Base" },
|
|
new() { Name = "oraInizio", Type = "time", Label = "Ora Inizio", Group = "Base" },
|
|
new() { Name = "oraFine", Type = "time", Label = "Ora Fine", Group = "Base" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione", Group = "Base" },
|
|
new() { Name = "stato", Type = "number", Label = "Stato (0=Scheda, 10=Preventivo, 20=Confermato)", Group = "Base" },
|
|
new() { Name = "confermato", Type = "boolean", Label = "Confermato", Group = "Base" },
|
|
|
|
// Ospiti
|
|
new() { Name = "numeroOspiti", Type = "number", Label = "Numero Ospiti Totale", Group = "Ospiti" },
|
|
new() { Name = "numeroOspitiAdulti", Type = "number", Label = "Ospiti Adulti", Group = "Ospiti" },
|
|
new() { Name = "numeroOspitiBambini", Type = "number", Label = "Ospiti Bambini", Group = "Ospiti" },
|
|
new() { Name = "numeroOspitiSeduti", Type = "number", Label = "Ospiti Seduti", Group = "Ospiti" },
|
|
new() { Name = "numeroOspitiBuffet", Type = "number", Label = "Ospiti Buffet", Group = "Ospiti" },
|
|
|
|
// Economici
|
|
new() { Name = "costoTotale", Type = "currency", Label = "Costo Totale", Group = "Economici" },
|
|
new() { Name = "costoPersona", Type = "currency", Label = "Costo per Persona", Group = "Economici" },
|
|
new() { Name = "totaleAcconti", Type = "currency", Label = "Totale Acconti", Group = "Economici" },
|
|
new() { Name = "saldo", Type = "currency", Label = "Saldo da Pagare", Group = "Economici" },
|
|
new() { Name = "dataScadenzaPreventivo", Type = "date", Label = "Scadenza Preventivo", Group = "Economici" },
|
|
|
|
// Note
|
|
new() { Name = "noteCliente", Type = "string", Label = "Note Cliente", Group = "Note" },
|
|
new() { Name = "noteInterne", Type = "string", Label = "Note Interne", Group = "Note" },
|
|
new() { Name = "noteCucina", Type = "string", Label = "Note Cucina", Group = "Note" },
|
|
new() { Name = "noteAllestimento", Type = "string", Label = "Note Allestimento", Group = "Note" },
|
|
|
|
// Cliente (relazione)
|
|
new() { Name = "cliente.id", Type = "number", Label = "ID Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.ragioneSociale", Type = "string", Label = "Ragione Sociale", Group = "Cliente" },
|
|
new() { Name = "cliente.indirizzo", Type = "string", Label = "Indirizzo Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.cap", Type = "string", Label = "CAP Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.citta", Type = "string", Label = "Città Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.provincia", Type = "string", Label = "Provincia Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.telefono", Type = "string", Label = "Telefono Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.email", Type = "string", Label = "Email Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.pec", Type = "string", Label = "PEC Cliente", Group = "Cliente" },
|
|
new() { Name = "cliente.codiceFiscale", Type = "string", Label = "Codice Fiscale", Group = "Cliente" },
|
|
new() { Name = "cliente.partitaIva", Type = "string", Label = "Partita IVA", Group = "Cliente" },
|
|
new() { Name = "cliente.codiceDestinatario", Type = "string", Label = "Codice SDI", Group = "Cliente" },
|
|
|
|
// Location (relazione)
|
|
new() { Name = "location.id", Type = "number", Label = "ID Location", Group = "Location" },
|
|
new() { Name = "location.nome", Type = "string", Label = "Nome Location", Group = "Location" },
|
|
new() { Name = "location.indirizzo", Type = "string", Label = "Indirizzo Location", Group = "Location" },
|
|
new() { Name = "location.cap", Type = "string", Label = "CAP Location", Group = "Location" },
|
|
new() { Name = "location.citta", Type = "string", Label = "Città Location", Group = "Location" },
|
|
new() { Name = "location.provincia", Type = "string", Label = "Provincia Location", Group = "Location" },
|
|
new() { Name = "location.telefono", Type = "string", Label = "Telefono Location", Group = "Location" },
|
|
new() { Name = "location.email", Type = "string", Label = "Email Location", Group = "Location" },
|
|
new() { Name = "location.referente", Type = "string", Label = "Referente Location", Group = "Location" },
|
|
new() { Name = "location.distanzaKm", Type = "number", Label = "Distanza (km)", Group = "Location" },
|
|
|
|
// Tipo Evento (relazione)
|
|
new() { Name = "tipoEvento.codice", Type = "string", Label = "Codice Tipo Evento", Group = "Tipo Evento" },
|
|
new() { Name = "tipoEvento.descrizione", Type = "string", Label = "Tipo Evento", Group = "Tipo Evento" },
|
|
new() { Name = "tipoEvento.tipoPasto.descrizione", Type = "string", Label = "Tipo Pasto", Group = "Tipo Evento" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>
|
|
{
|
|
new()
|
|
{
|
|
Name = "dettagliOspiti",
|
|
Label = "Dettaglio Ospiti",
|
|
Description = "Breakdown ospiti per tipologia",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "tipoOspite.codice", Type = "string", Label = "Codice Tipo" },
|
|
new() { Name = "tipoOspite.descrizione", Type = "string", Label = "Tipo Ospite" },
|
|
new() { Name = "numero", Type = "number", Label = "Numero" },
|
|
new() { Name = "costoUnitario", Type = "currency", Label = "Costo Unitario" },
|
|
new() { Name = "sconto", Type = "percent", Label = "Sconto %" },
|
|
new() { Name = "totale", Type = "currency", Label = "Totale" },
|
|
new() { Name = "note", Type = "string", Label = "Note" }
|
|
}
|
|
},
|
|
new()
|
|
{
|
|
Name = "altriCosti",
|
|
Label = "Altri Costi",
|
|
Description = "Costi aggiuntivi dell'evento",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
new() { Name = "costoUnitario", Type = "currency", Label = "Costo Unitario" },
|
|
new() { Name = "quantita", Type = "number", Label = "Quantità" },
|
|
new() { Name = "aliquotaIva", Type = "percent", Label = "Aliquota IVA" },
|
|
new() { Name = "totale", Type = "currency", Label = "Totale" }
|
|
}
|
|
},
|
|
new()
|
|
{
|
|
Name = "dettagliRisorse",
|
|
Label = "Risorse Assegnate",
|
|
Description = "Personale assegnato all'evento",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "risorsa.nome", Type = "string", Label = "Nome" },
|
|
new() { Name = "risorsa.cognome", Type = "string", Label = "Cognome" },
|
|
new() { Name = "risorsa.telefono", Type = "string", Label = "Telefono" },
|
|
new() { Name = "risorsa.tipoRisorsa.descrizione", Type = "string", Label = "Ruolo" },
|
|
new() { Name = "oraInizio", Type = "time", Label = "Ora Inizio" },
|
|
new() { Name = "oraFine", Type = "time", Label = "Ora Fine" },
|
|
new() { Name = "oreLavoro", Type = "number", Label = "Ore Lavoro" },
|
|
new() { Name = "costo", Type = "currency", Label = "Costo" },
|
|
new() { Name = "note", Type = "string", Label = "Note" }
|
|
}
|
|
},
|
|
new()
|
|
{
|
|
Name = "acconti",
|
|
Label = "Acconti",
|
|
Description = "Pagamenti anticipati ricevuti",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
new() { Name = "importo", Type = "currency", Label = "Importo" },
|
|
new() { Name = "dataPagamento", Type = "date", Label = "Data Pagamento" },
|
|
new() { Name = "metodoPagamento", Type = "string", Label = "Metodo Pagamento" },
|
|
new() { Name = "note", Type = "string", Label = "Note" }
|
|
}
|
|
},
|
|
new()
|
|
{
|
|
Name = "dettagliPrelievo",
|
|
Label = "Lista Prelievo",
|
|
Description = "Articoli necessari per l'evento",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "articolo.codice", Type = "string", Label = "Codice Articolo" },
|
|
new() { Name = "articolo.descrizione", Type = "string", Label = "Descrizione" },
|
|
new() { Name = "articolo.categoria.descrizione", Type = "string", Label = "Categoria" },
|
|
new() { Name = "articolo.unitaMisura", Type = "string", Label = "U.M." },
|
|
new() { Name = "qtaRichiesta", Type = "number", Label = "Qtà Richiesta" },
|
|
new() { Name = "qtaCalcolata", Type = "number", Label = "Qtà Calcolata" },
|
|
new() { Name = "qtaEffettiva", Type = "number", Label = "Qtà Effettiva" },
|
|
new() { Name = "note", Type = "string", Label = "Note" }
|
|
}
|
|
},
|
|
new()
|
|
{
|
|
Name = "degustazioni",
|
|
Label = "Degustazioni",
|
|
Description = "Degustazioni programmate",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "data", Type = "date", Label = "Data" },
|
|
new() { Name = "ora", Type = "time", Label = "Ora" },
|
|
new() { Name = "numeroPartecipanti", Type = "number", Label = "Partecipanti" },
|
|
new() { Name = "note", Type = "string", Label = "Note" }
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
private static DataSchemaDto GetClienteSchema() => new()
|
|
{
|
|
EntityType = "Cliente",
|
|
DatasetId = "cliente",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
|
|
new() { Name = "ragioneSociale", Type = "string", Label = "Ragione Sociale", Group = "Base" },
|
|
new() { Name = "indirizzo", Type = "string", Label = "Indirizzo", Group = "Indirizzo" },
|
|
new() { Name = "cap", Type = "string", Label = "CAP", Group = "Indirizzo" },
|
|
new() { Name = "citta", Type = "string", Label = "Città", Group = "Indirizzo" },
|
|
new() { Name = "provincia", Type = "string", Label = "Provincia", Group = "Indirizzo" },
|
|
new() { Name = "telefono", Type = "string", Label = "Telefono", Group = "Contatti" },
|
|
new() { Name = "email", Type = "string", Label = "Email", Group = "Contatti" },
|
|
new() { Name = "pec", Type = "string", Label = "PEC", Group = "Contatti" },
|
|
new() { Name = "codiceFiscale", Type = "string", Label = "Codice Fiscale", Group = "Fiscale" },
|
|
new() { Name = "partitaIva", Type = "string", Label = "Partita IVA", Group = "Fiscale" },
|
|
new() { Name = "codiceDestinatario", Type = "string", Label = "Codice Destinatario SDI", Group = "Fiscale" },
|
|
new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetLocationSchema() => new()
|
|
{
|
|
EntityType = "Location",
|
|
DatasetId = "location",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
|
|
new() { Name = "nome", Type = "string", Label = "Nome", Group = "Base" },
|
|
new() { Name = "indirizzo", Type = "string", Label = "Indirizzo", Group = "Indirizzo" },
|
|
new() { Name = "cap", Type = "string", Label = "CAP", Group = "Indirizzo" },
|
|
new() { Name = "citta", Type = "string", Label = "Città", Group = "Indirizzo" },
|
|
new() { Name = "provincia", Type = "string", Label = "Provincia", Group = "Indirizzo" },
|
|
new() { Name = "telefono", Type = "string", Label = "Telefono", Group = "Contatti" },
|
|
new() { Name = "email", Type = "string", Label = "Email", Group = "Contatti" },
|
|
new() { Name = "referente", Type = "string", Label = "Referente", Group = "Contatti" },
|
|
new() { Name = "distanzaKm", Type = "number", Label = "Distanza (km)", Group = "Base" },
|
|
new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetArticoloSchema() => new()
|
|
{
|
|
EntityType = "Articolo",
|
|
DatasetId = "articolo",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice", Group = "Base" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione", Group = "Base" },
|
|
new() { Name = "unitaMisura", Type = "string", Label = "Unità di Misura", Group = "Base" },
|
|
new() { Name = "qtaDisponibile", Type = "number", Label = "Quantità Disponibile", Group = "Quantità" },
|
|
new() { Name = "qtaStdA", Type = "number", Label = "Qtà Standard Adulti", Group = "Quantità" },
|
|
new() { Name = "qtaStdB", Type = "number", Label = "Qtà Standard Buffet", Group = "Quantità" },
|
|
new() { Name = "qtaStdS", Type = "number", Label = "Qtà Standard Seduti", Group = "Quantità" },
|
|
new() { Name = "categoria.codice", Type = "string", Label = "Codice Categoria", Group = "Categoria" },
|
|
new() { Name = "categoria.descrizione", Type = "string", Label = "Categoria", Group = "Categoria" },
|
|
new() { Name = "categoria.coeffA", Type = "number", Label = "Coefficiente Adulti", Group = "Categoria" },
|
|
new() { Name = "categoria.coeffB", Type = "number", Label = "Coefficiente Buffet", Group = "Categoria" },
|
|
new() { Name = "categoria.coeffS", Type = "number", Label = "Coefficiente Seduti", Group = "Categoria" },
|
|
new() { Name = "tipoMateriale.codice", Type = "string", Label = "Codice Tipo Materiale", Group = "Materiale" },
|
|
new() { Name = "tipoMateriale.descrizione", Type = "string", Label = "Tipo Materiale", Group = "Materiale" },
|
|
new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetRisorsaSchema() => new()
|
|
{
|
|
EntityType = "Risorsa",
|
|
DatasetId = "risorsa",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID", Group = "Base" },
|
|
new() { Name = "nome", Type = "string", Label = "Nome", Group = "Base" },
|
|
new() { Name = "cognome", Type = "string", Label = "Cognome", Group = "Base" },
|
|
new() { Name = "telefono", Type = "string", Label = "Telefono", Group = "Contatti" },
|
|
new() { Name = "email", Type = "string", Label = "Email", Group = "Contatti" },
|
|
new() { Name = "tipoRisorsa.codice", Type = "string", Label = "Codice Tipo", Group = "Tipo" },
|
|
new() { Name = "tipoRisorsa.descrizione", Type = "string", Label = "Tipo Risorsa", Group = "Tipo" },
|
|
new() { Name = "note", Type = "string", Label = "Note", Group = "Base" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetTipoEventoSchema() => new()
|
|
{
|
|
EntityType = "Tipo Evento",
|
|
DatasetId = "tipoEvento",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
new() { Name = "tipoPasto.codice", Type = "string", Label = "Codice Tipo Pasto" },
|
|
new() { Name = "tipoPasto.descrizione", Type = "string", Label = "Tipo Pasto" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetTipoOspiteSchema() => new()
|
|
{
|
|
EntityType = "Tipo Ospite",
|
|
DatasetId = "tipoOspite",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetCategoriaSchema() => new()
|
|
{
|
|
EntityType = "Categoria Articoli",
|
|
DatasetId = "categoria",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
new() { Name = "coeffA", Type = "number", Label = "Coefficiente Adulti" },
|
|
new() { Name = "coeffB", Type = "number", Label = "Coefficiente Buffet" },
|
|
new() { Name = "coeffS", Type = "number", Label = "Coefficiente Seduti" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetTipoRisorsaSchema() => new()
|
|
{
|
|
EntityType = "Tipo Risorsa",
|
|
DatasetId = "tipoRisorsa",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
private static DataSchemaDto GetTipoMaterialeSchema() => new()
|
|
{
|
|
EntityType = "Tipo Materiale",
|
|
DatasetId = "tipoMateriale",
|
|
Fields = new List<DataFieldDto>
|
|
{
|
|
new() { Name = "id", Type = "number", Label = "ID" },
|
|
new() { Name = "codice", Type = "string", Label = "Codice" },
|
|
new() { Name = "descrizione", Type = "string", Label = "Descrizione" },
|
|
},
|
|
ChildCollections = new List<DataCollectionDto>()
|
|
};
|
|
|
|
#endregion
|
|
}
|
|
|
|
// DTOs
|
|
public class DebugBindingRequest
|
|
{
|
|
public List<DataSourceSelection> DataSources { get; set; } = new();
|
|
public string? PropertyName { get; set; }
|
|
}
|
|
|
|
public class PreviewReportRequest
|
|
{
|
|
public int TemplateId { get; set; }
|
|
public List<DataSourceSelection> DataSources { get; set; } = new();
|
|
}
|
|
|
|
public class DataSourceSelection
|
|
{
|
|
public string DatasetId { get; set; } = string.Empty;
|
|
public int EntityId { get; set; }
|
|
public string? Alias { get; set; }
|
|
}
|
|
|
|
public class DatasetTypeDto
|
|
{
|
|
public string Id { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
public string Icon { get; set; } = string.Empty;
|
|
public string Category { get; set; } = "Principale";
|
|
public bool IsVirtual { get; set; } = false;
|
|
}
|
|
|
|
public class EntityListItemDto
|
|
{
|
|
public int Id { get; set; }
|
|
public string Label { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
public string? SecondaryInfo { get; set; }
|
|
public string? Status { get; set; }
|
|
}
|
|
|
|
public class DataSchemaDto
|
|
{
|
|
public string EntityType { get; set; } = string.Empty;
|
|
public string DatasetId { get; set; } = string.Empty;
|
|
public List<DataFieldDto> Fields { get; set; } = new();
|
|
public List<DataCollectionDto> ChildCollections { get; set; } = new();
|
|
}
|
|
|
|
public class DataFieldDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Type { get; set; } = "string";
|
|
public string Label { get; set; } = string.Empty;
|
|
public string? Group { get; set; }
|
|
}
|
|
|
|
public class DataCollectionDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Label { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public List<DataFieldDto> Fields { get; set; } = new();
|
|
}
|