diff --git a/Domus.Api/Domus.Api.csproj b/Domus.Api/Domus.Api.csproj
index 7206238..a9793dc 100644
--- a/Domus.Api/Domus.Api.csproj
+++ b/Domus.Api/Domus.Api.csproj
@@ -75,5 +75,7 @@
+
+
diff --git a/Domus.Api/Scripts/28.Add_Total_Price_To_QuotationRevision.sql b/Domus.Api/Scripts/28.Add_Total_Price_To_QuotationRevision.sql
new file mode 100644
index 0000000..819c3e2
--- /dev/null
+++ b/Domus.Api/Scripts/28.Add_Total_Price_To_QuotationRevision.sql
@@ -0,0 +1,2 @@
+ALTER TABLE [QuotationRevision]
+ADD [TotalPrice] [FLOAT] NOT NULL DEFAULT 0
diff --git a/Domus.Common/Helpers/PaginationHelper.cs b/Domus.Common/Helpers/PaginationHelper.cs
index 99df54e..ab5af5e 100644
--- a/Domus.Common/Helpers/PaginationHelper.cs
+++ b/Domus.Common/Helpers/PaginationHelper.cs
@@ -90,7 +90,7 @@ public static PaginatedResult BuildPaginatedResult(IMapper? mapper, ICo
var results = source.Skip((pageIndex - 1) * pageSize)
.Take(pageSize);
paginatedResult.Items = results;
- paginatedResult.Items = mapper is null ? results : mapper.Map>(results.AsEnumerable());
+ paginatedResult.Items = mapper is null ? results.ToList() : mapper.Map>(results.AsEnumerable());
return paginatedResult;
}
diff --git a/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs b/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs
index c2b4cb1..0ea4a04 100644
--- a/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs
+++ b/Domus.Domain/Dtos/Quotations/DtoProductDetailQuotationRevision.cs
@@ -4,6 +4,7 @@ namespace Domus.Domain.Dtos.Quotations;
public class DtoProductDetailQuotationRevision
{
+ public Guid Id { get; set; }
public string ProductName { get; set; } = null!;
public double Price { get; set; }
@@ -13,6 +14,4 @@ public class DtoProductDetailQuotationRevision
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 1eca71a..20bc152 100644
--- a/Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs
+++ b/Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs
@@ -17,7 +17,6 @@ public class DtoQuotationFullDetails
public DateTime? ExpireAt { get; set; }
[JsonPropertyName("products")]
- // public ICollection ProductDetailQuotations { get; set; } = new List();
public ICollection ProductDetailQuotations { get; set; } = new List();
[JsonPropertyName("services")]
diff --git a/Domus.Domain/Dtos/Quotations/DtoQuotationRevisionWithPriceAndVersion.cs b/Domus.Domain/Dtos/Quotations/DtoQuotationRevisionWithPriceAndVersion.cs
new file mode 100644
index 0000000..472a231
--- /dev/null
+++ b/Domus.Domain/Dtos/Quotations/DtoQuotationRevisionWithPriceAndVersion.cs
@@ -0,0 +1,8 @@
+namespace Domus.Domain.Dtos.Quotations;
+
+public class DtoQuotationRevisionWithPriceAndVersion
+{
+ public Guid Id { get; set; }
+ public int Version { get; set; }
+ public double TotalPrice { get; set; }
+}
diff --git a/Domus.Domain/Dtos/Quotations/DtoQuotationWithoutProductsAndServices.cs b/Domus.Domain/Dtos/Quotations/DtoQuotationWithoutProductsAndServices.cs
new file mode 100644
index 0000000..afaac67
--- /dev/null
+++ b/Domus.Domain/Dtos/Quotations/DtoQuotationWithoutProductsAndServices.cs
@@ -0,0 +1,16 @@
+namespace Domus.Domain.Dtos.Quotations;
+
+public class DtoQuotationWithoutProductsAndServices
+{
+ public Guid Id { get; set; }
+
+ public DtoDomusUser Customer { get; set; } = null!;
+
+ public DtoDomusUser Staff { get; set; } = null!;
+
+ public string Status { get; set; } = null!;
+
+ public double TotalPrice { get; set; }
+
+ public DateTime? ExpireAt { get; set; }
+}
\ No newline at end of file
diff --git a/Domus.Domain/Entities/QuotationRevision.cs b/Domus.Domain/Entities/QuotationRevision.cs
index f496000..d173b28 100644
--- a/Domus.Domain/Entities/QuotationRevision.cs
+++ b/Domus.Domain/Entities/QuotationRevision.cs
@@ -7,6 +7,7 @@ public partial class QuotationRevision : DeletableEntity
public Guid QuotationId { get; set; }
public int Version { get; set; }
public DateTime CreatedAt { get; set; }
+ public double TotalPrice { get; set; }
public virtual Quotation Quotation { get; set; } = null!;
public virtual ICollection ProductDetailQuotationRevisions { get; set; } = new List();
}
diff --git a/Domus.Service/AutoMappings/AutoMapperConfiguration.cs b/Domus.Service/AutoMappings/AutoMapperConfiguration.cs
index ddc2107..3efe206 100644
--- a/Domus.Service/AutoMappings/AutoMapperConfiguration.cs
+++ b/Domus.Service/AutoMappings/AutoMapperConfiguration.cs
@@ -138,7 +138,9 @@ private static void CreateProductMaps(IMapperConfigurationExpression mapper)
mapper.CreateMap()
.ForMember(dest => dest.ProductName,
- opt => opt.MapFrom(src => src.ProductDetail.Product.ProductName));
+ opt => opt.MapFrom(src => src.ProductDetail.Product.ProductName))
+ .ForMember(dest => dest.Id,
+ opt => opt.MapFrom(src => src.ProductDetailId));
mapper.CreateMap()
.ForMember(dest => dest.DisplayPrice,
@@ -200,14 +202,10 @@ private static void CreateQuotationMaps(IMapperConfigurationExpression mapper)
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();
+ mapper.CreateMap();
+ mapper.CreateMap();
}
private static void CreatePackageMaps(IMapperConfigurationExpression mapper)
diff --git a/Domus.Service/Implementations/QuotationService.cs b/Domus.Service/Implementations/QuotationService.cs
index f79c446..46b59ed 100644
--- a/Domus.Service/Implementations/QuotationService.cs
+++ b/Domus.Service/Implementations/QuotationService.cs
@@ -125,31 +125,32 @@ public async Task CreateQuotation(CreateQuotationRequest re
PackageId = request.PackageId
};
+ var quotationRevision = new QuotationRevision
+ {
+ Quotation = quotation,
+ Version = 0,
+ CreatedAt = DateTime.Now
+ };
+
foreach (var productDetail in request.ProductDetails)
{
var productDetailEntity = await _productDetailRepository.GetAsync(pd => pd.Id == productDetail.Id);
if (productDetailEntity == null)
throw new ProductDetailNotFoundException();
- var quotationRevision = new QuotationRevision
- {
- Quotation = quotation,
- Version = 0,
- CreatedAt = DateTime.Now
- };
var productDetailQuotationRevision = new ProductDetailQuotationRevision
{
ProductDetailId = productDetail.Id,
QuotationRevision = quotationRevision,
Quantity = Math.Max(productDetail.Quantity, 1),
- Price = productDetailEntity.DisplayPrice,
+ Price = productDetail.Price,
MonetaryUnit = "USD",
QuantityType = "Unit",
};
quotationRevision.ProductDetailQuotationRevisions.Add(productDetailQuotationRevision);
- quotation.QuotationRevisions.Add(quotationRevision);
+ quotationRevision.TotalPrice += productDetailQuotationRevision.Price * productDetailQuotationRevision.Quantity;
}
foreach (var service in request.Services)
@@ -166,8 +167,10 @@ public async Task CreateQuotation(CreateQuotationRequest re
};
quotation.ServiceQuotations.Add(serviceQuotation);
+ quotationRevision.TotalPrice += service.Price;
}
+ quotation.QuotationRevisions.Add(quotationRevision);
await _quotationRepository.AddAsync(quotation);
await _unitOfWork.CommitAsync();
@@ -228,7 +231,10 @@ public async Task GetAllQuotations()
foreach (var quotation in quotations)
{
- quotation.TotalPrice = (float)await GetQuotationTotalPrice(quotation.Id);
+ var quotationRevisions = (await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == quotation.Id))
+ .ProjectTo(_mapper.ConfigurationProvider);
+
+ quotation.TotalPrice = (float)(quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefault()?.TotalPrice ?? 0);
}
return new ServiceActionResult(true) { Data = quotations };
@@ -253,7 +259,10 @@ public async Task GetPaginatedQuotations(BasePaginatedReque
foreach (var quotation in await ((IQueryable)paginatedResult.Items!).ToListAsync())
{
- quotation.TotalPrice = (float)await GetQuotationTotalPrice(quotation.Id);
+ var quotationRevisions = (await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == quotation.Id))
+ .ProjectTo(_mapper.ConfigurationProvider);
+
+ quotation.TotalPrice = (float)(quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefault()?.TotalPrice ?? 0);
quotationList.Add(quotation);
}
@@ -280,9 +289,8 @@ public async Task GetQuotationById(Guid id)
quotation.ProductDetailQuotations = _mapper.Map>(products);
- var totalProductPrice = products.Sum(r => (float)(r?.Price ?? 0) * r?.Quantity ?? 0);
- var totalServicePrice = quotation.ServiceQuotations.Sum(s => s.Price);
- quotation.TotalPrice = totalProductPrice + totalServicePrice;
+ quotation.TotalPrice =
+ (await quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefaultAsync())?.TotalPrice ?? 0;
return new ServiceActionResult(true) { Data = quotation };
}
@@ -291,31 +299,40 @@ public async Task SearchQuotations(SearchUsingGetRequest re
{
var quotations = await (await _quotationRepository.FindAsync(p => !p.IsDeleted))
.OrderByDescending(p => p.CreatedAt)
- .ProjectTo(_mapper.ConfigurationProvider)
+ .ProjectTo(_mapper.ConfigurationProvider)
.ToListAsync();
-
- foreach (var quotation in quotations)
- {
- quotation.TotalPrice = await GetQuotationTotalPrice(quotation.Id);
- }
if (!string.IsNullOrEmpty(request.SearchField))
{
quotations = quotations
- .Where(p => ReflectionHelper.GetStringValueByName(typeof(DtoQuotationFullDetails), request.SearchField, p).Contains(request.SearchValue ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+ .Where(p => ReflectionHelper.GetStringValueByName(typeof(DtoQuotationWithoutProductsAndServices), request.SearchField, p).Contains(request.SearchValue ?? string.Empty, StringComparison.OrdinalIgnoreCase))
.ToList();
}
if (!string.IsNullOrEmpty(request.SortField))
{
- Expression> orderExpr = p => ReflectionHelper.GetValueByName(typeof(DtoQuotationFullDetails), request.SortField, p);
+ Expression> orderExpr = p => ReflectionHelper.GetValueByName(typeof(DtoQuotationWithoutProductsAndServices), request.SortField, p);
quotations = request.Descending
? quotations.OrderByDescending(orderExpr.Compile()).ToList()
: quotations.OrderBy(orderExpr.Compile()).ToList();
}
-
+
var paginatedResult = PaginationHelper.BuildPaginatedResult(quotations, request.PageSize, request.PageIndex);
+ var paginatedQuotations = (ICollection)paginatedResult.Items!;
+ var quotationList = new List();
+
+ foreach (var quotation in paginatedQuotations)
+ {
+ var quotationRevisions = (await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == quotation.Id))
+ .ProjectTo(_mapper.ConfigurationProvider);
+
+ quotation.TotalPrice = quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefault()?.TotalPrice ?? 0;
+ quotationList.Add(quotation);
+ }
+
+ paginatedResult.Items = quotationList;
+
return new ServiceActionResult(true) { Data = paginatedResult };
}
@@ -356,12 +373,14 @@ public async Task UpdateQuotation(UpdateQuotationRequest re
};
quotation.ServiceQuotations.Add(newServiceQuotation);
+ newQuotationRevision.TotalPrice += requestService.Price;
continue;
}
quotation.ServiceQuotations.Remove(serviceQuotation);
serviceQuotation.Price = requestService.Price;
quotation.ServiceQuotations.Add(serviceQuotation);
+ newQuotationRevision.TotalPrice += requestService.Price;
}
var excludedServices = new List(quotation.ServiceQuotations.Where(s => !request.Services.Select(rs => rs.ServiceId).Contains(s.ServiceId)));
@@ -376,7 +395,7 @@ public async Task UpdateQuotation(UpdateQuotationRequest re
{
Price = requestProductDetail.Price,
MonetaryUnit = string.IsNullOrEmpty(requestProductDetail.MonetaryUnit) ? "USD" : requestProductDetail.MonetaryUnit,
- Quantity = requestProductDetail.Quantity,
+ Quantity = Math.Max(requestProductDetail.Quantity, 1),
QuantityType = string.IsNullOrEmpty(requestProductDetail.QuantityType) ? "Unit" : requestProductDetail.QuantityType,
IsDeleted = false,
ProductDetailId = requestProductDetail.ProductDetailId,
@@ -384,6 +403,7 @@ public async Task UpdateQuotation(UpdateQuotationRequest re
};
newQuotationRevision.ProductDetailQuotationRevisions.Add(productDetailInQuotaionRevision);
+ newQuotationRevision.TotalPrice += productDetailInQuotaionRevision.Price * productDetailInQuotaionRevision.Quantity;
}
quotation.QuotationRevisions.Add(newQuotationRevision);
diff --git a/Domus.Service/Models/Requests/Quotations/ProductDetailInCreatingQuotationRequest.cs b/Domus.Service/Models/Requests/Quotations/ProductDetailInCreatingQuotationRequest.cs
index 58afbc8..aee8060 100644
--- a/Domus.Service/Models/Requests/Quotations/ProductDetailInCreatingQuotationRequest.cs
+++ b/Domus.Service/Models/Requests/Quotations/ProductDetailInCreatingQuotationRequest.cs
@@ -10,4 +10,7 @@ public class ProductDetailInCreatingQuotationRequest
[Range(0, int.MaxValue)]
public int Quantity { get; set; }
+
+ [Range(0, double.MaxValue)]
+ public double Price { get; set; }
}