using Dapper; using FastEndpoints; using Modules.WMS.Picking.Shared; using Npgsql; using Shared.Problems; namespace Modules.WMS.Picking.Start; public sealed record StartPickingRequest(long ExpectedVersion); public sealed class Endpoint(NpgsqlDataSource dataSource) : Endpoint { public override void Configure() { Post("/api/wms/picking/tasks/{taskId:guid}/start"); Permissions("wms.picking.execute"); } public override async Task HandleAsync(StartPickingRequest req, CancellationToken ct) { var taskId = Route("taskId"); await using var connection = await dataSource.OpenConnectionAsync(ct); await using var tx = await connection.BeginTransactionAsync(ct); const string sql = """ update wms.picking_tasks set status = 'IN_PROGRESS', started_at = coalesce(started_at, now()), version = version + 1, updated_at = now() where id = @TaskId and status = 'READY' and version = @ExpectedVersion returning version; """; var version = await connection.QuerySingleOrDefaultAsync( new CommandDefinition(sql, new { TaskId = taskId, req.ExpectedVersion }, tx, cancellationToken: ct)); if (version is null) { var current = await connection.QuerySingleOrDefaultAsync( new CommandDefinition("select version from wms.picking_tasks where id=@TaskId", new { TaskId = taskId }, tx, cancellationToken: ct)); await tx.RollbackAsync(ct); await Send.ResponseAsync(KbxConflictProblem.Version(current), 409, cancellation: ct); return; } await connection.ExecuteAsync(new CommandDefinition(""" insert into audit.entries(id, aggregate_type, aggregate_id, action, actor, occurred_at, data) values (@Id, 'WmsPickingTask', @TaskId, 'PickingStarted', @Actor, now(), '{}'::jsonb); """, new { Id = Guid.NewGuid(), TaskId = taskId, Actor = User.Identity?.Name ?? "unknown" }, tx, cancellationToken: ct)); await tx.CommitAsync(ct); var task = await PickingTaskQueries.GetAsync(connection, null, taskId, ct); await SendOkAsync(task!, ct); } }