feat(admin): GET /admin/completed-deliveries — abgeschlossene Lieferungen eines Tages
Neuer Admin-Endpunkt, der ALLE an einem Tag (Berliner Kalendertag von completed_at) abgeschlossenen Lieferungen liefert — unabhängig vom Mail-Versand-Status. Pro Lieferung: Belegnummer und ein Flag `positions_modified`, das true ist, wenn eine Belegzeile in der Menge reduziert/entfernt wurde (delivery_items.credited_quantity > 0) oder eine aktive Geld-Gutschrift vorliegt (jüngstes delivery_credit_audit-Event 'set'). Pflichtparameter `day` (DD-MM-YYYY). Geschützt per admin_api_key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -39,7 +39,8 @@ use holzleitner_application::usecases::{
|
||||
DeleteServiceUseCase, DevResetDeliveryUseCase, DevResyncToursUseCase,
|
||||
GenerateDeliveryReportUseCase, GetAccountUseCase,
|
||||
GetAttachmentPreviewUseCase, GetTourUseCase,
|
||||
ImportErpToursUseCase, ListDeliveredBelegnummernUseCase, ListMyCarsUseCase,
|
||||
ImportErpToursUseCase, ListCompletedDeliveriesUseCase, ListDeliveredBelegnummernUseCase,
|
||||
ListMyCarsUseCase,
|
||||
ListMyToursTodayUseCase, ListPaymentMethodsUseCase,
|
||||
ListPendingReviewsUseCase, ListServicesUseCase, MarkMailSentUseCase,
|
||||
PushCompletionToErpUseCase, ResolveReviewUseCase,
|
||||
@ -365,6 +366,11 @@ pub(crate) async fn run_app(
|
||||
let list_delivered_belegnummern = Arc::new(ListDeliveredBelegnummernUseCase::new(
|
||||
delivery_completion_repository.clone(),
|
||||
));
|
||||
// Admin-Lese-Use-Case: alle an einem Tag abgeschlossenen Lieferungen samt
|
||||
// Positions-Änderungs-Flag. `.clone()` VOR dem Move in `complete_delivery`.
|
||||
let list_completed_deliveries = Arc::new(ListCompletedDeliveriesUseCase::new(
|
||||
delivery_completion_repository.clone(),
|
||||
));
|
||||
let mark_mail_sent = Arc::new(MarkMailSentUseCase::new(
|
||||
delivery_completion_repository.clone(),
|
||||
));
|
||||
@ -460,6 +466,7 @@ pub(crate) async fn run_app(
|
||||
complete_delivery,
|
||||
push_completion_to_erp,
|
||||
list_delivered_belegnummern,
|
||||
list_completed_deliveries,
|
||||
mark_mail_sent,
|
||||
list_pending_reviews,
|
||||
resolve_review,
|
||||
|
||||
@ -54,6 +54,7 @@ use utoipa::openapi::security::{
|
||||
crate::routes::admin::import_erp,
|
||||
crate::routes::admin::push_completion,
|
||||
crate::routes::admin::delivered_belegnummern,
|
||||
crate::routes::admin::completed_deliveries,
|
||||
crate::routes::admin::mark_mail_sent,
|
||||
crate::routes::admin::list_reviews,
|
||||
crate::routes::admin::resolve_review,
|
||||
@ -127,6 +128,8 @@ use utoipa::openapi::security::{
|
||||
holzleitner_application::dto::DeliveryServiceResponse,
|
||||
holzleitner_application::usecases::ImportSummary,
|
||||
crate::routes::admin::DeliveredBelegnummernResponse,
|
||||
crate::routes::admin::CompletedDeliveriesResponse,
|
||||
crate::routes::admin::CompletedDeliveryItem,
|
||||
crate::routes::admin::PendingReviewResponse,
|
||||
crate::routes::admin::ReviewedItemResponse,
|
||||
crate::routes::admin::ResolveReviewRequest,
|
||||
|
||||
@ -29,6 +29,10 @@ pub fn router() -> Router<AppState> {
|
||||
"/admin/delivered-belegnummern",
|
||||
get(delivered_belegnummern),
|
||||
)
|
||||
.route(
|
||||
"/admin/completed-deliveries",
|
||||
get(completed_deliveries),
|
||||
)
|
||||
.route("/admin/mark-mail-sent", post(mark_mail_sent))
|
||||
.route("/admin/reviews", get(list_reviews))
|
||||
.route("/admin/reviews/{delivery_id}/resolve", post(resolve_review))
|
||||
@ -186,6 +190,80 @@ pub async fn delivered_belegnummern(
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CompletedDeliveriesQuery {
|
||||
/// Ziel-Tag im Format `DD-MM-YYYY`. **Pflicht** — der Endpunkt liefert nur
|
||||
/// Abschlüsse genau dieses (Berliner) Kalendertages.
|
||||
pub day: String,
|
||||
}
|
||||
|
||||
/// Eine abgeschlossene Lieferung im Tagesabruf.
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct CompletedDeliveryItem {
|
||||
/// ERP-Belegnummer der Lieferung.
|
||||
pub belegnummer: String,
|
||||
/// `true`, wenn an der Lieferung Positionen verändert wurden — eine Zeile
|
||||
/// wurde entfernt oder in der Menge reduziert (Stück-Gutschrift) **oder** es
|
||||
/// liegt eine aktive Geld-Gutschrift vor.
|
||||
pub positions_modified: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct CompletedDeliveriesResponse {
|
||||
/// Tag, nach dem gefiltert wurde (ISO `YYYY-MM-DD`).
|
||||
pub day: String,
|
||||
/// Anzahl der abgeschlossenen Lieferungen an diesem Tag.
|
||||
pub count: usize,
|
||||
/// Die abgeschlossenen Lieferungen, aufsteigend nach Abschluss-Zeitpunkt.
|
||||
pub deliveries: Vec<CompletedDeliveryItem>,
|
||||
}
|
||||
|
||||
/// Liefert **alle** an einem Tag abgeschlossenen (ausgelieferten) Lieferungen —
|
||||
/// unabhängig vom Mail-Versand-Status. Der Tag (`day`, `DD-MM-YYYY`) ist Pflicht
|
||||
/// und wird als Berliner Kalendertag auf den Abschluss-Zeitpunkt
|
||||
/// (`completed_at`) angewandt. Pro Lieferung: Belegnummer und ein Flag, ob
|
||||
/// Positionen verändert wurden (Menge reduziert/Zeile entfernt oder
|
||||
/// Geld-Gutschrift).
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/admin/completed-deliveries",
|
||||
tag = "admin",
|
||||
params(
|
||||
("day" = String, Query, description = "Tag DD-MM-YYYY (Pflicht)")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Abgeschlossene Lieferungen des Tages", body = CompletedDeliveriesResponse),
|
||||
(status = 400, description = "Ungültiger/fehlender Tag"),
|
||||
(status = 401, description = "Admin-API-Key fehlt/ungültig")
|
||||
),
|
||||
security(("admin_api_key" = []))
|
||||
)]
|
||||
pub async fn completed_deliveries(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<CompletedDeliveriesQuery>,
|
||||
) -> Result<Json<CompletedDeliveriesResponse>, ApiError> {
|
||||
let day = NaiveDate::parse_from_str(query.day.trim(), "%d-%m-%Y").map_err(|e| {
|
||||
ApiError(ApplicationError::Validation(format!(
|
||||
"ungültiger Tag '{}' (erwartet DD-MM-YYYY): {e}",
|
||||
query.day
|
||||
)))
|
||||
})?;
|
||||
tracing::info!(?day, "admin.completed_deliveries");
|
||||
let summaries = state.list_completed_deliveries.execute(day).await?;
|
||||
tracing::info!(?day, count = summaries.len(), "admin.completed_deliveries.done");
|
||||
Ok(Json(CompletedDeliveriesResponse {
|
||||
day: day.format("%Y-%m-%d").to_string(),
|
||||
count: summaries.len(),
|
||||
deliveries: summaries
|
||||
.into_iter()
|
||||
.map(|s| CompletedDeliveryItem {
|
||||
belegnummer: s.belegnummer,
|
||||
positions_modified: s.positions_modified,
|
||||
})
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct MarkMailSentRequest {
|
||||
/// Belegnummern, deren Liefermail erfolgreich versendet wurde und die als
|
||||
|
||||
@ -9,7 +9,8 @@ use holzleitner_application::usecases::{
|
||||
DeleteServiceUseCase, DevResetDeliveryUseCase, DevResyncToursUseCase,
|
||||
GenerateDeliveryReportUseCase, GetAccountUseCase,
|
||||
GetAttachmentPreviewUseCase, GetTourUseCase,
|
||||
ImportErpToursUseCase, ListDeliveredBelegnummernUseCase, ListMyCarsUseCase,
|
||||
ImportErpToursUseCase, ListCompletedDeliveriesUseCase, ListDeliveredBelegnummernUseCase,
|
||||
ListMyCarsUseCase,
|
||||
ListMyToursTodayUseCase, ListPaymentMethodsUseCase,
|
||||
ListPendingReviewsUseCase, ListServicesUseCase, MarkMailSentUseCase,
|
||||
ProcessDeliveryReportUseCase, PushCompletionToErpUseCase, ResolveReviewUseCase,
|
||||
@ -51,6 +52,8 @@ pub struct AppState {
|
||||
pub push_completion_to_erp: Arc<PushCompletionToErpUseCase>,
|
||||
/// Admin: Belegnummern offener (noch nicht versendeter) Lieferungen.
|
||||
pub list_delivered_belegnummern: Arc<ListDeliveredBelegnummernUseCase>,
|
||||
/// Admin: alle an einem Tag abgeschlossenen Lieferungen (mit Änderungs-Flag).
|
||||
pub list_completed_deliveries: Arc<ListCompletedDeliveriesUseCase>,
|
||||
/// Admin: Liefermails von Belegnummern als versendet markieren (Dedup).
|
||||
pub mark_mail_sent: Arc<MarkMailSentUseCase>,
|
||||
pub list_pending_reviews: Arc<ListPendingReviewsUseCase>,
|
||||
|
||||
Reference in New Issue
Block a user