-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/voucher #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/voucher #200
Changes from 6 commits
36f76fe
3d26f35
1ffec96
ae166d7
9d7f388
f7f19ab
78fe4db
3127840
e047a58
21884cc
732cf47
aee09a3
080532b
6722c6c
8d77607
185012d
46e5265
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| using ErrorOr; | ||
| using Microsoft.AspNetCore.Authorization; | ||
| using Microsoft.AspNetCore.Mvc; | ||
| using ShoeStore.Application.DTOs.VoucherDtos; | ||
| using ShoeStore.Application.Interface.VoucherInterface; | ||
|
|
||
| namespace ShoeStore.Api.Controllers; | ||
|
|
||
| /// <summary> | ||
| /// Controller for managing vouchers in the system. | ||
| /// Provides endpoints for voucher creation and management (Admin only). | ||
| /// </summary> | ||
| /// <param name="voucherService">Service for handling voucher logic operations.</param> | ||
| [ApiController] | ||
| [Route("api/admin/vouchers")] | ||
| // [Authorize(Roles = "Admin")] | ||
| public class VoucherController(IVoucherService voucherService) : ControllerBase | ||
| { | ||
| /// <summary> | ||
| /// Creates a new voucher for the store. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Requires Admin role authorization. | ||
| /// The request body should include: | ||
| /// - VoucherName: Name of the voucher | ||
| /// - Discount: Value of the discount | ||
| /// - DiscountType: Type of discount (Percentage/FixedAmount) | ||
| /// - TotalQuantity: Number of vouchers available | ||
| /// - ValidFrom/ValidTo: Expiration dates | ||
| /// </remarks> | ||
| /// <param name="createVoucherDto">Data transfer object containing voucher creation details.</param> | ||
| /// <param name="token">Cancellation token for the request.</param> | ||
| /// <response code="201">Voucher created successfully.</response> | ||
| /// <response code="400">Bad request; invalid voucher data provided.</response> | ||
| /// <response code="401">Unauthorized; user must be authenticated with Admin role.</response> | ||
| /// <response code="500">Internal server error; an unexpected error occurred.</response> | ||
| /// <returns>An action result with status 201 (Created) on success, or an error response.</returns> | ||
| [ProducesResponseType(StatusCodes.Status201Created)] | ||
| [ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)] | ||
| [ProducesResponseType(typeof(object), StatusCodes.Status401Unauthorized)] | ||
| [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] | ||
|
|
||
| [HttpPost] | ||
| public async Task<IActionResult> CreateVoucher([FromBody] CreateVoucherDto createVoucherDto, CancellationToken token) | ||
| { | ||
| var result = await voucherService.CreateVoucherAsync(createVoucherDto, token); | ||
|
|
||
| return result.Match<IActionResult>( | ||
| _ => Created("", new { message = "Voucher created successfully" }), | ||
| errors => BadRequest(new | ||
| { | ||
| message = "Failed to create voucher", | ||
| details = errors | ||
| })); | ||
| } | ||
|
Comment on lines
+166
to
+181
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Proposed fix [ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(object), StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
[HttpDelete("expire")]
public async Task<IActionResult> DeleteExpiredVouchers(CancellationToken token)
{
var result = await voucherService.DeleteVoucherExpireAsync(token);
return result.Match<IActionResult>(
_ => Ok(new { message = "Expired vouchers deleted successfully" }),
- errors => BadRequest(new
+ errors => errors[0].Code switch
{
- message = "Failed to delete expired vouchers",
- detail = errors[0].Description
- }));
+ "NO_EXPIRED_VOUCHERS" => NotFound(new
+ {
+ message = "No expired vouchers found",
+ detail = errors[0].Description
+ }),
+ _ => BadRequest(new
+ {
+ message = "Failed to delete expired vouchers",
+ detail = errors[0].Description
+ })
+ });
}🤖 Prompt for AI Agents |
||
|
|
||
| [HttpPut("{voucherGuid}")] | ||
| public async Task<IActionResult> UpdateVoucher(Guid voucherGuid, [FromBody] UpdateVoucherDto updateVoucherDto, CancellationToken token) | ||
| { | ||
| var result = await voucherService.UpdateVoucherAsync(voucherGuid, updateVoucherDto, token); | ||
| return result.Match<IActionResult>( | ||
| _ => Ok(new { message = "Voucher updated successfully" }), | ||
| errors => errors.Any(e => e.Type == ErrorType.NotFound) | ||
| ? NotFound(new { message = "Voucher not found" }) | ||
| : BadRequest(new | ||
| { | ||
| message = "Failed to update voucher", | ||
| details = errors | ||
| })); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| | ||
| namespace ShoeStore.Application.DTOs.VoucherDtos | ||
| { | ||
| public class CreateVoucherDto | ||
| { | ||
| public string? VoucherName { get; set; } | ||
| public string? VoucherDescription { get; set; } | ||
| public decimal? Discount { get; set; } | ||
| public int VoucherScope { get; set; } = 1; // Default to AllProducts | ||
| public int DiscountType { get; set; } = 1; // Default to Percentage | ||
| public decimal MaxPriceDiscount { get; set; } | ||
| private DateTime? _validFrom; | ||
| public DateTime? ValidFrom | ||
| { | ||
| get => _validFrom; | ||
| set => _validFrom = value.HasValue ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc) : null; | ||
| } | ||
|
|
||
| private DateTime? _validTo; | ||
| public DateTime? ValidTo | ||
| { | ||
| get => _validTo; | ||
| set => _validTo = value.HasValue ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc) : null; | ||
| } | ||
|
Comment on lines
+15
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: DateTime.SpecifyKind does not convert the DateTime value to UTC. It only sets the Kind property while keeping the same number of ticks (time value) unchanged. The official documentation explicitly states: "The returned DateTime value does not represent the same instant in time as the value parameter, and SpecifyKind is not a time zone conversion method. Instead, it leaves the time specified by the value parameter unchanged, and sets the Kind property to kind." For time zone conversions, use methods like ToUniversalTime or ToLocalTime. Citations:
🏁 Script executed: # First, locate and inspect the CreateVoucherDto.cs file
find . -name "CreateVoucherDto.cs" -type fRepository: tqha1011/ShoeStore Length of output: 134 🏁 Script executed: # Search for DateTime.SpecifyKind usage across the codebase
rg "SpecifyKind" -t cs -C 3Repository: tqha1011/ShoeStore Length of output: 5521 🏁 Script executed: # Search for similar DateTime assignment patterns in DTOs
rg "DateTime\.SpecifyKind|DateTimeKind\.Utc" -t cs --glob "**/*Dto.cs"Repository: tqha1011/ShoeStore Length of output: 419 Use Lines 16 and 23 use 🛠️ Proper UTC normalization+ private static DateTime? NormalizeUtc(DateTime? value)
+ {
+ if (!value.HasValue)
+ {
+ return null;
+ }
+
+ return value.Value.Kind == DateTimeKind.Unspecified
+ ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc)
+ : value.Value.ToUniversalTime();
+ }
+
private DateTime? _validFrom;
public DateTime? ValidFrom
{
get => _validFrom;
- set => _validFrom = value.HasValue ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc) : null;
+ set => _validFrom = NormalizeUtc(value);
}
private DateTime? _validTo;
public DateTime? ValidTo
{
get => _validTo;
- set => _validTo = value.HasValue ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc) : null;
+ set => _validTo = NormalizeUtc(value);
}🤖 Prompt for AI Agents |
||
| public int? MaxUsagePerUser { get; set; } | ||
| public int? TotalQuantity { get; set; } | ||
| public decimal? MinOrderPrice { get; set; } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Text; | ||
|
|
||
| namespace ShoeStore.Application.DTOs.VoucherDtos | ||
| { | ||
| public class ResponseVoucherDto | ||
| { | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| namespace ShoeStore.Application.DTOs.VoucherDtos | ||
| { | ||
| public class UpdateVoucherDto | ||
| { | ||
| public string? VoucherDescription { get; set; } | ||
| public decimal? Discount { get; set; } | ||
| public int VoucherScope { get; set; } = 1; // Default to AllProducts | ||
| public int DiscountType { get; set; } = 1; // Default to Percentage | ||
| public decimal MaxPriceDiscount { get; set; } | ||
| private DateTime? _validFrom; | ||
| public DateTime? ValidFrom | ||
| { | ||
| get => _validFrom; | ||
| set => _validFrom = value.HasValue ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc) : null; | ||
| } | ||
|
|
||
| private DateTime? _validTo; | ||
| public DateTime? ValidTo | ||
| { | ||
| get => _validTo; | ||
| set => _validTo = value.HasValue ? DateTime.SpecifyKind(value.Value, DateTimeKind.Utc) : null; | ||
| } | ||
| public int? MaxUsagePerUser { get; set; } | ||
| public int? TotalQuantity { get; set; } | ||
| public decimal? MinOrderPrice { get; set; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using ShoeStore.Application.Interface.Common; | ||
| using ShoeStore.Domain.Entities; | ||
|
|
||
| namespace ShoeStore.Application.Interface.VoucherInterface | ||
| { | ||
| public interface IVoucherRepository : IGenericRepository<Voucher, int> | ||
| { | ||
| IQueryable<Voucher> GetAllVouchers(); | ||
| IQueryable<Voucher> GetVoucherByGuid(Guid voucherGuid); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using ShoeStore.Application.DTOs.VoucherDtos; | ||
| using ErrorOr; | ||
| namespace ShoeStore.Application.Interface.VoucherInterface | ||
| { | ||
| public interface IVoucherService | ||
| { | ||
| Task<ErrorOr<Created>> CreateVoucherAsync(CreateVoucherDto voucherCreateDto, CancellationToken token); | ||
| //Task<ErrorOr<VoucherDto>> GetVoucherByGuidAsync(Guid voucherGuid, CancellationToken token); | ||
| //Task<ErrorOr<IEnumerable<VoucherDto>>> GetAllVouchersAsync(CancellationToken token); | ||
| Task<ErrorOr<Updated>> UpdateVoucherAsync(Guid voucherGuid, UpdateVoucherDto voucherUpdateDto, CancellationToken token); | ||
| //Task<ErrorOr<Deleted>> DeleteVoucherAsync(Guid voucherGuid, CancellationToken token ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| using ShoeStore.Application.Interface.VoucherInterface; | ||
| using ShoeStore.Application.DTOs.VoucherDtos; | ||
| using ErrorOr; | ||
| using ShoeStore.Application.Interface.Common; | ||
| using ShoeStore.Domain.Entities; | ||
| using ShoeStore.Domain.Enum; | ||
| using Microsoft.EntityFrameworkCore; | ||
|
|
||
| namespace ShoeStore.Application.Services | ||
| { | ||
| public class VoucherService : IVoucherService | ||
| { | ||
| private readonly IVoucherRepository repository; | ||
| private readonly IUnitOfWork uow; | ||
|
|
||
| public VoucherService(IVoucherRepository repository, IUnitOfWork uow) | ||
| { | ||
| this.repository = repository; | ||
| this.uow = uow; | ||
| } | ||
| public async Task<ErrorOr<Created>> CreateVoucherAsync(CreateVoucherDto voucherCreateDto, CancellationToken token) | ||
| { | ||
|
|
||
| var voucher = new Voucher | ||
| { | ||
| VoucherName = voucherCreateDto.VoucherName ?? string.Empty, | ||
| VoucherDescription = voucherCreateDto.VoucherDescription, | ||
| Discount = voucherCreateDto.Discount ?? 0, | ||
| VoucherScope = (VoucherScope)voucherCreateDto.VoucherScope, | ||
| DiscountType = (DiscountType)voucherCreateDto.DiscountType, | ||
|
Comment on lines
+40
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
fd CreateVoucherDtoValidation.cs --exec cat {}
fd CreateVoucherDto.cs --exec cat {}Repository: tqha1011/ShoeStore Length of output: 2141 Unvalidated Verified: Add to RuleFor(x => x.VoucherScope)
.IsInEnum().WithMessage("Invalid VoucherScope value.");
RuleFor(x => x.DiscountType)
.IsInEnum().WithMessage("Invalid DiscountType value.");🤖 Prompt for AI Agents |
||
| MaxPriceDiscount = voucherCreateDto.MaxPriceDiscount, | ||
| ValidFrom = voucherCreateDto.ValidFrom, | ||
| ValidTo = voucherCreateDto.ValidTo, | ||
| MaxUsagePerUser = voucherCreateDto.MaxUsagePerUser, | ||
| TotalQuantity = voucherCreateDto.TotalQuantity ?? 0, | ||
| MinOrderPrice = voucherCreateDto.MinOrderPrice ?? 0, | ||
| IsDeleted = false | ||
| }; | ||
|
|
||
| repository.Add(voucher); | ||
| await uow.SaveChangesAsync(token); | ||
| return Result.Created; | ||
|
|
||
| } | ||
|
|
||
| public async Task<ErrorOr<Updated>> UpdateVoucherAsync( | ||
| Guid voucherGuid, | ||
| UpdateVoucherDto voucherUpdateDto, | ||
| CancellationToken token) | ||
| { | ||
|
|
||
| var voucher = await repository | ||
| .GetVoucherByGuid(voucherGuid) | ||
| .FirstOrDefaultAsync(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify no-token EF async query calls in C# files.
# Expected: voucher query uses FirstOrDefaultAsync(token) or an equivalent CancellationToken overload.
rg -nP --type cs -C2 '\.FirstOrDefaultAsync\s*\(\s*\)'Repository: tqha1011/ShoeStore Length of output: 545 🏁 Script executed: cat -n Backend/src/ShoeStore.Application/Services/VoucherService.cs | head -100Repository: tqha1011/ShoeStore Length of output: 3990 🏁 Script executed: rg -n 'FirstOrDefaultAsync\s*\(\s*\)' Backend/src/ShoeStore.Application/Services/VoucherService.csRepository: tqha1011/ShoeStore Length of output: 104 Pass the cancellation token to the database query. The Proposed fix var voucher = await repository
.GetVoucherByGuid(voucherGuid)
- .FirstOrDefaultAsync();
+ .FirstOrDefaultAsync(token);🤖 Prompt for AI Agents |
||
|
|
||
| if (voucher == null) | ||
| { | ||
| return Error.NotFound( | ||
| "VOUCHER_NOT_FOUND", | ||
| "The voucher with the specified GUID does not exist." | ||
| ); | ||
| } | ||
|
|
||
| // Update logic | ||
| voucher.VoucherDescription = voucherUpdateDto.VoucherDescription ?? voucher.VoucherDescription; | ||
|
|
||
| voucher.VoucherScope = (VoucherScope)voucherUpdateDto.VoucherScope; | ||
| voucher.DiscountType = (DiscountType)voucherUpdateDto.DiscountType; | ||
|
|
||
| voucher.MaxPriceDiscount = voucherUpdateDto.MaxPriceDiscount; | ||
|
|
||
| voucher.ValidFrom = voucherUpdateDto.ValidFrom ?? voucher.ValidFrom; | ||
| voucher.ValidTo = voucherUpdateDto.ValidTo ?? voucher.ValidTo; | ||
|
|
||
| voucher.MaxUsagePerUser = voucherUpdateDto.MaxUsagePerUser ?? voucher.MaxUsagePerUser; | ||
| voucher.TotalQuantity = voucherUpdateDto.TotalQuantity ?? voucher.TotalQuantity; | ||
| voucher.MinOrderPrice = voucherUpdateDto.MinOrderPrice ?? voucher.MinOrderPrice; | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| voucher.UpdatedAt = DateTime.UtcNow; | ||
|
|
||
| repository.Update(voucher); | ||
| await uow.SaveChangesAsync(token); | ||
|
|
||
| return Result.Updated; | ||
|
|
||
| } | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| using FluentValidation; | ||
| using ShoeStore.Application.DTOs.VoucherDtos; | ||
|
|
||
| namespace ShoeStore.Application.Validations.VoucherValidation; | ||
|
|
||
| public class CreateVoucherDtoValidation : AbstractValidator<CreateVoucherDto> | ||
| { | ||
| public CreateVoucherDtoValidation() | ||
| { | ||
| RuleFor(x => x.VoucherName) | ||
| .NotEmpty().WithMessage("Voucher name is required") | ||
| .MaximumLength(100).WithMessage("Voucher name must not exceed 100 characters"); | ||
|
|
||
| RuleFor(x => x.Discount) | ||
| .NotNull().WithMessage("Discount is required") | ||
| .GreaterThanOrEqualTo(0).WithMessage("Discount must be greater than or equal to 0"); | ||
|
|
||
| RuleFor(x => x.TotalQuantity) | ||
| .GreaterThan(0).WithMessage("Total quantity must be greater than 0"); | ||
|
Comment on lines
+10
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Find the domain/entity definitions and usages for voucher scope/type/value fields.
# Expected: Use the discovered allowed values to replace the placeholder Must(...) checks above.
rg -n -C3 --type=cs '\b(VoucherScope|DiscountType|MaxPriceDiscount|MinOrderPrice|MaxUsagePerUser)\b'Repository: tqha1011/ShoeStore Length of output: 50374 🏁 Script executed: fd -n "CreateVoucherDto" --type fRepository: tqha1011/ShoeStore Length of output: 292 🏁 Script executed: fd "CreateVoucherDto" --type fRepository: tqha1011/ShoeStore Length of output: 226 🏁 Script executed: cat -n "Backend/src/ShoeStore.Application/DTOs/VoucherDtos/CreateVoucherDto.cs"Repository: tqha1011/ShoeStore Length of output: 1334 🏁 Script executed: fd "Voucher" --type f | grep -E "(Entity|Domain|Model)" | head -20Repository: tqha1011/ShoeStore Length of output: 268 🏁 Script executed: cat -n "Backend/src/ShoeStore.Domain/Entities/Voucher.cs"Repository: tqha1011/ShoeStore Length of output: 1532 🏁 Script executed: cat -n "Backend/src/ShoeStore.Domain/Enum/VoucherScope.cs"Repository: tqha1011/ShoeStore Length of output: 207 🏁 Script executed: fd "DiscountType" --type fRepository: tqha1011/ShoeStore Length of output: 111 🏁 Script executed: cat -n "Backend/src/ShoeStore.Domain/Enum/DiscountType.cs"Repository: tqha1011/ShoeStore Length of output: 213 🏁 Script executed: cat -n "Backend/src/ShoeStore.Application/Validations/VoucherValidation/CreateVoucherDtoValidation.cs"Repository: tqha1011/ShoeStore Length of output: 1253 🏁 Script executed: cat -n "Backend/src/ShoeStore.Application/Validations/VoucherValidation/CreateVoucherDtoValidation.cs" | head -30Repository: tqha1011/ShoeStore Length of output: 1253 Add missing validation rules for voucher enum fields and economic constraints. The validation is incomplete. 🛡️ Add missing validation rules+ RuleFor(x => x.VoucherScope)
+ .Must(value => value is 1 or 2)
+ .WithMessage("Voucher scope must be 1 (Product) or 2 (Shipping)");
+
+ RuleFor(x => x.DiscountType)
+ .Must(value => value is 1 or 2)
+ .WithMessage("Discount type must be 1 (FixedAmount) or 2 (Percentage)");
+
RuleFor(x => x.Discount)
.NotNull().WithMessage("Discount is required")
.GreaterThanOrEqualTo(0).WithMessage("Discount must be greater than or equal to 0");
+
+ RuleFor(x => x.Discount)
+ .LessThanOrEqualTo(100).WithMessage("Percentage discount must not exceed 100")
+ .When(x => x.DiscountType == 2);
+
+ RuleFor(x => x.MaxPriceDiscount)
+ .GreaterThanOrEqualTo(0).WithMessage("Max price discount must be greater than or equal to 0");
+
+ RuleFor(x => x.MaxUsagePerUser)
+ .GreaterThan(0).WithMessage("Max usage per user must be greater than 0")
+ .When(x => x.MaxUsagePerUser.HasValue);
+
+ RuleFor(x => x.MinOrderPrice)
+ .GreaterThanOrEqualTo(0).WithMessage("Minimum order price must be greater than or equal to 0")
+ .When(x => x.MinOrderPrice.HasValue);🤖 Prompt for AI Agents |
||
|
|
||
| RuleFor(x => x.ValidFrom) | ||
| .LessThanOrEqualTo(x => x.ValidTo) | ||
| .When(x => x.ValidFrom.HasValue && x.ValidTo.HasValue) | ||
| .WithMessage("Valid from date must be less than or equal to valid to date"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using Microsoft.EntityFrameworkCore; | ||
| using ShoeStore.Application.Interface.VoucherInterface; | ||
| using ShoeStore.Domain.Entities; | ||
| using ShoeStore.Infrastructure.Data; | ||
| namespace ShoeStore.Infrastructure.Repositories | ||
| { | ||
| public class VoucherRepository(AppDbContext context) : GenericRepository<Voucher, int>(context), IVoucherRepository | ||
| { | ||
| public IQueryable<Voucher> GetAllVouchers() | ||
| { | ||
| return context.Vouchers.AsNoTracking(); | ||
| } | ||
|
|
||
| public IQueryable<Voucher> GetVoucherByGuid(Guid voucherGuid) | ||
| { | ||
| return context.Vouchers.Where(v => v.PublicId == voucherGuid).AsNoTracking(); | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.