moved logic out of SalesWindows.xaml.cs to SaleTransactionServce

This commit is contained in:
Alex Pedersen
2026-07-30 09:41:47 +02:00
parent 236eb6792c
commit 97688542fe
3 changed files with 289 additions and 81 deletions

View File

@@ -29,7 +29,8 @@ namespace Pos
private ObservableCollection<ProductGroupEntity> _productGroups;
private ObservableCollection<SaleGridModel> _saleGridModels;
private ObservableCollection<AmountGridModel> _paymentGridModels;
private List<PriceLineModel> _priceLineModels;
private readonly SalesTransactionService _salesTransactionService =
new SalesTransactionService();
private ProductEntity _selectedProduct;
private ProductGroupEntity _selectedProductGroup;
@@ -37,14 +38,10 @@ namespace Pos
private int _employeeId = -1;
private int _productGroupId = -1;
private int _productId = -1;
private int _numberOfProducts = 1;
private int _selectedSaleGridIndex = -1;
private int _maxCharOfName = 0;
private decimal _price = 0;
private decimal _totalPrice = 0;
private Button _prevEmpButton = new Button();
private Button _prevProductGroupButton = new Button();
private Button _prevProduct = new Button();
@@ -61,7 +58,6 @@ namespace Pos
_employees = CacheService.GetEmployee() as ObservableCollection<EmployeeEntity>;
_productGroups = CacheService.GetProductGroupsIncludeProducts() as ObservableCollection<ProductGroupEntity>;
_saleGridModels = new ObservableCollection<SaleGridModel>();
_priceLineModels = new List<PriceLineModel>();
_paymentGridModels = new ObservableCollection<AmountGridModel>();
SaleGrid.ItemsSource = _saleGridModels;
PaymentGrid.ItemsSource = _paymentGridModels;
@@ -150,11 +146,25 @@ namespace Pos
return;
decimal price = Convert.ToDecimal(Price.Text);
int number = Convert.ToInt32(NumberOfProducts.Text);
int numberOfProducts = Convert.ToInt32(NumberOfProducts.Text);
try
{
BuildPrice(numberOfProducts, price);
}
catch (ArgumentException exception)
{
MessageBox.Show(
exception.Message,
"Ugyldig salgslinje",
MessageBoxButton.OK,
MessageBoxImage.Error);
return;
}
NumberOfProducts.Text = "1";
Price.Text = string.Empty;
TotalPrice.Content = "0";
BuildPrice(false);
_productId = -1;
_productGroupId = -1;
ManipulateBuySection();
@@ -162,16 +172,36 @@ namespace Pos
private void Payment_Click(object sender, RoutedEventArgs e)
{
//bool isValid = ValidateAmountInput();
//if (!isValid)
// return;
decimal amount = -1;
decimal? amount = null;
if (!string.IsNullOrEmpty(Amount.Text))
{
bool isValid = ValidateAmountInput();
if (!isValid)
return;
amount = Convert.ToDecimal(Amount.Text);
}
Amount.Text = string.Empty;
Button button = sender as Button;
BuildAmuntDataGrid(amount,(string)button.Tag);
string paymentMethodName =
((string)button.Tag).Replace("PaymentMethod.", string.Empty);
if (!Enum.TryParse(paymentMethodName, out PaymentMethod paymentMethod))
return;
try
{
BuildAmuntDataGrid(amount, paymentMethod);
}
catch (ArgumentException exception)
{
MessageBox.Show(
exception.Message,
"Ugyldig betaling",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
private void NumberOfProducts_LostFocus(object sender, RoutedEventArgs e)
@@ -249,61 +279,69 @@ namespace Pos
}
}
private void BuildPrice(bool returnProduct)
private void BuildPrice(int numberOfProducts, decimal price)
{
PriceLineModel pModel = new PriceLineModel();
pModel.NumberProducts = _numberOfProducts;
pModel.Price = _price;
PriceLineModel priceLineModel;
if (_selectedProduct != null)
{
pModel.Id = _selectedProduct.Id;
pModel.Name = _selectedProduct.Name;
pModel.IsProductGroup = false;
priceLineModel = _salesTransactionService.AddSaleLine(
_selectedProduct.Id,
_selectedProduct.Name,
numberOfProducts,
price,
false);
}
else
{
pModel.Id = _selectedProductGroup.Id;
pModel.Name = _selectedProductGroup.Name;
pModel.IsProductGroup = true;
priceLineModel = _salesTransactionService.AddSaleLine(
_selectedProductGroup.Id,
_selectedProductGroup.Name,
numberOfProducts,
price,
true);
}
BuildPriceDataGrid(pModel);
BuildPriceDataGrid(priceLineModel);
//BuildEmployee();
BuildProductGroup();
}
private void CalculateSubPrice()
{
_price = 0;
bool isValid = decimal.TryParse(Price.Text, out _price);
bool isValid = decimal.TryParse(Price.Text, out decimal price);
if (!isValid)
return;
isValid = Int32.TryParse(NumberOfProducts.Text, out _numberOfProducts);
isValid = Int32.TryParse(
NumberOfProducts.Text,
out int numberOfProducts);
if (!isValid)
return;
TotalPrice.Content = _price * _numberOfProducts;
try
{
TotalPrice.Content =
_salesTransactionService.CalculateLineTotal(
price,
numberOfProducts);
}
catch (ArgumentOutOfRangeException)
{
TotalPrice.Content = "0";
}
}
private void CalculateTotalLine()
{
_totalPrice = 0;
foreach (PriceLineModel model in _priceLineModels)
{
_totalPrice += model.Price * model.NumberProducts;
}
Total.Content = $"At betale: Kr. {_totalPrice}";
Total.Content =
$"At betale: Kr. {_salesTransactionService.TotalPrice}";
}
private void BuildPriceDataGrid(PriceLineModel priceLineModel)
{
_selectedProduct = null;
_selectedProductGroup = null;
_numberOfProducts = 1;
_priceLineModels.Add(priceLineModel);
CalculateTotalLine();
SaleGridModel model = new SaleGridModel();
@@ -399,38 +437,14 @@ namespace Pos
TotalPrice.IsEnabled = enabled;
}
private void BuildAmuntDataGrid(decimal amount, string paymentMethod)
private void BuildAmuntDataGrid(
decimal? amount,
PaymentMethod paymentMethod)
{
if (amount == -1)
{
amount = 0;
foreach (PriceLineModel lineModel in _priceLineModels)
{
amount += (lineModel.Price * lineModel.NumberProducts);
}
}
AmountGridModel amountGridModel =
_salesTransactionService.AddPayment(paymentMethod, amount);
AmountGridModel amountGridModel = new AmountGridModel();
amountGridModel.Amount = amount;
amountGridModel.AmountText = $"Kr. {amount}";
switch (paymentMethod)
{
case "PaymentMethod.Card":
amountGridModel.PaymentMethodText = "Kort";
break;
case "PaymentMethod.Cash":
amountGridModel.PaymentMethodText = "Kontant";
break;
case "PaymentMethod.GiftCard":
amountGridModel.PaymentMethodText = "Gavekort";
break;
case "PaymentMethod.MobilePay":
amountGridModel.PaymentMethodText = "MobilePay";
break;
}
amountGridModel.AmountText = $"Kr. {amountGridModel.Amount}";
_paymentGridModels.Add(amountGridModel);
AmountPayed();
@@ -438,28 +452,25 @@ namespace Pos
private void AmountPayed()
{
decimal amountPayed = 0;
foreach (AmountGridModel paymentGridModel in _paymentGridModels)
{
amountPayed += paymentGridModel.Amount;
}
if (amountPayed == _totalPrice)
if (_salesTransactionService.AmountPaid ==
_salesTransactionService.TotalPrice)
{
btnFinish_Click(null,null);
return;
}
if (amountPayed > _totalPrice)
if (_salesTransactionService.Change > 0)
{
lblRecived.Content = $"Byttepenge: Kr. {amountPayed - _totalPrice}";
lblRecived.Content =
$"Byttepenge: Kr. {_salesTransactionService.Change}";
Style style = this.FindResource("BoldLabelWarningNoMargin") as Style;
lblRecived.Style = style;
btnFinish.IsEnabled = true;
}
else
{
lblRecived.Content = $"At betale: Kr. {_totalPrice - amountPayed}";
lblRecived.Content =
$"At betale: Kr. {_salesTransactionService.RemainingAmount}";
Style style = this.FindResource("BoldLabelNoMargin") as Style;
lblRecived.Style = style;
}
@@ -473,21 +484,37 @@ namespace Pos
if (_selectedSaleGridIndex == -1)
return;
int index = _selectedSaleGridIndex;
//_saleGridModels.RemoveAt(_selectedSaleGridIndex);
_priceLineModels.RemoveAt(index);
_salesTransactionService.RemoveSaleLineAt(index);
_saleGridModels.RemoveAt(index);
_selectedSaleGridIndex = -1;
CalculateTotalLine();
if (_salesTransactionService.AmountPaid > 0)
AmountPayed();
}
}
private void btnFinish_Click(object sender, RoutedEventArgs e)
{
try
{
_salesTransactionService.EnsureCanComplete();
}
catch (InvalidOperationException exception)
{
MessageBox.Show(
exception.Message,
"Salget kan ikke afsluttes",
MessageBoxButton.OK,
MessageBoxImage.Error);
return;
}
SaleService saleService = new SaleService();
saleService.Save(_saleGridModels.ToList(),_paymentGridModels.ToList(),_employeeId);
_paymentGridModels.Clear();
_priceLineModels.Clear();
_salesTransactionService.Reset();
UpdateWindowTitle();
_totalPrice = 0;
_saleGridModels.Clear();
lblRecived.Content = string.Empty;
_selectedSaleGridIndex = -1;

View File

@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Pos.Models;
namespace Pos.Service
{
/// <summary>
/// Holder styr på den igangværende handel og dens beregninger.
/// Servicen har ingen afhængighed til WPF eller web.
/// </summary>
public class SalesTransactionService
{
private readonly List<PriceLineModel> _saleLines = new List<PriceLineModel>();
private readonly List<AmountGridModel> _payments = new List<AmountGridModel>();
public IReadOnlyList<PriceLineModel> SaleLines => _saleLines;
public IReadOnlyList<AmountGridModel> 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;
/// <summary>
/// Beregner totalen for en enkelt salgslinje.
/// Parsing og visning af resultatet er fortsat UI-ansvar.
/// </summary>
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);
}
/// <summary>
/// Registrerer en betaling. Hvis amount er null, betales restbeløbet.
/// </summary>
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));
}
}
}
}