using foodmarket.Domain.Common; namespace foodmarket.Domain.Sales; /// Тип скидки акции. public enum PromotionType { /// Процент от Subtotal (или от MatchingSubtotal если ProductGroupIds/ProductIds задан). Percent = 1, /// Фиксированная сумма скидки в валюте чека. FixedDiscount = 2, } /// Область применения акции — на весь чек или только на товары из /// указанных групп/конкретные товары. public enum PromotionScope { All = 1, ProductGroups = 2, Products = 3, } /// Промокод/акция. Применяется к розничному чеку при выбивании. /// /// Применение: либо кассир вводит вручную, либо для /// акций с пустым применяется автоматически если /// чек удовлетворяет правилам (период + минимум + scope). /// /// Период: <= sale.Date < /// (если EndsAt = null — бессрочная). позволяет /// быстро отключить акцию не меняя дат. public class Promotion : TenantEntity { public string Name { get; set; } = null!; public string? Description { get; set; } /// Код для ручного ввода. null/empty = auto-apply акция (без кода). /// Уникальный в рамках org через unique-index (`OrganizationId`, `Code`). /// public string? Code { get; set; } public PromotionType Type { get; set; } /// Значение скидки. Для Percent — [0..100], для FixedDiscount — в валюте чека. /// Хранится как decimal(18,4). public decimal Value { get; set; } public PromotionScope Scope { get; set; } = PromotionScope.All; /// Минимальная сумма чека для применения (на Subtotal). 0 = без ограничения. public decimal MinSaleAmount { get; set; } public DateTime StartsAt { get; set; } = DateTime.UtcNow; public DateTime? EndsAt { get; set; } public bool IsActive { get; set; } = true; /// Применимые ProductGroupId'ы (для Scope=ProductGroups). Сериализуется /// как JSONB-массив; пустой массив = ни одной группы (никогда не сматчит). public List ProductGroupIds { get; set; } = new(); /// Применимые ProductId'ы (для Scope=Products). public List ProductIds { get; set; } = new(); }