refinement phase 3
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.PartnerCenters.Commands.CreatePartnerCenter;
|
||||
using Baya.Application.Features.PartnerCenters.Commands.SetPartnerCenterActive;
|
||||
using Baya.Application.Features.PartnerCenters.Commands.SponsorNurse;
|
||||
using Baya.Application.Features.PartnerCenters.Commands.UpdatePartnerCenter;
|
||||
using Baya.Application.Features.PartnerCenters.Commands.VerifyPartnerCenter;
|
||||
@@ -48,6 +49,12 @@ public sealed class AdminPartnerCentersController(ISender sender) : BaseControll
|
||||
public async Task<IActionResult> SponsorNurse(long id, SponsorNurseCommand command, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(command with { CenterId = id }, cancellationToken));
|
||||
|
||||
// Activate/suspend toggle (distinct from verify, which records licensing approval) — REQ-032.
|
||||
[HttpPost("{id}/set-active")]
|
||||
[ProducesOkApiResponseType<PartnerCenterDetailDto>]
|
||||
public async Task<IActionResult> SetActive(long id, SetPartnerCenterActiveCommand command, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(command with { Id = id }, cancellationToken));
|
||||
|
||||
[HttpGet]
|
||||
[ProducesOkApiResponseType<PagedResult<PartnerCenterListItemDto>>]
|
||||
public async Task<IActionResult> List([FromQuery] ListPartnerCentersQuery query, CancellationToken cancellationToken)
|
||||
|
||||
@@ -5,6 +5,7 @@ using Baya.Application.Features.Booking.Commands.CancelBookingRequest;
|
||||
using Baya.Application.Features.Booking.Commands.CreateBookingRequest;
|
||||
using Baya.Application.Features.Booking.Commands.RejectBookingRequest;
|
||||
using Baya.Application.Features.Booking.Queries.GetBookingRequest;
|
||||
using Baya.Application.Features.Booking.Queries.GetCheckoutSummary;
|
||||
using Baya.Application.Features.Booking.Queries.ListBookingRequests;
|
||||
using Baya.Application.Models.Booking;
|
||||
using Baya.Application.Models.Common;
|
||||
@@ -57,4 +58,10 @@ public sealed class BookingRequestsController(ISender sender) : BaseController
|
||||
[ProducesOkApiResponseType<BookingRequestDto>]
|
||||
public async Task<IActionResult> Get(long id, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetBookingRequestQuery(id), cancellationToken));
|
||||
|
||||
// The C6 money breakdown for an accepted-awaiting-payment request (owner-scoped, server-computed).
|
||||
[HttpGet("[action]/{id}")]
|
||||
[ProducesOkApiResponseType<CheckoutSummaryDto>]
|
||||
public async Task<IActionResult> CheckoutSummary(long id, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetCheckoutSummaryQuery(id), cancellationToken));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Reviews.Commands.SubmitReview;
|
||||
using Baya.Application.Features.Reviews.Queries.GetMyReview;
|
||||
using Baya.Application.Features.Reviews.Queries.GetReviewEligibility;
|
||||
using Baya.Application.Models.Reviews;
|
||||
using Baya.WebFramework.Attributes;
|
||||
using Baya.WebFramework.BaseController;
|
||||
@@ -27,6 +29,18 @@ public sealed class BookingReviewsController(ISender sender) : BaseController
|
||||
=> OperationResult(await sender.Send(
|
||||
new SubmitReviewCommand(bookingId, body.Rating, body.Body, body.TagCodes), cancellationToken));
|
||||
|
||||
// Can the caller review this booking? (completed/closed AND not already reviewed) — REQ-026.
|
||||
[HttpGet("{bookingId}/review_eligibility")]
|
||||
[ProducesOkApiResponseType<ReviewEligibilityDto>]
|
||||
public async Task<IActionResult> ReviewEligibility(long bookingId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetReviewEligibilityQuery(bookingId), cancellationToken));
|
||||
|
||||
// The caller's own review for the booking (persistent "under review" state across sessions) — REQ-026.
|
||||
[HttpGet("{bookingId}/my_review")]
|
||||
[ProducesOkApiResponseType<MyReviewDto>]
|
||||
public async Task<IActionResult> MyReview(long bookingId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetMyReviewQuery(bookingId), cancellationToken));
|
||||
|
||||
/// <summary>The review body (the booking id comes from the route).</summary>
|
||||
public record SubmitReviewBody(int Rating, string? Body, IReadOnlyList<string>? TagCodes);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,11 @@ using Baya.Application.Features.Bookings.Commands.TransitionBookingStatus;
|
||||
using Baya.Application.Features.Bookings.Queries.GetBookingDetail;
|
||||
using Baya.Application.Features.Bookings.Queries.GetCareInstructions;
|
||||
using Baya.Application.Features.Bookings.Queries.ListBookings;
|
||||
using Baya.Application.Features.Refunds.Commands.CancelBookingAndRefund;
|
||||
using Baya.Application.Features.Refunds.Queries.GetCancellationPolicyPreview;
|
||||
using Baya.Application.Models.Booking;
|
||||
using Baya.Application.Models.Common;
|
||||
using Baya.Application.Models.Refunds;
|
||||
using Baya.WebFramework.Attributes;
|
||||
using Baya.WebFramework.BaseController;
|
||||
using Baya.WebFramework.ServiceConfiguration;
|
||||
@@ -60,6 +63,19 @@ public sealed class BookingsController(ISender sender) : BaseController
|
||||
public async Task<IActionResult> Cancel(long id, CancelBookingCommand command, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(command with { BookingId = id }, cancellationToken));
|
||||
|
||||
// Customer-initiated cancel: cancels the booking AND opens its refund in one call (REQ-019).
|
||||
[HttpPost("{id}/cancel")]
|
||||
[EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
|
||||
[ProducesOkApiResponseType<RefundStatusDto>]
|
||||
public async Task<IActionResult> CancelAndRefund(long id, CancelBookingAndRefundCommand command, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(command with { BookingId = id }, cancellationToken));
|
||||
|
||||
// Pre-cancel disclosure: the applicable policy + per-session refundability, resolved by current lead time (REQ-020).
|
||||
[HttpGet("{id}/cancellation_policy")]
|
||||
[ProducesOkApiResponseType<CancellationPolicyPreviewDto>]
|
||||
public async Task<IActionResult> CancellationPolicy(long id, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetCancellationPolicyPreviewQuery(id), cancellationToken));
|
||||
|
||||
[HttpPost("[action]/{id}")]
|
||||
[ProducesOkApiResponseType<CareInstructionsDto>]
|
||||
public async Task<IActionResult> SubmitCareInstructions(long id, SubmitCareInstructionsCommand command, CancellationToken cancellationToken)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Bnpl.Commands.InitiateBnplOrder;
|
||||
using Baya.Application.Features.Bnpl.Queries.CheckBnplEligibility;
|
||||
using Baya.Application.Features.Bnpl.Queries.GetBnplOrderByRequest;
|
||||
using Baya.Application.Features.Bnpl.Queries.GetBnplOrderStatus;
|
||||
using Baya.Application.Models.Bnpl;
|
||||
using Baya.WebFramework.Attributes;
|
||||
@@ -42,11 +43,17 @@ public sealed class CheckoutBnplController(ISender sender) : BaseController
|
||||
new InitiateBnplOrderCommand(body.BookingRequestId, body.ProviderCode, idempotencyKey), cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[HttpGet("{id:long}")]
|
||||
[ProducesOkApiResponseType<BnplOrderStatusDto>]
|
||||
public async Task<IActionResult> Get(long id, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetBnplOrderStatusQuery(id, AdminView: false), cancellationToken));
|
||||
|
||||
// Reach the BNPL order from the booking request id (the return-poll holds the request id, not the order id).
|
||||
[HttpGet("by_request/{bookingRequestId}")]
|
||||
[ProducesOkApiResponseType<BnplOrderStatusDto>]
|
||||
public async Task<IActionResult> ByRequest(long bookingRequestId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetBnplOrderByRequestQuery(bookingRequestId), cancellationToken));
|
||||
|
||||
/// <summary>The initiate body (the idempotency key comes from the <c>Idempotency-Key</c> header).</summary>
|
||||
public record InitiateBnplBody(long BookingRequestId, string ProviderCode);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Identity.Commands.UploadCustomerAvatar;
|
||||
using Baya.Application.Features.Identity.Commands.UpsertCustomerProfile;
|
||||
using Baya.Application.Features.Identity.Queries.GetMyCustomerProfile;
|
||||
using Baya.Application.Models.Common;
|
||||
using Baya.Application.Models.Identity;
|
||||
using Baya.WebFramework.Attributes;
|
||||
using Baya.WebFramework.BaseController;
|
||||
using Mediator;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Baya.Web.Api.Controllers.V1;
|
||||
@@ -27,4 +30,18 @@ public sealed class CustomerProfilesController(ISender sender) : BaseController
|
||||
[ProducesOkApiResponseType<CustomerProfileDto>]
|
||||
public async Task<IActionResult> Me(CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetMyCustomerProfileQuery(), cancellationToken));
|
||||
|
||||
// Multipart image upload → stored via IObjectStorage; the returned URL is persisted on the profile.
|
||||
[HttpPost("[action]")]
|
||||
[ProducesOkApiResponseType<AvatarUploadResult>]
|
||||
public async Task<IActionResult> Avatar(IFormFile file, CancellationToken cancellationToken)
|
||||
{
|
||||
if (file is null || file.Length == 0)
|
||||
return OperationResult(Baya.Application.Models.Common.OperationResult<AvatarUploadResult>.FailureResult("No file uploaded."));
|
||||
|
||||
using var buffer = new MemoryStream();
|
||||
await file.CopyToAsync(buffer, cancellationToken);
|
||||
var command = new UploadCustomerAvatarCommand(buffer.ToArray(), file.ContentType, file.Length);
|
||||
return OperationResult(await sender.Send(command, cancellationToken));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Payouts.Queries.GetNurseEarnings;
|
||||
using Baya.Application.Features.Payouts.Queries.GetNurseEarningsBalance;
|
||||
using Baya.Application.Features.Payouts.Queries.GetNursePayoutDetail;
|
||||
using Baya.Application.Features.Payouts.Queries.GetNursePayoutHistory;
|
||||
using Baya.Application.Models.Common;
|
||||
using Baya.Application.Models.Payouts;
|
||||
@@ -25,4 +28,22 @@ public sealed class NursePayoutsController(ISender sender) : BaseController
|
||||
[ProducesOkApiResponseType<PagedResult<NursePayoutHistoryDto>>]
|
||||
public async Task<IActionResult> History([FromQuery] GetNursePayoutHistoryQuery query, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(query, cancellationToken));
|
||||
|
||||
// The four-bucket balance + ledger-derived signed net payable (REQ-025).
|
||||
[HttpGet("earnings_balance")]
|
||||
[ProducesOkApiResponseType<NurseEarningsBalanceDto>]
|
||||
public async Task<IActionResult> EarningsBalance(CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetNurseEarningsBalanceQuery(), cancellationToken));
|
||||
|
||||
// The per-booking earnings list with server-derived money-state, filterable by state (REQ-025).
|
||||
[HttpGet("earnings")]
|
||||
[ProducesOkApiResponseType<PagedResult<NurseEarningsItemDto>>]
|
||||
public async Task<IActionResult> Earnings([FromQuery] GetNurseEarningsQuery query, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(query, cancellationToken));
|
||||
|
||||
// The nurse's own payout detail — batch window + covered bookings for reconciliation (REQ-025).
|
||||
[HttpGet("{id:long}")]
|
||||
[ProducesOkApiResponseType<NursePayoutDetailDto>]
|
||||
public async Task<IActionResult> Detail(long id, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetNursePayoutDetailQuery(id), cancellationToken));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Identity.Commands.SetNurseAcceptingBookings;
|
||||
using Baya.Application.Features.Identity.Commands.UploadNurseAvatar;
|
||||
using Baya.Application.Features.Identity.Commands.UpsertNurseProfile;
|
||||
using Baya.Application.Features.Identity.Queries.GetMyNurseProfile;
|
||||
using Baya.Application.Models.Common;
|
||||
using Baya.Application.Models.Identity;
|
||||
using Baya.WebFramework.Attributes;
|
||||
using Baya.WebFramework.BaseController;
|
||||
using Mediator;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Baya.Web.Api.Controllers.V1;
|
||||
@@ -33,4 +36,18 @@ public sealed class NurseProfilesController(ISender sender) : BaseController
|
||||
[ProducesOkApiResponseType<NurseProfileDto>]
|
||||
public async Task<IActionResult> Me(CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetMyNurseProfileQuery(), cancellationToken));
|
||||
|
||||
// Multipart image upload → stored via IObjectStorage; the returned URL is persisted on the profile.
|
||||
[HttpPost("[action]")]
|
||||
[ProducesOkApiResponseType<AvatarUploadResult>]
|
||||
public async Task<IActionResult> Avatar(IFormFile file, CancellationToken cancellationToken)
|
||||
{
|
||||
if (file is null || file.Length == 0)
|
||||
return OperationResult(Baya.Application.Models.Common.OperationResult<AvatarUploadResult>.FailureResult("No file uploaded."));
|
||||
|
||||
using var buffer = new MemoryStream();
|
||||
await file.CopyToAsync(buffer, cancellationToken);
|
||||
var command = new UploadNurseAvatarCommand(buffer.ToArray(), file.ContentType, file.Length);
|
||||
return OperationResult(await sender.Send(command, cancellationToken));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Baya.Application.Features.Verification.Commands.RequestDocumentUploadUrl;
|
||||
using Baya.Application.Features.Verification.Commands.RunBankAccountVerification;
|
||||
using Baya.Application.Features.Verification.Commands.RunIdentityKyc;
|
||||
using Baya.Application.Features.Verification.Commands.RunShahkarMatch;
|
||||
using Baya.Application.Features.Verification.Commands.SubmitCredentialDetails;
|
||||
using Baya.Application.Features.Verification.Commands.SubmitVerification;
|
||||
using Baya.Application.Features.Verification.Queries.GetStatus;
|
||||
using Baya.Application.Models.Verification;
|
||||
@@ -43,6 +44,13 @@ public sealed class NurseVerificationController(ISender sender) : BaseController
|
||||
public async Task<IActionResult> ConfirmDocument(long stepId, ConfirmDocumentUploadCommand command, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(command with { StepId = stepId }, cancellationToken));
|
||||
|
||||
// Captures the structured credential fields (INO number, specialties, optional license details) B5
|
||||
// collects — the real path used to drop them silently.
|
||||
[HttpPost("[action]")]
|
||||
[ProducesOkApiResponseType<VerificationStatusDto>]
|
||||
public async Task<IActionResult> CredentialDetails(SubmitCredentialDetailsCommand command, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(command, cancellationToken));
|
||||
|
||||
[HttpPost("steps/identity_kyc/run")]
|
||||
[ProducesOkApiResponseType<RunStepResult>]
|
||||
public async Task<IActionResult> RunIdentityKyc(RunIdentityKycCommand command, CancellationToken cancellationToken)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Nurses.Queries.GetNursePublicProfile;
|
||||
using Baya.Application.Features.Reviews.Queries.GetTagAggregates;
|
||||
using Baya.Application.Features.Reviews.Queries.ListReviewsForNurse;
|
||||
using Baya.Application.Features.Verification.Queries.GetTrustBadge;
|
||||
using Baya.Application.Models.Nurses;
|
||||
using Baya.Application.Models.Reviews;
|
||||
using Baya.Application.Models.Verification;
|
||||
using Baya.WebFramework.Attributes;
|
||||
@@ -26,6 +28,12 @@ public sealed class NursesController(ISender sender) : BaseController
|
||||
public async Task<IActionResult> TrustBadge(long nurseId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetVerifiedTrustBadgeQuery(nurseId), cancellationToken));
|
||||
|
||||
// Public: the aggregated discovery detail (identity + aggregates + verification + services + latest review).
|
||||
[HttpGet("{nurseId}/[action]")]
|
||||
[ProducesOkApiResponseType<NursePublicProfileDto>]
|
||||
public async Task<IActionResult> Profile(long nurseId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetNursePublicProfileQuery(nurseId), cancellationToken));
|
||||
|
||||
// Public: published reviews only (the publish gate is enforced in the query) + the cached rating aggregate.
|
||||
[HttpGet("{nurseProfileId}/reviews")]
|
||||
[ProducesOkApiResponseType<NurseReviewsResult>]
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.PatientCareRecords.Commands.UpsertCarePlan;
|
||||
using Baya.Application.Features.PatientCareRecords.Commands.WritePatientCareRecord;
|
||||
using Baya.Application.Features.PatientCareRecords.Queries.GetCarePlan;
|
||||
using Baya.Application.Features.PatientCareRecords.Queries.GetPatientHistory;
|
||||
using Baya.Application.Features.PatientCareRecords.Queries.GetRecordAccess;
|
||||
using Baya.Application.Models.Common;
|
||||
using Baya.Application.Models.Patients;
|
||||
using Baya.Application.Models.Reviews;
|
||||
using Baya.WebFramework.Attributes;
|
||||
using Baya.WebFramework.BaseController;
|
||||
@@ -29,13 +33,38 @@ public sealed class PatientCareRecordsController(ISender sender) : BaseControlle
|
||||
[ProducesOkApiResponseType<WriteCareRecordResult>]
|
||||
public async Task<IActionResult> Write(long patientId, WriteCareRecordBody body, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(
|
||||
new WritePatientCareRecordCommand(patientId, body.BookingId, body.Body), cancellationToken));
|
||||
new WritePatientCareRecordCommand(patientId, body.BookingId, body.Body, body.TaskResults), cancellationToken));
|
||||
|
||||
[HttpGet("{patientId}/care_records")]
|
||||
[ProducesOkApiResponseType<PagedResult<CareRecordDto>>]
|
||||
public async Task<IActionResult> History(long patientId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default)
|
||||
=> OperationResult(await sender.Send(new GetPatientHistoryQuery(patientId, page, pageSize), cancellationToken));
|
||||
|
||||
// The family-owned care plan (medications/routine/tasks) — read (owner/nurse/admin), REQ-027.
|
||||
[HttpGet("{patientId}/care_record")]
|
||||
[ProducesOkApiResponseType<CarePlanDto>]
|
||||
public async Task<IActionResult> CarePlan(long patientId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetCarePlanQuery(patientId), cancellationToken));
|
||||
|
||||
// Replace the family-owned care plan (owning customer only), REQ-027.
|
||||
[HttpPut("{patientId}/care_record")]
|
||||
[ProducesOkApiResponseType<CarePlanDto>]
|
||||
public async Task<IActionResult> UpsertCarePlan(long patientId, UpsertCarePlanBody body, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(
|
||||
new UpsertCarePlanCommand(patientId, body.Medications, body.Routine, body.Tasks), cancellationToken));
|
||||
|
||||
// The caller's access to this patient's records (view/edit/append-note + non-leaking denied), REQ-027.
|
||||
[HttpGet("{patientId}/record_access")]
|
||||
[ProducesOkApiResponseType<RecordAccessDto>]
|
||||
public async Task<IActionResult> RecordAccess(long patientId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetRecordAccessQuery(patientId), cancellationToken));
|
||||
|
||||
/// <summary>The care-record body (the patient id comes from the route).</summary>
|
||||
public record WriteCareRecordBody(long? BookingId, string Body);
|
||||
public record WriteCareRecordBody(long? BookingId, string Body, IReadOnlyList<TaskResultDto>? TaskResults);
|
||||
|
||||
/// <summary>The family care-plan body (the patient id comes from the route).</summary>
|
||||
public record UpsertCarePlanBody(
|
||||
IReadOnlyList<MedicationDto>? Medications,
|
||||
IReadOnlyList<RoutineItemDto>? Routine,
|
||||
IReadOnlyList<CareTaskDto>? Tasks);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Asp.Versioning;
|
||||
using Baya.Application.Features.Refunds.Queries.GetRefundByBooking;
|
||||
using Baya.Application.Features.Refunds.Queries.GetRefundStatus;
|
||||
using Baya.Application.Models.Refunds;
|
||||
using Baya.WebFramework.Attributes;
|
||||
@@ -23,4 +24,10 @@ public sealed class RefundsController(ISender sender) : BaseController
|
||||
[ProducesOkApiResponseType<RefundStatusDto>]
|
||||
public async Task<IActionResult> Status(long id, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetRefundStatusQuery(id), cancellationToken));
|
||||
|
||||
// Reach the refund from its booking id (the id the customer holds) — 404 when none exists (REQ-021).
|
||||
[HttpGet("by_booking/{bookingId}")]
|
||||
[ProducesOkApiResponseType<RefundStatusDto>]
|
||||
public async Task<IActionResult> ByBooking(long bookingId, CancellationToken cancellationToken)
|
||||
=> OperationResult(await sender.Send(new GetRefundByBookingQuery(bookingId), cancellationToken));
|
||||
}
|
||||
|
||||
@@ -50,6 +50,14 @@ public class BaseController : ControllerBase
|
||||
return new JsonResult(new ApiResult(false, ApiResultStatusCode.Conflict, FirstErrorMessage(result)))
|
||||
{ StatusCode = StatusCodes.Status409Conflict };
|
||||
|
||||
// A coded failure (e.g. otp_locked) is written as the full envelope so the client sees the stable
|
||||
// `code` (+ optional `data`), rather than the bare ModelState errors of an ordinary 400.
|
||||
if (result.ErrorCode is not null)
|
||||
return new JsonResult(
|
||||
new ApiResult<object>(false, ApiResultStatusCode.BadRequest, result.ErrorData, FirstErrorMessage(result))
|
||||
{ Code = result.ErrorCode })
|
||||
{ StatusCode = StatusCodes.Status400BadRequest };
|
||||
|
||||
AddErrors(result);
|
||||
|
||||
var badRequestErrors = new ValidationProblemDetails(ModelState);
|
||||
|
||||
Reference in New Issue
Block a user