backend phase 11

This commit is contained in:
hamid
2026-07-09 02:13:30 +03:30
parent 23605591eb
commit 465f75c29e
78 changed files with 9555 additions and 27 deletions
@@ -0,0 +1,32 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.Refunds.Commands.WriteOffClawback;
using Baya.Infrastructure.Identity.Identity.PermissionManager;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Baya.WebFramework.ServiceConfiguration;
using Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
namespace Baya.Web.Api.Controllers.V1;
/// <summary>Admin-only nurse-clawback management. Write-off marks a pending receivable uncollectable and posts
/// the balancing bad-debt correction; recovery via payout netting is b13.</summary>
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/admin_clawbacks")]
[Authorize(ConstantPolicies.DynamicPermission)]
[EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
[Display(Description = "Admin nurse-clawback write-off")]
public sealed class AdminClawbacksController(ISender sender) : BaseController
{
[HttpPost("{id}/[action]")]
[ProducesOkApiResponseType<bool>]
public async Task<IActionResult> WriteOff(long id, WriteOffClawbackBody body, CancellationToken cancellationToken)
=> OperationResult(await sender.Send(new WriteOffClawbackCommand(id, body.Reason), cancellationToken));
}
/// <summary>The write-off body (the id comes from the route).</summary>
public record WriteOffClawbackBody(string Reason);
@@ -0,0 +1,30 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.Invoices.Commands.IssueInvoice;
using Baya.Application.Models.Invoices;
using Baya.Infrastructure.Identity.Identity.PermissionManager;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Baya.WebFramework.ServiceConfiguration;
using Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
namespace Baya.Web.Api.Controllers.V1;
/// <summary>Admin-only invoice issuance. Issuing an invoice draws the next sequential number, computes VAT on
/// the commission line from config, and submits to (mocked) مودیان. Idempotent per booking.</summary>
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/admin_invoices")]
[Authorize(ConstantPolicies.DynamicPermission)]
[EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
[Display(Description = "Admin invoice issuance")]
public sealed class AdminInvoicesController(ISender sender) : BaseController
{
[HttpPost]
[ProducesOkApiResponseType<InvoiceDto>]
public async Task<IActionResult> Issue(IssueInvoiceCommand command, CancellationToken cancellationToken)
=> OperationResult(await sender.Send(command, cancellationToken));
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.Refunds.Commands.CreateRefund;
using Baya.Application.Features.Refunds.Queries.ListRefunds;
using Baya.Application.Models.Common;
using Baya.Application.Models.Refunds;
using Baya.Infrastructure.Identity.Identity.PermissionManager;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Baya.WebFramework.ServiceConfiguration;
using Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
namespace Baya.Web.Api.Controllers.V1;
/// <summary>
/// Admin-only refund console. Creating a refund reverses a captured booking payment across both fee legs, posts
/// the balanced ledger reversal, forks on whether the nurse was already paid (clawback), and is rate-limited as
/// a money endpoint. There is no customer refund-initiation path.
/// </summary>
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/admin_refunds")]
[Authorize(ConstantPolicies.DynamicPermission)]
[EnableRateLimiting(RateLimitingServiceExtension.SensitivePolicy)]
[Display(Description = "Admin refund creation + worklist")]
public sealed class AdminRefundsController(ISender sender) : BaseController
{
[HttpPost]
[ProducesOkApiResponseType<CreateRefundResult>]
public async Task<IActionResult> Create(CreateRefundCommand command, CancellationToken cancellationToken)
=> OperationResult(await sender.Send(command, cancellationToken));
[HttpGet]
[ProducesOkApiResponseType<PagedResult<RefundListItemDto>>]
public async Task<IActionResult> List([FromQuery] ListRefundsQuery query, CancellationToken cancellationToken)
=> OperationResult(await sender.Send(query, cancellationToken));
}
@@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.Invoices.Queries.GetInvoice;
using Baya.Application.Models.Invoices;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Baya.Web.Api.Controllers.V1;
/// <summary>The booking's invoice for the customer (tenancy-scoped) or an admin. Read-only.</summary>
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/invoices")]
[Authorize]
[Display(Description = "Booking invoice (customer/admin)")]
public sealed class InvoicesController(ISender sender) : BaseController
{
[HttpGet("{bookingId}")]
[ProducesOkApiResponseType<InvoiceDto>]
public async Task<IActionResult> Get(long bookingId, CancellationToken cancellationToken)
=> OperationResult(await sender.Send(new GetInvoiceQuery(bookingId), cancellationToken));
}
@@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Baya.Application.Features.Refunds.Queries.GetRefundStatus;
using Baya.Application.Models.Refunds;
using Baya.WebFramework.Attributes;
using Baya.WebFramework.BaseController;
using Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Baya.Web.Api.Controllers.V1;
/// <summary>The customer-facing refund status — the only customer-visible refund surface (there is no
/// self-service refund initiation). Tenancy-scoped: a customer sees only their own refund.</summary>
[ApiVersion("1")]
[ApiController]
[Route("api/v{version:apiVersion}/refunds")]
[Authorize]
[Display(Description = "Customer refund status")]
public sealed class RefundsController(ISender sender) : BaseController
{
[HttpGet("{id}/status")]
[ProducesOkApiResponseType<RefundStatusDto>]
public async Task<IActionResult> Status(long id, CancellationToken cancellationToken)
=> OperationResult(await sender.Send(new GetRefundStatusQuery(id), cancellationToken));
}