refactor: Migrate backend and frontend architecture from a module-based to an app-based structure.
This commit is contained in:
@@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.HR.Controllers;
|
||||
namespace Zentral.API.Apps.HR.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/hr/[controller]")]
|
||||
@@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.HR.Controllers;
|
||||
namespace Zentral.API.Apps.HR.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/hr/[controller]")]
|
||||
@@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.HR.Controllers;
|
||||
namespace Zentral.API.Apps.HR.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/hr/[controller]")]
|
||||
@@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.HR.Controllers;
|
||||
namespace Zentral.API.Apps.HR.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/hr/[controller]")]
|
||||
@@ -3,7 +3,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.HR.Controllers;
|
||||
namespace Zentral.API.Apps.HR.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/hr/[controller]")]
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Modules.Production.Services;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Controllers;
|
||||
namespace Zentral.API.Apps.Production.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/production/bom")]
|
||||
@@ -1,9 +1,9 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Modules.Production.Services;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Services;
|
||||
using Zentral.Domain.Entities.Production;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Controllers;
|
||||
namespace Zentral.API.Apps.Production.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/production/mrp")]
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Modules.Production.Services;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Controllers;
|
||||
namespace Zentral.API.Apps.Production.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/production/cycles")]
|
||||
@@ -1,9 +1,9 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Modules.Production.Services;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Services;
|
||||
using Zentral.Domain.Entities.Production;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Controllers;
|
||||
namespace Zentral.API.Apps.Production.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/production/orders")]
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Modules.Production.Services;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Controllers;
|
||||
namespace Zentral.API.Apps.Production.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/production/work-centers")]
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class BillOfMaterialsDto
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class CreateBillOfMaterialsDto
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class CreateProductionOrderDto
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class MrpConfigurationDto
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class ProductionCycleDto
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Zentral.Domain.Entities.Production;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class ProductionOrderDto
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class UpdateBillOfMaterialsDto
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Zentral.Domain.Entities.Production;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class UpdateProductionOrderDto
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Zentral.API.Modules.Production.Dtos;
|
||||
namespace Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
public class WorkCenterDto
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.Domain.Entities.Production;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Services;
|
||||
namespace Zentral.API.Apps.Production.Services;
|
||||
|
||||
public interface IMrpService
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.Domain.Entities.Production;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Services;
|
||||
namespace Zentral.API.Apps.Production.Services;
|
||||
|
||||
public interface IProductionService
|
||||
{
|
||||
@@ -2,9 +2,9 @@ using Zentral.Domain.Entities.Production;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Zentral.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Services;
|
||||
namespace Zentral.API.Apps.Production.Services;
|
||||
|
||||
public class MrpService : IMrpService
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using Zentral.API.Modules.Production.Dtos;
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Production.Dtos;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Production;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Zentral.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.Production.Services;
|
||||
namespace Zentral.API.Apps.Production.Services;
|
||||
|
||||
public class ProductionService : IProductionService
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Zentral.API.Modules.Purchases.Dtos;
|
||||
using Zentral.API.Modules.Purchases.Services;
|
||||
using Zentral.API.Apps.Purchases.Dtos;
|
||||
using Zentral.API.Apps.Purchases.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Purchases.Controllers;
|
||||
namespace Zentral.API.Apps.Purchases.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/purchases/orders")]
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Zentral.API.Modules.Purchases.Dtos;
|
||||
using Zentral.API.Modules.Purchases.Services;
|
||||
using Zentral.API.Apps.Purchases.Dtos;
|
||||
using Zentral.API.Apps.Purchases.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Purchases.Controllers;
|
||||
namespace Zentral.API.Apps.Purchases.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/purchases/suppliers")]
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Zentral.Domain.Entities.Purchases;
|
||||
|
||||
namespace Zentral.API.Modules.Purchases.Dtos;
|
||||
namespace Zentral.API.Apps.Purchases.Dtos;
|
||||
|
||||
public class PurchaseOrderDto
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Zentral.API.Modules.Purchases.Dtos;
|
||||
namespace Zentral.API.Apps.Purchases.Dtos;
|
||||
|
||||
public class SupplierDto
|
||||
{
|
||||
@@ -2,15 +2,15 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Zentral.API.Modules.Purchases.Dtos;
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Purchases.Dtos;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.API.Services;
|
||||
using Zentral.Domain.Entities.Purchases;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Zentral.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.Purchases.Services;
|
||||
namespace Zentral.API.Apps.Purchases.Services;
|
||||
|
||||
public class PurchaseService
|
||||
{
|
||||
@@ -2,13 +2,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Zentral.API.Modules.Purchases.Dtos;
|
||||
using Zentral.API.Apps.Purchases.Dtos;
|
||||
using Zentral.API.Services;
|
||||
using Zentral.Domain.Entities.Purchases;
|
||||
using Zentral.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.Purchases.Services;
|
||||
namespace Zentral.API.Apps.Purchases.Services;
|
||||
|
||||
public class SupplierService
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.ReportDesigner.Controllers;
|
||||
namespace Zentral.API.Apps.ReportDesigner.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/report-designer/resources")]
|
||||
@@ -4,7 +4,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.ReportDesigner.Controllers;
|
||||
namespace Zentral.API.Apps.ReportDesigner.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/report-designer/templates")]
|
||||
@@ -4,7 +4,7 @@ using Zentral.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.ReportDesigner.Controllers;
|
||||
namespace Zentral.API.Apps.ReportDesigner.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/report-designer/reports")]
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Zentral.API.Modules.Sales.Dtos;
|
||||
using Zentral.API.Modules.Sales.Services;
|
||||
using Zentral.API.Apps.Sales.Dtos;
|
||||
using Zentral.API.Apps.Sales.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Sales.Controllers;
|
||||
namespace Zentral.API.Apps.Sales.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/sales/orders")]
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Zentral.Domain.Entities.Sales;
|
||||
|
||||
namespace Zentral.API.Modules.Sales.Dtos;
|
||||
namespace Zentral.API.Apps.Sales.Dtos;
|
||||
|
||||
public class SalesOrderDto
|
||||
{
|
||||
@@ -2,15 +2,15 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Zentral.API.Modules.Sales.Dtos;
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Sales.Dtos;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.API.Services;
|
||||
using Zentral.Domain.Entities.Sales;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Zentral.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Zentral.API.Modules.Sales.Services;
|
||||
namespace Zentral.API.Apps.Sales.Services;
|
||||
|
||||
public class SalesService
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione delle partite/lotti
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione degli inventari fisici
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione dei seriali/matricole
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione delle giacenze e valorizzazione
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione dei movimenti di magazzino
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione degli articoli di magazzino
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione delle categorie articoli
|
||||
@@ -1,8 +1,8 @@
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Controllers;
|
||||
namespace Zentral.API.Apps.Warehouse.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione dei magazzini
|
||||
@@ -1,6 +1,6 @@
|
||||
using Zentral.Domain.Entities.Warehouse;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Services;
|
||||
namespace Zentral.API.Apps.Warehouse.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Interfaccia servizio principale per il modulo Magazzino
|
||||
@@ -6,7 +6,7 @@ using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
using Zentral.API.Hubs;
|
||||
|
||||
namespace Zentral.API.Modules.Warehouse.Services;
|
||||
namespace Zentral.API.Apps.Warehouse.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Implementazione del servizio principale per il modulo Magazzino
|
||||
@@ -5,87 +5,87 @@ using Microsoft.AspNetCore.Mvc;
|
||||
namespace Zentral.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller per la gestione dei moduli applicativi e delle subscription
|
||||
/// Controller per la gestione delle applicazioni e delle subscription
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ModulesController : ControllerBase
|
||||
public class AppsController : ControllerBase
|
||||
{
|
||||
private readonly ModuleService _moduleService;
|
||||
private readonly ILogger<ModulesController> _logger;
|
||||
private readonly AppService _appService;
|
||||
private readonly ILogger<AppsController> _logger;
|
||||
|
||||
public ModulesController(ModuleService moduleService, ILogger<ModulesController> logger)
|
||||
public AppsController(AppService appService, ILogger<AppsController> logger)
|
||||
{
|
||||
_moduleService = moduleService;
|
||||
_appService = appService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene tutti i moduli disponibili con stato subscription
|
||||
/// Ottiene tutte le applicazioni disponibili con stato subscription
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<ModuleDto>>> GetAllModules()
|
||||
public async Task<ActionResult<List<AppDto>>> GetAllApps()
|
||||
{
|
||||
var modules = await _moduleService.GetAllModulesAsync();
|
||||
return Ok(modules.Select(MapToDto).ToList());
|
||||
var apps = await _appService.GetAllAppsAsync();
|
||||
return Ok(apps.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene solo i moduli attivi (per costruzione menu)
|
||||
/// Ottiene solo le applicazioni attive (per costruzione menu)
|
||||
/// </summary>
|
||||
[HttpGet("active")]
|
||||
public async Task<ActionResult<List<ModuleDto>>> GetActiveModules()
|
||||
public async Task<ActionResult<List<AppDto>>> GetActiveApps()
|
||||
{
|
||||
var modules = await _moduleService.GetActiveModulesAsync();
|
||||
return Ok(modules.Select(MapToDto).ToList());
|
||||
var apps = await _appService.GetActiveAppsAsync();
|
||||
return Ok(apps.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene un modulo specifico per codice
|
||||
/// Ottiene un'applicazione specifica per codice
|
||||
/// </summary>
|
||||
[HttpGet("{code}")]
|
||||
public async Task<ActionResult<ModuleDto>> GetModule(string code)
|
||||
public async Task<ActionResult<AppDto>> GetApp(string code)
|
||||
{
|
||||
var module = await _moduleService.GetModuleByCodeAsync(code);
|
||||
if (module == null)
|
||||
return NotFound(new { message = $"Modulo '{code}' non trovato" });
|
||||
var app = await _appService.GetAppByCodeAsync(code);
|
||||
if (app == null)
|
||||
return NotFound(new { message = $"Applicazione '{code}' non trovata" });
|
||||
|
||||
return Ok(MapToDto(module));
|
||||
return Ok(MapToDto(app));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se un modulo è abilitato
|
||||
/// Verifica se un'applicazione è abilitata
|
||||
/// </summary>
|
||||
[HttpGet("{code}/enabled")]
|
||||
public async Task<ActionResult<ModuleStatusDto>> IsModuleEnabled(string code)
|
||||
public async Task<ActionResult<AppStatusDto>> IsAppEnabled(string code)
|
||||
{
|
||||
var module = await _moduleService.GetModuleByCodeAsync(code);
|
||||
if (module == null)
|
||||
return NotFound(new { message = $"Modulo '{code}' non trovato" });
|
||||
var app = await _appService.GetAppByCodeAsync(code);
|
||||
if (app == null)
|
||||
return NotFound(new { message = $"Applicazione '{code}' non trovata" });
|
||||
|
||||
var isEnabled = await _moduleService.IsModuleEnabledAsync(code);
|
||||
var hasValidSubscription = await _moduleService.HasValidSubscriptionAsync(code);
|
||||
var isEnabled = await _appService.IsAppEnabledAsync(code);
|
||||
var hasValidSubscription = await _appService.HasValidSubscriptionAsync(code);
|
||||
|
||||
return Ok(new ModuleStatusDto
|
||||
return Ok(new AppStatusDto
|
||||
{
|
||||
Code = code,
|
||||
IsEnabled = isEnabled,
|
||||
HasValidSubscription = hasValidSubscription,
|
||||
IsCore = module.IsCore,
|
||||
DaysRemaining = module.Subscription?.GetDaysRemaining(),
|
||||
IsExpiringSoon = module.Subscription?.IsExpiringSoon() ?? false
|
||||
IsCore = app.IsCore,
|
||||
DaysRemaining = app.Subscription?.GetDaysRemaining(),
|
||||
IsExpiringSoon = app.Subscription?.IsExpiringSoon() ?? false
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attiva un modulo
|
||||
/// Attiva un'applicazione
|
||||
/// </summary>
|
||||
[HttpPut("{code}/enable")]
|
||||
public async Task<ActionResult<SubscriptionDto>> EnableModule(string code, [FromBody] EnableModuleRequest request)
|
||||
public async Task<ActionResult<AppSubscriptionDto>> EnableApp(string code, [FromBody] EnableAppRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var subscription = await _moduleService.EnableModuleAsync(
|
||||
var subscription = await _appService.EnableAppAsync(
|
||||
code,
|
||||
request.SubscriptionType,
|
||||
request.StartDate,
|
||||
@@ -107,15 +107,15 @@ public class ModulesController : ControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disattiva un modulo
|
||||
/// Disattiva un'applicazione
|
||||
/// </summary>
|
||||
[HttpPut("{code}/disable")]
|
||||
public async Task<ActionResult> DisableModule(string code)
|
||||
public async Task<ActionResult> DisableApp(string code)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _moduleService.DisableModuleAsync(code);
|
||||
return Ok(new { message = $"Modulo '{code}' disattivato" });
|
||||
await _appService.DisableAppAsync(code);
|
||||
return Ok(new { message = $"Applicazione '{code}' disattivata" });
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
@@ -131,21 +131,21 @@ public class ModulesController : ControllerBase
|
||||
/// Ottiene tutte le subscription
|
||||
/// </summary>
|
||||
[HttpGet("subscriptions")]
|
||||
public async Task<ActionResult<List<SubscriptionDto>>> GetAllSubscriptions()
|
||||
public async Task<ActionResult<List<AppSubscriptionDto>>> GetAllAppSubscriptions()
|
||||
{
|
||||
var subscriptions = await _moduleService.GetAllSubscriptionsAsync();
|
||||
var subscriptions = await _appService.GetAllSubscriptionsAsync();
|
||||
return Ok(subscriptions.Select(MapSubscriptionToDto).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorna la subscription di un modulo
|
||||
/// Aggiorna la subscription di un'applicazione
|
||||
/// </summary>
|
||||
[HttpPut("{code}/subscription")]
|
||||
public async Task<ActionResult<SubscriptionDto>> UpdateSubscription(string code, [FromBody] UpdateSubscriptionRequest request)
|
||||
public async Task<ActionResult<AppSubscriptionDto>> UpdateAppSubscription(string code, [FromBody] UpdateAppSubscriptionRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var subscription = await _moduleService.UpdateSubscriptionAsync(
|
||||
var subscription = await _appService.UpdateSubscriptionAsync(
|
||||
code,
|
||||
request.SubscriptionType,
|
||||
request.EndDate,
|
||||
@@ -165,14 +165,14 @@ public class ModulesController : ControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rinnova la subscription di un modulo
|
||||
/// Rinnova la subscription di un'applicazione
|
||||
/// </summary>
|
||||
[HttpPost("{code}/subscription/renew")]
|
||||
public async Task<ActionResult<SubscriptionDto>> RenewSubscription(string code, [FromBody] RenewSubscriptionRequest? request = null)
|
||||
public async Task<ActionResult<AppSubscriptionDto>> RenewAppSubscription(string code, [FromBody] RenewAppSubscriptionRequest? request = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var subscription = await _moduleService.RenewSubscriptionAsync(code, request?.PaidPrice);
|
||||
var subscription = await _appService.RenewSubscriptionAsync(code, request?.PaidPrice);
|
||||
return Ok(MapSubscriptionToDto(subscription));
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
@@ -186,77 +186,77 @@ public class ModulesController : ControllerBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene i moduli in scadenza
|
||||
/// Ottiene le applicazioni in scadenza
|
||||
/// </summary>
|
||||
[HttpGet("expiring")]
|
||||
public async Task<ActionResult<List<ModuleDto>>> GetExpiringModules([FromQuery] int daysThreshold = 30)
|
||||
public async Task<ActionResult<List<AppDto>>> GetExpiringApps([FromQuery] int daysThreshold = 30)
|
||||
{
|
||||
var modules = await _moduleService.GetExpiringModulesAsync(daysThreshold);
|
||||
return Ok(modules.Select(MapToDto).ToList());
|
||||
var apps = await _appService.GetExpiringAppsAsync(daysThreshold);
|
||||
return Ok(apps.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inizializza i moduli di default (per setup iniziale)
|
||||
/// Inizializza le applicazioni di default (per setup iniziale)
|
||||
/// </summary>
|
||||
[HttpPost("seed")]
|
||||
public async Task<ActionResult> SeedDefaultModules()
|
||||
public async Task<ActionResult> SeedDefaultApps()
|
||||
{
|
||||
await _moduleService.SeedDefaultModulesAsync();
|
||||
return Ok(new { message = "Moduli di default inizializzati" });
|
||||
await _appService.SeedDefaultAppsAsync();
|
||||
return Ok(new { message = "Applicazioni di default inizializzate" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forza il controllo delle subscription scadute
|
||||
/// </summary>
|
||||
[HttpPost("check-expired")]
|
||||
public async Task<ActionResult> CheckExpiredSubscriptions()
|
||||
public async Task<ActionResult> CheckExpiredAppSubscriptions()
|
||||
{
|
||||
var count = await _moduleService.CheckExpiredSubscriptionsAsync();
|
||||
return Ok(new { message = $"Controllate le subscription, {count} moduli disattivati per scadenza" });
|
||||
var count = await _appService.CheckExpiredSubscriptionsAsync();
|
||||
return Ok(new { message = $"Controllate le subscription, {count} applicazioni disattivate per scadenza" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalida la cache dei moduli
|
||||
/// Invalida la cache delle applicazioni
|
||||
/// </summary>
|
||||
[HttpPost("invalidate-cache")]
|
||||
public ActionResult InvalidateCache()
|
||||
public ActionResult InvalidateAppsCache()
|
||||
{
|
||||
_moduleService.InvalidateCache();
|
||||
return Ok(new { message = "Cache moduli invalidata" });
|
||||
_appService.InvalidateCache();
|
||||
return Ok(new { message = "Cache applicazioni invalidata" });
|
||||
}
|
||||
|
||||
#region Mapping
|
||||
|
||||
private static ModuleDto MapToDto(AppModule module)
|
||||
private static AppDto MapToDto(App app)
|
||||
{
|
||||
return new ModuleDto
|
||||
return new AppDto
|
||||
{
|
||||
Id = module.Id,
|
||||
Code = module.Code,
|
||||
Name = module.Name,
|
||||
Description = module.Description,
|
||||
Icon = module.Icon,
|
||||
BasePrice = module.BasePrice,
|
||||
MonthlyPrice = module.GetMonthlyPrice(),
|
||||
MonthlyMultiplier = module.MonthlyMultiplier,
|
||||
SortOrder = module.SortOrder,
|
||||
IsCore = module.IsCore,
|
||||
Dependencies = module.GetDependencies().ToList(),
|
||||
RoutePath = module.RoutePath,
|
||||
IsAvailable = module.IsAvailable,
|
||||
IsEnabled = module.IsCore || ((module.Subscription?.IsEnabled ?? false) && (module.Subscription?.IsValid() ?? false)),
|
||||
Subscription = module.Subscription != null ? MapSubscriptionToDto(module.Subscription) : null
|
||||
Id = app.Id,
|
||||
Code = app.Code,
|
||||
Name = app.Name,
|
||||
Description = app.Description,
|
||||
Icon = app.Icon,
|
||||
BasePrice = app.BasePrice,
|
||||
MonthlyPrice = app.GetMonthlyPrice(),
|
||||
MonthlyMultiplier = app.MonthlyMultiplier,
|
||||
SortOrder = app.SortOrder,
|
||||
IsCore = app.IsCore,
|
||||
Dependencies = app.GetDependencies().ToList(),
|
||||
RoutePath = app.RoutePath,
|
||||
IsAvailable = app.IsAvailable,
|
||||
IsEnabled = app.IsCore || ((app.Subscription?.IsEnabled ?? false) && (app.Subscription?.IsValid() ?? false)),
|
||||
Subscription = app.Subscription != null ? MapSubscriptionToDto(app.Subscription) : null
|
||||
};
|
||||
}
|
||||
|
||||
private static SubscriptionDto MapSubscriptionToDto(ModuleSubscription subscription)
|
||||
private static AppSubscriptionDto MapSubscriptionToDto(AppSubscription subscription)
|
||||
{
|
||||
return new SubscriptionDto
|
||||
return new AppSubscriptionDto
|
||||
{
|
||||
Id = subscription.Id,
|
||||
ModuleId = subscription.ModuleId,
|
||||
ModuleCode = subscription.Module?.Code,
|
||||
ModuleName = subscription.Module?.Name,
|
||||
AppId = subscription.AppId,
|
||||
AppCode = subscription.App?.Code,
|
||||
AppName = subscription.App?.Name,
|
||||
IsEnabled = subscription.IsEnabled,
|
||||
SubscriptionType = subscription.SubscriptionType,
|
||||
SubscriptionTypeName = subscription.SubscriptionType.ToString(),
|
||||
@@ -277,7 +277,7 @@ public class ModulesController : ControllerBase
|
||||
|
||||
#region DTOs
|
||||
|
||||
public class ModuleDto
|
||||
public class AppDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
@@ -293,15 +293,15 @@ public class ModuleDto
|
||||
public string? RoutePath { get; set; }
|
||||
public bool IsAvailable { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
public SubscriptionDto? Subscription { get; set; }
|
||||
public AppSubscriptionDto? Subscription { get; set; }
|
||||
}
|
||||
|
||||
public class SubscriptionDto
|
||||
public class AppSubscriptionDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ModuleId { get; set; }
|
||||
public string? ModuleCode { get; set; }
|
||||
public string? ModuleName { get; set; }
|
||||
public int AppId { get; set; }
|
||||
public string? AppCode { get; set; }
|
||||
public string? AppName { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
public SubscriptionType SubscriptionType { get; set; }
|
||||
public string SubscriptionTypeName { get; set; } = string.Empty;
|
||||
@@ -316,7 +316,7 @@ public class SubscriptionDto
|
||||
public bool IsExpiringSoon { get; set; }
|
||||
}
|
||||
|
||||
public class ModuleStatusDto
|
||||
public class AppStatusDto
|
||||
{
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public bool IsEnabled { get; set; }
|
||||
@@ -326,7 +326,7 @@ public class ModuleStatusDto
|
||||
public bool IsExpiringSoon { get; set; }
|
||||
}
|
||||
|
||||
public class EnableModuleRequest
|
||||
public class EnableAppRequest
|
||||
{
|
||||
public SubscriptionType SubscriptionType { get; set; } = SubscriptionType.Annual;
|
||||
public DateTime? StartDate { get; set; }
|
||||
@@ -336,7 +336,7 @@ public class EnableModuleRequest
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateSubscriptionRequest
|
||||
public class UpdateAppSubscriptionRequest
|
||||
{
|
||||
public SubscriptionType? SubscriptionType { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
@@ -344,7 +344,7 @@ public class UpdateSubscriptionRequest
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class RenewSubscriptionRequest
|
||||
public class RenewAppSubscriptionRequest
|
||||
{
|
||||
public decimal? PaidPrice { get; set; }
|
||||
}
|
||||
@@ -2,10 +2,10 @@ using Zentral.API.Hubs;
|
||||
using Zentral.API.Services;
|
||||
// Trigger rebuild
|
||||
using Zentral.API.Services.Reports;
|
||||
using Zentral.API.Modules.Warehouse.Services;
|
||||
using Zentral.API.Modules.Purchases.Services;
|
||||
using Zentral.API.Modules.Sales.Services;
|
||||
using Zentral.API.Modules.Production.Services;
|
||||
using Zentral.API.Apps.Warehouse.Services;
|
||||
using Zentral.API.Apps.Purchases.Services;
|
||||
using Zentral.API.Apps.Sales.Services;
|
||||
using Zentral.API.Apps.Production.Services;
|
||||
using Zentral.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -22,7 +22,7 @@ builder.Services.AddDbContext<ZentralDbContext>(options =>
|
||||
builder.Services.AddScoped<EventoCostiService>();
|
||||
builder.Services.AddScoped<DemoDataService>();
|
||||
builder.Services.AddScoped<ReportGeneratorService>();
|
||||
builder.Services.AddScoped<ModuleService>();
|
||||
builder.Services.AddScoped<AppService>();
|
||||
builder.Services.AddScoped<AutoCodeService>();
|
||||
builder.Services.AddScoped<CustomFieldService>();
|
||||
builder.Services.AddSingleton<DataNotificationService>();
|
||||
@@ -110,9 +110,14 @@ using (var scope = app.Services.CreateScope())
|
||||
// Seed data (only in development or if database is empty)
|
||||
DbSeeder.Seed(db);
|
||||
|
||||
// Seed default modules
|
||||
var moduleService = scope.ServiceProvider.GetRequiredService<ModuleService>();
|
||||
await moduleService.SeedDefaultModulesAsync();
|
||||
// Seed default apps
|
||||
var appService = scope.ServiceProvider.GetRequiredService<AppService>();
|
||||
await appService.SeedDefaultAppsAsync();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
await appService.SeedDevSubscriptionsAsync();
|
||||
}
|
||||
|
||||
// Seed warehouse default data
|
||||
var warehouseService = scope.ServiceProvider.GetRequiredService<IWarehouseService>();
|
||||
|
||||
@@ -6,22 +6,22 @@ using Microsoft.Extensions.Caching.Memory;
|
||||
namespace Zentral.API.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service per la gestione dei moduli applicativi e delle relative subscription
|
||||
/// Service per la gestione delle applicazioni e delle relative subscription
|
||||
/// </summary>
|
||||
public class ModuleService
|
||||
public class AppService
|
||||
{
|
||||
private readonly ZentralDbContext _context;
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly ILogger<ModuleService> _logger;
|
||||
private readonly ILogger<AppService> _logger;
|
||||
|
||||
private const string MODULES_CACHE_KEY = "modules_all";
|
||||
private const string ACTIVE_MODULES_CACHE_KEY = "modules_active";
|
||||
private const string APPS_CACHE_KEY = "apps_all";
|
||||
private const string ACTIVE_APPS_CACHE_KEY = "apps_active";
|
||||
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(5);
|
||||
|
||||
public ModuleService(
|
||||
public AppService(
|
||||
ZentralDbContext context,
|
||||
IMemoryCache cache,
|
||||
ILogger<ModuleService> logger)
|
||||
ILogger<AppService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_cache = cache;
|
||||
@@ -29,80 +29,80 @@ public class ModuleService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene tutti i moduli con lo stato della subscription
|
||||
/// Ottiene tutte le applicazioni con lo stato della subscription
|
||||
/// </summary>
|
||||
public async Task<List<AppModule>> GetAllModulesAsync()
|
||||
public async Task<List<App>> GetAllAppsAsync()
|
||||
{
|
||||
return await _cache.GetOrCreateAsync(MODULES_CACHE_KEY, async entry =>
|
||||
return await _cache.GetOrCreateAsync(APPS_CACHE_KEY, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = CacheDuration;
|
||||
return await _context.AppModules
|
||||
return await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.Where(m => m.IsAvailable)
|
||||
.OrderBy(m => m.SortOrder)
|
||||
.ThenBy(m => m.Name)
|
||||
.ToListAsync();
|
||||
}) ?? new List<AppModule>();
|
||||
}) ?? new List<App>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene solo i moduli attivi (per la costruzione del menu)
|
||||
/// Ottiene solo le applicazioni attive (per la costruzione del menu)
|
||||
/// </summary>
|
||||
public async Task<List<AppModule>> GetActiveModulesAsync()
|
||||
public async Task<List<App>> GetActiveAppsAsync()
|
||||
{
|
||||
return await _cache.GetOrCreateAsync(ACTIVE_MODULES_CACHE_KEY, async entry =>
|
||||
return await _cache.GetOrCreateAsync(ACTIVE_APPS_CACHE_KEY, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = CacheDuration;
|
||||
var modules = await _context.AppModules
|
||||
var apps = await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.Where(m => m.IsAvailable)
|
||||
.OrderBy(m => m.SortOrder)
|
||||
.ThenBy(m => m.Name)
|
||||
.ToListAsync();
|
||||
|
||||
return modules.Where(m => m.IsCore || ((m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false))).ToList();
|
||||
}) ?? new List<AppModule>();
|
||||
return apps.Where(m => m.IsCore || ((m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false))).ToList();
|
||||
}) ?? new List<App>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene un modulo specifico per codice
|
||||
/// Ottiene un'applicazione specifica per codice
|
||||
/// </summary>
|
||||
public async Task<AppModule?> GetModuleByCodeAsync(string code)
|
||||
public async Task<App?> GetAppByCodeAsync(string code)
|
||||
{
|
||||
return await _context.AppModules
|
||||
return await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.FirstOrDefaultAsync(m => m.Code == code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se un modulo è attualmente abilitato
|
||||
/// Verifica se un'applicazione è attualmente abilitata
|
||||
/// </summary>
|
||||
public async Task<bool> IsModuleEnabledAsync(string code)
|
||||
public async Task<bool> IsAppEnabledAsync(string code)
|
||||
{
|
||||
var module = await GetModuleByCodeAsync(code);
|
||||
if (module == null)
|
||||
var app = await GetAppByCodeAsync(code);
|
||||
if (app == null)
|
||||
return false;
|
||||
|
||||
// I moduli core sono sempre abilitati
|
||||
if (module.IsCore)
|
||||
// Le applicazioni core sono sempre abilitate
|
||||
if (app.IsCore)
|
||||
return true;
|
||||
|
||||
return (module.Subscription?.IsEnabled ?? false) && (module.Subscription?.IsValid() ?? false);
|
||||
return (app.Subscription?.IsEnabled ?? false) && (app.Subscription?.IsValid() ?? false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se un modulo ha una subscription valida (non scaduta)
|
||||
/// Verifica se un'applicazione ha una subscription valida (non scaduta)
|
||||
/// </summary>
|
||||
public async Task<bool> HasValidSubscriptionAsync(string code)
|
||||
{
|
||||
var module = await GetModuleByCodeAsync(code);
|
||||
return module?.Subscription?.IsValid() ?? false;
|
||||
var app = await GetAppByCodeAsync(code);
|
||||
return app?.Subscription?.IsValid() ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attiva un modulo creando o aggiornando la subscription
|
||||
/// Attiva un'applicazione creando o aggiornando la subscription
|
||||
/// </summary>
|
||||
public async Task<ModuleSubscription> EnableModuleAsync(
|
||||
public async Task<AppSubscription> EnableAppAsync(
|
||||
string code,
|
||||
SubscriptionType subscriptionType,
|
||||
DateTime? startDate = null,
|
||||
@@ -111,21 +111,21 @@ public class ModuleService
|
||||
decimal? paidPrice = null,
|
||||
string? notes = null)
|
||||
{
|
||||
var module = await _context.AppModules
|
||||
var app = await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.FirstOrDefaultAsync(m => m.Code == code);
|
||||
|
||||
if (module == null)
|
||||
throw new ArgumentException($"Modulo con codice '{code}' non trovato");
|
||||
if (app == null)
|
||||
throw new ArgumentException($"Applicazione con codice '{code}' non trovata");
|
||||
|
||||
if (module.IsCore)
|
||||
throw new InvalidOperationException("I moduli core non possono essere attivati/disattivati manualmente");
|
||||
if (app.IsCore)
|
||||
throw new InvalidOperationException("Le applicazioni core non possono essere attivate/disattivate manualmente");
|
||||
|
||||
// Verifica dipendenze
|
||||
var missingDeps = await CheckDependenciesAsync(module);
|
||||
var missingDeps = await CheckDependenciesAsync(app);
|
||||
if (missingDeps.Any())
|
||||
throw new InvalidOperationException(
|
||||
$"Il modulo richiede i seguenti moduli attivi: {string.Join(", ", missingDeps)}");
|
||||
$"L'applicazione richiede le seguenti applicazioni attive: {string.Join(", ", missingDeps)}");
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var effectiveStartDate = startDate ?? now;
|
||||
@@ -142,178 +142,178 @@ public class ModuleService
|
||||
};
|
||||
}
|
||||
|
||||
if (module.Subscription == null)
|
||||
if (app.Subscription == null)
|
||||
{
|
||||
// Crea nuova subscription
|
||||
module.Subscription = new ModuleSubscription
|
||||
app.Subscription = new AppSubscription
|
||||
{
|
||||
ModuleId = module.Id,
|
||||
AppId = app.Id,
|
||||
IsEnabled = true,
|
||||
SubscriptionType = subscriptionType,
|
||||
StartDate = effectiveStartDate,
|
||||
EndDate = effectiveEndDate,
|
||||
AutoRenew = autoRenew,
|
||||
PaidPrice = paidPrice ?? module.BasePrice,
|
||||
PaidPrice = paidPrice ?? app.BasePrice,
|
||||
Notes = notes,
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
};
|
||||
_context.ModuleSubscriptions.Add(module.Subscription);
|
||||
_context.AppSubscriptions.Add(app.Subscription);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Aggiorna subscription esistente
|
||||
module.Subscription.IsEnabled = true;
|
||||
module.Subscription.SubscriptionType = subscriptionType;
|
||||
module.Subscription.StartDate = effectiveStartDate;
|
||||
module.Subscription.EndDate = effectiveEndDate;
|
||||
module.Subscription.AutoRenew = autoRenew;
|
||||
module.Subscription.PaidPrice = paidPrice ?? module.Subscription.PaidPrice ?? module.BasePrice;
|
||||
if (notes != null) module.Subscription.Notes = notes;
|
||||
module.Subscription.UpdatedAt = now;
|
||||
app.Subscription.IsEnabled = true;
|
||||
app.Subscription.SubscriptionType = subscriptionType;
|
||||
app.Subscription.StartDate = effectiveStartDate;
|
||||
app.Subscription.EndDate = effectiveEndDate;
|
||||
app.Subscription.AutoRenew = autoRenew;
|
||||
app.Subscription.PaidPrice = paidPrice ?? app.Subscription.PaidPrice ?? app.BasePrice;
|
||||
if (notes != null) app.Subscription.Notes = notes;
|
||||
app.Subscription.UpdatedAt = now;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
InvalidateCache();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Modulo {ModuleCode} attivato con subscription {Type} fino a {EndDate}",
|
||||
"Applicazione {AppCode} attivata con subscription {Type} fino a {EndDate}",
|
||||
code, subscriptionType, effectiveEndDate);
|
||||
|
||||
return module.Subscription;
|
||||
return app.Subscription;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disattiva un modulo (mantiene i dati ma rimuove l'accesso)
|
||||
/// Disattiva un'applicazione (mantiene i dati ma rimuove l'accesso)
|
||||
/// </summary>
|
||||
public async Task DisableModuleAsync(string code)
|
||||
public async Task DisableAppAsync(string code)
|
||||
{
|
||||
var module = await _context.AppModules
|
||||
var app = await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.FirstOrDefaultAsync(m => m.Code == code);
|
||||
|
||||
if (module == null)
|
||||
throw new ArgumentException($"Modulo con codice '{code}' non trovato");
|
||||
if (app == null)
|
||||
throw new ArgumentException($"Applicazione con codice '{code}' non trovata");
|
||||
|
||||
if (module.IsCore)
|
||||
throw new InvalidOperationException("I moduli core non possono essere disattivati");
|
||||
if (app.IsCore)
|
||||
throw new InvalidOperationException("Le applicazioni core non possono essere disattivate");
|
||||
|
||||
// Verifica se altri moduli dipendono da questo
|
||||
var dependentModules = await GetDependentModulesAsync(code);
|
||||
var activeDependents = dependentModules.Where(m => (m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false)).ToList();
|
||||
// Verifica se altre applicazioni dipendono da questa
|
||||
var dependentApps = await GetDependentAppsAsync(code);
|
||||
var activeDependents = dependentApps.Where(m => (m.Subscription?.IsEnabled ?? false) && (m.Subscription?.IsValid() ?? false)).ToList();
|
||||
if (activeDependents.Any())
|
||||
throw new InvalidOperationException(
|
||||
$"I seguenti moduli attivi dipendono da questo modulo: {string.Join(", ", activeDependents.Select(m => m.Name))}");
|
||||
$"Le seguenti applicazioni attive dipendono da questa applicazione: {string.Join(", ", activeDependents.Select(m => m.Name))}");
|
||||
|
||||
if (module.Subscription != null)
|
||||
if (app.Subscription != null)
|
||||
{
|
||||
module.Subscription.IsEnabled = false;
|
||||
module.Subscription.UpdatedAt = DateTime.UtcNow;
|
||||
app.Subscription.IsEnabled = false;
|
||||
app.Subscription.UpdatedAt = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
InvalidateCache();
|
||||
_logger.LogInformation("Modulo {ModuleCode} disattivato", code);
|
||||
_logger.LogInformation("Applicazione {AppCode} disattivata", code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorna i dettagli della subscription
|
||||
/// </summary>
|
||||
public async Task<ModuleSubscription> UpdateSubscriptionAsync(
|
||||
public async Task<AppSubscription> UpdateSubscriptionAsync(
|
||||
string code,
|
||||
SubscriptionType? subscriptionType = null,
|
||||
DateTime? endDate = null,
|
||||
bool? autoRenew = null,
|
||||
string? notes = null)
|
||||
{
|
||||
var module = await _context.AppModules
|
||||
var app = await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.FirstOrDefaultAsync(m => m.Code == code);
|
||||
|
||||
if (module == null)
|
||||
throw new ArgumentException($"Modulo con codice '{code}' non trovato");
|
||||
if (app == null)
|
||||
throw new ArgumentException($"Applicazione con codice '{code}' non trovata");
|
||||
|
||||
if (module.Subscription == null)
|
||||
throw new InvalidOperationException($"Il modulo '{code}' non ha una subscription attiva");
|
||||
if (app.Subscription == null)
|
||||
throw new InvalidOperationException($"L'applicazione '{code}' non ha una subscription attiva");
|
||||
|
||||
if (subscriptionType.HasValue)
|
||||
module.Subscription.SubscriptionType = subscriptionType.Value;
|
||||
app.Subscription.SubscriptionType = subscriptionType.Value;
|
||||
if (endDate.HasValue)
|
||||
module.Subscription.EndDate = endDate.Value;
|
||||
app.Subscription.EndDate = endDate.Value;
|
||||
if (autoRenew.HasValue)
|
||||
module.Subscription.AutoRenew = autoRenew.Value;
|
||||
app.Subscription.AutoRenew = autoRenew.Value;
|
||||
if (notes != null)
|
||||
module.Subscription.Notes = notes;
|
||||
app.Subscription.Notes = notes;
|
||||
|
||||
module.Subscription.UpdatedAt = DateTime.UtcNow;
|
||||
app.Subscription.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
InvalidateCache();
|
||||
|
||||
return module.Subscription;
|
||||
return app.Subscription;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rinnova una subscription esistente
|
||||
/// </summary>
|
||||
public async Task<ModuleSubscription> RenewSubscriptionAsync(string code, decimal? paidPrice = null)
|
||||
public async Task<AppSubscription> RenewSubscriptionAsync(string code, decimal? paidPrice = null)
|
||||
{
|
||||
var module = await _context.AppModules
|
||||
var app = await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.FirstOrDefaultAsync(m => m.Code == code);
|
||||
|
||||
if (module == null)
|
||||
throw new ArgumentException($"Modulo con codice '{code}' non trovato");
|
||||
if (app == null)
|
||||
throw new ArgumentException($"Applicazione con codice '{code}' non trovata");
|
||||
|
||||
if (module.Subscription == null)
|
||||
throw new InvalidOperationException($"Il modulo '{code}' non ha una subscription da rinnovare");
|
||||
if (app.Subscription == null)
|
||||
throw new InvalidOperationException($"L'applicazione '{code}' non ha una subscription da rinnovare");
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var currentEnd = module.Subscription.EndDate ?? now;
|
||||
var currentEnd = app.Subscription.EndDate ?? now;
|
||||
var newStart = currentEnd > now ? currentEnd : now;
|
||||
|
||||
var newEnd = module.Subscription.SubscriptionType switch
|
||||
var newEnd = app.Subscription.SubscriptionType switch
|
||||
{
|
||||
SubscriptionType.Monthly => newStart.AddMonths(1),
|
||||
SubscriptionType.Annual => newStart.AddYears(1),
|
||||
_ => newStart.AddYears(1) // Default to annual
|
||||
};
|
||||
|
||||
module.Subscription.StartDate = newStart;
|
||||
module.Subscription.EndDate = newEnd;
|
||||
module.Subscription.LastRenewalDate = now;
|
||||
module.Subscription.IsEnabled = true;
|
||||
module.Subscription.PaidPrice = paidPrice ?? module.Subscription.PaidPrice;
|
||||
module.Subscription.UpdatedAt = now;
|
||||
app.Subscription.StartDate = newStart;
|
||||
app.Subscription.EndDate = newEnd;
|
||||
app.Subscription.LastRenewalDate = now;
|
||||
app.Subscription.IsEnabled = true;
|
||||
app.Subscription.PaidPrice = paidPrice ?? app.Subscription.PaidPrice;
|
||||
app.Subscription.UpdatedAt = now;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
InvalidateCache();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Modulo {ModuleCode} rinnovato fino a {EndDate}",
|
||||
"Applicazione {AppCode} rinnovata fino a {EndDate}",
|
||||
code, newEnd);
|
||||
|
||||
return module.Subscription;
|
||||
return app.Subscription;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene tutte le subscription
|
||||
/// </summary>
|
||||
public async Task<List<ModuleSubscription>> GetAllSubscriptionsAsync()
|
||||
public async Task<List<AppSubscription>> GetAllSubscriptionsAsync()
|
||||
{
|
||||
return await _context.ModuleSubscriptions
|
||||
.Include(s => s.Module)
|
||||
.OrderBy(s => s.Module.SortOrder)
|
||||
return await _context.AppSubscriptions
|
||||
.Include(s => s.App)
|
||||
.OrderBy(s => s.App.SortOrder)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica e disattiva i moduli con subscription scaduta (per job schedulato)
|
||||
/// Verifica e disattiva le applicazioni con subscription scaduta (per job schedulato)
|
||||
/// </summary>
|
||||
public async Task<int> CheckExpiredSubscriptionsAsync()
|
||||
{
|
||||
var expiredSubscriptions = await _context.ModuleSubscriptions
|
||||
.Include(s => s.Module)
|
||||
var expiredSubscriptions = await _context.AppSubscriptions
|
||||
.Include(s => s.App)
|
||||
.Where(s => s.IsEnabled &&
|
||||
s.EndDate.HasValue &&
|
||||
s.EndDate.Value < DateTime.UtcNow &&
|
||||
@@ -325,8 +325,8 @@ public class ModuleService
|
||||
subscription.IsEnabled = false;
|
||||
subscription.UpdatedAt = DateTime.UtcNow;
|
||||
_logger.LogWarning(
|
||||
"Modulo {ModuleCode} disattivato per scadenza subscription",
|
||||
subscription.Module.Code);
|
||||
"Applicazione {AppCode} disattivata per scadenza subscription",
|
||||
subscription.App.Code);
|
||||
}
|
||||
|
||||
if (expiredSubscriptions.Any())
|
||||
@@ -339,13 +339,13 @@ public class ModuleService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene i moduli in scadenza entro N giorni
|
||||
/// Ottiene le applicazioni in scadenza entro N giorni
|
||||
/// </summary>
|
||||
public async Task<List<AppModule>> GetExpiringModulesAsync(int daysThreshold = 30)
|
||||
public async Task<List<App>> GetExpiringAppsAsync(int daysThreshold = 30)
|
||||
{
|
||||
var thresholdDate = DateTime.UtcNow.AddDays(daysThreshold);
|
||||
|
||||
return await _context.AppModules
|
||||
return await _context.Apps
|
||||
.Include(m => m.Subscription)
|
||||
.Where(m => m.Subscription != null &&
|
||||
m.Subscription.IsEnabled &&
|
||||
@@ -357,11 +357,11 @@ public class ModuleService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica le dipendenze mancanti per un modulo
|
||||
/// Verifica le dipendenze mancanti per un'applicazione
|
||||
/// </summary>
|
||||
private async Task<List<string>> CheckDependenciesAsync(AppModule module)
|
||||
private async Task<List<string>> CheckDependenciesAsync(App app)
|
||||
{
|
||||
var dependencies = module.GetDependencies().ToList();
|
||||
var dependencies = app.GetDependencies().ToList();
|
||||
if (!dependencies.Any())
|
||||
return new List<string>();
|
||||
|
||||
@@ -369,10 +369,10 @@ public class ModuleService
|
||||
|
||||
foreach (var depCode in dependencies)
|
||||
{
|
||||
if (!await IsModuleEnabledAsync(depCode))
|
||||
if (!await IsAppEnabledAsync(depCode))
|
||||
{
|
||||
var depModule = await GetModuleByCodeAsync(depCode);
|
||||
missingDeps.Add(depModule?.Name ?? depCode);
|
||||
var depApp = await GetAppByCodeAsync(depCode);
|
||||
missingDeps.Add(depApp?.Name ?? depCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,34 +380,34 @@ public class ModuleService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene i moduli che dipendono da un determinato modulo
|
||||
/// Ottiene le applicazioni che dipendono da una determinata applicazione
|
||||
/// </summary>
|
||||
private async Task<List<AppModule>> GetDependentModulesAsync(string code)
|
||||
private async Task<List<App>> GetDependentAppsAsync(string code)
|
||||
{
|
||||
var allModules = await GetAllModulesAsync();
|
||||
return allModules
|
||||
var allApps = await GetAllAppsAsync();
|
||||
return allApps
|
||||
.Where(m => m.GetDependencies().Contains(code))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalida la cache dei moduli
|
||||
/// Invalida la cache delle applicazioni
|
||||
/// </summary>
|
||||
public void InvalidateCache()
|
||||
{
|
||||
_cache.Remove(MODULES_CACHE_KEY);
|
||||
_cache.Remove(ACTIVE_MODULES_CACHE_KEY);
|
||||
_logger.LogDebug("Cache moduli invalidata");
|
||||
_cache.Remove(APPS_CACHE_KEY);
|
||||
_cache.Remove(ACTIVE_APPS_CACHE_KEY);
|
||||
_logger.LogDebug("Cache applicazioni invalidata");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inizializza i moduli di default se non esistono
|
||||
/// Inizializza le applicazioni di default se non esistono
|
||||
/// </summary>
|
||||
public async Task SeedDefaultModulesAsync()
|
||||
public async Task SeedDefaultAppsAsync()
|
||||
{
|
||||
var defaultModules = new List<AppModule>
|
||||
var defaultApps = new List<App>
|
||||
{
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "warehouse",
|
||||
Name = "Magazzino",
|
||||
@@ -421,7 +421,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "purchases",
|
||||
Name = "Acquisti",
|
||||
@@ -436,7 +436,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "sales",
|
||||
Name = "Vendite",
|
||||
@@ -451,7 +451,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "production",
|
||||
Name = "Produzione",
|
||||
@@ -466,7 +466,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "quality",
|
||||
Name = "Qualità",
|
||||
@@ -480,7 +480,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "events",
|
||||
Name = "Gestione Eventi",
|
||||
@@ -494,7 +494,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "hr",
|
||||
Name = "Gestione Personale",
|
||||
@@ -508,7 +508,7 @@ public class ModuleService
|
||||
IsAvailable = true,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
},
|
||||
new AppModule
|
||||
new App
|
||||
{
|
||||
Code = "report-designer",
|
||||
Name = "Report Designer",
|
||||
@@ -524,15 +524,64 @@ public class ModuleService
|
||||
}
|
||||
};
|
||||
|
||||
var existingCodes = await _context.AppModules.Select(m => m.Code).ToListAsync();
|
||||
var newModules = defaultModules.Where(m => !existingCodes.Contains(m.Code)).ToList();
|
||||
var existingCodes = await _context.Apps.Select(m => m.Code).ToListAsync();
|
||||
var newApps = defaultApps.Where(m => !existingCodes.Contains(m.Code)).ToList();
|
||||
|
||||
if (newModules.Any())
|
||||
if (newApps.Any())
|
||||
{
|
||||
_context.AppModules.AddRange(newModules);
|
||||
_context.Apps.AddRange(newApps);
|
||||
await _context.SaveChangesAsync();
|
||||
_logger.LogInformation("Added {Count} new default modules: {Modules}",
|
||||
newModules.Count, string.Join(", ", newModules.Select(m => m.Code)));
|
||||
_logger.LogInformation("Added {Count} new default apps: {Apps}",
|
||||
newApps.Count, string.Join(", ", newApps.Select(m => m.Code)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attiva tutte le applicazioni per ambiente di sviluppo
|
||||
/// </summary>
|
||||
public async Task SeedDevSubscriptionsAsync()
|
||||
{
|
||||
var apps = await _context.Apps
|
||||
.Include(a => a.Subscription)
|
||||
.ToListAsync();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
bool changes = false;
|
||||
|
||||
foreach (var app in apps)
|
||||
{
|
||||
if (app.Subscription == null)
|
||||
{
|
||||
app.Subscription = new AppSubscription
|
||||
{
|
||||
AppId = app.Id,
|
||||
IsEnabled = true,
|
||||
SubscriptionType = SubscriptionType.Annual,
|
||||
StartDate = now,
|
||||
EndDate = now.AddYears(1),
|
||||
AutoRenew = true,
|
||||
PaidPrice = 0,
|
||||
Notes = "Auto-generated for Development",
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
};
|
||||
_context.AppSubscriptions.Add(app.Subscription);
|
||||
changes = true;
|
||||
}
|
||||
else if (!app.Subscription.IsEnabled)
|
||||
{
|
||||
app.Subscription.IsEnabled = true;
|
||||
app.Subscription.EndDate = now.AddYears(1);
|
||||
app.Subscription.UpdatedAt = now;
|
||||
changes = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changes)
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
InvalidateCache();
|
||||
_logger.LogInformation("Dev subscriptions seeded for all apps");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user