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,
|
DeleteServiceUseCase, DevResetDeliveryUseCase, DevResyncToursUseCase,
|
||||||
GenerateDeliveryReportUseCase, GetAccountUseCase,
|
GenerateDeliveryReportUseCase, GetAccountUseCase,
|
||||||
GetAttachmentPreviewUseCase, GetTourUseCase,
|
GetAttachmentPreviewUseCase, GetTourUseCase,
|
||||||
ImportErpToursUseCase, ListDeliveredBelegnummernUseCase, ListMyCarsUseCase,
|
ImportErpToursUseCase, ListCompletedDeliveriesUseCase, ListDeliveredBelegnummernUseCase,
|
||||||
|
ListMyCarsUseCase,
|
||||||
ListMyToursTodayUseCase, ListPaymentMethodsUseCase,
|
ListMyToursTodayUseCase, ListPaymentMethodsUseCase,
|
||||||
ListPendingReviewsUseCase, ListServicesUseCase, MarkMailSentUseCase,
|
ListPendingReviewsUseCase, ListServicesUseCase, MarkMailSentUseCase,
|
||||||
PushCompletionToErpUseCase, ResolveReviewUseCase,
|
PushCompletionToErpUseCase, ResolveReviewUseCase,
|
||||||
@ -365,6 +366,11 @@ pub(crate) async fn run_app(
|
|||||||
let list_delivered_belegnummern = Arc::new(ListDeliveredBelegnummernUseCase::new(
|
let list_delivered_belegnummern = Arc::new(ListDeliveredBelegnummernUseCase::new(
|
||||||
delivery_completion_repository.clone(),
|
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(
|
let mark_mail_sent = Arc::new(MarkMailSentUseCase::new(
|
||||||
delivery_completion_repository.clone(),
|
delivery_completion_repository.clone(),
|
||||||
));
|
));
|
||||||
@ -460,6 +466,7 @@ pub(crate) async fn run_app(
|
|||||||
complete_delivery,
|
complete_delivery,
|
||||||
push_completion_to_erp,
|
push_completion_to_erp,
|
||||||
list_delivered_belegnummern,
|
list_delivered_belegnummern,
|
||||||
|
list_completed_deliveries,
|
||||||
mark_mail_sent,
|
mark_mail_sent,
|
||||||
list_pending_reviews,
|
list_pending_reviews,
|
||||||
resolve_review,
|
resolve_review,
|
||||||
|
|||||||
@ -54,6 +54,7 @@ use utoipa::openapi::security::{
|
|||||||
crate::routes::admin::import_erp,
|
crate::routes::admin::import_erp,
|
||||||
crate::routes::admin::push_completion,
|
crate::routes::admin::push_completion,
|
||||||
crate::routes::admin::delivered_belegnummern,
|
crate::routes::admin::delivered_belegnummern,
|
||||||
|
crate::routes::admin::completed_deliveries,
|
||||||
crate::routes::admin::mark_mail_sent,
|
crate::routes::admin::mark_mail_sent,
|
||||||
crate::routes::admin::list_reviews,
|
crate::routes::admin::list_reviews,
|
||||||
crate::routes::admin::resolve_review,
|
crate::routes::admin::resolve_review,
|
||||||
@ -127,6 +128,8 @@ use utoipa::openapi::security::{
|
|||||||
holzleitner_application::dto::DeliveryServiceResponse,
|
holzleitner_application::dto::DeliveryServiceResponse,
|
||||||
holzleitner_application::usecases::ImportSummary,
|
holzleitner_application::usecases::ImportSummary,
|
||||||
crate::routes::admin::DeliveredBelegnummernResponse,
|
crate::routes::admin::DeliveredBelegnummernResponse,
|
||||||
|
crate::routes::admin::CompletedDeliveriesResponse,
|
||||||
|
crate::routes::admin::CompletedDeliveryItem,
|
||||||
crate::routes::admin::PendingReviewResponse,
|
crate::routes::admin::PendingReviewResponse,
|
||||||
crate::routes::admin::ReviewedItemResponse,
|
crate::routes::admin::ReviewedItemResponse,
|
||||||
crate::routes::admin::ResolveReviewRequest,
|
crate::routes::admin::ResolveReviewRequest,
|
||||||
|
|||||||
@ -29,6 +29,10 @@ pub fn router() -> Router<AppState> {
|
|||||||
"/admin/delivered-belegnummern",
|
"/admin/delivered-belegnummern",
|
||||||
get(delivered_belegnummern),
|
get(delivered_belegnummern),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/admin/completed-deliveries",
|
||||||
|
get(completed_deliveries),
|
||||||
|
)
|
||||||
.route("/admin/mark-mail-sent", post(mark_mail_sent))
|
.route("/admin/mark-mail-sent", post(mark_mail_sent))
|
||||||
.route("/admin/reviews", get(list_reviews))
|
.route("/admin/reviews", get(list_reviews))
|
||||||
.route("/admin/reviews/{delivery_id}/resolve", post(resolve_review))
|
.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)]
|
#[derive(Debug, Deserialize, ToSchema)]
|
||||||
pub struct MarkMailSentRequest {
|
pub struct MarkMailSentRequest {
|
||||||
/// Belegnummern, deren Liefermail erfolgreich versendet wurde und die als
|
/// Belegnummern, deren Liefermail erfolgreich versendet wurde und die als
|
||||||
|
|||||||
@ -9,7 +9,8 @@ use holzleitner_application::usecases::{
|
|||||||
DeleteServiceUseCase, DevResetDeliveryUseCase, DevResyncToursUseCase,
|
DeleteServiceUseCase, DevResetDeliveryUseCase, DevResyncToursUseCase,
|
||||||
GenerateDeliveryReportUseCase, GetAccountUseCase,
|
GenerateDeliveryReportUseCase, GetAccountUseCase,
|
||||||
GetAttachmentPreviewUseCase, GetTourUseCase,
|
GetAttachmentPreviewUseCase, GetTourUseCase,
|
||||||
ImportErpToursUseCase, ListDeliveredBelegnummernUseCase, ListMyCarsUseCase,
|
ImportErpToursUseCase, ListCompletedDeliveriesUseCase, ListDeliveredBelegnummernUseCase,
|
||||||
|
ListMyCarsUseCase,
|
||||||
ListMyToursTodayUseCase, ListPaymentMethodsUseCase,
|
ListMyToursTodayUseCase, ListPaymentMethodsUseCase,
|
||||||
ListPendingReviewsUseCase, ListServicesUseCase, MarkMailSentUseCase,
|
ListPendingReviewsUseCase, ListServicesUseCase, MarkMailSentUseCase,
|
||||||
ProcessDeliveryReportUseCase, PushCompletionToErpUseCase, ResolveReviewUseCase,
|
ProcessDeliveryReportUseCase, PushCompletionToErpUseCase, ResolveReviewUseCase,
|
||||||
@ -51,6 +52,8 @@ pub struct AppState {
|
|||||||
pub push_completion_to_erp: Arc<PushCompletionToErpUseCase>,
|
pub push_completion_to_erp: Arc<PushCompletionToErpUseCase>,
|
||||||
/// Admin: Belegnummern offener (noch nicht versendeter) Lieferungen.
|
/// Admin: Belegnummern offener (noch nicht versendeter) Lieferungen.
|
||||||
pub list_delivered_belegnummern: Arc<ListDeliveredBelegnummernUseCase>,
|
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).
|
/// Admin: Liefermails von Belegnummern als versendet markieren (Dedup).
|
||||||
pub mark_mail_sent: Arc<MarkMailSentUseCase>,
|
pub mark_mail_sent: Arc<MarkMailSentUseCase>,
|
||||||
pub list_pending_reviews: Arc<ListPendingReviewsUseCase>,
|
pub list_pending_reviews: Arc<ListPendingReviewsUseCase>,
|
||||||
|
|||||||
@ -43,6 +43,17 @@ pub struct ErpWritebackData {
|
|||||||
pub payment_method_code: Option<String>,
|
pub payment_method_code: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Zusammenfassung einer abgeschlossenen Lieferung für den Admin-Tagesabruf.
|
||||||
|
/// Belegnummer plus ein Flag, ob an der Lieferung Positionen verändert wurden.
|
||||||
|
pub struct CompletedDeliverySummary {
|
||||||
|
pub belegnummer: String,
|
||||||
|
/// `true`, wenn an der Lieferung fachlich etwas von der ursprünglichen
|
||||||
|
/// Bestellung abweicht: eine Position wurde ganz entfernt oder in der Menge
|
||||||
|
/// reduziert (`delivery_items.credited_quantity > 0`) **oder** es liegt eine
|
||||||
|
/// aktive Geld-Gutschrift vor (jüngstes `delivery_credit_audit`-Event `set`).
|
||||||
|
pub positions_modified: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Vollständige Eingabe für den Abschluss — alles, was die Abschluss-Zeile
|
/// Vollständige Eingabe für den Abschluss — alles, was die Abschluss-Zeile
|
||||||
/// braucht plus die fachlichen Bestätigungs-Flags fürs Gate.
|
/// braucht plus die fachlichen Bestätigungs-Flags fürs Gate.
|
||||||
pub struct CompleteDeliveryInput {
|
pub struct CompleteDeliveryInput {
|
||||||
@ -121,6 +132,18 @@ pub trait DeliveryCompletionRepository: Send + Sync {
|
|||||||
day: Option<NaiveDate>,
|
day: Option<NaiveDate>,
|
||||||
) -> Result<Vec<String>, ApplicationError>;
|
) -> Result<Vec<String>, ApplicationError>;
|
||||||
|
|
||||||
|
/// Liefert alle **abgeschlossenen** (ausgelieferten) Lieferungen, deren
|
||||||
|
/// Abschluss-Zeitpunkt (`completed_at`) auf den Berliner Kalendertag `day`
|
||||||
|
/// fällt — unabhängig vom Mail-Versand-Status. Pro Lieferung Belegnummer
|
||||||
|
/// und das `positions_modified`-Flag (siehe [`CompletedDeliverySummary`]).
|
||||||
|
///
|
||||||
|
/// `completed_at` ist ein UTC-Zeitstempel; der Kalendertag wird in
|
||||||
|
/// **Europe/Berlin** bestimmt. Sortierung: aufsteigend nach Abschluss-Zeit.
|
||||||
|
async fn list_completed_deliveries_for_day(
|
||||||
|
&self,
|
||||||
|
day: NaiveDate,
|
||||||
|
) -> Result<Vec<CompletedDeliverySummary>, ApplicationError>;
|
||||||
|
|
||||||
/// Markiert die Liefermail der angegebenen Belegnummern als **versendet**
|
/// Markiert die Liefermail der angegebenen Belegnummern als **versendet**
|
||||||
/// (`mail_sent_at = now()`), aber nur dort, wo sie noch offen ist
|
/// (`mail_sent_at = now()`), aber nur dort, wo sie noch offen ist
|
||||||
/// (`mail_sent_at IS NULL`) — bereits markierte bleiben unverändert
|
/// (`mail_sent_at IS NULL`) — bereits markierte bleiben unverändert
|
||||||
|
|||||||
@ -51,7 +51,8 @@ pub use delivery_service_repository::DeliveryServiceRepository;
|
|||||||
pub use docuframe_report_gateway::DocuframeReportGateway;
|
pub use docuframe_report_gateway::DocuframeReportGateway;
|
||||||
pub use driver_identity_provisioner::{DriverIdentityProvisioner, ProvisionOutcome};
|
pub use driver_identity_provisioner::{DriverIdentityProvisioner, ProvisionOutcome};
|
||||||
pub use delivery_completion_repository::{
|
pub use delivery_completion_repository::{
|
||||||
CompleteDeliveryInput, DeliveryCompletionRepository, ErpWritebackData, ErpWritebackLine,
|
CompleteDeliveryInput, CompletedDeliverySummary, DeliveryCompletionRepository, ErpWritebackData,
|
||||||
|
ErpWritebackLine,
|
||||||
};
|
};
|
||||||
pub use erp_delivery_source::ErpDeliverySource;
|
pub use erp_delivery_source::ErpDeliverySource;
|
||||||
pub use erp_delivery_writeback::{
|
pub use erp_delivery_writeback::{
|
||||||
|
|||||||
33
crates/application/src/usecases/list_completed_deliveries.rs
Normal file
33
crates/application/src/usecases/list_completed_deliveries.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//! Use Case: abgeschlossene (ausgelieferte) Lieferungen eines Tages auflisten.
|
||||||
|
//!
|
||||||
|
//! Reine Lese-Operation für den Admin-Endpunkt. Im Gegensatz zu
|
||||||
|
//! [`crate::usecases::ListDeliveredBelegnummernUseCase`] (Mailclient-Sicht,
|
||||||
|
//! nur noch nicht versendete Belege) liefert dieser Use Case **alle**
|
||||||
|
//! Abschlüsse des Tages — unabhängig vom Mail-Status — samt einem Flag, ob an
|
||||||
|
//! der Lieferung Positionen verändert wurden (Menge reduziert/entfernt oder
|
||||||
|
//! Geld-Gutschrift). Tages-/TZ-Logik liegt im Repository (Europe/Berlin).
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
|
use crate::error::ApplicationError;
|
||||||
|
use crate::ports::{CompletedDeliverySummary, DeliveryCompletionRepository};
|
||||||
|
|
||||||
|
pub struct ListCompletedDeliveriesUseCase {
|
||||||
|
completions: Arc<dyn DeliveryCompletionRepository>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListCompletedDeliveriesUseCase {
|
||||||
|
pub fn new(completions: Arc<dyn DeliveryCompletionRepository>) -> Self {
|
||||||
|
Self { completions }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Liefert alle am `day` (Berliner Kalendertag) abgeschlossenen Lieferungen.
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
day: NaiveDate,
|
||||||
|
) -> Result<Vec<CompletedDeliverySummary>, ApplicationError> {
|
||||||
|
self.completions.list_completed_deliveries_for_day(day).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,6 +19,7 @@ pub mod get_account;
|
|||||||
pub mod get_attachment_preview;
|
pub mod get_attachment_preview;
|
||||||
pub mod get_tour;
|
pub mod get_tour;
|
||||||
pub mod import_erp_tours;
|
pub mod import_erp_tours;
|
||||||
|
pub mod list_completed_deliveries;
|
||||||
pub mod list_delivered_belegnummern;
|
pub mod list_delivered_belegnummern;
|
||||||
pub mod list_my_tours_today;
|
pub mod list_my_tours_today;
|
||||||
pub mod mark_mail_sent;
|
pub mod mark_mail_sent;
|
||||||
@ -48,6 +49,7 @@ pub use get_account::GetAccountUseCase;
|
|||||||
pub use get_attachment_preview::GetAttachmentPreviewUseCase;
|
pub use get_attachment_preview::GetAttachmentPreviewUseCase;
|
||||||
pub use get_tour::GetTourUseCase;
|
pub use get_tour::GetTourUseCase;
|
||||||
pub use import_erp_tours::{ImportErpToursUseCase, ImportSummary};
|
pub use import_erp_tours::{ImportErpToursUseCase, ImportSummary};
|
||||||
|
pub use list_completed_deliveries::ListCompletedDeliveriesUseCase;
|
||||||
pub use list_delivered_belegnummern::ListDeliveredBelegnummernUseCase;
|
pub use list_delivered_belegnummern::ListDeliveredBelegnummernUseCase;
|
||||||
pub use list_my_tours_today::ListMyToursTodayUseCase;
|
pub use list_my_tours_today::ListMyToursTodayUseCase;
|
||||||
pub use mark_mail_sent::MarkMailSentUseCase;
|
pub use mark_mail_sent::MarkMailSentUseCase;
|
||||||
|
|||||||
@ -14,7 +14,8 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use holzleitner_application::error::ApplicationError;
|
use holzleitner_application::error::ApplicationError;
|
||||||
use holzleitner_application::ports::{
|
use holzleitner_application::ports::{
|
||||||
CompleteDeliveryInput, DeliveryCompletionRepository, ErpWritebackData, ErpWritebackLine,
|
CompleteDeliveryInput, CompletedDeliverySummary, DeliveryCompletionRepository, ErpWritebackData,
|
||||||
|
ErpWritebackLine,
|
||||||
};
|
};
|
||||||
use holzleitner_domain::{Address, Delivery, DeliveryState};
|
use holzleitner_domain::{Address, Delivery, DeliveryState};
|
||||||
|
|
||||||
@ -472,6 +473,57 @@ impl DeliveryCompletionRepository for PgDeliveryCompletionRepository {
|
|||||||
Ok(belegnummern)
|
Ok(belegnummern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_completed_deliveries_for_day(
|
||||||
|
&self,
|
||||||
|
day: NaiveDate,
|
||||||
|
) -> Result<Vec<CompletedDeliverySummary>, ApplicationError> {
|
||||||
|
// Alle an `day` (Berliner Kalendertag von completed_at) abgeschlossenen
|
||||||
|
// Lieferungen — ohne Mail-Filter. `positions_modified` ist true, wenn
|
||||||
|
// (a) irgendeine Belegzeile eine Stück-Gutschrift trägt
|
||||||
|
// (credited_quantity > 0 ⇒ Zeile ganz entfernt oder Menge reduziert),
|
||||||
|
// ODER
|
||||||
|
// (b) eine aktive Geld-Gutschrift vorliegt: das jüngste Audit-Event
|
||||||
|
// (recorded_at, id als Tie-Break — identisch zur Credit-Repo-Logik)
|
||||||
|
// ist 'set'.
|
||||||
|
let rows: Vec<(String, bool)> = sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
d.erp_belegnummer,
|
||||||
|
COALESCE(
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM delivery_items di
|
||||||
|
WHERE di.delivery_id = d.id
|
||||||
|
AND di.credited_quantity > 0
|
||||||
|
)
|
||||||
|
OR (
|
||||||
|
SELECT dca.action
|
||||||
|
FROM delivery_credit_audit dca
|
||||||
|
WHERE dca.delivery_id = d.id
|
||||||
|
ORDER BY dca.recorded_at DESC, dca.id DESC
|
||||||
|
LIMIT 1
|
||||||
|
) = 'set',
|
||||||
|
false
|
||||||
|
) AS positions_modified
|
||||||
|
FROM deliveries d
|
||||||
|
JOIN delivery_completions c ON c.delivery_id = d.id
|
||||||
|
WHERE (c.completed_at AT TIME ZONE 'Europe/Berlin')::date = $1
|
||||||
|
ORDER BY c.completed_at
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(day)
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(db)?;
|
||||||
|
|
||||||
|
Ok(rows
|
||||||
|
.into_iter()
|
||||||
|
.map(|(belegnummer, positions_modified)| CompletedDeliverySummary {
|
||||||
|
belegnummer,
|
||||||
|
positions_modified,
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
async fn mark_mail_sent(
|
async fn mark_mail_sent(
|
||||||
&self,
|
&self,
|
||||||
belegnummern: &[String],
|
belegnummern: &[String],
|
||||||
|
|||||||
Reference in New Issue
Block a user