#!/usr/bin/awk -f
# Script adapted from suggestions on https://unix.stackexchange.com/a/527004/1925
#
# Passed a ledger file, this will:
# 1. Sort accretion postings before deductions
# 2. Sort postings by account alphabetically
# 3. Merge 1 set of postings with the same account and direction by clearing
#    the amount field. Note all posting meta data must also match to merge.
#
# Suggested usage:
# $ sortandmergepostings journal.ledger | hledger -f - print -x
#
# Given that each run will only merge and recalculate amounts on one account per
# transaction it may need to be run multiple times to fully normalize a ledger.

BEGIN { FS = "[[:space:]][[:space:]]+" }

function dump() {
    an = asorti(accretions, as)
    dn = asorti(deductions, ds)
    for (i=1; i<=an; i++) {
        postings[length(postings)+1] = accretions[as[i]]
    }
    for (i=1; i<=dn; i++) {
        postings[length(postings)+1] = deductions[ds[i]]
    }
    for (i in postings) {
        posting = postings[i]
        split(posting, parts, FS)
        currency = parts[3]
        gsub(/[[:digit:]., ]+/, "", currency)
        if (!inferred && (!merge || merge == parts[2]) && seen[parts[2] currency parts[4]]>1 && parts[3] !~ /@/) {
            if (!merge) merged[i] = "    " parts[2] "  " parts[4]
            merge = parts[2]
        } else {
            merged[i] = posting
        }
    }
    for (i in merged) print merged[i]
    if (inferred) print inferred
    inferred = ""
    merge = ""
    delete accretions
    delete deductions
    delete postings
    delete merged
    delete seen
}

!NF {
    dump()
    print
    next
}

END {
    dump()
}

/^[^[:space:]]/ {
    dump()
    print $0
    next
}

{
    postingct++
    account = $2
    amount = $3
    comments = $4
    currency = amount
    gsub(/[[:digit:]., ]+/, "", currency)
    sub(/^[*!] /, "", account)
}

account ~ /^;/ {
    print
    next
}

!amount {
    inferred = $0
    next
}

amount !~ /@/ {
    seen[account currency comments]++
}

amount !~ /-/ {
    accretions[account postingct] = $0
}

amount ~ /-/ {
    deductions[account postingct] = $0
}
