viewer: click to copy values, amounts, and identifiers
Company details (EIN, state number, address, every profile value), an entry's number and amount, an account's code and balance, a document's file name and SHA-256 copy on click: a pointer and copy icon on hover, a check for a moment after. Amounts copy as their exact plain decimal (fmt::plain: no "$", commas, or parentheses; "-" for negatives); where a click already drills down (report cells, register and transaction rows, general ledger lines), a right-click menu copies the amounts, the entry number, or the description. The sidebar's chain check copies the journal head and data folder. tests/copy.rs: every display style copies back to the exact value (property), and clicking the EIN, an entry's amount, and its number puts exactly that on the clipboard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
c126843ce1
commit
afd427a023
10 files changed
+344
-17
No files matched your search
@@ -87,6 +87,31 @@ fn group(d: Decimal) -> String {
|
||||
format!("{out}.{frac}")
|
||||
}
|
||||
|
||||
/// The plain decimal behind a displayed amount, for the clipboard:
|
||||
/// `$1,234.56` → `1234.56`, `(207.68)` and `−12.00` → `-207.68`, `-12.00`.
|
||||
/// Exact (the display never rounds). None when the text isn't an amount.
|
||||
pub fn plain(display: &str) -> Option<String> {
|
||||
let t = display.trim();
|
||||
let (neg, t) = match t.strip_prefix('(').and_then(|x| x.strip_suffix(')')) {
|
||||
Some(inner) => (true, inner),
|
||||
None => (false, t),
|
||||
};
|
||||
let (neg, t) = match t.strip_prefix('\u{2212}').or_else(|| t.strip_prefix('-')) {
|
||||
Some(rest) => (!neg, rest),
|
||||
None => (neg, t.strip_prefix('+').unwrap_or(t)),
|
||||
};
|
||||
let digits: String = t.chars().filter(|c| !matches!(c, '$' | ',')).collect();
|
||||
let d: Decimal = digits.parse().ok()?;
|
||||
if digits.is_empty() || !digits.chars().all(|c| c.is_ascii_digit() || c == '.') {
|
||||
return None;
|
||||
}
|
||||
Some(if neg && !d.is_zero() {
|
||||
format!("-{digits}")
|
||||
} else {
|
||||
digits
|
||||
})
|
||||
}
|
||||
|
||||
/// A percent change from `prev` to `now`, if `prev` isn't zero.
|
||||
pub fn pct_change(prev: Decimal, now: Decimal) -> Option<f64> {
|
||||
if prev.is_zero() {
|
||||
@@ -213,5 +238,12 @@ mod tests {
|
||||
assert_eq!(label("ein"), "EIN");
|
||||
assert_eq!(label("irs_name_control"), "IRS name control");
|
||||
assert_eq!(words("PrepaidExpense"), "Prepaid expense");
|
||||
assert_eq!(plain("$1,234.56").as_deref(), Some("1234.56"));
|
||||
assert_eq!(plain("(207.68)").as_deref(), Some("-207.68"));
|
||||
assert_eq!(plain("\u{2212}$12.00").as_deref(), Some("-12.00"));
|
||||
assert_eq!(plain("+1,000.005").as_deref(), Some("1000.005"));
|
||||
assert_eq!(plain("0.00").as_deref(), Some("0.00"));
|
||||
assert_eq!(plain("Debit"), None);
|
||||
assert_eq!(plain(""), None);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user