Distinguish debit and credit postings sharing an upstream reference
This commit is contained in:
@@ -18,7 +18,15 @@ func digest(parts ...string) string {
|
||||
h := sha256.Sum256(b)
|
||||
return hex.EncodeToString(h[:])
|
||||
}
|
||||
func identity(f domain.Facts) string { return digest(f.AccountID, f.Source, f.ExternalID) }
|
||||
func identity(f domain.Facts) string {
|
||||
// Banks can reuse a reference for a debit and its credit counterpart.
|
||||
// Facts are normalized before lookup; never rewrite stored journal IDs.
|
||||
direction := "credit"
|
||||
if strings.HasPrefix(string(f.Amount), "-") {
|
||||
direction = "debit"
|
||||
}
|
||||
return digest(f.AccountID, f.Source, f.ExternalID, direction)
|
||||
}
|
||||
func fingerprint(f domain.Facts) string {
|
||||
return digest(f.AccountID, f.BookingDate, f.ValueDate, f.Amount.String(), f.Currency, strings.Join(strings.Fields(f.RawDescription), " "), strings.ToLower(strings.Join(strings.Fields(f.Counterparty), " ")), f.CounterpartyIBAN)
|
||||
}
|
||||
@@ -30,9 +38,10 @@ func sameBookedMoney(a, b domain.Facts) bool {
|
||||
}
|
||||
|
||||
// NormalizeAndDedupe returns new records without mutating the input. Stable bank
|
||||
// entry references take precedence over text. CSV rows without references use
|
||||
// occurrence counts, not a set: two identical rows remain two transactions and
|
||||
// importing the same export again creates none. For overlapping partial exports,
|
||||
// entry references, scoped by account, source and debit/credit direction, take
|
||||
// precedence over text. Rows without references use occurrence counts, not a set:
|
||||
// two identical rows remain two transactions; reimporting creates none.
|
||||
// For overlapping partial exports,
|
||||
// indistinguishable rows cannot prove an additional occurrence; import complete
|
||||
// overlapping date windows to establish multiplicity.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user