use mako_engine::types::Pruefidentifikator;
use mako_engine::{
error::WorkflowError,
ids::DeadlineId,
outbox::PendingOutbox,
types::{MaLo, MarktpartnerCode, MessageRef},
workflow::{CommandPayload, EventPayload, Workflow, WorkflowOutput},
};
pub const WORKFLOW_NAME: &str = "gpke-sperrung-lf";
pub const SPERRUNG_ANFRAGE_PIDS: &[u32] = &[
17115, 17117, ];
pub const ORDRSP_SPERRUNG_PIDS: &[u32] = &[19116, 19117];
pub const ORDRSP_STORNO_PIDS: &[u32] = &[19128, 19129];
pub const IFTSTA_SPERRUNG_PID: Pruefidentifikator = Pruefidentifikator::const_new(21039);
pub const ANTWORT_WINDOW_LABEL: &str = "gpke-sperrung-lf-antwort-24h";
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SperrungAuftragData {
pub location_id: MaLo,
pub nb_mp_id: MarktpartnerCode,
pub pruefidentifikator: Pruefidentifikator,
pub message_ref: MessageRef,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum SperrungLfEvent {
AuftragInitiiert {
location_id: MaLo,
nb_mp_id: MarktpartnerCode,
pruefidentifikator: Pruefidentifikator,
message_ref: MessageRef,
},
OrdrsepEmpfangen {
pruefidentifikator: Pruefidentifikator,
is_confirmed: bool,
message_ref: MessageRef,
sender: MarktpartnerCode,
},
IftstaAuftragsstatus {
pruefidentifikator: Pruefidentifikator,
message_ref: MessageRef,
sender: MarktpartnerCode,
},
StornierungGesendet {
message_ref: MessageRef,
},
StornoOrdrsepEmpfangen {
pruefidentifikator: Pruefidentifikator,
is_confirmed: bool,
message_ref: MessageRef,
sender: MarktpartnerCode,
},
DeadlineExpired {
deadline_id: DeadlineId,
label: Box<str>,
},
}
impl EventPayload for SperrungLfEvent {
fn event_type(&self) -> &'static str {
match self {
Self::AuftragInitiiert { .. } => "SperrungLfAuftragInitiiert",
Self::OrdrsepEmpfangen { .. } => "SperrungLfOrdrsepEmpfangen",
Self::IftstaAuftragsstatus { .. } => "SperrungLfIftstaAuftragsstatus",
Self::StornierungGesendet { .. } => "SperrungLfStornierungGesendet",
Self::StornoOrdrsepEmpfangen { .. } => "SperrungLfStornoOrdrsepEmpfangen",
Self::DeadlineExpired { .. } => "SperrungLfDeadlineExpired",
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(tag = "status", content = "data")]
#[derive(Default)]
pub enum SperrungLfState {
#[default]
New,
AuftragGesendet(SperrungAuftragData),
OrdrsepBestaetigt(SperrungAuftragData),
OrdrsepAbgelehnt {
reason: Option<String>,
},
Ausgefuehrt(SperrungAuftragData),
StornierungGesendet(SperrungAuftragData),
StornoBestaetigt(SperrungAuftragData),
StornoAbgelehnt(SperrungAuftragData),
DeadlineExpired {
label: String,
},
}
impl SperrungLfState {
#[must_use]
pub fn label(&self) -> &'static str {
match self {
Self::New => "New",
Self::AuftragGesendet(_) => "AuftragGesendet",
Self::OrdrsepBestaetigt(_) => "OrdrsepBestaetigt",
Self::OrdrsepAbgelehnt { .. } => "OrdrsepAbgelehnt",
Self::Ausgefuehrt(_) => "Ausgefuehrt",
Self::StornierungGesendet(_) => "StornierungGesendet",
Self::StornoBestaetigt(_) => "StornoBestaetigt",
Self::StornoAbgelehnt(_) => "StornoAbgelehnt",
Self::DeadlineExpired { .. } => "DeadlineExpired",
}
}
#[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
self,
Self::OrdrsepAbgelehnt { .. }
| Self::Ausgefuehrt(_)
| Self::StornoBestaetigt(_)
| Self::StornoAbgelehnt(_)
| Self::DeadlineExpired { .. }
)
}
#[must_use]
pub fn auftrag_data(&self) -> Option<&SperrungAuftragData> {
match self {
Self::AuftragGesendet(d)
| Self::OrdrsepBestaetigt(d)
| Self::Ausgefuehrt(d)
| Self::StornierungGesendet(d)
| Self::StornoBestaetigt(d)
| Self::StornoAbgelehnt(d) => Some(d),
Self::New | Self::OrdrsepAbgelehnt { .. } | Self::DeadlineExpired { .. } => None,
}
}
}
#[derive(Clone)]
pub enum SperrungLfCommand {
InitiateSperrung {
pid: Pruefidentifikator,
nb_mp_id: MarktpartnerCode,
location_id: MaLo,
message_ref: MessageRef,
},
ReceiveOrdrsp {
pid: Pruefidentifikator,
is_confirmed: bool,
message_ref: MessageRef,
sender: MarktpartnerCode,
reason: Option<String>,
},
ReceiveIftsta {
pid: Pruefidentifikator,
message_ref: MessageRef,
sender: MarktpartnerCode,
},
SendStornierung {
message_ref: MessageRef,
},
ReceiveStornoOrdrsp {
pid: Pruefidentifikator,
is_confirmed: bool,
message_ref: MessageRef,
sender: MarktpartnerCode,
},
TimeoutExpired {
deadline_id: DeadlineId,
label: Box<str>,
},
}
impl CommandPayload for SperrungLfCommand {}
pub struct GpkeSperrungLfWorkflow;
impl Workflow for GpkeSperrungLfWorkflow {
type State = SperrungLfState;
type Event = SperrungLfEvent;
type Command = SperrungLfCommand;
fn on_deadline(
deadline: &mako_engine::deadline::Deadline,
state: &Self::State,
) -> Option<Self::Command> {
if deadline.label() == ANTWORT_WINDOW_LABEL && !state.is_terminal() {
return Some(SperrungLfCommand::TimeoutExpired {
deadline_id: deadline.deadline_id(),
label: deadline.label().into(),
});
}
None
}
fn apply(state: Self::State, event: &Self::Event) -> Self::State {
match event {
SperrungLfEvent::AuftragInitiiert {
location_id,
nb_mp_id,
pruefidentifikator,
message_ref,
} => SperrungLfState::AuftragGesendet(SperrungAuftragData {
location_id: location_id.clone(),
nb_mp_id: nb_mp_id.clone(),
pruefidentifikator: *pruefidentifikator,
message_ref: message_ref.clone(),
}),
SperrungLfEvent::OrdrsepEmpfangen {
is_confirmed,
pruefidentifikator: _,
message_ref: _,
sender: _,
} => match state {
SperrungLfState::AuftragGesendet(data) => {
if *is_confirmed {
SperrungLfState::OrdrsepBestaetigt(data)
} else {
SperrungLfState::OrdrsepAbgelehnt { reason: None }
}
}
other => other,
},
SperrungLfEvent::IftstaAuftragsstatus { .. } => match state {
SperrungLfState::OrdrsepBestaetigt(data)
| SperrungLfState::AuftragGesendet(data) => SperrungLfState::Ausgefuehrt(data),
other => other,
},
SperrungLfEvent::StornierungGesendet { .. } => match state {
SperrungLfState::AuftragGesendet(data)
| SperrungLfState::OrdrsepBestaetigt(data) => {
SperrungLfState::StornierungGesendet(data)
}
other => other,
},
SperrungLfEvent::StornoOrdrsepEmpfangen {
is_confirmed,
pruefidentifikator: _,
message_ref: _,
sender: _,
} => match state {
SperrungLfState::StornierungGesendet(data) => {
if *is_confirmed {
SperrungLfState::StornoBestaetigt(data)
} else {
SperrungLfState::StornoAbgelehnt(data)
}
}
other => other,
},
SperrungLfEvent::DeadlineExpired { label, .. } => {
if state.is_terminal() {
state
} else {
SperrungLfState::DeadlineExpired {
label: label.to_string(),
}
}
}
}
}
fn handle(
state: &Self::State,
command: Self::Command,
) -> Result<WorkflowOutput<Self::Event>, WorkflowError> {
match command {
SperrungLfCommand::InitiateSperrung {
pid,
nb_mp_id,
location_id,
message_ref,
} => {
if !matches!(state, SperrungLfState::New) {
return Err(WorkflowError::invalid_state("New", state.label()));
}
if !SPERRUNG_ANFRAGE_PIDS.contains(&pid.as_u32()) {
return Err(WorkflowError::rejected(format!(
"expected a Sperrung PID (17115 or 17117), got {pid}",
)));
}
let outbox = PendingOutbox::new(
"ORDERS",
nb_mp_id.as_str(),
serde_json::json!({
"type": "SperrungAuftrag",
"pid": pid.as_u32(),
"location_id": location_id.as_str(),
"message_ref": message_ref.as_str(),
}),
);
let event = SperrungLfEvent::AuftragInitiiert {
location_id,
nb_mp_id,
pruefidentifikator: pid,
message_ref,
};
Ok(WorkflowOutput::with_outbox(vec![event], vec![outbox]))
}
SperrungLfCommand::ReceiveOrdrsp {
pid,
is_confirmed,
message_ref,
sender,
reason: _,
} => {
if !ORDRSP_SPERRUNG_PIDS.contains(&pid.as_u32()) {
return Err(WorkflowError::rejected(format!(
"expected a Sperrung-ORDRSP PID (19116 or 19117), got {pid}",
)));
}
if state.is_terminal() {
return Ok(WorkflowOutput::events(vec![]));
}
Ok(vec![SperrungLfEvent::OrdrsepEmpfangen {
pruefidentifikator: pid,
is_confirmed,
message_ref,
sender,
}]
.into())
}
SperrungLfCommand::ReceiveIftsta {
pid,
message_ref,
sender,
} => {
if pid != IFTSTA_SPERRUNG_PID {
return Err(WorkflowError::rejected(format!(
"expected IFTSTA PID 21039, got {pid}",
)));
}
if state.is_terminal() {
return Ok(WorkflowOutput::events(vec![]));
}
Ok(vec![SperrungLfEvent::IftstaAuftragsstatus {
pruefidentifikator: pid,
message_ref,
sender,
}]
.into())
}
SperrungLfCommand::SendStornierung { message_ref } => {
if !matches!(
state,
SperrungLfState::AuftragGesendet(_) | SperrungLfState::OrdrsepBestaetigt(_)
) {
return Err(WorkflowError::invalid_state(
"AuftragGesendet or OrdrsepBestaetigt",
state.label(),
));
}
let nb_mp_id = state
.auftrag_data()
.map(|d| d.nb_mp_id.as_str().to_owned())
.unwrap_or_default();
let outbox = PendingOutbox::new(
"ORDCHG",
nb_mp_id.as_str(),
serde_json::json!({
"type": "SperrungStornierung",
"pid": 39000_u32,
"message_ref": message_ref.as_str(),
}),
);
Ok(WorkflowOutput::with_outbox(
vec![SperrungLfEvent::StornierungGesendet { message_ref }],
vec![outbox],
))
}
SperrungLfCommand::ReceiveStornoOrdrsp {
pid,
is_confirmed,
message_ref,
sender,
} => {
if !ORDRSP_STORNO_PIDS.contains(&pid.as_u32()) {
return Err(WorkflowError::rejected(format!(
"expected a Storno-ORDRSP PID (19128 or 19129), got {pid}",
)));
}
if state.is_terminal() {
return Ok(WorkflowOutput::events(vec![]));
}
Ok(vec![SperrungLfEvent::StornoOrdrsepEmpfangen {
pruefidentifikator: pid,
is_confirmed,
message_ref,
sender,
}]
.into())
}
SperrungLfCommand::TimeoutExpired { deadline_id, label } => {
if state.is_terminal() {
return Ok(WorkflowOutput::events(vec![]));
}
Ok(vec![SperrungLfEvent::DeadlineExpired { deadline_id, label }].into())
}
}
}
}