use rust_decimal::Decimal;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RemittanceInstruction {
pub company_id: Uuid,
pub run_id: Uuid,
pub gl_account_id: Uuid,
pub amount: Decimal,
pub statutory: bool,
pub idempotency_key: String,
}
impl RemittanceInstruction {
pub fn new(
company_id: Uuid,
run_id: Uuid,
gl_account_id: Uuid,
amount: Decimal,
statutory: bool,
) -> Self {
Self {
company_id,
run_id,
gl_account_id,
amount,
statutory,
idempotency_key: format!("payroll_remittance:{company_id}:{run_id}:{gl_account_id}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RemitAck {
pub payment_id: Option<Uuid>,
pub duplicate: bool,
}
#[derive(Debug, thiserror::Error)]
pub enum RemittanceSeamError {
#[error("the remittance seam is not wired — supply a RemittanceSink to remit posted payables")]
Unwired,
#[error("the remittance sink rejected the instruction ({code}): {message}")]
Rejected { code: String, message: String },
#[error("remittance seam transport error: {0}")]
Transport(String),
}
impl RemittanceSeamError {
pub fn code(&self) -> &'static str {
match self {
Self::Unwired => "remittance_seam_unwired",
Self::Rejected { .. } => "remittance_rejected",
Self::Transport(_) => "remittance_seam_error",
}
}
}
#[async_trait::async_trait]
pub trait RemittanceSink: Send + Sync {
async fn remit(&self, instruction: &RemittanceInstruction) -> Result<RemitAck, RemittanceSeamError>;
}
#[derive(Debug, Default, Clone, Copy)]
pub struct UnwiredRemittance;
#[async_trait::async_trait]
impl RemittanceSink for UnwiredRemittance {
async fn remit(&self, _instruction: &RemittanceInstruction) -> Result<RemitAck, RemittanceSeamError> {
Err(RemittanceSeamError::Unwired)
}
}