diff --git a/API/ROLAC.API.Tests/Services/OfferingSessionServiceTests.cs b/API/ROLAC.API.Tests/Services/OfferingSessionServiceTests.cs index 4e9593e..824fd45 100644 --- a/API/ROLAC.API.Tests/Services/OfferingSessionServiceTests.cs +++ b/API/ROLAC.API.Tests/Services/OfferingSessionServiceTests.cs @@ -129,4 +129,27 @@ public class OfferingSessionServiceTests Assert.Equal(200m, after.SystemTotal); Assert.Equal(1, await db.Givings.CountAsync(g => g.OfferingSessionId == id)); } + + [Fact] + public async Task GetByIdAsync_ReturnsCheckZelleAndPayPalRefs() + { + using var db = BuildDb(); + var catId = await SeedCategoryAsync(db); + var svc = new OfferingSessionService(db, BuildAccessor()); + var req = new CreateOfferingSessionRequest + { + SessionDate = new DateOnly(2026, 6, 7), CashTotal = 0m, CheckTotal = 100m, + Givings = [ new() { GivingCategoryId = catId, Amount = 100m, PaymentMethod = "Zelle", + ZelleReferenceCode = "Z-123", PayPalTransactionId = "PP-456", CheckNumber = "C-789" } ], + }; + var id = await svc.CreateAsync(req); + + var dto = await svc.GetByIdAsync(id); + + Assert.NotNull(dto); + var line = Assert.Single(dto!.Givings); + Assert.Equal("Z-123", line.ZelleReferenceCode); + Assert.Equal("PP-456", line.PayPalTransactionId); + Assert.Equal("C-789", line.CheckNumber); + } } diff --git a/API/ROLAC.API/DTOs/Giving/OfferingGivingLineDto.cs b/API/ROLAC.API/DTOs/Giving/OfferingGivingLineDto.cs index 28c822b..687f474 100644 --- a/API/ROLAC.API/DTOs/Giving/OfferingGivingLineDto.cs +++ b/API/ROLAC.API/DTOs/Giving/OfferingGivingLineDto.cs @@ -10,6 +10,8 @@ public class OfferingGivingLineDto public decimal Amount { get; set; } public string PaymentMethod { get; set; } = ""; public string? CheckNumber { get; set; } + public string? ZelleReferenceCode { get; set; } + public string? PayPalTransactionId { get; set; } public bool IsAnonymous { get; set; } public string? Notes { get; set; } } diff --git a/API/ROLAC.API/Services/OfferingSessionService.cs b/API/ROLAC.API/Services/OfferingSessionService.cs index 6b7df50..c90b368 100644 --- a/API/ROLAC.API/Services/OfferingSessionService.cs +++ b/API/ROLAC.API/Services/OfferingSessionService.cs @@ -57,6 +57,10 @@ public class OfferingSessionService : IOfferingSessionService public async Task DateExistsAsync(DateOnly date) => await _db.OfferingSessions.AnyAsync(s => s.SessionDate == date); + // Distinguishes a unique-index collision on SessionDate (concurrent insert) from other DB errors. + private async Task DateExistsConcurrentlyAsync(DateOnly date, int excludeId) + => await _db.OfferingSessions.AsNoTracking().AnyAsync(s => s.SessionDate == date && s.Id != excludeId); + public async Task GetByIdAsync(int id) { var s = await _db.OfferingSessions.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id); @@ -84,7 +88,10 @@ public class OfferingSessionService : IOfferingSessionService GivingCategoryId = l.GivingCategoryId, CategoryName = catNames.TryGetValue(l.GivingCategoryId, out var cn) ? cn : "", Amount = l.Amount, PaymentMethod = l.PaymentMethod, - CheckNumber = l.CheckNumber, IsAnonymous = l.IsAnonymous, Notes = l.Notes, + CheckNumber = l.CheckNumber, + ZelleReferenceCode = l.ZelleReferenceCode, + PayPalTransactionId = l.PayPalTransactionId, + IsAnonymous = l.IsAnonymous, Notes = l.Notes, }).ToList(), }; } @@ -106,7 +113,16 @@ public class OfferingSessionService : IOfferingSessionService Givings = r.Givings.Select(line => MapLine(line, r.SessionDate)).ToList(), }; _db.OfferingSessions.Add(session); - await _db.SaveChangesAsync(); + try + { + await _db.SaveChangesAsync(); + } + catch (DbUpdateException) + { + if (await DateExistsConcurrentlyAsync(r.SessionDate, session.Id)) + throw new InvalidOperationException($"An offering session for {r.SessionDate:yyyy-MM-dd} already exists."); + throw; + } return session.Id; }