diff --git a/Domus.DAL/Domus.DAL.csproj b/Domus.DAL/Domus.DAL.csproj
index 770beee..b38ad7f 100644
--- a/Domus.DAL/Domus.DAL.csproj
+++ b/Domus.DAL/Domus.DAL.csproj
@@ -16,4 +16,9 @@
+
+
+
+
+
diff --git a/Domus.DAL/Implementations/ProductDetailQuoationRevisionRepository.cs b/Domus.DAL/Implementations/ProductDetailQuoationRevisionRepository.cs
new file mode 100644
index 0000000..799d175
--- /dev/null
+++ b/Domus.DAL/Implementations/ProductDetailQuoationRevisionRepository.cs
@@ -0,0 +1,11 @@
+using Domus.DAL.Interfaces;
+using Domus.Domain.Entities;
+
+namespace Domus.DAL.Implementations;
+
+public class ProductDetailQuoationRevisionRepository : GenericRepository, IProductDetailQuotationRevisionRepository
+{
+ public ProductDetailQuoationRevisionRepository(IAppDbContext dbContext) : base(dbContext)
+ {
+ }
+}
\ No newline at end of file
diff --git a/Domus.DAL/Implementations/QuotationRevisionRepository.cs b/Domus.DAL/Implementations/QuotationRevisionRepository.cs
new file mode 100644
index 0000000..fca428d
--- /dev/null
+++ b/Domus.DAL/Implementations/QuotationRevisionRepository.cs
@@ -0,0 +1,11 @@
+using Domus.DAL.Interfaces;
+using Domus.Domain.Entities;
+
+namespace Domus.DAL.Implementations;
+
+public class QuotationRevisionRepository : GenericRepository, IQuotationRevisionRepository
+{
+ public QuotationRevisionRepository(IAppDbContext dbContext) : base(dbContext)
+ {
+ }
+}
\ No newline at end of file
diff --git a/Domus.DAL/Interfaces/IProductDetailQuotationRevisionRepository.cs b/Domus.DAL/Interfaces/IProductDetailQuotationRevisionRepository.cs
new file mode 100644
index 0000000..4e855af
--- /dev/null
+++ b/Domus.DAL/Interfaces/IProductDetailQuotationRevisionRepository.cs
@@ -0,0 +1,8 @@
+using Domus.Common.Interfaces;
+using Domus.Domain.Entities;
+
+namespace Domus.DAL.Interfaces;
+
+public interface IProductDetailQuotationRevisionRepository : IGenericRepository, IAutoRegisterable
+{
+}
diff --git a/Domus.DAL/Interfaces/IQuotationRevisionRepository.cs b/Domus.DAL/Interfaces/IQuotationRevisionRepository.cs
new file mode 100644
index 0000000..6955a86
--- /dev/null
+++ b/Domus.DAL/Interfaces/IQuotationRevisionRepository.cs
@@ -0,0 +1,8 @@
+using Domus.Common.Interfaces;
+using Domus.Domain.Entities;
+
+namespace Domus.DAL.Interfaces;
+
+public interface IQuotationRevisionRepository : IGenericRepository, IAutoRegisterable
+{
+}
diff --git a/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs b/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs
new file mode 100644
index 0000000..c2b4cb1
--- /dev/null
+++ b/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs
@@ -0,0 +1,18 @@
+using Domus.Domain.Dtos.Products;
+
+namespace Domus.Domain.Dtos.Quotations;
+
+public class DtoProductDetailQuotationRevision
+{
+ public string ProductName { get; set; } = null!;
+
+ public double Price { get; set; }
+
+ public string MonetaryUnit { get; set; } = null!;
+
+ public double Quantity { get; set; }
+
+ public string QuantityType { get; set; } = null!;
+
+ public DtoProductDetail Detail { get; set; } = null!;
+}
diff --git a/Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs b/Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs
index 24921bf..1eca71a 100644
--- a/Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs
+++ b/Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs
@@ -1,5 +1,4 @@
using System.Text.Json.Serialization;
-using Domus.Domain.Dtos.Products;
namespace Domus.Domain.Dtos.Quotations;
@@ -18,7 +17,8 @@ public class DtoQuotationFullDetails
public DateTime? ExpireAt { get; set; }
[JsonPropertyName("products")]
- public ICollection ProductDetailQuotations { get; set; } = new List();
+ // public ICollection ProductDetailQuotations { get; set; } = new List();
+ public ICollection ProductDetailQuotations { get; set; } = new List();
[JsonPropertyName("services")]
public ICollection ServiceQuotations { get; set; } = new List();
diff --git a/Domus.Service/AutoMappings/AutoMapperConfiguration.cs b/Domus.Service/AutoMappings/AutoMapperConfiguration.cs
index 4420ea3..ddc2107 100644
--- a/Domus.Service/AutoMappings/AutoMapperConfiguration.cs
+++ b/Domus.Service/AutoMappings/AutoMapperConfiguration.cs
@@ -136,7 +136,7 @@ private static void CreateProductMaps(IMapperConfigurationExpression mapper)
mapper.CreateMap()
.ForMember(dest => dest.ProductName, opt => opt.MapFrom(src => src.Product.ProductName));
- mapper.CreateMap()
+ mapper.CreateMap()
.ForMember(dest => dest.ProductName,
opt => opt.MapFrom(src => src.ProductDetail.Product.ProductName));
@@ -196,16 +196,16 @@ private static void CreateQuotationMaps(IMapperConfigurationExpression mapper)
opt => opt.Condition(src => !string.IsNullOrEmpty(src.Status)))
.ForMember(dest => dest.ExpireAt,
opt => opt.Condition(src => src.ExpireAt != default))
- .ForMember(dest => dest.ProductDetailQuotations,
+ .ForMember(dest => dest.QuotationRevisions,
opt => opt.Ignore())
.ForMember(dest => dest.ServiceQuotations,
opt => opt.Ignore());
- mapper.CreateMap()
- .ForMember(dest => dest.MonetaryUnit,
- opt => opt.Condition(src => !string.IsNullOrEmpty(src.MonetaryUnit)))
- .ForMember(dest => dest.QuantityType,
- opt => opt.Condition(src => !string.IsNullOrEmpty(src.QuantityType)));
+ // mapper.CreateMap()
+ // .ForMember(dest => dest.MonetaryUnit,
+ // opt => opt.Condition(src => !string.IsNullOrEmpty(src.MonetaryUnit)))
+ // .ForMember(dest => dest.QuantityType,
+ // opt => opt.Condition(src => !string.IsNullOrEmpty(src.QuantityType)));
mapper.CreateMap();
}
diff --git a/Domus.Service/Implementations/QuotationService.cs b/Domus.Service/Implementations/QuotationService.cs
index 61a6b91..f79c446 100644
--- a/Domus.Service/Implementations/QuotationService.cs
+++ b/Domus.Service/Implementations/QuotationService.cs
@@ -3,7 +3,6 @@
using AutoMapper.QueryableExtensions;
using Domus.Common.Helpers;
using Domus.DAL.Interfaces;
-using Domus.Domain.Dtos.Products;
using Domus.Domain.Dtos.Quotations;
using Domus.Domain.Entities;
using Domus.Service.Constants;
@@ -15,7 +14,6 @@
using Domus.Service.Models.Requests.Quotations;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
-using UnauthorizedAccessException = System.UnauthorizedAccessException;
namespace Domus.Service.Implementations;
@@ -26,7 +24,6 @@ public class QuotationService : IQuotationService
private readonly IMapper _mapper;
private readonly IUserRepository _userRepository;
private readonly IProductDetailRepository _productDetailRepository;
- private readonly IProductDetailQuotationRepository _productDetailQuotationRepository;
private readonly IQuotationNegotiationLogRepository _quotationNegotiationLogRepository;
private readonly INegotiationMessageRepository _negotiationMessageRepository;
private readonly IServiceRepository _serviceRepository;
@@ -34,27 +31,29 @@ public class QuotationService : IQuotationService
private readonly UserManager _userManager;
private readonly IPackageRepository _packageRepository;
private readonly IServiceQuotationRepository _serviceQuotationRepository;
+ private readonly IQuotationRevisionRepository _quotationRevisionRepository;
+ private readonly IProductDetailQuotationRevisionRepository _productDetailQuotationRevisionRepository;
public QuotationService(
- IQuotationRepository quotationRepository,
- IUnitOfWork unitOfWork,
- IMapper mapper,
- IUserRepository userRepository,
- IProductDetailRepository productDetailRepository,
- IProductDetailQuotationRepository productDetailQuotationRepository,
- IServiceRepository serviceRepository,
- INegotiationMessageRepository negotiationMessageRepository,
- IQuotationNegotiationLogRepository quotationNegotiationLogRepository,
- IJwtService jwtService,
- UserManager userManager,
- IPackageRepository packageRepository,
- IServiceQuotationRepository serviceQuotationRepository)
+ IQuotationRepository quotationRepository,
+ IUnitOfWork unitOfWork,
+ IMapper mapper,
+ IUserRepository userRepository,
+ IProductDetailRepository productDetailRepository,
+ IServiceRepository serviceRepository,
+ INegotiationMessageRepository negotiationMessageRepository,
+ IQuotationNegotiationLogRepository quotationNegotiationLogRepository,
+ IJwtService jwtService,
+ UserManager userManager,
+ IPackageRepository packageRepository,
+ IQuotationRevisionRepository quotationRevisionRepository,
+ IProductDetailQuotationRevisionRepository productDetailQuotationRevisionRepository,
+ IServiceQuotationRepository serviceQuotationRepository)
{
_quotationRepository = quotationRepository;
_unitOfWork = unitOfWork;
_userRepository = userRepository;
_productDetailRepository = productDetailRepository;
- _productDetailQuotationRepository = productDetailQuotationRepository;
_quotationNegotiationLogRepository = quotationNegotiationLogRepository;
_jwtService = jwtService;
_userManager = userManager;
@@ -62,6 +61,8 @@ public QuotationService(
_serviceRepository = serviceRepository;
_negotiationMessageRepository = negotiationMessageRepository;
_serviceQuotationRepository = serviceQuotationRepository;
+ _quotationRevisionRepository = quotationRevisionRepository;
+ _productDetailQuotationRevisionRepository = productDetailQuotationRevisionRepository;
_mapper = mapper;
}
@@ -130,26 +131,25 @@ public async Task CreateQuotation(CreateQuotationRequest re
if (productDetailEntity == null)
throw new ProductDetailNotFoundException();
- var productDetailQuotation = new ProductDetailQuotation
+ var quotationRevision = new QuotationRevision
+ {
+ Quotation = quotation,
+ Version = 0,
+ CreatedAt = DateTime.Now
+ };
+
+ var productDetailQuotationRevision = new ProductDetailQuotationRevision
{
ProductDetailId = productDetail.Id,
- QuotationId = quotation.Id,
+ QuotationRevision = quotationRevision,
Quantity = Math.Max(productDetail.Quantity, 1),
Price = productDetailEntity.DisplayPrice,
MonetaryUnit = "USD",
QuantityType = "Unit",
};
- var productDetailQuotationRevision = new ProductDetailQuotationRevision
- {
- ProductDetailQuotation = productDetailQuotation,
- Price = productDetailQuotation.Price,
- Quantity = Math.Max(productDetailQuotation.Quantity, 1),
- Version = 0
- };
-
- productDetailQuotation.ProductDetailQuotationRevisions.Add(productDetailQuotationRevision);
- quotation.ProductDetailQuotations.Add(productDetailQuotation);
+ quotationRevision.ProductDetailQuotationRevisions.Add(productDetailQuotationRevision);
+ quotation.QuotationRevisions.Add(quotationRevision);
}
foreach (var service in request.Services)
@@ -268,23 +268,19 @@ public async Task GetQuotationById(Guid id)
.ProjectTo(_mapper.ConfigurationProvider)
.FirstOrDefault() ?? throw new QuotationNotFoundException();
- var products = (await _productDetailQuotationRepository.GetAllAsync())
- .Include(pdq => pdq.ProductDetailQuotationRevisions)
- .Include(pdq => pdq.ProductDetail)
+ var quotationRevisions =
+ await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == id);
+
+ var latestVersion = quotationRevisions.Any() ? quotationRevisions.Select(r => r.Version).Max() : 0;
+
+ var products = await (await _productDetailQuotationRevisionRepository.FindAsync(r => r.QuotationRevision.QuotationId == id && r.QuotationRevision.Version == latestVersion))
+ .Include(r => r.ProductDetail)
.ThenInclude(pd => pd.Product)
- .Where(pdq => pdq.QuotationId == quotation.Id)
- .ToList()
- .Select(pdq =>
- {
- var latestRevision = pdq.ProductDetailQuotationRevisions.MaxBy(r => r.Version);
- pdq.Price = latestRevision?.Price ?? pdq.Price;
- pdq.Quantity = latestRevision?.Quantity ?? pdq.Quantity;
- return pdq;
- });
- quotation.ProductDetailQuotations = _mapper.Map>(products);
+ .ToListAsync();
+
+ quotation.ProductDetailQuotations = _mapper.Map>(products);
- var totalProductPrice = products.Select(pdq => pdq.ProductDetailQuotationRevisions.MaxBy(r => r.Version))
- .Sum(r => (float)(r?.Price ?? 0) * r?.Quantity ?? 0);
+ var totalProductPrice = products.Sum(r => (float)(r?.Price ?? 0) * r?.Quantity ?? 0);
var totalServicePrice = quotation.ServiceQuotations.Sum(s => s.Price);
quotation.TotalPrice = totalProductPrice + totalServicePrice;
@@ -300,25 +296,7 @@ public async Task SearchQuotations(SearchUsingGetRequest re
foreach (var quotation in quotations)
{
- var products = (await _productDetailQuotationRepository.GetAllAsync())
- .Where(pdq => pdq.QuotationId == quotation.Id)
- .Include(pdq => pdq.ProductDetailQuotationRevisions)
- .Include(pdq => pdq.ProductDetail)
- .ThenInclude(pd => pd.Product)
- .AsSplitQuery()
- .ToList()
- .Select(pdq =>
- {
- var latestRevision = pdq.ProductDetailQuotationRevisions.MaxBy(r => r.Version);
- pdq.Price = latestRevision?.Price ?? pdq.Price;
- pdq.Quantity = latestRevision?.Quantity ?? pdq.Quantity;
- return pdq;
- });
- quotation.ProductDetailQuotations = _mapper.Map>(products);
- var totalProductPrice = products.Select(pdq => pdq.ProductDetailQuotationRevisions.MaxBy(r => r.Version))
- .Sum(r => (float)(r?.Price ?? 0) * r?.Quantity ?? 0);
- var totalServicePrice = quotation.ServiceQuotations.Sum(s => s.Price);
- quotation.TotalPrice = totalProductPrice + totalServicePrice;
+ quotation.TotalPrice = await GetQuotationTotalPrice(quotation.Id);
}
if (!string.IsNullOrEmpty(request.SearchField))
@@ -343,17 +321,25 @@ public async Task SearchQuotations(SearchUsingGetRequest re
public async Task UpdateQuotation(UpdateQuotationRequest request, Guid id)
{
- if (!await _userRepository.ExistsAsync(u => u.Id == request.CustomerId))
+ if (request.CustomerId != default && !await _userRepository.ExistsAsync(u => u.Id == request.CustomerId))
throw new UserNotFoundException("Customer not found");
- if (!await _userRepository.ExistsAsync(u => u.Id == request.StaffId))
+ if (request.StaffId != default && !await _userRepository.ExistsAsync(u => u.Id == request.StaffId))
throw new UserNotFoundException("Staff not found");
var quotation = await (await _quotationRepository.FindAsync(q => !q.IsDeleted && q.Id == id))
.Include(q => q.ServiceQuotations)
- .Include(q => q.ProductDetailQuotations)
+ .Include(q => q.QuotationRevisions)
.FirstOrDefaultAsync() ?? throw new QuotationNotFoundException();
-
+
_mapper.Map(request, quotation);
+
+ var newQuotationRevision = new QuotationRevision
+ {
+ Quotation = quotation,
+ Version = quotation.QuotationRevisions.Count,
+ IsDeleted = false,
+ CreatedAt = DateTime.Now
+ };
foreach (var requestService in request.Services)
{
@@ -368,16 +354,16 @@ public async Task UpdateQuotation(UpdateQuotationRequest re
ServiceId = requestService.ServiceId,
Price = requestService.Price
};
-
+
quotation.ServiceQuotations.Add(newServiceQuotation);
continue;
}
-
+
quotation.ServiceQuotations.Remove(serviceQuotation);
serviceQuotation.Price = requestService.Price;
quotation.ServiceQuotations.Add(serviceQuotation);
}
-
+
var excludedServices = new List(quotation.ServiceQuotations.Where(s => !request.Services.Select(rs => rs.ServiceId).Contains(s.ServiceId)));
foreach (var excludedService in excludedServices)
quotation.ServiceQuotations.Remove(excludedService);
@@ -386,79 +372,46 @@ public async Task UpdateQuotation(UpdateQuotationRequest re
{
if (!await _productDetailRepository.ExistsAsync(x => x.Id == requestProductDetail.ProductDetailId))
throw new ProductDetailNotFoundException();
- var productDetail = await (await _productDetailQuotationRepository.FindAsync(s => s.ProductDetailId == requestProductDetail.ProductDetailId && s.QuotationId == quotation.Id))
- .Include(s => s.ProductDetailQuotationRevisions)
- .FirstOrDefaultAsync();
-
- if (productDetail == null)
+ var productDetailInQuotaionRevision = new ProductDetailQuotationRevision
{
- var newProductDetail = new ProductDetailQuotation
- {
- ProductDetailId = requestProductDetail.ProductDetailId,
- QuotationId = quotation.Id,
- Quantity = requestProductDetail.Quantity,
- Price = requestProductDetail.Price,
- MonetaryUnit = string.IsNullOrEmpty(requestProductDetail.MonetaryUnit) ? "USD" : requestProductDetail.MonetaryUnit,
- QuantityType = string.IsNullOrEmpty(requestProductDetail.QuantityType) ? "Unit" : requestProductDetail.QuantityType
- };
-
- var productDetailQuotationRevision = new ProductDetailQuotationRevision
- {
- ProductDetailQuotation = newProductDetail,
- Price = requestProductDetail.Price,
- Quantity = requestProductDetail.Quantity,
- Version = 0
- };
-
- newProductDetail.ProductDetailQuotationRevisions.Add(productDetailQuotationRevision);
- quotation.ProductDetailQuotations.Add(newProductDetail);
- continue;
- };
-
- var newProductDetailQuotationRevision = new ProductDetailQuotationRevision
- {
- ProductDetailQuotationId = productDetail.Id,
Price = requestProductDetail.Price,
+ MonetaryUnit = string.IsNullOrEmpty(requestProductDetail.MonetaryUnit) ? "USD" : requestProductDetail.MonetaryUnit,
Quantity = requestProductDetail.Quantity,
- Version = productDetail.ProductDetailQuotationRevisions.Count
+ QuantityType = string.IsNullOrEmpty(requestProductDetail.QuantityType) ? "Unit" : requestProductDetail.QuantityType,
+ IsDeleted = false,
+ ProductDetailId = requestProductDetail.ProductDetailId,
+ QuotationRevision = newQuotationRevision
};
-
- productDetail.ProductDetailQuotationRevisions.Add(newProductDetailQuotationRevision);
+
+ newQuotationRevision.ProductDetailQuotationRevisions.Add(productDetailInQuotaionRevision);
}
- var excludedProductDetails = new List(quotation.ProductDetailQuotations.Where(s => !request.ProductDetailQuotations.Select(pdq => pdq.ProductDetailId).Contains(s.ProductDetailId)));
- foreach (var excludedProductDetail in excludedProductDetails)
- quotation.ProductDetailQuotations.Remove(excludedProductDetail);
-
+ quotation.QuotationRevisions.Add(newQuotationRevision);
quotation.LastUpdatedAt = DateTime.Now;
await _quotationRepository.UpdateAsync(quotation);
await _unitOfWork.CommitAsync();
-
+
return new ServiceActionResult(true);
}
private async Task GetQuotationTotalPrice(Guid quotationId)
{
- var products = (await _productDetailQuotationRepository.GetAllAsync())
- .Include(pdq => pdq.ProductDetailQuotationRevisions)
- .Include(pdq => pdq.ProductDetail)
+ var quotationRevisions =
+ await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == quotationId);
+
+ var latestVersion = quotationRevisions.Any() ? quotationRevisions.Select(r => r.Version).Max() : 0;
+
+ var products = await (await _productDetailQuotationRevisionRepository.FindAsync(r => r.QuotationRevision.QuotationId == quotationId && r.QuotationRevision.Version == latestVersion))
+ .Include(r => r.ProductDetail)
.ThenInclude(pd => pd.Product)
- .Where(pdq => pdq.QuotationId == quotationId)
- .ToList()
- .Select(pdq =>
- {
- var latestRevision = pdq.ProductDetailQuotationRevisions.MaxBy(r => r.Version);
- pdq.Price = latestRevision?.Price ?? pdq.Price;
- pdq.Quantity = latestRevision?.Quantity ?? pdq.Quantity;
- return pdq;
- });
- var services = await (await _serviceQuotationRepository.FindAsync(s => s.QuotationId == quotationId))
.ToListAsync();
-
- var totalProductPrice = products.Select(pdq => pdq.ProductDetailQuotationRevisions.MaxBy(r => r.Version))
- .Sum(r => (float)(r?.Price ?? r?.ProductDetailQuotation.Price ?? 0) * r?.Quantity ?? r?.ProductDetailQuotation.Quantity ?? 0);
-
- var totalServicePrice = services.Sum(s => s.Price);
+
+ var totalProductPrice = products.Sum(r => (float)r.Price * r.Quantity);
+ var totalServicePrice = (await _serviceQuotationRepository.FindAsync(sq => sq.QuotationId == quotationId))
+ .ToList()
+ .Select(sq => sq.Price)
+ .Sum();
+
return totalProductPrice + totalServicePrice;
}
}