Domain Enter+EnterLine (мирорит Supply, но без SupplierId и без cost rollup). EF-конфигурация, миграция Phase6a_Enters (idempotent CREATE TABLE). Контроллер api/inventory/enters: CRUD + Post/Unpost. Post создаёт StockMovement тип Enter; Unpost блокируется, если остаток ушёл бы в минус. Web: /inventory/enters (list + edit), пункт «Оприходования» в сайдбаре Admin/Storekeeper. Тесты: 4 интеграционных (post раздаёт stock, unpost откатывает, double post→409, tenant-изоляция A/B, unpost блокируется при минусе после продажи). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
63 lines
2.5 KiB
C#
63 lines
2.5 KiB
C#
using foodmarket.Domain.Catalog;
|
||
using foodmarket.Domain.Common;
|
||
|
||
namespace foodmarket.Domain.Purchases;
|
||
|
||
public enum EnterStatus
|
||
{
|
||
Draft = 0,
|
||
Posted = 1,
|
||
}
|
||
|
||
/// <summary>Оприходование: документ постановки товара на склад БЕЗ поставщика.
|
||
/// Используется для начальных остатков (при запуске учёта), излишка по
|
||
/// результату инвентаризации, возврата товара из подразделения и т.п.
|
||
///
|
||
/// Отличается от <see cref="Supply"/>: нет SupplierId; total — не сумма закупки,
|
||
/// а сумма по UnitCost (стоимость оприходованного товара по балансовой цене).
|
||
/// При Post создаёт <see cref="Inventory.StockMovement"/> с типом
|
||
/// <see cref="Inventory.MovementType.Enter"/>.</summary>
|
||
public class Enter : TenantEntity
|
||
{
|
||
/// <summary>Уникальный в рамках организации номер документа (например "О-2026-000001").</summary>
|
||
public string Number { get; set; } = "";
|
||
|
||
public DateTime Date { get; set; } = DateTime.UtcNow;
|
||
public EnterStatus Status { get; set; } = EnterStatus.Draft;
|
||
|
||
public Guid StoreId { get; set; }
|
||
public Store Store { get; set; } = null!;
|
||
|
||
public Guid CurrencyId { get; set; }
|
||
public Currency Currency { get; set; } = null!;
|
||
|
||
public string? Notes { get; set; }
|
||
|
||
/// <summary>Сумма по строкам = Σ Quantity·UnitCost.</summary>
|
||
public decimal Total { get; set; }
|
||
|
||
public DateTime? PostedAt { get; set; }
|
||
public Guid? PostedByUserId { get; set; }
|
||
|
||
public ICollection<EnterLine> Lines { get; set; } = new List<EnterLine>();
|
||
}
|
||
|
||
public class EnterLine : TenantEntity
|
||
{
|
||
public Guid EnterId { get; set; }
|
||
public Enter Enter { get; set; } = null!;
|
||
|
||
public Guid ProductId { get; set; }
|
||
public Product Product { get; set; } = null!;
|
||
|
||
public decimal Quantity { get; set; }
|
||
|
||
/// <summary>Балансовая цена единицы (по которой товар ставится на учёт).
|
||
/// Не пересчитывает <c>Product.Cost</c> — оприходование не образует
|
||
/// себестоимости (в отличие от приёмки).</summary>
|
||
public decimal UnitCost { get; set; }
|
||
public decimal LineTotal { get; set; }
|
||
|
||
public int SortOrder { get; set; }
|
||
}
|