117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
package process
|
|
|
|
import (
|
|
"cmp"
|
|
"slices"
|
|
"strings"
|
|
|
|
"kryddan.xyz/bookkeep/types"
|
|
)
|
|
|
|
func PerOrgUnit(account types.BookkeepingAccount) map[string]types.BookkeepingAccount {
|
|
out := map[string]types.BookkeepingAccount{}
|
|
for _, row := range account.Rows {
|
|
unit := strings.ToLower(*cmp.Or(row.OrganizationUnit, new("")))
|
|
if _, ok := out[unit]; !ok {
|
|
out[unit] = types.BookkeepingAccount{
|
|
Name: account.Name,
|
|
AccountNumber: account.AccountNumber,
|
|
Rows: []types.BookkeepingAccountRow{},
|
|
}
|
|
}
|
|
v := out[unit]
|
|
v.Rows = append(v.Rows, row)
|
|
out[unit] = v
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
type FaultyOutlay struct {
|
|
Row types.BookkeepingAccountRow
|
|
ContributingVerifications []types.Verification
|
|
}
|
|
|
|
func ValidateOutlays(account types.BookkeepingAccount) ([]FaultyOutlay, bool) {
|
|
rows := sortRows(account.Rows)
|
|
slices.Reverse(rows)
|
|
|
|
talliedVerifications := map[string]types.BookkeepingAccountRow{}
|
|
originalVerifications := map[string]types.BookkeepingAccountRow{}
|
|
for _, row := range rows {
|
|
if r, ok := talliedVerifications[row.Verification.String()]; ok {
|
|
r.Debet += row.Debet
|
|
r.Kredit += row.Kredit
|
|
talliedVerifications[row.Verification.String()] = r
|
|
originalVerifications[row.Verification.String()] = r
|
|
continue
|
|
}
|
|
talliedVerifications[row.Verification.String()] = row
|
|
originalVerifications[row.Verification.String()] = row
|
|
}
|
|
|
|
backlinks := map[string][]string{}
|
|
for rowVerf, row := range originalVerifications {
|
|
if len(row.LinkedVerifications) == 0 {
|
|
continue
|
|
}
|
|
if row.Kredit == 0 && row.Debet == 0 {
|
|
continue
|
|
}
|
|
alteredRow := talliedVerifications[rowVerf]
|
|
if alteredRow.Kredit == 0 && alteredRow.Debet == 0 {
|
|
continue
|
|
}
|
|
|
|
alterValue := func(old int, adjustment int) (int, int) {
|
|
v := old + adjustment
|
|
if v > 0 {
|
|
return v, 0
|
|
} else {
|
|
return 0, -v
|
|
}
|
|
}
|
|
|
|
for _, linked := range row.LinkedVerifications {
|
|
linkedRow := talliedVerifications[linked.String()]
|
|
if linkedRow.Debet != 0 && row.Kredit != 0 {
|
|
linkedRow.Debet, row.Kredit = alterValue(linkedRow.Debet, -row.Kredit)
|
|
} else if linkedRow.Kredit != 0 && row.Debet != 0 {
|
|
linkedRow.Kredit, row.Debet = alterValue(linkedRow.Kredit, -row.Debet)
|
|
}
|
|
backlinks[linked.String()] = append(backlinks[linked.String()], row.Verification.String())
|
|
talliedVerifications[linked.String()] = linkedRow
|
|
}
|
|
|
|
talliedVerifications[row.Verification.String()] = row
|
|
}
|
|
|
|
faults := []FaultyOutlay{}
|
|
for verf, row := range talliedVerifications {
|
|
if row.Debet != 0 || row.Kredit != 0 {
|
|
contributingVerfs := []types.Verification{}
|
|
bls := backlinks[verf]
|
|
for _, v := range bls {
|
|
contributingVerfs = append(contributingVerfs, talliedVerifications[v].Verification)
|
|
}
|
|
faults = append(faults, FaultyOutlay{
|
|
Row: row,
|
|
ContributingVerifications: contributingVerfs,
|
|
})
|
|
}
|
|
}
|
|
|
|
return faults, len(faults) == 0
|
|
}
|
|
|
|
func sortRows(rows []types.BookkeepingAccountRow) []types.BookkeepingAccountRow {
|
|
slices.SortFunc(rows, func(a, b types.BookkeepingAccountRow) int {
|
|
if a.Verification.Series != b.Verification.Series {
|
|
return 0
|
|
}
|
|
return a.Verification.Number - b.Verification.Number
|
|
})
|
|
|
|
return rows
|
|
}
|