/// <summary> /// Change composer, based on "greedy algorithm". /// </summary> /// <param name="walletRepository">The source wallet of the change</param> /// <param name="changeValue">The sum of change to compose.</param> /// <returns>The change wallet with composed change.</returns> public static PaymentWalletRepository ChangeComposer(IWalletRepository walletRepository, Money changeValue) { // Collect change PaymentWalletRepository changeCollected = new PaymentWalletRepository(); // Collect the change for big to small value foreach (var position in walletRepository.WalletList.OrderByDescending(w => w.Item1.Nominal).Where(p => p.Item2 > 0)) { (INotionValue money, int count) = position; while (changeValue >= money.MoneyValue && count > 0) { count--; changeValue -= money.MoneyValue; changeCollected.AddToWallet(money, 1); walletRepository.RemoveFromWallet(money, 1); } if (changeValue == Money.Zero) { break; } } //Cannot get the change if (changeValue != Money.Zero) { // return all money back foreach (var mc in changeCollected.WalletList) { walletRepository.AddToWallet(mc.Item1, mc.Item2); } return(null); } return(changeCollected); }
/// <summary> /// Processing payment events from the coin receiver /// </summary> /// <param name="cmd">The event to process.</param> /// <param name="payment">The coin received or null</param> void PaymentAction(PaymentCmdEvent cmd, INotionValue payment) { if (VendingMachineState != VendingMachineState.Payment) { return; } if (cmd == PaymentCmdEvent.Cancel) { // Cancellation of the order or/and payment CancelOrder(); } else if (cmd == PaymentCmdEvent.Payment) { _paymentWalletRepository.AddToWallet(payment, 1); Money total = _orderedProducts.Aggregate(Money.Zero, (m, p) => m + p.Price); Money paid = _paymentWalletRepository.WalletList.Aggregate(Money.Zero, (m, p) => m + new Money(0, (int)p.Item1.Nominal * p.Item2)); string msg = String.Format(_vendingMessageRepository.GetMessage(MessageCode.BalancePayment), total, paid); _displayPanel.DisplayMessage(msg); if (paid >= total) { // give change or cancel if (paid > total) { Money change = paid - total; PaymentWalletRepository collectedChange = ChangeComposer(_walletRepository, change); if (collectedChange == null) { DisplayMessageByCode(MessageCode.RunOutOfChange); CancelOrder(); return; } foreach (var pm in collectedChange.WalletList) { msg = String.Format(_vendingMessageRepository.GetMessage(MessageCode.GivenChange), pm.Item1.MoneyValue, pm.Item2); _displayPanel.DisplayMessage(msg); } } // complete purchase foreach (var p in _orderedProducts) { msg = String.Format(_vendingMessageRepository.GetMessage(MessageCode.CollectYourPurchase), p); _saleRecord.Sold(p); _displayPanel.DisplayMessage(msg); } _orderedProducts.Clear(); foreach (var pm in _paymentWalletRepository.WalletList) { _walletRepository.AddToWallet(pm.Item1, pm.Item2); } _paymentWalletRepository.ClearMoney(); // switch back to selling TryToStartSelling(); _coinReceiver.Off(); } } }