feat(dev): /dev/reset-delivery — einzelne Lieferung per Belegnummer zurücksetzen

Setzt EINE Lieferung zurück, ohne die Tour zu löschen (Alternative zum
destruktiven /dev/resync): Abschluss (delivery_completions), Scan-/
Gutschrift-Audit + offener Report-Job weg, Positionen zurück (scanned/
credited = 0, Status in_progress), Lieferung wieder active (state_reason/
assigned_car/review_* geleert). Notizen/Dienstleistungen/Anhänge bleiben.

Vervollständigt den bisher nur als Port-Stub vorhandenen
reset_delivery_by_belegnummer (Build war dadurch kaputt) + Use Case +
DEV-Route (nur bei dev.sync_enabled gemountet, unauthentifiziert).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-06-24 13:35:45 +02:00
parent fb5f43ed7a
commit 819005eaa5
7 changed files with 173 additions and 2 deletions

View File

@ -912,6 +912,82 @@ impl TourRepository for PgTourRepository {
.map_err(db)?;
Ok(res.rows_affected())
}
async fn reset_delivery_by_belegnummer(
&self,
belegnummer: &str,
) -> Result<u64, ApplicationError> {
let mut tx = self.pool.begin().await.map_err(db)?;
// Subquery-Filter überall identisch: Lieferung(en) mit dieser Belegnr.
const BY_BELEG: &str =
"SELECT id FROM deliveries WHERE erp_belegnummer = $1";
// Abschluss + (evtl.) offener Report-Job + Scan-/Gutschrift-Audit löschen.
sqlx::query(&format!(
"DELETE FROM delivery_report_jobs WHERE delivery_id IN ({BY_BELEG})"
))
.bind(belegnummer)
.execute(&mut *tx)
.await
.map_err(db)?;
sqlx::query(&format!(
"DELETE FROM delivery_completions WHERE delivery_id IN ({BY_BELEG})"
))
.bind(belegnummer)
.execute(&mut *tx)
.await
.map_err(db)?;
sqlx::query(
"DELETE FROM scan_audit WHERE delivery_item_id IN (\
SELECT di.id FROM delivery_items di \
JOIN deliveries d ON d.id = di.delivery_id \
WHERE d.erp_belegnummer = $1)",
)
.bind(belegnummer)
.execute(&mut *tx)
.await
.map_err(db)?;
sqlx::query(&format!(
"DELETE FROM delivery_credit_audit WHERE delivery_id IN ({BY_BELEG})"
))
.bind(belegnummer)
.execute(&mut *tx)
.await
.map_err(db)?;
// Positionen zurück (verladen + gutgeschrieben = 0, Status offen).
sqlx::query(&format!(
"UPDATE delivery_items \
SET scanned_quantity = 0, scan_status = 'in_progress', \
held_reason = NULL, credited_quantity = 0, \
scan_last_updated_at = now() \
WHERE delivery_id IN ({BY_BELEG})"
))
.bind(belegnummer)
.execute(&mut *tx)
.await
.map_err(db)?;
// Lieferung wieder aktiv; Status-/Auto-/Review-Felder leeren.
let res = sqlx::query(
"UPDATE deliveries \
SET state = 'active', state_reason = NULL, assigned_car_id = NULL, \
review_resolved_at = NULL, review_resolved_by = NULL, \
review_note = NULL \
WHERE erp_belegnummer = $1",
)
.bind(belegnummer)
.execute(&mut *tx)
.await
.map_err(db)?;
tx.commit().await.map_err(db)?;
Ok(res.rows_affected())
}
}
// ===== Upsert-Helfer =====================================================