using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using ProposalSystem.Application.DTOs; using ProposalSystem.Domain.Entities; using ProposalSystem.Infrastructure.Data; namespace ProposalSystem.Api.Controllers; [ApiController] [Route("api/vendor-proposals")] [Authorize(Roles = "admins,sysadmins")] public class VendorProposalsController : ControllerBase { private readonly ProposalDbContext _db; public VendorProposalsController(ProposalDbContext db) { _db = db; } [HttpPut("{id:guid}")] public async Task Update(Guid id, [FromBody] UpdateVendorProposalRequest request, CancellationToken ct) { var vendor = await _db.VendorProposals.FindAsync(new object[] { id }, ct); if (vendor == null) return NotFound(); if (request.VendorName != null) vendor.VendorName = request.VendorName; if (request.ExtractedData != null) vendor.ExtractedData = request.ExtractedData; if (request.TotalVendorCost.HasValue) vendor.TotalVendorCost = request.TotalVendorCost.Value; if (request.ProcessingStatus != null && Enum.TryParse(request.ProcessingStatus, out var status)) vendor.ProcessingStatus = status; await _db.SaveChangesAsync(ct); var proposal = await _db.Proposals.FindAsync(new object[] { vendor.ProposalId }, ct); if (proposal != null) { proposal.VendorTotalCost = await _db.VendorProposals .Where(v => v.ProposalId == vendor.ProposalId) .SumAsync(v => v.TotalVendorCost, ct); // Aggregate write must bump the concurrency version (scanner sweep // finding): unguarded like line-item appends, but a lost race then // surfaces as the fallback 409 instead of silently overwriting a // concurrent guarded edit. proposal.Version++; proposal.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(ct); } return NoContent(); } [HttpPut("{id:guid}/status")] public async Task UpdateStatus(Guid id, [FromBody] UpdateStatusRequest request, CancellationToken ct) { var vendor = await _db.VendorProposals.FindAsync(new object[] { id }, ct); if (vendor == null) return NotFound(); if (Enum.TryParse(request.ProcessingStatus, out var status)) vendor.ProcessingStatus = status; await _db.SaveChangesAsync(ct); return NoContent(); } }