diff --git a/MP.Data/Controllers/MpSpecRepository.cs b/MP.Data/Controllers/MpSpecRepository.cs deleted file mode 100644 index 27b70da3..00000000 --- a/MP.Data/Controllers/MpSpecRepository.cs +++ /dev/null @@ -1,2029 +0,0 @@ -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using MP.Data.DbModels; -using NLog; -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Threading.Tasks; - -namespace MP.Data.Controllers -{ - public class MpSpecController - { - protected readonly IDbContextFactory _ctxFactory; - protected readonly IDbContextFactory _ctxFactoryFL; - #region Public Constructors - - public MpSpecController( - IConfiguration configuration, - IDbContextFactory ctxFactory, - IDbContextFactory ctxFactoryFL) - { - _configuration = configuration; - _ctxFactory = ctxFactory; - _ctxFactoryFL = ctxFactoryFL; -#if false - string connStr = _configuration.GetConnectionString("MP.Data"); - options = new DbContextOptionsBuilder() - .UseSqlServer(connStr) - .Options; -#endif - Log.Info("Avviata classe MpSpecController"); - } - - #endregion Public Constructors - - #region Public Methods - -#if false - /// - /// Stacca un nuovo counter x il tipo richiesto - /// - /// - public async Task AnagCountersGetNextAsync(string cntType) - { - AnagCountersModel answ = new AnagCountersModel(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - bool outTable = true; - if (outTable) - { - var pCntType = new SqlParameter("@CntType", cntType); - var pLastNum = new SqlParameter - { - ParameterName = "@LastNum", - SqlDbType = SqlDbType.Int, - Direction = ParameterDirection.Output - }; - - var dbResult = await dbCtx - .DbSetAnagCount - .FromSqlRaw("EXEC dbo.stp_getNextNumb @CntType, @LastNum OUTPUT", pCntType, pLastNum) - .AsNoTracking() - .FirstOrDefaultAsync(); - if (dbResult != null) - { - answ = dbResult; - } - } - else - { - // se si volessero impiegare parametri OUTPUT (qui ne mancherebbe 1 nella stored x CntCode...) - var pCntType = new SqlParameter("@CntType", cntType); - var pLastNum = new SqlParameter - { - ParameterName = "@LastNum", - SqlDbType = SqlDbType.Int, - Direction = ParameterDirection.Output - }; - var pCntCode = new SqlParameter - { - ParameterName = "@CntCode", - SqlDbType = SqlDbType.NVarChar, - Direction = ParameterDirection.Output - }; - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC dbo.stp_getNextNumb @CntType, @LastNum OUTPUT, @CntCode OUTPUT", pCntType, pLastNum, pCntCode); - if (dbResult != 0) - { - answ.CntType = cntType; - answ.CntCode = $"{pCntCode.Value}"; - int lNum = 0; - int.TryParse($"{pLastNum.Value}", out lNum); - answ.LastNum = lNum; - } - } - return answ; - } - - /// - /// Restituisce l'anagrafica EVENTI generalmente disponibile per OGNI macchina - /// - /// Nome Table x filtro (std: EvList) - /// Nome Field x filtro (std: Common) - /// - public async Task> AnagEventiGeneralAsync(string TableName = "EvList", string FieldName = "Common") - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var pTableName = new SqlParameter("@TableName", TableName); - var pFieldName = new SqlParameter("@FieldName", FieldName); - var dbResult = await dbCtx - .DbSetVSEB - .FromSqlRaw("exec dbo.stp_vseb_getGenerallyAvailable @TableName, @FieldName", pTableName, pFieldName) - .AsNoTracking() - .ToListAsync(); - return dbResult ?? new(); - } - - - /// - /// Elenco Gruppi tipo Azienda - /// - /// - public Task> AnagGruppiAziendeAsync() - { - return AnagGruppiGetTipoAsync("AZIENDA"); - } - - /// - /// Delete record AnagraficaGruppi - /// - /// - public async Task AnagGruppiDeleteAsync(AnagGruppiModel updRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var dbRec = await dbCtx - .DbSetAnagGruppi - .AsNoTracking() - .Where(x => x.CodGruppo == updRec.CodGruppo) - .FirstOrDefaultAsync(); - // se trovato aggiorno descrizione (resto immutato x sicurezza!) - if (dbRec != null) - { - dbCtx.DbSetAnagGruppi.Remove(dbRec); - } - var numRes = await dbCtx.SaveChangesAsync(); - return numRes != 0; - } - - /// - /// Elenco Gruppi tipo Fasi - /// - /// - public Task> AnagGruppiFaseAsync() - { - return AnagGruppiGetTipoAsync("FASE"); - } - - - /// - /// Gruppi x tipo modalità Async - /// - /// - /// - public async Task> AnagGruppiGetTipoAsync(string tipoGruppo) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetAnagGruppi - .Where(x => x.TipoGruppo == tipoGruppo) - .AsNoTracking() - .OrderBy(x => x.CodGruppo) - .ToListAsync(); - } - - /// - /// Elenco Gruppi tipo REPARTO (x associazione Macchine-Operatori) in formato DTO con conteggi del numero record trovati - /// - /// - public async Task> AnagGruppiRepartoDtoAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - // in primis recupero i reparti... - var listReparti = await AnagGruppiGetTipoAsync("REPARTO"); - - // recupero TUTTE le macchine da DbSetGrp2Macc - var listMacc = await dbCtx - .DbSetGrp2Macc - .AsNoTracking() - .ToListAsync(); - // recupero TUTTI gli operatori da DbSetGrp2Oper - var listOpr = await dbCtx - .DbSetGrp2Oper - .AsNoTracking() - .ToListAsync(); - - return listReparti - .Select(x => new RepartiDTO() - { - CodGruppo = x.CodGruppo, - TipoGruppo = x.TipoGruppo, - DescrGruppo = x.DescrGruppo, - SelEnabled = x.SelEnabled, - CountMacc = listMacc.Where(y => y.CodGruppo == x.CodGruppo).Select(x => x.IdxMacchina).Distinct().Count(), - CountOpr = listOpr.Where(y => y.CodGruppo == x.CodGruppo).Select(x => x.MatrOpr).Distinct().Count() - }) - .ToList(); - } - - /// - /// Upsert record AnagraficaGruppi (solo codice/descrizione) - /// - /// - /// - public async Task AnagGruppiUpsertAsync(AnagGruppiModel updRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var dbRec = await dbCtx - .DbSetAnagGruppi - .AsNoTracking() - .Where(x => x.CodGruppo == updRec.CodGruppo) - .FirstOrDefaultAsync(); - // se trovato aggiorno descrizione (resto immutato x sicurezza!) - if (dbRec != null) - { - dbRec.DescrGruppo = updRec.DescrGruppo; - dbCtx.Entry(dbRec).State = EntityState.Modified; - } - // altrimenti aggiungo - else - { - await dbCtx.DbSetAnagGruppi.AddAsync(updRec); - } - var numRes = await dbCtx.SaveChangesAsync(); - - return numRes != 0; - } - - - /// - /// Elenco valori ammessi x Stati commessa (es Yacht Baglietto) - /// - /// - public Task> AnagStatiCommAsync() - { - return ListValuesFiltAsync("PODL", "StatoComm"); - } - - /// - /// Elenco valori ammessi x Tipo articoli - /// - /// - public Task> AnagTipoArtLvAsync() - { - return ListValuesFiltAsync("AnagArticoli", "Tipo"); - } - -#endif - -#if false - /// - /// Elenco codice articoli che abbiano dati Dossier - /// - /// - public async Task> ArticleWithDossierAsync() - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - return await dbCtx - .DbSetDossiers - .AsNoTracking() - .Select(i => i.CodArticolo) - .Distinct() - .ToListAsync(); - } - -#endif - -#if false - /// - /// Conteggio num articoli Async - /// - /// - public async Task ArticoliCountAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var result = await dbCtx - .DbSetArticoli - .CountAsync(); - return result; - } - - /// - /// Conteggio articoli data condizione ricerca - /// - /// - /// - /// - /// - public async Task ArticoliCountSearchAsync(string tipoArt = "*", string azienda = "*", string searchVal = "") - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - IQueryable query = dbCtx.DbSetArticoli.AsNoTracking(); - - // filtro tipo articolo - if (tipoArt != "*") - { - //query = query.Where(x => x.Tipo.ToLower() == tipoArt.ToLower()); - query = query.Where(x => EF.Functions.Like(x.Tipo, tipoArt)); - } - // filtro azienda - if (azienda != "*") - { - //query = query.Where(x => x.Azienda.ToLower() == azienda.ToLower()); - query = query.Where(x => EF.Functions.Like(x.Azienda, azienda)); - } - // filtro ricerca - if (!string.IsNullOrWhiteSpace(searchVal)) - { - string pattern = $"%{searchVal}%"; - query = query.Where(x => - EF.Functions.Like(x.CodArticolo, pattern) || - EF.Functions.Like(x.DescArticolo, pattern) || - EF.Functions.Like(x.Disegno, pattern)); - } - - return await query - .OrderBy(x => x.CodArticolo) - .CountAsync(); - } - - /// - /// Elenco tabella Articoli IMPIEGATI (da stored stp_ART_getUsed) Async - /// - /// - public async Task ArticoliCountUsedAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var result = await dbCtx - .DbSetCounter - .FromSqlRaw("EXEC stp_ART_CountUsed") - .AsNoTracking() - .ToListAsync(); - - return result.FirstOrDefault()?.NumCount ?? 0; - } - - /// - /// Eliminazione Record - /// - /// - /// - public async Task ArticoliDeleteRecordAsync(AnagArticoliModel currRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currVal = dbCtx - .DbSetArticoli - .Where(x => x.CodArticolo == currRec.CodArticolo) - .FirstOrDefault(); - dbCtx - .DbSetArticoli - .Remove(currVal); - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Restitusice elenco articoli dato tipo (es KIT) - /// - /// - /// - /// - /// - public async Task> ArticoliGetByTipoAsync(string tipo, string azienda = "*") - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetArticoli - .AsNoTracking() - .Where(x => x.Tipo.ToUpper() == tipo.ToUpper() && (azienda == "*" || x.Azienda.ToUpper() == azienda.ToUpper())) - .OrderBy(x => x.CodArticolo) - .ToListAsync(); - } - - /// - /// Elenco tabella Articoli da filtro - /// - /// - /// - /// - /// - /// - public async Task> ArticoliGetSearchAsync(int numRecord, string tipoArt = "*", string azienda = "*", string searchVal = "") - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - IQueryable query = dbCtx.DbSetArticoli - .AsNoTracking(); - - // filtro tipo articolo - if (tipoArt != "*") - { - //query = query.Where(x => x.Tipo.ToLower() == tipoArt.ToLower()); - query = query.Where(x => EF.Functions.Like(x.Tipo, tipoArt)); - } - // filtro azienda - if (azienda != "*") - { - //query = query.Where(x => x.Azienda.ToLower() == azienda.ToLower()); - query = query.Where(x => EF.Functions.Like(x.Azienda, azienda)); - } - // filtro ricerca - if (!string.IsNullOrWhiteSpace(searchVal)) - { - string pattern = $"%{searchVal}%"; - query = query.Where(x => - EF.Functions.Like(x.CodArticolo, pattern) || - EF.Functions.Like(x.DescArticolo, pattern) || - EF.Functions.Like(x.Disegno, pattern)); - } - - return await query - .OrderBy(x => x.CodArticolo) - .Take(numRecord) - .ToListAsync(); - } - - /// - /// Elenco tabella Articoli NON IMPIEGATI (da stored stp_ART_getUsed) Async - /// - /// - public async Task> ArticoliGetUnusedAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetArticoli - .FromSqlRaw("EXEC stp_ART_getNotUsed") - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Elenco tabella Articoli IMPIEGATI (da stored stp_ART_getUsed) Async - /// - /// - public async Task> ArticoliGetUsedAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetArticoli - .FromSqlRaw("EXEC stp_ART_getUsed") - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Elenco Articoli che sono in KIT Child - /// - /// - public async Task> ArticoliInKitAsync() - { - List dbResult = new List(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - dbResult = await dbCtx - .DbSetArticoli - .FromSqlRaw("EXEC stp_TempKIT_getArtChild") - .AsNoTracking() - .ToListAsync(); - return dbResult; - } - - /// - /// Update Record - /// - /// - /// - public async Task ArticoliUpdateRecord(AnagArticoliModel editRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currRec = dbCtx - .DbSetArticoli - .Where(x => x.CodArticolo == editRec.CodArticolo) - .FirstOrDefault(); - if (currRec != null) - { - currRec.Disegno = editRec.Disegno; - currRec.DescArticolo = editRec.DescArticolo; - currRec.Tipo = editRec.Tipo; - currRec.Azienda = editRec.Azienda; - dbCtx.Entry(currRec).State = EntityState.Modified; - } - else - { - dbCtx - .DbSetArticoli - .Add(editRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } -#endif -#if false - - /// - /// Elenco da tabella Config Async - /// - /// - public async Task> ConfigGetAllAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetConfig - .AsNoTracking() - .OrderBy(x => x.Chiave) - .ToListAsync() ?? new(); - } - - /// - /// Update record config - /// - /// - public async Task ConfigUpdateAsync(ConfigModel updRec) - { - bool fatto = false; - ConfigModel dbResult = new ConfigModel(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - dbResult = await dbCtx - .DbSetConfig - .Where(x => x.Chiave == updRec.Chiave) - .FirstOrDefaultAsync(); - if (dbResult != null) - { - dbResult.Valore = updRec.Valore; - fatto = await dbCtx.SaveChangesAsync() > 0; - } - return fatto; - } - - /// - /// Eliminazione di un dossier - /// - /// record dossier da eliminare - /// - public async Task DossiersDeleteRecordAsync(DossierModel currRec) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - var currVal = await dbCtx - .DbSetDossiers - .Where(x => x.IdxDossier == currRec.IdxDossier) - .FirstOrDefaultAsync(); - dbCtx - .DbSetDossiers - .Remove(currVal); - - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elenco ultimi n record DOssiers (che contengono ad esempio "salvataggi" di FLuxLog) dato - /// macchina (ordinato x data registrazione) - /// - /// * = tutte, altrimenti solo x una data macchina - /// * = tutti, altrimenti solo x un dato articolo - /// Data minima per estrazione records - /// Data Massima per estrazione records - /// Num max record recuperati - /// - public async Task> DossiersGetLastFiltAsync(string IdxMacchina, string CodArticolo, DateTime DtStart, DateTime DtEnd, int MaxRec) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - return await dbCtx - .DbSetDossiers - .AsNoTracking() - .Where(x => (IdxMacchina == "*" || x.IdxMacchina == IdxMacchina) && (CodArticolo == "*" || x.CodArticolo == CodArticolo) && (x.DtRif >= DtStart && x.DtRif <= DtEnd)) - .Include(m => m.MachineNav) - .Include(a => a.ArticoloNav) - .OrderByDescending(x => x.DtRif) - .Take(MaxRec) - .ToListAsync(); - } - - /// - /// insert di un record Dossier - /// - /// record dossier da modificare - /// - public async Task DossiersInsertAsync(DossierModel newRec) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - dbCtx - .DbSetDossiers - .AddAsync(newRec); - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Effettua salvataggio snapshot parametri (con stored) + svuota eventuale cache _redisConn - /// - /// macchina - /// Data min x selezione - /// Data MAX x selezione - public async Task DossiersTakeParamsSnapshotLastAsync(string idxMacchina, DateTime dtMin, DateTime dtMax) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - var pIdxMacchina = new SqlParameter("@IdxMacchina", idxMacchina); - var pDtMin = new SqlParameter("@DtMin", dtMin); - var pDtMax = new SqlParameter("@DtMax", dtMax); - - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC stp_FL_TakeSnapshotLast @IdxMacchina,@DtMin,@DtMax", pIdxMacchina, pDtMin, pDtMax); - return dbResult != 0; - } - - /// - /// Update del campo VALORE di un dossier (che contiene json flux log serializzati) - /// - /// record dossier da modificare - /// - public async Task DossiersUpdateValoreAsync(DossierModel editRec) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - var currRec = await dbCtx - .DbSetDossiers - .Where(x => x.IdxDossier == editRec.IdxDossier) - .FirstOrDefaultAsync(); - if (currRec != null) - { - currRec.Valore = editRec.Valore; - dbCtx.Entry(currRec).State = EntityState.Modified; - } - else - { - await dbCtx - .DbSetDossiers - .AddAsync(editRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elenco valori link (x home e navMenu laterale) - /// - /// - public Task> ElencoLinkAsync() - { - return ListLinkFiltAsync("SpecLink"); - } - - /// - /// Aggiunta record EventList - /// - /// - /// - public async Task EvListInsertAsync(EventListModel newRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currRec = await dbCtx - .DbSetEvList - .AddAsync(newRec); - - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Funzione di Data Reduction x FluxLog - /// - /// - /// - /// - /// - /// - /// - /// Restitusice list dei record statistiche raccolti (da integrare a quelli rpesenti in Redis...) - /// - public async Task> FluxLogDataReduxAsync(string idxMaccSel, List fluxList, Periodo currPeriodo, Enums.ValSelection valMode, Enums.DataInterval intReq, int maxItem) - { - List procStats = new List(); - Log.Info($"Inizio FluxLogDataReduxAsync | idxMaccSel: {idxMaccSel} | periodo: {currPeriodo.Inizio:yyyy-MM-dd} --> {currPeriodo.Fine:yyyy-MM-dd}"); - TimeSpan step = TimeSpan.FromHours(1); - switch (intReq) - { - case Enums.DataInterval.minute: - step = TimeSpan.FromMinutes(1.00 / maxItem); - break; - - case Enums.DataInterval.hour: - step = TimeSpan.FromHours(1.00 / maxItem); - break; - - case Enums.DataInterval.day: - step = TimeSpan.FromDays(1.00 / maxItem); - break; - - default: - break; - } - - // setup parametri costanti x stored - var pIdxMacchina = new SqlParameter("@IdxMacchina", idxMaccSel); - var pOnlyTest = new SqlParameter("@OnlyTest", false); - - // dbContext condiviso - using var dbCtx = new MoonPro_FluxContext(_configuration); - // opzionalmente timeout comandi a 2 minuti... NON usato x ora e da testare - //dbCtx.Database.SetCommandTimeout(TimeSpan.FromMinutes(2)); - // processo 1:1 ogni flusso - foreach (var item in fluxList) - { - Log.Info($"FluxLogDataReduxAsync | Flux: {item}"); - int numRecProc = 0; - Stopwatch sw = new Stopwatch(); - sw.Start(); - // parametri x flusso - var pCodFlux = new SqlParameter("@CodFlux", item); - // inizializzo cursore timer - DateTime dtCursStart = currPeriodo.Inizio; - DateTime dtCursEnd = dtCursStart.Add(step); - bool setCompleted = false; - // li processo per intervallo richiesto, cercando dati nel periodo eselezionando VC - while (!setCompleted) - { - // ora recupero TUTTI i dati della macchina - var currFlux = await dbCtx - .DbSetFluxLog - .Where(x => (x.CodFlux == item) && (x.dtEvento >= dtCursStart && x.dtEvento < dtCursEnd) && (x.IdxMacchina == idxMaccSel)) - .ToListAsync(); - - int numRec = currFlux.Count; - numRecProc += numRec; - if (numRec > maxItem) - { - List listPeriodi = new List(); - - switch (valMode) - { - case Enums.ValSelection.First: - // recupero 2° item - var recStart = currFlux.Skip(1).FirstOrDefault(); - // salvo periodo! - listPeriodi.Add(new Periodo(recStart.dtEvento, dtCursEnd)); - break; - - case Enums.ValSelection.Last: - // recupero ultimo item - var recEnd = currFlux.LastOrDefault(); - // salvo periodo! - listPeriodi.Add(new Periodo(dtCursStart, recEnd.dtEvento)); - break; - - case Enums.ValSelection.Center: - int idx = 1; - // per iniziare mi metto a 1/(n+1) rec come step - var recCent = currFlux.Skip(idx / (maxItem + 1)).FirstOrDefault(); - listPeriodi.Add(new Periodo(dtCursStart, recCent.dtEvento)); - // salvo restanti periodi (se > 1)! - if (maxItem > 1) - { - for (int i = 2; i < maxItem; i++) - { - DateTime dtInizio = recCent.dtEvento; - recCent = currFlux.Skip(i / (maxItem + 1)).FirstOrDefault(); - listPeriodi.Add(new Periodo(dtInizio, recCent.dtEvento)); - } - } - // aggiungo ultimo... - listPeriodi.Add(new Periodo(recCent.dtEvento.AddSeconds(1), dtCursEnd)); - break; - - default: - break; - } - - // ciclo x tutti i periodi e chiamo stored... - foreach (var slot in listPeriodi) - { - // parametri x periodo (base) - var pDtStart = new SqlParameter("@DtStart", slot.Inizio); - var pDtEnd = new SqlParameter("@DtEnd", slot.Fine); - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC man.stp_ReduceFluxLog @IdxMacchina, @CodFlux, @DtStart, @DtEnd, @OnlyTest", pIdxMacchina, pCodFlux, pDtStart, pDtEnd, pOnlyTest); - } - } - - // incremento dt fine periodo - dtCursStart = dtCursEnd; - dtCursEnd = dtCursStart.Add(step); - setCompleted = dtCursStart >= currPeriodo.Fine; - } - // fermo cronometro e salvo su DB... - sw.Stop(); - StatDedupDTO currStat = new StatDedupDTO() - { - IdxMacchina = idxMaccSel, - CodFlux = item, - Interval = intReq, - Num4Int = maxItem, - NumRec = numRecProc, - ProcTime = sw.Elapsed.TotalSeconds - }; - procStats.Add(currStat); - } - Log.Info($"FINE FluxLogDataReduxAsync | idxMaccSel: {idxMaccSel} | periodo: {currPeriodo.Inizio:yyyy-MM-dd} --> {currPeriodo.Fine:yyyy-MM-dd}"); - return procStats; - } - - /// - /// Elenco ultimi n record flux log dato macchina e flusso (ordinato x data registrazione) - /// - /// Data massima x eventi - /// Data minima x eventi - /// * = tutte, altrimenti solo x una data macchina - /// *=tutti, altrimenti solo selezionato - /// numero massimo record da restituire - /// - public async Task> FluxLogGetLastFiltAsync(DateTime DtMax, DateTime DtMin, string IdxMacchina, string CodFlux, int MaxRec) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - return await dbCtx - .DbSetFluxLog - .AsNoTracking() - .Where(x => (x.dtEvento >= DtMin && x.dtEvento <= DtMax) && (IdxMacchina == "*" || x.IdxMacchina == IdxMacchina) && (CodFlux == "*" || x.CodFlux == CodFlux)) - .OrderByDescending(x => x.dtEvento) - .Take(MaxRec) - .ToListAsync() ?? new(); - } - - /// - /// Elenco Gruppi - /// - /// - public async Task> FluxLogParetoAsync(string idxMacchina, DateTime dtFrom, DateTime dtTo) - { - using var dbCtx = new MoonPro_FluxContext(_configuration); - return await dbCtx - .DbSetFluxLog - .Where(x => (string.IsNullOrEmpty(idxMacchina) || x.IdxMacchina == idxMacchina) && (dtFrom <= x.dtEvento && x.dtEvento <= dtTo)) - .AsNoTracking() - .GroupBy(x => x.CodFlux) - .Select(g => new ParetoFluxLogDTO() { IdxMacchina = idxMacchina, CodFlux = g.Key, Qty = g.Count() }) - .OrderByDescending(x => x.Qty) - .ToListAsync() ?? new(); - } - - /// - /// Stored manutenzione del DB - /// - /// Esegue realmente il task - /// Aggiornamento statistiche - /// Salvataggio - /// def: 1000 - /// def: 10 - /// def: 50 - /// - public async Task ForceDbMaintAsync(bool doExec, bool doUpdStat, bool doSave, int minPgCnt, int minAvgFrag, int maxAvgFragReb) - { - Log.Info($"Inizio ForceDbMaintAsync on MoonProAdminContext"); - // uso context admin x query lunghe - using var dbCtx = new MoonProAdminContext(_configuration); - var pFlgExec = new SqlParameter("@FlgExec", doExec ? "Y" : "N"); - var pFlgUpdStat = new SqlParameter("@FlgUpdStat", doUpdStat ? "Y" : "N"); - var pFlgSave = new SqlParameter("@FlgSave", doSave ? "Y" : "N"); - var pMinPgCnt = new SqlParameter("@min_page_count", minPgCnt); - var pMinAvgFrag = new SqlParameter("@min_avg_fragmentation_in_percent", minAvgFrag); - var pMaxAvgFrag = new SqlParameter("@max_avg_fragmentation_per_rebuild", maxAvgFragReb); - - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC man.stp_Utility_Maintanance"); - //.ExecuteSqlRaw("EXEC man.stp_Utility_Maintanance @FlgExec, @FlgUpdStat, @FlgSave, @min_page_count, @min_avg_fragmentation_in_percent, @max_avg_fragmentation_per_rebuild", pFlgExec, pFlgUpdStat, pFlgSave, pMinPgCnt, pMinAvgFrag, pMaxAvgFrag); - Log.Info($"FINE ForceDbMaintAsync on MoonProAdminContext"); - return dbResult != 0; - } - - /// - /// Eliminazione di un record macchina dal gruppo - /// - /// - /// - public async Task Grp2MaccDeleteAsync(Gruppi2MaccModel rec2del) - { - bool answ = false; - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var dbRec = await dbCtx - .DbSetGrp2Macc - .Where(x => x.CodGruppo == rec2del.CodGruppo && x.IdxMacchina == rec2del.IdxMacchina) - .FirstOrDefaultAsync(); - if (dbRec != null) - { - dbCtx.DbSetGrp2Macc.Remove(dbRec); - int numDone = await dbCtx.SaveChangesAsync(); - answ = numDone != 0; - } - return answ; - } - - /// - /// Insert di un record macchina - /// - /// - /// - public async Task Grp2MaccInsertAsync(Gruppi2MaccModel upsRec) - { - bool answ = false; - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var dbRec = await dbCtx - .DbSetGrp2Macc - .Where(x => x.CodGruppo == upsRec.CodGruppo && x.IdxMacchina == upsRec.IdxMacchina) - .FirstOrDefaultAsync(); - if (dbRec == null) - { - await dbCtx.DbSetGrp2Macc.AddAsync(upsRec); - // salvo - int numDone = await dbCtx.SaveChangesAsync(); - answ = numDone != 0; - } - return answ; - } - - /// - /// Eliminazione di un record operatore dal gruppo - /// - /// - /// - public async Task Grp2OperDeleteAsync(Gruppi2OperModel rec2del) - { - bool answ = false; - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var dbRec = await dbCtx - .DbSetGrp2Oper - .Where(x => x.CodGruppo == rec2del.CodGruppo && x.MatrOpr == rec2del.MatrOpr) - .FirstOrDefaultAsync(); - if (dbRec != null) - { - dbCtx.DbSetGrp2Oper.Remove(dbRec); - int numDone = await dbCtx.SaveChangesAsync(); - answ = numDone != 0; - } - return answ; - } - - /// - /// Insert di un record operatore - /// - /// - /// - public async Task Grp2OperInsertAsync(Gruppi2OperModel upsRec) - { - bool answ = false; - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var dbRec = await dbCtx - .DbSetGrp2Oper - .Where(x => x.CodGruppo == upsRec.CodGruppo && x.MatrOpr == upsRec.MatrOpr) - .FirstOrDefaultAsync(); - if (dbRec == null) - { - await dbCtx.DbSetGrp2Oper.AddAsync(upsRec); - // salvo - int numDone = await dbCtx.SaveChangesAsync(); - answ = numDone != 0; - } - return answ; - } - - /// - /// Elimina record - /// - /// - public async Task IstKitDeleteAsync(IstanzeKitModel rec2del) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetInstKit - .Where(x => x.KeyKit == rec2del.KeyKit && x.KeyExtOrd == rec2del.KeyExtOrd) - .FirstOrDefaultAsync(); - // se ci fosse aggiorno... - if (actRec != null) - { - dbCtx - .DbSetInstKit - .Remove(actRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elenco istanze KIT da ricerca - /// - /// - /// - /// - public async Task> IstKitFiltAsync(string keyKit, string keyExtOrd) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetInstKit - .Where(x => (string.IsNullOrEmpty(keyKit) && string.IsNullOrEmpty(keyExtOrd)) || (x.KeyKit.Contains(keyKit) && !string.IsNullOrEmpty(keyKit)) || (x.KeyExtOrd.Contains(keyExtOrd) && !string.IsNullOrEmpty(keyExtOrd))) - .AsNoTracking() - .ToListAsync() ?? new(); - } - - /// - /// Effettua creazione istanza KIT - /// - /// Articolo KIT (fittizio) - /// Chiave x filtro conf su tab WKS - public async Task IstKitInsertByWKSAsync(string CodArtParent, string KeyFilt) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - - var pCodArtParent = new SqlParameter("@CodArtParent", CodArtParent); - var pKeyFilt = new SqlParameter("@KeyFilt", KeyFilt); - - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC dbo.stp_IstKit_insertByWKS @CodArtParent,@KeyFilt", pCodArtParent, pKeyFilt); - return dbResult != 0; - } - - /// - /// Esegue upsert record - /// - /// - public async Task IstKitUpsertAsync(IstanzeKitModel editRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetInstKit - .Where(x => x.KeyKit == editRec.KeyKit && x.KeyExtOrd == editRec.KeyExtOrd) - .FirstOrDefaultAsync(); - - // se ci fosse aggiorno... - if (actRec == null) - { - await dbCtx - .DbSetInstKit - .AddAsync(editRec); - } - else - { - actRec.CodArtParent = editRec.CodArtParent; - actRec.CodArtChild = editRec.CodArtChild; - actRec.QtyART = editRec.QtyART; - actRec.QtyKIT = editRec.QtyKIT; - dbCtx.Entry(actRec).State = EntityState.Modified; - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elenco giacenze - /// - /// id odl da cercare - /// - public async Task> ListGiacenzeAsync(int IdxOdl) - { - List dbResult = new List(); - using var dbCtx = new MoonPro_InveContext(_configuration); - dbResult = await dbCtx - .DbGiacenzeData - .Where(x => x.IdxOdl == IdxOdl) - .AsNoTracking() - .ToListAsync(); - return dbResult; - } - - /// - /// Elenco link JQM completo - /// - /// - /// - public async Task> ListLinkAllAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetLinkMenu - .AsNoTracking() - .OrderBy(x => x.Ordine) - .ToListAsync(); - } - - /// - /// Elenco link JQM dato filtro tipo, Async - /// - /// - /// - public async Task> ListLinkFiltAsync(string tipoLink) - { - List dbResult = new List(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetLinkMenu - .Where(x => x.TipoLink == tipoLink) - .AsNoTracking() - .OrderBy(x => x.Ordine) - .ToListAsync(); - } - -#endif - - /// - /// Elenco ODL filtrati x stato, articolo, KeyRich (che contiene stato) - /// - /// Stato ODL: true=in corso/completato - /// Cod articolo - /// KeyRich (parziale) da cercare (es cod stato x yacht) - /// Reparto selezionato - /// Macchina selezionata - /// Data inizio - /// Data fine - /// - public async Task> ListODLFiltAsync(bool inCorso, string codArt, string keyRichPart, string Reparto, string IdxMacchina, DateTime startDate, DateTime endDate) - { - List dbResult = new List(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - - var InCorso = new SqlParameter("@InCorso", inCorso); - var CodArt = new SqlParameter("@CodArt", codArt); - var KeyRich = new SqlParameter("@KeyRich", keyRichPart); - var CodGruppo = new SqlParameter("@CodGruppo", Reparto); - var IdxMacc = new SqlParameter("@IdxMacchina", IdxMacchina); - var DataFrom = new SqlParameter("@DataFrom", startDate); - var DataTo = new SqlParameter("@DataTo", endDate); - - return await dbCtx - .DbSetODLExp - .FromSqlRaw("EXEC stp_ODL_getByFiltSpec @InCorso, @CodArt, @KeyRich, @CodGruppo, @IdxMacchina, @DataFrom, @DataTo", InCorso, CodArt, KeyRich, CodGruppo, IdxMacc, DataFrom, DataTo) - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Recupero elenco PODL EXPL filtrati - /// - /// - /// True = aperti (=senza ODL) - /// - public async Task> ListPODL_ByCodArtAsync(string CodArticolo, bool OnlyAvail) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var pCodArticolo = new SqlParameter("@CodArticolo", CodArticolo); - var pOnlyAvail = new SqlParameter("@onlyAvail", OnlyAvail); - - return await dbCtx - .DbSetPODLExp - .FromSqlRaw("EXEC stp_PODL_getByCodArt @CodArticolo, @onlyAvail", pCodArticolo, pOnlyAvail) - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Elenco PODL in un istanza KIT dall'ID del parent - /// - /// IDX PODL parent - /// - public async Task> ListPODL_ByKitParentAsync(int IdxPodlParent) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var pIdxPodlParent = new SqlParameter("@IdxPodlParent", IdxPodlParent); - - return await dbCtx - .DbSetPODLExp - .FromSqlRaw("EXEC stp_PODL_getByParentKitIdx @IdxPodlParent", pIdxPodlParent) - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Elenco PODL per composizione KIT non avviati filtrati x articolo, KeyRich (che contiene stato) - /// - /// Solo lanciati (1) o ancora disponibili (0) - /// KeyRich (parziale) da cercare (es cod stato x yacht) - /// Macchina - /// Gruppo - /// - public async Task> ListPODL_KitFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var Lanc = new SqlParameter("@Lanciato", lanciato); - var KeyRich = new SqlParameter("@KeyRich", keyRichPart); - var CodGrp = new SqlParameter("@CodGruppo", codGruppo); - var IdxMacc = new SqlParameter("@IdxMacchina", idxMacchina); - var DateFrom = new SqlParameter("@DtInizio", startDate); - var DateTo = new SqlParameter("@DtFine", endDate); - - return await dbCtx - .DbSetPODLExp - .FromSqlRaw("EXEC stp_PODL_getByFiltSpecKit @Lanciato, @KeyRich, @CodGruppo, @IdxMacchina, @DtInizio, @DtFine", Lanc, KeyRich, CodGrp, IdxMacc, DateFrom, DateTo) - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Elenco PODL non avviati filtrati x articolo, KeyRich (che contiene stato) - ASYNC - /// - /// Solo lanciati (1) o ancora disponibili (0) - /// KeyRich (parziale) da cercare (es cod stato x yacht) - /// Macchina - /// Gruppo - /// - public async Task> ListPODLFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var Lanc = new SqlParameter("@Lanciato", lanciato); - var KeyRich = new SqlParameter("@KeyRich", keyRichPart); - var CodGrp = new SqlParameter("@CodGruppo", codGruppo); - var IdxMacc = new SqlParameter("@IdxMacchina", idxMacchina); - var DateFrom = new SqlParameter("@DtInizio", startDate); - var DateTo = new SqlParameter("@DtFine", endDate); - - return await dbCtx - .DbSetPODLExp - .FromSqlRaw("EXEC stp_PODL_getByFiltSpec @Lanciato, @KeyRich, @CodGruppo, @IdxMacchina, @DtInizio, @DtFine", Lanc, KeyRich, CodGrp, IdxMacc, DateFrom, DateTo) - .AsNoTracking() - .ToListAsync(); - } - - /// - /// Elenco valori ammessi x tabella/colonna Async - /// - /// - /// - /// - public async Task> ListValuesFiltAsync(string tabName, string fieldName) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetListValues - .Where(x => x.TableName == tabName && x.FieldName == fieldName) - .AsNoTracking() - .OrderBy(x => x.ordinal) - .ToListAsync(); - } - - /// - /// Elenco Macchine dato operatore secondo gruppi (macchine/operatore) - /// - /// - /// - public async Task> MacchineByMatrOperAsync(int MatrOpr) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - if (MatrOpr == 0) - { - return await dbCtx - .DbSetMacchine - .AsNoTracking() - .OrderBy(x => x.IdxMacchina) - .ToListAsync(); - } - else - { - return await dbCtx - .DbSetGrp2Oper - .Where(g => g.MatrOpr == MatrOpr) - .Join(dbCtx.DbSetGrp2Macc, - g => g.CodGruppo, - m => m.CodGruppo, - (g, m) => m - ) - .Distinct() - .Join(dbCtx.DbSetMacchine, - g => g.IdxMacchina, - m => m.IdxMacchina, - (g, m) => m - ) - .Distinct() - .AsNoTracking() - .OrderBy(x => x.IdxMacchina) - .ToListAsync(); - } - } - - /// - /// Elenco da tabella Macchine filtro x gruppo - /// - /// - /// - public async Task> MacchineGetFiltAsync(string codGruppo) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - if (codGruppo == "*") - { - return await dbCtx - .DbSetMacchine - .AsNoTracking() - .OrderBy(x => x.IdxMacchina) - .ToListAsync(); - } - else - { - return await dbCtx - .DbSetGrp2Macc - .Where(g => g.CodGruppo == codGruppo) - .Join(dbCtx.DbSetMacchine, - g => g.IdxMacchina, - m => m.IdxMacchina, - (g, m) => m - ) - .AsNoTracking() - .OrderBy(x => x.IdxMacchina) - .ToListAsync(); - } - } - - /// - /// Elenco id MacchineModel che abbiano dati FLuxLog, nel periodo indicato - /// - /// - /// - /// - public async Task> MacchineWithFluxAsync(DateTime dtStart, DateTime dtEnd) - { - using var dbCtx = await _ctxFactoryFL.CreateDbContextAsync(); - return await dbCtx - .DbSetFluxLog - .AsNoTracking() - .Where(x => x.dtEvento >= dtStart && x.dtEvento <= dtEnd) - .Select(i => i.IdxMacchina) - .Distinct() - .ToListAsync() ?? new(); - } - - /// - /// Elenco da tabella MappaStatoExplModel - /// - /// - public async Task> MseGetAllAsync(int maxAge = 2000) - { - List dbResult = new List(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - - var maxAgeSec = new SqlParameter("@maxAgeSec", maxAge); - - dbResult = await dbCtx - .DbSetMSE - .FromSqlRaw("EXEC stp_MSE_getData @maxAgeSec", maxAgeSec) - .AsNoTracking() - .ToListAsync(); - - return dbResult; - } - - /// - /// Elenco ODL dato batch selezionato - /// - /// Batch richiesto - /// - public async Task> OdlByBatchAsync(string batchSel) - { - using var dbCtx = new MoonPro_InveContext(_configuration); - return await dbCtx - .DbGiacenzeData - .AsNoTracking() - .Where(x => x.IdentRG == batchSel) - .Select(x => x.IdxOdl) - .ToListAsync(); - } - - /// - /// ODL da chiave - /// - /// - public async Task OdlByKeyAsync(int IdxOdl) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetODLExp - .AsNoTracking() - .FirstOrDefaultAsync(x => x.IdxOdl == IdxOdl); - } - - /// - /// Chiusura ODL con eventuale conferma pezzi - /// - /// idx odl da chiudere - /// idx macchina - /// matricola operatore - /// indica se confermare i pezzi prima di chiudere ODL - /// Conferma con rettifica (ev 121) x pezzi lasciati in macchina - /// Modo conferma produzione (0=periodo, 1=giorno, 2=turno) - /// - public async Task ODLCloseAsync(int idxOdl, string idxMacchina, int matrOpr, bool confPezzi, bool confRett, int modoConfProd) - { - bool fatto = false; - if (idxOdl > 0) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - DateTime adesso = DateTime.Now; - // preparo i parametri - var IdxODL = new SqlParameter("@IdxODL", idxOdl); - var IdxMacchina = new SqlParameter("@IdxMacchina", idxMacchina); - - // se richiesto confermo produzione - if (confPezzi) - { - var MatrApp = new SqlParameter("@MatrApp", idxMacchina); - - /* ---------------------------------- - * CONFERMA PEZZI - * - * condizioni da verificare: - * - gestione rettifica (ev121) / pezzi da LASCIARE in macchina - * - conferma a zero pezzi (setup) oppure con i pezzi fatti e non ancora confermati - * - * - * - * */ - - // recupero i dati dei pezzi da confermare... con DbSetPzProd + exec - // stp_PzProd_getByMacchina 'SIMUL_01' - - // stp_ConfermaProduzCompletaFull - /* - * @idxMacchina NVARCHAR(50), - @MatrApp INT, - @dataFrom DATETIME, - @dataTo DATETIME, - @pezziConf INT, - @pezziLasciati INT, -- pezzi lasciati = evento 121 (-) pre conferma e (+) dopo --> da lasciare in macchina post conferma - @pezziScar INT = 0, -- pezzi scartati (registrati da 2016.11.20) DA INDICARE COME VALORE > 0!!! sennò faccio ABS... - @TipoConf INT = 0, -- Tipo intervallo conferma: 0 = periodo intero, 1 = per giorni, 2 = per turni - @DataOraApp DATETIME = NULL, -- di norma GETDATE() nel programma - serve per ricalcolo - @TestConferma BIT = 1 -- TestConferma : 1 = verifica conf. duplicata e inserisci in ElencoConfermeProd, 0 = nessuna verifica e inserimento ( per ricalcolo ) - */ - } - - // ora chiudo ODL con stored SENZA ritorno... - try - { - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC stp_ODL_fineProd @IdxODL, @IdxMacchina", IdxODL, IdxMacchina); - fatto = dbResult != 0; - } - catch (Exception exc) - { - Log.Error($"Eccezione durante ODLCloseAsync{Environment.NewLine}{exc}"); - } - } - return fatto; - } - - /// - /// Recupero Odl CORRENTI - /// - /// - public async Task> OdlGetCurrentAsync() - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetODL - .Where(x => x.DataInizio != null && x.DataFine == null) - .ToListAsync(); - } - - /// - /// Statistiche ODL calcolate (da stored stp_STAT_ODL) - /// - /// - public async Task> OdlGetStatAsync(int IdxOdl) - { - List dbResult = new List(); - if (IdxOdl > 0) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var IdxODL = new SqlParameter("@IdxODL", IdxOdl); - - dbResult = await dbCtx - .DbSetStatOdl - .FromSqlRaw("EXEC stp_STAT_ODL @IdxODL", IdxODL) - .AsNoTracking() - .ToListAsync(); - } - return dbResult; - } - - /// - /// Elenco da tabella Operatori filtro x gruppo - /// - /// - /// - public async Task> OperatoriGetFiltAsync(string codGruppo) - { - List dbResult = new List(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - if (codGruppo == "*") - { - dbResult = await dbCtx - .DbOperatori - .AsNoTracking() - .OrderBy(x => x.MatrOpr) - .ToListAsync(); - } - else - { - dbResult = await dbCtx - .DbSetGrp2Oper - .Where(g => g.CodGruppo == codGruppo) - .Join(dbCtx.DbOperatori, - g => g.MatrOpr, - m => m.MatrOpr, - (g, m) => m - ) - .AsNoTracking() - .OrderBy(x => x.MatrOpr) - .ToListAsync(); - } - return dbResult; - } - - /// - /// Elenco parametri validi x una data macchina - /// - /// - /// - public async Task> ParametriGetFiltAsync(string IdxMacchina) - { - using var dbCtx = await _ctxFactoryFL.CreateDbContextAsync(); - return await dbCtx - .DbSetFluxLog - .AsNoTracking() - .Where(x => (IdxMacchina == "*" || x.IdxMacchina == IdxMacchina)) - .Take(1000) - .Select(i => i.CodFlux) - .Distinct() - .OrderBy(x => x) - .ToListAsync(); - } - - /// - /// Recupero PODL da chiave - /// - /// - /// - public async Task PODL_getByKeyAsync(int idxPODL) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetPODL - .AsNoTracking() - .Where(x => x.IdxPromessa == idxPODL) - .Include(a => a.ArticoloNav) - .FirstOrDefaultAsync() ?? new(); - } - - /// - /// Recupero PODL da IdxOdl - /// - /// - /// - public async Task PODL_getByOdlAsync(int idxODL) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetPODL - .AsNoTracking() - .Where(x => x.IdxOdl == idxODL) - .FirstOrDefaultAsync() ?? new(); - } - - /// - /// Dizionario associazione ODL/PODL - /// - /// - /// - public async Task> PODL_getDictOdlPodlAsync(List missingIds) - { - if (missingIds == null || !missingIds.Any()) - return new Dictionary(); - - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetPODL - .AsNoTracking() - .Where(x => missingIds.Contains(x.IdxOdl)) - .ToDictionaryAsync(x => x.IdxOdl, x => x.IdxPromessa); - } - - /// - /// Avvio setup ODL da PODL - /// - /// - /// - /// - /// - /// - /// - public async Task PODL_startSetup(PODLExpModel editRec, int matrOpr, double tcRich, int pzPallet, string note, DateTime dtEvent) - { - bool answ = false; - PODLModel recPODL = new PODLModel() - { - IdxPromessa = editRec.IdxPromessa, - KeyRichiesta = editRec.KeyRichiesta, - KeyBCode = editRec.KeyBCode, - IdxOdl = editRec.IdxOdl, - CodArticolo = editRec.CodArticolo, - CodGruppo = editRec.CodGruppo, - IdxMacchina = editRec.IdxMacchina, - NumPezzi = editRec.NumPezzi, - Tcassegnato = editRec.Tcassegnato, - DueDate = editRec.DueDate, - Priorita = editRec.Priorita, - PzPallet = editRec.PzPallet, - Note = editRec.Note, - CodCli = editRec.CodCli, - InsertDate = editRec.InsertDate - }; - - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currRec = await dbCtx - .DbSetPODL - .AsNoTracking() - .Where(x => x.IdxPromessa == recPODL.IdxPromessa) - .FirstOrDefaultAsync(); - - if (currRec != null) - { - // eseguo stored attrezzaggio - var IdxPromessa = new SqlParameter("@idxPromessa", recPODL.IdxPromessa); - var MatrOpr = new SqlParameter("@MatrOpr", matrOpr); - var IdxMacchina = new SqlParameter("@IdxMacchina", recPODL.IdxMacchina); - var TCRichAttr = new SqlParameter("@TCRichAttr", tcRich); - var PzPallet = new SqlParameter("@PzPallet", pzPallet); - var Note = new SqlParameter("@Note", note); - var DtEvento = new SqlParameter("@dtEvento", dtEvent); - var callResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC stp_ODL_inizioSetupPromessa @idxPromessa, @MatrOpr, @IdxMacchina, @TCRichAttr, @PzPallet, @Note, @dtEvento", IdxPromessa, MatrOpr, IdxMacchina, TCRichAttr, PzPallet, Note, DtEvento); - - answ = true; - } - return answ; - } - - /// - /// Chiamata salvataggio ricetta su DB - /// - /// - /// - /// - public async Task PODL_updateRecipe(int idxPODL, string recipeName) - { - bool answ = false; - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currRec = await dbCtx - .DbSetPODL - .Where(x => x.IdxPromessa == idxPODL) - .FirstOrDefaultAsync(); - - if (currRec != null) - { - currRec.Recipe = recipeName; - dbCtx.Entry(currRec).State = EntityState.Modified; - answ = await dbCtx.SaveChangesAsync() > 0; - } - return answ; - } - - /// - /// Eliminazione Record - /// - /// - /// - public async Task PODLDeleteRecordAsync(PODLExpModel currRec) - { - PODLModel recPODL = new PODLModel() - { - IdxPromessa = currRec.IdxPromessa, - KeyRichiesta = currRec.KeyRichiesta, - KeyBCode = currRec.KeyBCode, - IdxOdl = currRec.IdxOdl, - CodArticolo = currRec.CodArticolo, - CodGruppo = currRec.CodGruppo, - IdxMacchina = currRec.IdxMacchina, - NumPezzi = currRec.NumPezzi, - Tcassegnato = currRec.Tcassegnato, - DueDate = currRec.DueDate, - Priorita = currRec.Priorita, - PzPallet = currRec.PzPallet, - Note = currRec.Note, - CodCli = currRec.CodCli, - InsertDate = currRec.InsertDate - }; - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currVal = await dbCtx - .DbSetPODL - .Where(x => x.IdxPromessa == recPODL.IdxPromessa) - .FirstOrDefaultAsync(); - dbCtx - .DbSetPODL - .Remove(currVal); - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Effettua il task di eliminazione PODL KIT + istanze + riattivazione PODL originali disattivate tramite stored - /// - /// IdxPODL parent - public async Task PodlIstKitDeleteAsync(int IdxPODL) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var pIdxPODL = new SqlParameter("@IdxPODL", IdxPODL); - - var dbResult = await dbCtx - .Database - .ExecuteSqlRawAsync("EXEC dbo.stp_PodlIstKit_delete @IdxPODL", pIdxPODL); - return dbResult != 0; - } - - /// - /// Update Record - /// - /// - /// - public async Task PODLUpdateRecordAsync(PODLModel editRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var currRec = await dbCtx - .DbSetPODL - .Where(x => x.IdxPromessa == editRec.IdxPromessa) - .FirstOrDefaultAsync(); - if (currRec != null) - { - currRec.CodGruppo = editRec.CodGruppo; - currRec.CodArticolo = editRec.CodArticolo; - currRec.IdxMacchina = editRec.IdxMacchina; - currRec.KeyBCode = editRec.KeyBCode; - currRec.KeyRichiesta = editRec.KeyRichiesta; - currRec.NumPezzi = editRec.NumPezzi; - currRec.Tcassegnato = editRec.Tcassegnato; - currRec.Attivabile = editRec.Attivabile; - currRec.Note = editRec.Note; - dbCtx.Entry(currRec).State = EntityState.Modified; - } - else - { - await dbCtx - .DbSetPODL - .AddAsync(editRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Stato macchina (da key) - /// - /// - /// - public async Task StatoMacchinaAsync(string idxMacchina) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - return await dbCtx - .DbSetStatoMacc - .Where(x => x.IdxMacchina == idxMacchina) - .AsNoTracking() - .FirstOrDefaultAsync(); - } - - /// - /// Elimina record - /// - /// - public async Task TemplateKitDeleteAsync(TemplateKitModel rec2del) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetTempKit - .Where(x => x.CodArtParent == rec2del.CodArtParent && x.CodArtChild == rec2del.CodArtChild) - .FirstOrDefaultAsync(); - // se ci fosse aggiorno... - if (actRec != null) - { - dbCtx - .DbSetTempKit - .Remove(actRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elenco template KIT da ricerca - /// - /// - /// - /// - public async Task> TemplateKitFiltAsync(string KitCode, string codChild) - { - List dbResult = new List(); - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - dbResult = await dbCtx - .DbSetTempKit - .Where(x => (string.IsNullOrEmpty(KitCode) && string.IsNullOrEmpty(codChild)) || (x.CodArtParent.Contains(KitCode) && !string.IsNullOrEmpty(KitCode)) || (x.CodArtChild.Contains(codChild) && !string.IsNullOrEmpty(codChild))) - .AsNoTracking() - .ToListAsync(); - return dbResult; - } - - /// - /// Esegue upsert record - /// - /// - /// - public async Task TemplateKitUpsertAsync(TemplateKitModel editRec, string codAzienda) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - // verifico preliminarmente articolo... - var recArt = dbCtx - .DbSetArticoli - .FirstOrDefault(x => x.CodArticolo == editRec.CodArtParent); - // se mancasse... - if (recArt == null) - { - // aggiungo! - AnagArticoliModel newRecArt = new AnagArticoliModel() - { - CodArticolo = editRec.CodArtParent, - Tipo = "KIT", - DescArticolo = $"Articolo KIT - {DateTime.Now:yyyy-MM-dd HH:mm:ss}", - Disegno = "", - Azienda = codAzienda, - CurrRev = "", - ProdRev = "" - }; - dbCtx - .DbSetArticoli - .Add(newRecArt); - } - - // proseguo col KIT - var actRec = await dbCtx - .DbSetTempKit - .Where(x => x.CodArtParent == editRec.CodArtParent && x.CodArtChild == editRec.CodArtChild) - .FirstOrDefaultAsync(); - - // se NON ci fosse aggiungo... - if (actRec == null) - { - await dbCtx - .DbSetTempKit - .AddAsync(editRec); - } - else - { - actRec.Qty = editRec.Qty; - dbCtx.Entry(actRec).State = EntityState.Modified; - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Punteggio compatibilità KIT per KeyFilt indicato - /// - /// - /// - /// - public async Task> TksScoreAsync(string KeyFilt, int MaxResult) - { - List dbResult = new List(); - if (!string.IsNullOrEmpty(KeyFilt)) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var pKeyFilt = new SqlParameter("@KeyFilt", KeyFilt); - var pMaxRes = new SqlParameter("@maxResult", MaxResult); - dbResult = await dbCtx - .DbSetTksScore - .FromSqlRaw("EXEC stp_TKS_Search @KeyFilt, @maxResult", pKeyFilt, pMaxRes) - .AsNoTracking() - .ToListAsync(); - } - return dbResult; - } - -#if false - /// - /// Elenco Vocabolario di una lingua - /// - /// - public Dictionary VocabolarioGetLang(string lingua) - { - using var dbCtx = _ctxFactory.CreateDbContext(); - var rawList = dbCtx - .DbSetVocabolario - .AsNoTracking() - .Where(x => x.Lingua.ToLower() == lingua.ToLower()) - .OrderBy(x => x.Lemma) - .ToList(); - // Proietto in dizionario - return rawList - .DistinctBy(t => t.Lemma, StringComparer.OrdinalIgnoreCase) - .ToDictionary(t => t.Lemma, t => t.Traduzione, StringComparer.OrdinalIgnoreCase); - } - - /// - /// Upsert record Vocabolario - /// - /// - /// - public async Task VocabolarioUpsertAsync(VocabolarioModel upsRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetVocabolario - .Where(x => x.Lingua == upsRec.Lingua && x.Lemma == upsRec.Lemma) - .FirstOrDefaultAsync(); - - // se ci fosse aggiorno... - if (actRec == null) - { - dbCtx - .DbSetVocabolario - .Add(upsRec); - } - else - { - actRec.Traduzione = upsRec.Traduzione; - dbCtx.Entry(actRec).State = EntityState.Modified; - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elimina record - /// - /// - public async Task WipKitDeleteAsync(WipSetupKitModel rec2del) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetWipKit - .Where(x => x.KeyFilt == rec2del.KeyFilt && x.CodOrd == rec2del.CodOrd) - .FirstOrDefaultAsync(); - // se ci fosse aggiorno... - if (actRec != null) - { - dbCtx - .DbSetWipKit - .Remove(actRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } -#endif - - /// - /// Elimina record + vecchi della data-ora indicata - /// - /// - /// - public async Task WipKitDeleteOlderAsync(DateTime dateLimit) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetWipKit - .Where(x => x.DataIns < dateLimit) - .ToListAsync(); - // se ci fosse aggiorno... - if (actRec != null) - { - dbCtx - .DbSetWipKit - .RemoveRange(actRec); - } - return await dbCtx.SaveChangesAsync() > 0; - } - - /// - /// Elenco record WipSetupKit da KeyFilt - /// - /// - /// - public async Task> WipKitFiltAsync(string KeyFilt) - { - List dbResult = new List(); - // solo se filtro valido... - if (!string.IsNullOrEmpty(KeyFilt)) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - dbResult = await dbCtx - .DbSetWipKit - .Where(x => x.KeyFilt.Contains(KeyFilt)) - .AsNoTracking() - .ToListAsync(); - } - return dbResult; - } - - /// - /// Esegue upsert record - /// - /// - public async Task WipKitUpsertAsync(WipSetupKitModel editRec) - { - using var dbCtx = await _ctxFactory.CreateDbContextAsync(); - var actRec = await dbCtx - .DbSetWipKit - .Where(x => x.KeyFilt == editRec.KeyFilt && x.CodOrd == editRec.CodOrd) - .FirstOrDefaultAsync(); - - // se ci fosse aggiorno... - if (actRec == null) - { - dbCtx - .DbSetWipKit - .Add(editRec); - } - else - { - actRec.CodArt = editRec.CodArt; - actRec.DescArt = editRec.DescArt; - actRec.Qta = editRec.Qta; - actRec.DataIns = editRec.DataIns; - dbCtx.Entry(actRec).State = EntityState.Modified; - } - return await dbCtx.SaveChangesAsync() > 0; - } - - #endregion Public Methods - - #region Private Fields - - private static IConfiguration _configuration; - private static NLog.Logger Log = LogManager.GetCurrentClassLogger(); -#if false - private DbContextOptions options; -#endif - - #endregion Private Fields - } -} \ No newline at end of file diff --git a/MP.SPEC/Controllers/RecipeArchiveController.cs b/MP.SPEC/Controllers/RecipeArchiveController.cs index 31114e5c..1679e37b 100644 --- a/MP.SPEC/Controllers/RecipeArchiveController.cs +++ b/MP.SPEC/Controllers/RecipeArchiveController.cs @@ -15,7 +15,6 @@ namespace MP.SPEC.Controllers public RecipeArchiveController(IConfiguration configuration, MpDataService DataService) { Log.Info("Starting RecipeArchiveController"); - _configuration = configuration; DService = DataService; Log.Info("Avviata classe RecipeArchiveController"); } @@ -71,8 +70,6 @@ namespace MP.SPEC.Controllers #region Private Fields - private static IConfiguration _configuration = null!; - private static Logger Log = LogManager.GetCurrentClassLogger(); #endregion Private Fields diff --git a/MP.SPEC/Controllers/RecipeController.cs b/MP.SPEC/Controllers/RecipeController.cs index 860a4409..d28b37aa 100644 --- a/MP.SPEC/Controllers/RecipeController.cs +++ b/MP.SPEC/Controllers/RecipeController.cs @@ -14,10 +14,9 @@ namespace MP.SPEC.Controllers { #region Public Constructors - public RecipeController(IConfiguration configuration, MpDataService DataService) + public RecipeController(MpDataService DataService) { Log.Info("Starting RecipeController"); - _configuration = configuration; DService = DataService; Log.Info("Avviata classe RecipeController"); } @@ -66,8 +65,6 @@ namespace MP.SPEC.Controllers #region Private Fields - private static IConfiguration _configuration = null!; - private static Logger Log = LogManager.GetCurrentClassLogger(); #endregion Private Fields diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs index 8ddbce9d..f16851c8 100644 --- a/MP.SPEC/Data/MpDataService.cs +++ b/MP.SPEC/Data/MpDataService.cs @@ -32,13 +32,13 @@ namespace MP.SPEC.Data private readonly IProductionRepository _productionRepository; public MpDataService( - IConnectionMultiplexer connMPlex, - IConfiguration configuration, - IFusionCache cache, - IAnagRepository anagRepository, - ISystemRepository systemRepository, - IDossierRepository dossierRepository, - IFluxLogRepository fluxLogRepository, + IConnectionMultiplexer connMPlex, + IConfiguration configuration, + IFusionCache cache, + IAnagRepository anagRepository, + ISystemRepository systemRepository, + IDossierRepository dossierRepository, + IFluxLogRepository fluxLogRepository, IProductionRepository productionRepository) { // salvataggio oggetti @@ -46,12 +46,6 @@ namespace MP.SPEC.Data redisConn = connMPlex; redisDb = redisConn.GetDatabase(); -#if false - // setup compoenti REDIS - redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis") ?? "localhost:6379"); - redisConnAdmin = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("RedisAdmin") ?? "localhost:6379"); - redisDb = redisConn.GetDatabase(); -#endif // leggo cache lungo/cordo periodo int.TryParse(_configuration.GetValue("ServerConf:redisShortTimeCache"), out redisShortTimeCache); int.TryParse(_configuration.GetValue("ServerConf:redisLongTimeCache"), out redisLongTimeCache); @@ -2100,8 +2094,6 @@ namespace MP.SPEC.Data #region Private Properties - private static MpSpecController dbController { get; set; } = null!; - private static MpMongoController mongoController { get; set; } = null!; #endregion Private Properties diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj index 5037cd5c..23f64e38 100644 --- a/MP.SPEC/MP.SPEC.csproj +++ b/MP.SPEC/MP.SPEC.csproj @@ -5,7 +5,7 @@ enable enable MP.SPEC - 8.16.2606.409 + 8.16.2606.410 1800a78a-6ff1-40f9-b490-87fb8bfc1394 en diff --git a/MP.SPEC/README.md b/MP.SPEC/README.md new file mode 100644 index 00000000..27f84734 --- /dev/null +++ b/MP.SPEC/README.md @@ -0,0 +1,89 @@ +# MP.SPEC + +MAPO SPEC: WebApp / Sito in versione dotNetCore 8 per la gestione del MES MAPO in particolare per le gestioni SPECiali (ina ttesa di finire di migrare SITE e ADM). + +Comprende funzionalità amministrative avanzate e funzionalità standard. + +## Sezioni Principali + +Sono gestiti + * Articoli + * Operatori + * Assegnazione Operatori/Macchine a reparti + * PODL (Promesse ODL) prima della produzione + * ODL (Ordini di lavoro) legate ad effettive attività di produzione + * Gestione speciale dei KIT + * Gestione Dossier (es caso Baglietto) + * Gestione Ricette (tramite dossier) + * Gestione Parametri macchina + * Gestione giacenze magazzino (ove gestite) + +Ci sono anche alcune pagine speciali di admin (ad esempio FluxLogStatus, usata per deduplicare i dati di FluxLog) + +## Architettura + +### Livello Dati (MP.Data) + +Il layer dati centralizzato in `MP.Data` fornisce: + +- **8 Repository**: Anag, Production, Dossier, FluxLog, System, MpVoc, MpMon, MpLand +- **Cache FusionCache** (Memory + Redis + DB) con invalidazione per tag +- **DI Registrations** attraverso `DataServiceCollectionExtensions` (`AddSpecDataLayer`, `AddLandDataLayer`, etc.) +- **MpDataService** come servizio singleton centrale per accesso a DB, Redis, MongoDB + +### Livello Applicazione (MP.SPEC) + +- **MpDataService** (Scoped, singleton in Program.cs): service wrapper sul DAL con caching FusionCache +- **Componenti Blazor Server**: layout interattivo server-side con `AddInteractiveServerComponents()` +- **API Controllers**: RecipeController, RecipeArchiveController per operazioni ricette +- **Autenticazione**: Windows Authentication (Negotiate) con Autorizzazione Blazor Server + +### Livello Infrastruttura + +- **SQL Server**: 4 DbContext (MoonProContext, MoonPro_VocContext, MoonPro_FluxContext, MoonPro_STATSContext) +- **MongoDB**: storage ricette +- **Redis**: caching distribuito (FusionCache) + backplane +- **OpenTelemetry**: tracing su Uptrace (abilitabile via conf) +- **MessagePipe**: broadcasting messaggi real-time + +## Refactoring Completati (Giugno 2026) + +### Repository Pattern - Decomposizione MpSpecController + +Il grande `MpSpecController/MpSpecRepository.cs` è stato scomposto in 8 repository specialistici: + +| # | Repository | Interfaccia | Metodi | DbContext | +|---|---|---|---|---| +| 1 | **Anag** | `IAnagRepository` | 26 | `MoonProContext` | +| 2 | **Production** | `IProductionRepository` | 32 | `MoonProContext` | +| 3 | **Dossier** | `IDossierRepository` | 6 | `MoonPro_FluxContext` | +| 4 | **FluxLog** | `IFluxLogRepository` | 3 | `MoonPro_FluxContext` | +| 5 | **System** | `ISystemRepository` | 7 | `MoonProContext` + `MoonProAdminContext` | +| 6 | **MpVoc** | `IMpVocRepository` | 3 | `MoonPro_VocContext` | +| 7 | **MpMon** | `IMpMonRepository` | 4 | `MoonProContext` | +| 8 | **MpLand** | `IMpLandRepository` | 6 | `MoonProContext` | + +Tutti i reference a `dbController.XXX()` nei servizi sono stati rimossi. I metodi originali rimangono nel file di repository come fallback documentato. + +### Migrazione FusionCache + +Tutti i metodi di lettura in `MpDataService.cs` sono stati migrati al pattern `GetOrFetchAsync()`: +- **L1 MemoryCache**: 1/3 della scadenza totale +- **L2 Redis (Distributed)**: TTL configurabile +- **L3 Database**: fetch diretto dal DbContext +- **Invalidazione**: per tag (es. `Utils.redisArtList`, `Utils.redisOdlByKey`) +- **48+ metodi** migrati o confermati corretti + +### Fix DI e Static State (MP.AppAuth) + +Risolto il null reference error originario (`Cannot provide a value for property 'AAService' on type 'CmpTop'`): + +- Rimpiazzati tutti i `static IConfiguration _configuration` con `readonly` istanza nei controllers +- `AppAuthService` ora riceve i controllers via constructor DI invece di crearli con `new()` +- Registrazioni DI centralizzate in `DataServiceCollectionExtensions.cs` +- Tutti i controllers e services mp.appauth registrati come Scoped + +### Build + +Tutte le 10 soluzioni compilano con successo (0 errori): +MP.SPEC, MP.Data, MP.Land, MP.MON, MP.TAB3, MP.Stats, MP.INVE, MP.IOC, MP.RIOC, MP.Prog, IobConf diff --git a/MP.SPEC/README.pdf b/MP.SPEC/README.pdf new file mode 100644 index 00000000..a23a58a6 Binary files /dev/null and b/MP.SPEC/README.pdf differ diff --git a/MP.SPEC/Resources/ChangeLog.html b/MP.SPEC/Resources/ChangeLog.html index b6126dfe..be2d3178 100644 --- a/MP.SPEC/Resources/ChangeLog.html +++ b/MP.SPEC/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo MAPOSPEC -

Versione: 8.16.2606.409

+

Versione: 8.16.2606.410


Note di rilascio:
  • diff --git a/MP.SPEC/Resources/VersNum.txt b/MP.SPEC/Resources/VersNum.txt index 722b7892..a6ed034a 100644 --- a/MP.SPEC/Resources/VersNum.txt +++ b/MP.SPEC/Resources/VersNum.txt @@ -1 +1 @@ -8.16.2606.409 +8.16.2606.410 diff --git a/MP.SPEC/Resources/manifest.xml b/MP.SPEC/Resources/manifest.xml index 85bf0bb0..4750dbeb 100644 --- a/MP.SPEC/Resources/manifest.xml +++ b/MP.SPEC/Resources/manifest.xml @@ -1,6 +1,6 @@ - 8.16.2606.409 + 8.16.2606.410 https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html false diff --git a/MP.SPEC/appsettings.Production.json b/MP.SPEC/appsettings.Production.json index f4ebc6a0..908746f3 100644 --- a/MP.SPEC/appsettings.Production.json +++ b/MP.SPEC/appsettings.Production.json @@ -29,7 +29,7 @@ "cacheCheckArtUsato": 2, "redisShortTimeCache": 10, "redisLongTimeCache": 600, - "slowLogThresh": 200, + "slowLogThresh": 100, "MpIoBaseUrl": "http://localhost/MP/IO/", "MpIoNS": "MoonPro:SQL2016DEV:MoonPro", "BasePathOdlReturn": "\\\\iis01\\W$\\Files\\ODL", diff --git a/MP.SPEC/appsettings.json b/MP.SPEC/appsettings.json index 6bda77be..b2744a43 100644 --- a/MP.SPEC/appsettings.json +++ b/MP.SPEC/appsettings.json @@ -73,7 +73,7 @@ "cacheCheckArtUsato": "2", "redisShortTimeCache": 5, "redisLongTimeCache": 120, - "slowLogThresh": 1, + "slowLogThresh": 1, "MpIoBaseUrl": "http://localhost:20967/", "MpIoNS": "MoonPro:SQL2016DEV:MoonPro", "BasePathOdlReturn": "\\\\iis01\\ODL\\ftpdata\\syncfolder", diff --git a/MP.SPEC/refactor_repository.md b/MP.SPEC/refactor_repository.md index d0a75046..d8829193 100644 --- a/MP.SPEC/refactor_repository.md +++ b/MP.SPEC/refactor_repository.md @@ -6,6 +6,17 @@ |---|---|---| | MP.Data | OK | 0 | | MP.SPEC | OK | 0 | +| MP.Land | OK | 0 | +| MP.MON | OK | 0 | +| MP-TAB3 | OK | 0 | +| MP.Stats | OK | 0 | +| MP.INVE | OK | 0 | +| MP.IOC | OK | 0 | +| MP.RIOC | OK | 0 | +| MP.Prog | OK | 0 | +| IobConf | OK | 0 | + +**Tutte le 10 soluzioni della codebase compilano con successo.** ## Repository Creati (8 nuovi) @@ -41,40 +52,108 @@ services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); -services.TryAddScoped(); +servicesTryAddScoped(); services.TryAddScoped(); + +// Controllers MP.AppAuth - Scoped (dipendenze di AppAuthService) +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); + +// Servizi Singleton +services.TryAddSingleton(); +services.TryAddSingleton(); +services.TryAddSingleton(); +services.TryAddSingleton(); +services.TryAddSingleton(); +services.TryAddSingleton(); +services.TryAddSingleton(); + +// Servizi Scoped +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); +services.TryAddScoped(); ``` -## File Modificati +## Livello MP.AppAuth - Refactoring DI Completato (Giugno 2026) -- `MP.Data/DataServiceCollectionExtensions.cs` (+10/-1) -- `MP.Data/Services/LandDataService.cs` (+10/-10) -- `MP.Data/Services/MonDataFeeder.cs` (+1/-1) -- `MP.Data/Services/StatusData.cs` (+12/-12) -- `MP.Data/Services/TabDataFeeder.cs` (+1/-1) -- `MP.Data/Services/TranslateSrv.cs` (+13/-13) -- `MP.SPEC/Data/MpDataService.cs` (+1/-3) +### Problema +`AppAuthService` (Scoped) conteneva istanze statiche dei controller e li creava con `new()` nel costruttore, causando race condition e null reference errors. -## File Nuovi (6) +### Fix Applicati -- `MP.Data/Repository/MpLand/IMpLandRepository.cs` -- `MP.Data/Repository/MpLand/MpLandRepository.cs` -- `MP.Data/Repository/MpMon/IMpMonRepository.cs` -- `MP.Data/Repository/MpMon/MpMonRepository.cs` +| File | Problema | Fix | +|------|----------|-----| +| `AppAuthController.cs` | `static IConfiguration _configuration` sovrascritto da ogni istanza | `readonly IConfiguration _configuration` | +| `MPController.cs` | `static IConfiguration _configuration` + `static dbController` | `readonly` istanza + dispose standard | +| `AppUserController.cs` | `static IConfiguration = null!` | `readonly IConfiguration _configuration` | +| `AppAuthService.cs` | Creava controllers con `new()` → bypassava DI | Constructor accetta controllers **via DI** | +| `MpLandController.cs` (MP.Data) | `static IConfiguration _configuration` + Dispose che impostava null | `readonly IConfiguration` + dispose standard | +| `BaseServ.cs` (MP.Data) | `protected static IConfiguration _configuration` | `protected IConfiguration` (istanza) | +| `LandDataService.cs` (MP.Data) | `public static MpLandController dbController` vuoto | Campo rimosso | +| `DataServiceCollectionExtensions.cs` (MP.Data) | `AppAuthService` non registrata con dipendenze | `AddLandDataLayer()` e `AddSpecDataLayer()` registrano controller + servizio | + +### Livelli DI per progetto + +| Progetto | Estensione DI | Registrazione | +|----------|--------------|---------------| +| **MP.SPEC** | `AddSpecDataLayer()` | Repository + controllers + AppAuthService (Scoped) | +| **MP.Land** | `AddLandDataLayer()` | Repository + controllers + AppAuthService (Scoped) | +| **MP.MON** | `AddMonDataLayer()` | Repository + MonDataFeeder (Singleton) | +| **MP.IOC** | `AddIocDataLayer()` | Repository + servizi IOC (Scoped/Singleton) | +| **MP-PROG / MP-INVE / MP-STATS / MP-TAB3 / MP-RIOC / IobConf** | Registration nel respective Program.cs | Completa e funzionante | + +## File Modificati per Fix DI + +- `MP.AppAuth/Controllers/AppAuthController.cs` (static → readonly) +- `MP.AppAuth/Controllers/MPController.cs` (static → readonly, dispose corretto) +- `MP.AppAuth/Controllers/AppUserController.cs` (static → readonly) +- `MP.AppAuth/Services/AppAuthService.cs` (DI constructor injection) +- `MP.Data/Controllers/MpLandController.cs` (static → readonly) +- `MP.Data/Services/BaseServ.cs` (static → instancia) +- `MP.Data/Services/LandDataService.cs` (campo statico rimosso) +- `MP.Data/DataServiceCollectionExtensions.cs` (registrazioni DI corrette) + +## File Nuovi (8) + +- `MP.Data/Repository/Anag/IAnagRepository.cs` +- `MP.Data/Repository/Anag/AnagRepository.cs` +- `MP.Data/Repository/Production/IProductionRepository.cs` +- `MP.Data/Repository/Production/ProductionRepository.cs` +- `MP.Data/Repository/Dossier/IDossierRepository.cs` +- `MP.Data/Repository/Dossier/DossierRepository.cs` +- `MP.Data/Repository/FluxLog/IFluxLogRepository.cs` +- `MP.Data/Repository/FluxLog/FluxLogRepository.cs` +- `MP.Data/Repository/System/ISystemRepository.cs` +- `MP.Data/Repository/System/SystemRepository.cs` - `MP.Data/Repository/MpVoc/IMpVocRepository.cs` - `MP.Data/Repository/MpVoc/MpVocRepository.cs` +- `MP.Data/Repository/MpMon/IMpMonRepository.cs` +- `MP.Data/Repository/MpMon/MpMonRepository.cs` +- `MP.Data/Repository/MpLand/IMpLandRepository.cs` +- `MP.Data/Repository/MpLand/MpLandRepository.cs` ## Verifiche -- Nessun riferimento a `dbController.XXX()` nei file di servizio -- `ArticleWithDossierAsync` esportato correttamente (rimossi `#if false`) -- `VocabolarioGetLang` reso sincrono (firma originale sincrona) -- `tryLoadIobTags` in StatusData usa `GetAwaiter().GetResult()` (contesto sync) -- `InitDict` in TranslateSrv usa `GetAwaiter().GetResult()` (contesto sync) +- ✅ Nessun riferimento a `dbController.XXX()` nei file di servizio +- ✅ `ArticleWithDossierAsync` esportato correttamente (rimossi `#if false`) +- ✅ `VocabolarioGetLang` reso sincrono (firma originale sincrona) +- ✅ `tryLoadIobTags` in StatusData usa `GetAwaiter().GetResult()` (contesto sync) +- ✅ `InitDict` in TranslateSrv usa `GetAwaiter().GetResult()` (contesto sync) +- ✅ Tutti i 10 progetti buildano con 0 errori +- ✅ Niente `static _configuration` nei controllers MP.AppAuth +- ✅ Niente `new()` di controllers fuori dal DI +- ✅ `AppAuthService` correttamente registrato in tutti le applicazioni ## MpSpecRepository (MpSpecController) -I metodi原价 sono ancora visibili nel file ma: +I metodi sono ancora visibili nel file ma: - Non sono usati dai layer superiori (tutti migrati ai repository) - Possono essere spostati a `#if false` come ultima fase di pulizia - Rimangono come fallback documentato diff --git a/MP.SPEC/refactor_repository.pdf b/MP.SPEC/refactor_repository.pdf index 806fa38d..9aaf51b2 100644 Binary files a/MP.SPEC/refactor_repository.pdf and b/MP.SPEC/refactor_repository.pdf differ