Skip to content

Commit

Permalink
Merge pull request #90 from SWD392-Domus/hotfix/getting-quotations-pe…
Browse files Browse the repository at this point in the history
…rformance-issue

Hotfix/getting quotations performance issue
  • Loading branch information
duykasama authored Mar 4, 2024
2 parents 951225a + ccd7307 commit 0ee37c5
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 34 deletions.
2 changes: 2 additions & 0 deletions Domus.Api/Domus.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,7 @@
<EmbeddedResource Include="Scripts\26.Add_Article_In_Package.sql" />
<None Remove="Scripts\27.Add_Table_Quotation_Revision.sql" />
<EmbeddedResource Include="Scripts\27.Add_Table_Quotation_Revision.sql" />
<None Remove="Scripts\28.Add_Total_Price_To_QuotationRevision.sql" />
<EmbeddedResource Include="Scripts\28.Add_Total_Price_To_QuotationRevision.sql" />
</ItemGroup>
</Project>
2 changes: 2 additions & 0 deletions Domus.Api/Scripts/28.Add_Total_Price_To_QuotationRevision.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE [QuotationRevision]
ADD [TotalPrice] [FLOAT] NOT NULL DEFAULT 0
2 changes: 1 addition & 1 deletion Domus.Common/Helpers/PaginationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public static PaginatedResult BuildPaginatedResult<T, TDto>(IMapper? mapper, ICo
var results = source.Skip((pageIndex - 1) * pageSize)
.Take(pageSize);
paginatedResult.Items = results;
paginatedResult.Items = mapper is null ? results : mapper.Map<IEnumerable<TDto>>(results.AsEnumerable());
paginatedResult.Items = mapper is null ? results.ToList() : mapper.Map<IEnumerable<TDto>>(results.AsEnumerable());
return paginatedResult;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -13,6 +14,4 @@ public class DtoProductDetailQuotationRevision
public double Quantity { get; set; }

public string QuantityType { get; set; } = null!;

public DtoProductDetail Detail { get; set; } = null!;
}
1 change: 0 additions & 1 deletion Domus.Domain/Dtos/Quotations/DtoQuotationFullDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public class DtoQuotationFullDetails
public DateTime? ExpireAt { get; set; }

[JsonPropertyName("products")]
// public ICollection<DtoProductDetailQuotation> ProductDetailQuotations { get; set; } = new List<DtoProductDetailQuotation>();
public ICollection<DtoProductDetailQuotationRevision> ProductDetailQuotations { get; set; } = new List<DtoProductDetailQuotationRevision>();

[JsonPropertyName("services")]
Expand Down
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
@@ -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; }
}
1 change: 1 addition & 0 deletions Domus.Domain/Entities/QuotationRevision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public partial class QuotationRevision : DeletableEntity<Guid>
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<ProductDetailQuotationRevision> ProductDetailQuotationRevisions { get; set; } = new List<ProductDetailQuotationRevision>();
}
12 changes: 5 additions & 7 deletions Domus.Service/AutoMappings/AutoMapperConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ private static void CreateProductMaps(IMapperConfigurationExpression mapper)

mapper.CreateMap<ProductDetailQuotationRevision, DtoProductDetailQuotationRevision>()
.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<ProductDetail, DtoProductDetail>()
.ForMember(dest => dest.DisplayPrice,
Expand Down Expand Up @@ -200,14 +202,10 @@ private static void CreateQuotationMaps(IMapperConfigurationExpression mapper)
opt => opt.Ignore())
.ForMember(dest => dest.ServiceQuotations,
opt => opt.Ignore());

// mapper.CreateMap<ProductDetailInUpdatingQuotationRequest, ProductDetailQuotation>()
// .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<NegotiationMessage, DtoNegotiationMessage>();
mapper.CreateMap<QuotationRevision, DtoQuotationRevisionWithPriceAndVersion>();
mapper.CreateMap<Quotation, DtoQuotationWithoutProductsAndServices>();
}

private static void CreatePackageMaps(IMapperConfigurationExpression mapper)
Expand Down
66 changes: 43 additions & 23 deletions Domus.Service/Implementations/QuotationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,31 +125,32 @@ public async Task<ServiceActionResult> 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)
Expand All @@ -166,8 +167,10 @@ public async Task<ServiceActionResult> CreateQuotation(CreateQuotationRequest re
};

quotation.ServiceQuotations.Add(serviceQuotation);
quotationRevision.TotalPrice += service.Price;
}

quotation.QuotationRevisions.Add(quotationRevision);
await _quotationRepository.AddAsync(quotation);
await _unitOfWork.CommitAsync();

Expand Down Expand Up @@ -228,7 +231,10 @@ public async Task<ServiceActionResult> 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<DtoQuotationRevisionWithPriceAndVersion>(_mapper.ConfigurationProvider);

quotation.TotalPrice = (float)(quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefault()?.TotalPrice ?? 0);
}

return new ServiceActionResult(true) { Data = quotations };
Expand All @@ -253,7 +259,10 @@ public async Task<ServiceActionResult> GetPaginatedQuotations(BasePaginatedReque

foreach (var quotation in await ((IQueryable<DtoQuotation>)paginatedResult.Items!).ToListAsync())
{
quotation.TotalPrice = (float)await GetQuotationTotalPrice(quotation.Id);
var quotationRevisions = (await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == quotation.Id))
.ProjectTo<DtoQuotationRevisionWithPriceAndVersion>(_mapper.ConfigurationProvider);

quotation.TotalPrice = (float)(quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefault()?.TotalPrice ?? 0);
quotationList.Add(quotation);
}

Expand All @@ -280,9 +289,8 @@ public async Task<ServiceActionResult> GetQuotationById(Guid id)

quotation.ProductDetailQuotations = _mapper.Map<ICollection<DtoProductDetailQuotationRevision>>(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 };
}
Expand All @@ -291,31 +299,40 @@ public async Task<ServiceActionResult> SearchQuotations(SearchUsingGetRequest re
{
var quotations = await (await _quotationRepository.FindAsync(p => !p.IsDeleted))
.OrderByDescending(p => p.CreatedAt)
.ProjectTo<DtoQuotationFullDetails>(_mapper.ConfigurationProvider)
.ProjectTo<DtoQuotationWithoutProductsAndServices>(_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<Func<DtoQuotationFullDetails, object>> orderExpr = p => ReflectionHelper.GetValueByName(typeof(DtoQuotationFullDetails), request.SortField, p);
Expression<Func<DtoQuotationWithoutProductsAndServices, object>> 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<DtoQuotationWithoutProductsAndServices>)paginatedResult.Items!;
var quotationList = new List<DtoQuotationWithoutProductsAndServices>();

foreach (var quotation in paginatedQuotations)
{
var quotationRevisions = (await _quotationRevisionRepository.FindAsync(r => !r.IsDeleted && r.QuotationId == quotation.Id))
.ProjectTo<DtoQuotationRevisionWithPriceAndVersion>(_mapper.ConfigurationProvider);

quotation.TotalPrice = quotationRevisions.OrderByDescending(qr => qr.Version).FirstOrDefault()?.TotalPrice ?? 0;
quotationList.Add(quotation);
}

paginatedResult.Items = quotationList;

return new ServiceActionResult(true) { Data = paginatedResult };
}

Expand Down Expand Up @@ -356,12 +373,14 @@ public async Task<ServiceActionResult> 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<ServiceQuotation>(quotation.ServiceQuotations.Where(s => !request.Services.Select(rs => rs.ServiceId).Contains(s.ServiceId)));
Expand All @@ -376,14 +395,15 @@ public async Task<ServiceActionResult> 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,
QuotationRevision = newQuotationRevision
};

newQuotationRevision.ProductDetailQuotationRevisions.Add(productDetailInQuotaionRevision);
newQuotationRevision.TotalPrice += productDetailInQuotaionRevision.Price * productDetailInQuotaionRevision.Quantity;
}

quotation.QuotationRevisions.Add(newQuotationRevision);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}

0 comments on commit 0ee37c5

Please sign in to comment.