fix(inventory): записывать автора движения (CauseUserId) для всех документов
CauseUserId в уведомлениях о минусах был null: StockService писал автора только если вызывающий явно передал CreatedByUserId в черновике, а большинство документов (касса/списание/перемещение/приёмка/…) его не передавали. Теперь автор берётся из черновика, иначе — из текущего пользователя (ITenantContext.UserId) в единой точке ApplyMovementAsync, поэтому пишется и в StockMovement, и в алерт для всех типов документов. Тест: минус от списания → causeUserId = текущий юзер. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4aceda3e41
commit
9dc8f6e044
|
|
@ -22,6 +22,10 @@ public StockService(AppDbContext db, ITenantContext tenant, INegativeStockAlertS
|
||||||
public async Task<decimal> ApplyMovementAsync(StockMovementDraft d, CancellationToken ct = default)
|
public async Task<decimal> ApplyMovementAsync(StockMovementDraft d, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("Tenant not set.");
|
var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("Tenant not set.");
|
||||||
|
// Автор движения: берём из черновика, иначе — текущий пользователь (JWT). Так
|
||||||
|
// автор пишется для ВСЕХ документов (касса/списание/перемещение/приёмка/…),
|
||||||
|
// даже если вызывающий его явно не указал — иначе CauseUserId в алертах был null.
|
||||||
|
var userId = d.CreatedByUserId ?? _tenant.UserId;
|
||||||
|
|
||||||
_db.StockMovements.Add(new StockMovement
|
_db.StockMovements.Add(new StockMovement
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +39,7 @@ public async Task<decimal> ApplyMovementAsync(StockMovementDraft d, Cancellation
|
||||||
DocumentId = d.DocumentId,
|
DocumentId = d.DocumentId,
|
||||||
DocumentNumber = d.DocumentNumber,
|
DocumentNumber = d.DocumentNumber,
|
||||||
OccurredAt = d.OccurredAt ?? DateTime.UtcNow,
|
OccurredAt = d.OccurredAt ?? DateTime.UtcNow,
|
||||||
CreatedByUserId = d.CreatedByUserId,
|
CreatedByUserId = userId,
|
||||||
Notes = d.Notes,
|
Notes = d.Notes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -61,7 +65,7 @@ public async Task<decimal> ApplyMovementAsync(StockMovementDraft d, Cancellation
|
||||||
// Предупреждение об отрицательном остатке: единая точка детекции для всех
|
// Предупреждение об отрицательном остатке: единая точка детекции для всех
|
||||||
// документов. Остаток < 0 → открыть/обновить алерт; ≥ 0 → закрыть.
|
// документов. Остаток < 0 → открыть/обновить алерт; ≥ 0 → закрыть.
|
||||||
await _negAlerts.OnStockLevelAsync(d.StoreId, d.ProductId, stock.Quantity,
|
await _negAlerts.OnStockLevelAsync(d.StoreId, d.ProductId, stock.Quantity,
|
||||||
d.DocumentType, d.DocumentId, d.DocumentNumber, d.CreatedByUserId, d.OccurredAt, ct);
|
d.DocumentType, d.DocumentId, d.DocumentNumber, userId, d.OccurredAt, ct);
|
||||||
|
|
||||||
return stock.Quantity;
|
return stock.Quantity;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,12 @@ public async Task Loss_into_negative_is_allowed_creates_alert_recount_and_resolv
|
||||||
alerts[0].GetProperty("quantity").GetDecimal().Should().Be(-3m);
|
alerts[0].GetProperty("quantity").GetDecimal().Should().Be(-3m);
|
||||||
alerts[0].GetProperty("productId").GetString().Should().Be(p);
|
alerts[0].GetProperty("productId").GetString().Should().Be(p);
|
||||||
alerts[0].GetProperty("causeDocType").GetString().Should().Be("loss");
|
alerts[0].GetProperty("causeDocType").GetString().Should().Be("loss");
|
||||||
|
// Автор документа записан (раньше CauseUserId был null). Сверяем с /api/me.
|
||||||
|
var me = await api.GetJsonAsync("/api/me");
|
||||||
|
var myId = me.GetProperty("sub").GetString();
|
||||||
|
alerts[0].TryGetProperty("causeUserId", out var cu).Should().BeTrue();
|
||||||
|
cu.GetString().Should().NotBeNullOrEmpty("автор минусового документа должен быть записан");
|
||||||
|
cu.GetString().Should().Be(myId, "автор — текущий пользователь");
|
||||||
|
|
||||||
// «Пересчитать эти товары» → черновик инвентаризации с этим товаром.
|
// «Пересчитать эти товары» → черновик инвентаризации с этим товаром.
|
||||||
var rc = await api.Http.PostAsJsonAsync("/api/negative-stock/recount", new { storeId = refs.StoreId });
|
var rc = await api.Http.PostAsJsonAsync("/api/negative-stock/recount", new { storeId = refs.StoreId });
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue