Add GetNextSerialSequence functionality to Stock services and repository

This commit is contained in:
2026-10-08 12:42:50 +05:30
parent 55aa35308a
commit 702efba62e
4 changed files with 34 additions and 0 deletions
@@ -28,5 +28,7 @@ public interface IStockRepository
// Serial numbers // Serial numbers
Task<bool> SerialNumberExistsAsync(Guid itemId, string serialNumber); Task<bool> SerialNumberExistsAsync(Guid itemId, string serialNumber);
/// <summary>One past the highest numeric suffix among the item's existing serials that start with <paramref name="prefix"/> (1 if none).</summary>
Task<int> GetNextSerialSequenceAsync(Guid itemId, string prefix);
Task AddItemSerialNumberAsync(ItemSerialNumber serial); Task AddItemSerialNumberAsync(ItemSerialNumber serial);
} }
@@ -37,6 +37,14 @@ public class StockController(StockService service, ICurrentUserService currentUs
return Ok(result); return Ok(result);
} }
/// <summary>Next free number for auto-generated serials (e.g. after ...-0005 the answer is 6).</summary>
[HttpGet("serials/next-sequence")]
public async Task<IActionResult> GetNextSerialSequence([FromQuery] Guid itemId, [FromQuery] string prefix = "")
{
var next = await service.GetNextSerialSequenceAsync(itemId, prefix);
return Ok(new { next });
}
[HttpGet("movements")] [HttpGet("movements")]
public async Task<IActionResult> GetMovements( public async Task<IActionResult> GetMovements(
[FromQuery] Guid? locationId, [FromQuery] Guid? locationId,
@@ -107,6 +107,26 @@ public class StockRepository(AppDBContext db) : IStockRepository
public Task<bool> SerialNumberExistsAsync(Guid itemId, string serialNumber) => public Task<bool> SerialNumberExistsAsync(Guid itemId, string serialNumber) =>
db.ItemSerialNumbers.AnyAsync(s => s.FK_ItemId == itemId && s.SerialNumber == serialNumber); db.ItemSerialNumbers.AnyAsync(s => s.FK_ItemId == itemId && s.SerialNumber == serialNumber);
public async Task<int> GetNextSerialSequenceAsync(Guid itemId, string prefix)
{
// Only serials of the form <prefix><digits> count; anything else (a supplier's own serial that
// happens to start with the prefix) is ignored for numbering.
var serials = await db.ItemSerialNumbers
.AsNoTracking()
.Where(s => s.FK_ItemId == itemId && s.SerialNumber.StartsWith(prefix))
.Select(s => s.SerialNumber)
.ToListAsync();
var max = 0L;
foreach (var serial in serials)
{
var suffix = serial[prefix.Length..];
if (suffix.Length is > 0 and <= 9 && suffix.All(char.IsDigit) && long.TryParse(suffix, out var n))
max = Math.Max(max, n);
}
return (int)max + 1;
}
public async Task AddItemSerialNumberAsync(ItemSerialNumber serial) => public async Task AddItemSerialNumberAsync(ItemSerialNumber serial) =>
await db.ItemSerialNumbers.AddAsync(serial); await db.ItemSerialNumbers.AddAsync(serial);
} }
@@ -96,6 +96,10 @@ public class StockService(
b.Quantity, b.Item.ReorderLevel!.Value)).ToList(); b.Quantity, b.Item.ReorderLevel!.Value)).ToList();
} }
/// <summary>The next unused sequence number for generating serials like "&lt;prefix&gt;0001" for an item.</summary>
public Task<int> GetNextSerialSequenceAsync(Guid itemId, string prefix) =>
repo.GetNextSerialSequenceAsync(itemId, prefix ?? string.Empty);
public async Task<StockTotalResponse?> GetItemTotalAsync(Guid itemId) public async Task<StockTotalResponse?> GetItemTotalAsync(Guid itemId)
{ {
var item = await itemRepo.GetByIdAsync(itemId); var item = await itemRepo.GetByIdAsync(itemId);