2017-05-11 16:04:35 +02:00
|
|
|
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
2017-03-14 18:02:28 +01:00
|
|
|
|
using Ordering.Domain.Events;
|
2017-05-15 19:17:16 +02:00
|
|
|
|
using Ordering.Domain.Exceptions;
|
2017-01-25 17:10:08 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
2016-11-21 12:41:36 +01:00
|
|
|
|
{
|
|
|
|
|
public class Order
|
2017-02-26 20:32:34 -08:00
|
|
|
|
: Entity, IAggregateRoot
|
2016-11-21 12:41:36 +01:00
|
|
|
|
{
|
2017-02-02 13:34:44 -08:00
|
|
|
|
// DDD Patterns comment
|
|
|
|
|
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation
|
|
|
|
|
// aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
|
|
|
|
|
private DateTime _orderDate;
|
|
|
|
|
|
2017-11-07 12:45:05 -08:00
|
|
|
|
// Address is a Value Object pattern example persisted as EF Core 2.0 owned entity
|
2017-02-08 19:26:05 +01:00
|
|
|
|
public Address Address { get; private set; }
|
2016-11-22 18:40:47 +01:00
|
|
|
|
|
2017-03-14 18:02:28 +01:00
|
|
|
|
private int? _buyerId;
|
2016-11-21 12:41:36 +01:00
|
|
|
|
|
2017-01-25 17:10:08 +01:00
|
|
|
|
public OrderStatus OrderStatus { get; private set; }
|
2017-02-02 13:34:44 -08:00
|
|
|
|
private int _orderStatusId;
|
2016-11-21 12:41:36 +01:00
|
|
|
|
|
2017-05-11 16:04:35 +02:00
|
|
|
|
private string _description;
|
2017-03-03 12:03:31 +01:00
|
|
|
|
|
2017-02-02 13:34:44 -08:00
|
|
|
|
// DDD Patterns comment
|
|
|
|
|
// Using a private collection field, better for DDD Aggregate's encapsulation
|
|
|
|
|
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
|
|
|
|
// but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour.
|
2017-02-02 17:30:15 -08:00
|
|
|
|
private readonly List<OrderItem> _orderItems;
|
2017-05-16 18:40:34 +02:00
|
|
|
|
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
|
2016-11-22 18:40:47 +01:00
|
|
|
|
|
2017-03-14 18:02:28 +01:00
|
|
|
|
private int? _paymentMethodId;
|
2016-11-22 18:40:47 +01:00
|
|
|
|
|
2017-05-11 13:44:38 +02:00
|
|
|
|
protected Order() { _orderItems = new List<OrderItem>(); }
|
2016-11-24 14:59:25 +01:00
|
|
|
|
|
2017-05-11 18:34:07 +02:00
|
|
|
|
public Order(string userId, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
|
2017-03-14 18:02:28 +01:00
|
|
|
|
string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null)
|
2016-11-24 14:59:25 +01:00
|
|
|
|
{
|
2017-02-08 19:26:05 +01:00
|
|
|
|
_orderItems = new List<OrderItem>();
|
2017-01-25 17:10:08 +01:00
|
|
|
|
_buyerId = buyerId;
|
2017-01-27 11:38:23 +01:00
|
|
|
|
_paymentMethodId = paymentMethodId;
|
2017-05-20 12:35:16 -07:00
|
|
|
|
_orderStatusId = OrderStatus.Submitted.Id;
|
2017-01-25 17:10:08 +01:00
|
|
|
|
_orderDate = DateTime.UtcNow;
|
2017-02-08 19:26:05 +01:00
|
|
|
|
Address = address;
|
2017-03-16 18:52:02 -07:00
|
|
|
|
|
2017-03-17 18:36:34 -07:00
|
|
|
|
// Add the OrderStarterDomainEvent to the domain events collection
|
2017-03-16 18:52:02 -07:00
|
|
|
|
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
|
2017-05-11 18:34:07 +02:00
|
|
|
|
AddOrderStartedDomainEvent(userId, cardTypeId, cardNumber,
|
2017-03-17 18:36:34 -07:00
|
|
|
|
cardSecurityNumber, cardHolderName, cardExpiration);
|
2016-11-24 14:59:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-02 13:34:44 -08:00
|
|
|
|
// DDD Patterns comment
|
|
|
|
|
// This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order,
|
|
|
|
|
// so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot
|
|
|
|
|
// in order to maintain consistency between the whole Aggregate.
|
2017-01-30 15:46:43 +01:00
|
|
|
|
public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
|
2016-11-24 14:59:25 +01:00
|
|
|
|
{
|
2017-01-25 17:10:08 +01:00
|
|
|
|
var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId)
|
|
|
|
|
.SingleOrDefault();
|
|
|
|
|
|
|
|
|
|
if (existingOrderForProduct != null)
|
2016-11-24 14:59:25 +01:00
|
|
|
|
{
|
2017-01-25 17:10:08 +01:00
|
|
|
|
//if previous line exist modify it with higher discount and units..
|
|
|
|
|
|
|
|
|
|
if (discount > existingOrderForProduct.GetCurrentDiscount())
|
|
|
|
|
{
|
|
|
|
|
existingOrderForProduct.SetNewDiscount(discount);
|
|
|
|
|
}
|
2017-08-22 12:04:40 +02:00
|
|
|
|
|
|
|
|
|
existingOrderForProduct.AddUnits(units);
|
2016-11-24 14:59:25 +01:00
|
|
|
|
}
|
2017-01-25 17:10:08 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//add validated new order item
|
2016-11-24 14:59:25 +01:00
|
|
|
|
|
2017-01-30 15:46:43 +01:00
|
|
|
|
var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units);
|
2017-01-25 17:10:08 +01:00
|
|
|
|
_orderItems.Add(orderItem);
|
|
|
|
|
}
|
2016-12-14 18:23:57 +01:00
|
|
|
|
}
|
2017-03-14 18:02:28 +01:00
|
|
|
|
|
|
|
|
|
public void SetPaymentId(int id)
|
|
|
|
|
{
|
|
|
|
|
_paymentMethodId = id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetBuyerId(int id)
|
|
|
|
|
{
|
|
|
|
|
_buyerId = id;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-15 19:17:16 +02:00
|
|
|
|
public void SetAwaitingValidationStatus()
|
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
if (_orderStatusId == OrderStatus.Cancelled.Id ||
|
2017-05-20 12:35:16 -07:00
|
|
|
|
_orderStatusId != OrderStatus.Submitted.Id)
|
2017-05-15 19:17:16 +02:00
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
StatusChangeException(OrderStatus.AwaitingValidation);
|
2017-05-15 19:17:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-16 15:11:03 +02:00
|
|
|
|
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
|
2017-05-15 19:17:16 +02:00
|
|
|
|
|
2017-05-16 15:11:03 +02:00
|
|
|
|
_orderStatusId = OrderStatus.AwaitingValidation.Id;
|
2017-05-09 13:58:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-18 11:42:22 +02:00
|
|
|
|
public void SetStockConfirmedStatus()
|
2017-05-10 19:48:36 +02:00
|
|
|
|
{
|
2017-05-15 19:17:16 +02:00
|
|
|
|
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
|
2017-05-10 19:48:36 +02:00
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
StatusChangeException(OrderStatus.StockConfirmed);
|
2017-05-10 19:48:36 +02:00
|
|
|
|
}
|
2017-05-11 18:39:06 +02:00
|
|
|
|
|
2017-05-18 11:42:22 +02:00
|
|
|
|
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
|
2017-05-11 16:04:35 +02:00
|
|
|
|
|
2017-05-18 11:42:22 +02:00
|
|
|
|
_orderStatusId = OrderStatus.StockConfirmed.Id;
|
|
|
|
|
_description = "All the items were confirmed with available stock.";
|
2017-05-15 19:17:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetPaidStatus()
|
|
|
|
|
{
|
|
|
|
|
if (_orderStatusId != OrderStatus.StockConfirmed.Id)
|
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
StatusChangeException(OrderStatus.Paid);
|
2017-05-15 19:17:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-16 15:11:03 +02:00
|
|
|
|
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
|
|
|
|
|
|
2017-05-15 19:17:16 +02:00
|
|
|
|
_orderStatusId = OrderStatus.Paid.Id;
|
|
|
|
|
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetShippedStatus()
|
|
|
|
|
{
|
|
|
|
|
if (_orderStatusId != OrderStatus.Paid.Id)
|
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
StatusChangeException(OrderStatus.Shipped);
|
2017-05-15 19:17:16 +02:00
|
|
|
|
}
|
2017-05-11 18:39:06 +02:00
|
|
|
|
|
2017-05-15 19:17:16 +02:00
|
|
|
|
_orderStatusId = OrderStatus.Shipped.Id;
|
2017-05-18 11:42:22 +02:00
|
|
|
|
_description = "The order was shipped.";
|
2017-05-10 19:48:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-16 10:19:35 +02:00
|
|
|
|
public void SetCancelledStatus()
|
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
if (_orderStatusId == OrderStatus.Paid.Id ||
|
|
|
|
|
_orderStatusId == OrderStatus.Shipped.Id)
|
2017-05-16 11:11:09 +02:00
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
StatusChangeException(OrderStatus.Cancelled);
|
2017-05-16 11:11:09 +02:00
|
|
|
|
}
|
2017-05-18 11:42:22 +02:00
|
|
|
|
|
|
|
|
|
_orderStatusId = OrderStatus.Cancelled.Id;
|
|
|
|
|
_description = $"The order was cancelled.";
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-18 12:03:17 +02:00
|
|
|
|
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
|
2017-05-18 11:42:22 +02:00
|
|
|
|
{
|
|
|
|
|
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
|
2017-05-16 11:11:09 +02:00
|
|
|
|
{
|
2017-05-18 11:42:22 +02:00
|
|
|
|
StatusChangeException(OrderStatus.Cancelled);
|
2017-05-16 10:19:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-16 09:58:06 +02:00
|
|
|
|
_orderStatusId = OrderStatus.Cancelled.Id;
|
|
|
|
|
|
2017-05-18 12:03:17 +02:00
|
|
|
|
var itemsStockRejectedProductNames = OrderItems
|
|
|
|
|
.Where(c => orderStockRejectedItems.Contains(c.ProductId))
|
2017-05-18 11:42:22 +02:00
|
|
|
|
.Select(c => c.GetOrderItemProductName());
|
|
|
|
|
|
2017-05-18 12:03:17 +02:00
|
|
|
|
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
|
|
|
|
|
_description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
|
2017-05-18 11:42:22 +02:00
|
|
|
|
}
|
2017-05-15 19:17:16 +02:00
|
|
|
|
|
2017-05-11 18:34:07 +02:00
|
|
|
|
private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber,
|
2017-03-14 18:02:28 +01:00
|
|
|
|
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
|
|
|
|
|
{
|
2017-11-07 16:53:41 -08:00
|
|
|
|
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, cardTypeId,
|
|
|
|
|
cardNumber, cardSecurityNumber,
|
|
|
|
|
cardHolderName, cardExpiration);
|
2017-03-14 18:02:28 +01:00
|
|
|
|
|
2017-03-17 18:36:34 -07:00
|
|
|
|
this.AddDomainEvent(orderStartedDomainEvent);
|
2017-03-14 18:02:28 +01:00
|
|
|
|
}
|
2017-05-15 19:17:16 +02:00
|
|
|
|
|
2017-05-18 11:42:22 +02:00
|
|
|
|
private void StatusChangeException(OrderStatus orderStatusToChange)
|
2017-05-15 19:17:16 +02:00
|
|
|
|
{
|
2017-11-07 16:53:41 -08:00
|
|
|
|
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
|
2017-05-15 19:17:16 +02:00
|
|
|
|
}
|
2017-08-22 12:04:40 +02:00
|
|
|
|
|
|
|
|
|
public decimal GetTotal()
|
|
|
|
|
{
|
|
|
|
|
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
|
|
|
|
|
}
|
2016-11-21 12:41:36 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-11 16:04:35 +02:00
|
|
|
|
|