using System; using System.Collections.Generic; using System.Linq; using Pos.Models; namespace Pos.Service { /// /// Holder styr på den igangværende handel og dens beregninger. /// Servicen har ingen afhængighed til WPF eller web. /// public class SalesTransactionService { private readonly List _saleLines = new List(); private readonly List _payments = new List(); public IReadOnlyList SaleLines => _saleLines; public IReadOnlyList Payments => _payments; public decimal TotalPrice => _saleLines.Sum(line => CalculateLineTotal(line.Price, line.NumberProducts)); public decimal AmountPaid => _payments.Sum(payment => payment.Amount); public decimal RemainingAmount => Math.Max(TotalPrice - AmountPaid, 0); public decimal Change => Math.Max(AmountPaid - TotalPrice, 0); public bool IsFullyPaid => TotalPrice > 0 && AmountPaid >= TotalPrice; /// /// Beregner totalen for en enkelt salgslinje. /// Parsing og visning af resultatet er fortsat UI-ansvar. /// public decimal CalculateLineTotal(decimal price, int numberOfProducts) { ValidatePriceAndQuantity(price, numberOfProducts); return price * numberOfProducts; } public PriceLineModel AddSaleLine( int id, string name, int numberOfProducts, decimal price, bool isProductGroup) { if (id <= 0) throw new ArgumentOutOfRangeException( nameof(id), "Id skal være større end 0."); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException( "Navn skal være udfyldt.", nameof(name)); ValidatePriceAndQuantity(price, numberOfProducts); PriceLineModel saleLine = new PriceLineModel { Id = id, Name = name, NumberProducts = numberOfProducts, Price = price, IsProductGroup = isProductGroup }; _saleLines.Add(saleLine); return saleLine; } public void RemoveSaleLineAt(int index) { if (index < 0 || index >= _saleLines.Count) throw new ArgumentOutOfRangeException(nameof(index)); _saleLines.RemoveAt(index); } /// /// Registrerer en betaling. Hvis amount er null, betales restbeløbet. /// public AmountGridModel AddPayment( PaymentMethod paymentMethod, decimal? amount = null) { if (_saleLines.Count == 0) throw new InvalidOperationException( "Der kan ikke registreres betaling på et tomt salg."); decimal paymentAmount = amount ?? RemainingAmount; if (paymentAmount <= 0) throw new ArgumentOutOfRangeException( nameof(amount), "Betalingsbeløbet skal være større end 0."); AmountGridModel payment = new AmountGridModel { Amount = paymentAmount, PaymentMethodText = GetPaymentMethodText(paymentMethod) }; _payments.Add(payment); return payment; } public void RemovePaymentAt(int index) { if (index < 0 || index >= _payments.Count) throw new ArgumentOutOfRangeException(nameof(index)); _payments.RemoveAt(index); } public void EnsureCanComplete() { if (_saleLines.Count == 0) throw new InvalidOperationException( "Et tomt salg kan ikke afsluttes."); if (!IsFullyPaid) throw new InvalidOperationException( "Salget er ikke fuldt betalt."); } public void Reset() { _saleLines.Clear(); _payments.Clear(); } private static void ValidatePriceAndQuantity( decimal price, int numberOfProducts) { if (price < 0) throw new ArgumentOutOfRangeException( nameof(price), "Prisen må ikke være negativ."); if (numberOfProducts <= 0) throw new ArgumentOutOfRangeException( nameof(numberOfProducts), "Antal skal være større end 0."); } private static string GetPaymentMethodText(PaymentMethod paymentMethod) { switch (paymentMethod) { case PaymentMethod.Card: return "Kort"; case PaymentMethod.Cash: return "Kontant"; case PaymentMethod.GiftCard: return "Gavekort"; case PaymentMethod.MobilePay: return "MobilePay"; default: throw new ArgumentOutOfRangeException(nameof(paymentMethod)); } } } }