got saved a RdvQuery
This commit is contained in:
parent
67715f9d67
commit
cbb59f019d
17 changed files with 616 additions and 288 deletions
|
|
@ -7,6 +7,8 @@ using Yavsc.Billing;
|
|||
using Yavsc.Helpers;
|
||||
using Yavsc.ViewModels;
|
||||
using Yavsc.Models.Billing;
|
||||
using Yavsc.Models.Haircut;
|
||||
using Yavsc.Models.Workflow;
|
||||
using Yavsc.Server.Models.FileSystem;
|
||||
|
||||
namespace Yavsc.ApiControllers
|
||||
|
|
@ -121,12 +123,38 @@ namespace Yavsc.ApiControllers
|
|||
WorkflowHelpers.ConfigureBillingService();
|
||||
}
|
||||
|
||||
var commands = dbContext.Set<NominativeServiceCommand>()
|
||||
// Query known derived types explicitly so legacy rows with
|
||||
// invalid/empty discriminator values are naturally ignored.
|
||||
var rdvCommands = dbContext.Set<RdvQuery>()
|
||||
.AsNoTracking()
|
||||
.Where(q => q.PerformerId == uid)
|
||||
.Where(q => q.Status == QueryStatus.Inserted
|
||||
|| q.Status == QueryStatus.Accepted
|
||||
|| q.Status == QueryStatus.InProgress)
|
||||
.Cast<NominativeServiceCommand>()
|
||||
.ToList();
|
||||
|
||||
var hairCommands = dbContext.Set<HairCutQuery>()
|
||||
.AsNoTracking()
|
||||
.Where(q => q.PerformerId == uid)
|
||||
.Where(q => q.Status == QueryStatus.Inserted
|
||||
|| q.Status == QueryStatus.Accepted
|
||||
|| q.Status == QueryStatus.InProgress)
|
||||
.Cast<NominativeServiceCommand>()
|
||||
.ToList();
|
||||
|
||||
var hairMultiCommands = dbContext.Set<HairMultiCutQuery>()
|
||||
.AsNoTracking()
|
||||
.Where(q => q.PerformerId == uid)
|
||||
.Where(q => q.Status == QueryStatus.Inserted
|
||||
|| q.Status == QueryStatus.Accepted
|
||||
|| q.Status == QueryStatus.InProgress)
|
||||
.Cast<NominativeServiceCommand>()
|
||||
.ToList();
|
||||
|
||||
var commands = rdvCommands
|
||||
.Concat(hairCommands)
|
||||
.Concat(hairMultiCommands)
|
||||
.OrderByDescending(q => q.DateModified)
|
||||
.ThenByDescending(q => q.Id)
|
||||
.ToList();
|
||||
|
|
|
|||
|
|
@ -1,193 +0,0 @@
|
|||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Yavsc.Controllers
|
||||
{
|
||||
using System;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Workflow;
|
||||
using Yavsc.Models.Billing;
|
||||
using Yavsc.Abstract.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Server.Helpers;
|
||||
|
||||
[Authorize]
|
||||
[Produces("application/json")]
|
||||
[Route(Constants.APIPrefix + "/bookquery"), Authorize("Performer")]
|
||||
public class BookQueryApiController : Controller
|
||||
{
|
||||
private ApplicationDbContext _context;
|
||||
private ILogger _logger;
|
||||
|
||||
public BookQueryApiController(ApplicationDbContext context, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_context = context;
|
||||
_logger = loggerFactory.CreateLogger<BookQueryApiController>();
|
||||
}
|
||||
|
||||
// GET: api/BookQueryApi
|
||||
/// <summary>
|
||||
/// Book queries, by creation order
|
||||
/// </summary>
|
||||
/// <param name="maxId">returned Ids must be lower than this value</param>
|
||||
/// <returns>book queries</returns>
|
||||
[HttpGet]
|
||||
public IEnumerable<RdvQueryProviderInfo> GetCommands(long maxId=long.MaxValue)
|
||||
{
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var result = _context.RdvQueries.Include(c => c.Location).
|
||||
Include(c => c.Client).Where(c => c.PerformerId == uid && c.Id < maxId && c.EventDate > now
|
||||
&& c.ValidationDate == null).
|
||||
Select(c => new RdvQueryProviderInfo
|
||||
{
|
||||
Client = new ClientProviderInfo {
|
||||
UserName = c.Client.UserName,
|
||||
UserId = c.ClientId,
|
||||
Avatar = c.Client.Avatar },
|
||||
Location = c.Location,
|
||||
EventDate = c.EventDate,
|
||||
Id = c.Id,
|
||||
Previsional = c.Provisional,
|
||||
Reason = c.Reason,
|
||||
ActivityCode = c.ActivityCode,
|
||||
BillingCode = BillingCodes.Rdv
|
||||
}).
|
||||
OrderBy(c=>c.Id).
|
||||
Take(25);
|
||||
return result;
|
||||
}
|
||||
|
||||
// GET: api/BookQueryApi/5
|
||||
[HttpGet("{id}", Name = "GetBookQuery")]
|
||||
public IActionResult GetBookQuery([FromRoute] long id)
|
||||
{
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
|
||||
RdvQuery bookQuery = _context.RdvQueries.Where(c => c.ClientId == uid || c.PerformerId == uid).Single(m => m.Id == id);
|
||||
|
||||
if (bookQuery == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(bookQuery);
|
||||
}
|
||||
|
||||
// PUT: api/BookQueryApi/5
|
||||
[HttpPut("{id}")]
|
||||
public IActionResult PutBookQuery(long id, [FromBody] RdvQuery bookQuery)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != bookQuery.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (bookQuery.ClientId != uid)
|
||||
return NotFound();
|
||||
|
||||
_context.Entry(bookQuery).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!BookQueryExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return new StatusCodeResult(StatusCodes.Status204NoContent);
|
||||
}
|
||||
|
||||
// POST: api/BookQueryApi
|
||||
[HttpPost]
|
||||
public IActionResult PostBookQuery([FromBody] RdvQuery bookQuery)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (bookQuery.ClientId != uid)
|
||||
{
|
||||
ModelState.AddModelError("ClientId", "You must be the client at creating a book query");
|
||||
return new BadRequestObjectResult(ModelState);
|
||||
}
|
||||
_context.RdvQueries.Add(bookQuery);
|
||||
try
|
||||
{
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
if (BookQueryExists(bookQuery.Id))
|
||||
{
|
||||
return new StatusCodeResult(StatusCodes.Status409Conflict);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return CreatedAtRoute("GetBookQuery", new { id = bookQuery.Id }, bookQuery);
|
||||
}
|
||||
|
||||
// DELETE: api/BookQueryApi/5
|
||||
[HttpDelete("{id}")]
|
||||
public IActionResult DeleteBookQuery(long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
RdvQuery bookQuery = _context.RdvQueries.Single(m => m.Id == id);
|
||||
|
||||
if (bookQuery == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (bookQuery.ClientId != uid) return NotFound();
|
||||
|
||||
_context.RdvQueries.Remove(bookQuery);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
|
||||
return Ok(bookQuery);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_context.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private bool BookQueryExists(long id)
|
||||
{
|
||||
return _context.RdvQueries.Count(e => e.Id == id) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
#nullable enable annotations
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Npgsql;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Billing;
|
||||
using Yavsc.Models.Relationship;
|
||||
using Yavsc.Models.Workflow;
|
||||
using Yavsc.Server.Helpers;
|
||||
|
||||
|
|
@ -83,30 +88,32 @@ public class RdvQueryApiController : Controller
|
|||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (query.Location is not null)
|
||||
if (query.Location is null)
|
||||
{
|
||||
var existingLocation = await _context.Locations.FirstOrDefaultAsync(
|
||||
x => x.Address == query.Location.Address
|
||||
&& x.Longitude == query.Location.Longitude
|
||||
&& x.Latitude == query.Location.Latitude,
|
||||
cancellationToken);
|
||||
|
||||
if (existingLocation is not null)
|
||||
{
|
||||
query.Location = existingLocation;
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Attach(query.Location);
|
||||
}
|
||||
return BadRequest(new { Error = "location is required" });
|
||||
}
|
||||
|
||||
_context.RdvQueries.Add(query);
|
||||
var resolvedLocation = await ResolveLocationAsync(query.Location, cancellationToken);
|
||||
if (resolvedLocation is null)
|
||||
{
|
||||
return BadRequest(new { Error = "location payload is invalid" });
|
||||
}
|
||||
|
||||
await PersistLocationIfNeededAsync(resolvedLocation, uid, cancellationToken);
|
||||
|
||||
query.Location = resolvedLocation;
|
||||
|
||||
var addedEntry = _context.RdvQueries.Add(query);
|
||||
EnsureLocationForeignKey(addedEntry, resolvedLocation.Id);
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync(User.GetUserId(), cancellationToken);
|
||||
}
|
||||
catch (DbUpdateException ex) when (IsLocationForeignKeyViolation(ex))
|
||||
{
|
||||
return BadRequest(new { Error = "location reference is invalid" });
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
if (QueryExists(query.Id))
|
||||
|
|
@ -149,17 +156,16 @@ public class RdvQueryApiController : Controller
|
|||
|
||||
if (query.Location is not null)
|
||||
{
|
||||
var resolvedLocation = await _context.Locations.FirstOrDefaultAsync(
|
||||
x => x.Address == query.Location.Address
|
||||
&& x.Longitude == query.Location.Longitude
|
||||
&& x.Latitude == query.Location.Latitude,
|
||||
cancellationToken);
|
||||
|
||||
existing.Location = resolvedLocation ?? query.Location;
|
||||
var resolvedLocation = await ResolveLocationAsync(query.Location, cancellationToken);
|
||||
if (resolvedLocation is null)
|
||||
{
|
||||
_context.Attach(query.Location);
|
||||
return BadRequest(new { Error = "location payload is invalid" });
|
||||
}
|
||||
|
||||
await PersistLocationIfNeededAsync(resolvedLocation, uid, cancellationToken);
|
||||
|
||||
existing.Location = resolvedLocation;
|
||||
EnsureLocationForeignKey(_context.Entry(existing), resolvedLocation.Id);
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -175,6 +181,10 @@ public class RdvQueryApiController : Controller
|
|||
|
||||
throw;
|
||||
}
|
||||
catch (DbUpdateException ex) when (IsLocationForeignKeyViolation(ex))
|
||||
{
|
||||
return BadRequest(new { Error = "location reference is invalid" });
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
|
@ -208,6 +218,78 @@ public class RdvQueryApiController : Controller
|
|||
return _context.RdvQueries.Any(e => e.Id == id);
|
||||
}
|
||||
|
||||
private async Task<Location?> ResolveLocationAsync(Location postedLocation, CancellationToken cancellationToken)
|
||||
{
|
||||
if (postedLocation.Id > 0)
|
||||
{
|
||||
var byId = await _context.Locations
|
||||
.FirstOrDefaultAsync(x => x.Id == postedLocation.Id, cancellationToken);
|
||||
if (byId is not null)
|
||||
{
|
||||
return byId;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(postedLocation.Address))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var existingByCoordinates = await _context.Locations.FirstOrDefaultAsync(
|
||||
x => x.Address == postedLocation.Address
|
||||
&& x.Longitude == postedLocation.Longitude
|
||||
&& x.Latitude == postedLocation.Latitude,
|
||||
cancellationToken);
|
||||
|
||||
if (existingByCoordinates is not null)
|
||||
{
|
||||
return existingByCoordinates;
|
||||
}
|
||||
|
||||
// Treat unknown location ids as client-side placeholders and insert a new row.
|
||||
postedLocation.Id = 0;
|
||||
_context.Locations.Add(postedLocation);
|
||||
return postedLocation;
|
||||
}
|
||||
|
||||
private async Task PersistLocationIfNeededAsync(Location location, string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_context.Entry(location).State != EntityState.Added)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(userId, cancellationToken);
|
||||
}
|
||||
|
||||
private static bool IsLocationForeignKeyViolation(DbUpdateException ex)
|
||||
{
|
||||
if (ex.InnerException is not PostgresException pg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return pg.SqlState == PostgresErrorCodes.ForeignKeyViolation
|
||||
&& string.Equals(pg.ConstraintName, "FK_NominativeServiceCommand_Locations_LocationId", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static void EnsureLocationForeignKey(EntityEntry<RdvQuery> entry, long locationId)
|
||||
{
|
||||
SetFkIfPresent(entry, "LocationId", locationId);
|
||||
SetFkIfPresent(entry, "RdvQuery_LocationId", locationId);
|
||||
}
|
||||
|
||||
private static void SetFkIfPresent(EntityEntry<RdvQuery> entry, string propertyName, long value)
|
||||
{
|
||||
var property = entry.Metadata.FindProperty(propertyName);
|
||||
if (property is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entry.Property(propertyName).CurrentValue = value;
|
||||
}
|
||||
|
||||
private static DateTime EnsureUtc(DateTime value)
|
||||
{
|
||||
return value.Kind switch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue