Commit 1ca24495 authored by gdlcf88's avatar gdlcf88

Complete creating order feature

parent 7983189a
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
......@@ -8,7 +8,15 @@ namespace EasyAbp.EShop.Orders.Authorization
{
public override void Define(IPermissionDefinitionContext context)
{
//var moduleGroup = context.AddGroup(OrdersPermissions.GroupName, L("Permission:Orders"));
var moduleGroup = context.AddGroup(OrdersPermissions.GroupName, L("Permission:Orders"));
var order = moduleGroup.AddPermission(OrdersPermissions.Orders.Default, L("Permission:Product"));
order.AddChild(OrdersPermissions.Orders.Manage, L("Permission:Manage"));
order.AddChild(OrdersPermissions.Orders.CrossStore, L("Permission:CrossStore"));
order.AddChild(OrdersPermissions.Orders.Create, L("Permission:Create"));
order.AddChild(OrdersPermissions.Orders.ConfirmReceipt, L("Permission:ConfirmReceipt"));
order.AddChild(OrdersPermissions.Orders.RequestCancellation, L("Permission:RequestCancellation"));
order.AddChild(OrdersPermissions.Orders.Cancel, L("Permission:Cancel"));
}
private static LocalizableString L(string name)
......
......@@ -6,6 +6,17 @@ namespace EasyAbp.EShop.Orders.Authorization
{
public const string GroupName = "EasyAbp.EShop.Orders";
public class Orders
{
public const string Default = GroupName + ".Order";
public const string Manage = Default + ".Manage";
public const string CrossStore = Default + ".CrossStore";
public const string Create = Default + ".Create";
public const string ConfirmReceipt = Default + ".ConfirmReceipt";
public const string RequestCancellation = Default + ".RequestCancellation";
public const string Cancel = Default + ".Cancel";
}
public static string[] GetAll()
{
return ReflectionHelper.GetPublicConstantsRecursively(typeof(OrdersPermissions));
......
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
public class CreateOrderDto : IValidatableObject
{
[DisplayName("OrderStoreId")]
public Guid StoreId { get; set; }
[DisplayName("OrderCustomerRemark")]
public string CustomerRemark { get; set; }
[DisplayName("OrderLine")]
public List<CreateOrderLineDto> OrderLines { get; set; }
[DisplayName("OrderExtraProperties")]
public Dictionary<string, object> ExtraProperties { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (OrderLines.Select(orderLine => orderLine.Quantity).Sum() <= 0)
{
yield return new ValidationResult(
"Total quantity should be greater than 0.",
new[] { "OrderLines" }
);
}
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
public class CreateOrderLineDto
{
[DisplayName("OrderLineProductId")]
public Guid ProductId { get; set; }
[DisplayName("OrderLineProductSkuId")]
public Guid ProductSkuId { get; set; }
[DisplayName("OrderLineQuantity")]
public int Quantity { get; set; }
}
}
\ No newline at end of file
using System;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
public class GetOrderListDto : PagedAndSortedResultRequestDto
{
public Guid? StoreId { get; set; }
public Guid? CustomerUserId { get; set; }
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
public class OrderDto : ExtensibleFullAuditedEntityDto<Guid>
{
public Guid StoreId { get; set; }
public Guid CustomerUserId { get; set; }
public OrderStatus OrderStatus { get; set; }
public string Currency { get; set; }
public decimal ProductTotalPrice { get; set; }
public decimal TotalDiscount { get; set; }
public decimal TotalPrice { get; set; }
public decimal RefundedAmount { get; set; }
public string CustomerRemark { get; set; }
public string StaffRemark { get; set; }
public DateTime? PaidTime { get; set; }
public DateTime? CompletionTime { get; set; }
public DateTime? CancelledTime { get; set; }
public DateTime? ReducedInventoryAfterPlacingTime { get; set; }
public DateTime? ReducedInventoryAfterPaymentTime { get; set; }
public List<OrderLineDto> OrderLines { get; set; }
}
}
\ No newline at end of file
using System;
using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Orders.Orders.Dtos
{
public class OrderLineDto : FullAuditedEntityDto<Guid>
{
public Guid ProductId { get; set; }
public Guid ProductSkuId { get; set; }
public DateTime ProductModificationTime { get; set; }
public DateTime ProductDetailModificationTime { get; set; }
public string ProductName { get; set; }
public string SkuDescription { get; set; }
public string MediaResources { get; set; }
public string Currency { get; set; }
public decimal UnitPrice { get; set; }
public decimal TotalPrice { get; set; }
public decimal TotalDiscount { get; set; }
public int Quantity { get; set; }
}
}
\ No newline at end of file
using System;
using EasyAbp.EShop.Orders.Orders.Dtos;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderAppService :
ICrudAppService<
OrderDto,
Guid,
GetOrderListDto,
CreateOrderDto,
CreateOrderDto>
{
}
}
\ No newline at end of file
......@@ -4,12 +4,13 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AutoMapper" Version="2.5.0" />
<PackageReference Include="Volo.Abp.Ddd.Application" Version="2.5.0" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Application.Contracts\EasyAbp.EShop.Products.Application.Contracts.csproj" />
<ProjectReference Include="..\EasyAbp.EShop.Orders.Application.Contracts\EasyAbp.EShop.Orders.Application.Contracts.csproj" />
<ProjectReference Include="..\EasyAbp.EShop.Orders.Domain\EasyAbp.EShop.Orders.Domain.csproj" />
</ItemGroup>
......
using Microsoft.Extensions.DependencyInjection;
using EasyAbp.EShop.Products;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
using Volo.Abp.Application;
......@@ -8,6 +9,7 @@ namespace EasyAbp.EShop.Orders
[DependsOn(
typeof(EShopOrdersDomainModule),
typeof(EShopOrdersApplicationContractsModule),
typeof(EShopProductsApplicationContractsModule),
typeof(AbpDddApplicationModule),
typeof(AbpAutoMapperModule)
)]
......
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
namespace EasyAbp.EShop.Orders.Orders
{
public interface INewOrderGenerator
{
Task<Order> GenerateAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict);
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products.Dtos;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace EasyAbp.EShop.Orders.Orders
{
public class NewOrderGenerator : INewOrderGenerator, ITransientDependency
{
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
private readonly ICurrentUser _currentUser;
private readonly IJsonSerializer _jsonSerializer;
public NewOrderGenerator(
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant,
ICurrentUser currentUser,
IJsonSerializer jsonSerializer)
{
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
_currentUser = currentUser;
_jsonSerializer = jsonSerializer;
}
public virtual async Task<Order> GenerateAsync(CreateOrderDto input, Dictionary<Guid, ProductDto> productDict)
{
var orderLines = new List<OrderLine>();
foreach (var orderLine in input.OrderLines)
{
orderLines.Add(await GenerateNewOrderLineAsync(orderLine, productDict));
}
var productTotalPrice = orderLines.Select(x => x.TotalPrice).Sum();
var order = new Order(
id: _guidGenerator.Create(),
tenantId: _currentTenant.Id,
storeId: input.StoreId,
customerUserId: _currentUser.GetId(),
currency: await GetStoreCurrencyAsync(input.StoreId),
productTotalPrice: productTotalPrice,
totalDiscount: orderLines.Select(x => x.TotalDiscount).Sum(),
totalPrice: productTotalPrice,
refundedAmount: 0,
customerRemark: input.CustomerRemark);
order.SetOrderLines(orderLines);
return order;
}
protected virtual async Task<OrderLine> GenerateNewOrderLineAsync(CreateOrderLineDto input, Dictionary<Guid, ProductDto> productDict)
{
var product = productDict[input.ProductId];
var productSku = product.ProductSkus.Single(x => x.Id == input.ProductSkuId);
return new OrderLine(
id: _guidGenerator.Create(),
productId: product.Id,
productSkuId: productSku.Id,
productModificationTime: product.LastModificationTime ?? product.CreationTime,
productDetailModificationTime: productSku.LastModificationTime ?? productSku.CreationTime,
productName: product.DisplayName,
skuDescription: await GenerateSkuDescriptionAsync(product, productSku),
mediaResources: product.MediaResources,
currency: productSku.Currency,
unitPrice: productSku.Price,
totalPrice: productSku.Price * input.Quantity,
totalDiscount: 0,
quantity: input.Quantity
);
}
protected virtual Task<string> GenerateSkuDescriptionAsync(ProductDto product, ProductSkuDto productSku)
{
var attributeOptionIds = _jsonSerializer.Deserialize<Guid[]>(productSku.SerializedAttributeOptionIds);
var names = new Collection<string[]>();
foreach (var attributeOptionId in attributeOptionIds)
{
names.Add(product.ProductAttributes.SelectMany(
attribute => attribute.ProductAttributeOptions.Where(option => option.Id == attributeOptionId),
(attribute, option) => new [] {attribute.DisplayName, option.DisplayName}).Single());
}
return Task.FromResult(_jsonSerializer.Serialize(names));
}
protected virtual Task<string> GetStoreCurrencyAsync(Guid storeId)
{
// Todo: Get real store currency configuration.
return Task.FromResult("CNY");
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Authorization;
using EasyAbp.EShop.Orders.Orders.Dtos;
using EasyAbp.EShop.Products.Products;
using EasyAbp.EShop.Products.Products.Dtos;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Json;
using Volo.Abp.Users;
namespace EasyAbp.EShop.Orders.Orders
{
[Authorize]
public class OrderAppService : CrudAppService<Order, OrderDto, Guid, GetOrderListDto, CreateOrderDto, CreateOrderDto>,
IOrderAppService
{
protected override string CreatePolicyName { get; set; } = OrdersPermissions.Orders.Create;
protected override string GetPolicyName { get; set; } = OrdersPermissions.Orders.Default;
protected override string GetListPolicyName { get; set; } = OrdersPermissions.Orders.Default;
private readonly INewOrderGenerator _newOrderGenerator;
private readonly IProductAppService _productAppService;
private readonly IOrderDiscountManager _orderDiscountManager;
private readonly IOrderRepository _repository;
public OrderAppService(
INewOrderGenerator newOrderGenerator,
IProductAppService productAppService,
IOrderDiscountManager orderDiscountManager,
IOrderRepository repository) : base(repository)
{
_newOrderGenerator = newOrderGenerator;
_productAppService = productAppService;
_orderDiscountManager = orderDiscountManager;
_repository = repository;
}
protected override IQueryable<Order> CreateFilteredQuery(GetOrderListDto input)
{
var query = base.CreateFilteredQuery(input);
if (input.StoreId.HasValue)
{
query = query.Where(x => x.StoreId == input.StoreId.Value);
}
if (input.CustomerUserId.HasValue)
{
query = query.Where(x => x.CustomerUserId == input.CustomerUserId.Value);
}
return query;
}
public override async Task<PagedResultDto<OrderDto>> GetListAsync(GetOrderListDto input)
{
if (input.CustomerUserId != CurrentUser.GetId())
{
await AuthorizationService.IsGrantedAsync(OrdersPermissions.Orders.Manage);
if (input.StoreId.HasValue)
{
// Todo: Check if current user is an admin of the store.
}
else
{
await AuthorizationService.IsGrantedAsync(OrdersPermissions.Orders.CrossStore);
}
}
return await base.GetListAsync(input);
}
public override async Task<OrderDto> GetAsync(Guid id)
{
await CheckGetPolicyAsync();
var order = await GetEntityByIdAsync(id);
if (order.CustomerUserId != CurrentUser.GetId())
{
await AuthorizationService.IsGrantedAsync(OrdersPermissions.Orders.Manage);
// Todo: Check if current user is an admin of the store.
}
return MapToGetOutputDto(order);
}
public override async Task<OrderDto> CreateAsync(CreateOrderDto input)
{
await CheckCreatePolicyAsync();
// Todo: Check if the store is open.
var productDict = await GetProductDictionaryAsync(input.OrderLines.Select(dto => dto.ProductId).ToList(),
input.StoreId);
// Todo: Check if the product is purchasable.
var order = await _newOrderGenerator.GenerateAsync(input, productDict);
await _orderDiscountManager.DiscountAsync(order, input.ExtraProperties);
await Repository.InsertAsync(order, autoSave: true);
return MapToGetOutputDto(order);
}
protected virtual async Task<Dictionary<Guid, ProductDto>> GetProductDictionaryAsync(
IEnumerable<Guid> productIds, Guid storeId)
{
var dict = new Dictionary<Guid, ProductDto>();
foreach (var productId in productIds)
{
dict.Add(productId, await _productAppService.GetAsync(productId, storeId));
}
return dict;
}
[RemoteService(false)]
public override Task<OrderDto> UpdateAsync(Guid id, CreateOrderDto input)
{
throw new NotSupportedException();
}
[RemoteService(false)]
public override Task DeleteAsync(Guid id)
{
throw new NotSupportedException();
}
}
}
\ No newline at end of file
using AutoMapper;
using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Orders.Orders.Dtos;
using AutoMapper;
namespace EasyAbp.EShop.Orders
{
......@@ -9,6 +11,8 @@ namespace EasyAbp.EShop.Orders
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<Order, OrderDto>();
CreateMap<OrderLine, OrderLineDto>();
}
}
}
\ No newline at end of file
}
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......@@ -12,8 +12,8 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\Orders\*.json" />
<Content Remove="Localization\Orders\*.json" />
<EmbeddedResource Include="EasyAbp\EShop\Orders\Localization\Orders\*.json" />
<Content Remove="EasyAbp\EShop\Orders\Localization\Orders\*.json" />
</ItemGroup>
</Project>
......@@ -17,7 +17,7 @@ namespace EasyAbp.EShop.Orders
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EShopOrdersDomainSharedModule>("EasyAbp.EShop.Orders");
options.FileSets.AddEmbedded<EShopOrdersDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
......@@ -25,12 +25,12 @@ namespace EasyAbp.EShop.Orders
options.Resources
.Add<OrdersResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/Orders");
.AddVirtualJson("EasyAbp/EShop/Orders/Localization/Orders");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Orders", typeof(OrdersResource));
options.MapCodeNamespace("EasyAbp.EShop.Orders", typeof(OrdersResource));
});
}
}
......
{
"culture": "cs",
"texts": {
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "en",
"texts": {
"ManageYourProfile": "Manage your profile",
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "pl",
"texts": {
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "pt-BR",
"texts": {
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "sl",
"texts": {
"ManageYourProfile": "Upravljajte svojim profilom",
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "tr",
"texts": {
"ManageYourProfile": "Profil y�netimi",
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "vi",
"texts": {
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "zh-Hans",
"texts": {
"ManageYourProfile": "管理个人资料",
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
{
"culture": "zh-Hant",
"texts": {
"ManageYourProfile": "管理個人資料",
"Menu:Order": "MenuOrder",
"Order": "Order",
"OrderStoreId": "OrderStoreId",
"OrderCustomerUserId": "OrderCustomerUserId",
"OrderOrderStatus": "OrderOrderStatus",
"OrderCurrency": "OrderCurrency",
"OrderProductTotalPrice": "OrderProductTotalPrice",
"OrderTotalDiscount": "OrderTotalDiscount",
"OrderTotalPrice": "OrderTotalPrice",
"OrderRefundedAmount": "OrderRefundedAmount",
"OrderCustomerRemark": "OrderCustomerRemark",
"OrderStaffRemark": "OrderStaffRemark",
"OrderPaidTime": "OrderPaidTime",
"OrderCompletionTime": "OrderCompletionTime",
"OrderCancelledTime": "OrderCancelledTime",
"OrderLine": "OrderLine",
"OrderLineProductId": "OrderLineProductId",
"OrderLineProductSkuId": "OrderLineProductSkuId",
"OrderLineQuantity": "OrderLineQuantity",
"CreateOrder": "CreateOrder",
"EditOrder": "EditOrder",
"OrderDeletionConfirmationMessage": "Are you sure to delete the order {0}?",
"SuccessfullyDeleted": "Successfully deleted"
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
namespace EasyAbp.EShop.Orders.Orders
{
[Serializable]
public class OrderEto
{
public Guid Id { get; set; }
public Guid StoreId { get; set; }
public Guid CustomerUserId { get; set; }
public OrderStatus OrderStatus { get; set; }
public string Currency { get; set; }
public decimal ProductTotalPrice { get; set; }
public decimal TotalDiscount { get; set; }
public decimal TotalPrice { get; set; }
public decimal RefundedAmount { get; set; }
public string CustomerRemark { get; set; }
public string StaffRemark { get; set; }
public DateTime? PaidTime { get; set; }
public DateTime? CompletionTime { get; set; }
public DateTime? CancelledTime { get; set; }
public List<OrderLineEto> OrderLines { get; set; }
}
}
\ No newline at end of file
using System;
namespace EasyAbp.EShop.Orders.Orders
{
[Serializable]
public class OrderLineEto
{
public Guid Id { get; set; }
public Guid ProductId { get; set; }
public Guid ProductSkuId { get; set; }
public DateTime ProductModificationTime { get; set; }
public DateTime ProductDetailModificationTime { get; set; }
public string ProductName { get; set; }
public string SkuDescription { get; set; }
public string MediaResources { get; set; }
public string Currency { get; set; }
public decimal UnitPrice { get; set; }
public decimal TotalPrice { get; set; }
public decimal TotalDiscount { get; set; }
public int Quantity { get; set; }
}
}
\ No newline at end of file
{
"culture": "en",
"texts": {
"ManageYourProfile": "Manage your profile"
}
}
\ No newline at end of file
{
"culture": "sl",
"texts": {
"ManageYourProfile": "Upravljajte svojim profilom"
}
}
\ No newline at end of file
{
"culture": "tr",
"texts": {
"ManageYourProfile": "Profil ynetimi"
}
}
\ No newline at end of file
{
"culture": "zh-Hans",
"texts": {
"ManageYourProfile": "管理个人资料"
}
}
\ No newline at end of file
{
"culture": "zh-Hant",
"texts": {
"ManageYourProfile": "管理個人資料"
}
}
\ No newline at end of file
using EasyAbp.EShop.Stores;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Orders
{
[DependsOn(
typeof(EShopOrdersDomainSharedModule),
typeof(EShopStoresDomainSharedModule)
)]
public class EShopOrdersDomainModule : AbpModule
{
}
}
......@@ -4,11 +4,13 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AutoMapper" Version="2.5.0" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="2.5.0" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Domain.Shared\EasyAbp.EShop.Products.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Stores\src\EasyAbp.EShop.Stores.Domain.Shared\EasyAbp.EShop.Stores.Domain.Shared.csproj" />
<ProjectReference Include="..\EasyAbp.EShop.Orders.Domain.Shared\EasyAbp.EShop.Orders.Domain.Shared.csproj" />
</ItemGroup>
......
using EasyAbp.EShop.Orders.Orders;
using EasyAbp.EShop.Stores;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Modularity;
namespace EasyAbp.EShop.Orders
{
[DependsOn(
typeof(AbpAutoMapperModule),
typeof(EShopOrdersDomainSharedModule),
typeof(EShopStoresDomainSharedModule)
)]
public class EShopOrdersDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<EShopOrdersDomainModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<OrdersDomainAutoMapperProfile>(validate: true);
});
Configure<AbpDistributedEventBusOptions>(options =>
{
options.EtoMappings.Add<Order, OrderEto>(typeof(EShopOrdersDomainModule));
options.EtoMappings.Add<OrderLine, OrderLineEto>(typeof(EShopOrdersDomainModule));
});
}
}
}
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderDiscountManager : IDomainService
{
Task<Order> DiscountAsync(Order order, Dictionary<string, object> inputExtraProperties);
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderDiscountProvider
{
Task<Order> DiscountAsync(Order order, Dictionary<string, object> inputExtraProperties);
}
}
\ No newline at end of file
using EasyAbp.EShop.Products.Products;
using Volo.Abp.EventBus.Distributed;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderProductInventoryReductionEventHandler : IDistributedEventHandler<ProductInventoryReductionAfterOrderPlacedResultEto>
{
}
}
\ No newline at end of file
using System;
using Volo.Abp.Domain.Repositories;
namespace EasyAbp.EShop.Orders.Orders
{
public interface IOrderRepository : IRepository<Order, Guid>
{
}
}
\ No newline at end of file
using System;
using System;
using System.Collections.Generic;
using EasyAbp.EShop.Stores.Stores;
using JetBrains.Annotations;
......@@ -40,6 +40,57 @@ namespace EasyAbp.EShop.Orders.Orders
public virtual DateTime? CancelledTime { get; protected set; }
public virtual DateTime? ReducedInventoryAfterPlacingTime { get; protected set; }
public virtual DateTime? ReducedInventoryAfterPaymentTime { get; protected set; }
public virtual List<OrderLine> OrderLines { get; protected set; }
protected Order()
{
OrderLines = new List<OrderLine>();
}
public Order(
Guid id,
Guid? tenantId,
Guid storeId,
Guid customerUserId,
string currency,
decimal productTotalPrice,
decimal totalDiscount,
decimal totalPrice,
decimal refundedAmount,
[CanBeNull] string customerRemark
) : base(id)
{
TenantId = tenantId;
StoreId = storeId;
CustomerUserId = customerUserId;
Currency = currency;
ProductTotalPrice = productTotalPrice;
TotalDiscount = totalDiscount;
TotalPrice = totalPrice;
RefundedAmount = refundedAmount;
CustomerRemark = customerRemark;
OrderStatus = OrderStatus.Pending;
OrderLines = new List<OrderLine>();
}
public void SetOrderLines(List<OrderLine> orderLines)
{
OrderLines = orderLines;
}
public void SetReducedInventoryAfterPlacingTime(DateTime? time)
{
ReducedInventoryAfterPlacingTime = time;
}
public void SetReducedInventoryAfterPaymentTime(DateTime? time)
{
ReducedInventoryAfterPaymentTime = time;
}
}
}
\ No newline at end of file
}
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Services;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderDiscountManager : DomainService, IOrderDiscountManager
{
public async Task<Order> DiscountAsync(Order order, Dictionary<string, object> inputExtraProperties)
{
foreach (var provider in ServiceProvider.GetServices<IOrderDiscountProvider>())
{
await provider.DiscountAsync(order, inputExtraProperties);
}
return order;
}
}
}
\ No newline at end of file
......@@ -6,8 +6,6 @@ namespace EasyAbp.EShop.Orders.Orders
{
public class OrderLine : FullAuditedEntity<Guid>
{
public virtual Guid OrderId { get; protected set; }
public virtual Guid ProductId { get; protected set; }
public virtual Guid ProductSkuId { get; protected set; }
......@@ -35,5 +33,36 @@ namespace EasyAbp.EShop.Orders.Orders
public virtual decimal TotalDiscount { get; protected set; }
public virtual int Quantity { get; protected set; }
protected OrderLine() {}
public OrderLine(
Guid id,
Guid productId,
Guid productSkuId,
DateTime productModificationTime,
DateTime productDetailModificationTime,
[NotNull] string productName,
[CanBeNull] string skuDescription,
[CanBeNull] string mediaResources,
[NotNull] string currency,
decimal unitPrice,
decimal totalPrice,
decimal totalDiscount,
int quantity) : base(id)
{
ProductId = productId;
ProductSkuId = productSkuId;
ProductModificationTime = productModificationTime;
ProductDetailModificationTime = productDetailModificationTime;
ProductName = productName;
SkuDescription = skuDescription;
MediaResources = mediaResources;
Currency = currency;
UnitPrice = unitPrice;
TotalPrice = totalPrice;
TotalDiscount = totalDiscount;
Quantity = quantity;
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using EasyAbp.EShop.Products.Products;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
using Volo.Abp.Uow;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderProductInventoryReductionEventHandler : IOrderProductInventoryReductionEventHandler, ITransientDependency
{
private readonly IClock _clock;
private readonly IOrderRepository _orderRepository;
public OrderProductInventoryReductionEventHandler(
IClock clock,
IOrderRepository orderRepository)
{
_clock = clock;
_orderRepository = orderRepository;
}
[UnitOfWork(true)]
public virtual async Task HandleEventAsync(ProductInventoryReductionAfterOrderPlacedResultEto eventData)
{
var order = await _orderRepository.GetAsync(eventData.OrderId);
if (order.OrderStatus != OrderStatus.Pending || order.ReducedInventoryAfterPlacingTime.HasValue)
{
return;
}
if (!eventData.IsSuccess)
{
// Todo: Cancel order.
return;
}
order.SetReducedInventoryAfterPaymentTime(_clock.Now);
await _orderRepository.UpdateAsync(order, true);
}
}
}
\ No newline at end of file
using EasyAbp.EShop.Orders.Orders;
using AutoMapper;
namespace EasyAbp.EShop.Orders
{
public class OrdersDomainAutoMapperProfile : Profile
{
public OrdersDomainAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<Order, OrderEto>();
CreateMap<OrderLine, OrderLineEto>();
}
}
}
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......@@ -12,4 +12,8 @@
<ProjectReference Include="..\EasyAbp.EShop.Orders.Domain\EasyAbp.EShop.Orders.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="EasyAbp\EShop\Orders" />
</ItemGroup>
</Project>
using Microsoft.Extensions.DependencyInjection;
using EasyAbp.EShop.Orders.Orders;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
......@@ -17,7 +18,8 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
/* Add custom repositories here. Example:
* options.AddRepository<Question, EfCoreQuestionRepository>();
*/
options.AddRepository<Order, OrderRepository>();
});
}
}
}
\ No newline at end of file
}
using Volo.Abp.Data;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using EasyAbp.EShop.Orders.Orders;
namespace EasyAbp.EShop.Orders.EntityFrameworkCore
{
......@@ -9,5 +11,6 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
/* Add DbSet for each Aggregate Root here. Example:
* DbSet<Question> Questions { get; }
*/
DbSet<Order> Orders { get; set; }
}
}
\ No newline at end of file
}
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using EasyAbp.EShop.Orders.Orders;
namespace EasyAbp.EShop.Orders.EntityFrameworkCore
{
......@@ -10,6 +11,8 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
/* Add DbSet for each Aggregate Root here. Example:
* public DbSet<Question> Questions { get; set; }
*/
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLines { get; set; }
public OrdersDbContext(DbContextOptions<OrdersDbContext> options)
: base(options)
......@@ -24,4 +27,4 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
builder.ConfigureOrders();
}
}
}
\ No newline at end of file
}
using System;
using EasyAbp.EShop.Orders.Orders;
using System;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace EasyAbp.EShop.Orders.EntityFrameworkCore
{
......@@ -38,6 +40,20 @@ namespace EasyAbp.EShop.Orders.EntityFrameworkCore
b.HasIndex(q => q.CreationTime);
});
*/
builder.Entity<Order>(b =>
{
b.ToTable(options.TablePrefix + "Orders", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
});
builder.Entity<OrderLine>(b =>
{
b.ToTable(options.TablePrefix + "OrderLines", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
});
}
}
}
\ No newline at end of file
}
using System;
using EasyAbp.EShop.Orders.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderRepository : EfCoreRepository<OrdersDbContext, Order, Guid>, IOrderRepository
{
public OrderRepository(IDbContextProvider<OrdersDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
}
\ No newline at end of file
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......@@ -12,4 +12,8 @@
<ProjectReference Include="..\EasyAbp.EShop.Orders.Domain\EasyAbp.EShop.Orders.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="EasyAbp\EShop\Orders" />
</ItemGroup>
</Project>
......@@ -17,6 +17,7 @@
<ItemGroup>
<ProjectReference Include="..\EasyAbp.EShop.Orders.HttpApi\EasyAbp.EShop.Orders.HttpApi.csproj" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Stores\src\EasyAbp.EShop.Stores.Application.Contracts\EasyAbp.EShop.Stores.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
......
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using EasyAbp.EShop.Orders.Localization;
using EasyAbp.EShop.Stores.Stores;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.UI.Navigation;
namespace EasyAbp.EShop.Orders.Web
......@@ -13,11 +20,32 @@ namespace EasyAbp.EShop.Orders.Web
}
}
private Task ConfigureMainMenu(MenuConfigurationContext context)
private async Task ConfigureMainMenu(MenuConfigurationContext context)
{
//Add main menu items.
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<OrdersResource>>(); //Add main menu items.
return Task.CompletedTask;
var authorizationService = context.ServiceProvider.GetRequiredService<IAuthorizationService>();
var orderManagementMenuItem = new ApplicationMenuItem("OrderManagement", l["Menu:OrderManagement"]);
if (await authorizationService.IsGrantedAsync(OrdersPermissions.Orders.Manage))
{
var storeAppService = context.ServiceProvider.GetRequiredService<IStoreAppService>();
var defaultStore = (await storeAppService.GetDefaultAsync())?.Id;
orderManagementMenuItem.AddItem(
new ApplicationMenuItem("Order", l["Menu:Order"], "/EShop/Orders/Orders/Order?storeId=" + defaultStore)
);
}
if (!orderManagementMenuItem.Items.IsNullOrEmpty())
{
var eShopMenuItem = context.Menu.Items.GetOrAdd(i => i.Name == "EasyAbpEShop",
() => new ApplicationMenuItem("EasyAbpEShop", l["Menu:EasyAbpEShop"]));
eShopMenuItem.Items.Add(orderManagementMenuItem);
}
}
}
}
\ No newline at end of file
}
using AutoMapper;
using EasyAbp.EShop.Orders.Orders.Dtos;
using AutoMapper;
namespace EasyAbp.EShop.Orders.Web
{
......@@ -9,6 +10,8 @@ namespace EasyAbp.EShop.Orders.Web
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<OrderDto, CreateOrderDto>();
CreateMap<OrderLineDto, CreateOrderLineDto>();
}
}
}
\ No newline at end of file
}
namespace EasyAbp.EShop.Orders.Web.Pages.Orders
namespace EasyAbp.EShop.Orders.Web.Pages.EShop.Orders
{
public class IndexModel : OrdersPageModel
{
......
@page
@using EasyAbp.EShop.Orders.Web.Pages.EShop.Orders.Orders.Order
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@inherits EasyAbp.EShop.Orders.Web.Pages.OrdersPage
@model IndexModel
@inject IPageLayout PageLayout
@{
PageLayout.Content.Title = L["Order"].Value;
PageLayout.Content.BreadCrumb.Add(L["Menu:Order"].Value);
PageLayout.Content.MenuItemName = "Order";
var cardTitle = @L["Order"].Value;
if (Model.StoreName != null)
{
cardTitle += $" - {Model.StoreName}";
}
if (Model.CustomerUserName != null)
{
cardTitle += $" - {Model.CustomerUserName}";
}
}
@section scripts
{
<abp-script src="/Pages/EShop/Orders/Orders/Order/index.js" />
}
@section styles
{
<abp-style src="/Pages/EShop/Orders/Orders/Order/index.css"/>
}
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_6">
<abp-card-title>@cardTitle</abp-card-title>
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="OrderTable" class="nowrap">
<thead>
<tr>
<th>@L["Actions"]</th>
<th>@L["OrderCustomerUserId"]</th>
<th>@L["OrderOrderStatus"]</th>
<th>@L["OrderTotalPrice"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>
\ No newline at end of file
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Stores.Stores;
using Microsoft.AspNetCore.Mvc;
namespace EasyAbp.EShop.Orders.Web.Pages.EShop.Orders.Orders.Order
{
public class IndexModel : OrdersPageModel
{
private readonly IStoreAppService _storeAppService;
[BindProperty(SupportsGet = true)]
public Guid? StoreId { get; set; }
[BindProperty(SupportsGet = true)]
public Guid? CustomerUserId { get; set; }
public string StoreName { get; set; }
public string CustomerUserName { get; set; }
public IndexModel(IStoreAppService storeAppService)
{
_storeAppService = storeAppService;
}
public virtual async Task OnGetAsync()
{
if (StoreId.HasValue)
{
StoreName = (await _storeAppService.GetAsync(StoreId.Value)).Name;
}
if (CustomerUserId.HasValue)
{
// Todo: get username
}
}
}
}
$(function () {
var l = abp.localization.getResource('Orders');
var service = easyAbp.eShop.orders.orders.order;
var dataTable = $('#OrderTable').DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: false,
autoWidth: false,
scrollCollapse: true,
order: [[1, "asc"]],
ajax: abp.libs.datatables.createAjax(service.getList),
columnDefs: [
{
rowAction: {
items:
[
{
text: l('Detail'),
action: function (data) {
editModal.open({ id: data.record.id });
}
}
]
}
},
{ data: "customerUserId" },
{ data: "orderStatus" },
{ data: "totalPrice" },
]
}));
createModal.onResult(function () {
dataTable.ajax.reload();
});
editModal.onResult(function () {
dataTable.ajax.reload();
});
$('#NewOrderButton').click(function (e) {
e.preventDefault();
createModal.open();
});
});
\ No newline at end of file
......@@ -6,7 +6,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace EasyAbp.EShop.Orders.Web.Pages
{
/* Inherit your UI Pages from this class. To do that, add this line to your Pages (.cshtml files under the Page folder):
* @inherits EasyAbp.EShop.Orders.Web.Pages.OrdersPage
* @inherits EasyAbp.EShop.Orders.Web.Pages.EShop.Orders.OrdersPage
*/
public abstract class OrdersPage : AbpPage
{
......
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
using Shouldly;
using System.Threading.Tasks;
using Xunit;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderAppServiceTests : OrdersApplicationTestBase
{
private readonly IOrderAppService _orderAppService;
public OrderAppServiceTests()
{
_orderAppService = GetRequiredService<IOrderAppService>();
}
[Fact]
public async Task Test1()
{
// Arrange
// Act
// Assert
}
}
}
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace EasyAbp.EShop.Orders.Orders
{
public class OrderDomainTests : OrdersDomainTestBase
{
public OrderDomainTests()
{
}
[Fact]
public async Task Test1()
{
// Arrange
// Assert
// Assert
}
}
}
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
using System;
using System.Threading.Tasks;
using EasyAbp.EShop.Orders.Orders;
using Volo.Abp.Domain.Repositories;
using Xunit;
namespace EasyAbp.EShop.Orders.EntityFrameworkCore.Orders
{
public class OrderRepositoryTests : OrdersEntityFrameworkCoreTestBase
{
private readonly IRepository<Order, Guid> _orderRepository;
public OrderRepositoryTests()
{
_orderRepository = GetRequiredService<IRepository<Order, Guid>>();
}
[Fact]
public async Task Test1()
{
await WithUnitOfWorkAsync(async () =>
{
// Arrange
// Act
//Assert
});
}
}
}
......@@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
......@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>EasyAbp.EShop.Orders</RootNamespace>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
......
......@@ -217,6 +217,8 @@ namespace EasyAbp.EShop.Products.Products
{
await CheckStoreIsProductOwnerAsync(id, storeId);
}
// Todo: get real inventory.
dto.CategoryIds = (await _productCategoryRepository.GetListByProductIdAsync(dto.Id))
.Select(x => x.CategoryId).ToList();
......@@ -250,6 +252,8 @@ namespace EasyAbp.EShop.Products.Products
var entities = await AsyncQueryableExecuter.ToListAsync(query);
// Todo: get real inventory.
return new PagedResultDto<ProductDto>(
totalCount,
entities.Select(MapToGetListOutputDto).ToList()
......
......@@ -3,8 +3,10 @@
namespace EasyAbp.EShop.Products.Products
{
[Serializable]
public class ProductInventoryReductionFailedEto
public class ProductInventoryReductionAfterOrderPlacedResultEto
{
public Guid OrderId { get; set; }
public bool IsSuccess { get; set; }
}
}
\ No newline at end of file
......@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="2.5.0" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Orders\src\EasyAbp.EShop.Orders.Domain.Shared\EasyAbp.EShop.Orders.Domain.Shared.csproj" />
<ProjectReference Include="..\EasyAbp.EShop.Products.Domain.Shared\EasyAbp.EShop.Products.Domain.Shared.csproj" />
</ItemGroup>
......
......@@ -18,5 +18,15 @@ namespace EasyAbp.EShop.Products.Products
{
return Task.FromResult(productSku.Inventory);
}
public Task<bool> TryIncreaseInventoryAsync(Product product, ProductSku productSku, Guid storeId, int quantity)
{
return Task.FromResult(productSku.TryIncreaseInventory(quantity));
}
public Task<bool> TryReduceInventoryAsync(Product product, ProductSku productSku, Guid storeId, int quantity)
{
return Task.FromResult(productSku.TryReduceInventory(quantity));
}
}
}
\ No newline at end of file
using EasyAbp.EShop.Orders.Orders;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
namespace EasyAbp.EShop.Products.Products
{
public interface IOrderCreatedEventHandler : IDistributedEventHandler<EntityCreatedEto<OrderEto>>
{
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment