feat(admin): completed-deliveries ohne Parameter = ALLE ausgelieferten Belege

Die "mindestens eine Grenze"-Sperre (400) entfernt. Ruft man
/admin/completed-deliveries ohne day/from/to, kommt jetzt der komplette
Bestand aller ausgelieferten (abgeschlossenen) Belege zurück, datumsunabhängig.
Der Repo-Query unterstützt from=NULL/to=NULL bereits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-07-09 22:33:07 +02:00
parent 865fcfcd23
commit 7c6d883d44

View File

@ -240,17 +240,18 @@ fn parse_ddmmyyyy(s: &str) -> Result<NaiveDate, ApiError> {
}) })
} }
/// Liefert **alle** abgeschlossenen (ausgelieferten) Lieferungen in einem /// Liefert **alle** abgeschlossenen (ausgelieferten) Lieferungen — optional auf
/// Datumsbereich unabhängig vom Mail-Versand-Status. Gefiltert wird über den /// einen Datumsbereich eingegrenzt, unabhängig vom Mail-Versand-Status.
/// **Berliner** Kalendertag des Abschluss-Zeitpunkts (`completed_at`). /// Gefiltert wird über den **Berliner** Kalendertag des Abschluss-Zeitpunkts
/// (`completed_at`).
/// ///
/// Parameter (alle `DD-MM-YYYY`): `day` = Einzeltag (Kurzform `from=to=day`), /// Parameter (alle `DD-MM-YYYY`, alle optional): `day` = Einzeltag (Kurzform
/// sonst `from`/`to` als **inklusive** Bereichsgrenzen (je optional/offen). /// `from=to=day`), sonst `from`/`to` als **inklusive** Bereichsgrenzen (je
/// Mindestens einer von `day`/`from`/`to` ist erforderlich. Pro Lieferung: /// offen). **Ohne jeden Parameter → ALLE ausgelieferten Belege** (kein
/// Belegnummer + `positions_modified` (Menge reduziert/Zeile entfernt oder /// Datumsfilter). Pro Lieferung: Belegnummer + `positions_modified` (Menge
/// Geld-Gutschrift). Die Halb-Grenzen-Variante ist für Range-Filter gedacht: /// reduziert/Zeile entfernt oder Geld-Gutschrift). Die Halb-Grenzen-Variante ist
/// `?from=…` und `?to=…` liefern je eine Menge, deren SQL-`AND`-Schnitt den /// für Range-Filter gedacht: `?from=…` und `?to=…` liefern je eine Menge, deren
/// Zeitraum ergibt. /// SQL-`AND`-Schnitt den Zeitraum ergibt.
#[utoipa::path( #[utoipa::path(
get, get,
path = "/admin/completed-deliveries", path = "/admin/completed-deliveries",
@ -261,8 +262,8 @@ fn parse_ddmmyyyy(s: &str) -> Result<NaiveDate, ApiError> {
("to" = Option<String>, Query, description = "Obere Grenze DD-MM-YYYY (inklusive)") ("to" = Option<String>, Query, description = "Obere Grenze DD-MM-YYYY (inklusive)")
), ),
responses( responses(
(status = 200, description = "Abgeschlossene Lieferungen im Bereich", body = CompletedDeliveriesResponse), (status = 200, description = "Ausgelieferte Lieferungen (optional bereichsgefiltert)", body = CompletedDeliveriesResponse),
(status = 400, description = "Ungültiges Datum oder keine Grenze angegeben"), (status = 400, description = "Ungültiges Datum"),
(status = 401, description = "Admin-API-Key fehlt/ungültig") (status = 401, description = "Admin-API-Key fehlt/ungültig")
), ),
security(("admin_api_key" = [])) security(("admin_api_key" = []))
@ -272,6 +273,7 @@ pub async fn completed_deliveries(
Query(query): Query<CompletedDeliveriesQuery>, Query(query): Query<CompletedDeliveriesQuery>,
) -> Result<Json<CompletedDeliveriesResponse>, ApiError> { ) -> Result<Json<CompletedDeliveriesResponse>, ApiError> {
// `day` ist die Kurzform und hat Vorrang; sonst freie Halb-/Vollgrenzen. // `day` ist die Kurzform und hat Vorrang; sonst freie Halb-/Vollgrenzen.
// Ohne jeden Parameter (from == to == None) ⇒ alle ausgelieferten Belege.
let (from, to) = match query.day.as_deref() { let (from, to) = match query.day.as_deref() {
Some(day) => { Some(day) => {
let d = parse_ddmmyyyy(day)?; let d = parse_ddmmyyyy(day)?;
@ -283,13 +285,6 @@ pub async fn completed_deliveries(
), ),
}; };
// Unbegrenzt (alles) wäre ein versehentlicher Full-Table-Dump → ablehnen.
if from.is_none() && to.is_none() {
return Err(ApiError(ApplicationError::Validation(
"mindestens einer von `day`, `from`, `to` (DD-MM-YYYY) ist erforderlich".into(),
)));
}
tracing::info!(?from, ?to, "admin.completed_deliveries"); tracing::info!(?from, ?to, "admin.completed_deliveries");
let summaries = state.list_completed_deliveries.execute(from, to).await?; let summaries = state.list_completed_deliveries.execute(from, to).await?;
tracing::info!(?from, ?to, count = summaries.len(), "admin.completed_deliveries.done"); tracing::info!(?from, ?to, count = summaries.len(), "admin.completed_deliveries.done");