diff --git a/src/Libraries/Nop.Core/Domain/Vendors/Vendor.cs b/src/Libraries/Nop.Core/Domain/Vendors/Vendor.cs
index a89652794f8..564037c34a5 100644
--- a/src/Libraries/Nop.Core/Domain/Vendors/Vendor.cs
+++ b/src/Libraries/Nop.Core/Domain/Vendors/Vendor.cs
@@ -1,106 +1,125 @@
-using Nop.Core.Domain.Common;
-using Nop.Core.Domain.Localization;
-using Nop.Core.Domain.Seo;
-
-namespace Nop.Core.Domain.Vendors;
-
-///
-/// Represents a vendor
-///
-public partial class Vendor : BaseEntity, ILocalizedEntity, ISlugSupported, ISoftDeletedEntity
-{
- ///
- /// Gets or sets the name
- ///
- public string Name { get; set; }
-
- ///
- /// Gets or sets the email
- ///
- public string Email { get; set; }
-
- ///
- /// Gets or sets the description
- ///
- public string Description { get; set; }
-
- ///
- /// Gets or sets the picture identifier
- ///
- public int PictureId { get; set; }
-
- ///
- /// Gets or sets the address identifier
- ///
- public int AddressId { get; set; }
-
- ///
- /// Gets or sets the admin comment
- ///
- public string AdminComment { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the entity is active
- ///
- public bool Active { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the entity has been deleted
- ///
- public bool Deleted { get; set; }
-
- ///
- /// Gets or sets the display order
- ///
- public int DisplayOrder { get; set; }
-
- ///
- /// Gets or sets the meta keywords
- ///
- public string MetaKeywords { get; set; }
-
- ///
- /// Gets or sets the meta description
- ///
- public string MetaDescription { get; set; }
-
- ///
- /// Gets or sets the meta title
- ///
- public string MetaTitle { get; set; }
-
- ///
- /// Gets or sets the page size
- ///
- public int PageSize { get; set; }
-
- ///
- /// Gets or sets a value indicating whether customers can select the page size
- ///
- public bool AllowCustomersToSelectPageSize { get; set; }
-
- ///
- /// Gets or sets the available customer selectable page size options
- ///
- public string PageSizeOptions { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the price range filtering is enabled
- ///
- public bool PriceRangeFiltering { get; set; }
-
- ///
- /// Gets or sets the "from" price
- ///
- public decimal PriceFrom { get; set; }
-
- ///
- /// Gets or sets the "to" price
- ///
- public decimal PriceTo { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the price range should be entered manually
- ///
- public bool ManuallyPriceRange { get; set; }
-}
\ No newline at end of file
+using Nop.Core.Domain.Common;
+using Nop.Core.Domain.Localization;
+using Nop.Core.Domain.Seo;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Nop.Core.Domain.Vendors;
+
+///
+/// Represents a vendor
+///
+public partial class Vendor : BaseEntity, ILocalizedEntity, ISlugSupported, ISoftDeletedEntity
+{
+ ///
+ /// Gets or sets the name
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Gets or sets the email
+ ///
+ public string Email { get; set; }
+
+ ///
+ /// Gets or sets the description
+ ///
+ public string Description { get; set; }
+
+ ///
+ /// Gets or sets the picture identifier
+ ///
+ public int PictureId { get; set; }
+
+ ///
+ /// Gets or sets the address identifier
+ ///
+ public int AddressId { get; set; }
+
+ ///
+ /// Gets or sets the admin comment
+ ///
+ public string AdminComment { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the entity is active
+ ///
+ public bool Active { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the entity has been deleted
+ ///
+ public bool Deleted { get; set; }
+
+ ///
+ /// Gets or sets the display order
+ ///
+ public int DisplayOrder { get; set; }
+
+ ///
+ /// Gets or sets the meta keywords
+ ///
+ public string MetaKeywords { get; set; }
+
+ ///
+ /// Gets or sets the meta description
+ ///
+ public string MetaDescription { get; set; }
+
+ ///
+ /// Gets or sets the meta title
+ ///
+ public string MetaTitle { get; set; }
+
+ ///
+ /// Gets or sets the page size
+ ///
+ public int PageSize { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether customers can select the page size
+ ///
+ public bool AllowCustomersToSelectPageSize { get; set; }
+
+ ///
+ /// Gets or sets the available customer selectable page size options
+ ///
+ public string PageSizeOptions { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the price range filtering is enabled
+ ///
+ public bool PriceRangeFiltering { get; set; }
+
+ ///
+ /// Gets or sets the "from" price
+ ///
+ public decimal PriceFrom { get; set; }
+
+ ///
+ /// Gets or sets the "to" price
+ ///
+ public decimal PriceTo { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the price range should be entered manually
+ ///
+ public bool ManuallyPriceRange { get; set; }
+
+ ///
+ /// Gets or sets the list of vendor reviews
+ ///
+ public virtual ICollection VendorReviews { get; set; } = new List();
+
+ ///
+ /// Calculates the average rating from the vendor reviews
+ ///
+ /// The average rating
+ public double CalculateAverageRating()
+ {
+ if (VendorReviews == null || !VendorReviews.Any())
+ return 0;
+
+ return VendorReviews.Average(r => r.Rating);
+ }
+}
diff --git a/src/Libraries/Nop.Core/Domain/Vendors/VendorReview.cs b/src/Libraries/Nop.Core/Domain/Vendors/VendorReview.cs
new file mode 100644
index 00000000000..85c67963cb2
--- /dev/null
+++ b/src/Libraries/Nop.Core/Domain/Vendors/VendorReview.cs
@@ -0,0 +1,37 @@
+namespace Nop.Core.Domain.Vendors;
+
+///
+/// Represents a vendor review
+///
+public partial class VendorReview : BaseEntity
+{
+ ///
+ /// Gets or sets the vendor identifier
+ ///
+ public int VendorId { get; set; }
+
+ ///
+ /// Gets or sets the customer identifier
+ ///
+ public int CustomerId { get; set; }
+
+ ///
+ /// Gets or sets the rating
+ ///
+ public int Rating { get; set; }
+
+ ///
+ /// Gets or sets the review text
+ ///
+ public string ReviewText { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the content is approved
+ ///
+ public bool IsApproved { get; set; }
+
+ ///
+ /// Gets or sets the date and time of instance creation
+ ///
+ public DateTime CreatedOnUtc { get; set; }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/Caching/VendorReviewCacheEventConsumer.cs b/src/Libraries/Nop.Services/Catalog/Caching/VendorReviewCacheEventConsumer.cs
new file mode 100644
index 00000000000..dc3497b25c7
--- /dev/null
+++ b/src/Libraries/Nop.Services/Catalog/Caching/VendorReviewCacheEventConsumer.cs
@@ -0,0 +1,11 @@
+using Nop.Core.Domain.Vendors;
+using Nop.Services.Caching;
+
+namespace Nop.Services.Catalog.Caching;
+
+///
+/// Represents a vendor review cache event consumer
+///
+public partial class VendorReviewCacheEventConsumer : CacheEventConsumer
+{
+}
diff --git a/src/Presentation/Nop.Web/Areas/Admin/Controllers/VendorController.cs b/src/Presentation/Nop.Web/Areas/Admin/Controllers/VendorController.cs
index f8f693b3b9e..ee63ad2c5d5 100644
--- a/src/Presentation/Nop.Web/Areas/Admin/Controllers/VendorController.cs
+++ b/src/Presentation/Nop.Web/Areas/Admin/Controllers/VendorController.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
@@ -41,6 +41,7 @@ public partial class VendorController : BaseAdminController
protected readonly IUrlRecordService _urlRecordService;
protected readonly IVendorModelFactory _vendorModelFactory;
protected readonly IVendorService _vendorService;
+ protected readonly IVendorReviewService _vendorReviewService;
private static readonly char[] _separator = [','];
#endregion
@@ -61,7 +62,8 @@ public VendorController(IAddressService addressService,
IPictureService pictureService,
IUrlRecordService urlRecordService,
IVendorModelFactory vendorModelFactory,
- IVendorService vendorService)
+ IVendorService vendorService,
+ IVendorReviewService vendorReviewService)
{
_addressService = addressService;
_addressAttributeParser = addressAttributeParser;
@@ -78,6 +80,7 @@ public VendorController(IAddressService addressService,
_urlRecordService = urlRecordService;
_vendorModelFactory = vendorModelFactory;
_vendorService = vendorService;
+ _vendorReviewService = vendorReviewService;
}
#endregion
@@ -499,4 +502,61 @@ public virtual async Task VendorNoteDelete(int id)
}
#endregion
-}
\ No newline at end of file
+
+ #region Vendor reviews
+
+ [HttpPost]
+ [CheckPermission(StandardPermission.Customers.VENDORS_VIEW)]
+ public virtual async Task VendorReviewsSelect(VendorReviewSearchModel searchModel)
+ {
+ //try to get a vendor with the specified id
+ var vendor = await _vendorService.GetVendorByIdAsync(searchModel.VendorId)
+ ?? throw new ArgumentException("No vendor found with the specified id");
+
+ //prepare model
+ var model = await _vendorModelFactory.PrepareVendorReviewListModelAsync(searchModel, vendor);
+
+ return Json(model);
+ }
+
+ [CheckPermission(StandardPermission.Customers.VENDORS_CREATE_EDIT_DELETE)]
+ public virtual async Task VendorReviewAdd(int vendorId, VendorReviewModel model)
+ {
+ if (model == null)
+ return ErrorJson(await _localizationService.GetResourceAsync("Admin.Vendors.VendorReviews.Fields.Review.Validation"));
+
+ //try to get a vendor with the specified id
+ var vendor = await _vendorService.GetVendorByIdAsync(vendorId);
+ if (vendor == null)
+ return ErrorJson("Vendor cannot be loaded");
+
+ var vendorReview = new VendorReview
+ {
+ VendorId = vendor.Id,
+ CustomerId = model.CustomerId,
+ Rating = model.Rating,
+ ReviewText = model.ReviewText,
+ IsApproved = model.IsApproved,
+ CreatedOnUtc = DateTime.UtcNow
+ };
+
+ await _vendorReviewService.InsertVendorReviewAsync(vendorReview);
+
+ return Json(new { Result = true });
+ }
+
+ [HttpPost]
+ [CheckPermission(StandardPermission.Customers.VENDORS_CREATE_EDIT_DELETE)]
+ public virtual async Task VendorReviewDelete(int id)
+ {
+ //try to get a vendor review with the specified id
+ var vendorReview = await _vendorReviewService.GetVendorReviewByIdAsync(id)
+ ?? throw new ArgumentException("No vendor review found with the specified id", nameof(id));
+
+ await _vendorReviewService.DeleteVendorReviewAsync(vendorReview);
+
+ return new NullJsonResult();
+ }
+
+ #endregion
+}