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
/// Datumsbereich unabhängig vom Mail-Versand-Status. Gefiltert wird über den
/// **Berliner** Kalendertag des Abschluss-Zeitpunkts (`completed_at`).
/// Liefert **alle** abgeschlossenen (ausgelieferten) Lieferungen — optional auf
/// einen Datumsbereich eingegrenzt, unabhängig vom Mail-Versand-Status.
/// Gefiltert wird über den **Berliner** Kalendertag des Abschluss-Zeitpunkts
/// (`completed_at`).
///
/// Parameter (alle `DD-MM-YYYY`): `day` = Einzeltag (Kurzform `from=to=day`),
/// sonst `from`/`to` als **inklusive** Bereichsgrenzen (je optional/offen).
/// Mindestens einer von `day`/`from`/`to` ist erforderlich. Pro Lieferung:
/// Belegnummer + `positions_modified` (Menge reduziert/Zeile entfernt oder
/// Geld-Gutschrift). Die Halb-Grenzen-Variante ist für Range-Filter gedacht:
/// `?from=…` und `?to=…` liefern je eine Menge, deren SQL-`AND`-Schnitt den
/// Zeitraum ergibt.
/// Parameter (alle `DD-MM-YYYY`, alle optional): `day` = Einzeltag (Kurzform
/// `from=to=day`), sonst `from`/`to` als **inklusive** Bereichsgrenzen (je
/// offen). **Ohne jeden Parameter → ALLE ausgelieferten Belege** (kein
/// Datumsfilter). Pro Lieferung: Belegnummer + `positions_modified` (Menge
/// reduziert/Zeile entfernt oder Geld-Gutschrift). Die Halb-Grenzen-Variante ist
/// für Range-Filter gedacht: `?from=…` und `?to=…` liefern je eine Menge, deren
/// SQL-`AND`-Schnitt den Zeitraum ergibt.
#[utoipa::path(
get,
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)")
),
responses(
(status = 200, description = "Abgeschlossene Lieferungen im Bereich", body = CompletedDeliveriesResponse),
(status = 400, description = "Ungültiges Datum oder keine Grenze angegeben"),
(status = 200, description = "Ausgelieferte Lieferungen (optional bereichsgefiltert)", body = CompletedDeliveriesResponse),
(status = 400, description = "Ungültiges Datum"),
(status = 401, description = "Admin-API-Key fehlt/ungültig")
),
security(("admin_api_key" = []))
@ -272,6 +273,7 @@ pub async fn completed_deliveries(
Query(query): Query<CompletedDeliveriesQuery>,
) -> Result<Json<CompletedDeliveriesResponse>, ApiError> {
// `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() {
Some(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");
let summaries = state.list_completed_deliveries.execute(from, to).await?;
tracing::info!(?from, ?to, count = summaries.len(), "admin.completed_deliveries.done");