use chrono::{DateTime, NaiveDate, Utc};
use rust_decimal::Decimal;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineLocator {
pub source_type: String,
pub source_id: Uuid,
pub account_id: Uuid,
pub reversing: bool,
}
impl LineLocator {
pub fn new(source_type: &str, source_id: Uuid, account_id: Uuid) -> Self {
Self {
source_type: source_type.to_string(),
source_id,
account_id,
reversing: false,
}
}
pub fn reversing(mut self) -> Self {
self.reversing = true;
self
}
}
pub const ORIGIN_SETTLEMENT: &str = "settlement";
pub const ORIGIN_CLEARING: &str = "clearing";
pub const ORIGIN_MANUAL: &str = "manual";
#[derive(Debug, Clone)]
pub struct ReconcileLineSnapshot {
pub id: Uuid,
pub journal_id: Uuid,
pub company_id: Uuid,
pub account_id: Uuid,
pub account_subtype: String,
pub party_type: Option<String>,
pub party_id: Option<Uuid>,
pub debit_amount: Decimal,
pub credit_amount: Decimal,
pub currency: String,
pub exchange_rate: Decimal,
pub transaction_date: NaiveDate,
pub is_posted: bool,
pub journal_status: String,
pub journal_is_reversing: bool,
pub source_type: Option<String>,
pub source_id: Option<Uuid>,
pub is_reconciled: bool,
pub full_reconcile_id: Option<Uuid>,
pub base_amount: Decimal,
}
#[derive(Debug, Clone)]
pub struct NewPartial {
pub company_id: Uuid,
pub debit_move_id: Uuid,
pub credit_move_id: Uuid,
pub amount: Decimal,
pub currency: String,
pub max_date: NaiveDate,
pub origin: String,
pub source_type: Option<String>,
pub source_id: Option<Uuid>,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone)]
pub struct PartialRow {
pub id: Uuid,
pub company_id: Uuid,
pub debit_move_id: Uuid,
pub credit_move_id: Uuid,
pub amount: Decimal,
pub max_date: NaiveDate,
pub origin: String,
pub full_reconcile_id: Option<Uuid>,
pub exchange_move_id: Option<Uuid>,
pub source_type: Option<String>,
pub source_id: Option<Uuid>,
}
#[derive(Debug, Clone)]
pub struct AccountReconcileFlags {
pub is_reconcilable: bool,
pub subtype: String,
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum LocatorResolution {
One(ReconcileLineSnapshot),
NotFound,
Ambiguous(usize),
}
#[derive(Debug, Clone)]
pub struct MatchingGroup {
pub label: String,
pub full_reconcile_id: Option<Uuid>,
pub line_ids: Vec<Uuid>,
pub partial_ids: Vec<Uuid>,
pub residuals: Vec<(Uuid, Decimal)>,
}
impl MatchingGroup {
pub fn residual_of(&self, line_id: Uuid) -> Option<Decimal> {
self.residuals
.iter()
.find(|(id, _)| *id == line_id)
.map(|(_, r)| *r)
}
}
#[derive(Debug, Clone)]
pub struct PartyResidual {
pub line_id: Uuid,
pub journal_id: Uuid,
pub journal_number: String,
pub transaction_date: NaiveDate,
pub source_reference: Option<String>,
pub residual: Decimal,
pub currency: String,
pub is_reconciled: bool,
}
#[derive(Debug)]
pub enum ReconcileError {
LineNotFound,
AmbiguousLocator(usize),
SameCompanyRequired,
SameAccountRequired,
CurrencyMismatch(String, String),
AccountNotReconcilable,
DirectionMismatch,
LineNotPosted,
PartyMismatch,
ExchangeAccountUnconfigured,
PeriodClosed,
Conflict(String),
Internal(String),
}
impl ReconcileError {
pub fn code(&self) -> &'static str {
match self {
ReconcileError::LineNotFound => "line_not_found",
ReconcileError::AmbiguousLocator(_) => "ambiguous_locator",
ReconcileError::SameCompanyRequired => "same_company_required",
ReconcileError::SameAccountRequired => "same_account_required",
ReconcileError::CurrencyMismatch(..) => "currency_mismatch",
ReconcileError::AccountNotReconcilable => "account_not_reconcilable",
ReconcileError::DirectionMismatch => "direction_mismatch",
ReconcileError::LineNotPosted => "line_not_posted",
ReconcileError::PartyMismatch => "party_mismatch",
ReconcileError::ExchangeAccountUnconfigured => "exchange_account_unconfigured",
ReconcileError::PeriodClosed => "period_closed",
ReconcileError::Conflict(_) => "conflict",
ReconcileError::Internal(_) => "internal_error",
}
}
pub fn http_status(&self) -> u16 {
match self {
ReconcileError::LineNotFound => 404,
ReconcileError::AmbiguousLocator(_) | ReconcileError::Conflict(_) => 409,
ReconcileError::Internal(_) => 500,
_ => 422,
}
}
}
impl std::fmt::Display for ReconcileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.code())
}
}
impl std::error::Error for ReconcileError {}
#[derive(Debug, Clone)]
pub struct EdgeOutcome {
pub partial_id: Option<Uuid>,
pub applied: Decimal,
pub full_reconcile_id: Option<Uuid>,
}
#[derive(Debug, Clone)]
pub struct PairRequest {
pub debit: LineLocator,
pub credit: LineLocator,
pub amount: Decimal,
pub origin: String,
pub actor: Option<Uuid>,
}
pub type ReconciledAt = DateTime<Utc>;