feat(web): use compact cut list identifiers
Build CutList image / build-and-push (push) Successful in 18s
Build CutList image / build-and-push (push) Successful in 18s
This commit is contained in:
@@ -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-%';
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user