feature: Barcode/QR Cover Sheet System
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class CoverSheetService : ICoverSheetService
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly ILogger<CoverSheetService> _logger;
|
||||
|
||||
public CoverSheetService(AppDbContext db, ILogger<CoverSheetService> logger)
|
||||
{
|
||||
_db = db;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<CoverSheet>> GenerateAsync(
|
||||
GenerateCoverSheetsRequest request, Guid actorUserId)
|
||||
{
|
||||
var count = Math.Clamp(request.Count, 1, 100);
|
||||
var batchType = BatchTypeExtensions.FromDbString(request.BatchType.ToUpperInvariant());
|
||||
var track = string.IsNullOrEmpty(request.Track)
|
||||
? BatchTrack.Backfill
|
||||
: BatchTrackExtensions.FromDbString(request.Track.ToUpperInvariant());
|
||||
|
||||
if (request.PatientId.HasValue)
|
||||
{
|
||||
var patientExists = await _db.Patients.AnyAsync(p => p.Id == request.PatientId.Value);
|
||||
if (!patientExists)
|
||||
throw new NotFoundException("Patient not found.", "PATIENT_NOT_FOUND");
|
||||
}
|
||||
|
||||
if (request.AssignToUserId.HasValue)
|
||||
{
|
||||
var user = await _db.Users.FindAsync(request.AssignToUserId.Value);
|
||||
if (user is null || !user.IsActive)
|
||||
throw new NotFoundException("User not found or inactive.", "USER_NOT_FOUND");
|
||||
}
|
||||
|
||||
var sheets = new List<CoverSheet>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var code = $"VCR-CS-{Guid.NewGuid().ToString("N")[..8].ToUpperInvariant()}";
|
||||
sheets.Add(new CoverSheet
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Code = code,
|
||||
BatchType = batchType,
|
||||
Track = track,
|
||||
PatientId = request.PatientId,
|
||||
AssignToUserId = request.AssignToUserId,
|
||||
GeneratedByUserId = actorUserId,
|
||||
IsUsed = false,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
});
|
||||
}
|
||||
|
||||
_db.CoverSheets.AddRange(sheets);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Generated {Count} cover sheets for batch type {BatchType}, track {Track}",
|
||||
count, batchType.ToDbString(), track.ToDbString());
|
||||
|
||||
return sheets;
|
||||
}
|
||||
|
||||
public async Task<CoverSheet?> LookupByCodeAsync(string code)
|
||||
{
|
||||
return await _db.CoverSheets
|
||||
.Include(c => c.Patient)
|
||||
.Include(c => c.AssignToUser)
|
||||
.FirstOrDefaultAsync(c => c.Code == code.ToUpperInvariant().Trim());
|
||||
}
|
||||
|
||||
public async Task<CoverSheet?> LookupByIdAsync(Guid id)
|
||||
{
|
||||
return await _db.CoverSheets
|
||||
.Include(c => c.Patient)
|
||||
.Include(c => c.AssignToUser)
|
||||
.FirstOrDefaultAsync(c => c.Id == id);
|
||||
}
|
||||
|
||||
public async Task<List<CoverSheet>> GetByIdsAsync(IReadOnlyList<Guid> ids)
|
||||
{
|
||||
if (ids.Count == 0)
|
||||
return new List<CoverSheet>();
|
||||
|
||||
var idSet = ids.Distinct().ToList();
|
||||
var sheets = await _db.CoverSheets
|
||||
.AsNoTracking()
|
||||
.Include(c => c.Patient)
|
||||
.Include(c => c.AssignToUser)
|
||||
.Where(c => idSet.Contains(c.Id))
|
||||
.ToListAsync();
|
||||
|
||||
var byId = sheets.ToDictionary(c => c.Id);
|
||||
return idSet
|
||||
.Where(byId.ContainsKey)
|
||||
.Select(id => byId[id])
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task RedeemAsync(Guid coverSheetId, Guid batchId)
|
||||
{
|
||||
var sheet = await _db.CoverSheets.FindAsync(coverSheetId);
|
||||
if (sheet is null)
|
||||
throw new NotFoundException("Cover sheet not found.", "COVER_SHEET_NOT_FOUND");
|
||||
|
||||
if (sheet.IsUsed)
|
||||
throw new ConflictException(
|
||||
$"Cover sheet {sheet.Code} has already been used for batch {sheet.BatchId}.",
|
||||
"COVER_SHEET_ALREADY_USED");
|
||||
|
||||
sheet.IsUsed = true;
|
||||
sheet.BatchId = batchId;
|
||||
sheet.UsedAt = DateTimeOffset.UtcNow;
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<CoverSheet>> ListAsync(
|
||||
bool? isUsed, Guid? patientId, int page, int pageSize)
|
||||
{
|
||||
var query = _db.CoverSheets
|
||||
.AsNoTracking()
|
||||
.Include(c => c.Patient)
|
||||
.Include(c => c.AssignToUser)
|
||||
.AsQueryable();
|
||||
|
||||
if (isUsed.HasValue)
|
||||
query = query.Where(c => c.IsUsed == isUsed.Value);
|
||||
if (patientId.HasValue)
|
||||
query = query.Where(c => c.PatientId == patientId.Value);
|
||||
|
||||
return await query
|
||||
.OrderByDescending(c => c.CreatedAt)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user