用Obsidian记账

用Obsidian记账

本文仅限给有Obsidian和Dataview使用经验的人参考。不属适用人群的,请先了解这两个东西,慢慢入坑。

用了Obsidian之后,就什么都想用Obsidian管起来。拥有Metadata特性并安装了Dataview的Obsidian,已经可以作为小型数据库使用了。作为一个“数据库软件”,管个帐总可以吧?

在折腾这条道路上,总是有瑞士军刀和专业工具箱的纠结。到底是折腾一个工具以满足各类需求,还是根据需求分别寻求最佳方案,折腾一堆工具?这个问题恐怕永远是我们这种人的痛。

市场上专业的个人账单/财务管理软件已经非常多了,但我一直不太放心将个人账单信息存放到别人的服务器上。也有过自研程序的想法,核心功能不难做,难在兼容多端和细节体验。能不能用Obsidian实现一个简单、多端、灵活、本地存储的个人账单管理系统呢?

经过一个周末的探索,答案是基本可行(文章结尾部分有GIF动画效果演示)。

目前,我已经实现了Obsidian版的个人账单管理,并拥有以下功能:

  1. 实现多端应用(购买了Obsidian官方的Sync服务用来实现多端同步)
  2. 通过类似表单的引导方式录入账单
  3. 可以通过关键词、日期等快速查询账单
  4. 可以通过查询条件列出表格,并统计账单数量和总金额
虽然Obsidian的Sync也必然将数据存放在远端,但相比个人财务软件集中掌握所有人的单一数据而言,Obsidian同步服务器上的数据杂乱无章,毫无利用价值可言。归根到底,数据安全风险的本质不在于被窃取,而是被非法使用。

下面就分享一下我的实现过程。大致可以分成三个步骤:

  1. 构建个人账单笔记体系
  2. 用Dataview实现账单统计
  3. 用Templater实现向导式账单录入

1. 设计账单笔记

首先创建一个bills文件夹,用于存放账单笔记。账单笔记命名建议采用zk的命名方式。我的命名规则是YYYYMMDDHHmmss

内容如下:

---
tags: 
- 账单
date: {{date}}
goods:
- 商品摘要
type: 商品类别
price: 
from: 购买渠道
---

[[{{date}}]]
初期可以使用zk插件创建文件,通过官方提供的模板插件快速插入账单模板,再将文件另存到bills目录中。

笔记设计好后,就可以录入数据了。

录入账单最常规的方法是一条笔记一条笔记的记录。但是面对淘宝里近1000个订单,逐条录入是不现实的。所以我写了个爬虫,把自己在淘宝等平台上的订单都爬下来,然后自动生成了符合上面格式标准的账单笔记。这样,几百条账单就自动进入了我的obsidian仓库。

到这里,账单笔记的数据结构已经确定了,可以通过原始的方式记账,也可以通过Obsidian官方搜索功能快速定位指定账单。

比如,path:bills file:202109 小米,这条查询语句就能够将2021年9月包含小米的账单搜索出来。

下面说一下实现比较复杂的查询、列表和统计的方法。

2. 用Dataview实现账单统计

Dataview是一款非常强大的Obsidian三方插件。Dataview允许用户在笔记中撰写代码,用以实现类似的场景:将笔记的查询、汇聚结果显示成表格。

如果你会SQL语言或者Javascript(Typescript)的话,Dataview不难使用。不会也没关系,只要按照我写的一步一步做就能实现同样的效果。

  1. 首先要安装Dataview并启动。
  2. 然后创建一个笔记,在笔记中录入一段DataviewJs代码块:
const data = dv.pages('"bills"') //查询bills目录下的笔记
    .where(b => b.date>=dv.date('2021-01-01')) //过滤出2021年的账单
    .where(b => b.date<dv.date('2022-01-01'))
 
const tableData = data.sort(b => b.date,'desc') //按日期倒序
    .map(b => [b.file.link, b.from, b.type, b.date, b.goods, b.price]) //取出主要字段

var totle = 0

// 统计消费总计
for(let price of data.price.array()) {
    totle += price
}

dv.paragraph('- 账单数量: '+ data.length); //输出账单数量
dv.paragraph('- 消费总计: ¥'+ Math.round(totle*100)/100); //输出消费总计

dv.table(["笔记", "来源","类别", "日期", "商品", "价格¥"], tableData) //输出表格

这样,一个2021年账单表格和统计结果就出来了:

至此,用Obsidian管账的基本需求已经可以满足了。下面讨论通过Templater打造账单录入工作流,实现更快、更方便的录入账单。

3. 向导式账单录入

Templater是一款可编程的模板引擎,要比官方模板强大和自由,但也要求一定的技术能力。

不过还是那句话,如果你不是程序员也没关系,照我的做就行了。

  1. 安装Templater插件,启动。

2. 设置存放模板的路径。我将模板存放在template/templater中。


用两层目录存放模板,是为了对官方模板、日历模板、ZK模板等文件进行统一管理、分类存放。

3. 在上步指定的模板目录下创建模板笔记,命名为bill,内容如下:

<%*
//判断模板应用模式
if(tp.config.run_mode==0){
    //如果通过新建笔记应用模板,则将笔记存放到指定位置
    tp.file.move('/bills/'+tp.date.now("YYYYMMDDHHmmss"))
}
const froms = {
    "淘宝": "淘宝",
    "京东": "京东",
    "小米": "小米",
    "拼多多": "拼多多",
    "线下": "线下"
}
const types = [
    "日用",
    "电器",
    "汽车",
    "差旅",
    "食品"
]
from = await tp.system.suggester(Object.keys(froms), Object.values(froms),false,'请选择账单来源') 
type = await tp.system.suggester(types,types,false,'请选择账单类型') 
date = await tp.system.prompt("请输入账单日期",tp.date.now("YYYY-MM-DD"))
goods = await tp.system.prompt("请输入账单内容")
price = await tp.system.prompt("请输入价格")
%>---
tags: 
- 账单
date: <% date %>
goods: 
- <% goods  %><% tp.file.cursor(0) %>
type: <% type  %>
price: <% price  %>
from: <% from %>
---

[[<% date %>]]

在执行这个模板后,程序会做这么几件事儿(详见代码注释):

  • 判断是不是通过新笔记应用了该模板,如果是,那么将笔记存放到bills目录中,并用当前时间命名笔记。
  • 引导用户填写账单信息,包括账单来源、类型、日期、内容、价格等
  • 将用户录入的信息输出到相应的位置
  • 将由模板渲染好的内容插入笔记

4. 通过快捷键alt+n新建账单笔记。

或者在命令窗口中输入templater,找到并运行命令:

Templater: Create new note from template


也可以在已有的笔记应用账单模板,但是需要自行命名和修改路径。

执行该命令后,Obsidian会激活账单模板,执行模板里面的程序代码。

运行效果

PC端效果

移动端效果

编辑于 2021-10-20 17:13