Added Order API
This commit is contained in:
parent
2bca44ebe5
commit
344ecb8762
56
API/Controllers/OrdersController.cs
Normal file
56
API/Controllers/OrdersController.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using API.Dtos;
|
||||||
|
using API.Errors;
|
||||||
|
using API.Extensions;
|
||||||
|
using AutoMapper;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
using Core.Interfaces;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace API.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class OrdersController : BaseApiController
|
||||||
|
{
|
||||||
|
private readonly IOrderService _orderService;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
public OrdersController(IOrderService orderService, IMapper mapper)
|
||||||
|
{
|
||||||
|
_mapper = mapper;
|
||||||
|
_orderService = orderService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<Order>> CreateOrder(OrderDto orderDto){
|
||||||
|
var email = HttpContext.User.RetrieveEmailFromPrincipal();
|
||||||
|
var address = _mapper.Map<AddressDto, Address>(orderDto.ShipToAddress);
|
||||||
|
var order = await _orderService.CreateOrderAsync(email, orderDto.DeliveryMethodId, orderDto.BasketId, address);
|
||||||
|
if(order == null) return BadRequest(new ApiResponse(400, "Problem creating order"));
|
||||||
|
return Ok(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<IReadOnlyList<OrderDto>>> GetOrdersForUser()
|
||||||
|
{
|
||||||
|
var email = HttpContext.User.RetrieveEmailFromPrincipal();
|
||||||
|
var orders = await _orderService.GetOrdersForUserAsync(email);
|
||||||
|
return Ok(_mapper.Map<IReadOnlyList<Order>, IReadOnlyList<OrderToReturnDto>>(orders));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<OrderToReturnDto>> GetOrderByIdForUser(int id)
|
||||||
|
{
|
||||||
|
var email = HttpContext.User.RetrieveEmailFromPrincipal();
|
||||||
|
var order = await _orderService.GetOrderByIdAsync(id, email);
|
||||||
|
if(order == null) return NotFound(new ApiResponse(404));
|
||||||
|
return _mapper.Map<Order, OrderToReturnDto>(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("deliveryMethods")]
|
||||||
|
public async Task<ActionResult<IReadOnlyList<DeliveryMethod>>> GetDeliveryMethod()
|
||||||
|
{
|
||||||
|
return Ok(await _orderService.GetDeliveryMethodsAsync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
API/Dtos/OrderDto.cs
Normal file
14
API/Dtos/OrderDto.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace API.Dtos
|
||||||
|
{
|
||||||
|
public class OrderDto
|
||||||
|
{
|
||||||
|
public string BasketId { get; set; }
|
||||||
|
public int DeliveryMethodId { get; set; }
|
||||||
|
public AddressDto ShipToAddress { get; set; }
|
||||||
|
}
|
||||||
|
}
|
16
API/Dtos/OrderItemDto.cs
Normal file
16
API/Dtos/OrderItemDto.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace API.Dtos
|
||||||
|
{
|
||||||
|
public class OrderItemDto
|
||||||
|
{
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
public string ProductName { get; set; }
|
||||||
|
public string PictureUrl { get; set; }
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
22
API/Dtos/OrderToReturnDto.cs
Normal file
22
API/Dtos/OrderToReturnDto.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
|
||||||
|
namespace API.Dtos
|
||||||
|
{
|
||||||
|
public class OrderToReturnDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string BuyerEmail { get; set; }
|
||||||
|
public DateTimeOffset OrderDate { get; set; }
|
||||||
|
public Address ShipToAddress { get; set; }
|
||||||
|
public string DeliveryMethod { get; set; }
|
||||||
|
public decimal ShippingPrice { get; set; }
|
||||||
|
public IReadOnlyList<OrderItemDto> OrderItems { get; set; }
|
||||||
|
public decimal Subtotal { get; set; }
|
||||||
|
public decimal Total { get; set; }
|
||||||
|
public string Status { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,8 @@ namespace API.Extensions
|
|||||||
services.AddScoped<ITokenService, TokenService>();
|
services.AddScoped<ITokenService, TokenService>();
|
||||||
services.AddScoped<iProductRepository, ProductRepository>();
|
services.AddScoped<iProductRepository, ProductRepository>();
|
||||||
services.AddScoped<IBasketRepository, BasketRepository>();
|
services.AddScoped<IBasketRepository, BasketRepository>();
|
||||||
|
services.AddScoped<IOrderService, OrderService>();
|
||||||
|
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||||
services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>)));
|
services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>)));
|
||||||
services.Configure<ApiBehaviorOptions>(options =>
|
services.Configure<ApiBehaviorOptions>(options =>
|
||||||
options.InvalidModelStateResponseFactory = actionContext =>
|
options.InvalidModelStateResponseFactory = actionContext =>
|
||||||
|
15
API/Extensions/ClaimsPrincipalExtentensions.cs
Normal file
15
API/Extensions/ClaimsPrincipalExtentensions.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace API.Extensions
|
||||||
|
{
|
||||||
|
public static class ClaimsPrincipalExtentensions
|
||||||
|
{
|
||||||
|
public static string RetrieveEmailFromPrincipal(this ClaimsPrincipal user){
|
||||||
|
return user.FindFirstValue(ClaimTypes.Email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ using API.Dtos;
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Core.Entities;
|
using Core.Entities;
|
||||||
using Core.Entities.Identity;
|
using Core.Entities.Identity;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
|
||||||
namespace API.Helpers
|
namespace API.Helpers
|
||||||
{
|
{
|
||||||
@ -10,13 +11,22 @@ namespace API.Helpers
|
|||||||
public MappingProfiles()
|
public MappingProfiles()
|
||||||
{
|
{
|
||||||
CreateMap<Product, ProductToReturnDto>()
|
CreateMap<Product, ProductToReturnDto>()
|
||||||
.ForMember(d => d.ProductBrand, o => o.MapFrom(s => s.ProductBrand.Name))
|
.ForMember(d => d.ProductBrand, o => o.MapFrom(s => s.ProductBrand.Name))
|
||||||
.ForMember(d => d.ProductType, o => o.MapFrom(s => s.ProductType.Name))
|
.ForMember(d => d.ProductType, o => o.MapFrom(s => s.ProductType.Name))
|
||||||
.ForMember(d => d.PictureUrl, o => o.MapFrom<ProductUrlResolver>());
|
.ForMember(d => d.PictureUrl, o => o.MapFrom<ProductUrlResolver>());
|
||||||
|
|
||||||
CreateMap<Address, AddressDto>().ReverseMap();
|
CreateMap<Core.Entities.Identity.Address, AddressDto>().ReverseMap();
|
||||||
CreateMap<CustomerBasketDto, CustomerBasket>();
|
CreateMap<CustomerBasketDto, CustomerBasket>();
|
||||||
CreateMap<BasketItemDto, BasketItem>();
|
CreateMap<BasketItemDto, BasketItem>();
|
||||||
|
CreateMap<AddressDto, Core.Entities.OrderAggregate.Address>();
|
||||||
|
CreateMap<Order, OrderToReturnDto>()
|
||||||
|
.ForMember(d => d.DeliveryMethod, o => o.MapFrom(s => s.DeliveryMethod.ShortName))
|
||||||
|
.ForMember(d => d.ShippingPrice, o => o.MapFrom(s => s.DeliveryMethod.Price));
|
||||||
|
CreateMap<OrderItem, OrderItemDto>()
|
||||||
|
.ForMember(d => d.ProductId, o => o.MapFrom(s => s.ItemOrdered.ProductItemId))
|
||||||
|
.ForMember(d => d.ProductName, o => o.MapFrom(s => s.ItemOrdered.ProductName))
|
||||||
|
.ForMember(d => d.PictureUrl, o => o.MapFrom(s => s.ItemOrdered.PictureUrl))
|
||||||
|
.ForMember(d => d.PictureUrl, o => o.MapFrom<OrderItemUrlResolver>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
26
API/Helpers/OrderItemUrlResolver.cs
Normal file
26
API/Helpers/OrderItemUrlResolver.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using API.Dtos;
|
||||||
|
using AutoMapper;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
|
||||||
|
namespace API.Helpers
|
||||||
|
{
|
||||||
|
public class OrderItemUrlResolver : IValueResolver<OrderItem, OrderItemDto, string>
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
|
||||||
|
public OrderItemUrlResolver(IConfiguration config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Resolve(OrderItem source, OrderItemDto destination, string destMember, ResolutionContext context)
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrEmpty(source.ItemOrdered.PictureUrl))
|
||||||
|
{
|
||||||
|
return _config["ApiUrl"] + source.ItemOrdered.PictureUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
Core/Entities/OrderAggregate/Address.cs
Normal file
32
Core/Entities/OrderAggregate/Address.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Core.Entities.OrderAggregate
|
||||||
|
{
|
||||||
|
public class Address
|
||||||
|
{
|
||||||
|
public Address()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address(string firstName, string lastName, string street, string city, string state, string zipCode)
|
||||||
|
{
|
||||||
|
FirstName = firstName;
|
||||||
|
LastName = lastName;
|
||||||
|
Street = street;
|
||||||
|
City = city;
|
||||||
|
State = state;
|
||||||
|
ZipCode = zipCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
public string Street { get; set; }
|
||||||
|
public string City { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
public string ZipCode { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
Core/Entities/OrderAggregate/DeliveryMethod.cs
Normal file
15
Core/Entities/OrderAggregate/DeliveryMethod.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Core.Entities.OrderAggregate
|
||||||
|
{
|
||||||
|
public class DeliveryMethod : BaseEntity
|
||||||
|
{
|
||||||
|
public string ShortName { get; set; }
|
||||||
|
public string DeliveryTime { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
Core/Entities/OrderAggregate/Order.cs
Normal file
31
Core/Entities/OrderAggregate/Order.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
namespace Core.Entities.OrderAggregate
|
||||||
|
{
|
||||||
|
public class Order : BaseEntity
|
||||||
|
{
|
||||||
|
public Order()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order(IReadOnlyList<OrderItem> orderItems, string buyerEmail, Address shipToAddress, DeliveryMethod deliveryMethod, decimal subtotal)
|
||||||
|
{
|
||||||
|
BuyerEmail = buyerEmail;
|
||||||
|
ShipToAddress = shipToAddress;
|
||||||
|
DeliveryMethod = deliveryMethod;
|
||||||
|
OrderItems = orderItems;
|
||||||
|
Subtotal = subtotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BuyerEmail { get; set; }
|
||||||
|
public DateTimeOffset OrderDate { get; set; } = DateTimeOffset.Now;
|
||||||
|
public Address ShipToAddress { get; set; }
|
||||||
|
public DeliveryMethod DeliveryMethod { get; set; }
|
||||||
|
public IReadOnlyList<OrderItem> OrderItems { get; set; }
|
||||||
|
public decimal Subtotal { get; set; }
|
||||||
|
public OrderStatus Status { get; set; } = OrderStatus.Pending;
|
||||||
|
public string PaymentIntentId { get; set; }
|
||||||
|
|
||||||
|
public decimal GetTotal(){
|
||||||
|
return Subtotal + DeliveryMethod.Price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
Core/Entities/OrderAggregate/OrderItem.cs
Normal file
25
Core/Entities/OrderAggregate/OrderItem.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Core.Entities.OrderAggregate
|
||||||
|
{
|
||||||
|
public class OrderItem : BaseEntity
|
||||||
|
{
|
||||||
|
public OrderItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderItem(ProductItemOrdered itemOrdered, decimal price, int quantity)
|
||||||
|
{
|
||||||
|
ItemOrdered = itemOrdered;
|
||||||
|
Price = price;
|
||||||
|
Quantity = quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProductItemOrdered ItemOrdered { get; set; }
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
18
Core/Entities/OrderAggregate/OrderStatus.cs
Normal file
18
Core/Entities/OrderAggregate/OrderStatus.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Core.Entities.OrderAggregate
|
||||||
|
{
|
||||||
|
public enum OrderStatus
|
||||||
|
{
|
||||||
|
[EnumMember(Value = "Pending")]
|
||||||
|
Pending,
|
||||||
|
[EnumMember(Value = "Payment Received")]
|
||||||
|
PaymentReceived,
|
||||||
|
[EnumMember(Value = "Payment Failed")]
|
||||||
|
PaymentFailed
|
||||||
|
}
|
||||||
|
}
|
25
Core/Entities/OrderAggregate/ProductItemOrdered.cs
Normal file
25
Core/Entities/OrderAggregate/ProductItemOrdered.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Core.Entities.OrderAggregate
|
||||||
|
{
|
||||||
|
public class ProductItemOrdered
|
||||||
|
{
|
||||||
|
public ProductItemOrdered()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProductItemOrdered(int productItemId, string productName, string pictureUrl)
|
||||||
|
{
|
||||||
|
ProductItemId = productItemId;
|
||||||
|
ProductName = productName;
|
||||||
|
PictureUrl = pictureUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ProductItemId { get; set; }
|
||||||
|
public string ProductName { get; set; }
|
||||||
|
public string PictureUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -10,5 +10,9 @@ namespace Core.Interfaces
|
|||||||
Task<T> GetEntityWithSpec(ISpecification<T> spec);
|
Task<T> GetEntityWithSpec(ISpecification<T> spec);
|
||||||
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
|
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
|
||||||
Task<int> CountAsync(ISpecification<T> spec);
|
Task<int> CountAsync(ISpecification<T> spec);
|
||||||
|
|
||||||
|
void Add(T entity);
|
||||||
|
void Update(T entity);
|
||||||
|
void Delete(T entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
12
Core/Interfaces/IOrderService.cs
Normal file
12
Core/Interfaces/IOrderService.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
|
||||||
|
namespace Core.Interfaces
|
||||||
|
{
|
||||||
|
public interface IOrderService
|
||||||
|
{
|
||||||
|
Task<Order> CreateOrderAsync(string buyerEmail, int deliverMethod, string basketId, Address ShippingAddress);
|
||||||
|
Task<IReadOnlyList<Order>> GetOrdersForUserAsync(string buyerEmail);
|
||||||
|
Task<Order> GetOrderByIdAsync(int id, string buyerEmail);
|
||||||
|
Task<IReadOnlyList<DeliveryMethod>> GetDeliveryMethodsAsync();
|
||||||
|
}
|
||||||
|
}
|
10
Core/Interfaces/IUnitOfWork.cs
Normal file
10
Core/Interfaces/IUnitOfWork.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
|
||||||
|
namespace Core.Interfaces
|
||||||
|
{
|
||||||
|
public interface IUnitOfWork : IDisposable
|
||||||
|
{
|
||||||
|
IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity;
|
||||||
|
Task<int> Complete();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
|
||||||
|
namespace Core.Specifications
|
||||||
|
{
|
||||||
|
public class OrdersWithItemsAndOrderingSpecification : BaseSpecification<Order>
|
||||||
|
{
|
||||||
|
public OrdersWithItemsAndOrderingSpecification(string email) : base(o => o.BuyerEmail == email)
|
||||||
|
{
|
||||||
|
AddInclude(o => o.OrderItems);
|
||||||
|
AddInclude(o => o.DeliveryMethod);
|
||||||
|
AddOrdeByDescending(o => o.OrderDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrdersWithItemsAndOrderingSpecification(int id, string email) : base(o => o.Id == id && o.BuyerEmail == email)
|
||||||
|
{
|
||||||
|
AddInclude(o => o.OrderItems);
|
||||||
|
AddInclude(o => o.DeliveryMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Infrastructure/Data/Config/DeliveryMethodConfiguration.cs
Normal file
15
Infrastructure/Data/Config/DeliveryMethodConfiguration.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Infrastructure.Data.Config
|
||||||
|
{
|
||||||
|
public class DeliveryMethodConfiguration : IEntityTypeConfiguration<DeliveryMethod>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<DeliveryMethod> builder)
|
||||||
|
{
|
||||||
|
builder.Property(d => d.Price)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
Infrastructure/Data/Config/OrderConfiguration.cs
Normal file
22
Infrastructure/Data/Config/OrderConfiguration.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Infrastructure.Data.Config
|
||||||
|
{
|
||||||
|
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Order> builder)
|
||||||
|
{
|
||||||
|
builder.OwnsOne(o => o.ShipToAddress, a => {
|
||||||
|
a.WithOwner();
|
||||||
|
});
|
||||||
|
builder.Property(s => s.Status)
|
||||||
|
.HasConversion(
|
||||||
|
o => o.ToString(),
|
||||||
|
o => (OrderStatus) Enum.Parse(typeof(OrderStatus), o)
|
||||||
|
);
|
||||||
|
builder.HasMany(o => o.OrderItems).WithOne().OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Infrastructure/Data/Config/OrderItemConfiguration.cs
Normal file
20
Infrastructure/Data/Config/OrderItemConfiguration.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Infrastructure.Data.Config
|
||||||
|
{
|
||||||
|
public class OrderItemConfiguration : IEntityTypeConfiguration<OrderItem>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<OrderItem> builder)
|
||||||
|
{
|
||||||
|
builder.OwnsOne(i => i.ItemOrdered, io => {io.WithOwner();});
|
||||||
|
builder.Property(i => i.Price)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,5 +41,21 @@ namespace Infrastructure.Data
|
|||||||
{
|
{
|
||||||
return SpecificationEvaluator<T>.GetQuery(_context.Set<T>().AsQueryable(), spec);
|
return SpecificationEvaluator<T>.GetQuery(_context.Set<T>().AsQueryable(), spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Add(T entity)
|
||||||
|
{
|
||||||
|
_context.Set<T>().Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(T entity)
|
||||||
|
{
|
||||||
|
_context.Set<T>().Attach(entity);
|
||||||
|
_context.Entry(entity).State = EntityState.Modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(T entity)
|
||||||
|
{
|
||||||
|
_context.Set<T>().Remove(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
265
Infrastructure/Data/Migrations/20220524182205_OrderEntityAdded.Designer.cs
generated
Normal file
265
Infrastructure/Data/Migrations/20220524182205_OrderEntityAdded.Designer.cs
generated
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Infrastructure.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Infrastructure.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(StoreContext))]
|
||||||
|
[Migration("20220524182205_OrderEntityAdded")]
|
||||||
|
partial class OrderEntityAdded
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "6.0.5");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.DeliveryMethod", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("DeliveryTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DeliveryMethod");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.Order", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("BuyerEmail")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("DeliveryMethodId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("OrderDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PaymentIntentId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Subtotal")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DeliveryMethodId");
|
||||||
|
|
||||||
|
b.ToTable("Orders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.OrderItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("OrderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<int>("Quantity")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OrderId");
|
||||||
|
|
||||||
|
b.ToTable("OrderItems");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Product", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PictureUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<int>("ProductBrandId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ProductTypeId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ProductBrandId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductTypeId");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.ProductBrand", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ProductBrands");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.ProductType", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ProductTypes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.Order", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.OrderAggregate.DeliveryMethod", "DeliveryMethod")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("DeliveryMethodId");
|
||||||
|
|
||||||
|
b.OwnsOne("Core.Entities.OrderAggregate.Address", "ShipToAddress", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("OrderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("City")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("FirstName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("LastName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("State")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("Street")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("ZipCode")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("OrderId");
|
||||||
|
|
||||||
|
b1.ToTable("Orders");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("OrderId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("DeliveryMethod");
|
||||||
|
|
||||||
|
b.Navigation("ShipToAddress");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.OrderItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.OrderAggregate.Order", null)
|
||||||
|
.WithMany("OrderItems")
|
||||||
|
.HasForeignKey("OrderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.OwnsOne("Core.Entities.OrderAggregate.ProductItemOrdered", "ItemOrdered", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("OrderItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("PictureUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<int>("ProductItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("ProductName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("OrderItemId");
|
||||||
|
|
||||||
|
b1.ToTable("OrderItems");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("OrderItemId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("ItemOrdered");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.Product", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.ProductBrand", "ProductBrand")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductBrandId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Core.Entities.ProductType", "ProductType")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProductTypeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("ProductBrand");
|
||||||
|
|
||||||
|
b.Navigation("ProductType");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.Order", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OrderItems");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Infrastructure.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class OrderEntityAdded : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "DeliveryMethod",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
ShortName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
DeliveryTime = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Description = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Price = table.Column<double>(type: "decimal(18,2)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_DeliveryMethod", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Orders",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
BuyerEmail = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
OrderDate = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
|
||||||
|
ShipToAddress_FirstName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ShipToAddress_LastName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ShipToAddress_Street = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ShipToAddress_City = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ShipToAddress_State = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ShipToAddress_ZipCode = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
DeliveryMethodId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
Subtotal = table.Column<double>(type: "REAL", nullable: false),
|
||||||
|
Status = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
PaymentIntentId = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Orders", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Orders_DeliveryMethod_DeliveryMethodId",
|
||||||
|
column: x => x.DeliveryMethodId,
|
||||||
|
principalTable: "DeliveryMethod",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OrderItems",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
ItemOrdered_ProductItemId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
ItemOrdered_ProductName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ItemOrdered_PictureUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Price = table.Column<double>(type: "decimal(18,2)", nullable: false),
|
||||||
|
Quantity = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
OrderId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OrderItems", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_OrderItems_Orders_OrderId",
|
||||||
|
column: x => x.OrderId,
|
||||||
|
principalTable: "Orders",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OrderItems_OrderId",
|
||||||
|
table: "OrderItems",
|
||||||
|
column: "OrderId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Orders_DeliveryMethodId",
|
||||||
|
table: "Orders",
|
||||||
|
column: "DeliveryMethodId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OrderItems");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Orders");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "DeliveryMethod");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Infrastructure.Data;
|
using Infrastructure.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
@ -14,7 +15,84 @@ namespace Infrastructure.Data.Migrations
|
|||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
|
modelBuilder.HasAnnotation("ProductVersion", "6.0.5");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.DeliveryMethod", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("DeliveryTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DeliveryMethod");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.Order", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("BuyerEmail")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("DeliveryMethodId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("OrderDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PaymentIntentId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Subtotal")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DeliveryMethodId");
|
||||||
|
|
||||||
|
b.ToTable("Orders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.OrderItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("OrderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<int>("Quantity")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OrderId");
|
||||||
|
|
||||||
|
b.ToTable("OrderItems");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Core.Entities.Product", b =>
|
modelBuilder.Entity("Core.Entities.Product", b =>
|
||||||
{
|
{
|
||||||
@ -36,7 +114,7 @@ namespace Infrastructure.Data.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<decimal>("Price")
|
b.Property<double>("Price")
|
||||||
.HasColumnType("decimal(18,2)");
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
b.Property<int>("ProductBrandId")
|
b.Property<int>("ProductBrandId")
|
||||||
@ -82,6 +160,80 @@ namespace Infrastructure.Data.Migrations
|
|||||||
b.ToTable("ProductTypes");
|
b.ToTable("ProductTypes");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.Order", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.OrderAggregate.DeliveryMethod", "DeliveryMethod")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("DeliveryMethodId");
|
||||||
|
|
||||||
|
b.OwnsOne("Core.Entities.OrderAggregate.Address", "ShipToAddress", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("OrderId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("City")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("FirstName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("LastName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("State")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("Street")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<string>("ZipCode")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("OrderId");
|
||||||
|
|
||||||
|
b1.ToTable("Orders");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("OrderId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("DeliveryMethod");
|
||||||
|
|
||||||
|
b.Navigation("ShipToAddress");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.OrderItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Core.Entities.OrderAggregate.Order", null)
|
||||||
|
.WithMany("OrderItems")
|
||||||
|
.HasForeignKey("OrderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.OwnsOne("Core.Entities.OrderAggregate.ProductItemOrdered", "ItemOrdered", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<int>("OrderItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("PictureUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.Property<int>("ProductItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b1.Property<string>("ProductName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b1.HasKey("OrderItemId");
|
||||||
|
|
||||||
|
b1.ToTable("OrderItems");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("OrderItemId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("ItemOrdered");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Core.Entities.Product", b =>
|
modelBuilder.Entity("Core.Entities.Product", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Core.Entities.ProductBrand", "ProductBrand")
|
b.HasOne("Core.Entities.ProductBrand", "ProductBrand")
|
||||||
@ -100,6 +252,11 @@ namespace Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
b.Navigation("ProductType");
|
b.Navigation("ProductType");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Core.Entities.OrderAggregate.Order", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OrderItems");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
Infrastructure/Data/SeedData/delivery.json
Normal file
30
Infrastructure/Data/SeedData/delivery.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"Id": 1,
|
||||||
|
"ShortName": "UPS1",
|
||||||
|
"Description": "Fastest delivery time",
|
||||||
|
"DeliveryTime": "1-2 Days",
|
||||||
|
"Price": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 2,
|
||||||
|
"ShortName": "UPS2",
|
||||||
|
"Description": "Get it within 5 days",
|
||||||
|
"DeliveryTime": "2-5 Days",
|
||||||
|
"Price": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 3,
|
||||||
|
"ShortName": "UPS3",
|
||||||
|
"Description": "Slower but cheap",
|
||||||
|
"DeliveryTime": "5-10 Days",
|
||||||
|
"Price": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": 4,
|
||||||
|
"ShortName": "FREE",
|
||||||
|
"Description": "Free! You get what you pay for",
|
||||||
|
"DeliveryTime": "1-2 Weeks",
|
||||||
|
"Price": 0
|
||||||
|
}
|
||||||
|
]
|
@ -1,6 +1,8 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Core.Entities;
|
using Core.Entities;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
namespace Infrastructure.Data
|
namespace Infrastructure.Data
|
||||||
{
|
{
|
||||||
@ -12,6 +14,9 @@ namespace Infrastructure.Data
|
|||||||
public DbSet<Product> Products { get; set; }
|
public DbSet<Product> Products { get; set; }
|
||||||
public DbSet<ProductBrand> ProductBrands { get; set; }
|
public DbSet<ProductBrand> ProductBrands { get; set; }
|
||||||
public DbSet<ProductType> ProductTypes { get; set; }
|
public DbSet<ProductType> ProductTypes { get; set; }
|
||||||
|
public DbSet<Order> Orders { get; set; }
|
||||||
|
public DbSet<OrderItem> OrderItems { get; set; }
|
||||||
|
public DbSet<DeliveryMethod> DeliveryMethod { get; set; }
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
@ -22,10 +27,16 @@ namespace Infrastructure.Data
|
|||||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
{
|
{
|
||||||
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(decimal));
|
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(decimal));
|
||||||
|
var dateTimeProperties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset));
|
||||||
foreach (var property in properties)
|
foreach (var property in properties)
|
||||||
{
|
{
|
||||||
modelBuilder.Entity(entityType.Name).Property(property.Name).HasConversion<double>();
|
modelBuilder.Entity(entityType.Name).Property(property.Name).HasConversion<double>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var property in dateTimeProperties)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity(entityType.Name).Property(property.Name).HasConversion(new DateTimeOffsetToBinaryConverter());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Core.Entities;
|
using Core.Entities;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
using Infrastructure.Data;
|
using Infrastructure.Data;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@ -46,6 +47,20 @@ namespace Infrastructure
|
|||||||
|
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!context.DeliveryMethod.Any())
|
||||||
|
{
|
||||||
|
var dmData = File.ReadAllText("../Infrastructure/Data/SeedData/delivery.json");
|
||||||
|
var methods = JsonSerializer.Deserialize<List<DeliveryMethod>>(dmData);
|
||||||
|
foreach (var item in methods)
|
||||||
|
{
|
||||||
|
context.DeliveryMethod.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
38
Infrastructure/Data/UnitOfWork.cs
Normal file
38
Infrastructure/Data/UnitOfWork.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using Core.Entities;
|
||||||
|
using Core.Interfaces;
|
||||||
|
|
||||||
|
namespace Infrastructure.Data
|
||||||
|
{
|
||||||
|
public class UnitOfWork : IUnitOfWork
|
||||||
|
{
|
||||||
|
private readonly StoreContext _context;
|
||||||
|
private Hashtable _repositories;
|
||||||
|
public UnitOfWork(StoreContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> Complete()
|
||||||
|
{
|
||||||
|
return await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_context.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity
|
||||||
|
{
|
||||||
|
if(_repositories == null) _repositories = new Hashtable();
|
||||||
|
var type = typeof(TEntity).Name;
|
||||||
|
if(!_repositories.ContainsKey(type)){
|
||||||
|
var repositoryType = typeof(GenericRepository<>);
|
||||||
|
var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), _context);
|
||||||
|
_repositories.Add(type, repositoryInstance);
|
||||||
|
}
|
||||||
|
return (IGenericRepository<TEntity>) _repositories[type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.5" />
|
||||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.17.0" />
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.17.0" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.5.61" />
|
<PackageReference Include="StackExchange.Redis" Version="2.5.61" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.17.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.17.0" />
|
||||||
|
57
Infrastructure/Services/OrderService.cs
Normal file
57
Infrastructure/Services/OrderService.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
using Core.Entities.OrderAggregate;
|
||||||
|
using Core.Interfaces;
|
||||||
|
using Core.Specifications;
|
||||||
|
|
||||||
|
namespace Infrastructure.Services
|
||||||
|
{
|
||||||
|
public class OrderService : IOrderService
|
||||||
|
{
|
||||||
|
private readonly IBasketRepository _basketRepo;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public OrderService(IBasketRepository basketRepo, IUnitOfWork unitOfWork)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_basketRepo = basketRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Order> CreateOrderAsync(string buyerEmail, int deliverMethodId, string basketId, Address shippingAddress)
|
||||||
|
{
|
||||||
|
var basket = await _basketRepo.GetBasketAsync(basketId);
|
||||||
|
var items = new List<OrderItem>();
|
||||||
|
foreach (var item in basket.Items)
|
||||||
|
{
|
||||||
|
var productItem = await _unitOfWork.Repository<Product>().GetByIdAsync(item.Id);
|
||||||
|
var itemOrdered = new ProductItemOrdered(productItem.Id, productItem.Name, productItem.PictureUrl);
|
||||||
|
var orderItem = new OrderItem(itemOrdered, productItem.Price, item.Quantity);
|
||||||
|
items.Add(orderItem);
|
||||||
|
}
|
||||||
|
var deliveryMethod = await _unitOfWork.Repository<DeliveryMethod>().GetByIdAsync(deliverMethodId);
|
||||||
|
var subtotal = items.Sum(item => item.Price * item.Quantity);
|
||||||
|
var order = new Order(items, buyerEmail, shippingAddress, deliveryMethod, subtotal);
|
||||||
|
_unitOfWork.Repository<Order>().Add(order);
|
||||||
|
var result = await _unitOfWork.Complete();
|
||||||
|
if(result <= 0) return null;
|
||||||
|
await _basketRepo.DeleteBasketAsysnc(basketId);
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<DeliveryMethod>> GetDeliveryMethodsAsync()
|
||||||
|
{
|
||||||
|
return await _unitOfWork.Repository<DeliveryMethod>().ListAllAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Order> GetOrderByIdAsync(int id, string buyerEmail)
|
||||||
|
{
|
||||||
|
var spec = new OrdersWithItemsAndOrderingSpecification(id, buyerEmail);
|
||||||
|
return await _unitOfWork.Repository<Order>().GetEntityWithSpec(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<Order>> GetOrdersForUserAsync(string buyerEmail)
|
||||||
|
{
|
||||||
|
var spec = new OrdersWithItemsAndOrderingSpecification(buyerEmail);
|
||||||
|
return await _unitOfWork.Repository<Order>().ListAsync(spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user