use chrono::{DateTime, Utc, NaiveDate};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
use rust_decimal::Decimal;
use super::ReconcileOrigin;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PartialReconcileId(pub Uuid);
impl PartialReconcileId {
pub fn new(id: Uuid) -> Self { Self(id) }
pub fn generate() -> Self { Self(Uuid::new_v4()) }
pub fn into_inner(self) -> Uuid { self.0 }
}
impl std::fmt::Display for PartialReconcileId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for PartialReconcileId {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(Uuid::parse_str(s)?))
}
}
impl From<Uuid> for PartialReconcileId {
fn from(id: Uuid) -> Self { Self(id) }
}
impl From<PartialReconcileId> for Uuid {
fn from(id: PartialReconcileId) -> Self { id.0 }
}
impl AsRef<Uuid> for PartialReconcileId {
fn as_ref(&self) -> &Uuid { &self.0 }
}
impl std::ops::Deref for PartialReconcileId {
type Target = Uuid;
fn deref(&self) -> &Self::Target { &self.0 }
}
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct PartialReconcile {
pub id: Uuid,
pub debit_move_id: Uuid,
pub credit_move_id: Uuid,
pub full_reconcile_id: Option<Uuid>,
pub exchange_move_id: Option<Uuid>,
pub amount: Decimal,
pub debit_amount_currency: Option<Decimal>,
pub credit_amount_currency: Option<Decimal>,
pub currency: String,
pub max_date: NaiveDate,
pub origin: ReconcileOrigin,
pub source_type: Option<String>,
pub source_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub metadata: Option<serde_json::Value>,
}
impl PartialReconcile {
pub fn builder() -> PartialReconcileBuilder {
<PartialReconcileBuilder as Default>::default()
}
pub fn new(debit_move_id: Uuid, credit_move_id: Uuid, amount: Decimal, currency: String, max_date: NaiveDate, origin: ReconcileOrigin) -> Self {
Self {
id: Uuid::new_v4(),
debit_move_id,
credit_move_id,
full_reconcile_id: None,
exchange_move_id: None,
amount,
debit_amount_currency: None,
credit_amount_currency: None,
currency,
max_date,
origin,
source_type: None,
source_id: None,
created_at: Utc::now(),
updated_at: Utc::now(),
metadata: None,
}
}
pub fn id(&self) -> &Uuid {
&self.id
}
pub fn typed_id(&self) -> PartialReconcileId {
PartialReconcileId(self.id)
}
pub fn created_at(&self) -> &DateTime<Utc> {
&self.created_at
}
pub fn updated_at(&self) -> &DateTime<Utc> {
&self.updated_at
}
pub fn with_full_reconcile_id(mut self, value: Uuid) -> Self {
self.full_reconcile_id = Some(value);
self
}
pub fn with_exchange_move_id(mut self, value: Uuid) -> Self {
self.exchange_move_id = Some(value);
self
}
pub fn with_debit_amount_currency(mut self, value: Decimal) -> Self {
self.debit_amount_currency = Some(value);
self
}
pub fn with_credit_amount_currency(mut self, value: Decimal) -> Self {
self.credit_amount_currency = Some(value);
self
}
pub fn with_source_type(mut self, value: String) -> Self {
self.source_type = Some(value);
self
}
pub fn with_source_id(mut self, value: Uuid) -> Self {
self.source_id = Some(value);
self
}
pub fn with_metadata(mut self, value: serde_json::Value) -> Self {
self.metadata = Some(value);
self
}
pub fn apply_patch(&mut self, fields: std::collections::HashMap<String, serde_json::Value>) {
for (key, value) in fields {
match key.as_str() {
"debit_move_id" => {
if let Ok(v) = serde_json::from_value(value) { self.debit_move_id = v; }
}
"credit_move_id" => {
if let Ok(v) = serde_json::from_value(value) { self.credit_move_id = v; }
}
"full_reconcile_id" => {
if let Ok(v) = serde_json::from_value(value) { self.full_reconcile_id = v; }
}
"exchange_move_id" => {
if let Ok(v) = serde_json::from_value(value) { self.exchange_move_id = v; }
}
"amount" => {
if let Ok(v) = serde_json::from_value(value) { self.amount = v; }
}
"debit_amount_currency" => {
if let Ok(v) = serde_json::from_value(value) { self.debit_amount_currency = v; }
}
"credit_amount_currency" => {
if let Ok(v) = serde_json::from_value(value) { self.credit_amount_currency = v; }
}
"currency" => {
if let Ok(v) = serde_json::from_value(value) { self.currency = v; }
}
"max_date" => {
if let Ok(v) = serde_json::from_value(value) { self.max_date = v; }
}
"origin" => {
if let Ok(v) = serde_json::from_value(value) { self.origin = v; }
}
"source_type" => {
if let Ok(v) = serde_json::from_value(value) { self.source_type = v; }
}
"source_id" => {
if let Ok(v) = serde_json::from_value(value) { self.source_id = v; }
}
"metadata" => {
if let Ok(v) = serde_json::from_value(value) { self.metadata = v; }
}
_ => {} }
}
}
}
impl super::Entity for PartialReconcile {
type Id = Uuid;
fn entity_id(&self) -> &Self::Id {
&self.id
}
fn entity_type() -> &'static str {
"PartialReconcile"
}
}
impl backbone_core::PersistentEntity for PartialReconcile {
fn write_protected_fields() -> &'static [&'static str] {
&["created_at"]
}
fn entity_id(&self) -> String {
self.id.to_string()
}
fn set_entity_id(&mut self, id: String) {
if let Ok(uuid) = uuid::Uuid::parse_str(&id) {
self.id = uuid;
}
}
fn created_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
Some(self.created_at)
}
fn set_created_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
self.created_at = ts;
}
fn updated_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
Some(self.updated_at)
}
fn set_updated_at(&mut self, ts: chrono::DateTime<chrono::Utc>) {
self.updated_at = ts;
}
fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
None
}
fn set_deleted_at(&mut self, ts: Option<chrono::DateTime<chrono::Utc>>) {
let _ = ts;
}
}
impl backbone_orm::EntityRepoMeta for PartialReconcile {
fn column_types() -> std::collections::HashMap<String, String> {
let mut m = std::collections::HashMap::new();
m.insert("id".to_string(), "uuid".to_string());
m.insert("debit_move_id".to_string(), "uuid".to_string());
m.insert("credit_move_id".to_string(), "uuid".to_string());
m.insert("full_reconcile_id".to_string(), "uuid".to_string());
m.insert("exchange_move_id".to_string(), "uuid".to_string());
m.insert("source_id".to_string(), "uuid".to_string());
m.insert("origin".to_string(), "reconcile_origin".to_string());
m.insert("max_date".to_string(), "date".to_string());
m.insert("created_at".to_string(), "timestamptz".to_string());
m.insert("updated_at".to_string(), "timestamptz".to_string());
m
}
fn search_fields() -> &'static [&'static str] {
&["currency"]
}
fn relations() -> &'static [(&'static str, &'static str, &'static str)] {
&[("debitMove", "journal_lines", "debitMoveId"), ("creditMove", "journal_lines", "creditMoveId"), ("fullReconcile", "full_reconciles", "fullReconcileId")]
}
}
#[derive(Debug, Clone, Default)]
pub struct PartialReconcileBuilder {
debit_move_id: Option<Uuid>,
credit_move_id: Option<Uuid>,
full_reconcile_id: Option<Uuid>,
exchange_move_id: Option<Uuid>,
amount: Option<Decimal>,
debit_amount_currency: Option<Decimal>,
credit_amount_currency: Option<Decimal>,
currency: Option<String>,
max_date: Option<NaiveDate>,
origin: Option<ReconcileOrigin>,
source_type: Option<String>,
source_id: Option<Uuid>,
metadata: Option<serde_json::Value>,
}
impl PartialReconcileBuilder {
pub fn debit_move_id(mut self, value: Uuid) -> Self {
self.debit_move_id = Some(value);
self
}
pub fn credit_move_id(mut self, value: Uuid) -> Self {
self.credit_move_id = Some(value);
self
}
pub fn full_reconcile_id(mut self, value: Uuid) -> Self {
self.full_reconcile_id = Some(value);
self
}
pub fn exchange_move_id(mut self, value: Uuid) -> Self {
self.exchange_move_id = Some(value);
self
}
pub fn amount(mut self, value: Decimal) -> Self {
self.amount = Some(value);
self
}
pub fn debit_amount_currency(mut self, value: Decimal) -> Self {
self.debit_amount_currency = Some(value);
self
}
pub fn credit_amount_currency(mut self, value: Decimal) -> Self {
self.credit_amount_currency = Some(value);
self
}
pub fn currency(mut self, value: String) -> Self {
self.currency = Some(value);
self
}
pub fn max_date(mut self, value: NaiveDate) -> Self {
self.max_date = Some(value);
self
}
pub fn origin(mut self, value: ReconcileOrigin) -> Self {
self.origin = Some(value);
self
}
pub fn source_type(mut self, value: String) -> Self {
self.source_type = Some(value);
self
}
pub fn source_id(mut self, value: Uuid) -> Self {
self.source_id = Some(value);
self
}
pub fn metadata(mut self, value: serde_json::Value) -> Self {
self.metadata = Some(value);
self
}
pub fn build(self) -> Result<PartialReconcile, String> {
let debit_move_id = self.debit_move_id.ok_or_else(|| "debit_move_id is required".to_string())?;
let credit_move_id = self.credit_move_id.ok_or_else(|| "credit_move_id is required".to_string())?;
let amount = self.amount.ok_or_else(|| "amount is required".to_string())?;
let max_date = self.max_date.ok_or_else(|| "max_date is required".to_string())?;
Ok(PartialReconcile {
id: Uuid::new_v4(),
debit_move_id,
credit_move_id,
full_reconcile_id: self.full_reconcile_id,
exchange_move_id: self.exchange_move_id,
amount,
debit_amount_currency: self.debit_amount_currency,
credit_amount_currency: self.credit_amount_currency,
currency: self.currency.unwrap_or("IDR".to_string()),
max_date,
origin: self.origin.unwrap_or_default(),
source_type: self.source_type,
source_id: self.source_id,
created_at: Utc::now(),
updated_at: Utc::now(),
metadata: self.metadata,
})
}
}