moved logic out of SalesWindows.xaml.cs to SaleTransactionServce
This commit is contained in:
@@ -17,6 +17,16 @@ namespace Database
|
||||
public DbSet<SaleLineEntity> SalesLines { get; set; }
|
||||
public DbSet<PaymentEntity> Payment { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<SaleLineEntity>()
|
||||
.Property(saleLine => saleLine.Pieces)
|
||||
.HasConversion<short>()
|
||||
.HasColumnType("smallint");
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(
|
||||
DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
171
PointOfSale/Pos.Ui/Pos/Service/SalesTransactionService.cs
Normal file
171
PointOfSale/Pos.Ui/Pos/Service/SalesTransactionService.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user