mirror of https://github.com/EasyAbp/EShop.git
committed by
GitHub
38 changed files with 834 additions and 98 deletions
@ -0,0 +1,72 @@ |
|||
# EShop.Plugins.Baskets |
|||
|
|||
[](https://abp.io) |
|||
[](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Baskets.Domain.Shared) |
|||
[](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Baskets.Domain.Shared) |
|||
[](https://discord.gg/S6QaezrCRq) |
|||
[](https://www.github.com/EasyAbp/EShop) |
|||
|
|||
🛒 A baskets (cart) plugin for EShop. It supports both the server-side pattern and the client-side pattern. |
|||
|
|||
## Installation |
|||
|
|||
1. Install the following NuGet packages. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-nuget-packages)) |
|||
|
|||
* EasyAbp.EShop.Plugins.Baskets.Application |
|||
* EasyAbp.EShop.Plugins.Baskets.Application.Contracts |
|||
* EasyAbp.EShop.Plugins.Baskets.Domain |
|||
* EasyAbp.EShop.Plugins.Baskets.Domain.Shared |
|||
* EasyAbp.EShop.Plugins.Baskets.EntityFrameworkCore |
|||
* EasyAbp.EShop.Plugins.Baskets.HttpApi |
|||
* EasyAbp.EShop.Plugins.Baskets.HttpApi.Client |
|||
* (Optional) EasyAbp.EShop.Plugins.Baskets.MongoDB |
|||
* (Optional) EasyAbp.EShop.Plugins.Baskets.Web |
|||
|
|||
1. Add `DependsOn(typeof(EShopXxxModule))` attribute to configure the module dependencies. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-module-dependencies)) |
|||
|
|||
1. Add `builder.ConfigureEShopPluginsBaskets();` to the `OnModelCreating()` method in **MyProjectMigrationsDbContext.cs**. |
|||
|
|||
1. Add EF Core migrations and update your database. See: [ABP document](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF#add-database-migration). |
|||
|
|||
## Server-side Baskets Pattern |
|||
|
|||
The server-side basket is for identified(logon) users. It requests the basket [backend service APIs](https://github.com/EasyAbp/EShop/blob/dev/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.HttpApi/EasyAbp/EShop/Plugins/Baskets/BasketItems/BasketItemController.cs) are available. |
|||
|
|||
1. Before you create a basket item, use `/api/e-shop/orders/order/check-create` (POST) to check whether the current user is allowed to create an order with it. |
|||
1. Use `/api/e-shop/plugins/baskets/basket-item` (POST) to create basket items on the server side. Extra properties are allowed. |
|||
1. Use `/api/e-shop/plugins/baskets/basket-item` (GET) to get basket the item list. The returned "IsInvalid" property shows whether you can use the item to create an order. |
|||
1. Use `/api/e-shop/plugins/baskets/basket-item/{id}` (PUT) or `/api/e-shop/plugins/baskets/basket-item/{id}` (DELETE) to change an item's quantity or remove an item. |
|||
|
|||
## Client-side Baskets Pattern |
|||
|
|||
You should store the basket items in the client(browser) cache as a collection of [IBasketItem](https://github.com/EasyAbp/EShop/blob/dev/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.Domain.Shared/EasyAbp/EShop/Plugins/Baskets/BasketItems/IBasketItem.cs). The client-side doesn't depend on the baskets module backend service APIs. |
|||
|
|||
### With Backend |
|||
|
|||
If you install the backend, it provides APIs to help refresh your client-side basket items. |
|||
|
|||
1. Before you create a basket item, use `/api/e-shop/orders/order/check-create` (POST) to check whether the current user is allowed to create an order with it. |
|||
1. Use `/api/e-shop/plugins/baskets/basket-item/generate-client-side-data` (POST) to refresh your client-side basket items anytime. |
|||
|
|||
### Without Backend |
|||
|
|||
If you don't install the backend, you can still create and store the basket items locally, but the client-side basket items will not validate and never update. |
|||
|
|||
## How To Determine a Basket Pattern |
|||
|
|||
The server-side baskets pattern will take effect if all the following conditions are met: |
|||
1. The baskets module backend has been installed and is available. |
|||
1. The `EasyAbp.EShop.Plugins.Baskets.EnableServerSideBaskets` setting value is "True". |
|||
1. The current user has the `EasyAbp.EShop.Plugins.Baskets.BasketItem` permission. |
|||
|
|||
## What if Anonymous Users Add Basket Items and Then Log In? |
|||
|
|||
The client(browser) should try to add the existing items to the server-side and remove them from the client-side. You can implement it by referring to the [index.js](https://github.com/EasyAbp/EShop/blob/dev/plugins/Baskets/src/EasyAbp.EShop.Plugins.Baskets.Web/Pages/EShop/Plugins/Baskets/BasketItems/BasketItem/index.js). |
|||
|
|||
## Use Basket Items To Create an Order |
|||
|
|||
We don't provide a built-in way to convert basket items to an order creation DTO. |
|||
|
|||
Create an order manually by the front end, and remember to map the basket item's extra properties to the order creation DTO since some special product may need them. |
|||
|
|||
After creating the order, the front end should clear the unused basket items. |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Orders.Dtos; |
|||
|
|||
[Serializable] |
|||
public class CheckCreateOrderInput : CreateOrderDto |
|||
{ |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace EasyAbp.EShop.Orders.Orders.Dtos; |
|||
|
|||
[Serializable] |
|||
public class CheckCreateOrderResultDto |
|||
{ |
|||
public bool CanCreate { get; set; } |
|||
|
|||
public string Reason { get; set; } |
|||
} |
|||
@ -0,0 +1 @@ |
|||
../../docs/plugins/baskets/README.md |
|||
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using EasyAbp.EShop.Products.Products.Dtos; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.BasketItems; |
|||
|
|||
public class BasicBasketItemProductInfoUpdater : IBasketItemProductInfoUpdater, ITransientDependency |
|||
{ |
|||
protected IProductSkuDescriptionProvider ProductSkuDescriptionProvider { get; } |
|||
|
|||
public BasicBasketItemProductInfoUpdater(IProductSkuDescriptionProvider productSkuDescriptionProvider) |
|||
{ |
|||
ProductSkuDescriptionProvider = productSkuDescriptionProvider; |
|||
} |
|||
|
|||
public virtual Task UpdateForAnonymousAsync(int targetQuantity, IBasketItem item, ProductDto productDto) |
|||
{ |
|||
return InternalUpdateAsync(targetQuantity, item, productDto); |
|||
} |
|||
|
|||
public virtual Task UpdateForIdentifiedAsync(int targetQuantity, IBasketItem item, ProductDto productDto) |
|||
{ |
|||
return InternalUpdateAsync(targetQuantity, item, productDto); |
|||
} |
|||
|
|||
protected virtual async Task InternalUpdateAsync(int targetQuantity, IBasketItem item, ProductDto productDto) |
|||
{ |
|||
var productSkuDto = productDto.FindSkuById(item.ProductSkuId); |
|||
|
|||
if (productSkuDto == null) |
|||
{ |
|||
item.SetIsInvalid(true); |
|||
return; |
|||
} |
|||
|
|||
item.UpdateProductData(targetQuantity, new ProductDataModel |
|||
{ |
|||
MediaResources = productSkuDto.MediaResources ?? productDto.MediaResources, |
|||
ProductUniqueName = productDto.UniqueName, |
|||
ProductDisplayName = productDto.DisplayName, |
|||
SkuName = productSkuDto.Name, |
|||
SkuDescription = await ProductSkuDescriptionProvider.GenerateAsync(productDto, productSkuDto), |
|||
Currency = productSkuDto.Currency, |
|||
UnitPrice = productSkuDto.DiscountedPrice, |
|||
TotalPrice = productSkuDto.DiscountedPrice * item.Quantity, |
|||
TotalDiscount = (productSkuDto.Price - productSkuDto.DiscountedPrice) * item.Quantity, |
|||
Inventory = productSkuDto.Inventory |
|||
}); |
|||
|
|||
if (productDto.InventoryStrategy != InventoryStrategy.NoNeed && targetQuantity > productSkuDto.Inventory) |
|||
{ |
|||
item.SetIsInvalid(true); |
|||
return; |
|||
} |
|||
|
|||
if (!productDto.IsPublished) |
|||
{ |
|||
item.SetIsInvalid(true); |
|||
return; |
|||
} |
|||
|
|||
if (!targetQuantity.IsBetween(productSkuDto.OrderMinQuantity, productSkuDto.OrderMaxQuantity)) |
|||
{ |
|||
item.SetIsInvalid(true); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.EShop.Products.Products.Dtos; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.BasketItems; |
|||
|
|||
public interface IBasketItemProductInfoUpdater |
|||
{ |
|||
Task UpdateForAnonymousAsync(int targetQuantity, IBasketItem item, ProductDto productDto); |
|||
|
|||
Task UpdateForIdentifiedAsync(int targetQuantity, IBasketItem item, ProductDto productDto); |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
using EasyAbp.EShop.Products.ProductInventories; |
|||
using EasyAbp.EShop.Products.Products; |
|||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.BasketItems |
|||
{ |
|||
public interface IProductUpdateRecorder |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace EasyAbp.EShop.Plugins.Baskets.ProductUpdates; |
|||
|
|||
public interface IProductUpdateRecorder |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\Baskets\src\EasyAbp.EShop.Plugins.Baskets.Application\EasyAbp.EShop.Plugins.Baskets.Application.csproj" /> |
|||
<ProjectReference Include="..\EasyAbp.EShop.Plugins.Booking.Application.Contracts\EasyAbp.EShop.Plugins.Booking.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Caching" Version="$(AbpVersion)" /> |
|||
<PackageReference Include="EasyAbp.BookingService.Application.Contracts" Version="$(EasyAbpBookingServiceModuleVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,125 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EasyAbp.BookingService.AssetOccupancies; |
|||
using EasyAbp.BookingService.AssetOccupancies.Dtos; |
|||
using EasyAbp.EShop.Plugins.Baskets.BasketItems; |
|||
using EasyAbp.EShop.Plugins.Baskets.Booking.ObjectExtending; |
|||
using EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions; |
|||
using EasyAbp.EShop.Products.Products.Dtos; |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.Booking.BasketItems; |
|||
|
|||
public class BookingBasketItemProductInfoUpdater : IBasketItemProductInfoUpdater, ITransientDependency |
|||
{ |
|||
protected List<string> ProductGroupNames { get; set; } |
|||
protected bool? AllowedToUpdate { get; set; } |
|||
|
|||
protected ICurrentUser CurrentUser { get; } |
|||
protected IDistributedCache<BookingBasketItemUpdatedCacheItem> Cache { get; } |
|||
protected IAssetOccupancyAppService AssetOccupancyAppService { get; } |
|||
protected IBookingProductGroupDefinitionAppService DefinitionAppService { get; } |
|||
|
|||
public BookingBasketItemProductInfoUpdater( |
|||
ICurrentUser currentUser, |
|||
IDistributedCache<BookingBasketItemUpdatedCacheItem> cache, |
|||
IAssetOccupancyAppService assetOccupancyAppService, |
|||
IBookingProductGroupDefinitionAppService definitionAppService) |
|||
{ |
|||
CurrentUser = currentUser; |
|||
Cache = cache; |
|||
AssetOccupancyAppService = assetOccupancyAppService; |
|||
DefinitionAppService = definitionAppService; |
|||
} |
|||
|
|||
public virtual Task UpdateForAnonymousAsync(int targetQuantity, IBasketItem item, ProductDto productDto) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public virtual async Task UpdateForIdentifiedAsync(int targetQuantity, IBasketItem item, ProductDto productDto) |
|||
{ |
|||
if (!await IsAllowedToUpdateAsync(item, productDto) || |
|||
!await IsBookingProductGroupAsync(productDto.ProductGroupName)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var assetId = item.FindBookingAssetId(); |
|||
var assetCategoryId = item.FindBookingAssetCategoryId(); |
|||
|
|||
if (assetId is not null) |
|||
{ |
|||
try |
|||
{ |
|||
await AssetOccupancyAppService.CheckCreateAsync(new CreateAssetOccupancyDto |
|||
{ |
|||
AssetId = assetId.Value, |
|||
Volume = item.GetBookingVolume(), |
|||
Date = item.GetBookingDate(), |
|||
StartingTime = item.GetBookingStartingTime(), |
|||
Duration = item.GetBookingDuration() |
|||
}); |
|||
} |
|||
catch |
|||
{ |
|||
item.SetIsInvalid(true); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
try |
|||
{ |
|||
await AssetOccupancyAppService.CheckCreateByCategoryIdAsync(new CreateAssetOccupancyByCategoryIdDto |
|||
{ |
|||
AssetCategoryId = assetCategoryId!.Value, |
|||
Volume = item.GetBookingVolume(), |
|||
Date = item.GetBookingDate(), |
|||
StartingTime = item.GetBookingStartingTime(), |
|||
Duration = item.GetBookingDuration() |
|||
}); |
|||
} |
|||
catch |
|||
{ |
|||
item.SetIsInvalid(true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<bool> IsAllowedToUpdateAsync(IBasketItem item, ProductDto productDto) |
|||
{ |
|||
if (AllowedToUpdate.HasValue) |
|||
{ |
|||
return AllowedToUpdate.Value; |
|||
} |
|||
|
|||
var key = GetCacheItemKey(item, productDto); |
|||
|
|||
AllowedToUpdate = await Cache.GetAsync(key) is null; |
|||
|
|||
await Cache.SetAsync(key, new BookingBasketItemUpdatedCacheItem(), new DistributedCacheEntryOptions |
|||
{ |
|||
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30) |
|||
}); |
|||
|
|||
return AllowedToUpdate.Value; |
|||
} |
|||
|
|||
protected virtual string GetCacheItemKey(IBasketItem item, ProductDto productDto) |
|||
{ |
|||
return CurrentUser.GetId().ToString(); |
|||
} |
|||
|
|||
protected virtual async Task<bool> IsBookingProductGroupAsync(string productGroupName) |
|||
{ |
|||
ProductGroupNames ??= |
|||
(await DefinitionAppService.GetListAsync()).Items.Select(x => x.ProductGroupName).ToList(); |
|||
|
|||
return ProductGroupNames.Contains(productGroupName); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace EasyAbp.EShop.Plugins.Baskets.Booking.BasketItems; |
|||
|
|||
public class BookingBasketItemUpdatedCacheItem |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using EasyAbp.EShop.Orders; |
|||
using EasyAbp.EShop.Plugins.Baskets.Booking.ObjectExtending; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.Booking; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpCachingModule), |
|||
typeof(EShopPluginsBasketsApplicationModule), |
|||
typeof(EShopOrdersApplicationContractsModule) |
|||
)] |
|||
public class EShopPluginsBasketsBookingApplicationModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
EShopPluginsBasketsBookingObjectExtensions.Configure(); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
namespace EasyAbp.EShop.Plugins.Baskets.Booking.ObjectExtending; |
|||
|
|||
public class BookingBasketItemProperties |
|||
{ |
|||
public const string BasketItemBookingAssetId = "BookingAssetId"; |
|||
|
|||
public const string BasketItemBookingAssetCategoryId = "BookingAssetCategoryId"; |
|||
|
|||
public const string BasketItemBookingPeriodSchemeId = "BookingPeriodSchemeId"; |
|||
|
|||
public const string BasketItemBookingPeriodId = "BookingPeriodId"; |
|||
|
|||
public const string BasketItemBookingDate = "BookingDate"; |
|||
|
|||
public const string BasketItemBookingStartingTime = "BookingStartingTime"; |
|||
|
|||
public const string BasketItemBookingDuration = "BookingDuration"; |
|||
|
|||
public const string BasketItemBookingAssetOccupancyId = "BookingAssetOccupancyId"; |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Orders; |
|||
using EasyAbp.EShop.Plugins.Baskets.BasketItems; |
|||
using EasyAbp.EShop.Plugins.Baskets.BasketItems.Dtos; |
|||
using Volo.Abp.ObjectExtending; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.Booking.ObjectExtending; |
|||
|
|||
public static class EShopPluginsBasketsBookingObjectExtensions |
|||
{ |
|||
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); |
|||
|
|||
public static void Configure() |
|||
{ |
|||
OneTimeRunner.Run(() => |
|||
{ |
|||
/* You can configure extension properties to entities or other object types |
|||
* defined in the depended modules. |
|||
* |
|||
* If you are using EF Core and want to map the entity extension properties to new |
|||
* table fields in the database, then configure them in the EShopSampleEfCoreEntityExtensionMappings |
|||
* |
|||
* Example: |
|||
* |
|||
* ObjectExtensionManager.Instance |
|||
* .AddOrUpdateProperty<IdentityRole, string>("Title"); |
|||
* |
|||
* See the documentation for more: |
|||
* https://docs.abp.io/en/abp/latest/Object-Extensions
|
|||
*/ |
|||
|
|||
ObjectExtensionManager.Instance |
|||
.AddOrUpdate( |
|||
new[] |
|||
{ |
|||
typeof(BasketItem), |
|||
typeof(BasketItemDto), |
|||
typeof(CreateBasketItemDto), |
|||
typeof(UpdateBasketItemDto) |
|||
}, |
|||
config => |
|||
{ |
|||
config.AddOrUpdateProperty<Guid?>(BookingBasketItemProperties.BasketItemBookingAssetId); |
|||
config.AddOrUpdateProperty<Guid?>(BookingBasketItemProperties.BasketItemBookingAssetCategoryId); |
|||
config.AddOrUpdateProperty<Guid?>(BookingBasketItemProperties.BasketItemBookingPeriodSchemeId); |
|||
config.AddOrUpdateProperty<Guid?>(BookingBasketItemProperties.BasketItemBookingPeriodId); |
|||
config.AddOrUpdateProperty<DateTime?>(BookingBasketItemProperties.BasketItemBookingDate); |
|||
config.AddOrUpdateProperty<TimeSpan?>(BookingBasketItemProperties.BasketItemBookingStartingTime); |
|||
config.AddOrUpdateProperty<TimeSpan?>(BookingBasketItemProperties.BasketItemBookingDuration); |
|||
} |
|||
); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
using System; |
|||
using EasyAbp.EShop.Orders; |
|||
using EasyAbp.EShop.Plugins.Baskets.BasketItems; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace EasyAbp.EShop.Plugins.Baskets.Booking.ObjectExtending; |
|||
|
|||
public static class OrderLineExtensions |
|||
{ |
|||
public static Guid? FindBookingAssetId(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.GetProperty<Guid?>(BookingOrderProperties.OrderLineBookingAssetId); |
|||
} |
|||
|
|||
public static Guid GetBookingAssetId(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingAssetId(basketItem), |
|||
BookingOrderProperties.OrderLineBookingAssetId)!.Value; |
|||
} |
|||
|
|||
public static Guid? FindBookingAssetCategoryId(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.GetProperty<Guid?>(BookingOrderProperties.OrderLineBookingAssetCategoryId); |
|||
} |
|||
|
|||
public static Guid GetBookingAssetCategoryId(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingAssetCategoryId(basketItem), |
|||
BookingOrderProperties.OrderLineBookingAssetCategoryId)!.Value; |
|||
} |
|||
|
|||
public static Guid? FindBookingPeriodSchemeId(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.GetProperty<Guid?>(BookingOrderProperties.OrderLineBookingPeriodSchemeId); |
|||
} |
|||
|
|||
public static Guid GetBookingPeriodSchemeId(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingPeriodSchemeId(basketItem), |
|||
BookingOrderProperties.OrderLineBookingPeriodSchemeId)!.Value; |
|||
} |
|||
|
|||
public static Guid? FindBookingPeriodId(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.GetProperty<Guid?>(BookingOrderProperties.OrderLineBookingPeriodId); |
|||
} |
|||
|
|||
public static Guid GetBookingPeriodId(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingPeriodId(basketItem), |
|||
BookingOrderProperties.OrderLineBookingPeriodId)!.Value; |
|||
} |
|||
|
|||
public static int? FindBookingVolume(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.Quantity; |
|||
} |
|||
|
|||
public static int GetBookingVolume(this IBasketItem basketItem) |
|||
{ |
|||
return FindBookingVolume(basketItem)!.Value; |
|||
} |
|||
|
|||
public static DateTime? FindBookingDate(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.FindDateTimeProperty(BookingOrderProperties.OrderLineBookingDate); |
|||
} |
|||
|
|||
public static DateTime GetBookingDate(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingDate(basketItem), |
|||
BookingOrderProperties.OrderLineBookingDate)!.Value; |
|||
} |
|||
|
|||
public static TimeSpan? FindBookingStartingTime(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.FindTimeSpanProperty(BookingOrderProperties.OrderLineBookingStartingTime); |
|||
} |
|||
|
|||
public static TimeSpan GetBookingStartingTime(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingStartingTime(basketItem), |
|||
BookingOrderProperties.OrderLineBookingStartingTime)!.Value; |
|||
} |
|||
|
|||
public static TimeSpan? FindBookingDuration(this IBasketItem basketItem) |
|||
{ |
|||
return basketItem.FindTimeSpanProperty(BookingOrderProperties.OrderLineBookingDuration); |
|||
} |
|||
|
|||
public static TimeSpan GetBookingDuration(this IBasketItem basketItem) |
|||
{ |
|||
return Check.NotNull(FindBookingDuration(basketItem), |
|||
BookingOrderProperties.OrderLineBookingDuration)!.Value; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
Loading…
Reference in new issue