Files
lux/EgwCoreLib.Lux.Data/Services/Sales/OfferRowService.cs
T
2026-03-27 18:55:08 +01:00

485 lines
18 KiB
C#

namespace EgwCoreLib.Lux.Data.Services.Sales
{
public class OfferRowService : BaseServ, IOfferRowService
{
#region Public Constructors
public OfferRowService(
IConfiguration config,
IConnectionMultiplexer redis,
IOfferRowRepository repo) : base(config, redis)
{
_className = "OfferRow";
_repo = repo;
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Eliminazione record
/// </summary>
/// <param name="rec2del"></param>
/// <returns></returns>
public async Task<bool> DeleteAsync(OfferRowModel rec2del)
{
return await TraceAsync($"{_className}.Delete", async (activity) =>
{
var dbResult = await _repo.GetByIdAsync(rec2del.OfferRowID);
if (dbResult == null) return false;
bool success = await _repo.DeleteAsync(dbResult);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Effettua fix TipoImg righe child dell' Offer indicato
/// </summary>
/// <param name="offerId">Key</param>
/// <returns></returns>
public async Task<bool> FixImgTypeAsync(int offerId)
{
return await TraceAsync($"{_className}.FixImgType", async (activity) =>
{
// 1. Recupero righe
var rows = await _repo.GetByParentAsync(offerId);
// 2. Trovo quelle da sistemare
var list2fix = rows
.Where(x => x.ImgType == ImageType.ND)
.ToList();
// 3. Se non c'è nulla da fare → ritorno (nessun cambio necessario)
if (list2fix.Count == 0)
return true;
// 5. Aggiorno i record
foreach (var row in list2fix)
{
// se è calcolato il selling item --> img calcolata
if (row.SellingItemNav != null && (row.SellingItemNav.SourceType == ItemSourceType.Jwd || row.SellingItemNav.SourceType == ItemSourceType.FileBTL))
{
row.ImgType = ImageType.Calculated;
}
}
// 6. Salvo
bool success = await _repo.SaveRowsAsync(list2fix);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return true;
});
}
/// <summary>
/// Effettua fix UID righe child del temoplate indicato e restituisce elenco UID da chiamare x refresh
/// </summary>
/// <param name="offerId">Key</param>
/// <returns></returns>
public async Task<List<string>> FixUidAsync(int offerId)
{
return await TraceAsync($"{_className}.FixUid", async (activity) =>
{
var rows = await _repo.GetByParentAsync(offerId);
// 2. Trovo quelle da sistemare
var list2fix = rows
.Where(x => string.IsNullOrEmpty(x.OfferRowUID) || x.OfferRowUID != x.OfferRowDtx)
.ToList();
// 3. Se non c'è nulla da fare → ritorno
if (list2fix.Count == 0)
return new List<string>();
// 4. Preparo la lista da restituire
var result = list2fix
.Select(x => x.OfferRowDtx)
.ToList();
// 5. Aggiorno i record
foreach (var row in list2fix)
row.OfferRowUID = row.OfferRowDtx;
// 6. Salvo
bool success = await _repo.SaveRowsAsync(list2fix);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return result;
});
}
public async Task<OfferRowModel?> GetByIdAsync(int offerRowId)
{
return await TraceAsync($"{_className}.GetById", async (activity) =>
{
return await GetOrSetCacheAsync(
$"{_redisBaseKey}:{_className}:ById:{offerRowId}",
async () => await _repo.GetByIdAsync(offerRowId),
LongCache
);
});
}
/// <summary>
/// Elenco filtrato (parent) OfferRow da DB
/// </summary>
/// <param name="offerId"></param>
/// <returns></returns>
public async Task<List<OfferRowModel>> GetByParentAsync(int offerId)
{
return await TraceAsync($"{_className}.GetByParent", async (activity) =>
{
return await GetOrSetCacheAsync(
$"{_redisBaseKey}:{_className}:ByParent:{offerId}",
async () => await _repo.GetByParentAsync(offerId),
LongCache
);
});
}
public async Task<OfferRowModel?> GetByUidAsync(string offerRowUid)
{
return await TraceAsync($"{_className}.GetByUid", async (activity) =>
{
return await GetOrSetCacheAsync(
$"{_redisBaseKey}:{_className}:ByUid:{offerRowUid}",
async () => await _repo.GetByUidAsync(offerRowUid),
LongCache
);
});
}
public async Task<bool> UpdateAwaitStateAsync(int OfferRowId, bool? awaitBom, bool? awaitPrice, bool flushCache = false)
{
return await TraceAsync($"{_className}.UpdateAwaitState", async (activity) =>
{
var currRec = await _repo.GetByIdAsync(OfferRowId);
if (currRec == null)
return false;
currRec.AwaitBom = awaitBom ?? currRec.AwaitBom;
currRec.AwaitPrice = awaitPrice ?? currRec.AwaitPrice;
activity?.SetTag("db.operation", "UPDATE");
bool success = await _repo.UpdateAsync(currRec);
if (success && flushCache)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Effettua update della BOM (esplicita) della Riga Offer
/// </summary>
/// <param name="OfferRowID">ID Riga Offer da aggiornare</param>
/// <param name="newBomList">BOM List esplicita</param>
/// <returns></returns>
public async Task<bool> UpdateBomAsync(int OfferRowID, List<BomItemDTO> newBomList)
{
return await TraceAsync($"{_className}.UpdateBomById", async (activity) =>
{
var currRec = await _repo.GetByIdAsync(OfferRowID);
if (currRec == null)
return false;
var itemGroups = await _repo.GetItemGroupsAsync();
var bomItems = await _repo.GetBomItemsAsync();
// calcolo il NUOVO costo e lo aggiorno...
double totCost = 0;
double totPrice = 0;
int totItemQty = 0;
int numGroupOk = 0;
int numItemOk = 0;
int numElems = newBomList.Count;
// validazione e completamento BOM
BomCalculator.Validate(itemGroups, bomItems, ref newBomList, null, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk);
// salvo BOM...
string itemBom = JsonConvert.SerializeObject(newBomList);
currRec.ItemBOM = itemBom;
// salvo arrotondato alla 3° decimale
currRec.BomCost = Math.Round(totCost, 3);
currRec.BomPrice = Math.Round(totPrice, 3);
currRec.BomOk = numElems == numGroupOk;
currRec.ItemOk = numElems == numItemOk;
currRec.ProdItemQty = totItemQty;
activity?.SetTag("db.operation", "UpdateBomById");
bool success = await _repo.UpdateAsync(currRec);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Effettua update della BOM (esplicita) della Riga Offer
/// </summary>
/// <param name="uID">UID Riga Offer da aggiornare</param>
/// <param name="newBomList">BOM List esplicita</param>
/// <returns></returns>
public async Task<bool> UpdateBomAsync(string uID, List<BomItemDTO> newBomList)
{
return await TraceAsync($"{_className}.UpdateBomByUid", async (activity) =>
{
var currRec = await _repo.GetByUidAsync(uID);
if (currRec == null)
return false;
var itemGroups = await _repo.GetItemGroupsAsync();
var bomItems = await _repo.GetBomItemsAsync();
// recupero la BOM list precedente
var bomListPrev = JsonConvert.DeserializeObject<List<BomItemDTO>>(currRec.ItemBOM);
// calcolo il NUOVO costo e lo aggiorno...
double totCost = 0;
double totPrice = 0;
int totItemQty = 0;
int numGroupOk = 0;
int numItemOk = 0;
int numElems = newBomList.Count;
// validazione e completamento BOM
BomCalculator.Validate(itemGroups, bomItems, ref newBomList, bomListPrev, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk);
// salvo BOM...
string itemBom = JsonConvert.SerializeObject(newBomList);
currRec.ItemBOM = itemBom;
// salvo arrotondato alla 3° decimale
currRec.BomCost = Math.Round(totCost, 3);
currRec.BomPrice = Math.Round(totPrice, 3);
currRec.BomOk = numElems == numGroupOk;
currRec.ItemOk = numElems == numItemOk;
// setto ok await di BOM e Price
currRec.AwaitBom = false;
currRec.AwaitPrice = false;
currRec.ProdItemQty = totItemQty;
activity?.SetTag("db.operation", "UpdateBomByUid");
bool success = await _repo.UpdateAsync(currRec);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Effettua update delle info legate al file per il Offer indicato
/// </summary>
/// <param name="updRec">Riga Offer coi dati da aggiornare</param>
/// <returns></returns>
public async Task<bool> UpdateFileDataAsync(OfferRowModel updRec)
{
return await TraceAsync($"{_className}.UpdateFileData", async (activity) =>
{
var currRec = await _repo.GetByIdAsync(updRec.OfferRowID);
if (currRec == null)
return false;
currRec.FileName = updRec.FileName;
currRec.FileResource = updRec.FileResource;
currRec.FileSize = updRec.FileSize;
currRec.SerStruct = updRec.SerStruct;
currRec.ImgType = Core.Enums.ImageType.Calculated;
activity?.SetTag("db.operation", "UPDATE");
bool success = await _repo.UpdateAsync(currRec);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// <inheritdoc />
/// </summary>
public async Task<bool> UpdatePendReqAsync(int OfferRowID, Dictionary<string, string> reqDict)
{
return await TraceAsync($"{_className}.UpdatePendReqAsync", async (activity) =>
{
var currRec = await _repo.GetByIdAsync(OfferRowID);
if (currRec == null)
return false;
currRec.DictPendPresRaw = JsonConvert.SerializeObject(reqDict);
activity?.SetTag("db.operation", "UPDATE");
bool success = await _repo.UpdateAsync(currRec);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Effettua update del valore serializzato della Riga Offer
/// </summary>
/// <param name="OfferRowID">ID Riga Offer da aggiornare</param>
/// <param name="serStruct">Serializzazione oggetto (es JWD)</param>
/// <returns></returns>
public async Task<bool> UpdateSerStructAsync(int OfferRowID, string serStruct)
{
return await TraceAsync($"{_className}.UpdateSerStruct", async (activity) =>
{
var currRec = await _repo.GetByIdAsync(OfferRowID);
if (currRec == null)
return false;
currRec.SerStruct = serStruct;
currRec.ImgType = Core.Enums.ImageType.Calculated;
activity?.SetTag("db.operation", "UPDATE");
bool success = await _repo.UpdateAsync(currRec);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Effettua update del valore serializzato della Riga Offer + dizionario richieste ancora da applicare (stato dirty)
/// </summary>
/// <param name="offerRowID">ID Riga Offer da aggiornare</param>
/// <param name="serStruct">Serializzazione oggetto (es JWD)</param>
/// <param name="reqDict">Dizionario richieste da salvare x review manuale</param>
/// <returns></returns>
public async Task<bool> UpdateSerStructDirtyAsync(int OfferRowID, string serStruct, Dictionary<string, string> reqDict)
{
return await TraceAsync($"{_className}.UpdateSerStructDirty", async (activity) =>
{
var currRec = await _repo.GetByIdAsync(OfferRowID);
if (currRec == null)
return false;
currRec.SerStruct = serStruct;
currRec.ImgType = Core.Enums.ImageType.Calculated;
currRec.DictPendPresRaw = JsonConvert.SerializeObject(reqDict);
activity?.SetTag("db.operation", "UPDATE");
bool success = await _repo.UpdateAsync(currRec);
if (success)
{
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return success;
});
}
/// <summary>
/// Upsert record OfferRow
/// </summary>
/// <param name="updRec"></param>
/// <returns></returns>
public async Task<OfferRowModel?> UpsertAsync(OfferRowModel upsRec)
{
return await TraceAsync($"{_className}.Upsert", async (activity) =>
{
OfferRowModel? currRec = await _repo.GetByIdAsync(upsRec.OfferRowID);
string operation = "UPDATE";
bool success = false;
if (currRec != null)
{
success = await _repo.UpdateAsync(upsRec);
}
else
{
operation = "INSERT";
success = await _repo.AddAsync(upsRec);
if (!success) return null; // Return null on insert failure
}
activity?.SetTag("db.operation", operation);
if (success)
{
// sistemo UID...
await FixUidAsync(upsRec.OfferID);
await FixImgTypeAsync(upsRec.OfferID);
currRec = await _repo.GetByIdAsync(upsRec.OfferRowID);
}
else
{
// svuoto comunque cache...
await ClearCacheAsync($"{_redisBaseKey}:Offer:*");
await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
}
return currRec;
});
}
#endregion Public Methods
#region Private Fields
private readonly string _className;
private readonly IOfferRowRepository _repo;
#endregion Private Fields
}
}