diff --git a/Directory.Build.props b/Directory.Build.props
index 87451b5e..8ebd2c05 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,6 +5,7 @@
2.7.1
2.0.14
1.0.1
+ 0.1.0
1.7.0
3.6.2
1.0.5
diff --git a/EShop.sln b/EShop.sln
index 9031a10d..cb7e623f 100644
--- a/EShop.sln
+++ b/EShop.sln
@@ -375,6 +375,48 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F58B6EEF-5
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests", "plugins\Inventories\OrleansGrains\test\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests\EasyAbp.EShop.Products.OrleansGrainsInventory.Domain.Tests.csproj", "{D652EBF0-27CA-44C2-BB78-F446B87377C7}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booking", "Booking", "{CE945F1D-6636-47D5-A619-C16C4E14CF8C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A8C4583C-034E-47AF-B7EC-1A34EE288E2F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Application", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.Application\EasyAbp.EShop.Plugins.Booking.Application.csproj", "{10C98582-61EB-49B9-9E6B-83E90CA3795D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Application.Contracts", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.Application.Contracts\EasyAbp.EShop.Plugins.Booking.Application.Contracts.csproj", "{BA8BAB83-D8D9-4716-BB80-C58A67C5F7A3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Domain", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.Domain\EasyAbp.EShop.Plugins.Booking.Domain.csproj", "{1E65087A-DAC5-4595-9E1B-1BBE6C333D7D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Domain.Shared", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.Domain.Shared\EasyAbp.EShop.Plugins.Booking.Domain.Shared.csproj", "{E78D741F-F071-4824-9386-E370717F7CBF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.csproj", "{926BA9EC-AB2F-4E4F-8363-54426411A17C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.HttpApi", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.HttpApi\EasyAbp.EShop.Plugins.Booking.HttpApi.csproj", "{878A5B7A-269F-4356-8B97-E5BC5EE6BED9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.HttpApi.Client", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.HttpApi.Client\EasyAbp.EShop.Plugins.Booking.HttpApi.Client.csproj", "{4EFA90C2-3EFE-4E9C-BDEB-274493EB8C9D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Installer", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.Installer\EasyAbp.EShop.Plugins.Booking.Installer.csproj", "{FF764D80-17E6-4DE4-92CC-591F706B39DD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.MongoDB", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.MongoDB\EasyAbp.EShop.Plugins.Booking.MongoDB.csproj", "{B7944CFA-31AB-4D35-925E-847E1115BF88}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Web", "plugins\Booking\src\EasyAbp.EShop.Plugins.Booking.Web\EasyAbp.EShop.Plugins.Booking.Web.csproj", "{E0ABB505-A788-4E66-A51E-94A9A0DD18FA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Application.Tests", "plugins\Booking\test\EasyAbp.EShop.Plugins.Booking.Application.Tests\EasyAbp.EShop.Plugins.Booking.Application.Tests.csproj", "{84C09B9B-97D8-4BAE-9741-ACE5707D5120}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Domain.Tests", "plugins\Booking\test\EasyAbp.EShop.Plugins.Booking.Domain.Tests\EasyAbp.EShop.Plugins.Booking.Domain.Tests.csproj", "{43DA07BB-DE8F-4D8F-B580-9BA75B39CFF3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests", "plugins\Booking\test\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests.csproj", "{435ED217-120E-472D-A9F6-B298FFFC3ADB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp", "plugins\Booking\test\EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp\EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp.csproj", "{0F0A4F84-419F-4547-9001-0853C1E509F9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.MongoDB.Tests", "plugins\Booking\test\EasyAbp.EShop.Plugins.Booking.MongoDB.Tests\EasyAbp.EShop.Plugins.Booking.MongoDB.Tests.csproj", "{5AE1B9B6-BE5D-4919-9612-7E11D384A985}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.TestBase", "plugins\Booking\test\EasyAbp.EShop.Plugins.Booking.TestBase\EasyAbp.EShop.Plugins.Booking.TestBase.csproj", "{739CAE6A-14E6-44FC-8863-DA905CBD289F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Orders.Booking.Application", "plugins\Booking\src\EasyAbp.EShop.Orders.Booking.Application\EasyAbp.EShop.Orders.Booking.Application.csproj", "{C0399352-1278-4D91-8D4E-7491FD77C18B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Payments.Booking.Application", "plugins\Booking\src\EasyAbp.EShop.Payments.Booking.Application\EasyAbp.EShop.Payments.Booking.Application.csproj", "{4480BFAF-C981-4242-A509-EDA6F572E45C}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FlashSales", "FlashSales", "{867AD9F8-FD56-469D-A90D-C569EB9C3D2A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.FlashSales.Application", "plugins\FlashSales\src\EasyAbp.EShop.Plugins.FlashSales.Application\EasyAbp.EShop.Plugins.FlashSales.Application.csproj", "{26611C4C-6910-498A-9FBA-BECC09392ADE}"
@@ -1019,6 +1061,78 @@ Global
{D652EBF0-27CA-44C2-BB78-F446B87377C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D652EBF0-27CA-44C2-BB78-F446B87377C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D652EBF0-27CA-44C2-BB78-F446B87377C7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10C98582-61EB-49B9-9E6B-83E90CA3795D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10C98582-61EB-49B9-9E6B-83E90CA3795D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10C98582-61EB-49B9-9E6B-83E90CA3795D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10C98582-61EB-49B9-9E6B-83E90CA3795D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BA8BAB83-D8D9-4716-BB80-C58A67C5F7A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA8BAB83-D8D9-4716-BB80-C58A67C5F7A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA8BAB83-D8D9-4716-BB80-C58A67C5F7A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA8BAB83-D8D9-4716-BB80-C58A67C5F7A3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1E65087A-DAC5-4595-9E1B-1BBE6C333D7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1E65087A-DAC5-4595-9E1B-1BBE6C333D7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1E65087A-DAC5-4595-9E1B-1BBE6C333D7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1E65087A-DAC5-4595-9E1B-1BBE6C333D7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E78D741F-F071-4824-9386-E370717F7CBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E78D741F-F071-4824-9386-E370717F7CBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E78D741F-F071-4824-9386-E370717F7CBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E78D741F-F071-4824-9386-E370717F7CBF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {926BA9EC-AB2F-4E4F-8363-54426411A17C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {926BA9EC-AB2F-4E4F-8363-54426411A17C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {926BA9EC-AB2F-4E4F-8363-54426411A17C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {926BA9EC-AB2F-4E4F-8363-54426411A17C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {878A5B7A-269F-4356-8B97-E5BC5EE6BED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {878A5B7A-269F-4356-8B97-E5BC5EE6BED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {878A5B7A-269F-4356-8B97-E5BC5EE6BED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {878A5B7A-269F-4356-8B97-E5BC5EE6BED9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4EFA90C2-3EFE-4E9C-BDEB-274493EB8C9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4EFA90C2-3EFE-4E9C-BDEB-274493EB8C9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4EFA90C2-3EFE-4E9C-BDEB-274493EB8C9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4EFA90C2-3EFE-4E9C-BDEB-274493EB8C9D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FF764D80-17E6-4DE4-92CC-591F706B39DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FF764D80-17E6-4DE4-92CC-591F706B39DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FF764D80-17E6-4DE4-92CC-591F706B39DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FF764D80-17E6-4DE4-92CC-591F706B39DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B7944CFA-31AB-4D35-925E-847E1115BF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7944CFA-31AB-4D35-925E-847E1115BF88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7944CFA-31AB-4D35-925E-847E1115BF88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7944CFA-31AB-4D35-925E-847E1115BF88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E0ABB505-A788-4E66-A51E-94A9A0DD18FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E0ABB505-A788-4E66-A51E-94A9A0DD18FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E0ABB505-A788-4E66-A51E-94A9A0DD18FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E0ABB505-A788-4E66-A51E-94A9A0DD18FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {84C09B9B-97D8-4BAE-9741-ACE5707D5120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {84C09B9B-97D8-4BAE-9741-ACE5707D5120}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84C09B9B-97D8-4BAE-9741-ACE5707D5120}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {84C09B9B-97D8-4BAE-9741-ACE5707D5120}.Release|Any CPU.Build.0 = Release|Any CPU
+ {43DA07BB-DE8F-4D8F-B580-9BA75B39CFF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {43DA07BB-DE8F-4D8F-B580-9BA75B39CFF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {43DA07BB-DE8F-4D8F-B580-9BA75B39CFF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {43DA07BB-DE8F-4D8F-B580-9BA75B39CFF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {435ED217-120E-472D-A9F6-B298FFFC3ADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {435ED217-120E-472D-A9F6-B298FFFC3ADB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {435ED217-120E-472D-A9F6-B298FFFC3ADB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {435ED217-120E-472D-A9F6-B298FFFC3ADB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0F0A4F84-419F-4547-9001-0853C1E509F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0F0A4F84-419F-4547-9001-0853C1E509F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0F0A4F84-419F-4547-9001-0853C1E509F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0F0A4F84-419F-4547-9001-0853C1E509F9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5AE1B9B6-BE5D-4919-9612-7E11D384A985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5AE1B9B6-BE5D-4919-9612-7E11D384A985}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5AE1B9B6-BE5D-4919-9612-7E11D384A985}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5AE1B9B6-BE5D-4919-9612-7E11D384A985}.Release|Any CPU.Build.0 = Release|Any CPU
+ {739CAE6A-14E6-44FC-8863-DA905CBD289F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {739CAE6A-14E6-44FC-8863-DA905CBD289F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {739CAE6A-14E6-44FC-8863-DA905CBD289F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {739CAE6A-14E6-44FC-8863-DA905CBD289F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C0399352-1278-4D91-8D4E-7491FD77C18B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C0399352-1278-4D91-8D4E-7491FD77C18B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C0399352-1278-4D91-8D4E-7491FD77C18B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C0399352-1278-4D91-8D4E-7491FD77C18B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4480BFAF-C981-4242-A509-EDA6F572E45C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4480BFAF-C981-4242-A509-EDA6F572E45C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4480BFAF-C981-4242-A509-EDA6F572E45C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4480BFAF-C981-4242-A509-EDA6F572E45C}.Release|Any CPU.Build.0 = Release|Any CPU
{26611C4C-6910-498A-9FBA-BECC09392ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26611C4C-6910-498A-9FBA-BECC09392ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26611C4C-6910-498A-9FBA-BECC09392ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1294,6 +1408,27 @@ Global
{DB1C55BF-0C0D-488C-9AFC-992A3DED2EAD} = {8E978749-7972-4703-8A94-6A90080C78DE}
{F58B6EEF-5AFF-4B79-BC71-A2D8C71F5E77} = {88D17635-75D7-48A1-B622-E6FB3DCACEF8}
{D652EBF0-27CA-44C2-BB78-F446B87377C7} = {F58B6EEF-5AFF-4B79-BC71-A2D8C71F5E77}
+ {CE945F1D-6636-47D5-A619-C16C4E14CF8C} = {94CC5A11-DA0F-413C-96CA-01DB0FC426E0}
+ {A8C4583C-034E-47AF-B7EC-1A34EE288E2F} = {CE945F1D-6636-47D5-A619-C16C4E14CF8C}
+ {10C98582-61EB-49B9-9E6B-83E90CA3795D} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {BA8BAB83-D8D9-4716-BB80-C58A67C5F7A3} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {1E65087A-DAC5-4595-9E1B-1BBE6C333D7D} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {E78D741F-F071-4824-9386-E370717F7CBF} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {926BA9EC-AB2F-4E4F-8363-54426411A17C} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {878A5B7A-269F-4356-8B97-E5BC5EE6BED9} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {4EFA90C2-3EFE-4E9C-BDEB-274493EB8C9D} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {FF764D80-17E6-4DE4-92CC-591F706B39DD} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {B7944CFA-31AB-4D35-925E-847E1115BF88} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {E0ABB505-A788-4E66-A51E-94A9A0DD18FA} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8} = {CE945F1D-6636-47D5-A619-C16C4E14CF8C}
+ {84C09B9B-97D8-4BAE-9741-ACE5707D5120} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}
+ {43DA07BB-DE8F-4D8F-B580-9BA75B39CFF3} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}
+ {435ED217-120E-472D-A9F6-B298FFFC3ADB} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}
+ {0F0A4F84-419F-4547-9001-0853C1E509F9} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}
+ {5AE1B9B6-BE5D-4919-9612-7E11D384A985} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}
+ {739CAE6A-14E6-44FC-8863-DA905CBD289F} = {7087FDFF-196A-4C9F-8C66-EEBC2C49F2F8}
+ {C0399352-1278-4D91-8D4E-7491FD77C18B} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
+ {4480BFAF-C981-4242-A509-EDA6F572E45C} = {A8C4583C-034E-47AF-B7EC-1A34EE288E2F}
{867AD9F8-FD56-469D-A90D-C569EB9C3D2A} = {94CC5A11-DA0F-413C-96CA-01DB0FC426E0}
{26611C4C-6910-498A-9FBA-BECC09392ADE} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B}
{FCD72398-F832-4914-8ACF-EA4C1DD24BBF} = {F29C5BCD-E6C0-4556-A631-CACA41B1050B}
diff --git a/common.props b/common.props
index 88327577..9ea44847 100644
--- a/common.props
+++ b/common.props
@@ -1,7 +1,7 @@
latest
- 2.9.3
+ 3.0.0-preview.2
$(NoWarn);CS1591
true
EasyAbp Team
diff --git a/docs/README.md b/docs/README.md
index 826d5836..56cb754c 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -109,6 +109,7 @@ We can customize some features to use EShop in complex application scenarios.
* Inventories
* [DaprActors](https://github.com/EasyAbp/EShop/tree/dev/plugins/Inventories/DaprActors)
* [OrleansGrains](https://github.com/EasyAbp/EShop/tree/dev/plugins/Inventories/OrleansGrains)
+ * [Booking](https://github.com/EasyAbp/EShop/tree/dev/plugins/Booking)
## Roadmap
diff --git a/docs/plugins/booking/README.md b/docs/plugins/booking/README.md
new file mode 100644
index 00000000..ebf463d6
--- /dev/null
+++ b/docs/plugins/booking/README.md
@@ -0,0 +1,64 @@
+# EShop.Plugins.Booking
+
+[](https://abp.io)
+[](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Booking.Domain.Shared)
+[](https://www.nuget.org/packages/EasyAbp.EShop.Plugins.Booking.Domain.Shared)
+[](https://discord.gg/S6QaezrCRq)
+[](https://www.github.com/EasyAbp/EShop)
+
+A booking-business plugin for EShop. It extends EShop to use the [EasyAbp.BookingService](https://github.com/EasyAbp/BookingService) module to help end-users to book some assets online.
+
+## Installation
+
+1. Install the [EasyAbp.BookingService](https://github.com/EasyAbp/BookingService) module locally or remotely.
+
+1. Install the following NuGet packages. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-nuget-packages))
+
+ * EasyAbp.EShop.Orders.Booking.Application (install at EasyAbp.EShop.Orders.Application location)
+ * (Optional) EasyAbp.EShop.Payments.Booking.Application (install at EasyAbp.EShop.Payments.Application location)
+ * EasyAbp.EShop.Plugins.Booking.Application
+ * EasyAbp.EShop.Plugins.Booking.Application
+ * EasyAbp.EShop.Plugins.Booking.Application.Contracts
+ * EasyAbp.EShop.Plugins.Booking.Domain
+ * EasyAbp.EShop.Plugins.Booking.Domain.Shared
+ * EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore
+ * EasyAbp.EShop.Plugins.Booking.HttpApi
+ * EasyAbp.EShop.Plugins.Booking.HttpApi.Client
+ * (Optional) EasyAbp.EShop.Plugins.Booking.MongoDB
+ * (Optional) EasyAbp.EShop.Plugins.Booking.Web
+
+ > Skip installing the `EasyAbp.EShop.Payments.Plugins.Booking` module if you don't want to check assets are bookable during payment.
+
+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.ConfigureEShopPluginsBooking();` 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).
+
+## Usage
+
+[](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNrFlU1v2zAMhv-KoMtWIO3uxhCg6wLs1qzBbrkoEp0IlSWPkjZ4Rf_7qI84TpA2LTBgOVmU-JB8RTFPXDoFvOEefkawEr5qsUXRrS2jn4jB2dhtAMv6hwe8ns-_OPeo7XYF-EtLaNj3CDiwDVnFxgALugPWOmTCewi-uB77XBMlwRr2ACGi9awH1E75oziL1c71N_eoAH3D7hBEACZyIEIxlzbYbx12JRITVrEgcEufKYfCmkJG5tLErbb-pmbVsIX1ESn1HRTWp1yE9mNVnzc4_1iPZ1hORzt7G8POof6TF98oBQN4NQ19EuuMfheDn6lkFDCvmcziqDPyLcXQgQ0k4Iq0IZFYXyz5ilLMrONRxtXlH8lVcf9LsLGaUbJqeZNod67rDYQSrCr3Rnr1VK9Ku-_uvNortfdcBPeeJs77S6FP_S4Lei9l7If6YMvdRfNYrLfJSMSrsw_55WSSs7ByYAg-mjBSM2_cfcibB7wwYeLqo5RA56uGLxd0MiqmlzZpbzAeJvRWaPNuNHmCOYBTWWnuILQxjZ_TLgGrXplpZXJ2Q2F98MwHEaK__N574lJ6s0OXzYjBZM4OFJ_xDrCjVqDR_pRwa06p0UzkDX0qaAWpvuZr-0xHY6_oHSyUDg550wpSacbT7F8NVvImYIT9ofr3UE89_wU5QDGI)
+
+### Admins
+
+1. Define a "booking" product group. Customers can use only these configured product groups for booking.
+ ```CSharp
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.BookingProductGroups.Add(new BookingProductGroupDefinition("MyBookingProductGroup"));
+ });
+ }
+ ```
+2. Use the management pages to create [ProductAsset](https://github.com/EasyAbp/EShop/blob/dev/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAsset.cs) or [ProductAssetCategory](https://github.com/EasyAbp/EShop/blob/dev/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory.cs) entities to set prices and more information.
+
+### Customers
+
+1. Use BookingService module's `/api/booking-service/asset-occupancy/search-booking-periods` (GET) or `/api/booking-service/asset-occupancy/search-category-booking-periods` (GET) to get available periods for an asset or an asset category.
+2. Create an EShop order with these ExtraProperties:
+ * `BookingAssetId` or `BookingAssetCategoryId`.
+ * `BookingPeriodSchemeId`, `BookingPeriodId`, `BookingVolume`, `BookingDate`, `BookingStartingTime`, `BookingDuration`.
+3. Pay for the order, and then it will automatically call the BookingService module to occupy the asset.
+ * If the occupancy succeeds, it will set the order to complete.
+ * If the occupancy fails, it will cancel the order and refund the payment.
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductsConsts.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductsConsts.cs
index c64d00af..7fe691a7 100644
--- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductsConsts.cs
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.Domain.Shared/EasyAbp/EShop/Products/ProductsConsts.cs
@@ -8,11 +8,15 @@
public const string DefaultProductGroupDescription = "";
- public const string CategoryRouteBase = "/api/e-shop/products/category";
+ public const string RouteBase = "/api/e-shop/products";
- public const string GetCategorySummaryListedDataSourceUrl = CategoryRouteBase + "/summary";
+ public const string GetCategorySummaryListedDataSourceUrl = RouteBase + "/category/summary";
- public const string GetCategorySummarySingleDataSourceUrl = CategoryRouteBase + "/{id}";
+ public const string GetCategorySummarySingleDataSourceUrl = RouteBase + "/category/{id}";
+
+ public const string GetProductListedDataSourceUrl = RouteBase + "/product";
+
+ public const string GetProductSingleDataSourceUrl = RouteBase + "/product/{id}";
public const string DefaultPaymentExpireInSettingName = "EasyAbp.EShop.Products.Product.DefaultPaymentExpireIn";
}
diff --git a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.HttpApi/EasyAbp/EShop/Products/Categories/CategoryController.cs b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.HttpApi/EasyAbp/EShop/Products/Categories/CategoryController.cs
index 9b1efca7..e0cee874 100644
--- a/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.HttpApi/EasyAbp/EShop/Products/Categories/CategoryController.cs
+++ b/modules/EasyAbp.EShop.Products/src/EasyAbp.EShop.Products.HttpApi/EasyAbp/EShop/Products/Categories/CategoryController.cs
@@ -8,7 +8,7 @@ using Volo.Abp.Application.Dtos;
namespace EasyAbp.EShop.Products.Categories
{
[RemoteService(Name = EShopProductsRemoteServiceConsts.RemoteServiceName)]
- [Route(ProductsConsts.CategoryRouteBase)]
+ [Route("/api/e-shop/products/category")]
public class CategoryController : ProductsController, ICategoryAppService
{
private readonly ICategoryAppService _service;
diff --git a/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs b/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs
index 0995f97e..a7d0ad8b 100644
--- a/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs
+++ b/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs
@@ -62,7 +62,7 @@ namespace EasyAbp.EShop.Stores.Stores
where TEntity : class, IEntity, IMultiStore
where TCreateInput : IMultiStore
{
- protected virtual string CrossStorePolicyName { get; set; }
+ protected abstract string CrossStorePolicyName { get; set; }
protected MultiStoreAbstractKeyCrudAppService(IRepository repository)
: base(repository)
@@ -110,7 +110,6 @@ namespace EasyAbp.EShop.Stores.Stores
await DeleteByIdAsync(id);
}
-
protected virtual async Task CheckMultiStorePolicyAsync(Guid? storeId, string policyName, bool crossStoreAllowed = true)
{
if (crossStoreAllowed)
diff --git a/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreCrudAppService.cs b/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreCrudAppService.cs
index 352579f3..6f505821 100644
--- a/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreCrudAppService.cs
+++ b/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application.Shared/EasyAbp/EShop/Stores/Stores/MultiStoreCrudAppService.cs
@@ -79,14 +79,14 @@ namespace EasyAbp.EShop.Stores.Stores
return await Repository.GetAsync(id);
}
- protected override void MapToEntity(TUpdateInput updateInput, TEntity entity)
+ protected override Task MapToEntityAsync(TUpdateInput updateInput, TEntity entity)
{
if (updateInput is IEntityDto entityDto)
{
entityDto.Id = entity.Id;
}
- base.MapToEntity(updateInput, entity);
+ return base.MapToEntityAsync(updateInput, entity);
}
protected override IQueryable ApplyDefaultSorting(IQueryable query)
diff --git a/plugins/Booking/.gitattributes b/plugins/Booking/.gitattributes
new file mode 100644
index 00000000..c941e526
--- /dev/null
+++ b/plugins/Booking/.gitattributes
@@ -0,0 +1 @@
+**/wwwroot/libs/** linguist-vendored
diff --git a/plugins/Booking/.gitignore b/plugins/Booking/.gitignore
new file mode 100644
index 00000000..48c0cf28
--- /dev/null
+++ b/plugins/Booking/.gitignore
@@ -0,0 +1,259 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# Booking
+host/EasyAbp.EShop.Plugins.Booking.IdentityServer/Logs/logs.txt
+host/EasyAbp.EShop.Plugins.Booking.HttpApi.Host/Logs/logs.txt
+host/EasyAbp.EShop.Plugins.Booking.Web.Host/Logs/logs.txt
+host/EasyAbp.EShop.Plugins.Booking.Web.Unified/Logs/logs.txt
+host/EasyAbp.EShop.Plugins.Booking.Blazor.Server.Host/Logs/logs.txt
\ No newline at end of file
diff --git a/plugins/Booking/.prettierrc b/plugins/Booking/.prettierrc
new file mode 100644
index 00000000..56af76bd
--- /dev/null
+++ b/plugins/Booking/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "singleQuote": true,
+ "useTabs": false,
+ "tabWidth": 4
+}
diff --git a/plugins/Booking/EasyAbp.EShop.Plugins.Booking.abpmdl.json b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.abpmdl.json
new file mode 100644
index 00000000..c000ed24
--- /dev/null
+++ b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.abpmdl.json
@@ -0,0 +1,111 @@
+{
+ "folders": {
+ "items": {
+ "src": {},
+ "test": {},
+ "host": {}
+ }
+ },
+ "packages": {
+ "EasyAbp.EShop.Plugins.Booking.Domain.Shared": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Domain": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Application.Contracts": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Application": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.MongoDB": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.HttpApi": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.HttpApi.Client": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.TestBase": {
+ "path": "test/EasyAbp.EShop.Plugins.Booking.TestBase/EasyAbp.EShop.Plugins.Booking.TestBase.abppkg.json",
+ "folder": "test"
+ },
+ "EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests": {
+ "path": "test/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests.abppkg.json",
+ "folder": "test"
+ },
+ "EasyAbp.EShop.Plugins.Booking.MongoDB.Tests": {
+ "path": "test/EasyAbp.EShop.Plugins.Booking.MongoDB.Tests/EasyAbp.EShop.Plugins.Booking.MongoDB.Tests.abppkg.json",
+ "folder": "test"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Domain.Tests": {
+ "path": "test/EasyAbp.EShop.Plugins.Booking.Domain.Tests/EasyAbp.EShop.Plugins.Booking.Domain.Tests.abppkg.json",
+ "folder": "test"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Application.Tests": {
+ "path": "test/EasyAbp.EShop.Plugins.Booking.Application.Tests/EasyAbp.EShop.Plugins.Booking.Application.Tests.abppkg.json",
+ "folder": "test"
+ },
+ "EasyAbp.EShop.Plugins.Booking.HttpApi.Host": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.HttpApi.Host/EasyAbp.EShop.Plugins.Booking.HttpApi.Host.abppkg.json",
+ "folder": "host"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Web": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp": {
+ "path": "test/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp.abppkg.json",
+ "folder": "test"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Web.Host": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.Web.Host/EasyAbp.EShop.Plugins.Booking.Web.Host.abppkg.json",
+ "folder": "host"
+ },
+ "EasyAbp.EShop.Plugins.Booking.IdentityServer": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.IdentityServer/EasyAbp.EShop.Plugins.Booking.IdentityServer.abppkg.json",
+ "folder": "host"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Host.Shared": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.abppkg.json",
+ "folder": "host"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Web.Unified": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.Web.Unified/EasyAbp.EShop.Plugins.Booking.Web.Unified.abppkg.json",
+ "folder": "host"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Blazor": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Blazor/EasyAbp.EShop.Plugins.Booking.Blazor.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Blazor.Host": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.Blazor.Host/EasyAbp.EShop.Plugins.Booking.Blazor.Host.abppkg.json",
+ "folder": "host"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Blazor.Server": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Blazor.Server/EasyAbp.EShop.Plugins.Booking.Blazor.Server.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Blazor.WebAssembly": {
+ "path": "src/EasyAbp.EShop.Plugins.Booking.Blazor.WebAssembly/EasyAbp.EShop.Plugins.Booking.Blazor.WebAssembly.abppkg.json",
+ "folder": "src"
+ },
+ "EasyAbp.EShop.Plugins.Booking.Blazor.Server.Host": {
+ "path": "host/EasyAbp.EShop.Plugins.Booking.Blazor.Server.Host/EasyAbp.EShop.Plugins.Booking.Blazor.Server.Host.abppkg.json",
+ "folder": "host"
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/EasyAbp.EShop.Plugins.Booking.abpsln.json b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.abpsln.json
new file mode 100644
index 00000000..814d89ab
--- /dev/null
+++ b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.abpsln.json
@@ -0,0 +1,7 @@
+{
+ "modules": {
+ "EasyAbp.EShop.Plugins.Booking": {
+ "path": "EasyAbp.EShop.Plugins.Booking.abpmdl.json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/EasyAbp.EShop.Plugins.Booking.sln b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.sln
new file mode 100644
index 00000000..7739bb4c
--- /dev/null
+++ b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.sln
@@ -0,0 +1,146 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29001.49
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Domain.Shared", "src\EasyAbp.EShop.Plugins.Booking.Domain.Shared\EasyAbp.EShop.Plugins.Booking.Domain.Shared.csproj", "{D64C1577-4929-4B60-939E-96DE1534891A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Domain", "src\EasyAbp.EShop.Plugins.Booking.Domain\EasyAbp.EShop.Plugins.Booking.Domain.csproj", "{F2840BC7-0188-4606-9126-DADD0F5ABF7A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Application.Contracts", "src\EasyAbp.EShop.Plugins.Booking.Application.Contracts\EasyAbp.EShop.Plugins.Booking.Application.Contracts.csproj", "{BD65D04F-08D5-40C1-8C24-77CA0BACB877}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Application", "src\EasyAbp.EShop.Plugins.Booking.Application\EasyAbp.EShop.Plugins.Booking.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{649A3FFA-182F-4E56-9717-E6A9A2BEC545}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{E400416D-2895-4512-9D17-90681EEC7E0A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore", "src\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.csproj", "{0CE86223-D31D-4315-A1F5-87BA3EE1B844}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.MongoDB", "src\EasyAbp.EShop.Plugins.Booking.MongoDB\EasyAbp.EShop.Plugins.Booking.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.HttpApi", "src\EasyAbp.EShop.Plugins.Booking.HttpApi\EasyAbp.EShop.Plugins.Booking.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.HttpApi.Client", "src\EasyAbp.EShop.Plugins.Booking.HttpApi.Client\EasyAbp.EShop.Plugins.Booking.HttpApi.Client.csproj", "{36E2735F-CEAB-44C8-A6D1-2CDAFF399751}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.TestBase", "test\EasyAbp.EShop.Plugins.Booking.TestBase\EasyAbp.EShop.Plugins.Booking.TestBase.csproj", "{C5BB573D-3030-4BCB-88B7-F6A85C32766C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests", "test\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests\EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.Tests.csproj", "{527F645C-C1FC-406E-8479-81386C8ECF13}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.MongoDB.Tests", "test\EasyAbp.EShop.Plugins.Booking.MongoDB.Tests\EasyAbp.EShop.Plugins.Booking.MongoDB.Tests.csproj", "{D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Domain.Tests", "test\EasyAbp.EShop.Plugins.Booking.Domain.Tests\EasyAbp.EShop.Plugins.Booking.Domain.Tests.csproj", "{E60895E5-79C4-447D-88B7-85CB5BA336A4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Application.Tests", "test\EasyAbp.EShop.Plugins.Booking.Application.Tests\EasyAbp.EShop.Plugins.Booking.Application.Tests.csproj", "{90CB5DC4-C040-45C7-8900-9688B26405BC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Web", "src\EasyAbp.EShop.Plugins.Booking.Web\EasyAbp.EShop.Plugins.Booking.Web.csproj", "{3B7B6317-1B85-4164-8E11-75574F80AE17}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp", "test\EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp\EasyAbp.EShop.Plugins.Booking.HttpApi.Client.ConsoleTestApp.csproj", "{1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.EShop.Plugins.Booking.Host.Shared", "host\EasyAbp.EShop.Plugins.Booking.Host.Shared\EasyAbp.EShop.Plugins.Booking.Host.Shared.csproj", "{F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.EShop.Plugins.Booking.Installer", "src\EasyAbp.EShop.Plugins.Booking.Installer\EasyAbp.EShop.Plugins.Booking.Installer.csproj", "{BE39FD00-745B-4049-8161-FC129817CBE4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.Build.0 = Release|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.Build.0 = Release|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.Build.0 = Release|Any CPU
+ {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {527F645C-C1FC-406E-8479-81386C8ECF13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {527F645C-C1FC-406E-8479-81386C8ECF13}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {527F645C-C1FC-406E-8479-81386C8ECF13}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {527F645C-C1FC-406E-8479-81386C8ECF13}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {90CB5DC4-C040-45C7-8900-9688B26405BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {90CB5DC4-C040-45C7-8900-9688B26405BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {90CB5DC4-C040-45C7-8900-9688B26405BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {90CB5DC4-C040-45C7-8900-9688B26405BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B7B6317-1B85-4164-8E11-75574F80AE17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B7B6317-1B85-4164-8E11-75574F80AE17}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B7B6317-1B85-4164-8E11-75574F80AE17}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B7B6317-1B85-4164-8E11-75574F80AE17}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BE39FD00-745B-4049-8161-FC129817CBE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE39FD00-745B-4049-8161-FC129817CBE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE39FD00-745B-4049-8161-FC129817CBE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE39FD00-745B-4049-8161-FC129817CBE4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D64C1577-4929-4B60-939E-96DE1534891A} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {78040F9E-3501-4A40-82DF-00A597710F35} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {0CE86223-D31D-4315-A1F5-87BA3EE1B844} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {F1C58097-4C08-4D88-8976-6B3389391481} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {36E2735F-CEAB-44C8-A6D1-2CDAFF399751} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {C5BB573D-3030-4BCB-88B7-F6A85C32766C} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
+ {527F645C-C1FC-406E-8479-81386C8ECF13} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
+ {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
+ {E60895E5-79C4-447D-88B7-85CB5BA336A4} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
+ {90CB5DC4-C040-45C7-8900-9688B26405BC} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
+ {3B7B6317-1B85-4164-8E11-75574F80AE17} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
+ {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB} = {E400416D-2895-4512-9D17-90681EEC7E0A}
+ {BE39FD00-745B-4049-8161-FC129817CBE4} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}
+ EndGlobalSection
+EndGlobal
diff --git a/plugins/Booking/EasyAbp.EShop.Plugins.Booking.sln.DotSettings b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.sln.DotSettings
new file mode 100644
index 00000000..cb0b2c91
--- /dev/null
+++ b/plugins/Booking/EasyAbp.EShop.Plugins.Booking.sln.DotSettings
@@ -0,0 +1,23 @@
+
+ True
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ Required
+ Required
+ Required
+ Required
+ False
+ True
+ False
+ False
+ True
+ False
+ False
+ SQL
+
\ No newline at end of file
diff --git a/plugins/Booking/NuGet.Config b/plugins/Booking/NuGet.Config
new file mode 100644
index 00000000..be8a1ece
--- /dev/null
+++ b/plugins/Booking/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/README.md b/plugins/Booking/README.md
new file mode 120000
index 00000000..906661f6
--- /dev/null
+++ b/plugins/Booking/README.md
@@ -0,0 +1 @@
+../../docs/plugins/booking/README.md
\ No newline at end of file
diff --git a/plugins/Booking/docker-compose.migrations.yml b/plugins/Booking/docker-compose.migrations.yml
new file mode 100644
index 00000000..ae5abd8d
--- /dev/null
+++ b/plugins/Booking/docker-compose.migrations.yml
@@ -0,0 +1,13 @@
+version: '3.4'
+
+services:
+ migrations:
+ build:
+ context: ../../
+ dockerfile: templates/service/database/Dockerfile
+ depends_on:
+ - sqlserver
+ environment:
+ - IdentityServer_DB=Booking_Identity
+ - Booking_DB=Booking_ModuleDb
+ - SA_PASSWORD=yourStrong(!)Password
diff --git a/plugins/Booking/docker-compose.override.yml b/plugins/Booking/docker-compose.override.yml
new file mode 100644
index 00000000..ffdb5bbd
--- /dev/null
+++ b/plugins/Booking/docker-compose.override.yml
@@ -0,0 +1,29 @@
+version: '3.4'
+
+services:
+ sqlserver:
+ environment:
+ - SA_PASSWORD=yourStrong(!)Password
+ - ACCEPT_EULA=Y
+ ports:
+ - "51599:1433"
+
+ identity-server:
+ environment:
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionStrings__Default=Server=sqlserver;Database=Booking_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ - ConnectionStrings__SqlServerCache=Server=sqlserver;Database=Booking_Cache;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ ports:
+ - "51600:80"
+
+ booking:
+ environment:
+ - ASPNETCORE_URLS=http://0.0.0.0:80
+ - ConnectionStrings__Default=Server=sqlserver;Database=Booking_ModuleDb;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ - ConnectionStrings__AbpSettingManagement=Server=sqlserver;Database=Booking_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ - ConnectionStrings__AbpPermissionManagement=Server=sqlserver;Database=Booking_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ - ConnectionStrings__AbpAuditLogging=Server=sqlserver;Database=Booking_Identity;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ - ConnectionStrings__SqlServerCache=Server=sqlserver;Database=Booking_Cache;Trusted_Connection=True;User=sa;Password=yourStrong(!)Password;Integrated Security=false
+ - AuthServer__Authority=http://identity-server
+ ports:
+ - "51601:80"
\ No newline at end of file
diff --git a/plugins/Booking/docker-compose.yml b/plugins/Booking/docker-compose.yml
new file mode 100644
index 00000000..de6ce679
--- /dev/null
+++ b/plugins/Booking/docker-compose.yml
@@ -0,0 +1,25 @@
+version: '3.4'
+
+services:
+ sqlserver:
+ image: mcr.microsoft.com/mssql/server
+ volumes:
+ - dbdata:/var/opt/mssql
+
+ identity-server:
+ build:
+ context: ../../
+ dockerfile: templates/service/host/IdentityServerHost/Dockerfile
+ depends_on:
+ - sqlserver
+
+ booking:
+ build:
+ context: ../../
+ dockerfile: templates/service/host/EasyAbp.EShop.Plugins.Booking.Host/Dockerfile
+ depends_on:
+ - sqlserver
+ - identity-server
+
+volumes:
+ dbdata:
\ No newline at end of file
diff --git a/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.abppkg.json b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.abppkg.json
new file mode 100644
index 00000000..9e26dfee
--- /dev/null
+++ b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.abppkg.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.csproj b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.csproj
new file mode 100644
index 00000000..1b005456
--- /dev/null
+++ b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/EasyAbp.EShop.Plugins.Booking.Host.Shared.csproj
@@ -0,0 +1,10 @@
+
+
+
+
+
+ netstandard2.0
+ EasyAbp.EShop.Plugins.Booking
+
+
+
diff --git a/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/FodyWeavers.xml b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/FodyWeavers.xsd b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/MultiTenancy/MultiTenancyConsts.cs b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/MultiTenancy/MultiTenancyConsts.cs
new file mode 100644
index 00000000..2170d762
--- /dev/null
+++ b/plugins/Booking/host/EasyAbp.EShop.Plugins.Booking.Host.Shared/MultiTenancy/MultiTenancyConsts.cs
@@ -0,0 +1,9 @@
+namespace EasyAbp.EShop.Plugins.Booking.MultiTenancy;
+
+public static class MultiTenancyConsts
+{
+ /* Enable/disable multi-tenancy in a single point
+ * to test your module with multi-tenancy.
+ */
+ public const bool IsEnabled = false;
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp.EShop.Orders.Booking.Application.csproj b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp.EShop.Orders.Booking.Application.csproj
new file mode 100644
index 00000000..2ba5950b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp.EShop.Orders.Booking.Application.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/Authorization/BookingOrderCreationAuthorizationHandler.cs b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/Authorization/BookingOrderCreationAuthorizationHandler.cs
new file mode 100644
index 00000000..d678cb18
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/Authorization/BookingOrderCreationAuthorizationHandler.cs
@@ -0,0 +1,231 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using EasyAbp.BookingService.AssetOccupancies;
+using EasyAbp.BookingService.AssetOccupancies.Dtos;
+using EasyAbp.BookingService.AssetOccupancyProviders;
+using EasyAbp.BookingService.PeriodSchemes;
+using EasyAbp.EShop.Orders.Orders;
+using EasyAbp.EShop.Orders.Orders.Dtos;
+using EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using Microsoft.AspNetCore.Authorization;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Orders.Booking.Authorization
+{
+ public class BookingOrderCreationAuthorizationHandler : OrderCreationAuthorizationHandler
+ {
+ private readonly IPeriodSchemeAppService _periodSchemeAppService;
+ private readonly IProductAssetAppService _productAssetAppService;
+ private readonly IGrantedStoreAppService _grantedStoreAppService;
+ private readonly IProductAssetCategoryAppService _productAssetCategoryAppService;
+ private readonly IAssetOccupancyAppService _assetOccupancyAppService;
+ private readonly IBookingProductGroupDefinitionAppService _definitionAppService;
+
+ public BookingOrderCreationAuthorizationHandler(
+ IPeriodSchemeAppService periodSchemeAppService,
+ IProductAssetAppService productAssetAppService,
+ IGrantedStoreAppService grantedStoreAppService,
+ IProductAssetCategoryAppService productAssetCategoryAppService,
+ IAssetOccupancyAppService assetOccupancyAppService,
+ IBookingProductGroupDefinitionAppService definitionAppService)
+ {
+ _periodSchemeAppService = periodSchemeAppService;
+ _productAssetAppService = productAssetAppService;
+ _grantedStoreAppService = grantedStoreAppService;
+ _productAssetCategoryAppService = productAssetCategoryAppService;
+ _assetOccupancyAppService = assetOccupancyAppService;
+ _definitionAppService = definitionAppService;
+ }
+
+ protected override async Task HandleOrderCreationAsync(AuthorizationHandlerContext context,
+ OrderOperationAuthorizationRequirement requirement, OrderCreationResource resource)
+ {
+ var productGroupNames = (await _definitionAppService.GetListAsync()).Items.Select(x => x.ProductGroupName);
+
+ var bookingOrderLines = resource.Input.OrderLines.Where(x =>
+ productGroupNames.Contains(resource.ProductDictionary[x.ProductId].ProductGroupName)).ToList();
+
+ if (!bookingOrderLines.Any())
+ {
+ return;
+ }
+
+ var models = new List();
+ var byCategoryModels = new List();
+
+ foreach (var orderLine in bookingOrderLines)
+ {
+ if (!await IsPeriodInfoValidAsync(orderLine))
+ {
+ context.Fail();
+ return;
+ }
+
+ var assetId = orderLine.FindBookingAssetId();
+ var assetCategoryId = orderLine.FindBookingAssetCategoryId();
+
+ if (assetId is not null)
+ {
+ if (!await IsAssetInfoValidAsync(orderLine, resource))
+ {
+ context.Fail();
+ return;
+ }
+
+ models.Add(CreateOccupyAssetInfoModel(assetId.Value, orderLine));
+ }
+ else if (assetCategoryId is not null)
+ {
+ if (!await IsAssetCategoryInfoValidAsync(orderLine, resource))
+ {
+ context.Fail();
+ return;
+ }
+
+ byCategoryModels.Add(CreateOccupyAssetByCategoryInfoModel(assetCategoryId.Value, orderLine));
+ }
+ else
+ {
+ context.Fail();
+ return;
+ }
+ }
+
+ try
+ {
+ await _assetOccupancyAppService.CheckBulkCreateAsync(new BulkCreateAssetOccupancyDto
+ {
+ OccupierUserId = Check.NotNull(context.User.FindUserId(), "CurrentUserId"),
+ Models = models,
+ ByCategoryModels = byCategoryModels
+ });
+ }
+ catch
+ {
+ context.Fail();
+ return;
+ }
+ }
+
+ protected virtual OccupyAssetInfoModel CreateOccupyAssetInfoModel(Guid assetId, CreateOrderLineDto orderLine)
+ {
+ return new OccupyAssetInfoModel(
+ assetId,
+ orderLine.GetBookingVolume(),
+ orderLine.GetBookingDate(),
+ orderLine.GetBookingStartingTime(),
+ orderLine.GetBookingDuration()
+ );
+ }
+
+ protected virtual OccupyAssetByCategoryInfoModel CreateOccupyAssetByCategoryInfoModel(Guid assetCategoryId,
+ CreateOrderLineDto orderLine)
+ {
+ return new OccupyAssetByCategoryInfoModel(
+ assetCategoryId,
+ orderLine.GetBookingVolume(),
+ orderLine.GetBookingDate(),
+ orderLine.GetBookingStartingTime(),
+ orderLine.GetBookingDuration()
+ );
+ }
+
+ protected virtual async Task IsAssetInfoValidAsync(CreateOrderLineDto orderLine,
+ OrderCreationResource resource)
+ {
+ var mapping = (await _grantedStoreAppService.GetListAsync(new GetGrantedStoreListDto
+ {
+ MaxResultCount = 1,
+ StoreId = resource.Input.StoreId,
+ AssetId = orderLine.GetBookingAssetId()
+ })).Items.FirstOrDefault();
+
+ if (mapping is null)
+ {
+ mapping = (await _grantedStoreAppService.GetListAsync(new GetGrantedStoreListDto
+ {
+ MaxResultCount = 1,
+ AllowAll = true
+ })).Items.FirstOrDefault();
+ }
+
+ if (mapping is null)
+ {
+ return false;
+ }
+
+ var productAsset = (await _productAssetAppService.GetListAsync(
+ new GetProductAssetListDto
+ {
+ MaxResultCount = 1,
+ StoreId = resource.Input.StoreId,
+ ProductId = orderLine.ProductId,
+ ProductSkuId = orderLine.ProductSkuId,
+ AssetId = orderLine.GetBookingAssetId(),
+ PeriodSchemeId = orderLine.GetBookingPeriodSchemeId()
+ }
+ )).Items.FirstOrDefault();
+
+ return productAsset is not null;
+ }
+
+ protected virtual async Task IsAssetCategoryInfoValidAsync(CreateOrderLineDto orderLine,
+ OrderCreationResource resource)
+ {
+ var mapping = (await _grantedStoreAppService.GetListAsync(new GetGrantedStoreListDto
+ {
+ MaxResultCount = 1,
+ StoreId = resource.Input.StoreId,
+ AssetCategoryId = orderLine.GetBookingAssetCategoryId()
+ })).Items.FirstOrDefault();
+
+ if (mapping is null)
+ {
+ mapping = (await _grantedStoreAppService.GetListAsync(new GetGrantedStoreListDto
+ {
+ MaxResultCount = 1,
+ AllowAll = true
+ })).Items.FirstOrDefault();
+ }
+
+ if (mapping is null)
+ {
+ return false;
+ }
+
+ var productAssetCategory = (await _productAssetCategoryAppService.GetListAsync(
+ new GetProductAssetCategoryListDto
+ {
+ MaxResultCount = 1,
+ StoreId = resource.Input.StoreId,
+ ProductId = orderLine.ProductId,
+ ProductSkuId = orderLine.ProductSkuId,
+ AssetCategoryId = orderLine.GetBookingAssetCategoryId(),
+ PeriodSchemeId = orderLine.GetBookingPeriodSchemeId()
+ }
+ )).Items.FirstOrDefault();
+
+ return productAssetCategory is not null;
+ }
+
+ protected virtual async Task IsPeriodInfoValidAsync(CreateOrderLineDto orderLine)
+ {
+ var periodSchemeId = orderLine.GetBookingPeriodSchemeId();
+ var periodId = orderLine.GetBookingPeriodId();
+
+ var periodScheme = await _periodSchemeAppService.GetAsync(periodSchemeId);
+ var period = periodScheme.Periods.Find(x => x.Id == periodId);
+
+ return period is not null;
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/BookingOrderConsts.cs b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/BookingOrderConsts.cs
new file mode 100644
index 00000000..fa2ff8db
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/BookingOrderConsts.cs
@@ -0,0 +1,6 @@
+namespace EasyAbp.EShop.Orders.Booking;
+
+public class BookingOrderConsts
+{
+ public static string BookingOrderAutoCancellationResult = "Asset occupancy failed";
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/BookingOrderLinePriceOverrider.cs b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/BookingOrderLinePriceOverrider.cs
new file mode 100644
index 00000000..49af4375
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/BookingOrderLinePriceOverrider.cs
@@ -0,0 +1,123 @@
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Orders.Orders;
+using EasyAbp.EShop.Orders.Orders.Dtos;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using EasyAbp.EShop.Products.Products.Dtos;
+using NodaMoney;
+using Volo.Abp.DependencyInjection;
+
+namespace EasyAbp.EShop.Orders.Booking;
+
+public class BookingOrderLinePriceOverrider : IOrderLinePriceOverrider, ITransientDependency
+{
+ private readonly IProductAssetAppService _productAssetAppService;
+ private readonly IProductAssetCategoryAppService _productAssetCategoryAppService;
+
+ public BookingOrderLinePriceOverrider(
+ IProductAssetAppService productAssetAppService,
+ IProductAssetCategoryAppService productAssetCategoryAppService)
+ {
+ _productAssetAppService = productAssetAppService;
+ _productAssetCategoryAppService = productAssetCategoryAppService;
+ }
+
+ public virtual async Task GetUnitPriceOrNullAsync(CreateOrderDto input, CreateOrderLineDto inputOrderLine,
+ ProductDto product, ProductSkuDto productSku, Currency effectiveCurrency)
+ {
+ if (inputOrderLine.FindBookingAssetId() is not null)
+ {
+ return await GetAssetBookingUnitPriceAsync(input, inputOrderLine, effectiveCurrency);
+ }
+
+ if (inputOrderLine.FindBookingAssetCategoryId() is not null)
+ {
+ return await GetAssetCategoryBookingUnitPriceAsync(input, inputOrderLine, effectiveCurrency);
+ }
+
+ return null;
+ }
+
+ public virtual async Task GetAssetBookingUnitPriceAsync(CreateOrderDto input,
+ CreateOrderLineDto inputOrderLine, Currency effectiveCurrency)
+ {
+ var productAsset = (await _productAssetAppService.GetListAsync(
+ new GetProductAssetListDto
+ {
+ MaxResultCount = 1,
+ StoreId = input.StoreId,
+ ProductId = inputOrderLine.ProductId,
+ ProductSkuId = inputOrderLine.ProductSkuId,
+ AssetId = inputOrderLine.GetBookingAssetId(),
+ PeriodSchemeId = inputOrderLine.GetBookingPeriodSchemeId()
+ }
+ )).Items.First();
+
+ var productAssetPeriod =
+ productAsset.Periods.FirstOrDefault(x => x.PeriodId == inputOrderLine.GetBookingPeriodId());
+
+ if (productAssetPeriod is not null)
+ {
+ await CheckCurrencyAsync(productAssetPeriod.Currency, effectiveCurrency);
+
+ return new Money(productAssetPeriod.Price, effectiveCurrency);
+ }
+
+ if (productAsset.Price.HasValue)
+ {
+ await CheckCurrencyAsync(productAsset.Currency, effectiveCurrency);
+
+ return new Money(productAsset.Price.Value, effectiveCurrency);
+ }
+
+ return null;
+ }
+
+ public virtual async Task GetAssetCategoryBookingUnitPriceAsync(CreateOrderDto input,
+ CreateOrderLineDto inputOrderLine, Currency effectiveCurrency)
+ {
+ var productAssetCategory = (await _productAssetCategoryAppService.GetListAsync(
+ new GetProductAssetCategoryListDto
+ {
+ MaxResultCount = 1,
+ StoreId = input.StoreId,
+ ProductId = inputOrderLine.ProductId,
+ ProductSkuId = inputOrderLine.ProductSkuId,
+ AssetCategoryId = inputOrderLine.GetBookingAssetCategoryId(),
+ PeriodSchemeId = inputOrderLine.GetBookingPeriodSchemeId()
+ }
+ )).Items.First();
+
+ var productAssetCategoryPeriod =
+ productAssetCategory.Periods.FirstOrDefault(x => x.PeriodId == inputOrderLine.GetBookingPeriodId());
+
+ if (productAssetCategoryPeriod is not null)
+ {
+ await CheckCurrencyAsync(productAssetCategoryPeriod.Currency, effectiveCurrency);
+
+ return new Money(productAssetCategoryPeriod.Price, effectiveCurrency);
+ }
+
+ if (productAssetCategory.Price.HasValue)
+ {
+ await CheckCurrencyAsync(productAssetCategory.Currency, effectiveCurrency);
+
+ return new Money(productAssetCategory.Price.Value, effectiveCurrency);
+ }
+
+ return null;
+ }
+
+ protected virtual Task CheckCurrencyAsync(string currency, Currency effectiveCurrency)
+ {
+ if (currency != effectiveCurrency.Code)
+ {
+ throw new UnexpectedCurrencyException(effectiveCurrency.Code);
+ }
+
+ return Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/EShopOrdersBookingApplicationModule.cs b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/EShopOrdersBookingApplicationModule.cs
new file mode 100644
index 00000000..3409275b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/EShopOrdersBookingApplicationModule.cs
@@ -0,0 +1,23 @@
+using EasyAbp.BookingService;
+using EasyAbp.EShop.Orders.Booking.Authorization;
+using EasyAbp.EShop.Orders.Booking.ObjectExtending;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Orders.Booking
+{
+ [DependsOn(
+ typeof(EShopOrdersApplicationModule),
+ typeof(EShopOrdersApplicationContractsModule),
+ typeof(BookingServiceApplicationContractsModule)
+ )]
+ public class EShopOrdersBookingApplicationModule : AbpModule
+ {
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ EShopOrdersPluginsBookingObjectExtensions.Configure();
+ context.Services.AddSingleton();
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/EventHandlers/BulkAssetOccupancyResultEventHandler.cs b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/EventHandlers/BulkAssetOccupancyResultEventHandler.cs
new file mode 100644
index 00000000..cfeca034
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/EventHandlers/BulkAssetOccupancyResultEventHandler.cs
@@ -0,0 +1,80 @@
+using System.Threading.Tasks;
+using EasyAbp.BookingService.AssetOccupancies;
+using EasyAbp.BookingService.AssetOccupancyProviders;
+using EasyAbp.EShop.Orders.Orders;
+using EasyAbp.EShop.Payments.Refunds;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.Uow;
+
+namespace EasyAbp.EShop.Orders.Booking.EventHandlers;
+
+public class BulkAssetOccupancyResultEventHandler : IDistributedEventHandler, ITransientDependency
+{
+ private readonly IOrderManager _orderManager;
+ private readonly IOrderRepository _orderRepository;
+ private readonly IDistributedEventBus _distributedEventBus;
+
+ public BulkAssetOccupancyResultEventHandler(
+ IOrderManager orderManager,
+ IOrderRepository orderRepository,
+ IDistributedEventBus distributedEventBus)
+ {
+ _orderManager = orderManager;
+ _orderRepository = orderRepository;
+ _distributedEventBus = distributedEventBus;
+ }
+
+ [UnitOfWork(true)]
+ public virtual async Task HandleEventAsync(BulkAssetOccupancyResultEto eventData)
+ {
+ var orderId = eventData.FindBookingOrderId();
+
+ if (orderId is null)
+ {
+ return;
+ }
+
+ var order = await _orderRepository.GetAsync(orderId.Value);
+
+ if (eventData.Success)
+ {
+ if (order.CompletionTime.HasValue)
+ {
+ return;
+ }
+
+ await _orderManager.CompleteAsync(order);
+ }
+ else
+ {
+ if (order.CanceledTime.HasValue)
+ {
+ return;
+ }
+
+ await _orderManager.CancelAsync(order, BookingOrderConsts.BookingOrderAutoCancellationResult);
+
+ await TryRefundOrderAsync(order);
+ }
+ }
+
+ protected virtual async Task TryRefundOrderAsync(Order order)
+ {
+ if (order.PaymentId is null)
+ {
+ return;
+ }
+
+ var eto = new RefundOrderEto(
+ order.TenantId,
+ order.Id,
+ order.StoreId,
+ order.PaymentId.Value,
+ BookingOrderConsts.BookingOrderAutoCancellationResult,
+ BookingOrderConsts.BookingOrderAutoCancellationResult,
+ BookingOrderConsts.BookingOrderAutoCancellationResult);
+
+ await _distributedEventBus.PublishAsync(eto);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/ObjectExtending/EShopOrdersPluginsBookingObjectExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/ObjectExtending/EShopOrdersPluginsBookingObjectExtensions.cs
new file mode 100644
index 00000000..a0514863
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/EasyAbp/EShop/Orders/Booking/ObjectExtending/EShopOrdersPluginsBookingObjectExtensions.cs
@@ -0,0 +1,66 @@
+using System;
+using EasyAbp.EShop.Orders.Orders;
+using EasyAbp.EShop.Orders.Orders.Dtos;
+using Volo.Abp.ObjectExtending;
+using Volo.Abp.Threading;
+
+namespace EasyAbp.EShop.Orders.Booking.ObjectExtending
+{
+ public static class EShopOrdersPluginsBookingObjectExtensions
+ {
+ 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("Title");
+ *
+ * See the documentation for more:
+ * https://docs.abp.io/en/abp/latest/Object-Extensions
+ */
+
+ ObjectExtensionManager.Instance
+ .AddOrUpdate(
+ new[]
+ {
+ typeof(OrderLine),
+ typeof(OrderLineDto),
+ typeof(OrderLineEto),
+ typeof(CreateOrderLineDto)
+ },
+ config =>
+ {
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingAssetId);
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingAssetCategoryId);
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingPeriodSchemeId);
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingPeriodId);
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingDate);
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingStartingTime);
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingDuration);
+ }
+ )
+ .AddOrUpdate(
+ new[]
+ {
+ typeof(OrderLine),
+ typeof(OrderLineDto),
+ typeof(OrderLineEto)
+ },
+ config =>
+ {
+ config.AddOrUpdateProperty(BookingOrderProperties.OrderLineBookingAssetOccupancyId);
+ });
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Orders.Booking.Application/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp.EShop.Payments.Booking.Application.csproj b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp.EShop.Payments.Booking.Application.csproj
new file mode 100644
index 00000000..53f74395
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp.EShop.Payments.Booking.Application.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp/EShop/Payments/Booking/Authorization/BookingPaymentCreationAuthorizationHandler.cs b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp/EShop/Payments/Booking/Authorization/BookingPaymentCreationAuthorizationHandler.cs
new file mode 100644
index 00000000..649b3e00
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp/EShop/Payments/Booking/Authorization/BookingPaymentCreationAuthorizationHandler.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using EasyAbp.BookingService.AssetOccupancies;
+using EasyAbp.BookingService.AssetOccupancies.Dtos;
+using EasyAbp.BookingService.AssetOccupancyProviders;
+using EasyAbp.BookingService.PeriodSchemes;
+using EasyAbp.EShop.Orders;
+using EasyAbp.EShop.Orders.Orders.Dtos;
+using EasyAbp.EShop.Payments.Payments;
+using Microsoft.AspNetCore.Authorization;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Payments.Booking.Authorization
+{
+ public class BookingPaymentCreationAuthorizationHandler : PaymentCreationAuthorizationHandler
+ {
+ private readonly IPeriodSchemeAppService _periodSchemeAppService;
+ private readonly IAssetOccupancyAppService _assetOccupancyAppService;
+
+ public BookingPaymentCreationAuthorizationHandler(
+ IPeriodSchemeAppService periodSchemeAppService,
+ IAssetOccupancyAppService assetOccupancyAppService)
+ {
+ _periodSchemeAppService = periodSchemeAppService;
+ _assetOccupancyAppService = assetOccupancyAppService;
+ }
+
+ protected override async Task HandlePaymentCreationAsync(AuthorizationHandlerContext context,
+ PaymentOperationAuthorizationRequirement requirement, PaymentCreationResource resource)
+ {
+ foreach (var order in resource.Orders)
+ {
+ if (await IsBookingOrderValidAsync(context, order))
+ {
+ continue;
+ }
+
+ context.Fail();
+ return;
+ }
+ }
+
+ protected virtual async Task IsBookingOrderValidAsync(AuthorizationHandlerContext context, OrderDto order)
+ {
+ var bookingOrderLines = order.OrderLines.Where(x => x.FindBookingAssetId() is not null).ToList();
+
+ if (!bookingOrderLines.Any())
+ {
+ return true;
+ }
+
+ var models = new List();
+ var byCategoryModels = new List();
+
+ foreach (var orderLine in bookingOrderLines)
+ {
+ if (!await IsPeriodInfoValidAsync(orderLine))
+ {
+ return false;
+ }
+
+ var assetId = orderLine.FindBookingAssetId();
+ var assetCategoryId = orderLine.FindBookingAssetCategoryId();
+
+ if (assetId is not null)
+ {
+ models.Add(CreateOccupyAssetInfoModel(assetId.Value, orderLine));
+ }
+ else if (assetCategoryId is not null)
+ {
+ byCategoryModels.Add(CreateOccupyAssetByCategoryInfoModel(assetCategoryId.Value, orderLine));
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ try
+ {
+ await _assetOccupancyAppService.CheckBulkCreateAsync(new BulkCreateAssetOccupancyDto
+ {
+ OccupierUserId = Check.NotNull(context.User.FindUserId(), "CurrentUserId"),
+ Models = models,
+ ByCategoryModels = byCategoryModels
+ });
+ }
+ catch
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected virtual OccupyAssetInfoModel CreateOccupyAssetInfoModel(Guid assetId, OrderLineDto orderLine)
+ {
+ return new OccupyAssetInfoModel(
+ assetId,
+ orderLine.GetBookingVolume(),
+ orderLine.GetBookingDate(),
+ orderLine.GetBookingStartingTime(),
+ orderLine.GetBookingDuration()
+ );
+ }
+
+ protected virtual OccupyAssetByCategoryInfoModel CreateOccupyAssetByCategoryInfoModel(Guid assetCategoryId,
+ OrderLineDto orderLine)
+ {
+ return new OccupyAssetByCategoryInfoModel(
+ assetCategoryId,
+ orderLine.GetBookingVolume(),
+ orderLine.GetBookingDate(),
+ orderLine.GetBookingStartingTime(),
+ orderLine.GetBookingDuration()
+ );
+ }
+
+ protected virtual async Task IsPeriodInfoValidAsync(OrderLineDto orderLine)
+ {
+ var periodSchemeId = orderLine.GetBookingPeriodSchemeId();
+ var periodId = orderLine.GetBookingPeriodId();
+
+ var periodScheme = await _periodSchemeAppService.GetAsync(periodSchemeId);
+ var period = periodScheme.Periods.Find(x => x.Id == periodId);
+
+ return period is not null;
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp/EShop/Payments/Booking/EShopPaymentsBookingApplicationModule.cs b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp/EShop/Payments/Booking/EShopPaymentsBookingApplicationModule.cs
new file mode 100644
index 00000000..14b356ab
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/EasyAbp/EShop/Payments/Booking/EShopPaymentsBookingApplicationModule.cs
@@ -0,0 +1,22 @@
+using EasyAbp.BookingService;
+using EasyAbp.EShop.Payments.Booking.Authorization;
+using EasyAbp.EShop.Plugins.Booking;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Payments.Booking
+{
+ [DependsOn(
+ typeof(EShopPaymentsApplicationModule),
+ typeof(EShopPluginsBookingApplicationContractsModule),
+ typeof(BookingServiceApplicationContractsModule)
+ )]
+ public class EShopPaymentsBookingApplicationModule : AbpModule
+ {
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddSingleton();
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Payments.Booking.Application/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.abppkg.json
new file mode 100644
index 00000000..49032794
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.application-contracts"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.csproj
new file mode 100644
index 00000000..c4ac441a
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp.EShop.Plugins.Booking.Application.Contracts.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkAssetOccupancyResultEtoExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkAssetOccupancyResultEtoExtensions.cs
new file mode 100644
index 00000000..81211843
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkAssetOccupancyResultEtoExtensions.cs
@@ -0,0 +1,13 @@
+using System;
+using EasyAbp.BookingService.AssetOccupancyProviders;
+using Volo.Abp.Data;
+
+namespace EasyAbp.BookingService.AssetOccupancies;
+
+public static class BulkAssetOccupancyResultEtoExtensions
+{
+ public static Guid? FindBookingOrderId(this BulkAssetOccupancyResultEto eto)
+ {
+ return eto.GetProperty(BulkAssetOccupancyResultEtoProperties.BookingOrderId);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkAssetOccupancyResultEtoProperties.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkAssetOccupancyResultEtoProperties.cs
new file mode 100644
index 00000000..caf7643b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkAssetOccupancyResultEtoProperties.cs
@@ -0,0 +1,6 @@
+namespace EasyAbp.BookingService.AssetOccupancies;
+
+public class BulkAssetOccupancyResultEtoProperties
+{
+ public const string BookingOrderId = "BookingOrderId";
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkOccupyAssetEtoExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkOccupyAssetEtoExtensions.cs
new file mode 100644
index 00000000..316ec7f9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkOccupyAssetEtoExtensions.cs
@@ -0,0 +1,18 @@
+using System;
+using EasyAbp.BookingService.AssetOccupancyProviders;
+using Volo.Abp.Data;
+
+namespace EasyAbp.BookingService.AssetOccupancies;
+
+public static class BulkOccupyAssetEtoExtensions
+{
+ public static Guid? FindBookingOrderId(this BulkOccupyAssetEto eto)
+ {
+ return eto.GetProperty(BulkOccupyAssetEtoProperties.BookingOrderId);
+ }
+
+ public static void SetBookingOrderId(this BulkOccupyAssetEto eto, Guid? value)
+ {
+ eto.SetProperty(BulkOccupyAssetEtoProperties.BookingOrderId, value);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkOccupyAssetEtoProperties.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkOccupyAssetEtoProperties.cs
new file mode 100644
index 00000000..e32655b7
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/BookingService/AssetOccupancies/BulkOccupyAssetEtoProperties.cs
@@ -0,0 +1,6 @@
+namespace EasyAbp.BookingService.AssetOccupancies;
+
+public class BulkOccupyAssetEtoProperties
+{
+ public const string BookingOrderId = "BookingOrderId";
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/BookingOrderProperties.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/BookingOrderProperties.cs
new file mode 100644
index 00000000..aa3cf141
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/BookingOrderProperties.cs
@@ -0,0 +1,22 @@
+namespace EasyAbp.EShop.Orders;
+
+public class BookingOrderProperties
+{
+ public const string OrderLineBookingAssetId = "BookingAssetId";
+
+ public const string OrderLineBookingAssetCategoryId = "BookingAssetCategoryId";
+
+ public const string OrderLineBookingPeriodSchemeId = "BookingPeriodSchemeId";
+
+ public const string OrderLineBookingPeriodId = "BookingPeriodId";
+
+ public const string OrderLineBookingVolume = "BookingVolume";
+
+ public const string OrderLineBookingDate = "BookingDate";
+
+ public const string OrderLineBookingStartingTime = "BookingStartingTime";
+
+ public const string OrderLineBookingDuration = "BookingDuration";
+
+ public const string OrderLineBookingAssetOccupancyId = "BookingAssetOccupancyId";
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/CreateOrderLineDtoExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/CreateOrderLineDtoExtensions.cs
new file mode 100644
index 00000000..9dc313c3
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/CreateOrderLineDtoExtensions.cs
@@ -0,0 +1,97 @@
+using System;
+using EasyAbp.EShop.Orders.Orders.Dtos;
+using Volo.Abp;
+using Volo.Abp.Data;
+
+namespace EasyAbp.EShop.Orders;
+
+public static class CreateOrderLineDtoExtensions
+{
+ public static Guid? FindBookingAssetId(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingAssetId);
+ }
+
+ public static Guid GetBookingAssetId(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingAssetId(orderLine),
+ BookingOrderProperties.OrderLineBookingAssetId)!.Value;
+ }
+
+ public static Guid? FindBookingAssetCategoryId(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingAssetCategoryId);
+ }
+
+ public static Guid GetBookingAssetCategoryId(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingAssetCategoryId(orderLine),
+ BookingOrderProperties.OrderLineBookingAssetCategoryId)!.Value;
+ }
+
+ public static Guid? FindBookingPeriodSchemeId(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingPeriodSchemeId);
+ }
+
+ public static Guid GetBookingPeriodSchemeId(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingPeriodSchemeId(orderLine),
+ BookingOrderProperties.OrderLineBookingPeriodSchemeId)!.Value;
+ }
+
+ public static Guid? FindBookingPeriodId(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingPeriodId);
+ }
+
+ public static Guid GetBookingPeriodId(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingPeriodId(orderLine),
+ BookingOrderProperties.OrderLineBookingPeriodId)!.Value;
+ }
+
+ public static int? FindBookingVolume(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.Quantity;
+ }
+
+ public static int GetBookingVolume(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingVolume(orderLine),
+ BookingOrderProperties.OrderLineBookingVolume)!.Value;
+ }
+
+ public static DateTime? FindBookingDate(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.FindDateTimeProperty(BookingOrderProperties.OrderLineBookingDate);
+ }
+
+ public static DateTime GetBookingDate(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingDate(orderLine),
+ BookingOrderProperties.OrderLineBookingDate)!.Value;
+ }
+
+ public static TimeSpan? FindBookingStartingTime(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.FindTimeSpanProperty(BookingOrderProperties.OrderLineBookingStartingTime);
+ }
+
+ public static TimeSpan GetBookingStartingTime(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingStartingTime(orderLine),
+ BookingOrderProperties.OrderLineBookingStartingTime)!.Value;
+ }
+
+ public static TimeSpan? FindBookingDuration(this CreateOrderLineDto orderLine)
+ {
+ return orderLine.FindTimeSpanProperty(BookingOrderProperties.OrderLineBookingDuration);
+ }
+
+ public static TimeSpan GetBookingDuration(this CreateOrderLineDto orderLine)
+ {
+ return Check.NotNull(FindBookingDuration(orderLine),
+ BookingOrderProperties.OrderLineBookingDuration)!.Value;
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/HasExtraPropertiesExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/HasExtraPropertiesExtensions.cs
new file mode 100644
index 00000000..02318b4e
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/HasExtraPropertiesExtensions.cs
@@ -0,0 +1,31 @@
+using System;
+using Volo.Abp.Data;
+
+namespace EasyAbp.EShop.Orders;
+
+public static class HasExtraPropertiesExtensions
+{
+ public static DateTime? FindDateTimeProperty(this IHasExtraProperties extraProperties, string name)
+ {
+ var objValue = extraProperties.GetProperty(name);
+
+ return objValue switch
+ {
+ null => null,
+ DateTime span => span,
+ _ => DateTime.Parse((string)objValue)
+ };
+ }
+
+ public static TimeSpan? FindTimeSpanProperty(this IHasExtraProperties extraProperties, string name)
+ {
+ var objValue = extraProperties.GetProperty(name);
+
+ return objValue switch
+ {
+ null => null,
+ TimeSpan span => span,
+ _ => TimeSpan.Parse((string)objValue)
+ };
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/OrderLineExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/OrderLineExtensions.cs
new file mode 100644
index 00000000..c5cb1722
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Orders/OrderLineExtensions.cs
@@ -0,0 +1,97 @@
+using System;
+using EasyAbp.EShop.Orders.Orders;
+using Volo.Abp;
+using Volo.Abp.Data;
+
+namespace EasyAbp.EShop.Orders;
+
+public static class OrderLineExtensions
+{
+ public static Guid? FindBookingAssetId(this IOrderLine orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingAssetId);
+ }
+
+ public static Guid GetBookingAssetId(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingAssetId(orderLine),
+ BookingOrderProperties.OrderLineBookingAssetId)!.Value;
+ }
+
+ public static Guid? FindBookingAssetCategoryId(this IOrderLine orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingAssetCategoryId);
+ }
+
+ public static Guid GetBookingAssetCategoryId(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingAssetCategoryId(orderLine),
+ BookingOrderProperties.OrderLineBookingAssetCategoryId)!.Value;
+ }
+
+ public static Guid? FindBookingPeriodSchemeId(this IOrderLine orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingPeriodSchemeId);
+ }
+
+ public static Guid GetBookingPeriodSchemeId(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingPeriodSchemeId(orderLine),
+ BookingOrderProperties.OrderLineBookingPeriodSchemeId)!.Value;
+ }
+
+ public static Guid? FindBookingPeriodId(this IOrderLine orderLine)
+ {
+ return orderLine.GetProperty(BookingOrderProperties.OrderLineBookingPeriodId);
+ }
+
+ public static Guid GetBookingPeriodId(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingPeriodId(orderLine),
+ BookingOrderProperties.OrderLineBookingPeriodId)!.Value;
+ }
+
+ public static int? FindBookingVolume(this IOrderLine orderLine)
+ {
+ return orderLine.Quantity;
+ }
+
+ public static int GetBookingVolume(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingVolume(orderLine),
+ BookingOrderProperties.OrderLineBookingVolume)!.Value;
+ }
+
+ public static DateTime? FindBookingDate(this IOrderLine orderLine)
+ {
+ return orderLine.FindDateTimeProperty(BookingOrderProperties.OrderLineBookingDate);
+ }
+
+ public static DateTime GetBookingDate(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingDate(orderLine),
+ BookingOrderProperties.OrderLineBookingDate)!.Value;
+ }
+
+ public static TimeSpan? FindBookingStartingTime(this IOrderLine orderLine)
+ {
+ return orderLine.FindTimeSpanProperty(BookingOrderProperties.OrderLineBookingStartingTime);
+ }
+
+ public static TimeSpan GetBookingStartingTime(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingStartingTime(orderLine),
+ BookingOrderProperties.OrderLineBookingStartingTime)!.Value;
+ }
+
+ public static TimeSpan? FindBookingDuration(this IOrderLine orderLine)
+ {
+ return orderLine.FindTimeSpanProperty(BookingOrderProperties.OrderLineBookingDuration);
+ }
+
+ public static TimeSpan GetBookingDuration(this IOrderLine orderLine)
+ {
+ return Check.NotNull(FindBookingDuration(orderLine),
+ BookingOrderProperties.OrderLineBookingDuration)!.Value;
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/Dtos/BookingProductGroupDefinitionDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/Dtos/BookingProductGroupDefinitionDto.cs
new file mode 100644
index 00000000..43f448bc
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/Dtos/BookingProductGroupDefinitionDto.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions.Dtos;
+
+[Serializable]
+public class BookingProductGroupDefinitionDto
+{
+ public string ProductGroupName { get; set; }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/IBookingProductGroupDefinitionAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/IBookingProductGroupDefinitionAppService.cs
new file mode 100644
index 00000000..84409a17
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/IBookingProductGroupDefinitionAppService.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions.Dtos;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions;
+
+public interface IBookingProductGroupDefinitionAppService : IApplicationService
+{
+ Task> GetListAsync();
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingApplicationContractsModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingApplicationContractsModule.cs
new file mode 100644
index 00000000..0926d4d8
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingApplicationContractsModule.cs
@@ -0,0 +1,19 @@
+using EasyAbp.BookingService;
+using EasyAbp.EShop.Orders;
+using Volo.Abp.Application;
+using Volo.Abp.Modularity;
+using Volo.Abp.Authorization;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(BookingServiceDomainSharedModule),
+ typeof(EShopPluginsBookingDomainSharedModule),
+ typeof(EShopOrdersApplicationContractsModule),
+ typeof(AbpDddApplicationContractsModule),
+ typeof(AbpAuthorizationModule)
+ )]
+public class EShopPluginsBookingApplicationContractsModule : AbpModule
+{
+
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingRemoteServiceConsts.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingRemoteServiceConsts.cs
new file mode 100644
index 00000000..fe705f76
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingRemoteServiceConsts.cs
@@ -0,0 +1,8 @@
+namespace EasyAbp.EShop.Plugins.Booking;
+
+public class EShopPluginsBookingRemoteServiceConsts
+{
+ public const string RemoteServiceName = "EasyAbpEShopPluginsBooking";
+
+ public const string ModuleName = "easyAbpEShopPluginsBooking";
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/CreateUpdateGrantedStoreDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/CreateUpdateGrantedStoreDto.cs
new file mode 100644
index 00000000..c9931a44
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/CreateUpdateGrantedStoreDto.cs
@@ -0,0 +1,16 @@
+using System;
+using System.ComponentModel;
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos
+{
+ [Serializable]
+ public class CreateUpdateGrantedStoreDto
+ {
+ public Guid StoreId { get; set; }
+
+ public Guid? AssetId { get; set; }
+
+ public Guid? AssetCategoryId { get; set; }
+
+ public bool AllowAll { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/GetGrantedStoreListDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/GetGrantedStoreListDto.cs
new file mode 100644
index 00000000..04879c4e
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/GetGrantedStoreListDto.cs
@@ -0,0 +1,16 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+
+[Serializable]
+public class GetGrantedStoreListDto : PagedAndSortedResultRequestDto
+{
+ public Guid? StoreId { get; set; }
+
+ public Guid? AssetId { get; set; }
+
+ public Guid? AssetCategoryId { get; set; }
+
+ public bool? AllowAll { get; set; }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/GrantedStoreDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/GrantedStoreDto.cs
new file mode 100644
index 00000000..e7cf6c7e
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/Dtos/GrantedStoreDto.cs
@@ -0,0 +1,17 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos
+{
+ [Serializable]
+ public class GrantedStoreDto : AuditedEntityDto
+ {
+ public Guid StoreId { get; set; }
+
+ public Guid? AssetId { get; set; }
+
+ public Guid? AssetCategoryId { get; set; }
+
+ public bool AllowAll { get; set; }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/IGrantedStoreAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/IGrantedStoreAppService.cs
new file mode 100644
index 00000000..73d53120
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/GrantedStores/IGrantedStoreAppService.cs
@@ -0,0 +1,17 @@
+using System;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores
+{
+ public interface IGrantedStoreAppService :
+ ICrudAppService<
+ GrantedStoreDto,
+ Guid,
+ GetGrantedStoreListDto,
+ CreateUpdateGrantedStoreDto,
+ CreateUpdateGrantedStoreDto>
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/Permissions/BookingPermissionDefinitionProvider.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/Permissions/BookingPermissionDefinitionProvider.cs
new file mode 100644
index 00000000..9b15811d
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/Permissions/BookingPermissionDefinitionProvider.cs
@@ -0,0 +1,35 @@
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Localization;
+
+namespace EasyAbp.EShop.Plugins.Booking.Permissions;
+
+public class BookingPermissionDefinitionProvider : PermissionDefinitionProvider
+{
+ public override void Define(IPermissionDefinitionContext context)
+ {
+ var myGroup = context.AddGroup(BookingPermissions.GroupName, L("Permission:Booking"));
+
+ var productAssetPermission = myGroup.AddPermission(BookingPermissions.ProductAsset.Default, L("Permission:ProductAsset"));
+ productAssetPermission.AddChild(BookingPermissions.ProductAsset.Manage, L("Permission:Manage"));
+ productAssetPermission.AddChild(BookingPermissions.ProductAsset.Create, L("Permission:Create"));
+ productAssetPermission.AddChild(BookingPermissions.ProductAsset.Update, L("Permission:Update"));
+ productAssetPermission.AddChild(BookingPermissions.ProductAsset.Delete, L("Permission:Delete"));
+
+ var productAssetCategoryPermission = myGroup.AddPermission(BookingPermissions.ProductAssetCategory.Default, L("Permission:ProductAssetCategory"));
+ productAssetCategoryPermission.AddChild(BookingPermissions.ProductAssetCategory.Manage, L("Permission:Manage"));
+ productAssetCategoryPermission.AddChild(BookingPermissions.ProductAssetCategory.Create, L("Permission:Create"));
+ productAssetCategoryPermission.AddChild(BookingPermissions.ProductAssetCategory.Update, L("Permission:Update"));
+ productAssetCategoryPermission.AddChild(BookingPermissions.ProductAssetCategory.Delete, L("Permission:Delete"));
+
+ var grantedStorePermission = myGroup.AddPermission(BookingPermissions.GrantedStore.Default, L("Permission:GrantedStore"));
+ grantedStorePermission.AddChild(BookingPermissions.GrantedStore.Create, L("Permission:Create"));
+ grantedStorePermission.AddChild(BookingPermissions.GrantedStore.Update, L("Permission:Update"));
+ grantedStorePermission.AddChild(BookingPermissions.GrantedStore.Delete, L("Permission:Delete"));
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/Permissions/BookingPermissions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/Permissions/BookingPermissions.cs
new file mode 100644
index 00000000..080319c5
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/Permissions/BookingPermissions.cs
@@ -0,0 +1,39 @@
+using Volo.Abp.Reflection;
+
+namespace EasyAbp.EShop.Plugins.Booking.Permissions;
+
+public class BookingPermissions
+{
+ public const string GroupName = "EasyAbp.EShop.Plugins.Booking";
+
+ public static string[] GetAll()
+ {
+ return ReflectionHelper.GetPublicConstantsRecursively(typeof(BookingPermissions));
+ }
+
+ public class ProductAsset
+ {
+ public const string Default = GroupName + ".ProductAsset";
+ public const string Manage = Default + ".Manage";
+ public const string Update = Default + ".Update";
+ public const string Create = Default + ".Create";
+ public const string Delete = Default + ".Delete";
+ }
+
+ public class ProductAssetCategory
+ {
+ public const string Default = GroupName + ".ProductAssetCategory";
+ public const string Manage = Default + ".Manage";
+ public const string Update = Default + ".Update";
+ public const string Create = Default + ".Create";
+ public const string Delete = Default + ".Delete";
+ }
+
+ public class GrantedStore
+ {
+ public const string Default = GroupName + ".GrantedStore";
+ public const string Update = Default + ".Update";
+ public const string Create = Default + ".Create";
+ public const string Delete = Default + ".Delete";
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/CreateProductAssetCategoryDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/CreateProductAssetCategoryDto.cs
new file mode 100644
index 00000000..4249ef1c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/CreateProductAssetCategoryDto.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using EasyAbp.EShop.Stores.Stores;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos
+{
+ [Serializable]
+ public class CreateProductAssetCategoryDto : IMultiStore, IValidatableObject
+ {
+ public Guid StoreId { get; set; }
+
+ public Guid ProductId { get; set; }
+
+ public Guid ProductSkuId { get; set; }
+
+ public Guid AssetCategoryId { get; set; }
+
+ public Guid PeriodSchemeId { get; set; }
+
+ public DateTime FromTime { get; set; }
+
+ public DateTime? ToTime { get; set; }
+
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal? Price { get; set; }
+
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ if (Price is not null && Currency.IsNullOrWhiteSpace())
+ {
+ yield return new ValidationResult(
+ "Currency should not be empty when the Price has a value!",
+ new[] { nameof(Currency) }
+ );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/CreateProductAssetCategoryPeriodDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/CreateProductAssetCategoryPeriodDto.cs
new file mode 100644
index 00000000..080c759c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/CreateProductAssetCategoryPeriodDto.cs
@@ -0,0 +1,17 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos
+{
+ [Serializable]
+ public class CreateProductAssetCategoryPeriodDto
+ {
+ public Guid PeriodId { get; set; }
+
+ [Required]
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/GetProductAssetCategoryListDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/GetProductAssetCategoryListDto.cs
new file mode 100644
index 00000000..47f6dc8d
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/GetProductAssetCategoryListDto.cs
@@ -0,0 +1,18 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+
+[Serializable]
+public class GetProductAssetCategoryListDto : PagedAndSortedResultRequestDto
+{
+ public Guid? StoreId { get; set; }
+
+ public Guid? ProductId { get; set; }
+
+ public Guid? ProductSkuId { get; set; }
+
+ public Guid? AssetCategoryId { get; set; }
+
+ public Guid? PeriodSchemeId { get; set; }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/ProductAssetCategoryDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/ProductAssetCategoryDto.cs
new file mode 100644
index 00000000..8f8bc895
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/ProductAssetCategoryDto.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos
+{
+ [Serializable]
+ public class ProductAssetCategoryDto : AuditedEntityDto
+ {
+ public Guid StoreId { get; set; }
+
+ public Guid ProductId { get; set; }
+
+ public Guid ProductSkuId { get; set; }
+
+ public Guid AssetCategoryId { get; set; }
+
+ public Guid PeriodSchemeId { get; set; }
+
+ public DateTime FromTime { get; set; }
+
+ public DateTime? ToTime { get; set; }
+
+ public string Currency { get; set; }
+
+ public decimal? Price { get; set; }
+
+ public List Periods { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/ProductAssetCategoryPeriodDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/ProductAssetCategoryPeriodDto.cs
new file mode 100644
index 00000000..e08fc9ba
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/ProductAssetCategoryPeriodDto.cs
@@ -0,0 +1,15 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos
+{
+ [Serializable]
+ public class ProductAssetCategoryPeriodDto : EntityDto
+ {
+ public Guid PeriodId { get; set; }
+
+ public string Currency { get; set; }
+
+ public decimal Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/UpdateProductAssetCategoryDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/UpdateProductAssetCategoryDto.cs
new file mode 100644
index 00000000..1ba5d75f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/UpdateProductAssetCategoryDto.cs
@@ -0,0 +1,20 @@
+using System;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos
+{
+ [Serializable]
+ public class UpdateProductAssetCategoryDto
+ {
+ public DateTime FromTime { get; set; }
+
+ public DateTime? ToTime { get; set; }
+
+ [Required]
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal? Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/UpdateProductAssetCategoryPeriodDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/UpdateProductAssetCategoryPeriodDto.cs
new file mode 100644
index 00000000..e3d3a645
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/Dtos/UpdateProductAssetCategoryPeriodDto.cs
@@ -0,0 +1,16 @@
+using System;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos
+{
+ [Serializable]
+ public class UpdateProductAssetCategoryPeriodDto
+ {
+ [Required]
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/IProductAssetCategoryAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/IProductAssetCategoryAppService.cs
new file mode 100644
index 00000000..8e38e9c9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/IProductAssetCategoryAppService.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories
+{
+ public interface IProductAssetCategoryAppService :
+ ICrudAppService<
+ ProductAssetCategoryDto,
+ Guid,
+ GetProductAssetCategoryListDto,
+ CreateProductAssetCategoryDto,
+ UpdateProductAssetCategoryDto>
+ {
+ Task CreatePeriodAsync(Guid productAssetCategoryId, CreateProductAssetCategoryPeriodDto input);
+
+ Task UpdatePeriodAsync(Guid productAssetCategoryId, Guid periodId, UpdateProductAssetCategoryPeriodDto input);
+
+ Task DeletePeriodAsync(Guid productAssetCategoryId, Guid periodId);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/CreateProductAssetDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/CreateProductAssetDto.cs
new file mode 100644
index 00000000..54796f32
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/CreateProductAssetDto.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using EasyAbp.EShop.Stores.Stores;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos
+{
+ [Serializable]
+ public class CreateProductAssetDto : IMultiStore, IValidatableObject
+ {
+ public Guid StoreId { get; set; }
+
+ public Guid ProductId { get; set; }
+
+ public Guid ProductSkuId { get; set; }
+
+ public Guid AssetId { get; set; }
+
+ public Guid PeriodSchemeId { get; set; }
+
+ public DateTime FromTime { get; set; }
+
+ public DateTime? ToTime { get; set; }
+
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal? Price { get; set; }
+
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ if (Price is not null && Currency.IsNullOrWhiteSpace())
+ {
+ yield return new ValidationResult(
+ "Currency should not be empty when the Price has a value!",
+ new[] { nameof(Currency) }
+ );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/CreateProductAssetPeriodDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/CreateProductAssetPeriodDto.cs
new file mode 100644
index 00000000..deed79d6
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/CreateProductAssetPeriodDto.cs
@@ -0,0 +1,17 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos
+{
+ [Serializable]
+ public class CreateProductAssetPeriodDto
+ {
+ public Guid PeriodId { get; set; }
+
+ [Required]
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/GetProductAssetListDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/GetProductAssetListDto.cs
new file mode 100644
index 00000000..d5262816
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/GetProductAssetListDto.cs
@@ -0,0 +1,18 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+
+[Serializable]
+public class GetProductAssetListDto : PagedAndSortedResultRequestDto
+{
+ public Guid? StoreId { get; set; }
+
+ public Guid? ProductId { get; set; }
+
+ public Guid? ProductSkuId { get; set; }
+
+ public Guid? AssetId { get; set; }
+
+ public Guid? PeriodSchemeId { get; set; }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/ProductAssetDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/ProductAssetDto.cs
new file mode 100644
index 00000000..631061fe
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/ProductAssetDto.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos
+{
+ [Serializable]
+ public class ProductAssetDto : AuditedEntityDto
+ {
+ public Guid StoreId { get; set; }
+
+ public Guid ProductId { get; set; }
+
+ public Guid ProductSkuId { get; set; }
+
+ public Guid AssetId { get; set; }
+
+ public Guid PeriodSchemeId { get; set; }
+
+ public DateTime FromTime { get; set; }
+
+ public DateTime? ToTime { get; set; }
+
+ public string Currency { get; set; }
+
+ public decimal? Price { get; set; }
+
+ public List Periods { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/ProductAssetPeriodDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/ProductAssetPeriodDto.cs
new file mode 100644
index 00000000..899599cf
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/ProductAssetPeriodDto.cs
@@ -0,0 +1,15 @@
+using System;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos
+{
+ [Serializable]
+ public class ProductAssetPeriodDto : EntityDto
+ {
+ public Guid PeriodId { get; set; }
+
+ public string Currency { get; set; }
+
+ public decimal Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/UpdateProductAssetDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/UpdateProductAssetDto.cs
new file mode 100644
index 00000000..5aec3466
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/UpdateProductAssetDto.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos
+{
+ [Serializable]
+ public class UpdateProductAssetDto
+ {
+ public DateTime FromTime { get; set; }
+
+ public DateTime? ToTime { get; set; }
+
+ [Required]
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal? Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/UpdateProductAssetPeriodDto.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/UpdateProductAssetPeriodDto.cs
new file mode 100644
index 00000000..5b32c202
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/Dtos/UpdateProductAssetPeriodDto.cs
@@ -0,0 +1,15 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos
+{
+ [Serializable]
+ public class UpdateProductAssetPeriodDto
+ {
+ [Required]
+ public string Currency { get; set; }
+
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/IProductAssetAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/IProductAssetAppService.cs
new file mode 100644
index 00000000..6ea9b213
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/EasyAbp/EShop/Plugins/Booking/ProductAssets/IProductAssetAppService.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using Volo.Abp.Application.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets
+{
+ public interface IProductAssetAppService :
+ ICrudAppService<
+ ProductAssetDto,
+ Guid,
+ GetProductAssetListDto,
+ CreateProductAssetDto,
+ UpdateProductAssetDto>
+ {
+ Task CreatePeriodAsync(Guid productAssetId, CreateProductAssetPeriodDto input);
+
+ Task UpdatePeriodAsync(Guid productAssetId, Guid periodId, UpdateProductAssetPeriodDto input);
+
+ Task DeletePeriodAsync(Guid productAssetId, Guid periodId);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application.Contracts/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.abppkg.json
new file mode 100644
index 00000000..412567ac
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.application"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.csproj
new file mode 100644
index 00000000..b0983c6a
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp.EShop.Plugins.Booking.Application.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingAppService.cs
new file mode 100644
index 00000000..e511876f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingAppService.cs
@@ -0,0 +1,13 @@
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using Volo.Abp.Application.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+public abstract class BookingAppService : ApplicationService
+{
+ protected BookingAppService()
+ {
+ LocalizationResource = typeof(BookingResource);
+ ObjectMapperContext = typeof(EShopPluginsBookingApplicationModule);
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingApplicationAutoMapperProfile.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingApplicationAutoMapperProfile.cs
new file mode 100644
index 00000000..bf15e402
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingApplicationAutoMapperProfile.cs
@@ -0,0 +1,33 @@
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using AutoMapper;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+public class BookingApplicationAutoMapperProfile : Profile
+{
+ public BookingApplicationAutoMapperProfile()
+ {
+ /* You can configure your AutoMapper mapping configuration here.
+ * Alternatively, you can split your mapping configurations
+ * into multiple profile classes for a better organization. */
+ CreateMap();
+
+ CreateMap();
+ CreateMap(MemberList.Source);
+ CreateMap(MemberList.Source);
+
+ CreateMap();
+
+ CreateMap();
+ CreateMap(MemberList.Source);
+ CreateMap(MemberList.Source);
+
+ CreateMap();
+ CreateMap(MemberList.Source);
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/BookingProductGroupDefinitionAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/BookingProductGroupDefinitionAppService.cs
new file mode 100644
index 00000000..d97bf1ff
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/BookingProductGroupDefinitions/BookingProductGroupDefinitionAppService.cs
@@ -0,0 +1,31 @@
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Options;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.BookingProductGroupDefinitions;
+
+public class BookingProductGroupDefinitionAppService : BookingAppService, IBookingProductGroupDefinitionAppService
+{
+ private readonly EShopBookingOptions _options;
+
+ public BookingProductGroupDefinitionAppService(IOptions options)
+ {
+ _options = options.Value;
+ }
+
+ public virtual Task> GetListAsync()
+ {
+ return Task.FromResult(
+ new ListResultDto(
+ _options.BookingProductGroups.Select(x =>
+ new BookingProductGroupDefinitionDto
+ {
+ ProductGroupName = x.ProductGroupName
+ }).ToList()
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingApplicationModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingApplicationModule.cs
new file mode 100644
index 00000000..b0c882fb
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingApplicationModule.cs
@@ -0,0 +1,30 @@
+using EasyAbp.BookingService;
+using EasyAbp.EShop.Products;
+using EasyAbp.EShop.Stores;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Modularity;
+using Volo.Abp.Application;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(EShopStoresApplicationSharedModule),
+ typeof(EShopProductsApplicationContractsModule),
+ typeof(EShopPluginsBookingDomainModule),
+ typeof(EShopPluginsBookingApplicationContractsModule),
+ typeof(BookingServiceApplicationContractsModule),
+ typeof(AbpDddApplicationModule),
+ typeof(AbpAutoMapperModule)
+ )]
+public class EShopPluginsBookingApplicationModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAutoMapperObjectMapper();
+ Configure(options =>
+ {
+ options.AddMaps(validate: true);
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/EventHandlers/BookingOrderPaidEventHandler.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/EventHandlers/BookingOrderPaidEventHandler.cs
new file mode 100644
index 00000000..bce92c5a
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/EventHandlers/BookingOrderPaidEventHandler.cs
@@ -0,0 +1,69 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using EasyAbp.BookingService.AssetOccupancies;
+using EasyAbp.BookingService.AssetOccupancyProviders;
+using EasyAbp.EShop.Orders;
+using EasyAbp.EShop.Orders.Orders;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus.Distributed;
+
+namespace EasyAbp.EShop.Plugins.Booking.EventHandlers;
+
+public class BookingOrderPaidEventHandler : IDistributedEventHandler, ITransientDependency
+{
+ private readonly IDistributedEventBus _distributedEventBus;
+
+ public BookingOrderPaidEventHandler(IDistributedEventBus distributedEventBus)
+ {
+ _distributedEventBus = distributedEventBus;
+ }
+
+ public virtual async Task HandleEventAsync(OrderPaidEto eventData)
+ {
+ var occupyModels = new List();
+ var occupyByCategoryModels = new List();
+
+ foreach (var orderLine in eventData.Order.OrderLines)
+ {
+ var assetId = orderLine.FindBookingAssetId();
+ var assetCategoryId = orderLine.FindBookingAssetCategoryId();
+ var volume = orderLine.FindBookingVolume();
+ var date = orderLine.FindBookingDate();
+ var startingTime = orderLine.FindBookingStartingTime();
+ var duration = orderLine.FindBookingDuration();
+
+ if (volume is null || date is null || startingTime is null || duration is null)
+ {
+ continue;
+ }
+
+ if (assetId.HasValue)
+ {
+ occupyModels.Add(new OccupyAssetInfoModel(
+ assetId: assetId.Value,
+ volume: volume.Value,
+ date: date.Value,
+ startingTime: startingTime.Value,
+ duration: duration.Value
+ ));
+ }
+ else if (assetCategoryId.HasValue)
+ {
+ occupyByCategoryModels.Add(new OccupyAssetByCategoryInfoModel(
+ assetCategoryId: assetCategoryId.Value,
+ volume: volume.Value,
+ date: date.Value,
+ startingTime: startingTime.Value,
+ duration: duration.Value
+ ));
+ }
+ }
+
+ var eto = new BulkOccupyAssetEto(eventData.TenantId, eventData.Order.Id, eventData.Order.CustomerUserId,
+ occupyModels, occupyByCategoryModels);
+
+ eto.SetBookingOrderId(eventData.Order.Id);
+
+ await _distributedEventBus.PublishAsync(eto);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreAppService.cs
new file mode 100644
index 00000000..685dc4d1
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreAppService.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.Permissions;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using Volo.Abp.Application.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores
+{
+ public class GrantedStoreAppService : CrudAppService,
+ IGrantedStoreAppService
+ {
+ protected override string GetPolicyName { get; set; } = null;
+ protected override string GetListPolicyName { get; set; } = null;
+ protected override string CreatePolicyName { get; set; } = BookingPermissions.GrantedStore.Create;
+ protected override string UpdatePolicyName { get; set; } = BookingPermissions.GrantedStore.Update;
+ protected override string DeletePolicyName { get; set; } = BookingPermissions.GrantedStore.Delete;
+
+ private readonly IGrantedStoreRepository _repository;
+
+ public GrantedStoreAppService(IGrantedStoreRepository repository) : base(repository)
+ {
+ _repository = repository;
+ }
+
+ protected override async Task> CreateFilteredQueryAsync(GetGrantedStoreListDto input)
+ {
+ return (await base.CreateFilteredQueryAsync(input))
+ .WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId)
+ .WhereIf(input.AssetId.HasValue, x => x.AssetId == input.AssetId)
+ .WhereIf(input.AssetCategoryId.HasValue, x => x.AssetCategoryId == input.AssetCategoryId)
+ .WhereIf(input.AllowAll.HasValue, x => x.AllowAll == input.AllowAll);
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryAppService.cs
new file mode 100644
index 00000000..f92493c4
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryAppService.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.BookingService.AssetCategories;
+using EasyAbp.BookingService.PeriodSchemes;
+using EasyAbp.EShop.Plugins.Booking.Permissions;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Products.Products;
+using EasyAbp.EShop.Stores.Stores;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories
+{
+ public class ProductAssetCategoryAppService : MultiStoreCrudAppService,
+ IProductAssetCategoryAppService
+ {
+ protected override string CrossStorePolicyName { get; set; } = BookingPermissions.ProductAssetCategory.Manage;
+ protected override string GetPolicyName { get; set; } = BookingPermissions.ProductAssetCategory.Default;
+ protected override string GetListPolicyName { get; set; } = BookingPermissions.ProductAssetCategory.Default;
+ protected override string CreatePolicyName { get; set; } = BookingPermissions.ProductAssetCategory.Create;
+ protected override string UpdatePolicyName { get; set; } = BookingPermissions.ProductAssetCategory.Update;
+ protected override string DeletePolicyName { get; set; } = BookingPermissions.ProductAssetCategory.Delete;
+
+ private readonly IProductAppService _productAppService;
+ private readonly IPeriodSchemeAppService _periodSchemeAppService;
+ private readonly IAssetCategoryAppService _assetCategoryAppService;
+ private readonly IProductAssetCategoryRepository _repository;
+ private readonly ProductAssetCategoryManager _productAssetCategoryManager;
+
+ public ProductAssetCategoryAppService(
+ IProductAppService productAppService,
+ IPeriodSchemeAppService periodSchemeAppService,
+ IAssetCategoryAppService assetCategoryAppService,
+ IProductAssetCategoryRepository repository,
+ ProductAssetCategoryManager productAssetCategoryManager) : base(repository)
+ {
+ _productAppService = productAppService;
+ _periodSchemeAppService = periodSchemeAppService;
+ _assetCategoryAppService = assetCategoryAppService;
+ _repository = repository;
+ _productAssetCategoryManager = productAssetCategoryManager;
+ }
+
+ protected override async Task> CreateFilteredQueryAsync(
+ GetProductAssetCategoryListDto input)
+ {
+ return (await base.CreateFilteredQueryAsync(input))
+ .WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId)
+ .WhereIf(input.ProductId.HasValue, x => x.ProductId == input.ProductId)
+ .WhereIf(input.ProductSkuId.HasValue, x => x.ProductSkuId == input.ProductSkuId)
+ .WhereIf(input.AssetCategoryId.HasValue, x => x.AssetCategoryId == input.AssetCategoryId)
+ .WhereIf(input.PeriodSchemeId.HasValue, x => x.PeriodSchemeId == input.PeriodSchemeId);
+ }
+
+ public override async Task> GetListAsync(
+ GetProductAssetCategoryListDto input)
+ {
+ await CheckMultiStorePolicyAsync(input.StoreId, GetListPolicyName);
+
+ var query = await CreateFilteredQueryAsync(input);
+
+ var totalCount = await AsyncExecuter.CountAsync(query);
+
+ query = ApplySorting(query, input);
+ query = ApplyPaging(query, input);
+
+ var entities = await AsyncExecuter.ToListAsync(query);
+ var entityDtos = await MapToGetListOutputDtosAsync(entities);
+
+ return new PagedResultDto(
+ totalCount,
+ entityDtos
+ );
+ }
+
+ public override async Task CreateAsync(CreateProductAssetCategoryDto input)
+ {
+ await CheckMultiStorePolicyAsync(input.StoreId, CreatePolicyName);
+
+ await EnsureProductSkuExistAsync(input.StoreId, input.ProductId, input.ProductSkuId);
+ await EnsureAssetCategoryExistAsync(input.AssetCategoryId);
+ await EnsurePeriodSchemeExistAsync(input.PeriodSchemeId);
+
+ var entity = await MapToEntityAsync(input);
+
+ TryToSetTenantId(entity);
+
+ await Repository.InsertAsync(entity, autoSave: true);
+
+ return await MapToGetOutputDtoAsync(entity);
+ }
+
+ protected virtual async Task EnsureProductSkuExistAsync(Guid storeId, Guid productId, Guid productSkuId)
+ {
+ var product = await _productAppService.GetAsync(productId);
+
+ if (product.StoreId != storeId)
+ {
+ throw (new BusinessException(BookingErrorCodes.WrongStoreIdForProduct))
+ .WithData(nameof(storeId), storeId)
+ .WithData(nameof(productId), productId);
+ }
+
+ product.GetSkuById(productSkuId);
+ }
+
+ protected virtual async Task EnsureAssetCategoryExistAsync(Guid assetCategoryId)
+ {
+ await _assetCategoryAppService.GetAsync(assetCategoryId);
+ }
+
+ protected virtual async Task EnsurePeriodSchemeExistAsync(Guid periodSchemeId)
+ {
+ await _periodSchemeAppService.GetAsync(periodSchemeId);
+ }
+
+ protected override async Task MapToEntityAsync(CreateProductAssetCategoryDto input)
+ {
+ return await _productAssetCategoryManager.CreateAsync(input.StoreId, input.ProductId, input.ProductSkuId,
+ input.AssetCategoryId, input.PeriodSchemeId, input.FromTime, input.ToTime, input.Currency, input.Price);
+ }
+
+ protected override async Task MapToEntityAsync(UpdateProductAssetCategoryDto input, ProductAssetCategory entity)
+ {
+ await _productAssetCategoryManager.UpdateAsync(
+ entity, input.FromTime, input.ToTime, input.Currency, input.Price);
+ }
+
+ public virtual async Task CreatePeriodAsync(Guid productAssetCategoryId,
+ CreateProductAssetCategoryPeriodDto input)
+ {
+ var productAssetCategory = await GetEntityByIdAsync(productAssetCategoryId);
+
+ await CheckMultiStorePolicyAsync(productAssetCategory.StoreId, UpdatePolicyName);
+
+ productAssetCategory.AddPeriod(
+ ObjectMapper.Map(input));
+
+ await _repository.UpdateAsync(productAssetCategory, true);
+
+ return await MapToGetOutputDtoAsync(productAssetCategory);
+ }
+
+ public virtual async Task UpdatePeriodAsync(Guid productAssetCategoryId, Guid periodId,
+ UpdateProductAssetCategoryPeriodDto input)
+ {
+ var productAssetCategory = await GetEntityByIdAsync(productAssetCategoryId);
+
+ await CheckMultiStorePolicyAsync(productAssetCategory.StoreId, UpdatePolicyName);
+
+ var productAssetCategoryPeriod = productAssetCategory.GetPeriod(periodId);
+
+ ObjectMapper.Map(input, productAssetCategoryPeriod);
+
+ await _repository.UpdateAsync(productAssetCategory, true);
+
+ return await MapToGetOutputDtoAsync(productAssetCategory);
+ }
+
+ public virtual async Task DeletePeriodAsync(Guid productAssetCategoryId, Guid periodId)
+ {
+ var productAssetCategory = await GetEntityByIdAsync(productAssetCategoryId);
+
+ await CheckMultiStorePolicyAsync(productAssetCategory.StoreId, UpdatePolicyName);
+
+ productAssetCategory.RemovePeriod(periodId);
+
+ await _repository.UpdateAsync(productAssetCategory, true);
+
+ return await MapToGetOutputDtoAsync(productAssetCategory);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetAppService.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetAppService.cs
new file mode 100644
index 00000000..b62604ba
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetAppService.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.BookingService.Assets;
+using EasyAbp.BookingService.PeriodSchemes;
+using EasyAbp.EShop.Plugins.Booking.Permissions;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using EasyAbp.EShop.Products.Products;
+using EasyAbp.EShop.Stores.Stores;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets
+{
+ public class ProductAssetAppService : MultiStoreCrudAppService,
+ IProductAssetAppService
+ {
+ protected override string CrossStorePolicyName { get; set; } = BookingPermissions.ProductAsset.Manage;
+ protected override string GetPolicyName { get; set; } = BookingPermissions.ProductAsset.Default;
+ protected override string GetListPolicyName { get; set; } = BookingPermissions.ProductAsset.Default;
+ protected override string CreatePolicyName { get; set; } = BookingPermissions.ProductAsset.Create;
+ protected override string UpdatePolicyName { get; set; } = BookingPermissions.ProductAsset.Update;
+ protected override string DeletePolicyName { get; set; } = BookingPermissions.ProductAsset.Delete;
+
+ private readonly IProductAppService _productAppService;
+ private readonly IPeriodSchemeAppService _periodSchemeAppService;
+ private readonly IAssetAppService _assetAppService;
+ private readonly IProductAssetRepository _repository;
+ private readonly ProductAssetManager _productAssetManager;
+
+ public ProductAssetAppService(
+ IProductAppService productAppService,
+ IPeriodSchemeAppService periodSchemeAppService,
+ IAssetAppService assetAppService,
+ IProductAssetRepository repository,
+ ProductAssetManager productAssetManager) : base(repository)
+ {
+ _productAppService = productAppService;
+ _periodSchemeAppService = periodSchemeAppService;
+ _assetAppService = assetAppService;
+ _repository = repository;
+ _productAssetManager = productAssetManager;
+ }
+
+ protected override async Task> CreateFilteredQueryAsync(GetProductAssetListDto input)
+ {
+ return (await base.CreateFilteredQueryAsync(input))
+ .WhereIf(input.StoreId.HasValue, x => x.StoreId == input.StoreId)
+ .WhereIf(input.ProductId.HasValue, x => x.ProductId == input.ProductId)
+ .WhereIf(input.ProductSkuId.HasValue, x => x.ProductSkuId == input.ProductSkuId)
+ .WhereIf(input.AssetId.HasValue, x => x.AssetId == input.AssetId)
+ .WhereIf(input.PeriodSchemeId.HasValue, x => x.PeriodSchemeId == input.PeriodSchemeId);
+ }
+
+ public override async Task> GetListAsync(
+ GetProductAssetListDto input)
+ {
+ await CheckMultiStorePolicyAsync(input.StoreId, GetListPolicyName);
+
+ var query = await CreateFilteredQueryAsync(input);
+
+ var totalCount = await AsyncExecuter.CountAsync(query);
+
+ query = ApplySorting(query, input);
+ query = ApplyPaging(query, input);
+
+ var entities = await AsyncExecuter.ToListAsync(query);
+ var entityDtos = await MapToGetListOutputDtosAsync(entities);
+
+ return new PagedResultDto(
+ totalCount,
+ entityDtos
+ );
+ }
+
+ public override async Task CreateAsync(CreateProductAssetDto input)
+ {
+ await CheckMultiStorePolicyAsync(input.StoreId, CreatePolicyName);
+
+ await EnsureProductSkuExistAsync(input.StoreId, input.ProductId, input.ProductSkuId);
+ await EnsureAssetExistAsync(input.AssetId);
+ await EnsurePeriodSchemeExistAsync(input.PeriodSchemeId);
+
+ var entity = await MapToEntityAsync(input);
+
+ TryToSetTenantId(entity);
+
+ await Repository.InsertAsync(entity, autoSave: true);
+
+ return await MapToGetOutputDtoAsync(entity);
+ }
+
+ protected virtual async Task EnsureProductSkuExistAsync(Guid storeId, Guid productId, Guid productSkuId)
+ {
+ var product = await _productAppService.GetAsync(productId);
+
+ if (product.StoreId != storeId)
+ {
+ throw (new BusinessException(BookingErrorCodes.WrongStoreIdForProduct))
+ .WithData(nameof(storeId), storeId)
+ .WithData(nameof(productId), productId);
+ }
+
+ product.GetSkuById(productSkuId);
+ }
+
+ protected virtual async Task EnsureAssetExistAsync(Guid assetId)
+ {
+ await _assetAppService.GetAsync(assetId);
+ }
+
+ protected virtual async Task EnsurePeriodSchemeExistAsync(Guid periodSchemeId)
+ {
+ await _periodSchemeAppService.GetAsync(periodSchemeId);
+ }
+
+ protected override async Task MapToEntityAsync(CreateProductAssetDto input)
+ {
+ return await _productAssetManager.CreateAsync(input.StoreId, input.ProductId, input.ProductSkuId,
+ input.AssetId, input.PeriodSchemeId, input.FromTime, input.ToTime, input.Currency, input.Price);
+ }
+
+ protected override async Task MapToEntityAsync(UpdateProductAssetDto input, ProductAsset entity)
+ {
+ await _productAssetManager.UpdateAsync(entity, input.FromTime, input.ToTime, input.Currency, input.Price);
+ }
+
+ public virtual async Task CreatePeriodAsync(Guid productAssetId,
+ CreateProductAssetPeriodDto input)
+ {
+ var productAsset = await GetEntityByIdAsync(productAssetId);
+
+ await CheckMultiStorePolicyAsync(productAsset.StoreId, UpdatePolicyName);
+
+ productAsset.AddPeriod(
+ ObjectMapper.Map(input));
+
+ await _repository.UpdateAsync(productAsset, true);
+
+ return await MapToGetOutputDtoAsync(productAsset);
+ }
+
+ public virtual async Task UpdatePeriodAsync(Guid productAssetId, Guid periodId,
+ UpdateProductAssetPeriodDto input)
+ {
+ var productAsset = await GetEntityByIdAsync(productAssetId);
+
+ await CheckMultiStorePolicyAsync(productAsset.StoreId, UpdatePolicyName);
+
+ var productAssetPeriod = productAsset.GetPeriod(periodId);
+
+ ObjectMapper.Map(input, productAssetPeriod);
+
+ await _repository.UpdateAsync(productAsset, true);
+
+ return await MapToGetOutputDtoAsync(productAsset);
+ }
+
+ public virtual async Task DeletePeriodAsync(Guid productAssetId, Guid periodId)
+ {
+ var productAsset = await GetEntityByIdAsync(productAssetId);
+
+ await CheckMultiStorePolicyAsync(productAsset.StoreId, UpdatePolicyName);
+
+ productAsset.RemovePeriod(periodId);
+
+ await _repository.UpdateAsync(productAsset, true);
+
+ return await MapToGetOutputDtoAsync(productAsset);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Application/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.abppkg.json
new file mode 100644
index 00000000..8b3de05f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.domain-shared"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.csproj
new file mode 100644
index 00000000..595187c9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp.EShop.Plugins.Booking.Domain.Shared.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+ netstandard2.0
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/BookingConsts.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/BookingConsts.cs
new file mode 100644
index 00000000..b481429d
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/BookingConsts.cs
@@ -0,0 +1,7 @@
+namespace EasyAbp.EShop.Plugins.Booking;
+
+public class BookingConsts
+{
+ public const double MinimumPrice = 0f;
+ public const double MaximumPrice = 999999999999.99999999f;
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/BookingErrorCodes.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/BookingErrorCodes.cs
new file mode 100644
index 00000000..b112a761
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/BookingErrorCodes.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Plugins.Booking;
+
+public static class BookingErrorCodes
+{
+ public const string PeriodNotFound = "EasyAbp.EShop.Plugins.Booking:PeriodNotFound";
+ public const string DuplicatePeriod = "EasyAbp.EShop.Plugins.Booking:DuplicatePeriod";
+ public const string ConflictingProductAsset = "EasyAbp.EShop.Plugins.Booking:ConflictingProductAsset";
+ public const string ConflictingProductAssetCategory = "EasyAbp.EShop.Plugins.Booking:ConflictingProductAssetCategory";
+ public const string WrongStoreIdForProduct = "EasyAbp.EShop.Plugins.Booking:WrongStoreIdForProduct";
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingDomainSharedModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingDomainSharedModule.cs
new file mode 100644
index 00000000..abdf3e88
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingDomainSharedModule.cs
@@ -0,0 +1,36 @@
+using Volo.Abp.Modularity;
+using Volo.Abp.Localization;
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using Volo.Abp.Localization.ExceptionHandling;
+using Volo.Abp.Validation;
+using Volo.Abp.Validation.Localization;
+using Volo.Abp.VirtualFileSystem;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(AbpValidationModule)
+)]
+public class EShopPluginsBookingDomainSharedModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add("en")
+ .AddBaseTypes(typeof(AbpValidationResource))
+ .AddVirtualJson("EasyAbp/EShop/Plugins/Booking/Localization");
+ });
+
+ Configure(options =>
+ {
+ options.MapCodeNamespace("EasyAbp.EShop.Plugins.Booking", typeof(BookingResource));
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/BookingResource.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/BookingResource.cs
new file mode 100644
index 00000000..262e073f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/BookingResource.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Localization;
+
+namespace EasyAbp.EShop.Plugins.Booking.Localization;
+
+[LocalizationResourceName("EasyAbpEShopPluginsBooking")]
+public class BookingResource
+{
+
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/en.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/en.json
new file mode 100644
index 00000000..98c4323a
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/en.json
@@ -0,0 +1,73 @@
+{
+ "culture": "en",
+ "texts": {
+ "Menu:Booking": "Booking business",
+ "Permission:ProductAsset": "Asset prices",
+ "Permission:Create": "Create",
+ "Permission:Update": "Update",
+ "Permission:Delete": "Delete",
+ "Menu:ProductAsset": "Asset prices",
+ "ProductAsset": "Asset prices",
+ "ProductAssetStoreId": "Store ID",
+ "ProductAssetProductId": "Product ID",
+ "ProductAssetProductSkuId": "Product SKU ID",
+ "ProductAssetAssetId": "Asset ID",
+ "ProductAssetPeriodSchemeId": "Period Scheme ID",
+ "ProductAssetFromTime": "From time",
+ "ProductAssetToTime": "To time",
+ "ProductAssetCurrency": "Currency",
+ "ProductAssetPrice": "Price",
+ "ProductAssetPeriods": "Period prices",
+ "CreateProductAsset": "New",
+ "EditProductAsset": "Edit",
+ "ProductAssetDeletionConfirmationMessage": "Are you sure to delete the product asset {0}?",
+ "SuccessfullyDeleted": "Successfully deleted",
+ "Menu:ProductAssetPeriod": "Asset period prices",
+ "ProductAssetPeriod": "Asset period prices",
+ "ProductAssetPeriodPeriodId": "Period ID",
+ "ProductAssetPeriodCurrency": "Currency",
+ "ProductAssetPeriodPrice": "Price",
+ "CreateProductAssetPeriod": "New",
+ "EditProductAssetPeriod": "Edit",
+ "ProductAssetPeriodDeletionConfirmationMessage": "Are you sure to delete the product asset period {0}?",
+ "Permission:ProductAssetCategory": "Asset category prices",
+ "Menu:ProductAssetCategory": "Asset category prices",
+ "ProductAssetCategory": "Asset category prices",
+ "ProductAssetCategoryStoreId": "Store ID",
+ "ProductAssetCategoryProductId": "Product ID",
+ "ProductAssetCategoryProductSkuId": "Product SKU ID",
+ "ProductAssetCategoryAssetCategoryId": "Asset Category ID",
+ "ProductAssetCategoryPeriodSchemeId": "Period Scheme ID",
+ "ProductAssetCategoryFromTime": "From time",
+ "ProductAssetCategoryToTime": "To time",
+ "ProductAssetCategoryCurrency": "Currency",
+ "ProductAssetCategoryPrice": "Price",
+ "ProductAssetCategoryPeriods": "Period prices",
+ "CreateProductAssetCategory": "New",
+ "EditProductAssetCategory": "Edit",
+ "ProductAssetCategoryDeletionConfirmationMessage": "Are you sure to delete the product asset category {0}?",
+ "Menu:ProductAssetCategoryPeriod": "Asset category period prices",
+ "ProductAssetCategoryPeriod": "Asset category period prices",
+ "ProductAssetCategoryPeriodPeriodId": "Period ID",
+ "ProductAssetCategoryPeriodCurrency": "Currency",
+ "ProductAssetCategoryPeriodPrice": "Price",
+ "CreateProductAssetCategoryPeriod": "New",
+ "EditProductAssetCategoryPeriod": "Edit",
+ "ProductAssetCategoryPeriodDeletionConfirmationMessage": "Are you sure to delete the product asset category period {0}?",
+ "EasyAbp.EShop.Plugins.Booking:PeriodNotFound": "Period {id} not found.",
+ "EasyAbp.EShop.Plugins.Booking:DuplicatePeriod": "Period {id} exists.",
+ "EasyAbp.EShop.Plugins.Booking:ConflictingProductAsset": "Exist conflicting product asset mapping.",
+ "EasyAbp.EShop.Plugins.Booking:ConflictingProductAssetCategory": "Exist conflicting product asset category mapping.",
+ "EasyAbp.EShop.Plugins.Booking:WrongStoreIdForProduct": "Wrong StoreId {storeId} for product {productId}.",
+ "Permission:GrantedStore": "Store assets",
+ "Menu:GrantedStore": "Store assets",
+ "GrantedStore": "Store assets",
+ "GrantedStoreStoreId": "Store ID",
+ "GrantedStoreAssetId": "Asset ID",
+ "GrantedStoreAssetCategoryId": "Asset category ID",
+ "GrantedStoreAllowAll": "Allow all",
+ "CreateGrantedStore": "New",
+ "EditGrantedStore": "Edit",
+ "GrantedStoreDeletionConfirmationMessage": "Are you sure to delete the store asset category {0}?"
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/zh-Hans.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/zh-Hans.json
new file mode 100644
index 00000000..5b3c1ee3
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/zh-Hans.json
@@ -0,0 +1,73 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Menu:Booking": "预约业务",
+ "Permission:ProductAsset": "资产价格",
+ "Permission:Create": "新建",
+ "Permission:Update": "更新",
+ "Permission:Delete": "删除",
+ "Menu:ProductAsset": "资产价格",
+ "ProductAsset": "资产价格",
+ "ProductAssetStoreId": "店铺 ID",
+ "ProductAssetProductId": "产品 ID",
+ "ProductAssetProductSkuId": "产品 SKU ID",
+ "ProductAssetAssetId": "资产 ID",
+ "ProductAssetPeriodSchemeId": "时段方案 ID",
+ "ProductAssetFromTime": "生效时间",
+ "ProductAssetToTime": "结束时间",
+ "ProductAssetCurrency": "币种",
+ "ProductAssetPrice": "价格",
+ "ProductAssetPeriods": "时段价格",
+ "CreateProductAsset": "新建",
+ "EditProductAsset": "编辑",
+ "ProductAssetDeletionConfirmationMessage": "确认删除资产价格 {0}?",
+ "SuccessfullyDeleted": "删除成功",
+ "Menu:ProductAssetPeriod": "资产时段价格",
+ "ProductAssetPeriod": "资产时段价格",
+ "ProductAssetPeriodPeriodId": "时段 ID",
+ "ProductAssetPeriodCurrency": "币种",
+ "ProductAssetPeriodPrice": "价格",
+ "CreateProductAssetPeriod": "新建",
+ "EditProductAssetPeriod": "编辑",
+ "ProductAssetPeriodDeletionConfirmationMessage": "确认删除资产时段价格 {0}?",
+ "Permission:ProductAssetCategory": "资产类目价格",
+ "Menu:ProductAssetCategory": "资产类目价格",
+ "ProductAssetCategory": "资产类目价格",
+ "ProductAssetCategoryStoreId": "店铺 ID",
+ "ProductAssetCategoryProductId": "产品 ID",
+ "ProductAssetCategoryProductSkuId": "产品 SKU ID",
+ "ProductAssetCategoryAssetCategoryId": "资产目录 ID",
+ "ProductAssetCategoryPeriodSchemeId": "时段方案 ID",
+ "ProductAssetCategoryFromTime": "生效时间",
+ "ProductAssetCategoryToTime": "结束时间",
+ "ProductAssetCategoryCurrency": "币种",
+ "ProductAssetCategoryPrice": "价格",
+ "ProductAssetCategoryPeriods": "时段价格",
+ "CreateProductAssetCategory": "新建",
+ "EditProductAssetCategory": "编辑",
+ "ProductAssetCategoryDeletionConfirmationMessage": "确认删除资产类目价格 {0}?",
+ "Menu:ProductAssetCategoryPeriod": "资产类目时段价格",
+ "ProductAssetCategoryPeriod": "资产类目时段价格",
+ "ProductAssetCategoryPeriodPeriodId": "时段 ID",
+ "ProductAssetCategoryPeriodCurrency": "币种",
+ "ProductAssetCategoryPeriodPrice": "价格",
+ "CreateProductAssetCategoryPeriod": "新建",
+ "EditProductAssetCategoryPeriod": "编辑",
+ "ProductAssetCategoryPeriodDeletionConfirmationMessage": "确认删除资产类目时段价格 {0}?",
+ "EasyAbp.EShop.Plugins.Booking:PeriodNotFound": "时段 {id} 不存在。",
+ "EasyAbp.EShop.Plugins.Booking:DuplicatePeriod": "时段 {id} 已存在。",
+ "EasyAbp.EShop.Plugins.Booking:ConflictingProductAsset": "存在冲突的产品资产映射。",
+ "EasyAbp.EShop.Plugins.Booking:ConflictingProductAssetCategory": "存在冲突的产品资产映射。",
+ "EasyAbp.EShop.Plugins.Booking:WrongStoreIdForProduct": "错误的店铺 ID {storeId} 之于产品 {productId}。",
+ "Permission:GrantedStore": "店铺资产",
+ "Menu:GrantedStore": "店铺资产",
+ "GrantedStore": "店铺资产",
+ "GrantedStoreStoreId": "店铺 ID",
+ "GrantedStoreAssetId": "资产 ID",
+ "GrantedStoreAssetCategoryId": "资产目录 ID",
+ "GrantedStoreAllowAll": "允许所有",
+ "CreateGrantedStore": "新建",
+ "EditGrantedStore": "编辑",
+ "GrantedStoreDeletionConfirmationMessage": "确认删除店铺资产 {0}?"
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/zh-Hant.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/zh-Hant.json
new file mode 100644
index 00000000..17ac42d1
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/EasyAbp/EShop/Plugins/Booking/Localization/zh-Hant.json
@@ -0,0 +1,73 @@
+{
+ "culture": "zh-Hant",
+ "texts": {
+ "Menu:Booking": "預約業務",
+ "Permission:ProductAsset": "資產價格",
+ "Permission:Create": "新建",
+ "Permission:Update": "更新",
+ "Permission:Delete": "刪除",
+ "Menu:ProductAsset": "資產價格",
+ "ProductAsset": "資產價格",
+ "ProductAssetStoreId": "店鋪 ID",
+ "ProductAssetProductId": "產品 ID",
+ "ProductAssetProductSkuId": "產品 SKU ID",
+ "ProductAssetAssetId": "資產 ID",
+ "ProductAssetPeriodSchemeId": "時段方案 ID",
+ "ProductAssetFromTime": "生效時間",
+ "ProductAssetToTime": "結束時間",
+ "ProductAssetCurrency": "幣種",
+ "ProductAssetPrice": "價格",
+ "ProductAssetPeriods": "時段價格",
+ "CreateProductAsset": "新建",
+ "EditProductAsset": "編輯",
+ "ProductAssetDeletionConfirmationMessage": "確認刪除資產價格 {0}?",
+ "SuccessfullyDeleted": "刪除成功",
+ "Menu:ProductAssetPeriod": "資產時段價格",
+ "ProductAssetPeriod": "資產時段價格",
+ "ProductAssetPeriodPeriodId": "時段 ID",
+ "ProductAssetPeriodCurrency": "幣種",
+ "ProductAssetPeriodPrice": "價格",
+ "CreateProductAssetPeriod": "新建",
+ "EditProductAssetPeriod": "編輯",
+ "ProductAssetPeriodDeletionConfirmationMessage": "確認刪除資產時段價格 {0}?",
+ "Permission:ProductAssetCategory": "資產類目價格",
+ "Menu:ProductAssetCategory": "資產類目價格",
+ "ProductAssetCategory": "資產類目價格",
+ "ProductAssetCategoryStoreId": "店鋪 ID",
+ "ProductAssetCategoryProductId": "產品 ID",
+ "ProductAssetCategoryProductSkuId": "產品 SKU ID",
+ "ProductAssetCategoryAssetCategoryId": "資產目錄 ID",
+ "ProductAssetCategoryPeriodSchemeId": "時段方案 ID",
+ "ProductAssetCategoryFromTime": "生效時間",
+ "ProductAssetCategoryToTime": "結束時間",
+ "ProductAssetCategoryCurrency": "幣種",
+ "ProductAssetCategoryPrice": "價格",
+ "ProductAssetCategoryPeriods": "時段價格",
+ "CreateProductAssetCategory": "新建",
+ "EditProductAssetCategory": "編輯",
+ "ProductAssetCategoryDeletionConfirmationMessage": "確認刪除資產類目價格 {0}?",
+ "Menu:ProductAssetCategoryPeriod": "資產類目時段價格",
+ "ProductAssetCategoryPeriod": "資產類目時段價格",
+ "ProductAssetCategoryPeriodPeriodId": "時段 ID",
+ "ProductAssetCategoryPeriodCurrency": "幣種",
+ "ProductAssetCategoryPeriodPrice": "價格",
+ "CreateProductAssetCategoryPeriod": "新建",
+ "EditProductAssetCategoryPeriod": "編輯",
+ "ProductAssetCategoryPeriodDeletionConfirmationMessage": "確認刪除資產類目時段價格 {0}?",
+ "EasyAbp.EShop.Plugins.Booking:PeriodNotFound": "時段 {id} 不存在。",
+ "EasyAbp.EShop.Plugins.Booking:DuplicatePeriod": "時段 {id} 已存在。",
+ "EasyAbp.EShop.Plugins.Booking:ConflictingProductAsset": "存在衝突的產品資產映射。",
+ "EasyAbp.EShop.Plugins.Booking:ConflictingProductAssetCategory": "存在衝突的產品資產映射。",
+ "EasyAbp.EShop.Plugins.Booking:WrongStoreIdForProduct": "錯誤的店鋪 ID {storeId} 之於產品 {productId}。",
+ "Permission:GrantedStore": "店鋪資產",
+ "Menu:GrantedStore": "店鋪資產",
+ "GrantedStore": "店鋪資產",
+ "GrantedStoreStoreId": "店鋪 ID",
+ "GrantedStoreAssetId": "資產 ID",
+ "GrantedStoreAssetCategoryId": "資產目錄 ID",
+ "GrantedStoreAllowAll": "允許所有",
+ "CreateGrantedStore": "新建",
+ "EditGrantedStore": "編輯",
+ "GrantedStoreDeletionConfirmationMessage": "確認刪除店鋪資產 {0}?"
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain.Shared/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.abppkg.json
new file mode 100644
index 00000000..1d574efe
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.domain"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.csproj
new file mode 100644
index 00000000..5f474ec4
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp.EShop.Plugins.Booking.Domain.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/BookingDbProperties.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/BookingDbProperties.cs
new file mode 100644
index 00000000..5e2e0dfd
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/BookingDbProperties.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Plugins.Booking;
+
+public static class BookingDbProperties
+{
+ public static string DbTablePrefix { get; set; } = "EasyAbpEShopPluginsBooking";
+
+ public static string DbSchema { get; set; } = null;
+
+ public const string ConnectionStringName = "EasyAbpEShopPluginsBooking";
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingDomainModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingDomainModule.cs
new file mode 100644
index 00000000..3887ae06
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingDomainModule.cs
@@ -0,0 +1,15 @@
+using EasyAbp.EShop.Stores;
+using Volo.Abp.Domain;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(AbpDddDomainModule),
+ typeof(EShopStoresDomainSharedModule),
+ typeof(EShopPluginsBookingDomainSharedModule)
+)]
+public class EShopPluginsBookingDomainModule : AbpModule
+{
+
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStore.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStore.cs
new file mode 100644
index 00000000..4bd15e4d
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStore.cs
@@ -0,0 +1,39 @@
+using System;
+using Volo.Abp.Domain.Entities.Auditing;
+using Volo.Abp.MultiTenancy;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores;
+
+///
+/// Mapping of Store to Asset or AssetCategory.
+/// It determines which Asset or AssetCategory can a store owner set to provide booking service as a product of its store.
+///
+public class GrantedStore : AuditedAggregateRoot, IMultiTenant
+{
+ public virtual Guid? TenantId { get; protected set; }
+
+ public virtual Guid StoreId { get; protected set; }
+
+ public virtual Guid? AssetId { get; protected set; }
+
+ public virtual Guid? AssetCategoryId { get; protected set; }
+
+ ///
+ /// Allow using any assets in any categories.
+ ///
+ public virtual bool AllowAll { get; protected set; }
+
+ protected GrantedStore()
+ {
+ }
+
+ public GrantedStore(Guid id, Guid? tenantId, Guid storeId, Guid? assetId, Guid? assetCategoryId,
+ bool allowAll) : base(id)
+ {
+ TenantId = tenantId;
+ StoreId = storeId;
+ AssetId = assetId;
+ AssetCategoryId = assetCategoryId;
+ AllowAll = allowAll;
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/GrantedStores/IGrantedStoreRepository.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/GrantedStores/IGrantedStoreRepository.cs
new file mode 100644
index 00000000..3378ac16
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/GrantedStores/IGrantedStoreRepository.cs
@@ -0,0 +1,9 @@
+using System;
+using Volo.Abp.Domain.Repositories;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores
+{
+ public interface IGrantedStoreRepository : IRepository
+ {
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Options/BookingProductGroupDefinition.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Options/BookingProductGroupDefinition.cs
new file mode 100644
index 00000000..600105a6
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Options/BookingProductGroupDefinition.cs
@@ -0,0 +1,14 @@
+using JetBrains.Annotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.Options;
+
+public class BookingProductGroupDefinition
+{
+ [NotNull]
+ public string ProductGroupName { get; set; }
+
+ public BookingProductGroupDefinition([NotNull] string productGroupName)
+ {
+ ProductGroupName = productGroupName;
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Options/EShopBookingOptions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Options/EShopBookingOptions.cs
new file mode 100644
index 00000000..cc93687e
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Options/EShopBookingOptions.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace EasyAbp.EShop.Plugins.Booking.Options;
+
+public class EShopBookingOptions
+{
+ ///
+ /// Which product groups are for booking business.
+ ///
+ public List BookingProductGroups { get; } = new();
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/IProductAssetCategoryRepository.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/IProductAssetCategoryRepository.cs
new file mode 100644
index 00000000..cb60e3b9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/IProductAssetCategoryRepository.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories
+{
+ public interface IProductAssetCategoryRepository : IRepository
+ {
+ Task FindEffectiveAsync(DateTime currentTime, Guid storeId, Guid productId,
+ Guid productSkuId, Guid assetCategoryId, Guid periodSchemeId);
+
+ Task ExistConflictAsync(Guid id, Guid storeId, Guid productId, Guid productSkuId, Guid assetCategoryId,
+ Guid periodSchemeId, DateTime fromTime);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory.cs
new file mode 100644
index 00000000..8c4eedb4
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using EasyAbp.EShop.Plugins.Booking.Shared;
+using EasyAbp.EShop.Stores.Stores;
+using JetBrains.Annotations;
+using Volo.Abp;
+using Volo.Abp.Domain.Entities.Auditing;
+using Volo.Abp.MultiTenancy;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+
+///
+/// Mapping of ProductSku to AssetCategory.
+/// Set which SKU can use to book a specified asset category.
+/// The matched with the larger takes precedence.
+///
+public class ProductAssetCategory : AuditedAggregateRoot, IMultiStore, IMultiTenant
+{
+ public virtual Guid? TenantId { get; protected set; }
+
+ public virtual Guid StoreId { get; protected set; }
+
+ public virtual Guid ProductId { get; protected set; }
+
+ public virtual Guid ProductSkuId { get; protected set; }
+
+ public virtual Guid AssetCategoryId { get; protected set; }
+
+ public virtual Guid PeriodSchemeId { get; protected set; }
+
+ ///
+ /// When will this mapping start taking effect.
+ ///
+ public virtual DateTime FromTime { get; protected set; }
+
+ ///
+ /// When will this mapping stop taking effect.
+ /// Setting to null means until forever.
+ ///
+ public virtual DateTime? ToTime { get; protected set; }
+
+ ///
+ /// Should set if the is not null.
+ ///
+ [CanBeNull]
+ public virtual string Currency { get; protected set; }
+
+ ///
+ /// Price for any period.
+ /// Fall back to the price of ProductSku if null.
+ ///
+ public virtual decimal? Price { get; protected set; }
+
+ ///
+ /// Customize prices for specified periods.
+ ///
+ public List Periods { get; protected set; }
+
+ protected ProductAssetCategory()
+ {
+ }
+
+ public ProductAssetCategory(
+ Guid id,
+ Guid? tenantId,
+ Guid storeId,
+ Guid productId,
+ Guid productSkuId,
+ Guid assetCategoryId,
+ Guid periodSchemeId,
+ DateTime fromTime,
+ DateTime? toTime,
+ [CanBeNull] string currency,
+ decimal? price,
+ List periods
+ ) : base(id)
+ {
+ TenantId = tenantId;
+ StoreId = storeId;
+ ProductId = productId;
+ ProductSkuId = productSkuId;
+ AssetCategoryId = assetCategoryId;
+ PeriodSchemeId = periodSchemeId;
+ FromTime = fromTime;
+ ToTime = toTime;
+ SetPrice(currency, price);
+
+ Periods = periods ?? new List();
+ }
+
+ internal void Update(DateTime fromTime, DateTime? toTime, [CanBeNull] string currency, decimal? price)
+ {
+ FromTime = fromTime;
+ ToTime = toTime;
+ SetPrice(currency, price);
+ }
+
+ protected void SetPrice([CanBeNull] string currency, decimal? price)
+ {
+ if (price is not null)
+ {
+ Check.NotNull(currency, nameof(currency));
+ }
+
+ Currency = currency;
+ Price = price;
+ }
+
+ public void AddPeriod(ProductAssetCategoryPeriod productAssetCategoryPeriod)
+ {
+ if (FindPeriod(productAssetCategoryPeriod.PeriodId) is not null)
+ {
+ throw new DuplicatePeriodException(productAssetCategoryPeriod.PeriodId);
+ }
+
+ Periods.Add(productAssetCategoryPeriod);
+ }
+
+ public void RemovePeriod(Guid periodId)
+ {
+ Periods.Remove(GetPeriod(periodId));
+ }
+
+ public ProductAssetCategoryPeriod GetPeriod(Guid periodId)
+ {
+ return FindPeriod(periodId) ?? throw new PeriodNotFoundException(periodId);
+ }
+
+ public ProductAssetCategoryPeriod FindPeriod(Guid periodId)
+ {
+ return Periods.FirstOrDefault(x => x.PeriodId == periodId);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod.cs
new file mode 100644
index 00000000..9ca6cb8b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod.cs
@@ -0,0 +1,31 @@
+using System;
+using JetBrains.Annotations;
+using Volo.Abp.Domain.Entities;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+
+public class ProductAssetCategoryPeriod : Entity
+{
+ public virtual Guid PeriodId { get; protected set; }
+
+ [NotNull]
+ public virtual string Currency { get; protected set; }
+
+ public virtual decimal Price { get; protected set; }
+
+ protected ProductAssetCategoryPeriod()
+ {
+ }
+
+ public ProductAssetCategoryPeriod(
+ Guid id,
+ Guid periodId,
+ [NotNull] string currency,
+ decimal price
+ ) : base(id)
+ {
+ PeriodId = periodId;
+ Currency = currency;
+ Price = price;
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetManager.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetManager.cs
new file mode 100644
index 00000000..077a957d
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetManager.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Volo.Abp;
+using Volo.Abp.Domain.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+
+public class ProductAssetCategoryManager : DomainService
+{
+ private readonly IProductAssetCategoryRepository _repository;
+
+ public ProductAssetCategoryManager(IProductAssetCategoryRepository repository)
+ {
+ _repository = repository;
+ }
+
+ public virtual async Task CreateAsync(Guid storeId, Guid productId, Guid productSkuId,
+ Guid assetCategoryId,
+ Guid periodSchemeId, DateTime fromTime, DateTime? toTime, [CanBeNull] string currency, decimal? price)
+ {
+ var id = GuidGenerator.Create();
+
+ if (await _repository.ExistConflictAsync(
+ id, storeId, productId, productSkuId, assetCategoryId, periodSchemeId, fromTime))
+ {
+ throw new BusinessException(BookingErrorCodes.ConflictingProductAssetCategory);
+ }
+
+ return new ProductAssetCategory(GuidGenerator.Create(), CurrentTenant.Id, storeId, productId, productSkuId,
+ assetCategoryId,
+ periodSchemeId, fromTime, toTime, currency, price, new List());
+ }
+
+ public virtual async Task UpdateAsync(ProductAssetCategory entity, DateTime fromTime, DateTime? toTime,
+ [NotNull] string currency, decimal? price)
+ {
+ if (await _repository.ExistConflictAsync(entity.Id, entity.StoreId, entity.ProductId, entity.ProductSkuId,
+ entity.AssetCategoryId, entity.PeriodSchemeId, fromTime))
+ {
+ throw new BusinessException(BookingErrorCodes.ConflictingProductAssetCategory);
+ }
+
+ entity.Update(fromTime, toTime, currency, price);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/IProductAssetRepository.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/IProductAssetRepository.cs
new file mode 100644
index 00000000..6f28b848
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/IProductAssetRepository.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets
+{
+ public interface IProductAssetRepository : IRepository
+ {
+ Task FindEffectiveAsync(DateTime currentTime, Guid storeId, Guid productId, Guid productSkuId,
+ Guid assetId, Guid periodSchemeId);
+
+ Task ExistConflictAsync(Guid id, Guid storeId, Guid productId, Guid productSkuId, Guid assetId,
+ Guid periodSchemeId, DateTime fromTime);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAsset.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAsset.cs
new file mode 100644
index 00000000..96b030a7
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAsset.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.Shared;
+using EasyAbp.EShop.Stores.Stores;
+using JetBrains.Annotations;
+using Volo.Abp;
+using Volo.Abp.Domain.Entities.Auditing;
+using Volo.Abp.MultiTenancy;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets;
+
+///
+/// Mapping of ProductSku to Asset.
+/// Set which SKU can use to book a specified asset.
+/// The matched with the larger takes precedence.
+/// Fall back to if the booking asset has no matched .
+///
+public class ProductAsset : AuditedAggregateRoot, IMultiStore, IMultiTenant
+{
+ public virtual Guid? TenantId { get; protected set; }
+
+ public virtual Guid StoreId { get; protected set; }
+
+ public virtual Guid ProductId { get; protected set; }
+
+ public virtual Guid ProductSkuId { get; protected set; }
+
+ public virtual Guid AssetId { get; protected set; }
+
+ public virtual Guid PeriodSchemeId { get; protected set; }
+
+ ///
+ /// When will this mapping start taking effect.
+ ///
+ public virtual DateTime FromTime { get; protected set; }
+
+ ///
+ /// When will this mapping stop taking effect.
+ /// Setting to null means until forever.
+ ///
+ public virtual DateTime? ToTime { get; protected set; }
+
+ ///
+ /// Should set if the is not null.
+ ///
+ [CanBeNull]
+ public virtual string Currency { get; protected set; }
+
+ ///
+ /// Price for any period.
+ /// Fall back to the price of ProductSku if null.
+ ///
+ public virtual decimal? Price { get; protected set; }
+
+ ///
+ /// Customize prices for specified periods.
+ ///
+ public List Periods { get; protected set; }
+
+ protected ProductAsset()
+ {
+ }
+
+ internal ProductAsset(
+ Guid id,
+ Guid? tenantId,
+ Guid storeId,
+ Guid productId,
+ Guid productSkuId,
+ Guid assetId,
+ Guid periodSchemeId,
+ DateTime fromTime,
+ DateTime? toTime,
+ [CanBeNull] string currency,
+ decimal? price,
+ List periods
+ ) : base(id)
+ {
+ TenantId = tenantId;
+ StoreId = storeId;
+ ProductId = productId;
+ ProductSkuId = productSkuId;
+ AssetId = assetId;
+ PeriodSchemeId = periodSchemeId;
+ FromTime = fromTime;
+ ToTime = toTime;
+ SetPrice(currency, price);
+
+ Periods = periods ?? new List();
+ }
+
+ internal void Update(DateTime fromTime, DateTime? toTime, [CanBeNull] string currency, decimal? price)
+ {
+ FromTime = fromTime;
+ ToTime = toTime;
+ SetPrice(currency, price);
+ }
+
+ protected void SetPrice([CanBeNull] string currency, decimal? price)
+ {
+ if (price is not null)
+ {
+ Check.NotNull(currency, nameof(currency));
+ }
+
+ Currency = currency;
+ Price = price;
+ }
+
+ public void AddPeriod(ProductAssetPeriod productAssetPeriod)
+ {
+ if (FindPeriod(productAssetPeriod.PeriodId) is not null)
+ {
+ throw new DuplicatePeriodException(productAssetPeriod.PeriodId);
+ }
+
+ Periods.Add(productAssetPeriod);
+ }
+
+ public void RemovePeriod(Guid periodId)
+ {
+ Periods.Remove(GetPeriod(periodId));
+ }
+
+ public ProductAssetPeriod GetPeriod(Guid periodId)
+ {
+ return FindPeriod(periodId) ?? throw new PeriodNotFoundException(periodId);
+ }
+
+ public ProductAssetPeriod FindPeriod(Guid periodId)
+ {
+ return Periods.FirstOrDefault(x => x.PeriodId == periodId);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetManager.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetManager.cs
new file mode 100644
index 00000000..4a4db6f9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetManager.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Volo.Abp;
+using Volo.Abp.Domain.Services;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets;
+
+public class ProductAssetManager : DomainService
+{
+ private readonly IProductAssetRepository _repository;
+
+ public ProductAssetManager(IProductAssetRepository repository)
+ {
+ _repository = repository;
+ }
+
+ public virtual async Task CreateAsync(Guid storeId, Guid productId, Guid productSkuId, Guid assetId,
+ Guid periodSchemeId, DateTime fromTime, DateTime? toTime, [CanBeNull] string currency, decimal? price)
+ {
+ var id = GuidGenerator.Create();
+
+ if (await _repository.ExistConflictAsync(
+ id, storeId, productId, productSkuId, assetId, periodSchemeId, fromTime))
+ {
+ throw new BusinessException(BookingErrorCodes.ConflictingProductAsset);
+ }
+
+ return new ProductAsset(GuidGenerator.Create(), CurrentTenant.Id, storeId, productId, productSkuId, assetId,
+ periodSchemeId, fromTime, toTime, currency, price, new List());
+ }
+
+ public virtual async Task UpdateAsync(ProductAsset entity, DateTime fromTime, DateTime? toTime,
+ [NotNull] string currency, decimal? price)
+ {
+ if (await _repository.ExistConflictAsync(entity.Id, entity.StoreId, entity.ProductId, entity.ProductSkuId,
+ entity.AssetId, entity.PeriodSchemeId, fromTime))
+ {
+ throw new BusinessException(BookingErrorCodes.ConflictingProductAsset);
+ }
+
+ entity.Update(fromTime, toTime, currency, price);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetPeriod.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetPeriod.cs
new file mode 100644
index 00000000..03ba7948
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetPeriod.cs
@@ -0,0 +1,31 @@
+using System;
+using JetBrains.Annotations;
+using Volo.Abp.Domain.Entities;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets;
+
+public class ProductAssetPeriod : Entity
+{
+ public virtual Guid PeriodId { get; protected set; }
+
+ [NotNull]
+ public virtual string Currency { get; protected set; }
+
+ public virtual decimal Price { get; protected set; }
+
+ protected ProductAssetPeriod()
+ {
+ }
+
+ public ProductAssetPeriod(
+ Guid id,
+ Guid periodId,
+ [NotNull] string currency,
+ decimal price
+ ) : base(id)
+ {
+ PeriodId = periodId;
+ Currency = currency;
+ Price = price;
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Settings/BookingSettingDefinitionProvider.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Settings/BookingSettingDefinitionProvider.cs
new file mode 100644
index 00000000..35056318
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Settings/BookingSettingDefinitionProvider.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Settings;
+
+namespace EasyAbp.EShop.Plugins.Booking.Settings;
+
+public class BookingSettingDefinitionProvider : SettingDefinitionProvider
+{
+ public override void Define(ISettingDefinitionContext context)
+ {
+ /* Define module settings here.
+ * Use names from BookingSettings class.
+ */
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Settings/BookingSettings.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Settings/BookingSettings.cs
new file mode 100644
index 00000000..cc227799
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Settings/BookingSettings.cs
@@ -0,0 +1,10 @@
+namespace EasyAbp.EShop.Plugins.Booking.Settings;
+
+public static class BookingSettings
+{
+ public const string GroupName = "EasyAbp.EShop.Plugins.Booking";
+
+ /* Add constants for setting names. Example:
+ * public const string MySettingName = GroupName + ".MySettingName";
+ */
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Shared/DuplicatePeriodException.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Shared/DuplicatePeriodException.cs
new file mode 100644
index 00000000..7a491415
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Shared/DuplicatePeriodException.cs
@@ -0,0 +1,12 @@
+using System;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Plugins.Booking.Shared;
+
+public class DuplicatePeriodException : BusinessException
+{
+ public DuplicatePeriodException(Guid id) : base(BookingErrorCodes.DuplicatePeriod)
+ {
+ WithData(nameof(id), id);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Shared/PeriodNotFoundException.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Shared/PeriodNotFoundException.cs
new file mode 100644
index 00000000..a98ffe5c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/EasyAbp/EShop/Plugins/Booking/Shared/PeriodNotFoundException.cs
@@ -0,0 +1,12 @@
+using System;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Plugins.Booking.Shared;
+
+public class PeriodNotFoundException : BusinessException
+{
+ public PeriodNotFoundException(Guid id) : base(BookingErrorCodes.PeriodNotFound)
+ {
+ WithData(nameof(id), id);
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Domain/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.abppkg.json
new file mode 100644
index 00000000..e1c64f01
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.ef"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.csproj
new file mode 100644
index 00000000..25f08b68
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/BookingDbContext.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/BookingDbContext.cs
new file mode 100644
index 00000000..b8b429a8
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/BookingDbContext.cs
@@ -0,0 +1,34 @@
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Data;
+using Volo.Abp.EntityFrameworkCore;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+
+namespace EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+
+[ConnectionStringName(BookingDbProperties.ConnectionStringName)]
+public class BookingDbContext : AbpDbContext, IBookingDbContext
+{
+ /* Add DbSet for each Aggregate Root here. Example:
+ * public DbSet Questions { get; set; }
+ */
+ public DbSet ProductAssets { get; set; }
+ public DbSet ProductAssetPeriods { get; set; }
+ public DbSet ProductAssetCategoryPeriods { get; set; }
+ public DbSet ProductAssetCategories { get; set; }
+ public DbSet GrantedStores { get; set; }
+
+ public BookingDbContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+
+ protected override void OnModelCreating(ModelBuilder builder)
+ {
+ base.OnModelCreating(builder);
+
+ builder.ConfigureEShopPluginsBooking();
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/BookingDbContextModelCreatingExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/BookingDbContextModelCreatingExtensions.cs
new file mode 100644
index 00000000..c9d4f7fa
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/BookingDbContextModelCreatingExtensions.cs
@@ -0,0 +1,82 @@
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp;
+using Volo.Abp.EntityFrameworkCore.Modeling;
+
+namespace EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+
+public static class BookingDbContextModelCreatingExtensions
+{
+ public static void ConfigureEShopPluginsBooking(
+ this ModelBuilder builder)
+ {
+ Check.NotNull(builder, nameof(builder));
+
+ /* Configure all entities here. Example:
+
+ builder.Entity(b =>
+ {
+ //Configure table & schema name
+ b.ToTable(BookingDbProperties.DbTablePrefix + "Questions", BookingDbProperties.DbSchema);
+
+ b.ConfigureByConvention();
+
+ //Properties
+ b.Property(q => q.Title).IsRequired().HasMaxLength(QuestionConsts.MaxTitleLength);
+
+ //Relations
+ b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.QuestionId);
+
+ //Indexes
+ b.HasIndex(q => q.CreationTime);
+ });
+ */
+
+ builder.Entity(b =>
+ {
+ b.ToTable(BookingDbProperties.DbTablePrefix + "ProductAssets", BookingDbProperties.DbSchema);
+ b.ConfigureByConvention();
+
+ /* Configure more properties here */
+ b.Property(x => x.Price).HasColumnType("decimal(20,8)");
+ });
+
+ builder.Entity(b =>
+ {
+ b.ToTable(BookingDbProperties.DbTablePrefix + "ProductAssetPeriods", BookingDbProperties.DbSchema);
+ b.ConfigureByConvention();
+
+ /* Configure more properties here */
+ b.Property(x => x.Price).HasColumnType("decimal(20,8)");
+ });
+
+ builder.Entity(b =>
+ {
+ b.ToTable(BookingDbProperties.DbTablePrefix + "ProductAssetCategoryPeriods", BookingDbProperties.DbSchema);
+ b.ConfigureByConvention();
+
+ /* Configure more properties here */
+ b.Property(x => x.Price).HasColumnType("decimal(20,8)");
+ });
+
+ builder.Entity(b =>
+ {
+ b.ToTable(BookingDbProperties.DbTablePrefix + "ProductAssetCategories", BookingDbProperties.DbSchema);
+ b.ConfigureByConvention();
+
+ /* Configure more properties here */
+ b.Property(x => x.Price).HasColumnType("decimal(20,8)");
+ });
+
+
+ builder.Entity(b =>
+ {
+ b.ToTable(BookingDbProperties.DbTablePrefix + "GrantedStores", BookingDbProperties.DbSchema);
+ b.ConfigureByConvention();
+
+ /* Configure more properties here */
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/EShopPluginsBookingEntityFrameworkCoreModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/EShopPluginsBookingEntityFrameworkCoreModule.cs
new file mode 100644
index 00000000..43f60718
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/EShopPluginsBookingEntityFrameworkCoreModule.cs
@@ -0,0 +1,28 @@
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+
+[DependsOn(
+ typeof(EShopPluginsBookingDomainModule),
+ typeof(AbpEntityFrameworkCoreModule)
+)]
+public class EShopPluginsBookingEntityFrameworkCoreModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAbpDbContext(options =>
+ {
+ /* Add custom repositories here. Example:
+ * options.AddRepository();
+ */
+ options.AddRepository();
+ options.AddRepository();
+ options.AddRepository();
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/IBookingDbContext.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/IBookingDbContext.cs
new file mode 100644
index 00000000..d4c857b9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/EntityFrameworkCore/IBookingDbContext.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Data;
+using Volo.Abp.EntityFrameworkCore;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+
+namespace EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+
+[ConnectionStringName(BookingDbProperties.ConnectionStringName)]
+public interface IBookingDbContext : IEfCoreDbContext
+{
+ /* Add DbSet for each Aggregate Root here. Example:
+ * DbSet Questions { get; }
+ */
+ DbSet ProductAssets { get; set; }
+ DbSet ProductAssetPeriods { get; set; }
+ DbSet ProductAssetCategoryPeriods { get; set; }
+ DbSet ProductAssetCategories { get; set; }
+ DbSet GrantedStores { get; set; }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreEfCoreQuerableExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreEfCoreQuerableExtensions.cs
new file mode 100644
index 00000000..7588e700
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreEfCoreQuerableExtensions.cs
@@ -0,0 +1,18 @@
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores
+{
+ public static class GrantedStoreEfCoreQueryableExtensions
+ {
+ public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true)
+ {
+ if (!include)
+ {
+ return queryable;
+ }
+
+ return queryable;
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreRepository.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreRepository.cs
new file mode 100644
index 00000000..e0f9ee51
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreRepository.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores
+{
+ public class GrantedStoreRepository : EfCoreRepository, IGrantedStoreRepository
+ {
+ public GrantedStoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
+ {
+ }
+
+ public override async Task> WithDetailsAsync()
+ {
+ return (await GetQueryableAsync()).IncludeDetails();
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryEfCoreQuerableExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryEfCoreQuerableExtensions.cs
new file mode 100644
index 00000000..8e492583
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryEfCoreQuerableExtensions.cs
@@ -0,0 +1,18 @@
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories
+{
+ public static class ProductAssetCategoryEfCoreQueryableExtensions
+ {
+ public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true)
+ {
+ if (!include)
+ {
+ return queryable;
+ }
+
+ return queryable.Include(x => x.Periods);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryRepository.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryRepository.cs
new file mode 100644
index 00000000..eadbabb2
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryRepository.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories
+{
+ public class ProductAssetCategoryRepository : EfCoreRepository, IProductAssetCategoryRepository
+ {
+ public ProductAssetCategoryRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
+ {
+ }
+
+ public override async Task> WithDetailsAsync()
+ {
+ return (await GetQueryableAsync()).IncludeDetails();
+ }
+
+ public virtual async Task FindEffectiveAsync(DateTime currentTime, Guid storeId,
+ Guid productId, Guid productSkuId, Guid assetCategoryId, Guid periodSchemeId)
+ {
+ return await (await GetQueryableAsync())
+ .Where(x =>
+ x.StoreId == storeId &&
+ x.ProductId == productId &&
+ x.ProductSkuId == productSkuId &&
+ x.AssetCategoryId == assetCategoryId &&
+ x.PeriodSchemeId == periodSchemeId
+ && x.FromTime <= currentTime &&
+ (!x.ToTime.HasValue || x.ToTime.Value > currentTime))
+ .OrderByDescending(x => x.FromTime)
+ .FirstOrDefaultAsync();
+ }
+
+ public virtual async Task ExistConflictAsync(Guid id, Guid storeId, Guid productId, Guid productSkuId,
+ Guid assetCategoryId, Guid periodSchemeId, DateTime fromTime)
+ {
+ var conflict = await (await GetQueryableAsync())
+ .Where(x =>
+ x.StoreId == storeId &&
+ x.ProductId == productId &&
+ x.ProductSkuId == productSkuId &&
+ x.AssetCategoryId == assetCategoryId &&
+ x.PeriodSchemeId == periodSchemeId
+ && x.FromTime == fromTime)
+ .FirstOrDefaultAsync();
+
+ return conflict is not null && conflict.Id != id;
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetEfCoreQuerableExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetEfCoreQuerableExtensions.cs
new file mode 100644
index 00000000..ace46a94
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetEfCoreQuerableExtensions.cs
@@ -0,0 +1,18 @@
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets
+{
+ public static class ProductAssetEfCoreQueryableExtensions
+ {
+ public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true)
+ {
+ if (!include)
+ {
+ return queryable;
+ }
+
+ return queryable.Include(x => x.Periods);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetRepository.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetRepository.cs
new file mode 100644
index 00000000..de96b54b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetRepository.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets
+{
+ public class ProductAssetRepository : EfCoreRepository,
+ IProductAssetRepository
+ {
+ public ProductAssetRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
+ {
+ }
+
+ public override async Task> WithDetailsAsync()
+ {
+ return (await GetQueryableAsync()).IncludeDetails();
+ }
+
+ public virtual async Task FindEffectiveAsync(DateTime currentTime, Guid storeId, Guid productId,
+ Guid productSkuId, Guid assetId, Guid periodSchemeId)
+ {
+ return await (await GetQueryableAsync())
+ .Where(x =>
+ x.StoreId == storeId &&
+ x.ProductId == productId &&
+ x.ProductSkuId == productSkuId &&
+ x.AssetId == assetId &&
+ x.PeriodSchemeId == periodSchemeId
+ && x.FromTime <= currentTime &&
+ (!x.ToTime.HasValue || x.ToTime.Value > currentTime))
+ .OrderByDescending(x => x.FromTime)
+ .FirstOrDefaultAsync();
+ }
+
+ public virtual async Task ExistConflictAsync(Guid id, Guid storeId, Guid productId, Guid productSkuId,
+ Guid assetId, Guid periodSchemeId, DateTime fromTime)
+ {
+ var conflict = await (await GetQueryableAsync())
+ .Where(x =>
+ x.StoreId == storeId &&
+ x.ProductId == productId &&
+ x.ProductSkuId == productSkuId &&
+ x.AssetId == assetId &&
+ x.PeriodSchemeId == periodSchemeId
+ && x.FromTime == fromTime)
+ .FirstOrDefaultAsync();
+
+ return conflict is not null && conflict.Id != id;
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.EntityFrameworkCore/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.abppkg.json
new file mode 100644
index 00000000..7deef5e3
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.http-api-client"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.csproj
new file mode 100644
index 00000000..b0a9d266
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp.EShop.Plugins.Booking.HttpApi.Client.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingHttpApiClientModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingHttpApiClientModule.cs
new file mode 100644
index 00000000..01141509
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingHttpApiClientModule.cs
@@ -0,0 +1,26 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Http.Client;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(EShopPluginsBookingApplicationContractsModule),
+ typeof(AbpHttpClientModule))]
+public class EShopPluginsBookingHttpApiClientModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddHttpClientProxies(
+ typeof(EShopPluginsBookingApplicationContractsModule).Assembly,
+ EShopPluginsBookingRemoteServiceConsts.RemoteServiceName
+ );
+
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi.Client/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.abppkg.json
new file mode 100644
index 00000000..515bfe64
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.http-api"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.csproj
new file mode 100644
index 00000000..db3b4bc2
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp.EShop.Plugins.Booking.HttpApi.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/BookingController.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/BookingController.cs
new file mode 100644
index 00000000..e070f398
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/BookingController.cs
@@ -0,0 +1,14 @@
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[Area(EShopPluginsBookingRemoteServiceConsts.ModuleName)]
+public abstract class BookingController : AbpControllerBase
+{
+ protected BookingController()
+ {
+ LocalizationResource = typeof(BookingResource);
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingHttpApiModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingHttpApiModule.cs
new file mode 100644
index 00000000..fa0e39eb
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingHttpApiModule.cs
@@ -0,0 +1,32 @@
+using Localization.Resources.AbpUi;
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(EShopPluginsBookingApplicationContractsModule),
+ typeof(AbpAspNetCoreMvcModule))]
+public class EShopPluginsBookingHttpApiModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(EShopPluginsBookingHttpApiModule).Assembly);
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddBaseTypes(typeof(AbpUiResource));
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreController.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreController.cs
new file mode 100644
index 00000000..6e1025ba
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/GrantedStores/GrantedStoreController.cs
@@ -0,0 +1,57 @@
+using System;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using Volo.Abp.Application.Dtos;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp;
+using Volo.Abp.Application.Dtos;
+
+namespace EasyAbp.EShop.Plugins.Booking.GrantedStores
+{
+ [RemoteService(Name = "BookingGrantedStore")]
+ [Route("/api/booking/store-asset-category")]
+ public class GrantedStoreController : BookingController, IGrantedStoreAppService
+ {
+ private readonly IGrantedStoreAppService _service;
+
+ public GrantedStoreController(IGrantedStoreAppService service)
+ {
+ _service = service;
+ }
+
+ [HttpPost]
+ [Route("")]
+ public virtual Task CreateAsync(CreateUpdateGrantedStoreDto input)
+ {
+ return _service.CreateAsync(input);
+ }
+
+ [HttpPut]
+ [Route("{id}")]
+ public virtual Task UpdateAsync(Guid id, CreateUpdateGrantedStoreDto input)
+ {
+ return _service.UpdateAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}")]
+ public virtual Task DeleteAsync(Guid id)
+ {
+ return _service.DeleteAsync(id);
+ }
+
+ [HttpGet]
+ [Route("{id}")]
+ public virtual Task GetAsync(Guid id)
+ {
+ return _service.GetAsync(id);
+ }
+
+ [HttpGet]
+ [Route("")]
+ public virtual Task> GetListAsync(GetGrantedStoreListDto input)
+ {
+ return _service.GetListAsync(input);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryController.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryController.cs
new file mode 100644
index 00000000..4d037c7e
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryController.cs
@@ -0,0 +1,77 @@
+using System;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using Volo.Abp.Application.Dtos;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssetCategories
+{
+ [RemoteService(Name = EShopPluginsBookingRemoteServiceConsts.RemoteServiceName)]
+ [Route("/api/e-shop/plugins/booking/product-asset-category")]
+ public class ProductAssetCategoryController : BookingController, IProductAssetCategoryAppService
+ {
+ private readonly IProductAssetCategoryAppService _service;
+
+ public ProductAssetCategoryController(IProductAssetCategoryAppService service)
+ {
+ _service = service;
+ }
+
+ [HttpPost]
+ [Route("{productAssetCategoryId}/period")]
+ public virtual Task CreatePeriodAsync(Guid productAssetCategoryId, CreateProductAssetCategoryPeriodDto input)
+ {
+ return _service.CreatePeriodAsync(productAssetCategoryId, input);
+ }
+
+ [HttpPut]
+ [Route("{productAssetCategoryId}/period/{periodId}")]
+ public virtual Task UpdatePeriodAsync(Guid productAssetCategoryId, Guid periodId, UpdateProductAssetCategoryPeriodDto input)
+ {
+ return _service.UpdatePeriodAsync(productAssetCategoryId, periodId, input);
+ }
+
+ [HttpDelete]
+ [Route("{productAssetCategoryId}/period/{periodId}")]
+ public virtual Task DeletePeriodAsync(Guid productAssetCategoryId, Guid periodId)
+ {
+ return _service.DeletePeriodAsync(productAssetCategoryId, periodId);
+ }
+
+ [HttpPost]
+ [Route("")]
+ public virtual Task CreateAsync(CreateProductAssetCategoryDto input)
+ {
+ return _service.CreateAsync(input);
+ }
+
+ [HttpPut]
+ [Route("{id}")]
+ public virtual Task UpdateAsync(Guid id, UpdateProductAssetCategoryDto input)
+ {
+ return _service.UpdateAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}")]
+ public virtual Task DeleteAsync(Guid id)
+ {
+ return _service.DeleteAsync(id);
+ }
+
+ [HttpGet]
+ [Route("{id}")]
+ public virtual Task GetAsync(Guid id)
+ {
+ return _service.GetAsync(id);
+ }
+
+ [HttpGet]
+ [Route("")]
+ public virtual Task> GetListAsync(GetProductAssetCategoryListDto input)
+ {
+ return _service.GetListAsync(input);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetController.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetController.cs
new file mode 100644
index 00000000..90ddfca9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/EasyAbp/EShop/Plugins/Booking/ProductAssets/ProductAssetController.cs
@@ -0,0 +1,77 @@
+using System;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using Volo.Abp.Application.Dtos;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp;
+
+namespace EasyAbp.EShop.Plugins.Booking.ProductAssets
+{
+ [RemoteService(Name = EShopPluginsBookingRemoteServiceConsts.RemoteServiceName)]
+ [Route("/api/e-shop/plugins/booking/product-asset")]
+ public class ProductAssetController : BookingController, IProductAssetAppService
+ {
+ private readonly IProductAssetAppService _service;
+
+ public ProductAssetController(IProductAssetAppService service)
+ {
+ _service = service;
+ }
+
+ [HttpPost]
+ [Route("{productAssetId}/period")]
+ public virtual Task CreatePeriodAsync(Guid productAssetId, CreateProductAssetPeriodDto input)
+ {
+ return _service.CreatePeriodAsync(productAssetId, input);
+ }
+
+ [HttpPut]
+ [Route("{productAssetId}/period/{periodId}")]
+ public virtual Task UpdatePeriodAsync(Guid productAssetId, Guid periodId, UpdateProductAssetPeriodDto input)
+ {
+ return _service.UpdatePeriodAsync(productAssetId, periodId, input);
+ }
+
+ [HttpDelete]
+ [Route("{productAssetId}/period/{periodId}")]
+ public virtual Task DeletePeriodAsync(Guid productAssetId, Guid periodId)
+ {
+ return _service.DeletePeriodAsync(productAssetId, periodId);
+ }
+
+ [HttpPost]
+ [Route("")]
+ public virtual Task CreateAsync(CreateProductAssetDto input)
+ {
+ return _service.CreateAsync(input);
+ }
+
+ [HttpPut]
+ [Route("{id}")]
+ public virtual Task UpdateAsync(Guid id, UpdateProductAssetDto input)
+ {
+ return _service.UpdateAsync(id, input);
+ }
+
+ [HttpDelete]
+ [Route("{id}")]
+ public virtual Task DeleteAsync(Guid id)
+ {
+ return _service.DeleteAsync(id);
+ }
+
+ [HttpGet]
+ [Route("{id}")]
+ public virtual Task GetAsync(Guid id)
+ {
+ return _service.GetAsync(id);
+ }
+
+ [HttpGet]
+ [Route("")]
+ public virtual Task> GetListAsync(GetProductAssetListDto input)
+ {
+ return _service.GetListAsync(input);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.HttpApi/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp.EShop.Plugins.Booking.Installer.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp.EShop.Plugins.Booking.Installer.csproj
new file mode 100644
index 00000000..becba310
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp.EShop.Plugins.Booking.Installer.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+ net5.0
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp/EShop/Plugins/Booking/BookingInstallerPipelineBuilder.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp/EShop/Plugins/Booking/BookingInstallerPipelineBuilder.cs
new file mode 100644
index 00000000..1f13ec23
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp/EShop/Plugins/Booking/BookingInstallerPipelineBuilder.cs
@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Studio.ModuleInstalling;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
+[ExposeServices(typeof(IModuleInstallingPipelineBuilder))]
+public class BookingInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency
+{
+ public async Task BuildAsync(ModuleInstallingContext context)
+ {
+ return GetBasePipeline(context);
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingInstallerModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingInstallerModule.cs
new file mode 100644
index 00000000..3d2c9ffb
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/EasyAbp/EShop/Plugins/Booking/EShopPluginsBookingInstallerModule.cs
@@ -0,0 +1,20 @@
+using Volo.Abp.Studio;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace EasyAbp.EShop.Plugins.Booking;
+
+[DependsOn(
+ typeof(AbpStudioModuleInstallerModule),
+ typeof(AbpVirtualFileSystemModule)
+ )]
+public class EShopPluginsBookingInstallerModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/FodyWeavers.xml
new file mode 100644
index 00000000..be0de3a9
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/FodyWeavers.xsd
new file mode 100644
index 00000000..3f3946e2
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Installer/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.abppkg.json
new file mode 100644
index 00000000..8b23fd1e
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.mongodb"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.csproj
new file mode 100644
index 00000000..e9240415
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp.EShop.Plugins.Booking.MongoDB.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/BookingMongoDbContext.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/BookingMongoDbContext.cs
new file mode 100644
index 00000000..839188ee
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/BookingMongoDbContext.cs
@@ -0,0 +1,19 @@
+using Volo.Abp.Data;
+using Volo.Abp.MongoDB;
+
+namespace EasyAbp.EShop.Plugins.Booking.MongoDB;
+
+[ConnectionStringName(BookingDbProperties.ConnectionStringName)]
+public class BookingMongoDbContext : AbpMongoDbContext, IBookingMongoDbContext
+{
+ /* Add mongo collections here. Example:
+ * public IMongoCollection Questions => Collection();
+ */
+
+ protected override void CreateModel(IMongoModelBuilder modelBuilder)
+ {
+ base.CreateModel(modelBuilder);
+
+ modelBuilder.ConfigureBooking();
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/BookingMongoDbContextExtensions.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/BookingMongoDbContextExtensions.cs
new file mode 100644
index 00000000..f3443d25
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/BookingMongoDbContextExtensions.cs
@@ -0,0 +1,13 @@
+using Volo.Abp;
+using Volo.Abp.MongoDB;
+
+namespace EasyAbp.EShop.Plugins.Booking.MongoDB;
+
+public static class BookingMongoDbContextExtensions
+{
+ public static void ConfigureBooking(
+ this IMongoModelBuilder builder)
+ {
+ Check.NotNull(builder, nameof(builder));
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/EShopPluginsBookingMongoDbModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/EShopPluginsBookingMongoDbModule.cs
new file mode 100644
index 00000000..e846b769
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/EShopPluginsBookingMongoDbModule.cs
@@ -0,0 +1,22 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+using Volo.Abp.MongoDB;
+
+namespace EasyAbp.EShop.Plugins.Booking.MongoDB;
+
+[DependsOn(
+ typeof(EShopPluginsBookingDomainModule),
+ typeof(AbpMongoDbModule)
+ )]
+public class EShopPluginsBookingMongoDbModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddMongoDbContext(options =>
+ {
+ /* Add custom repositories here. Example:
+ * options.AddRepository();
+ */
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/IBookingMongoDbContext.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/IBookingMongoDbContext.cs
new file mode 100644
index 00000000..fcd15597
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/EasyAbp/EShop/Plugins/Booking/MongoDB/IBookingMongoDbContext.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.Data;
+using Volo.Abp.MongoDB;
+
+namespace EasyAbp.EShop.Plugins.Booking.MongoDB;
+
+[ConnectionStringName(BookingDbProperties.ConnectionStringName)]
+public interface IBookingMongoDbContext : IAbpMongoDbContext
+{
+ /* Define mongo collections here. Example:
+ * IMongoCollection Questions { get; }
+ */
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.MongoDB/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/BookingWebAutoMapperProfile.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/BookingWebAutoMapperProfile.cs
new file mode 100644
index 00000000..fac87b9f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/BookingWebAutoMapperProfile.cs
@@ -0,0 +1,41 @@
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssets.ProductAsset.ViewModels;
+using EasyAbp.EShop.Plugins.Booking.ProductAssets.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssets.ProductAssetPeriod.ViewModels;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using AutoMapper;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.ViewModels;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod.ViewModels;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore.ViewModels;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web;
+
+public class BookingWebAutoMapperProfile : Profile
+{
+ public BookingWebAutoMapperProfile()
+ {
+ /* You can configure your AutoMapper mapping configuration here.
+ * Alternatively, you can split your mapping configurations
+ * into multiple profile classes for a better organization. */
+ CreateMap();
+ CreateMap();
+ CreateMap();
+
+ CreateMap();
+ CreateMap();
+ CreateMap();
+
+ CreateMap();
+ CreateMap();
+ CreateMap();
+
+ CreateMap();
+ CreateMap();
+ CreateMap();
+
+ CreateMap();
+ CreateMap();
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EShopPluginsBookingWebModule.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EShopPluginsBookingWebModule.cs
new file mode 100644
index 00000000..405f8371
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EShopPluginsBookingWebModule.cs
@@ -0,0 +1,65 @@
+using EasyAbp.Abp.TagHelperPlus;
+using EasyAbp.BookingService;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.Extensions.DependencyInjection;
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using EasyAbp.EShop.Plugins.Booking.Web.Menus;
+using EasyAbp.EShop.Products;
+using EasyAbp.EShop.Stores;
+using Volo.Abp.AspNetCore.Mvc.Localization;
+using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Modularity;
+using Volo.Abp.UI.Navigation;
+using Volo.Abp.VirtualFileSystem;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web;
+
+[DependsOn(
+ typeof(AbpTagHelperPlusModule),
+ typeof(BookingServiceApplicationContractsModule),
+ typeof(EShopProductsApplicationContractsModule),
+ typeof(EShopStoresApplicationContractsModule),
+ typeof(EShopPluginsBookingApplicationContractsModule),
+ typeof(AbpAspNetCoreMvcUiThemeSharedModule),
+ typeof(AbpAutoMapperModule)
+)]
+public class EShopPluginsBookingWebModule : AbpModule
+{
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.PreConfigure(options =>
+ {
+ options.AddAssemblyResource(typeof(BookingResource), typeof(EShopPluginsBookingWebModule).Assembly);
+ });
+
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(EShopPluginsBookingWebModule).Assembly);
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.MenuContributors.Add(new BookingMenuContributor());
+ });
+
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ context.Services.AddAutoMapperObjectMapper();
+ Configure(options =>
+ {
+ options.AddMaps(validate: true);
+ });
+
+ Configure(options =>
+ {
+ //Configure authorization.
+ });
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.abppkg.json b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.abppkg.json
new file mode 100644
index 00000000..930c4018
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.abppkg.json
@@ -0,0 +1,3 @@
+{
+ "role": "lib.mvc"
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.csproj b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.csproj
new file mode 100644
index 00000000..7dc0e5e7
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/EasyAbp.EShop.Plugins.Booking.Web.csproj
@@ -0,0 +1,47 @@
+
+
+
+
+
+ net6.0
+ $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
+ true
+ Library
+ EasyAbp.EShop.Plugins.Booking.Web
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/FodyWeavers.xml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/FodyWeavers.xml
new file mode 100644
index 00000000..1715698c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/FodyWeavers.xsd b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/FodyWeavers.xsd
new file mode 100644
index 00000000..ffa6fc4b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Menus/BookingMenuContributor.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Menus/BookingMenuContributor.cs
new file mode 100644
index 00000000..e05b6a4c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Menus/BookingMenuContributor.cs
@@ -0,0 +1,60 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using EasyAbp.EShop.Plugins.Booking.Permissions;
+using Volo.Abp.UI.Navigation;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Menus;
+
+public class BookingMenuContributor : IMenuContributor
+{
+ public async Task ConfigureMenuAsync(MenuConfigurationContext context)
+ {
+ if (context.Menu.Name == StandardMenus.Main)
+ {
+ await ConfigureMainMenuAsync(context);
+ }
+ }
+
+ private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
+ {
+ var l = context.GetLocalizer();
+
+ //Add main menu items.
+
+ var bookingMenuItem = new ApplicationMenuItem(BookingMenus.Prefix, l["Menu:Booking"]);
+
+
+ if (await context.IsGrantedAsync(BookingPermissions.GrantedStore.Default))
+ {
+ bookingMenuItem.AddItem(
+ new ApplicationMenuItem(BookingMenus.GrantedStore, l["Menu:GrantedStore"],
+ "/EShop/Plugins/Booking/GrantedStores/GrantedStore")
+ );
+ }
+
+ if (await context.IsGrantedAsync(BookingPermissions.ProductAsset.Default))
+ {
+ bookingMenuItem.AddItem(
+ new ApplicationMenuItem(BookingMenus.ProductAsset, l["Menu:ProductAsset"],
+ "~/EShop/Plugins/Booking/ProductAssets/ProductAsset")
+ );
+ }
+
+ if (await context.IsGrantedAsync(BookingPermissions.ProductAssetCategory.Default))
+ {
+ bookingMenuItem.AddItem(
+ new ApplicationMenuItem(BookingMenus.ProductAssetCategory, l["Menu:ProductAssetCategory"],
+ "~/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory")
+ );
+ }
+
+ if (!bookingMenuItem.Items.IsNullOrEmpty())
+ {
+ var eShopMenuItem = context.Menu.Items.GetOrAdd(i => i.Name == BookingMenus.ModuleGroupPrefix,
+ () => new ApplicationMenuItem(BookingMenus.ModuleGroupPrefix, l["Menu:EasyAbpEShop"]));
+
+ eShopMenuItem.Items.Add(bookingMenuItem);
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Menus/BookingMenus.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Menus/BookingMenus.cs
new file mode 100644
index 00000000..f1021d5b
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Menus/BookingMenus.cs
@@ -0,0 +1,15 @@
+namespace EasyAbp.EShop.Plugins.Booking.Web.Menus;
+
+public class BookingMenus
+{
+ public const string ModuleGroupPrefix = "EasyAbp.EShop";
+
+ public const string Prefix = ModuleGroupPrefix + ".Plugins.Booking";
+
+ //Add your menu items here...
+ //public const string Home = Prefix + ".MyNewMenuItem";
+
+ public const string ProductAsset = Prefix + ".ProductAsset";
+ public const string ProductAssetCategory = Prefix + ".ProductAssetCategory";
+ public const string GrantedStore = Prefix + ".GrantedStore";
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/BookingPageModel.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/BookingPageModel.cs
new file mode 100644
index 00000000..a7999cd0
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/BookingPageModel.cs
@@ -0,0 +1,15 @@
+using EasyAbp.EShop.Plugins.Booking.Localization;
+using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages;
+
+/* Inherit your PageModel classes from this class.
+ */
+public abstract class BookingPageModel : AbpPageModel
+{
+ protected BookingPageModel()
+ {
+ LocalizationResourceType = typeof(BookingResource);
+ ObjectMapperContext = typeof(EShopPluginsBookingWebModule);
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal.cshtml
new file mode 100644
index 00000000..dca3c2e0
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal.cshtml
@@ -0,0 +1,18 @@
+@page
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@inject IHtmlLocalizer L
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore.CreateModalModel
+@{
+ Layout = null;
+}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal.cshtml.cs
new file mode 100644
index 00000000..2c493022
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal.cshtml.cs
@@ -0,0 +1,28 @@
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore.ViewModels;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore
+{
+ public class CreateModalModel : BookingPageModel
+ {
+ [BindProperty]
+ public CreateEditGrantedStoreViewModel ViewModel { get; set; }
+
+ private readonly IGrantedStoreAppService _service;
+
+ public CreateModalModel(IGrantedStoreAppService service)
+ {
+ _service = service;
+ }
+
+ public virtual async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(ViewModel);
+ await _service.CreateAsync(dto);
+ return NoContent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal.cshtml
new file mode 100644
index 00000000..b83d4373
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal.cshtml
@@ -0,0 +1,19 @@
+@page
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
+@inject IHtmlLocalizer L
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore.EditModalModel
+@{
+ Layout = null;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal.cshtml.cs
new file mode 100644
index 00000000..752f30f7
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal.cshtml.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores;
+using EasyAbp.EShop.Plugins.Booking.GrantedStores.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore.ViewModels;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore
+{
+ public class EditModalModel : BookingPageModel
+ {
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public Guid Id { get; set; }
+
+ [BindProperty]
+ public CreateEditGrantedStoreViewModel ViewModel { get; set; }
+
+ private readonly IGrantedStoreAppService _service;
+
+ public EditModalModel(IGrantedStoreAppService service)
+ {
+ _service = service;
+ }
+
+ public virtual async Task OnGetAsync()
+ {
+ var dto = await _service.GetAsync(Id);
+ ViewModel = ObjectMapper.Map(dto);
+ }
+
+ public virtual async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(ViewModel);
+ await _service.UpdateAsync(Id, dto);
+ return NoContent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/Index.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/Index.cshtml
new file mode 100644
index 00000000..5deda183
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/Index.cshtml
@@ -0,0 +1,48 @@
+@page
+@using EasyAbp.EShop.Plugins.Booking.Permissions
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Layout
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@using EasyAbp.EShop.Plugins.Booking.Web.Menus
+@using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore
+@model IndexModel
+@inject IPageLayout PageLayout
+@inject IHtmlLocalizer L
+@inject IAuthorizationService Authorization
+@{
+ PageLayout.Content.Title = L["GrantedStore"].Value;
+ PageLayout.Content.BreadCrumb.Add(L["Menu:GrantedStore"].Value);
+ PageLayout.Content.MenuItemName = BookingMenus.GrantedStore;
+}
+
+@section scripts
+{
+
+}
+@section styles
+{
+
+}
+
+
+
+
+
+ @L["GrantedStore"]
+
+
+ @if (await Authorization.IsGrantedAsync(BookingPermissions.GrantedStore.Create))
+ {
+
+ }
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/Index.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/Index.cshtml.cs
new file mode 100644
index 00000000..f3eca6ba
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/Index.cshtml.cs
@@ -0,0 +1,12 @@
+using System.Threading.Tasks;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore
+{
+ public class IndexModel : BookingPageModel
+ {
+ public virtual async Task OnGetAsync()
+ {
+ await Task.CompletedTask;
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/ViewModels/CreateEditGrantedStoreViewModel.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/ViewModels/CreateEditGrantedStoreViewModel.cs
new file mode 100644
index 00000000..d2c9551f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/ViewModels/CreateEditGrantedStoreViewModel.cs
@@ -0,0 +1,41 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using EasyAbp.Abp.TagHelperPlus.EasySelector;
+using EasyAbp.BookingService;
+using EasyAbp.EShop.Stores;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.GrantedStores.GrantedStore.ViewModels
+{
+ public class CreateEditGrantedStoreViewModel
+ {
+ [EasySelector(
+ getListedDataSourceUrl: StoresConsts.GetStoreListedDataSourceUrl + "?onlyManageable=true",
+ getSingleDataSourceUrl: StoresConsts.GetStoreSingleDataSourceUrl,
+ keyPropertyName: "id",
+ textPropertyName: "name",
+ moduleName: EShopStoresRemoteServiceConsts.ModuleName)]
+ [Display(Name = "GrantedStoreStoreId")]
+ public Guid StoreId { get; set; }
+
+ [EasySelector(
+ getListedDataSourceUrl: BookingServiceUrls.GetAssetListedDataSourceUrl,
+ getSingleDataSourceUrl: BookingServiceUrls.GetAssetSingleDataSourceUrl,
+ keyPropertyName: "id",
+ textPropertyName: "name",
+ moduleName: BookingServiceRemoteServiceConsts.ModuleName)]
+ [Display(Name = "GrantedStoreAssetId")]
+ public Guid? AssetId { get; set; }
+
+ [EasySelector(
+ getListedDataSourceUrl: BookingServiceUrls.GetAssetCategoryListedDataSourceUrl,
+ getSingleDataSourceUrl: BookingServiceUrls.GetAssetCategorySingleDataSourceUrl,
+ keyPropertyName: "id",
+ textPropertyName: "name",
+ moduleName: BookingServiceRemoteServiceConsts.ModuleName)]
+ [Display(Name = "GrantedStoreAssetCategoryId")]
+ public Guid? AssetCategoryId { get; set; }
+
+ [Display(Name = "GrantedStoreAllowAll")]
+ public bool AllowAll { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/index.css b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/index.css
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/index.js b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/index.js
new file mode 100644
index 00000000..86592191
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/GrantedStores/GrantedStore/index.js
@@ -0,0 +1,78 @@
+$(function () {
+
+ var l = abp.localization.getResource('EasyAbpEShopPluginsBooking');
+
+ var service = easyAbp.eShop.plugins.booking.grantedStores.grantedStore;
+ var createModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/Booking/GrantedStores/GrantedStore/CreateModal');
+ var editModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/Booking/GrantedStores/GrantedStore/EditModal');
+
+ var dataTable = $('#GrantedStoreTable').DataTable(abp.libs.datatables.normalizeConfiguration({
+ processing: true,
+ serverSide: true,
+ paging: true,
+ searching: false,
+ autoWidth: false,
+ scrollCollapse: true,
+ order: [[0, "asc"]],
+ ajax: abp.libs.datatables.createAjax(service.getList),
+ columnDefs: [
+ {
+ rowAction: {
+ items:
+ [
+ {
+ text: l('Edit'),
+ visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.Booking.GrantedStore.Update'),
+ action: function (data) {
+ editModal.open({ id: data.record.id });
+ }
+ },
+ {
+ text: l('Delete'),
+ visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.Booking.GrantedStore.Delete'),
+ confirmMessage: function (data) {
+ return l('GrantedStoreDeletionConfirmationMessage', data.record.id);
+ },
+ action: function (data) {
+ service.delete(data.record.id)
+ .then(function () {
+ abp.notify.info(l('SuccessfullyDeleted'));
+ dataTable.ajax.reload();
+ });
+ }
+ }
+ ]
+ }
+ },
+ {
+ title: l('GrantedStoreStoreId'),
+ data: "storeId"
+ },
+ {
+ title: l('GrantedStoreAssetId'),
+ data: "assetId"
+ },
+ {
+ title: l('GrantedStoreAssetCategoryId'),
+ data: "assetCategoryId"
+ },
+ {
+ title: l('GrantedStoreAllowAll'),
+ data: "allowAll"
+ },
+ ]
+ }));
+
+ createModal.onResult(function () {
+ dataTable.ajax.reload();
+ });
+
+ editModal.onResult(function () {
+ dataTable.ajax.reload();
+ });
+
+ $('#NewGrantedStoreButton').click(function (e) {
+ e.preventDefault();
+ createModal.open();
+ });
+});
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/Index.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/Index.cshtml
new file mode 100644
index 00000000..ba08a49c
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/Index.cshtml
@@ -0,0 +1,10 @@
+@page
+@using Microsoft.Extensions.Localization
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.IndexModel
+@inject IStringLocalizer L
+
+@{
+}
+Booking
+@L["SamplePageMessage"]
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/Index.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/Index.cshtml.cs
new file mode 100644
index 00000000..f0b1b4b3
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/Index.cshtml.cs
@@ -0,0 +1,8 @@
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking;
+
+public class IndexModel : BookingPageModel
+{
+ public void OnGet()
+ {
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal.cshtml
new file mode 100644
index 00000000..a161ba19
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal.cshtml
@@ -0,0 +1,18 @@
+@page
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@inject IHtmlLocalizer L
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.CreateModalModel
+@{
+ Layout = null;
+}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal.cshtml.cs
new file mode 100644
index 00000000..a6950c50
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal.cshtml.cs
@@ -0,0 +1,28 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.ViewModels;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory
+{
+ public class CreateModalModel : BookingPageModel
+ {
+ [BindProperty(SupportsGet = true)]
+ public CreateProductAssetCategoryViewModel ViewModel { get; set; }
+
+ private readonly IProductAssetCategoryAppService _service;
+
+ public CreateModalModel(IProductAssetCategoryAppService service)
+ {
+ _service = service;
+ }
+
+ public virtual async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(ViewModel);
+ await _service.CreateAsync(dto);
+ return NoContent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal.cshtml
new file mode 100644
index 00000000..9a04bee4
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal.cshtml
@@ -0,0 +1,19 @@
+@page
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
+@inject IHtmlLocalizer L
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.EditModalModel
+@{
+ Layout = null;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal.cshtml.cs
new file mode 100644
index 00000000..0dee727f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal.cshtml.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.ViewModels;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory
+{
+ public class EditModalModel : BookingPageModel
+ {
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public Guid Id { get; set; }
+
+ [BindProperty]
+ public EditProductAssetCategoryViewModel ViewModel { get; set; }
+
+ private readonly IProductAssetCategoryAppService _service;
+
+ public EditModalModel(IProductAssetCategoryAppService service)
+ {
+ _service = service;
+ }
+
+ public virtual async Task OnGetAsync()
+ {
+ var dto = await _service.GetAsync(Id);
+ ViewModel = ObjectMapper.Map(dto);
+ }
+
+ public virtual async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(ViewModel);
+ await _service.UpdateAsync(Id, dto);
+ return NoContent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/Index.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/Index.cshtml
new file mode 100644
index 00000000..8aa54d66
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/Index.cshtml
@@ -0,0 +1,75 @@
+@page
+@using EasyAbp.EShop.Plugins.Booking.Permissions
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Layout
+@using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@using EasyAbp.EShop.Plugins.Booking.Web.Menus
+@model IndexModel
+@inject IPageLayout PageLayout
+@inject IHtmlLocalizer L
+@inject IAuthorizationService Authorization
+@{
+ PageLayout.Content.Title = L["ProductAssetCategory"].Value;
+ PageLayout.Content.BreadCrumb.Add(L["Menu:ProductAssetCategory"].Value);
+ PageLayout.Content.MenuItemName = BookingMenus.ProductAssetCategory;
+}
+
+@section scripts
+{
+
+}
+@section styles
+{
+
+}
+
+
+
+@if (Model.Filter.StoreId == null)
+{
+
+
+
+
+
+ @L["ProductAssetCategory"]
+
+
+
+
+
+
+
+
+
+
+
+}
+else
+{
+
+
+
+
+ @L["ProductAssetCategory"]
+
+
+ @if (await Authorization.IsGrantedAsync(BookingPermissions.ProductAssetCategory.Create))
+ {
+
+ }
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/Index.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/Index.cshtml.cs
new file mode 100644
index 00000000..ab9be1b1
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/Index.cshtml.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.ViewModels;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory
+{
+ public class IndexModel : BookingPageModel
+ {
+ [BindProperty(SupportsGet = true)]
+ public ProductAssetCategoryListFilterViewModel Filter { get; set; }
+
+ public virtual async Task OnGetAsync()
+ {
+ await Task.CompletedTask;
+ }
+ }
+}
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/CreateProductAssetCategoryViewModel.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/CreateProductAssetCategoryViewModel.cs
new file mode 100644
index 00000000..463e6ee2
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/CreateProductAssetCategoryViewModel.cs
@@ -0,0 +1,38 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.ViewModels
+{
+ public class CreateProductAssetCategoryViewModel
+ {
+ [HiddenInput]
+ [Display(Name = "ProductAssetCategoryStoreId")]
+ public Guid StoreId { get; set; }
+
+ [Display(Name = "ProductAssetCategoryProductId")]
+ public Guid ProductId { get; set; }
+
+ [Display(Name = "ProductAssetCategoryProductSkuId")]
+ public Guid ProductSkuId { get; set; }
+
+ [Display(Name = "ProductAssetCategoryAssetCategoryId")]
+ public Guid AssetCategoryId { get; set; }
+
+ [Display(Name = "ProductAssetCategoryPeriodSchemeId")]
+ public Guid PeriodSchemeId { get; set; }
+
+ [Display(Name = "ProductAssetCategoryFromTime")]
+ public DateTime FromTime { get; set; }
+
+ [Display(Name = "ProductAssetCategoryToTime")]
+ public DateTime? ToTime { get; set; }
+
+ [Display(Name = "ProductAssetCategoryCurrency")]
+ public string Currency { get; set; }
+
+ [Display(Name = "ProductAssetCategoryPrice")]
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal? Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/EditProductAssetCategoryViewModel.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/EditProductAssetCategoryViewModel.cs
new file mode 100644
index 00000000..9a65756f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/EditProductAssetCategoryViewModel.cs
@@ -0,0 +1,22 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.
+ ViewModels
+{
+ public class EditProductAssetCategoryViewModel
+ {
+ [Display(Name = "ProductAssetCategoryFromTime")]
+ public DateTime FromTime { get; set; }
+
+ [Display(Name = "ProductAssetCategoryToTime")]
+ public DateTime? ToTime { get; set; }
+
+ [Display(Name = "ProductAssetCategoryCurrency")]
+ public string Currency { get; set; }
+
+ [Display(Name = "ProductAssetCategoryPrice")]
+ [Range(BookingConsts.MinimumPrice, BookingConsts.MaximumPrice)]
+ public decimal? Price { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/ProductAssetCategoryListFilterViewModel.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/ProductAssetCategoryListFilterViewModel.cs
new file mode 100644
index 00000000..5fdf891a
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/ViewModels/ProductAssetCategoryListFilterViewModel.cs
@@ -0,0 +1,19 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using EasyAbp.Abp.TagHelperPlus.EasySelector;
+using EasyAbp.EShop.Stores;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategory.ViewModels
+{
+ public class ProductAssetCategoryListFilterViewModel
+ {
+ [EasySelector(
+ getListedDataSourceUrl: StoresConsts.GetStoreListedDataSourceUrl + "?onlyManageable=true",
+ getSingleDataSourceUrl: StoresConsts.GetStoreSingleDataSourceUrl,
+ keyPropertyName: "id",
+ textPropertyName: "name",
+ runScriptOnWindowLoad: true)]
+ [Display(Name = "ProductAssetCategoryStoreId")]
+ public Guid? StoreId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/index.css b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/index.css
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/index.js b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/index.js
new file mode 100644
index 00000000..651cda5f
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/index.js
@@ -0,0 +1,109 @@
+$(function () {
+
+ var l = abp.localization.getResource('EasyAbpEShopPluginsBooking');
+
+ var service = easyAbp.eShop.plugins.booking.productAssetCategories.productAssetCategory;
+ var createModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/CreateModal');
+ var editModal = new abp.ModalManager(abp.appPath + 'EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategory/EditModal');
+
+ var dataTable = $('#ProductAssetCategoryTable').DataTable(abp.libs.datatables.normalizeConfiguration({
+ processing: true,
+ serverSide: true,
+ paging: true,
+ searching: false,
+ autoWidth: false,
+ scrollCollapse: true,
+ order: [[0, "asc"]],
+ ajax: abp.libs.datatables.createAjax(service.getList, function () {
+ return { storeId: storeId }
+ }),
+ columnDefs: [
+ {
+ rowAction: {
+ items:
+ [
+ {
+ text: l('ProductAssetCategoryPeriods'),
+ action: function (data) {
+ document.location.href = abp.appPath + 'EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod?ProductAssetCategoryId=' + data.record.id;
+ }
+ },
+ {
+ text: l('Edit'),
+ visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.Booking.ProductAssetCategory.Update'),
+ action: function (data) {
+ editModal.open({ id: data.record.id });
+ }
+ },
+ {
+ text: l('Delete'),
+ visible: abp.auth.isGranted('EasyAbp.EShop.Plugins.Booking.ProductAssetCategory.Delete'),
+ confirmMessage: function (data) {
+ return l('ProductAssetCategoryDeletionConfirmationMessage', data.record.id);
+ },
+ action: function (data) {
+ service.delete(data.record.id)
+ .then(function () {
+ abp.notify.info(l('SuccessfullyDeleted'));
+ dataTable.ajax.reload();
+ });
+ }
+ }
+ ]
+ }
+ },
+ {
+ title: l('ProductAssetCategoryAssetCategoryId'),
+ data: "assetCategoryId"
+ },
+ {
+ title: l('ProductAssetCategoryCurrency'),
+ data: "currency"
+ },
+ {
+ title: l('ProductAssetCategoryPrice'),
+ data: "price"
+ },
+ {
+ title: l('ProductAssetCategoryPeriodSchemeId'),
+ data: "periodSchemeId"
+ },
+ {
+ title: l('ProductAssetCategoryFromTime'),
+ data: "fromTime"
+ },
+ {
+ title: l('ProductAssetCategoryToTime'),
+ data: "toTime"
+ },
+ {
+ title: l('ProductAssetCategoryProductId'),
+ data: "productId"
+ },
+ {
+ title: l('ProductAssetCategoryProductSkuId'),
+ data: "productSkuId"
+ },
+ ]
+ }));
+
+ createModal.onResult(function () {
+ dataTable.ajax.reload();
+ });
+
+ editModal.onResult(function () {
+ dataTable.ajax.reload();
+ });
+
+ $('#NewProductAssetCategoryButton').click(function (e) {
+ e.preventDefault();
+ createModal.open({ storeId: storeId });
+ });
+
+ $('#enter-button').click(function (e) {
+ e.preventDefault();
+ var storeId = $('#Filter_StoreId').val();
+
+ document.location.href = document.location.origin + document.location.pathname + '?storeId=' + storeId;
+ })
+});
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/CreateModal.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/CreateModal.cshtml
new file mode 100644
index 00000000..707aabee
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/CreateModal.cshtml
@@ -0,0 +1,19 @@
+@page
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@inject IHtmlLocalizer L
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod.CreateModalModel
+@{
+ Layout = null;
+}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/CreateModal.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/CreateModal.cshtml.cs
new file mode 100644
index 00000000..39283e00
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/CreateModal.cshtml.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod.ViewModels;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod
+{
+ public class CreateModalModel : BookingPageModel
+ {
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public Guid ProductAssetCategoryId { get; set; }
+
+ [BindProperty]
+ public CreateProductAssetCategoryPeriodViewModel ViewModel { get; set; }
+
+ private readonly IProductAssetCategoryAppService _service;
+
+ public CreateModalModel(IProductAssetCategoryAppService service)
+ {
+ _service = service;
+ }
+
+ public virtual async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(ViewModel);
+ await _service.CreatePeriodAsync(ProductAssetCategoryId, dto);
+ return NoContent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/EditModal.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/EditModal.cshtml
new file mode 100644
index 00000000..354c1266
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/EditModal.cshtml
@@ -0,0 +1,20 @@
+@page
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal;
+@inject IHtmlLocalizer L
+@model EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod.EditModalModel
+@{
+ Layout = null;
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/EditModal.cshtml.cs b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/EditModal.cshtml.cs
new file mode 100644
index 00000000..a92297fb
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/EditModal.cshtml.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories;
+using EasyAbp.EShop.Plugins.Booking.ProductAssetCategories.Dtos;
+using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod.ViewModels;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod
+{
+ public class EditModalModel : BookingPageModel
+ {
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public Guid ProductAssetCategoryId { get; set; }
+
+ [HiddenInput]
+ [BindProperty(SupportsGet = true)]
+ public Guid PeriodId { get; set; }
+
+ [BindProperty]
+ public EditProductAssetCategoryPeriodViewModel ViewModel { get; set; }
+
+ private readonly IProductAssetCategoryAppService _service;
+
+ public EditModalModel(IProductAssetCategoryAppService service)
+ {
+ _service = service;
+ }
+
+ public virtual async Task OnGetAsync()
+ {
+ var dto = await _service.GetAsync(ProductAssetCategoryId);
+ ViewModel = ObjectMapper.Map(
+ dto.Periods.Single(x => x.PeriodId == PeriodId));
+ }
+
+ public virtual async Task OnPostAsync()
+ {
+ var dto = ObjectMapper.Map(ViewModel);
+ await _service.UpdatePeriodAsync(ProductAssetCategoryId, PeriodId, dto);
+ return NoContent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/Index.cshtml b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/Index.cshtml
new file mode 100644
index 00000000..5095bfe4
--- /dev/null
+++ b/plugins/Booking/src/EasyAbp.EShop.Plugins.Booking.Web/Pages/EShop/Plugins/Booking/ProductAssetCategories/ProductAssetCategoryPeriod/Index.cshtml
@@ -0,0 +1,52 @@
+@page
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Mvc.Localization
+@using Volo.Abp.AspNetCore.Mvc.UI.Layout
+@using EasyAbp.EShop.Plugins.Booking.Web.Pages.EShop.Plugins.Booking.ProductAssetCategories.ProductAssetCategoryPeriod
+@using EasyAbp.EShop.Plugins.Booking.Localization
+@using EasyAbp.EShop.Plugins.Booking.Permissions
+@using EasyAbp.EShop.Plugins.Booking.Web.Menus
+@model IndexModel
+@inject IPageLayout PageLayout
+@inject IHtmlLocalizer L
+@inject IAuthorizationService Authorization
+@{
+ PageLayout.Content.Title = L["ProductAssetCategoryPeriod"].Value;
+ PageLayout.Content.BreadCrumb.Add(L["Menu:ProductAssetCategoryPeriod"].Value);
+ PageLayout.Content.MenuItemName = BookingMenus.ProductAssetCategory;
+}
+
+@section scripts
+{
+
+}
+@section styles
+{
+
+}
+
+
+
+
+
+
+