feat(web): use compact cut list identifiers
Build CutList image / build-and-push (push) Successful in 18s

This commit is contained in:
aj
2026-08-02 21:33:29 +00:00
parent 34c9b77501
commit 57c7aa5f9f
3 changed files with 68 additions and 24 deletions
@@ -0,0 +1,31 @@
using CutList.Web.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CutList.Web.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20260802212500_RemoveJobNumberPadding")]
public partial class RemoveJobNumberPadding : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
UPDATE Jobs
SET JobNumber = 'JOB-' + CAST(Id AS varchar(11))
WHERE JobNumber LIKE 'JOB-%';
");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
UPDATE Jobs
SET JobNumber = 'JOB-' + RIGHT('00000' + CAST(Id AS varchar(5)), 5)
WHERE JobNumber LIKE 'JOB-%';
");
}
}
}
+9 -24
View File
@@ -42,35 +42,17 @@ public class JobService
{
await using var context = _factory.CreateDbContext();
job ??= new Job();
job.JobNumber = await GenerateJobNumberAsync(context);
job.JobNumber = CreateTemporaryJobNumber();
job.CreatedAt = DateTime.UtcNow;
context.Jobs.Add(job);
await context.SaveChangesAsync();
job.JobNumber = $"JOB-{job.Id}";
await context.SaveChangesAsync();
return job;
}
public async Task<string> GenerateJobNumberAsync()
{
await using var context = _factory.CreateDbContext();
return await GenerateJobNumberAsync(context);
}
private static async Task<string> GenerateJobNumberAsync(ApplicationDbContext context)
{
var maxNumber = await context.Jobs
.Where(j => j.JobNumber.StartsWith("JOB-"))
.Select(j => j.JobNumber)
.MaxAsync() as string;
if (maxNumber == null)
return "JOB-00001";
var numPart = maxNumber.Substring(4);
if (int.TryParse(numPart, out var num))
return $"JOB-{num + 1:D5}";
return $"JOB-{DateTime.UtcNow:yyyyMMddHHmmss}";
}
private static string CreateTemporaryJobNumber() => $"TMP-{Guid.NewGuid():N}"[..20];
public async Task<Job> QuickCreateAsync(string? customer = null)
{
@@ -142,7 +124,7 @@ public class JobService
var duplicate = new Job
{
JobNumber = await GenerateJobNumberAsync(context),
JobNumber = CreateTemporaryJobNumber(),
Name = string.IsNullOrWhiteSpace(original.Name) ? null : $"{original.Name} (Copy)",
Customer = original.Customer,
CuttingToolId = original.CuttingToolId,
@@ -153,6 +135,9 @@ public class JobService
context.Jobs.Add(duplicate);
await context.SaveChangesAsync();
duplicate.JobNumber = $"JOB-{duplicate.Id}";
await context.SaveChangesAsync();
// Copy parts
foreach (var part in original.Parts)
{
+28
View File
@@ -0,0 +1,28 @@
"""Regression checks for compact CutList identifiers."""
from __future__ import annotations
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
JOB_SERVICE = REPO_ROOT / "CutList.Web/Services/JobService.cs"
UNPAD_MIGRATION = REPO_ROOT / "CutList.Web/Migrations/20260802212500_RemoveJobNumberPadding.cs"
class JobNumberFormatTests(unittest.TestCase):
def test_new_jobs_use_the_database_id_without_zero_padding(self) -> None:
service = JOB_SERVICE.read_text()
self.assertIn('job.JobNumber = $"JOB-{job.Id}";', service)
self.assertNotIn("D5", service)
self.assertNotIn("MaxAsync() as string", service)
def test_existing_job_numbers_are_converted_to_their_database_ids(self) -> None:
migration = UNPAD_MIGRATION.read_text()
self.assertIn("SET JobNumber = 'JOB-' + CAST(Id AS varchar(11))", migration)
if __name__ == "__main__":
unittest.main()