This commit is contained in:
2025-11-29 16:06:13 +01:00
parent c7dbcde5dd
commit cedcc503fa
34 changed files with 9097 additions and 191 deletions

View File

@@ -1,3 +1,4 @@
using Apollinare.API.Services;
using Apollinare.Domain.Entities;
using Apollinare.Infrastructure.Data;
using Microsoft.AspNetCore.Mvc;
@@ -10,10 +11,12 @@ namespace Apollinare.API.Controllers;
public class ArticoliController : ControllerBase
{
private readonly AppollinareDbContext _context;
private readonly AutoCodeService _autoCodeService;
public ArticoliController(AppollinareDbContext context)
public ArticoliController(AppollinareDbContext context, AutoCodeService autoCodeService)
{
_context = context;
_autoCodeService = autoCodeService;
}
[HttpGet]
@@ -60,6 +63,13 @@ public class ArticoliController : ControllerBase
[HttpPost]
public async Task<ActionResult<Articolo>> CreateArticolo(Articolo articolo)
{
// Genera codice automatico
var codice = await _autoCodeService.GenerateNextCodeAsync("articolo");
if (!string.IsNullOrEmpty(codice))
{
articolo.Codice = codice;
}
articolo.CreatedAt = DateTime.UtcNow;
_context.Articoli.Add(articolo);
await _context.SaveChangesAsync();

View File

@@ -0,0 +1,240 @@
using Apollinare.API.Services;
using Apollinare.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
namespace Apollinare.API.Controllers;
[ApiController]
[Route("api/[controller]")]
public class AutoCodesController : ControllerBase
{
private readonly AutoCodeService _autoCodeService;
private readonly ILogger<AutoCodesController> _logger;
public AutoCodesController(AutoCodeService autoCodeService, ILogger<AutoCodesController> logger)
{
_autoCodeService = autoCodeService;
_logger = logger;
}
/// <summary>
/// Ottiene tutte le configurazioni AutoCode.
/// </summary>
[HttpGet]
public async Task<ActionResult<List<AutoCodeDto>>> GetAll()
{
var configs = await _autoCodeService.GetAllConfigurationsAsync();
return Ok(configs.Select(ToDto).ToList());
}
/// <summary>
/// Ottiene le configurazioni AutoCode per un modulo specifico.
/// </summary>
[HttpGet("module/{moduleCode}")]
public async Task<ActionResult<List<AutoCodeDto>>> GetByModule(string moduleCode)
{
var configs = await _autoCodeService.GetConfigurationsByModuleAsync(moduleCode);
return Ok(configs.Select(ToDto).ToList());
}
/// <summary>
/// Ottiene una configurazione AutoCode specifica.
/// </summary>
[HttpGet("{entityCode}")]
public async Task<ActionResult<AutoCodeDto>> Get(string entityCode)
{
var config = await _autoCodeService.GetConfigurationAsync(entityCode);
if (config == null)
return NotFound($"Configurazione per '{entityCode}' non trovata");
return Ok(ToDto(config));
}
/// <summary>
/// Genera un nuovo codice per un'entità specifica.
/// </summary>
[HttpPost("{entityCode}/generate")]
public async Task<ActionResult<GenerateCodeResponse>> GenerateCode(string entityCode)
{
try
{
var code = await _autoCodeService.GenerateNextCodeAsync(entityCode);
if (code == null)
{
return BadRequest(new { error = "Generazione automatica disabilitata o configurazione non trovata" });
}
return Ok(new GenerateCodeResponse { Code = code, EntityCode = entityCode });
}
catch (Exception ex)
{
_logger.LogError(ex, "Errore durante la generazione del codice per {EntityCode}", entityCode);
return StatusCode(500, new { error = "Errore durante la generazione del codice" });
}
}
/// <summary>
/// Ottiene un'anteprima del prossimo codice senza incrementare la sequenza.
/// </summary>
[HttpGet("{entityCode}/preview")]
public async Task<ActionResult<GenerateCodeResponse>> PreviewCode(string entityCode)
{
var code = await _autoCodeService.PreviewNextCodeAsync(entityCode);
if (code == null)
{
return BadRequest(new { error = "Generazione automatica disabilitata o configurazione non trovata" });
}
return Ok(new GenerateCodeResponse { Code = code, EntityCode = entityCode, IsPreview = true });
}
/// <summary>
/// Aggiorna una configurazione AutoCode.
/// </summary>
[HttpPut("{id:int}")]
public async Task<ActionResult<AutoCodeDto>> Update(int id, [FromBody] AutoCodeUpdateDto dto)
{
try
{
var config = await _autoCodeService.UpdateConfigurationAsync(id, dto);
return Ok(ToDto(config));
}
catch (KeyNotFoundException ex)
{
return NotFound(new { error = ex.Message });
}
catch (ArgumentException ex)
{
return BadRequest(new { error = ex.Message });
}
}
/// <summary>
/// Resetta la sequenza per un'entità specifica.
/// </summary>
[HttpPost("{entityCode}/reset-sequence")]
public async Task<ActionResult> ResetSequence(string entityCode, [FromBody] ResetSequenceRequest? request)
{
try
{
await _autoCodeService.ResetSequenceAsync(entityCode, request?.NewValue ?? 0);
return Ok(new { message = $"Sequenza resettata per '{entityCode}'" });
}
catch (KeyNotFoundException ex)
{
return NotFound(new { error = ex.Message });
}
}
/// <summary>
/// Verifica se un codice è unico per un'entità.
/// </summary>
[HttpGet("{entityCode}/check-unique")]
public async Task<ActionResult<CheckUniqueResponse>> CheckUnique(
string entityCode,
[FromQuery] string code,
[FromQuery] int? excludeId = null)
{
var isUnique = await _autoCodeService.IsCodeUniqueAsync(entityCode, code, excludeId);
return Ok(new CheckUniqueResponse { IsUnique = isUnique, Code = code, EntityCode = entityCode });
}
/// <summary>
/// Ottiene i placeholder disponibili per i pattern.
/// </summary>
[HttpGet("placeholders")]
public ActionResult<List<PlaceholderInfo>> GetPlaceholders()
{
var placeholders = new List<PlaceholderInfo>
{
new() { Placeholder = "{PREFIX}", Description = "Prefisso configurato", Example = "ART" },
new() { Placeholder = "{SEQ:n}", Description = "Sequenza numerica con n cifre", Example = "{SEQ:5} → 00001" },
new() { Placeholder = "{YYYY}", Description = "Anno a 4 cifre", Example = "2025" },
new() { Placeholder = "{YY}", Description = "Anno a 2 cifre", Example = "25" },
new() { Placeholder = "{MM}", Description = "Mese a 2 cifre", Example = "11" },
new() { Placeholder = "{DD}", Description = "Giorno a 2 cifre", Example = "29" },
new() { Placeholder = "{YEAR}", Description = "Alias per {YYYY}", Example = "2025" },
new() { Placeholder = "{MONTH}", Description = "Alias per {MM}", Example = "11" },
new() { Placeholder = "{DAY}", Description = "Alias per {DD}", Example = "29" },
};
return Ok(placeholders);
}
private static AutoCodeDto ToDto(AutoCode entity)
{
return new AutoCodeDto
{
Id = entity.Id,
EntityCode = entity.EntityCode,
EntityName = entity.EntityName,
Prefix = entity.Prefix,
Pattern = entity.Pattern,
LastSequence = entity.LastSequence,
ResetSequenceYearly = entity.ResetSequenceYearly,
ResetSequenceMonthly = entity.ResetSequenceMonthly,
LastResetYear = entity.LastResetYear,
LastResetMonth = entity.LastResetMonth,
IsEnabled = entity.IsEnabled,
IsReadOnly = entity.IsReadOnly,
ModuleCode = entity.ModuleCode,
Description = entity.Description,
SortOrder = entity.SortOrder,
ExampleCode = entity.GetExampleCode(),
CreatedAt = entity.CreatedAt,
UpdatedAt = entity.UpdatedAt
};
}
}
#region DTOs
public class AutoCodeDto
{
public int Id { get; set; }
public string EntityCode { get; set; } = string.Empty;
public string EntityName { get; set; } = string.Empty;
public string? Prefix { get; set; }
public string Pattern { get; set; } = string.Empty;
public long LastSequence { get; set; }
public bool ResetSequenceYearly { get; set; }
public bool ResetSequenceMonthly { get; set; }
public int? LastResetYear { get; set; }
public int? LastResetMonth { get; set; }
public bool IsEnabled { get; set; }
public bool IsReadOnly { get; set; }
public string? ModuleCode { get; set; }
public string? Description { get; set; }
public int SortOrder { get; set; }
public string ExampleCode { get; set; } = string.Empty;
public DateTime? CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
public class GenerateCodeResponse
{
public string Code { get; set; } = string.Empty;
public string EntityCode { get; set; } = string.Empty;
public bool IsPreview { get; set; }
}
public class ResetSequenceRequest
{
public long NewValue { get; set; } = 0;
}
public class CheckUniqueResponse
{
public bool IsUnique { get; set; }
public string Code { get; set; } = string.Empty;
public string EntityCode { get; set; } = string.Empty;
}
public class PlaceholderInfo
{
public string Placeholder { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Example { get; set; } = string.Empty;
}
#endregion

View File

@@ -1,3 +1,4 @@
using Apollinare.API.Services;
using Apollinare.Domain.Entities;
using Apollinare.Infrastructure.Data;
using Microsoft.AspNetCore.Mvc;
@@ -10,10 +11,12 @@ namespace Apollinare.API.Controllers;
public class ClientiController : ControllerBase
{
private readonly AppollinareDbContext _context;
private readonly AutoCodeService _autoCodeService;
public ClientiController(AppollinareDbContext context)
public ClientiController(AppollinareDbContext context, AutoCodeService autoCodeService)
{
_context = context;
_autoCodeService = autoCodeService;
}
[HttpGet]
@@ -47,6 +50,13 @@ public class ClientiController : ControllerBase
[HttpPost]
public async Task<ActionResult<Cliente>> CreateCliente(Cliente cliente)
{
// Genera codice automatico
var codice = await _autoCodeService.GenerateNextCodeAsync("cliente");
if (!string.IsNullOrEmpty(codice))
{
cliente.Codice = codice;
}
cliente.CreatedAt = DateTime.UtcNow;
_context.Clienti.Add(cliente);
await _context.SaveChangesAsync();

View File

@@ -1,4 +1,5 @@
using Apollinare.API.Hubs;
using Apollinare.API.Services;
using Apollinare.Domain.Entities;
using Apollinare.Domain.Enums;
using Apollinare.Infrastructure.Data;
@@ -13,11 +14,13 @@ public class EventiController : ControllerBase
{
private readonly AppollinareDbContext _context;
private readonly DataNotificationService _notifier;
private readonly AutoCodeService _autoCodeService;
public EventiController(AppollinareDbContext context, DataNotificationService notifier)
public EventiController(AppollinareDbContext context, DataNotificationService notifier, AutoCodeService autoCodeService)
{
_context = context;
_notifier = notifier;
_autoCodeService = autoCodeService;
}
[HttpGet]
@@ -343,6 +346,12 @@ public class EventiController : ControllerBase
private async Task<string> GeneraCodiceEvento()
{
// Usa AutoCodeService per generare il codice
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("evento");
if (generatedCode != null)
return generatedCode;
// Fallback: metodo legacy
var anno = DateTime.Now.Year;
var ultimoEvento = await _context.Eventi
.Where(e => e.Codice != null && e.Codice.StartsWith($"EV{anno}"))

View File

@@ -239,6 +239,7 @@ public class WarehouseArticlesController : ControllerBase
public record ArticleDto(
int Id,
string Code,
string? AlternativeCode,
string Description,
string? ShortDescription,
string? Barcode,
@@ -273,36 +274,36 @@ public class WarehouseArticlesController : ControllerBase
);
public record CreateArticleDto(
string Code,
string Description,
string? ShortDescription,
string? Barcode,
string? ManufacturerCode,
int? CategoryId,
string UnitOfMeasure,
string? SecondaryUnitOfMeasure,
decimal? UnitConversionFactor,
StockManagementType StockManagement,
bool IsBatchManaged,
bool IsSerialManaged,
bool HasExpiry,
int? ExpiryWarningDays,
decimal? MinimumStock,
decimal? MaximumStock,
decimal? ReorderPoint,
decimal? ReorderQuantity,
int? LeadTimeDays,
ValuationMethod? ValuationMethod,
decimal? StandardCost,
decimal? BaseSellingPrice,
decimal? Weight,
decimal? Volume,
string? Notes
string? AlternativeCode = null,
string? ShortDescription = null,
string? Barcode = null,
string? ManufacturerCode = null,
int? CategoryId = null,
string? SecondaryUnitOfMeasure = null,
decimal? UnitConversionFactor = null,
StockManagementType StockManagement = StockManagementType.Standard,
bool IsBatchManaged = false,
bool IsSerialManaged = false,
bool HasExpiry = false,
int? ExpiryWarningDays = null,
decimal? MinimumStock = null,
decimal? MaximumStock = null,
decimal? ReorderPoint = null,
decimal? ReorderQuantity = null,
int? LeadTimeDays = null,
ValuationMethod? ValuationMethod = null,
decimal? StandardCost = null,
decimal? BaseSellingPrice = null,
decimal? Weight = null,
decimal? Volume = null,
string? Notes = null
);
public record UpdateArticleDto(
string Code,
string Description,
string? AlternativeCode,
string? ShortDescription,
string? Barcode,
string? ManufacturerCode,
@@ -363,6 +364,7 @@ public class WarehouseArticlesController : ControllerBase
private static ArticleDto MapToDto(WarehouseArticle article) => new(
article.Id,
article.Code,
article.AlternativeCode,
article.Description,
article.ShortDescription,
article.Barcode,
@@ -398,7 +400,8 @@ public class WarehouseArticlesController : ControllerBase
private static WarehouseArticle MapFromDto(CreateArticleDto dto) => new()
{
Code = dto.Code,
// Code viene generato automaticamente da WarehouseService.CreateArticleAsync
AlternativeCode = dto.AlternativeCode,
Description = dto.Description,
ShortDescription = dto.ShortDescription,
Barcode = dto.Barcode,
@@ -428,7 +431,8 @@ public class WarehouseArticlesController : ControllerBase
private static void UpdateFromDto(WarehouseArticle article, UpdateArticleDto dto)
{
article.Code = dto.Code;
// Code non viene aggiornato - è generato automaticamente e immutabile
article.AlternativeCode = dto.AlternativeCode;
article.Description = dto.Description;
article.ShortDescription = dto.ShortDescription;
article.Barcode = dto.Barcode;

View File

@@ -1,3 +1,4 @@
using Apollinare.API.Services;
using Apollinare.Domain.Entities.Warehouse;
using Apollinare.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
@@ -13,6 +14,7 @@ public class WarehouseService : IWarehouseService
private readonly AppollinareDbContext _context;
private readonly IMemoryCache _cache;
private readonly ILogger<WarehouseService> _logger;
private readonly AutoCodeService _autoCodeService;
private const string WAREHOUSES_CACHE_KEY = "warehouse_locations";
private const string CATEGORIES_CACHE_KEY = "warehouse_categories";
@@ -22,11 +24,13 @@ public class WarehouseService : IWarehouseService
public WarehouseService(
AppollinareDbContext context,
IMemoryCache cache,
ILogger<WarehouseService> logger)
ILogger<WarehouseService> logger,
AutoCodeService autoCodeService)
{
_context = context;
_cache = cache;
_logger = logger;
_autoCodeService = autoCodeService;
}
#region Articoli
@@ -118,6 +122,16 @@ public class WarehouseService : IWarehouseService
public async Task<WarehouseArticle> CreateArticleAsync(WarehouseArticle article)
{
// Genera codice automaticamente se non specificato
if (string.IsNullOrWhiteSpace(article.Code))
{
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("warehouse_article");
if (generatedCode != null)
article.Code = generatedCode;
else
throw new InvalidOperationException("Impossibile generare codice automatico per l'articolo");
}
// Verifica unicità codice
if (await _context.WarehouseArticles.AnyAsync(a => a.Code == article.Code))
throw new InvalidOperationException($"Esiste già un articolo con codice '{article.Code}'");
@@ -230,6 +244,16 @@ public class WarehouseService : IWarehouseService
public async Task<WarehouseArticleCategory> CreateCategoryAsync(WarehouseArticleCategory category)
{
// Genera codice automaticamente se non specificato
if (string.IsNullOrWhiteSpace(category.Code))
{
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("warehouse_category");
if (generatedCode != null)
category.Code = generatedCode;
else
throw new InvalidOperationException("Impossibile generare codice automatico per la categoria");
}
if (await _context.WarehouseArticleCategories.AnyAsync(c => c.Code == category.Code))
throw new InvalidOperationException($"Esiste già una categoria con codice '{category.Code}'");
@@ -336,6 +360,16 @@ public class WarehouseService : IWarehouseService
public async Task<WarehouseLocation> CreateWarehouseAsync(WarehouseLocation warehouse)
{
// Genera codice automaticamente se non specificato
if (string.IsNullOrWhiteSpace(warehouse.Code))
{
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("warehouse_location");
if (generatedCode != null)
warehouse.Code = generatedCode;
else
throw new InvalidOperationException("Impossibile generare codice automatico per il magazzino");
}
if (await _context.WarehouseLocations.AnyAsync(w => w.Code == warehouse.Code))
throw new InvalidOperationException($"Esiste già un magazzino con codice '{warehouse.Code}'");
@@ -464,6 +498,16 @@ public class WarehouseService : IWarehouseService
if (!article.IsBatchManaged)
throw new InvalidOperationException("L'articolo non è gestito a lotti");
// Genera numero lotto automaticamente se non specificato
if (string.IsNullOrWhiteSpace(batch.BatchNumber))
{
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("article_batch");
if (generatedCode != null)
batch.BatchNumber = generatedCode;
else
throw new InvalidOperationException("Impossibile generare numero lotto automatico");
}
// Verifica unicità batch number per articolo
if (await _context.ArticleBatches.AnyAsync(b => b.ArticleId == batch.ArticleId && b.BatchNumber == batch.BatchNumber))
throw new InvalidOperationException($"Esiste già un lotto '{batch.BatchNumber}' per questo articolo");
@@ -809,9 +853,15 @@ public class WarehouseService : IWarehouseService
public async Task<StockMovement> CreateMovementAsync(StockMovement movement)
{
// Genera numero documento se non specificato
// Genera numero documento automaticamente se non specificato
if (string.IsNullOrEmpty(movement.DocumentNumber))
movement.DocumentNumber = await GenerateDocumentNumberAsync(movement.Type);
{
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("stock_movement");
if (generatedCode != null)
movement.DocumentNumber = generatedCode;
else
movement.DocumentNumber = await GenerateDocumentNumberAsync(movement.Type); // Fallback
}
// Verifica unicità documento
if (await _context.StockMovements.AnyAsync(m => m.DocumentNumber == movement.DocumentNumber))
@@ -1428,8 +1478,15 @@ public class WarehouseService : IWarehouseService
public async Task<InventoryCount> CreateInventoryCountAsync(InventoryCount inventory)
{
// Genera codice automaticamente se non specificato
if (string.IsNullOrEmpty(inventory.Code))
inventory.Code = $"INV/{DateTime.UtcNow:yyyyMMdd}/{await GenerateInventorySequenceAsync()}";
{
var generatedCode = await _autoCodeService.GenerateNextCodeAsync("inventory_count");
if (generatedCode != null)
inventory.Code = generatedCode;
else
inventory.Code = $"INV/{DateTime.UtcNow:yyyyMMdd}/{await GenerateInventorySequenceAsync()}"; // Fallback
}
inventory.CreatedAt = DateTime.UtcNow;
_context.InventoryCounts.Add(inventory);

View File

@@ -19,6 +19,7 @@ builder.Services.AddScoped<EventoCostiService>();
builder.Services.AddScoped<DemoDataService>();
builder.Services.AddScoped<ReportGeneratorService>();
builder.Services.AddScoped<ModuleService>();
builder.Services.AddScoped<AutoCodeService>();
builder.Services.AddSingleton<DataNotificationService>();
// Warehouse Module Services
@@ -100,6 +101,10 @@ using (var scope = app.Services.CreateScope())
// Seed warehouse default data
var warehouseService = scope.ServiceProvider.GetRequiredService<IWarehouseService>();
await warehouseService.SeedDefaultDataAsync();
// Seed AutoCode configurations
var autoCodeService = scope.ServiceProvider.GetRequiredService<AutoCodeService>();
await autoCodeService.SeedDefaultConfigurationsAsync();
}
if (app.Environment.IsDevelopment())

View File

@@ -0,0 +1,489 @@
using System.Text.RegularExpressions;
using Apollinare.Domain.Entities;
using Apollinare.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace Apollinare.API.Services;
/// <summary>
/// Servizio per la generazione automatica di codici univoci.
/// Thread-safe grazie all'uso di transazioni database.
/// </summary>
public class AutoCodeService
{
private readonly AppollinareDbContext _db;
private readonly ILogger<AutoCodeService> _logger;
private static readonly Regex SequencePattern = new(@"\{SEQ:(\d+)\}", RegexOptions.Compiled);
public AutoCodeService(AppollinareDbContext db, ILogger<AutoCodeService> logger)
{
_db = db;
_logger = logger;
}
/// <summary>
/// Genera il prossimo codice per un'entità specifica.
/// Incrementa automaticamente la sequenza e gestisce i reset periodici.
/// </summary>
/// <param name="entityCode">Codice dell'entità (es. "warehouse_article")</param>
/// <returns>Il nuovo codice generato, o null se la generazione automatica è disabilitata</returns>
public async Task<string?> GenerateNextCodeAsync(string entityCode)
{
// Usa una transazione per garantire atomicità
await using var transaction = await _db.Database.BeginTransactionAsync();
try
{
var config = await _db.AutoCodes
.FirstOrDefaultAsync(c => c.EntityCode == entityCode);
if (config == null)
{
_logger.LogWarning("Configurazione AutoCode non trovata per entità: {EntityCode}", entityCode);
return null;
}
if (!config.IsEnabled)
{
_logger.LogDebug("Generazione automatica disabilitata per entità: {EntityCode}", entityCode);
return null;
}
var now = DateTime.Now;
// Gestione reset sequenza
if (ShouldResetSequence(config, now))
{
config.LastSequence = 0;
config.LastResetYear = now.Year;
config.LastResetMonth = now.Month;
_logger.LogInformation("Sequenza resettata per entità {EntityCode} (Anno: {Year}, Mese: {Month})",
entityCode, now.Year, now.Month);
}
// Incrementa sequenza
config.LastSequence++;
config.UpdatedAt = now;
// Genera codice dal pattern
var code = GenerateCodeFromPattern(config.Pattern, config.Prefix, config.LastSequence, now);
await _db.SaveChangesAsync();
await transaction.CommitAsync();
_logger.LogDebug("Codice generato per {EntityCode}: {Code}", entityCode, code);
return code;
}
catch (Exception ex)
{
await transaction.RollbackAsync();
_logger.LogError(ex, "Errore durante la generazione del codice per {EntityCode}", entityCode);
throw;
}
}
/// <summary>
/// Genera un codice di anteprima senza incrementare la sequenza.
/// Utile per mostrare all'utente cosa verrà generato.
/// </summary>
public async Task<string?> PreviewNextCodeAsync(string entityCode)
{
var config = await _db.AutoCodes
.AsNoTracking()
.FirstOrDefaultAsync(c => c.EntityCode == entityCode);
if (config == null || !config.IsEnabled)
return null;
var now = DateTime.Now;
var nextSequence = config.LastSequence + 1;
// Simula reset se necessario
if (ShouldResetSequence(config, now))
nextSequence = 1;
return GenerateCodeFromPattern(config.Pattern, config.Prefix, nextSequence, now);
}
/// <summary>
/// Verifica se un codice è già utilizzato per un'entità.
/// </summary>
public async Task<bool> IsCodeUniqueAsync(string entityCode, string code, int? excludeId = null)
{
return entityCode switch
{
"warehouse_article" => !await _db.WarehouseArticles
.AnyAsync(a => a.Code == code && (excludeId == null || a.Id != excludeId)),
"warehouse_location" => !await _db.WarehouseLocations
.AnyAsync(w => w.Code == code && (excludeId == null || w.Id != excludeId)),
"warehouse_category" => !await _db.WarehouseArticleCategories
.AnyAsync(c => c.Code == code && (excludeId == null || c.Id != excludeId)),
"stock_movement" => !await _db.StockMovements
.AnyAsync(m => m.DocumentNumber == code && (excludeId == null || m.Id != excludeId)),
"inventory_count" => !await _db.InventoryCounts
.AnyAsync(i => i.Code == code && (excludeId == null || i.Id != excludeId)),
"cliente" => !await _db.Clienti
.AnyAsync(c => c.Codice == code && (excludeId == null || c.Id != excludeId)),
"evento" => !await _db.Eventi
.AnyAsync(e => e.Codice == code && (excludeId == null || e.Id != excludeId)),
"articolo" => !await _db.Articoli
.AnyAsync(a => a.Codice == code && (excludeId == null || a.Id != excludeId)),
_ => true // Entità non gestita, assume codice unico
};
}
/// <summary>
/// Ottiene tutte le configurazioni AutoCode.
/// </summary>
public async Task<List<AutoCode>> GetAllConfigurationsAsync()
{
return await _db.AutoCodes
.OrderBy(c => c.ModuleCode)
.ThenBy(c => c.SortOrder)
.ThenBy(c => c.EntityName)
.ToListAsync();
}
/// <summary>
/// Ottiene le configurazioni AutoCode per un modulo specifico.
/// </summary>
public async Task<List<AutoCode>> GetConfigurationsByModuleAsync(string moduleCode)
{
return await _db.AutoCodes
.Where(c => c.ModuleCode == moduleCode)
.OrderBy(c => c.SortOrder)
.ThenBy(c => c.EntityName)
.ToListAsync();
}
/// <summary>
/// Ottiene una configurazione AutoCode per codice entità.
/// </summary>
public async Task<AutoCode?> GetConfigurationAsync(string entityCode)
{
return await _db.AutoCodes
.FirstOrDefaultAsync(c => c.EntityCode == entityCode);
}
/// <summary>
/// Aggiorna una configurazione AutoCode.
/// </summary>
public async Task<AutoCode> UpdateConfigurationAsync(int id, AutoCodeUpdateDto dto)
{
var config = await _db.AutoCodes.FindAsync(id)
?? throw new KeyNotFoundException($"Configurazione AutoCode con ID {id} non trovata");
if (dto.Prefix != null)
config.Prefix = dto.Prefix;
if (dto.Pattern != null)
{
// Valida il pattern
ValidatePattern(dto.Pattern);
config.Pattern = dto.Pattern;
}
if (dto.ResetSequenceYearly.HasValue)
config.ResetSequenceYearly = dto.ResetSequenceYearly.Value;
if (dto.ResetSequenceMonthly.HasValue)
config.ResetSequenceMonthly = dto.ResetSequenceMonthly.Value;
if (dto.IsEnabled.HasValue)
config.IsEnabled = dto.IsEnabled.Value;
if (dto.IsReadOnly.HasValue)
config.IsReadOnly = dto.IsReadOnly.Value;
if (dto.Description != null)
config.Description = dto.Description;
config.UpdatedAt = DateTime.Now;
await _db.SaveChangesAsync();
return config;
}
/// <summary>
/// Resetta manualmente la sequenza per un'entità.
/// </summary>
public async Task ResetSequenceAsync(string entityCode, long newValue = 0)
{
var config = await _db.AutoCodes
.FirstOrDefaultAsync(c => c.EntityCode == entityCode)
?? throw new KeyNotFoundException($"Configurazione AutoCode per '{entityCode}' non trovata");
config.LastSequence = newValue;
config.LastResetYear = DateTime.Now.Year;
config.LastResetMonth = DateTime.Now.Month;
config.UpdatedAt = DateTime.Now;
await _db.SaveChangesAsync();
_logger.LogInformation("Sequenza resettata manualmente per {EntityCode} a {Value}", entityCode, newValue);
}
/// <summary>
/// Inizializza le configurazioni di default per tutte le entità.
/// Chiamato al seed dell'applicazione.
/// </summary>
public async Task SeedDefaultConfigurationsAsync()
{
var defaults = new List<AutoCode>
{
// Core
new()
{
EntityCode = "cliente",
EntityName = "Cliente",
Prefix = "CLI",
Pattern = "{PREFIX}-{SEQ:5}",
ModuleCode = "core",
Description = "Codice cliente (es. CLI-00001)",
SortOrder = 10
},
new()
{
EntityCode = "evento",
EntityName = "Evento",
Prefix = "EVT",
Pattern = "{PREFIX}{YYYY}-{SEQ:5}",
ResetSequenceYearly = true,
ModuleCode = "core",
Description = "Codice evento con anno (es. EVT2025-00001)",
SortOrder = 20
},
new()
{
EntityCode = "articolo",
EntityName = "Articolo (Legacy)",
Prefix = "ART",
Pattern = "{PREFIX}-{SEQ:5}",
ModuleCode = "core",
Description = "Codice articolo legacy (es. ART-00001)",
SortOrder = 30
},
// Warehouse module
new()
{
EntityCode = "warehouse_location",
EntityName = "Magazzino",
Prefix = "MAG",
Pattern = "{PREFIX}-{SEQ:3}",
ModuleCode = "warehouse",
Description = "Codice magazzino (es. MAG-001)",
SortOrder = 10
},
new()
{
EntityCode = "warehouse_article",
EntityName = "Articolo Magazzino",
Prefix = "WA",
Pattern = "{PREFIX}{SEQ:6}",
ModuleCode = "warehouse",
Description = "Codice articolo magazzino (es. WA000001)",
SortOrder = 20
},
new()
{
EntityCode = "warehouse_category",
EntityName = "Categoria Articolo",
Prefix = "CAT",
Pattern = "{PREFIX}-{SEQ:4}",
ModuleCode = "warehouse",
Description = "Codice categoria (es. CAT-0001)",
SortOrder = 30
},
new()
{
EntityCode = "stock_movement",
EntityName = "Movimento Magazzino",
Prefix = "MOV",
Pattern = "{PREFIX}{YYYY}{MM}-{SEQ:5}",
ResetSequenceMonthly = true,
ModuleCode = "warehouse",
Description = "Numero documento movimento (es. MOV202511-00001)",
SortOrder = 40
},
new()
{
EntityCode = "inventory_count",
EntityName = "Inventario",
Prefix = "INV",
Pattern = "{PREFIX}{YYYY}-{SEQ:4}",
ResetSequenceYearly = true,
ModuleCode = "warehouse",
Description = "Codice inventario (es. INV2025-0001)",
SortOrder = 50
},
new()
{
EntityCode = "article_batch",
EntityName = "Lotto Articolo",
Prefix = "LOT",
Pattern = "{PREFIX}{YYYY}{MM}{DD}-{SEQ:4}",
ResetSequenceMonthly = true,
ModuleCode = "warehouse",
Description = "Numero lotto (es. LOT20251129-0001)",
SortOrder = 60
},
// Future: Purchases module
new()
{
EntityCode = "purchase_order",
EntityName = "Ordine Acquisto",
Prefix = "ODA",
Pattern = "{PREFIX}{YYYY}-{SEQ:5}",
ResetSequenceYearly = true,
ModuleCode = "purchases",
Description = "Numero ordine acquisto (es. ODA2025-00001)",
SortOrder = 10,
IsEnabled = false // Disabilitato finché il modulo non è implementato
},
new()
{
EntityCode = "supplier",
EntityName = "Fornitore",
Prefix = "FOR",
Pattern = "{PREFIX}-{SEQ:5}",
ModuleCode = "purchases",
Description = "Codice fornitore (es. FOR-00001)",
SortOrder = 20,
IsEnabled = false
},
// Future: Sales module
new()
{
EntityCode = "sales_order",
EntityName = "Ordine Vendita",
Prefix = "ODV",
Pattern = "{PREFIX}{YYYY}-{SEQ:5}",
ResetSequenceYearly = true,
ModuleCode = "sales",
Description = "Numero ordine vendita (es. ODV2025-00001)",
SortOrder = 10,
IsEnabled = false
},
new()
{
EntityCode = "invoice",
EntityName = "Fattura",
Prefix = "FT",
Pattern = "{PREFIX}{YYYY}/{SEQ:5}",
ResetSequenceYearly = true,
ModuleCode = "sales",
Description = "Numero fattura (es. FT2025/00001)",
SortOrder = 20,
IsEnabled = false
},
};
foreach (var config in defaults)
{
var exists = await _db.AutoCodes.AnyAsync(c => c.EntityCode == config.EntityCode);
if (!exists)
{
config.CreatedAt = DateTime.Now;
_db.AutoCodes.Add(config);
_logger.LogInformation("Configurazione AutoCode creata per {EntityCode}", config.EntityCode);
}
}
await _db.SaveChangesAsync();
}
#region Private Methods
private bool ShouldResetSequence(AutoCode config, DateTime now)
{
if (config.ResetSequenceMonthly)
{
return config.LastResetYear != now.Year || config.LastResetMonth != now.Month;
}
if (config.ResetSequenceYearly)
{
return config.LastResetYear != now.Year;
}
return false;
}
private static string GenerateCodeFromPattern(string pattern, string? prefix, long sequence, DateTime date)
{
var code = pattern
.Replace("{PREFIX}", prefix ?? "")
.Replace("{YEAR}", date.Year.ToString())
.Replace("{YYYY}", date.Year.ToString())
.Replace("{YY}", date.Year.ToString().Substring(2))
.Replace("{MONTH}", date.Month.ToString("D2"))
.Replace("{MM}", date.Month.ToString("D2"))
.Replace("{DAY}", date.Day.ToString("D2"))
.Replace("{DD}", date.Day.ToString("D2"));
// Gestisci {SEQ:n}
code = SequencePattern.Replace(code, match =>
{
var digits = int.Parse(match.Groups[1].Value);
return sequence.ToString($"D{digits}");
});
return code;
}
private static void ValidatePattern(string pattern)
{
// Verifica che ci sia almeno un placeholder sequenza
if (!SequencePattern.IsMatch(pattern))
{
throw new ArgumentException(
"Il pattern deve contenere almeno un placeholder {SEQ:n} per la sequenza numerica");
}
// Verifica che i placeholder siano validi
var validPlaceholders = new[]
{
"{PREFIX}", "{YEAR}", "{YYYY}", "{YY}",
"{MONTH}", "{MM}", "{DAY}", "{DD}"
};
var placeholderRegex = new Regex(@"\{[^}]+\}");
var matches = placeholderRegex.Matches(pattern);
foreach (Match match in matches)
{
var placeholder = match.Value;
if (!SequencePattern.IsMatch(placeholder) && !validPlaceholders.Contains(placeholder))
{
throw new ArgumentException($"Placeholder non valido: {placeholder}");
}
}
}
#endregion
}
/// <summary>
/// DTO per l'aggiornamento di una configurazione AutoCode.
/// </summary>
public class AutoCodeUpdateDto
{
public string? Prefix { get; set; }
public string? Pattern { get; set; }
public bool? ResetSequenceYearly { get; set; }
public bool? ResetSequenceMonthly { get; set; }
public bool? IsEnabled { get; set; }
public bool? IsReadOnly { get; set; }
public string? Description { get; set; }
}

Binary file not shown.

Binary file not shown.

View File

@@ -2,7 +2,16 @@ namespace Apollinare.Domain.Entities;
public class Articolo : BaseEntity
{
/// <summary>
/// Codice articolo - generato automaticamente
/// </summary>
public string Codice { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? CodiceAlternativo { get; set; }
public string Descrizione { get; set; } = string.Empty;
public int? TipoMaterialeId { get; set; }
public int? CategoriaId { get; set; }

View File

@@ -0,0 +1,117 @@
namespace Apollinare.Domain.Entities;
/// <summary>
/// Configurazione per la generazione automatica di codici.
/// Ogni entità può avere la propria configurazione con pattern personalizzabile.
///
/// Pattern supportati:
/// - {SEQ:n} - Sequenza numerica con n cifre (es. {SEQ:5} → 00001)
/// - {YEAR} o {YYYY} - Anno corrente a 4 cifre
/// - {YY} - Anno corrente a 2 cifre
/// - {MONTH} o {MM} - Mese corrente a 2 cifre
/// - {DAY} o {DD} - Giorno corrente a 2 cifre
/// - {PREFIX} - Usa il prefisso definito
/// - Testo statico (es. "ART-", "-", "/")
///
/// Esempi di pattern:
/// - "ART-{YYYY}-{SEQ:5}" → ART-2025-00001
/// - "{PREFIX}{YY}{MM}{SEQ:4}" → MAG2511-0001
/// - "CLI/{YYYY}/{SEQ:6}" → CLI/2025/000001
/// </summary>
public class AutoCode : BaseEntity
{
/// <summary>
/// Codice univoco dell'entità (es. "warehouse_article", "warehouse_location", "cliente")
/// </summary>
public string EntityCode { get; set; } = string.Empty;
/// <summary>
/// Nome visualizzato dell'entità (es. "Articolo Magazzino", "Magazzino", "Cliente")
/// </summary>
public string EntityName { get; set; } = string.Empty;
/// <summary>
/// Prefisso opzionale da usare nel pattern con {PREFIX}
/// </summary>
public string? Prefix { get; set; }
/// <summary>
/// Pattern per la generazione del codice
/// </summary>
public string Pattern { get; set; } = "{PREFIX}{SEQ:5}";
/// <summary>
/// Ultimo numero di sequenza utilizzato (per {SEQ:n})
/// </summary>
public long LastSequence { get; set; } = 0;
/// <summary>
/// Se true, la sequenza viene resettata ogni anno
/// </summary>
public bool ResetSequenceYearly { get; set; } = false;
/// <summary>
/// Se true, la sequenza viene resettata ogni mese
/// </summary>
public bool ResetSequenceMonthly { get; set; } = false;
/// <summary>
/// Anno dell'ultimo reset della sequenza
/// </summary>
public int? LastResetYear { get; set; }
/// <summary>
/// Mese dell'ultimo reset della sequenza (se ResetSequenceMonthly)
/// </summary>
public int? LastResetMonth { get; set; }
/// <summary>
/// Se true, la generazione automatica è abilitata
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// Se true, il codice non può essere modificato manualmente
/// </summary>
public bool IsReadOnly { get; set; } = false;
/// <summary>
/// Modulo di appartenenza (es. "core", "warehouse", "purchases")
/// </summary>
public string? ModuleCode { get; set; }
/// <summary>
/// Descrizione della configurazione
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Ordine di visualizzazione nel pannello admin
/// </summary>
public int SortOrder { get; set; } = 0;
/// <summary>
/// Esempio di codice generato (calcolato, non persistito)
/// </summary>
public string GetExampleCode()
{
var now = DateTime.Now;
return Pattern
.Replace("{PREFIX}", Prefix ?? "")
.Replace("{YEAR}", now.Year.ToString())
.Replace("{YYYY}", now.Year.ToString())
.Replace("{YY}", now.Year.ToString().Substring(2))
.Replace("{MONTH}", now.Month.ToString("D2"))
.Replace("{MM}", now.Month.ToString("D2"))
.Replace("{DAY}", now.Day.ToString("D2"))
.Replace("{DD}", now.Day.ToString("D2"))
.Replace("{SEQ:1}", "X")
.Replace("{SEQ:2}", "XX")
.Replace("{SEQ:3}", "XXX")
.Replace("{SEQ:4}", "XXXX")
.Replace("{SEQ:5}", "XXXXX")
.Replace("{SEQ:6}", "XXXXXX")
.Replace("{SEQ:7}", "XXXXXXX")
.Replace("{SEQ:8}", "XXXXXXXX");
}
}

View File

@@ -2,6 +2,16 @@ namespace Apollinare.Domain.Entities;
public class Cliente : BaseEntity
{
/// <summary>
/// Codice cliente - generato automaticamente
/// </summary>
public string Codice { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? CodiceAlternativo { get; set; }
public string RagioneSociale { get; set; } = string.Empty;
public string? Indirizzo { get; set; }
public string? Cap { get; set; }

View File

@@ -6,10 +6,15 @@ namespace Apollinare.Domain.Entities.Warehouse;
public class WarehouseArticle : BaseEntity
{
/// <summary>
/// Codice univoco articolo (SKU)
/// Codice univoco articolo (SKU) - generato automaticamente
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? AlternativeCode { get; set; }
/// <summary>
/// Descrizione articolo
/// </summary>

View File

@@ -6,10 +6,15 @@ namespace Apollinare.Domain.Entities.Warehouse;
public class WarehouseArticleCategory : BaseEntity
{
/// <summary>
/// Codice categoria
/// Codice categoria - generato automaticamente
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? AlternativeCode { get; set; }
/// <summary>
/// Nome categoria
/// </summary>

View File

@@ -6,10 +6,15 @@ namespace Apollinare.Domain.Entities.Warehouse;
public class WarehouseLocation : BaseEntity
{
/// <summary>
/// Codice univoco del magazzino (es. "MAG01", "CENTRALE")
/// Codice univoco del magazzino - generato automaticamente
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Codice alternativo (opzionale, inserito dall'utente)
/// </summary>
public string? AlternativeCode { get; set; }
/// <summary>
/// Nome descrittivo del magazzino
/// </summary>

View File

@@ -41,6 +41,9 @@ public class AppollinareDbContext : DbContext
public DbSet<AppModule> AppModules => Set<AppModule>();
public DbSet<ModuleSubscription> ModuleSubscriptions => Set<ModuleSubscription>();
// Auto Code system
public DbSet<AutoCode> AutoCodes => Set<AutoCode>();
// Warehouse module entities
public DbSet<WarehouseLocation> WarehouseLocations => Set<WarehouseLocation>();
public DbSet<WarehouseArticle> WarehouseArticles => Set<WarehouseArticle>();
@@ -274,6 +277,13 @@ public class AppollinareDbContext : DbContext
.OnDelete(DeleteBehavior.Cascade);
});
// AutoCode
modelBuilder.Entity<AutoCode>(entity =>
{
entity.HasIndex(e => e.EntityCode).IsUnique();
entity.HasIndex(e => e.ModuleCode);
});
// ===============================================
// WAREHOUSE MODULE ENTITIES
// ===============================================

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Apollinare.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddAutoCodeSystem : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AutoCodes",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
EntityCode = table.Column<string>(type: "TEXT", nullable: false),
EntityName = table.Column<string>(type: "TEXT", nullable: false),
Prefix = table.Column<string>(type: "TEXT", nullable: true),
Pattern = table.Column<string>(type: "TEXT", nullable: false),
LastSequence = table.Column<long>(type: "INTEGER", nullable: false),
ResetSequenceYearly = table.Column<bool>(type: "INTEGER", nullable: false),
ResetSequenceMonthly = table.Column<bool>(type: "INTEGER", nullable: false),
LastResetYear = table.Column<int>(type: "INTEGER", nullable: true),
LastResetMonth = table.Column<int>(type: "INTEGER", nullable: true),
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
IsReadOnly = table.Column<bool>(type: "INTEGER", nullable: false),
ModuleCode = table.Column<string>(type: "TEXT", nullable: true),
Description = table.Column<string>(type: "TEXT", nullable: true),
SortOrder = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
CreatedBy = table.Column<string>(type: "TEXT", nullable: true),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
UpdatedBy = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AutoCodes", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_AutoCodes_EntityCode",
table: "AutoCodes",
column: "EntityCode",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AutoCodes_ModuleCode",
table: "AutoCodes",
column: "ModuleCode");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AutoCodes");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Apollinare.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddAlternativeCodeFields : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "AlternativeCode",
table: "WarehouseLocations",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "AlternativeCode",
table: "WarehouseArticles",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "AlternativeCode",
table: "WarehouseArticleCategories",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Codice",
table: "Clienti",
type: "TEXT",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "CodiceAlternativo",
table: "Clienti",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "CodiceAlternativo",
table: "Articoli",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AlternativeCode",
table: "WarehouseLocations");
migrationBuilder.DropColumn(
name: "AlternativeCode",
table: "WarehouseArticles");
migrationBuilder.DropColumn(
name: "AlternativeCode",
table: "WarehouseArticleCategories");
migrationBuilder.DropColumn(
name: "Codice",
table: "Clienti");
migrationBuilder.DropColumn(
name: "CodiceAlternativo",
table: "Clienti");
migrationBuilder.DropColumn(
name: "CodiceAlternativo",
table: "Articoli");
}
}
}

View File

@@ -98,6 +98,9 @@ namespace Apollinare.Infrastructure.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("CodiceAlternativo")
.HasColumnType("TEXT");
b.Property<DateTime?>("CreatedAt")
.HasColumnType("TEXT");
@@ -153,6 +156,79 @@ namespace Apollinare.Infrastructure.Migrations
b.ToTable("Articoli");
});
modelBuilder.Entity("Apollinare.Domain.Entities.AutoCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("EntityCode")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("EntityName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<bool>("IsReadOnly")
.HasColumnType("INTEGER");
b.Property<int?>("LastResetMonth")
.HasColumnType("INTEGER");
b.Property<int?>("LastResetYear")
.HasColumnType("INTEGER");
b.Property<long>("LastSequence")
.HasColumnType("INTEGER");
b.Property<string>("ModuleCode")
.HasColumnType("TEXT");
b.Property<string>("Pattern")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Prefix")
.HasColumnType("TEXT");
b.Property<bool>("ResetSequenceMonthly")
.HasColumnType("INTEGER");
b.Property<bool>("ResetSequenceYearly")
.HasColumnType("INTEGER");
b.Property<int>("SortOrder")
.HasColumnType("INTEGER");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("UpdatedBy")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("EntityCode")
.IsUnique();
b.HasIndex("ModuleCode");
b.ToTable("AutoCodes");
});
modelBuilder.Entity("Apollinare.Domain.Entities.Cliente", b =>
{
b.Property<int>("Id")
@@ -168,6 +244,13 @@ namespace Apollinare.Infrastructure.Migrations
b.Property<string>("Citta")
.HasColumnType("TEXT");
b.Property<string>("Codice")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("CodiceAlternativo")
.HasColumnType("TEXT");
b.Property<string>("CodiceDestinatario")
.HasColumnType("TEXT");
@@ -2170,6 +2253,9 @@ namespace Apollinare.Infrastructure.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AlternativeCode")
.HasColumnType("TEXT");
b.Property<string>("Barcode")
.HasColumnType("TEXT");
@@ -2315,6 +2401,9 @@ namespace Apollinare.Infrastructure.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AlternativeCode")
.HasColumnType("TEXT");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
@@ -2386,6 +2475,9 @@ namespace Apollinare.Infrastructure.Migrations
b.Property<string>("Address")
.HasColumnType("TEXT");
b.Property<string>("AlternativeCode")
.HasColumnType("TEXT");
b.Property<string>("City")
.HasColumnType("TEXT");