Merge branch 'feature/MigrateDataLayerService' of https://gitlab.steamware.net/etis/lux into feature/MigrateDataLayerService

This commit is contained in:
Samuele Locatelli
2026-03-18 11:20:47 +01:00
4 changed files with 317 additions and 193 deletions
@@ -35,86 +35,99 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
public async Task<bool> CloneAsync(OfferModel rec2clone)
{
await using var dbCtx = await CreateContextAsync();
DateTime now = DateTime.Now;
var currRec = await dbCtx.DbSetOffer
.Include(x => x.OfferRowNav)
.FirstOrDefaultAsync(x => x.OfferID == rec2clone.OfferID);
if (currRec == null)
return false;
DateTime adesso = DateTime.Now;
var lastRec = dbCtx
.DbSetOffer
.Where(x => x.RefYear == adesso.Year)
.OrderByDescending(x => x.RefNum)
.FirstOrDefault();
int newRefNum = lastRec != null ? lastRec.RefNum + 1 : 1;
// 2. Creo il nuovo parent
var newRec = new OfferModel()
// Wrap in transaction for atomicity (parent + children clone)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
ConsNote = rec2clone.ConsNote,
CustomerID = rec2clone.CustomerID,
DealerID = rec2clone.DealerID,
Description = rec2clone.Description,
DictPresel = rec2clone.DictPresel,
Discount = rec2clone.Discount,
DueDateProm = rec2clone.DueDateProm,
DueDateReq = rec2clone.DueDateReq,
Envir = rec2clone.Envir,
Inserted = adesso,
Modified = adesso,
OffertState = OfferStates.Open,
RefNum = newRefNum,
RefRev = 1,
RefYear = adesso.Year,
ValidUntil = currRec.ValidUntil
};
DateTime now = DateTime.Now;
// 3. Clono i child
// sistemo child...
newRec.OfferRowNav = currRec.OfferRowNav
.Select(c => new OfferRowModel()
var currRec = await dbCtx.DbSetOffer
.Include(x => x.OfferRowNav)
.FirstOrDefaultAsync(x => x.OfferID == rec2clone.OfferID);
if (currRec == null)
return false;
DateTime adesso = DateTime.Now;
var lastRec = dbCtx
.DbSetOffer
.Where(x => x.RefYear == adesso.Year)
.OrderByDescending(x => x.RefNum)
.FirstOrDefault();
int newRefNum = lastRec != null ? lastRec.RefNum + 1 : 1;
// 2. Creo il nuovo parent
var newRec = new OfferModel()
{
AwaitBom = c.AwaitBom,
AwaitPrice = c.AwaitPrice,
BomCost = c.BomCost,
BomOk = c.BomOk,
BomPrice = c.BomPrice,
Envir = c.Envir,
FileName = c.FileName,
FileResource = c.FileResource,
FileSize = c.FileSize,
ConsNote = rec2clone.ConsNote,
CustomerID = rec2clone.CustomerID,
DealerID = rec2clone.DealerID,
Description = rec2clone.Description,
DictPresel = rec2clone.DictPresel,
Discount = rec2clone.Discount,
DueDateProm = rec2clone.DueDateProm,
DueDateReq = rec2clone.DueDateReq,
Envir = rec2clone.Envir,
Inserted = adesso,
ItemBOM = c.ItemBOM,
ItemJCD = c.ItemJCD,
ItemOk = c.ItemOk,
ItemSteps = c.ItemSteps,
ItemTags = c.ItemTags,
JobID = c.JobID,
Modified = c.Modified,
Note = c.Note,
ProdItemQty = c.ProdItemQty,
Qty = c.Qty,
RowNum = c.RowNum,
SellingItemID = c.SellingItemID,
SerStruct = c.SerStruct,
StepCost = c.StepCost,
StepFlowTime = c.StepFlowTime,
StepLeadTime = c.StepLeadTime,
StepPrice = c.StepPrice,
//OrderID = dbRec.OrderID,
OfferRowUID = c.OfferRowDtx
})
.ToList();
Modified = adesso,
OffertState = OfferStates.Open,
RefNum = newRefNum,
RefRev = 1,
RefYear = adesso.Year,
ValidUntil = currRec.ValidUntil
};
// 4. Aggiungo il nuovo parent (EF aggiunge anche i child)
dbCtx.DbSetOffer.Add(newRec);
// 3. Clono i child
// sistemo child...
newRec.OfferRowNav = currRec.OfferRowNav
.Select(c => new OfferRowModel()
{
AwaitBom = c.AwaitBom,
AwaitPrice = c.AwaitPrice,
BomCost = c.BomCost,
BomOk = c.BomOk,
BomPrice = c.BomPrice,
Envir = c.Envir,
FileName = c.FileName,
FileResource = c.FileResource,
FileSize = c.FileSize,
Inserted = adesso,
ItemBOM = c.ItemBOM,
ItemJCD = c.ItemJCD,
ItemOk = c.ItemOk,
ItemSteps = c.ItemSteps,
ItemTags = c.ItemTags,
JobID = c.JobID,
Modified = c.Modified,
Note = c.Note,
ProdItemQty = c.ProdItemQty,
Qty = c.Qty,
RowNum = c.RowNum,
SellingItemID = c.SellingItemID,
SerStruct = c.SerStruct,
StepCost = c.StepCost,
StepFlowTime = c.StepFlowTime,
StepLeadTime = c.StepLeadTime,
StepPrice = c.StepPrice,
//OrderID = dbRec.OrderID,
OfferRowUID = c.OfferRowDtx
})
.ToList();
// 5. Salvo tutto
return await dbCtx.SaveChangesAsync() > 0;
// 4. Aggiungo il nuovo parent (EF aggiunge anche i child)
dbCtx.DbSetOffer.Add(newRec);
// 5. Salvo tutto in transazione
await dbCtx.SaveChangesAsync();
tx.Commit();
return true;
}
catch
{
tx.Rollback();
throw;
}
}
public async Task<bool> DeleteAsync(OfferModel entity)
@@ -178,10 +191,26 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
public async Task<bool> SaveRowsAsync(List<OfferRowModel> rows)
{
await using var dbCtx = await CreateContextAsync();
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
return await dbCtx.SaveChangesAsync() > 0;
// Wrap in transaction for atomicity (batch update multiple rows)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
}
public async Task<bool> UpdateAsync(OfferModel entity)
@@ -28,27 +28,44 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
{
await using var dbCtx = await CreateContextAsync();
// 1. Recupero il record da eliminare
var dbResult = await dbCtx.DbSetOfferRow
.FirstOrDefaultAsync(x => x.OfferRowID == entity.OfferRowID);
if (dbResult == null)
return false;
// 2. Recupero i record successivi da shiftare
var list2Move = await dbCtx.DbSetOfferRow
.Where(x => x.OfferID == entity.OfferID && x.RowNum > dbResult.RowNum)
.ToListAsync();
foreach (var item in list2Move)
// Wrap in transaction for atomicity (multi-row update + delete)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
item.RowNum--;
dbCtx.Entry(item).State = EntityState.Modified;
}
// 1. Recupero il record da eliminare
var dbResult = await dbCtx.DbSetOfferRow
.FirstOrDefaultAsync(x => x.OfferRowID == entity.OfferRowID);
// 3. Rimuovo il record
dbCtx.DbSetOfferRow.Remove(dbResult);
return await dbCtx.SaveChangesAsync() > 0;
if (dbResult == null)
return false;
// 2. Recupero i record successivi da shiftare
var list2Move = await dbCtx.DbSetOfferRow
.Where(x => x.OfferID == entity.OfferID && x.RowNum > dbResult.RowNum)
.ToListAsync();
foreach (var item in list2Move)
{
item.RowNum--;
dbCtx.Entry(item).State = EntityState.Modified;
}
// 3. Rimuovo il record
dbCtx.DbSetOfferRow.Remove(dbResult);
// 4. Salvo tutto
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
}
public async Task<List<ItemModel>> GetBomItemsAsync()
@@ -94,10 +111,26 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
public async Task<bool> SaveRowsAsync(List<OfferRowModel> rows)
{
await using var dbCtx = await CreateContextAsync();
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
return await dbCtx.SaveChangesAsync() > 0;
// Wrap in transaction for atomicity (batch update multiple rows)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
}
public async Task<bool> UpdateAsync(OfferRowModel entity)
@@ -241,10 +241,26 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
public async Task<bool> SaveRowsAsync(List<OrderRowModel> rows)
{
await using var dbCtx = await CreateContextAsync();
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
return await dbCtx.SaveChangesAsync() > 0;
// Wrap in transaction for atomicity (batch update multiple rows)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
}
public async Task<bool> UpdateAsync(OrderModel entity)
@@ -269,58 +285,73 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
{
await using var dbCtx = await CreateContextAsync();
// recupero righe Orderta...
var offRowList = await dbCtx
.DbSetOrderRow
.Where(x => x.OrderID == OrderID)
.ToListAsync();
// recupero l'elenco degli itemGroup gestiti
var itemGroupList = await dbCtx
.DbSetItemGroup
.ToListAsync();
// recupero il subset item da BOM / BomAlt...
var bomGenList = await dbCtx
.DbSetItem
.Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt))
.ToListAsync();
// ciclo!
foreach (var currRec in offRowList)
// Wrap in transaction for atomicity (multi-row update in loop)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
// se contiene qualcosa x BOM...
if (!string.IsNullOrEmpty(currRec.ItemBOM) && currRec.ItemBOM.Length > 2)
// recupero righe Orderta...
var offRowList = await dbCtx
.DbSetOrderRow
.Where(x => x.OrderID == OrderID)
.ToListAsync();
// recupero l'elenco degli itemGroup gestiti
var itemGroupList = await dbCtx
.DbSetItemGroup
.ToListAsync();
// recupero il subset item da BOM / BomAlt...
var bomGenList = await dbCtx
.DbSetItem
.Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt))
.ToListAsync();
// ciclo!
foreach (var currRec in offRowList)
{
// deserializzo
var bomList = JsonConvert.DeserializeObject<List<BomItemDTO>>(currRec.ItemBOM);
// se ho trovato elementi...
if (bomList != null)
// se contiene qualcosa x BOM...
if (!string.IsNullOrEmpty(currRec.ItemBOM) && currRec.ItemBOM.Length > 2)
{
// calcolo il NUOVO costo e lo aggiorno...
double totCost = 0;
double totPrice = 0;
int totItemQty = 0;
int numGroupOk = 0;
int numItemOk = 0;
int numElems = bomList.Count;
// validazione e completamento BOM
BomCalculator.Validate(itemGroupList, bomGenList, ref bomList, null, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk);
// salvo BOM...
string itemBom = JsonConvert.SerializeObject(bomList);
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;
dbCtx.Entry(currRec).State = EntityState.Modified;
// deserializzo
var bomList = JsonConvert.DeserializeObject<List<BomItemDTO>>(currRec.ItemBOM);
// se ho trovato elementi...
if (bomList != null)
{
// calcolo il NUOVO costo e lo aggiorno...
double totCost = 0;
double totPrice = 0;
int totItemQty = 0;
int numGroupOk = 0;
int numItemOk = 0;
int numElems = bomList.Count;
// validazione e completamento BOM
BomCalculator.Validate(itemGroupList, bomGenList, ref bomList, null, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk);
// salvo BOM...
string itemBom = JsonConvert.SerializeObject(bomList);
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;
dbCtx.Entry(currRec).State = EntityState.Modified;
}
}
}
}
return await dbCtx.SaveChangesAsync() > 0;
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
}
#endregion Public Methods
@@ -26,29 +26,44 @@ namespace EgwCoreLib.Lux.Data.Repository.Utils
{
await using var dbCtx = await CreateContextAsync();
// 1. Recupero il record da eliminare
var dbResult = await dbCtx.DbSetGenVal
.FirstOrDefaultAsync(x => x.GenValID == rec2del.GenValID);
if (dbResult == null)
return false;
// 2. Recupero i record successivi da shiftare
var list2Move = await dbCtx.DbSetGenVal
.Where(x => x.ClassCod == rec2del.ClassCod && x.Index > dbResult.Index)
.ToListAsync();
foreach (var item in list2Move)
// Wrap in transaction for atomicity (multi-row update + delete)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
item.Index--;
dbCtx.Entry(item).State = EntityState.Modified;
// 1. Recupero il record da eliminare
var dbResult = await dbCtx.DbSetGenVal
.FirstOrDefaultAsync(x => x.GenValID == rec2del.GenValID);
if (dbResult == null)
return false;
// 2. Recupero i record successivi da shiftare
var list2Move = await dbCtx.DbSetGenVal
.Where(x => x.ClassCod == rec2del.ClassCod && x.Index > dbResult.Index)
.ToListAsync();
foreach (var item in list2Move)
{
item.Index--;
dbCtx.Entry(item).State = EntityState.Modified;
}
// 3. Rimuovo il record
dbCtx.DbSetGenVal.Remove(dbResult);
// 4. Salvo tutto
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
// 3. Rimuovo il record
dbCtx.DbSetGenVal.Remove(dbResult);
// 4. Salvo tutto
return await dbCtx.SaveChangesAsync() > 0;
}
public async Task<GenValueModel?> GetByIdAsync(int Id)
@@ -69,40 +84,56 @@ namespace EgwCoreLib.Lux.Data.Repository.Utils
public async Task<bool> MoveAsync(GenValueModel selRec, bool moveUp)
{
await using var dbCtx = await CreateContextAsync();
// 1. Recupero il record corrente
var currRec = await dbCtx.DbSetGenVal
.FirstOrDefaultAsync(x => x.GenValID == selRec.GenValID);
if (currRec == null)
return false;
// Wrap in transaction for atomicity (multi-row update - swap positions)
await using var tx = dbCtx.Database.BeginTransaction();
try
{
// 1. Recupero il record corrente
var currRec = await dbCtx.DbSetGenVal
.FirstOrDefaultAsync(x => x.GenValID == selRec.GenValID);
// 2. Numero totale record della classe
int numRec = await dbCtx.DbSetGenVal
.CountAsync(x => x.ClassCod == selRec.ClassCod);
if (currRec == null)
return false;
// 3. Calcolo nuova posizione
int newPos = moveUp ? currRec.Index - 1 : currRec.Index + 1;
// 2. Numero totale record della classe
int numRec = await dbCtx.DbSetGenVal
.CountAsync(x => x.ClassCod == selRec.ClassCod);
bool canMove = moveUp ? newPos > 0 : newPos <= numRec;
if (!canMove)
return false;
// 3. Calcolo nuova posizione
int newPos = moveUp ? currRec.Index - 1 : currRec.Index + 1;
// 4. Recupero il record da scambiare
var otherRec = await dbCtx.DbSetGenVal
.FirstOrDefaultAsync(x => x.ClassCod == selRec.ClassCod && x.Index == newPos);
bool canMove = moveUp ? newPos > 0 : newPos <= numRec;
if (!canMove)
return false;
if (otherRec == null)
return false;
// 4. Recupero il record da scambiare
var otherRec = await dbCtx.DbSetGenVal
.FirstOrDefaultAsync(x => x.ClassCod == selRec.ClassCod && x.Index == newPos);
// 5. Swap indici
otherRec.Index = currRec.Index;
currRec.Index = newPos;
if (otherRec == null)
return false;
dbCtx.Entry(otherRec).State = EntityState.Modified;
dbCtx.Entry(currRec).State = EntityState.Modified;
// 5. Swap indici
otherRec.Index = currRec.Index;
currRec.Index = newPos;
// 6. Salvo
return await dbCtx.SaveChangesAsync() > 0;
dbCtx.Entry(otherRec).State = EntityState.Modified;
dbCtx.Entry(currRec).State = EntityState.Modified;
// 6. Salvo
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
tx.Commit();
return done;
}
catch
{
tx.Rollback();
throw;
}
}
public async Task<bool> UpdateAsync(GenValueModel entity)