Abbozzato comportamento selezione ODL (tranne grafica)

This commit is contained in:
Samuele Locatelli
2022-11-29 18:14:42 +01:00
parent 5ffac79ac4
commit f768468131
10 changed files with 383 additions and 254 deletions
+130 -104
View File
@@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MP.Data.DatabaseModels;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Data;
@@ -330,80 +329,6 @@ namespace MP.Data.Controllers
return answ;
}
/// <summary>
/// Elenco giacenze
/// </summary>
/// <param name="IdxOdl">id odl da cercare</param>
/// <returns></returns>
public List<AnagGiacenzeModel> ListGiacenze(int IdxOdl)
{
List<AnagGiacenzeModel> dbResult = new List<AnagGiacenzeModel>();
using (var dbCtx = new MoonPro_InveContext(_configuration))
{
try
{
dbResult = dbCtx
.DbGiacenzeData
.AsNoTracking()
.Where(x => x.IdxOdl == IdxOdl)
.ToList();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ListGiacenze{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// Elenco TUTTI GLI ODL
/// </summary>
/// <returns></returns>
public List<ODLModel> ListOdlAll()
{
List<ODLModel> dbResult = new List<ODLModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
dbResult = dbCtx
.DbSetODL
.AsNoTracking()
.ToList();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ListOdlAll{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// ODL da chiave
/// </summary>
/// <returns></returns>
public ODLExpModel OdlByKey(int IdxOdl)
{
ODLExpModel dbResult = new ODLExpModel();
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
dbResult = dbCtx
.DbSetODLExp
.AsNoTracking()
.Where(x=> x.IdxOdl == IdxOdl)
.FirstOrDefault();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante OdlByKey{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// Elenco ultimi n record DOssiers (che contengono ad esempio "salvataggi" di FLuxLog) dato
/// macchina (ordinato x data registrazione)
@@ -601,6 +526,32 @@ namespace MP.Data.Controllers
return dbResult;
}
/// <summary>
/// Elenco giacenze
/// </summary>
/// <param name="IdxOdl">id odl da cercare</param>
/// <returns></returns>
public List<AnagGiacenzeModel> ListGiacenze(int IdxOdl)
{
List<AnagGiacenzeModel> dbResult = new List<AnagGiacenzeModel>();
using (var dbCtx = new MoonPro_InveContext(_configuration))
{
try
{
dbResult = dbCtx
.DbGiacenzeData
.AsNoTracking()
.Where(x => x.IdxOdl == IdxOdl)
.ToList();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ListGiacenze{Environment.NewLine}{exc}");
}
}
return dbResult;
}
public List<LinkMenu> ListLinkFilt(string tipoLink)
{
List<LinkMenu> dbResult = new List<LinkMenu>();
@@ -616,6 +567,30 @@ namespace MP.Data.Controllers
return dbResult;
}
/// <summary>
/// Elenco TUTTI GLI ODL
/// </summary>
/// <returns></returns>
public List<ODLModel> ListOdlAll()
{
List<ODLModel> dbResult = new List<ODLModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
dbResult = dbCtx
.DbSetODL
.AsNoTracking()
.ToList();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ListOdlAll{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// Elenco ODL filtrati x stato, articolo, KeyRich (che contiene stato)
/// </summary>
@@ -632,7 +607,6 @@ namespace MP.Data.Controllers
List<ODLExpModel> dbResult = new List<ODLExpModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
var InCorso = new SqlParameter("@InCorso", inCorso);
var CodArt = new SqlParameter("@CodArt", codArt);
var KeyRich = new SqlParameter("@KeyRich", keyRichPart);
@@ -661,7 +635,7 @@ namespace MP.Data.Controllers
.Include(m => m.MachineNav)
.Include(a => a.ArticoloNav)
.OrderByDescending(x => x.IdxOdl)
.ToList();
.ToList();
#endif
}
return dbResult;
@@ -677,7 +651,6 @@ namespace MP.Data.Controllers
/// <returns></returns>
public List<PODLExpModel> ListPODLFilt(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate)
{
List<PODLExpModel> dbResult = new List<PODLExpModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
@@ -698,32 +671,6 @@ namespace MP.Data.Controllers
return dbResult;
}
#if false
/// <summary>
/// Elenco PODL non avviati filtrati x articolo, KeyRich (che contiene stato)
/// </summary>
/// <param name="codArt">Cod articolo</param>
/// <param name="keyRichPart">KeyRich (parziale) da cercare (es cod stato x yacht)</param>
/// <returns></returns>
public List<PODLModel> ListPODLFiltNOOdl(string codArt, string keyRichPart)
{
List<PODLModel> dbResult = new List<PODLModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
dbResult = dbCtx
.DbSetPODL
.Where(x => (x.IdxOdl != 0) && (x.KeyRichiesta.Contains(keyRichPart) || keyRichPart == "*") && (codArt == "*" || x.CodArticolo.Contains(codArt)))
.AsNoTracking()
.Include(m => m.MachineNav)
.Include(a => a.ArticoloNav)
.OrderByDescending(x => x.InsertDate)
.ToList();
}
return dbResult;
}
#endif
/// <summary>
/// Elenco valori ammessi x tabella/colonna
/// </summary>
@@ -805,6 +752,84 @@ namespace MP.Data.Controllers
return dbResult;
}
/// <summary>
/// Elenco ODL dato batch selezionato
/// </summary>
/// <param name="BatchSel">Batch richiesto</param>
/// <returns></returns>
public List<int> OdlByBatch(string batchSel)
{
List<int> dbResult = new List<int>();
using (var dbCtx = new MoonPro_InveContext(_configuration))
{
try
{
dbResult = dbCtx
.DbGiacenzeData
.AsNoTracking()
.Where(x => x.IdentRG == batchSel)
.Select(x => x.IdxOdl)
.ToList();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante OdlByBatch{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// ODL da chiave
/// </summary>
/// <returns></returns>
public ODLExpModel OdlByKey(int IdxOdl)
{
ODLExpModel dbResult = new ODLExpModel();
using (var dbCtx = new MoonProContext(_configuration))
{
try
{
dbResult = dbCtx
.DbSetODLExp
.AsNoTracking()
.Where(x => x.IdxOdl == IdxOdl)
.FirstOrDefault();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante OdlByKey{Environment.NewLine}{exc}");
}
}
return dbResult;
}
#if false
/// <summary>
/// Elenco PODL non avviati filtrati x articolo, KeyRich (che contiene stato)
/// </summary>
/// <param name="codArt">Cod articolo</param>
/// <param name="keyRichPart">KeyRich (parziale) da cercare (es cod stato x yacht)</param>
/// <returns></returns>
public List<PODLModel> ListPODLFiltNOOdl(string codArt, string keyRichPart)
{
List<PODLModel> dbResult = new List<PODLModel>();
using (var dbCtx = new MoonProContext(_configuration))
{
dbResult = dbCtx
.DbSetPODL
.Where(x => (x.IdxOdl != 0) && (x.KeyRichiesta.Contains(keyRichPart) || keyRichPart == "*") && (codArt == "*" || x.CodArticolo.Contains(codArt)))
.AsNoTracking()
.Include(m => m.MachineNav)
.Include(a => a.ArticoloNav)
.OrderByDescending(x => x.InsertDate)
.ToList();
}
return dbResult;
}
#endif
/// <summary>
/// Chiusura ODL con eventuale conferma pezzi
/// </summary>
@@ -995,6 +1020,7 @@ namespace MP.Data.Controllers
await Task.Delay(1);
return dbResult;
}
/// <summary>
/// Recupero PODL da IdxOdl
/// </summary>
+1 -1
View File
@@ -1,5 +1,5 @@
<table class="table">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
+32
View File
@@ -0,0 +1,32 @@
<div class="bg-info p-2">
<div class="bg-light">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>ODL</th>
</tr>
</thead>
<tbody>
@if (elencoODL == null || elencoODL.Count == 0)
{
<div class="alert alert-warning" role="alert">No record</div>
}
else
{
@foreach (var item in elencoODL)
{
<tr>
<td class="fw-bold">
<a class="btn btn-sm btn-primary" target="_blank">
@item
</a>
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;
using MP.SPEC;
using MP.SPEC.Shared;
using MP.SPEC.Components;
using MP.SPEC.Data;
namespace MP.SPEC.Components
{
public partial class ListOdlLotto
{
[Parameter]
public string BatchSel { get; set; }
private List<int> elencoODL { get; set; } = new List<int>();
[Inject]
private MpDataService MDService { get; set; } = null!;
protected override async Task OnParametersSetAsync()
{
if (!string.IsNullOrEmpty(BatchSel))
{
elencoODL = await MDService.OdlByBatch(BatchSel);
}
else
{
elencoODL = new List<int>();
}
}
}
}
+170 -143
View File
@@ -312,27 +312,6 @@ namespace MP.SPEC.Data
return result;
}
/// <summary>
/// Restituisce valore della stringa (SE disponibile)
/// </summary>
/// <param name="keyName"></param>
/// <returns></returns>
public async Task<string> tryGetConfig(string keyName)
{
string answ = "";
// preselezione valori
var configData = await ConfigGetAll();
var currRec = configData.FirstOrDefault(x => x.Chiave == keyName);
if (currRec != null)
{
answ = currRec.Valore;
}
return answ;
}
/// <summary>
/// Reset dati cache config
/// </summary>
@@ -500,7 +479,7 @@ namespace MP.SPEC.Data
public Task<List<AnagGruppi>> ElencoGruppiFase()
{
#if false
return Task.FromResult(dbController.AnagGruppiFase());
return Task.FromResult(dbController.AnagGruppiFase());
#endif
List<AnagGruppi> result = new List<AnagGruppi>();
Stopwatch stopWatch = new Stopwatch();
@@ -560,7 +539,6 @@ namespace MP.SPEC.Data
return answ;
}
/// <summary>
/// Elenco ultimi n record flux log dato macchina e flusso (ordinato x data registrazione)
/// </summary>
@@ -649,6 +627,79 @@ namespace MP.SPEC.Data
return outVal;
}
/// <summary>
/// </summary>
/// <param name="IdxOdl">id odl da cercare</param>
/// <returns></returns>
public async Task<List<AnagGiacenzeModel>> ListGiacenze(int IdxOdl)
{
List<AnagGiacenzeModel>? result = new List<AnagGiacenzeModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
string currKey = $"{redisGiacenzaList}:{IdxOdl}";
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<AnagGiacenzeModel>>($"{rawData}");
readType = "REDIS";
}
else
{
result = await Task.FromResult(dbController.ListGiacenze(IdxOdl));
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<AnagGiacenzeModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"ListGiacenze | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// elenco TUTTI gli ODL
/// </summary>
/// <param name="IdxOdl"></param>
/// <returns></returns>
public List<ODLModel> ListOdlAll()
{
List<ODLModel>? result = new List<ODLModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
#if false
string currKey = $"{redisGiacenzaList}:{IdxOdl}";
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<AnagGiacenzeModel>>($"{rawData}");
readType = "REDIS";
}
else
{
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<AnagGiacenzeModel>();
}
#endif
result = dbController.ListOdlAll();
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"ListOdlAll | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco ODL filtrati x stato, articolo, KeyRich (che contiene stato)
/// </summary>
@@ -690,7 +741,6 @@ namespace MP.SPEC.Data
Log.Debug($"ListODLFilt | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
//return await Task.FromResult(dbController.ListODLFilt(inCorso, codArt, keyRichPart, Reparto, IdxMacchina, startDate, endDate));
}
@@ -735,117 +785,6 @@ namespace MP.SPEC.Data
return result;
}
/// <summary>
/// </summary>
/// <param name="IdxOdl">id odl da cercare</param>
/// <returns></returns>
public async Task<List<AnagGiacenzeModel>> ListGiacenze(int IdxOdl)
{
List<AnagGiacenzeModel>? result = new List<AnagGiacenzeModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
string currKey = $"{redisGiacenzaList}:{IdxOdl}";
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<AnagGiacenzeModel>>($"{rawData}");
readType = "REDIS";
}
else
{
result = await Task.FromResult(dbController.ListGiacenze(IdxOdl));
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<AnagGiacenzeModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"ListGiacenze | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// elenco TUTTI gli ODL
/// </summary>
/// <param name="IdxOdl"></param>
/// <returns></returns>
public List<ODLModel> ListOdlAll()
{
List<ODLModel>? result = new List<ODLModel>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
#if false
string currKey = $"{redisGiacenzaList}:{IdxOdl}";
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<AnagGiacenzeModel>>($"{rawData}");
readType = "REDIS";
}
else
{
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<AnagGiacenzeModel>();
}
#endif
result = dbController.ListOdlAll();
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"ListOdlAll | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// ODL da chiave
/// </summary>
/// <param name="IdxOdl"></param>
/// <returns></returns>
public ODLExpModel OdlByKey(int IdxOdl)
{
ODLExpModel? result = new ODLExpModel();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
#if false
string currKey = $"{redisGiacenzaList}:{IdxOdl}";
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<AnagGiacenzeModel>>($"{rawData}");
readType = "REDIS";
}
else
{
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<AnagGiacenzeModel>();
}
#endif
result = dbController.OdlByKey(IdxOdl);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"OdlByKey | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco di tutte le macchine gestite
/// </summary>
@@ -918,6 +857,80 @@ namespace MP.SPEC.Data
return result;
}
/// <summary>
/// Elenco ODL dato batch selezionato
/// </summary>
/// <param name="BatchSel">Batch richiesto</param>
/// <returns></returns>
public async Task<List<int>> OdlByBatch(string BatchSel)
{
List<int>? result = new List<int>();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
string currKey = redisOdlByBatch;
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<int>>($"{rawData}");
readType = "REDIS";
}
else
{
result = await Task.FromResult(dbController.OdlByBatch(BatchSel));
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
}
if (result == null)
{
result = new List<int>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"OdlByBatch | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// ODL da chiave
/// </summary>
/// <param name="IdxOdl"></param>
/// <returns></returns>
public ODLExpModel OdlByKey(int IdxOdl)
{
ODLExpModel? result = new ODLExpModel();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string readType = "DB";
#if false
string currKey = $"{redisGiacenzaList}:{IdxOdl}";
// cerco in redis dato valore sel macchina...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<AnagGiacenzeModel>>($"{rawData}");
readType = "REDIS";
}
else
{
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<AnagGiacenzeModel>();
}
#endif
result = dbController.OdlByKey(IdxOdl);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"OdlByKey | Read from {readType}: {ts.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Effettua chiusura dell'ODL indicato, andand
/// </summary>
@@ -1207,6 +1220,25 @@ namespace MP.SPEC.Data
return answ;
}
/// <summary>
/// Restituisce valore della stringa (SE disponibile)
/// </summary>
/// <param name="keyName"></param>
/// <returns></returns>
public async Task<string> tryGetConfig(string keyName)
{
string answ = "";
// preselezione valori
var configData = await ConfigGetAll();
var currRec = configData.FirstOrDefault(x => x.Chiave == keyName);
if (currRec != null)
{
answ = currRec.Valore;
}
return answ;
}
public async Task<bool> updateDossierValue(DossierModel currDoss, FluxLogDTO editFL)
{
bool answ = false;
@@ -1322,27 +1354,22 @@ namespace MP.SPEC.Data
private const string redisFluxLogFilt = redisBaseAddr + "SPEC:Cache:FluxLogFilt";
private const string redisGiacenzaList = redisBaseAddr + ":GiacenzaList";
private const string redisMacByFlux = redisBaseAddr + "SPEC:Cache:MacByFlux";
private const string redisMacList = redisBaseAddr + "SPEC:Cache:MacList";
private const string redisXdlData = redisBaseAddr + "SPEC:Cache:XDL";
private const string redisOdlByBatch = redisXdlData + ":OdlByBatch";
private const string redisOdlCurrByMac = redisXdlData + ":OdlByMac";
private const string redisOdlList = redisXdlData + ":OdlList";
private const string redisPOdlByOdl = redisXdlData + ":POdlByOdl";
private const string redisPOdlByPOdl = redisXdlData + ":POdlByPOdl";
private const string redisPOdlList = redisXdlData + ":POdlList";
private const string redisGiacenzaList = redisBaseAddr + ":GiacenzaList";
private const string redisPOdlListNOOdl = redisXdlData + ":POdlListNOOdl";
private const string redisStatoCom = redisBaseAddr + "SPEC:Cache:StatoCom";
private const string redisTipoArt = redisBaseAddr + "SPEC:Cache:TipoArt";
private const string redisVocabolario = redisBaseAddr + "SPEC:Cache:Vocabolario";
private const string redisXdlData = redisBaseAddr + "SPEC:Cache:XDL";
private static IConfiguration _configuration = null!;
private static ILogger<MpDataService> _logger = null!;
+1 -1
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>MP.SPEC</RootNamespace>
<Version>6.16.2211.2917</Version>
<Version>6.16.2211.2918</Version>
</PropertyGroup>
<ItemGroup>
+2 -2
View File
@@ -53,8 +53,8 @@
</div>
@if (!string.IsNullOrEmpty(BatchSel))
{
<div class="col-3">
LIST ODL x <b>@BatchSel</b>
<div class="col-2">
<ListOdlLotto BatchSel="@BatchSel"></ListOdlLotto>
</div>
}
</div>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MAPOSPEC </i>
<h4>Versione: 6.16.2211.2917</h4>
<h4>Versione: 6.16.2211.2918</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
6.16.2211.2917
6.16.2211.2918
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2211.2917</version>
<version>6.16.2211.2918</version>
<url>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>