Skip to content

Commit

Permalink
[cashflow] remove flat option
Browse files Browse the repository at this point in the history
  • Loading branch information
ananthakumaran committed Dec 24, 2023
1 parent a238cde commit 43cdac6
Show file tree
Hide file tree
Showing 11 changed files with 490 additions and 864 deletions.
49 changes: 2 additions & 47 deletions internal/server/expense.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,9 @@ func GetExpense(db *gorm.DB) gin.H {
taxes := query.Init(db).AccountPrefix("Expenses:Tax").All()
postings := query.Init(db).All()

graph := make(map[string]map[string]Graph)
graph := make(map[string]Graph)
for fy, ps := range utils.GroupByFY(postings) {
graph[fy] = make(map[string]Graph)
graph[fy]["flat"] = sortGraph(computeGraph(ps))
graph[fy]["hierarchy"] = sortGraph(computeHierarchyGraph(ps))
graph[fy] = sortGraph(computeHierarchyGraph(ps))
}

return gin.H{
Expand Down Expand Up @@ -86,49 +84,6 @@ func sortGraph(graph Graph) Graph {

}

func computeGraph(postings []posting.Posting) Graph {
nodes := make(map[string]Node)
links := make(map[Pair]decimal.Decimal)

var nodeID uint = 0

transactions := transaction.Build(postings)

for _, p := range postings {
_, ok := nodes[p.Account]
if !ok {
nodeID++
nodes[p.Account] = Node{ID: nodeID, Name: p.Account}
}

}

for _, t := range transactions {
from := lo.Filter(t.Postings, func(p posting.Posting, _ int) bool { return p.Amount.LessThan(decimal.Zero) })
to := lo.Filter(t.Postings, func(p posting.Posting, _ int) bool { return p.Amount.GreaterThan(decimal.Zero) })

for _, f := range from {
for f.Amount.Abs().GreaterThan(decimal.NewFromFloat(0.1)) && len(to) > 0 {
top := to[0]
if top.Amount.GreaterThan(f.Amount.Neg()) {
links[Pair{Source: nodes[f.Account].ID, Target: nodes[top.Account].ID}] = links[Pair{Source: nodes[f.Account].ID, Target: nodes[top.Account].ID}].Add(f.Amount.Neg())
top.Amount = top.Amount.Sub(f.Amount)
f.Amount = decimal.Zero
} else {
links[Pair{Source: nodes[f.Account].ID, Target: nodes[top.Account].ID}] = links[Pair{Source: nodes[f.Account].ID, Target: nodes[top.Account].ID}].Add(top.Amount)
f.Amount = f.Amount.Add(top.Amount)
to = to[1:]
}
}
}
}

return Graph{Nodes: lo.Values(nodes), Links: lo.Map(lo.Keys(links), func(k Pair, _ int) Link {
return Link{Source: k.Source, Target: k.Target, Value: links[k]}
})}

}

func computeHierarchyGraph(postings []posting.Posting) Graph {
nodes := make(map[string]Node)
links := make(map[Pair]decimal.Decimal)
Expand Down
11 changes: 4 additions & 7 deletions src/lib/cash_flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export function renderMonthlyFlow(
};
}

export function renderFlow(graph: Graph, cashflowType: string) {
export function renderFlow(graph: Graph) {
const id = "#d3-expense-flow";
const svg = d3.select(id);
const margin = { top: rem(60), right: rem(20), bottom: rem(40), left: rem(20) },
Expand Down Expand Up @@ -401,7 +401,7 @@ export function renderFlow(graph: Graph, cashflowType: string) {
return "middle";
})
.classed("svg-text-grey-dark", true)
.text((d: any) => `${name(d, cashflowType)} ${formatCurrencyCrude(d.value)}`);
.text((d: any) => `${name(d)} ${formatCurrencyCrude(d.value)}`);

const link = linkG.data(sankeyLinks).enter().append("g");

Expand Down Expand Up @@ -429,12 +429,9 @@ export function renderFlow(graph: Graph, cashflowType: string) {
linkG.data(sankeyLinks).enter().append("g").attr("class", "g-arrow").call(arrows);
}

function name(node: Node, cashflowType: string) {
function name(node: Node) {
let name: string, group: string;
if (
cashflowType === "hierarchy" &&
(node.name.startsWith("Income") || node.name.startsWith("Expenses"))
) {
if (node.name.startsWith("Income") || node.name.startsWith("Expenses")) {
name = lastName(node.name);
group = parentName(node.name);
} else {
Expand Down
16 changes: 1 addition & 15 deletions src/lib/components/Navbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import Actions from "$lib/components/Actions.svelte";
import { month, year, dateMax, dateMin, dateRangeOption } from "../../store";
import {
cashflowType,
cashflowExpenseDepth,
cashflowExpenseDepthAllowed,
cashflowIncomeDepth,
Expand All @@ -16,7 +15,6 @@
import { get } from "svelte/store";
import DateRange from "./DateRange.svelte";
import ThemeSwitcher from "./ThemeSwitcher.svelte";
import BoxedTabs from "./BoxedTabs.svelte";
import MonthPicker from "./MonthPicker.svelte";
import Logo from "./Logo.svelte";
import InputRange from "./InputRange.svelte";
Expand Down Expand Up @@ -44,7 +42,6 @@
hide?: boolean;
dateRangeSelector?: boolean;
monthPicker?: boolean;
cashflowTypePicker?: boolean;
financialYearPicker?: boolean;
maxDepthSelector?: boolean;
recurringIcons?: boolean;
Expand All @@ -61,7 +58,6 @@
{
label: "Yearly",
href: "/yearly",
cashflowTypePicker: true,
financialYearPicker: true,
maxDepthSelector: true
},
Expand Down Expand Up @@ -377,17 +373,7 @@
</div>
{/if}

{#if selectedSubLink?.cashflowTypePicker}
<BoxedTabs
options={[
{ label: "Flat", value: "flat" },
{ label: "Hierarchy", value: "hierarchy" }
]}
bind:value={$cashflowType}
/>
{/if}

{#if selectedSubLink?.maxDepthSelector && ($cashflowExpenseDepthAllowed.max > 1 || $cashflowIncomeDepthAllowed.max > 1) && $cashflowType == "hierarchy"}
{#if selectedSubLink?.maxDepthSelector && ($cashflowExpenseDepthAllowed.max > 1 || $cashflowIncomeDepthAllowed.max > 1)}
<div class="dropdown is-right is-hoverable">
<div class="dropdown-trigger">
<button class="button is-small" aria-haspopup="true">
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ export function ajax(route: "/api/expense"): Promise<{
investments: { [key: string]: Posting[] };
taxes: { [key: string]: Posting[] };
};
graph: { [key: string]: { flat: Graph; hierarchy: Graph } };
graph: { [key: string]: Graph };
}>;

export function ajax(route: "/api/budget"): Promise<{
Expand Down
2 changes: 0 additions & 2 deletions src/persisted_store.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { persisted } from "svelte-local-storage-store";
import { writable, get } from "svelte/store";

export const cashflowType = persisted("cashflowType", "hierarchy");

export const obscure = persisted("obscure", false);

export const cashflowExpenseDepthAllowed = writable({ min: 1, max: 1 });
Expand Down
18 changes: 4 additions & 14 deletions src/routes/(app)/cash_flow/yearly/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@
import { ajax, depth, firstName, type Graph, type Posting } from "$lib/utils";
import { dateMin, year } from "../../../../store";
import {
cashflowType,
setCashflowDepthAllowed,
cashflowExpenseDepth,
cashflowIncomeDepth
} from "../../../../persisted_store";
import ZeroState from "$lib/components/ZeroState.svelte";
let graph: Record<string, Record<string, Graph>>, expenses: Posting[];
let graph: Record<string, Graph>, expenses: Posting[];
let isEmpty = false;
function maxDepth(prefix: string) {
if (!graph) return 1;
const max = _.chain(graph)
.flatMap((g) => g["hierarchy"])
.flatMap((g) => g.nodes)
.filter((n) => n.name.startsWith(prefix))
.map((n) => depth(n.name))
Expand All @@ -28,8 +26,8 @@
return max || 1;
}
function filter(graph: Graph, incomeDepth: number, expenseDepth: number, flowType: string) {
if (!graph || flowType != "hierarchy") return graph;
function filter(graph: Graph, incomeDepth: number, expenseDepth: number) {
if (!graph) return graph;
const [removed, allowed] = _.partition(graph.nodes, (n) => {
const account = firstName(n.name);
Expand All @@ -51,15 +49,7 @@
if (graph[$year] == null) {
isEmpty = true;
} else {
renderFlow(
filter(
_.cloneDeep(graph[$year][$cashflowType]),
$cashflowIncomeDepth,
$cashflowExpenseDepth,
$cashflowType
),
$cashflowType
);
renderFlow(filter(_.cloneDeep(graph[$year]), $cashflowIncomeDepth, $cashflowExpenseDepth));
isEmpty = false;
}
}
Expand Down
118 changes: 44 additions & 74 deletions tests/fixture/eur-hledger/expense.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,50 @@
"expenses": [],
"graph": {
"2021 - 22": {
"flat": {
"nodes": [
{
"id": 1,
"name": "Assets:Checking"
},
{
"id": 3,
"name": "Assets:Equity:AAPL"
},
{
"id": 2,
"name": "Income:Salary:Acme"
}
],
"links": [
{
"source": 1,
"target": 3,
"value": 10005.05
},
{
"source": 2,
"target": 1,
"value": 11000
}
]
},
"hierarchy": {
"nodes": [
{
"id": 1,
"name": "Assets:Checking"
},
{
"id": 5,
"name": "Assets:Equity:AAPL"
},
{
"id": 2,
"name": "Income"
},
{
"id": 3,
"name": "Income:Salary"
},
{
"id": 4,
"name": "Income:Salary:Acme"
}
],
"links": [
{
"source": 1,
"target": 5,
"value": 10005.05
},
{
"source": 2,
"target": 1,
"value": 11000
},
{
"source": 3,
"target": 2,
"value": 11000
},
{
"source": 4,
"target": 3,
"value": 11000
}
]
}
"nodes": [
{
"id": 1,
"name": "Assets:Checking"
},
{
"id": 5,
"name": "Assets:Equity:AAPL"
},
{
"id": 2,
"name": "Income"
},
{
"id": 3,
"name": "Income:Salary"
},
{
"id": 4,
"name": "Income:Salary:Acme"
}
],
"links": [
{
"source": 1,
"target": 5,
"value": 10005.05
},
{
"source": 2,
"target": 1,
"value": 11000
},
{
"source": 3,
"target": 2,
"value": 11000
},
{
"source": 4,
"target": 3,
"value": 11000
}
]
}
},
"month_wise": {
Expand Down
Loading

0 comments on commit 43cdac6

Please sign in to comment.