style(src-tauri): cargo fmt
Pure mechanical rustfmt reformat (default config, no rustfmt.toml).
This commit is contained in:
@@ -53,7 +53,11 @@ pub struct PaymentInput {
|
||||
/// Today's date in India (IST, UTC+05:30), as YYYY-MM-DD. Due dates are plain calendar dates.
|
||||
pub fn today_ist() -> String {
|
||||
let ist = FixedOffset::east_opt(5 * 3600 + 1800).expect("valid offset");
|
||||
Utc::now().with_timezone(&ist).date_naive().format("%Y-%m-%d").to_string()
|
||||
Utc::now()
|
||||
.with_timezone(&ist)
|
||||
.date_naive()
|
||||
.format("%Y-%m-%d")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Derives the payment summary. Only an issued invoice takes payments; anything else is `none`
|
||||
@@ -112,7 +116,14 @@ pub fn summary_for(
|
||||
params![invoice_id],
|
||||
|r| Ok((r.get(0)?, r.get(1)?)),
|
||||
)?;
|
||||
Ok(derive_summary(status, rupees_to_paise(total), paid, tds, due_date, &today_ist()))
|
||||
Ok(derive_summary(
|
||||
status,
|
||||
rupees_to_paise(total),
|
||||
paid,
|
||||
tds,
|
||||
due_date,
|
||||
&today_ist(),
|
||||
))
|
||||
}
|
||||
|
||||
fn rupees(paise: i64) -> String {
|
||||
@@ -130,7 +141,9 @@ fn map_payment(r: &rusqlite::Row) -> rusqlite::Result<Payment> {
|
||||
reference: r.get(6)?,
|
||||
notes: r.get(7)?,
|
||||
created_at: r.get(8)?,
|
||||
erpnext_payment_entry: r.get::<_, Option<String>>(9)?.filter(|e| !e.trim().is_empty()),
|
||||
erpnext_payment_entry: r
|
||||
.get::<_, Option<String>>(9)?
|
||||
.filter(|e| !e.trim().is_empty()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -165,7 +178,9 @@ pub fn record_payment_impl(conn: &mut Connection, input: PaymentInput) -> Result
|
||||
.map_err(db)?
|
||||
.ok_or_else(|| "Invoice not found".to_string())?;
|
||||
if status != "issued" {
|
||||
return Err(format!("Payments can only be recorded on an issued invoice (this one is {status})"));
|
||||
return Err(format!(
|
||||
"Payments can only be recorded on an issued invoice (this one is {status})"
|
||||
));
|
||||
}
|
||||
let total_paise = rupees_to_paise(total);
|
||||
let settled: i64 = tx
|
||||
@@ -203,7 +218,11 @@ pub fn record_payment_impl(conn: &mut Connection, input: PaymentInput) -> Result
|
||||
.map_err(db)?;
|
||||
let id = tx.last_insert_rowid();
|
||||
let payment = tx
|
||||
.query_row(&format!("SELECT {COLS} FROM payments WHERE id = ?1"), params![id], map_payment)
|
||||
.query_row(
|
||||
&format!("SELECT {COLS} FROM payments WHERE id = ?1"),
|
||||
params![id],
|
||||
map_payment,
|
||||
)
|
||||
.map_err(db)?;
|
||||
tx.commit().map_err(db)?;
|
||||
Ok(payment)
|
||||
@@ -270,7 +289,9 @@ mod tests {
|
||||
"items": [{"description": "Design", "mode": "fixed", "amount": amount}],
|
||||
}))
|
||||
.unwrap();
|
||||
issue_invoice_impl(conn, Path::new("/nonexistent"), input, &json!({})).unwrap().id
|
||||
issue_invoice_impl(conn, Path::new("/nonexistent"), input, &json!({}))
|
||||
.unwrap()
|
||||
.id
|
||||
}
|
||||
|
||||
fn pay(invoice_id: i64, amount: i64, tds: i64) -> PaymentInput {
|
||||
@@ -287,9 +308,11 @@ mod tests {
|
||||
|
||||
fn summary(conn: &Connection, id: i64) -> PaymentSummary {
|
||||
let (status, total, due): (String, f64, String) = conn
|
||||
.query_row("SELECT status, total, due_date FROM invoices WHERE id = ?1", params![id], |r| {
|
||||
Ok((r.get(0)?, r.get(1)?, r.get(2)?))
|
||||
})
|
||||
.query_row(
|
||||
"SELECT status, total, due_date FROM invoices WHERE id = ?1",
|
||||
params![id],
|
||||
|r| Ok((r.get(0)?, r.get(1)?, r.get(2)?)),
|
||||
)
|
||||
.unwrap();
|
||||
summary_for(conn, id, &status, total, &due).unwrap()
|
||||
}
|
||||
@@ -303,7 +326,10 @@ mod tests {
|
||||
|
||||
record_payment_impl(&mut conn, pay(id, 40_000, 0)).unwrap();
|
||||
let s = summary(&conn, id);
|
||||
assert_eq!((s.payment_status.as_str(), s.paid_paise, s.balance_paise), ("partially_paid", 40_000, 60_000));
|
||||
assert_eq!(
|
||||
(s.payment_status.as_str(), s.paid_paise, s.balance_paise),
|
||||
("partially_paid", 40_000, 60_000)
|
||||
);
|
||||
|
||||
record_payment_impl(&mut conn, pay(id, 60_000, 0)).unwrap();
|
||||
let s = summary(&conn, id);
|
||||
@@ -317,7 +343,15 @@ mod tests {
|
||||
// 10% TDS: the client pays 900.00 in cash and deducts 100.00.
|
||||
record_payment_impl(&mut conn, pay(id, 90_000, 10_000)).unwrap();
|
||||
let s = summary(&conn, id);
|
||||
assert_eq!((s.payment_status.as_str(), s.paid_paise, s.tds_paise, s.balance_paise), ("paid", 90_000, 10_000, 0));
|
||||
assert_eq!(
|
||||
(
|
||||
s.payment_status.as_str(),
|
||||
s.paid_paise,
|
||||
s.tds_paise,
|
||||
s.balance_paise
|
||||
),
|
||||
("paid", 90_000, 10_000, 0)
|
||||
);
|
||||
// TDS alone is a valid payment too.
|
||||
let id2 = issued(&mut conn, 500.0, "2999-01-01");
|
||||
record_payment_impl(&mut conn, pay(id2, 0, 5_000)).unwrap();
|
||||
@@ -329,13 +363,18 @@ mod tests {
|
||||
let mut conn = crate::db::open_in_memory().unwrap();
|
||||
let id = issued(&mut conn, 1000.0, "2999-01-01");
|
||||
let err = record_payment_impl(&mut conn, pay(id, 100_001, 0)).unwrap_err();
|
||||
assert!(err.contains("more than the outstanding balance of ₹1000.00"), "{err}");
|
||||
assert!(
|
||||
err.contains("more than the outstanding balance of ₹1000.00"),
|
||||
"{err}"
|
||||
);
|
||||
// Cash plus TDS is what counts.
|
||||
assert!(record_payment_impl(&mut conn, pay(id, 95_000, 6_000)).is_err());
|
||||
record_payment_impl(&mut conn, pay(id, 50_000, 0)).unwrap();
|
||||
let err = record_payment_impl(&mut conn, pay(id, 50_001, 0)).unwrap_err();
|
||||
assert!(err.contains("₹500.00"), "{err}");
|
||||
let n: i64 = conn.query_row("SELECT COUNT(*) FROM payments", [], |r| r.get(0)).unwrap();
|
||||
let n: i64 = conn
|
||||
.query_row("SELECT COUNT(*) FROM payments", [], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(n, 1);
|
||||
}
|
||||
|
||||
@@ -374,7 +413,11 @@ mod tests {
|
||||
assert_eq!((s.payment_status.as_str(), s.balance_paise), ("none", 0));
|
||||
|
||||
let id2 = issued(&mut conn, 1000.0, "2999-01-01");
|
||||
conn.execute("UPDATE invoices SET status = 'draft' WHERE id = ?1", params![id2]).unwrap();
|
||||
conn.execute(
|
||||
"UPDATE invoices SET status = 'draft' WHERE id = ?1",
|
||||
params![id2],
|
||||
)
|
||||
.unwrap();
|
||||
assert!(record_payment_impl(&mut conn, pay(id2, 100, 0)).is_err());
|
||||
}
|
||||
|
||||
@@ -397,27 +440,38 @@ mod tests {
|
||||
assert_eq!(summary(&conn, id).payment_status, "paid");
|
||||
delete_payment_impl(&conn, full.id).unwrap();
|
||||
let s = summary(&conn, id);
|
||||
assert_eq!((s.payment_status.as_str(), s.balance_paise), ("unpaid", 100_000));
|
||||
assert_eq!(
|
||||
(s.payment_status.as_str(), s.balance_paise),
|
||||
("unpaid", 100_000)
|
||||
);
|
||||
assert!(list_payments_impl(&conn, id).unwrap().is_empty());
|
||||
assert!(delete_payment_impl(&conn, full.id).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overdue_is_derived_from_due_date_and_balance() {
|
||||
let d = |due: &str, paid: i64, tds: i64| derive_summary("issued", 100_000, paid, tds, due, "2026-05-10");
|
||||
let d = |due: &str, paid: i64, tds: i64| {
|
||||
derive_summary("issued", 100_000, paid, tds, due, "2026-05-10")
|
||||
};
|
||||
// Due today is not overdue yet; the day after is.
|
||||
assert_eq!(d("2026-05-10", 0, 0).payment_status, "unpaid");
|
||||
assert_eq!(d("2026-05-09", 0, 0).payment_status, "overdue");
|
||||
// Overdue wins over partially paid; the paid amount is still reported.
|
||||
let s = d("2026-05-09", 10_000, 0);
|
||||
assert_eq!((s.payment_status.as_str(), s.paid_paise), ("overdue", 10_000));
|
||||
assert_eq!(
|
||||
(s.payment_status.as_str(), s.paid_paise),
|
||||
("overdue", 10_000)
|
||||
);
|
||||
// A settled invoice is never overdue.
|
||||
assert_eq!(d("2026-05-09", 90_000, 10_000).payment_status, "paid");
|
||||
// No usable due date means never overdue.
|
||||
assert_eq!(d("", 0, 0).payment_status, "unpaid");
|
||||
assert_eq!(d("not a date", 0, 0).payment_status, "unpaid");
|
||||
// Cancelled invoices are not owed.
|
||||
assert_eq!(derive_summary("cancelled", 100_000, 0, 0, "2020-01-01", "2026-05-10").balance_paise, 0);
|
||||
assert_eq!(
|
||||
derive_summary("cancelled", 100_000, 0, 0, "2020-01-01", "2026-05-10").balance_paise,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -429,7 +483,10 @@ mod tests {
|
||||
record_payment_impl(&mut conn, pay(id, 3333, 0)).unwrap();
|
||||
record_payment_impl(&mut conn, pay(id, 3333, 0)).unwrap();
|
||||
let s = summary(&conn, id);
|
||||
assert_eq!((s.paid_paise, s.balance_paise, s.payment_status.as_str()), (9999, 0, "paid"));
|
||||
assert_eq!(
|
||||
(s.paid_paise, s.balance_paise, s.payment_status.as_str()),
|
||||
(9999, 0, "paid")
|
||||
);
|
||||
let inv = super::super::invoice::get_invoice_impl(&conn, id).unwrap();
|
||||
assert_eq!(inv.total_paise, 9999);
|
||||
}
|
||||
@@ -445,7 +502,14 @@ mod tests {
|
||||
let rows = super::super::invoice::list_invoices_impl(&conn).unwrap();
|
||||
let get = |id: i64| rows.iter().find(|r| r.id == id).unwrap();
|
||||
let ra = get(a);
|
||||
assert_eq!((ra.payment.paid_paise, ra.payment.tds_paise, ra.payment.balance_paise), (40_000, 5_000, 55_000));
|
||||
assert_eq!(
|
||||
(
|
||||
ra.payment.paid_paise,
|
||||
ra.payment.tds_paise,
|
||||
ra.payment.balance_paise
|
||||
),
|
||||
(40_000, 5_000, 55_000)
|
||||
);
|
||||
assert_eq!(ra.payment.payment_status, "overdue");
|
||||
assert_eq!(ra.total_paise, 100_000);
|
||||
assert_eq!(get(b).payment.payment_status, "paid");
|
||||
|
||||
Reference in New Issue
Block a user