fix(migration): приёмки — сохранение по одному документу + агрегация позиций по товару
Батч из 50 приёмок с общим товаром давал дубль строки stocks (ApplyMovementAsync создавал новую строку, не видя ещё не сохранённую). Теперь: SaveChanges после КАЖДОГО документа (следующий видит закоммиченные остатки) + агрегация позиций по товару в документе (средневзвешенная цена) + ChangeTracker.Clear (не копим граф на 7k докумов). Идемпотентно по msId — повторный запуск продолжает с места. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
10cd9f989b
commit
6fdc9e527a
|
|
@ -71,7 +71,6 @@ private static DateTime MomentToUtc(string? moment)
|
||||||
var done = (await _db.Supplies.Where(s => s.MoySkladExternalId != null)
|
var done = (await _db.Supplies.Where(s => s.MoySkladExternalId != null)
|
||||||
.Select(s => s.MoySkladExternalId!).ToListAsync(ct)).ToHashSet();
|
.Select(s => s.MoySkladExternalId!).ToListAsync(ct)).ToHashSet();
|
||||||
|
|
||||||
var batch = 0;
|
|
||||||
// expand=positions (НЕ positions.assortment — это раскрывает весь товар и делает
|
// expand=positions (НЕ positions.assortment — это раскрывает весь товар и делает
|
||||||
// страницу гигантской/зависающей). assortment.meta.href в позиции есть и так — из него
|
// страницу гигантской/зависающей). assortment.meta.href в позиции есть и так — из него
|
||||||
// берём id товара.
|
// берём id товара.
|
||||||
|
|
@ -102,15 +101,25 @@ private static DateTime MomentToUtc(string? moment)
|
||||||
PostedAt = posted ? momentUtc : null,
|
PostedAt = posted ? momentUtc : null,
|
||||||
PostedByUserId = posted ? adminUserId : null,
|
PostedByUserId = posted ? adminUserId : null,
|
||||||
};
|
};
|
||||||
|
// Агрегируем позиции по товару (один и тот же товар в документе — суммируем
|
||||||
|
// кол-во, цена — средневзвешенная): иначе два движения по одному товару дадут
|
||||||
|
// дубликат строки stocks в рамках одного SaveChanges.
|
||||||
|
var byProduct = new Dictionary<Guid, (decimal Qty, decimal Amount)>();
|
||||||
foreach (var pos in d.Positions?.Rows ?? new())
|
foreach (var pos in d.Positions?.Rows ?? new())
|
||||||
{
|
{
|
||||||
var prodMs = IdFromHref(pos.Assortment?.Meta?.Href);
|
var prodMs = IdFromHref(pos.Assortment?.Meta?.Href);
|
||||||
if (prodMs is null || !products.TryGetValue(prodMs, out var pid)) continue; // услуга/неизвестный — пропуск позиции
|
if (prodMs is null || !products.TryGetValue(prodMs, out var pid)) continue; // услуга/неизвестный
|
||||||
var up = pos.Price / 100m;
|
var up = pos.Price / 100m;
|
||||||
|
var cur = byProduct.GetValueOrDefault(pid);
|
||||||
|
byProduct[pid] = (cur.Qty + pos.Quantity, cur.Amount + pos.Quantity * up);
|
||||||
|
}
|
||||||
|
foreach (var (pid, agg) in byProduct)
|
||||||
|
{
|
||||||
|
var unit = agg.Qty != 0 ? decimal.Round(agg.Amount / agg.Qty, 4) : 0m;
|
||||||
supply.Lines.Add(new foodmarket.Domain.Purchases.SupplyLine
|
supply.Lines.Add(new foodmarket.Domain.Purchases.SupplyLine
|
||||||
{
|
{
|
||||||
OrganizationId = orgId, ProductId = pid, Quantity = pos.Quantity, UnitPrice = up,
|
OrganizationId = orgId, ProductId = pid, Quantity = agg.Qty, UnitPrice = unit,
|
||||||
LineTotal = decimal.Round(pos.Quantity * up, 2),
|
LineTotal = decimal.Round(agg.Amount, 2),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_db.Supplies.Add(supply);
|
_db.Supplies.Add(supply);
|
||||||
|
|
@ -123,9 +132,12 @@ private static DateTime MomentToUtc(string? moment)
|
||||||
DocumentNumber: supply.Number, UnitCost: l.UnitPrice, OccurredAt: momentUtc,
|
DocumentNumber: supply.Number, UnitCost: l.UnitPrice, OccurredAt: momentUtc,
|
||||||
CreatedByUserId: adminUserId), ct);
|
CreatedByUserId: adminUserId), ct);
|
||||||
|
|
||||||
|
// Сохраняем ПО ОДНОМУ документу: следующий документ увидит уже
|
||||||
|
// закоммиченные строки stocks (ApplyMovementAsync ищет в БД) — без дублей.
|
||||||
|
await _db.SaveChangesAsync(ct);
|
||||||
|
_db.ChangeTracker.Clear(); // не копим tracked-граф на 7k документов
|
||||||
done.Add(d.Id);
|
done.Add(d.Id);
|
||||||
progress.Created++;
|
progress.Created++;
|
||||||
if (++batch >= 50) { await _db.SaveChangesAsync(ct); batch = 0; }
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -134,7 +146,6 @@ private static DateTime MomentToUtc(string? moment)
|
||||||
_log.LogWarning(ex, "supply import failed {Id}", d.Id);
|
_log.LogWarning(ex, "supply import failed {Id}", d.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (batch > 0) await _db.SaveChangesAsync(ct);
|
|
||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue