spltty 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +351 -0
- data/config.example.json +33 -0
- data/exe/spltty +11 -0
- data/lib/spltty_cli/commands/add.rb +274 -0
- data/lib/spltty_cli/commands/groups.rb +47 -0
- data/lib/spltty_cli/commands/groups_add.rb +73 -0
- data/lib/spltty_cli/commands/groups_rm.rb +73 -0
- data/lib/spltty_cli/commands/help.rb +44 -0
- data/lib/spltty_cli/commands/install.rb +407 -0
- data/lib/spltty_cli/commands/list.rb +37 -0
- data/lib/spltty_cli/commands/methods.rb +33 -0
- data/lib/spltty_cli/commands/methods_add.rb +58 -0
- data/lib/spltty_cli/commands/settle.rb +302 -0
- data/lib/spltty_cli/commands/sync.rb +41 -0
- data/lib/spltty_cli/commands/totals.rb +85 -0
- data/lib/spltty_cli/config.rb +140 -0
- data/lib/spltty_cli/discovery.rb +95 -0
- data/lib/spltty_cli/groups.rb +60 -0
- data/lib/spltty_cli/ledger.rb +68 -0
- data/lib/spltty_cli/notes.rb +67 -0
- data/lib/spltty_cli/notes_sync.rb +137 -0
- data/lib/spltty_cli/prompt.rb +75 -0
- data/lib/spltty_cli/table.rb +95 -0
- data/lib/spltty_cli/totals.rb +222 -0
- data/lib/spltty_cli/version.rb +5 -0
- data/lib/spltty_cli.rb +98 -0
- data/templates/CLAUDE.md.erb +229 -0
- data/templates/gitignore +4 -0
- data/templates/notes.md.erb +39 -0
- data/templates/skills/ingest/SKILL.md +66 -0
- data/templates/sources-INDEX.md +13 -0
- metadata +94 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3bf97f1aff65eb471face732214bed57413de422a2e1bbae72d562931849db79
|
|
4
|
+
data.tar.gz: aaaf79b631b2972234bf56dbec3a9a2db7c37448addba068fdb84026cc04aa3c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f5d18872e7428a31860edc70d027e4e922ea71c29eb0a008890986cbe1f24cc1ba6449fb47f48683c0b56af1a71157bb92b27a1d11aac4544b26c64df765135c
|
|
7
|
+
data.tar.gz: 3000707668e2ca9604cd5f411ed973dbcac99067192b172783d1a32a19ba2c4ae29afc76ae7f0488c57019376954ccc5e780c99c253a8576739eab8a0b11c892
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thiago Diniz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# spltty
|
|
2
|
+
|
|
3
|
+
Shared-expense tracking in **plain markdown ledgers**, with the arithmetic owned by
|
|
4
|
+
a CLI instead of a spreadsheet — and a generated `CLAUDE.md` so an AI agent can do
|
|
5
|
+
the transcription while `spltty` does the math.
|
|
6
|
+
|
|
7
|
+
The data is markdown tables you can read, diff and grep. `spltty` owns the parts
|
|
8
|
+
that must be exact: appending rows, resolving split groups, computing who owes
|
|
9
|
+
whom, and recording settlements.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
mkdir ~/finances && cd ~/finances
|
|
13
|
+
spltty install # asks who splits what, scaffolds the workspace
|
|
14
|
+
spltty add "Groceries" -v 84.20 -p Ana -m cash -r Both
|
|
15
|
+
spltty totals
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
**Homebrew**
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
brew install thiagodiniz/spltty/spltty
|
|
24
|
+
# from a checkout, before the tap exists:
|
|
25
|
+
brew install --build-from-source ./Formula/spltty.rb
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**RubyGems** (needs Ruby >= 3.1)
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
gem install spltty
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Linux/Unix script** — installs the gem, falls back to a per-user install and
|
|
35
|
+
tells you if the bin dir is not on your `PATH`:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
curl -fsSL https://raw.githubusercontent.com/thiagodiniz/spltty/main/install.sh | sh
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**From a checkout** (development). Ruby is pinned via [mise](https://mise.jdx.dev)
|
|
42
|
+
(`.tool-versions` → ruby 3.4.2); `bin/spltty` selects it automatically, so the CLI
|
|
43
|
+
runs from any directory without activating mise:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
mise install && bundle install
|
|
47
|
+
./bin/spltty help
|
|
48
|
+
rake install # build the gem and put `spltty` on your PATH
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Setting up a workspace
|
|
52
|
+
|
|
53
|
+
`spltty install [PATH]` creates everything a tracker needs in a folder (the current
|
|
54
|
+
directory by default):
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
.spltty/config.json participants, split groups, payment methods, ledger cache
|
|
58
|
+
accounts/ one ledger per account being tracked
|
|
59
|
+
sources/INDEX.md tracker for raw statements/receipts
|
|
60
|
+
CLAUDE.md generated workflow guide for the AI agent
|
|
61
|
+
.claude/skills/ingest/ the /ingest skill for raw inputs
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
It asks for the participants (at least one is required), a shared split group, the
|
|
65
|
+
ledgers to create, optional payment-method defaults, and whether to `git init`.
|
|
66
|
+
Every step but the participants can be skipped — pressing enter through it yields
|
|
67
|
+
one monthly ledger named `expenses`.
|
|
68
|
+
|
|
69
|
+
Non-interactively:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
spltty install ~/finances -y -P Ana,Bruno -s Ana:60,Bruno:40 -l HOME,TRIP --no-git
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| Flag | Meaning |
|
|
76
|
+
|------|---------|
|
|
77
|
+
| `-P, --participants` | comma-separated names (required) |
|
|
78
|
+
| `-g, --group` / `-s, --split` | shared group name and `NAME:PCT,NAME:PCT` split (default: `Both`, even) |
|
|
79
|
+
| `-l, --ledgers` | comma-separated ledger names (default: `expenses`) |
|
|
80
|
+
| `--single` | create ledgers as single files instead of per-month |
|
|
81
|
+
| `--no-git` / `--git` | skip or force `git init` |
|
|
82
|
+
| `-f, --force` | overwrite an existing workspace config |
|
|
83
|
+
|
|
84
|
+
Every command afterwards finds the workspace by walking up from the current
|
|
85
|
+
directory for `.spltty/config.json` — like git. Override with `--config` /
|
|
86
|
+
`SPLTTY_CONFIG`.
|
|
87
|
+
|
|
88
|
+
## Usage
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
spltty install [PATH] # scaffold a new workspace (accounts, sources, CLAUDE.md)
|
|
92
|
+
spltty add [options] # (alias: a) append an entry (flags + interactive confirm)
|
|
93
|
+
spltty list # (alias: ls) list discovered ledgers (also runs the notes<->config sync)
|
|
94
|
+
spltty sync # sync ledger config between notes-file headers and config.json
|
|
95
|
+
spltty totals [LEDGER..] # per-person totals & settlement per ledger (--combined to merge)
|
|
96
|
+
spltty settle [LEDGER..] # record a settlement (one ledger = cash; several/none = offsets + optional cash)
|
|
97
|
+
spltty groups # list split groups (global + per-ledger)
|
|
98
|
+
spltty groups add NAME # define/update a split group (in a ledger header, or --global)
|
|
99
|
+
spltty groups rm NAME # remove a split group
|
|
100
|
+
spltty methods # list configured payment methods
|
|
101
|
+
spltty methods add NAME # add/update a payment method in the config
|
|
102
|
+
spltty help # show every command with its arguments/options
|
|
103
|
+
spltty add --help # options for a single command
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Add or update a payment method without hand-editing the config:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
spltty methods add "Visa 1234" -s visa-1234 -p Ana -r Both -b 10
|
|
110
|
+
# -s slug -p paid-by -r responsible -b bill-day (1-31 or "last")
|
|
111
|
+
# Only the properties you pass are set; existing ones are preserved.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`add` takes the **title as a positional argument**; everything else is a flag
|
|
115
|
+
with a short alias. It prompts for any required field you omit, applies
|
|
116
|
+
payment-method defaults, shows the resolved row, and asks before writing. Pass
|
|
117
|
+
`--yes`/`-y` to skip prompts (required fields must then be supplied).
|
|
118
|
+
|
|
119
|
+
**Unknown ledger names are treated as typos, not new accounts.** `-l CAS` when
|
|
120
|
+
you meant `CASA` would otherwise scaffold a junk ledger that then shows up in
|
|
121
|
+
every totals run. Interactively you get a menu — create it, or pick the one you
|
|
122
|
+
meant:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
$ spltty add "Coffee" -l CAS -v 12.90
|
|
126
|
+
Ledger "CAS" is not known yet.
|
|
127
|
+
1) Create a new ledger "CAS"
|
|
128
|
+
2) HOME
|
|
129
|
+
3) TRIP
|
|
130
|
+
Which ledger? [1]: 2 # or type HOME
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
With `--yes` there is nobody to ask, so it errors and lists the known ledgers.
|
|
134
|
+
Creating a ledger from a script needs `--create-ledger`:
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
spltty add "Mooring" -l BOAT -v 40 -p Ana -m cash -y --create-ledger
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Examples
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
# Positional title + short flags:
|
|
144
|
+
spltty add "Leite 1L" -l HOME -v 14.20 -p Ana -m cash
|
|
145
|
+
|
|
146
|
+
# Card entry — Paid By/Responsible/Date default from the card's config:
|
|
147
|
+
spltty add "Extra" -l HOME -v 66.69 -m "Visa 1234" -y
|
|
148
|
+
|
|
149
|
+
# Multi-currency entry:
|
|
150
|
+
spltty add "Metro" -l TRIP -o 3.75 -c CAD -v 14.20 -p Bruno -m Wise -y
|
|
151
|
+
|
|
152
|
+
# Fully interactive (prompts for what's missing):
|
|
153
|
+
spltty add
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Arguments & options
|
|
157
|
+
|
|
158
|
+
| Flag | Short | Meaning |
|
|
159
|
+
|------------------|-------|-------------------------------------------------------------|
|
|
160
|
+
| `<title>` | | Expense description — **positional** (first bare argument) |
|
|
161
|
+
| `--ledger` | `-l` | Ledger name (default: `default_ledger` in config; case-insensitive) |
|
|
162
|
+
| `--date` | `-d` | `YYYY-MM-DD` (default: today, or the card's bill day) |
|
|
163
|
+
| `--value` | `-v` | Amount in R$ (`12.5`, `12,50`, `1.234,56` all accepted) |
|
|
164
|
+
| `--paid-by` | `-p` | Who fronted the money |
|
|
165
|
+
| `--method` | `-m` | Payment instrument; matches a configured payment method |
|
|
166
|
+
| `--responsible` | `-r` | Who bears the cost — a split-group name (`Both`, …) or a person (`Ana`) |
|
|
167
|
+
| `--paid-responsible` | `-pr` | Shortcut: sets **both** Paid By and Responsible (e.g. `-pr Ana`); `-p`/`-r` override |
|
|
168
|
+
| `--source` | `-s` | Provenance (default `text`) |
|
|
169
|
+
| `--orig-value` | `-o` | Original amount — multi-currency schema only |
|
|
170
|
+
| `--currency` | `-c` | Original currency — multi-currency schema only |
|
|
171
|
+
| `--create-ledger`| | Create `--ledger` when it doesn't exist (required with `--yes`) |
|
|
172
|
+
| `--yes` | `-y` | Non-interactive; no prompts or confirmation |
|
|
173
|
+
| `--config` | `-C` | Config path (default `.spltty/config.json`; or `SPLTTY_CONFIG`) |
|
|
174
|
+
| `--accounts-dir` | `-A` | Override accounts dir (or `SPLTTY_ACCOUNTS_DIR`) |
|
|
175
|
+
|
|
176
|
+
## Config (`.spltty/config.json`)
|
|
177
|
+
|
|
178
|
+
Only `payment_methods` is authored by hand. The `ledgers` block is **optional and
|
|
179
|
+
auto-maintained**: on every run the CLI scans `accounts/` and records what it
|
|
180
|
+
finds. Discovery treats as a ledger:
|
|
181
|
+
|
|
182
|
+
- a root file named `<NAME>.ledger.md` (single-file ledger), or
|
|
183
|
+
- a subfolder containing `YYYY-MM.md` files (monthly ledger, e.g. `HOME`).
|
|
184
|
+
|
|
185
|
+
Everything else is ignored. Structural fields (`type`/`file`/`dir`/`schema`) are
|
|
186
|
+
set automatically; `title`, `default_responsible`, `default_currency`, and
|
|
187
|
+
`notes` are user overrides that discovery preserves. See `config.example.json`.
|
|
188
|
+
|
|
189
|
+
### Notes-header sync
|
|
190
|
+
|
|
191
|
+
The three tunable fields — `title`, `default_responsible`, `default_currency` —
|
|
192
|
+
also live in each ledger's `*.notes.md` file, in a YAML frontmatter header under a
|
|
193
|
+
`spltty:` key, so the config sits next to the rules it documents:
|
|
194
|
+
|
|
195
|
+
```markdown
|
|
196
|
+
---
|
|
197
|
+
spltty:
|
|
198
|
+
default_responsible: Both
|
|
199
|
+
groups:
|
|
200
|
+
Both: {Ana: 65, Bruno: 35}
|
|
201
|
+
---
|
|
202
|
+
# WEDDING — Wedding — Notes
|
|
203
|
+
…
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`spltty sync` (and `spltty list`) keep the header and `config.json` in agreement.
|
|
207
|
+
**The notes file is the source of truth.** Each `ledgers` entry carries a
|
|
208
|
+
`synced_at` timestamp; the direction is decided by comparing it to the notes
|
|
209
|
+
file's filesystem mtime:
|
|
210
|
+
|
|
211
|
+
- **notes file edited more recently** (or never synced) → the header wins and
|
|
212
|
+
updates config.
|
|
213
|
+
- **config newer, or the header is missing the field** → config is copied into
|
|
214
|
+
the header.
|
|
215
|
+
|
|
216
|
+
Every field is optional: only keys present on the winning side are merged, an
|
|
217
|
+
absent key is never cleared, and a ledger with no tunable config gets no
|
|
218
|
+
`spltty:` block. Any other frontmatter keys you add are left untouched. Don't
|
|
219
|
+
hand-edit `synced_at` — the sync manages it (and stamps it to the post-write
|
|
220
|
+
mtime so a write never ping-pongs). `spltty add` does **not** run this sync.
|
|
221
|
+
|
|
222
|
+
### Split groups
|
|
223
|
+
|
|
224
|
+
A **split group** is a named split — `name → {participant: percentage}` — such as
|
|
225
|
+
`Both → {Ana: 70, Bruno: 30}` or `Presente → {Ana: 100}`. Percentages are
|
|
226
|
+
integers and should sum to 100 (a mismatch only warns).
|
|
227
|
+
|
|
228
|
+
Each ledger's own groups live in its notes header, under `spltty.groups`, and are
|
|
229
|
+
synced into `config.json` (as `ledgers.<NAME>.groups`) by the same
|
|
230
|
+
notes↔config sync above:
|
|
231
|
+
|
|
232
|
+
```yaml
|
|
233
|
+
---
|
|
234
|
+
spltty:
|
|
235
|
+
default_responsible: Both
|
|
236
|
+
groups:
|
|
237
|
+
Both: {Ana: 70, Bruno: 30}
|
|
238
|
+
Presente: {Ana: 100}
|
|
239
|
+
---
|
|
240
|
+
# HOME — … — Notes
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
When the **identical** definition (same name + participants + percentages) appears
|
|
244
|
+
in **two or more** ledgers, it is promoted to a shared **global** group at the top
|
|
245
|
+
level of `config.json` (`"groups": { … }`). Global groups are config-only — they
|
|
246
|
+
are never written back into any header, and the sync only manages ledger-specific
|
|
247
|
+
groups. A ledger group shadows a global group of the same name (e.g. a global
|
|
248
|
+
`Both` 50/50 vs HOME's own `Both` 70/30).
|
|
249
|
+
|
|
250
|
+
Manage groups without hand-editing YAML:
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
spltty groups # list global + per-ledger groups
|
|
254
|
+
spltty groups add Both -s Ana:70,Bruno:30 -l HOME # write into HOME's header
|
|
255
|
+
spltty groups add Both -s Ana:50,Bruno:50 --global # write a global group
|
|
256
|
+
spltty groups rm Both -l HOME # remove from a ledger (or --global)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Consumed by `spltty totals`:** when computing settlement, each row's
|
|
260
|
+
`Responsible` value is resolved against these groups (ledger header groups first,
|
|
261
|
+
then global). A match splits the cost by the group's percentages; anything
|
|
262
|
+
unmatched is treated as a person (100%). There is no `Both (NN/NN T/C)`
|
|
263
|
+
string-parsing anymore — the split lives in the group. (`spltty add` still writes
|
|
264
|
+
the `Responsible` cell verbatim; it does not yet validate it against known groups.)
|
|
265
|
+
|
|
266
|
+
## Totals & settlement
|
|
267
|
+
|
|
268
|
+
`spltty totals` compiles per-person **Paid**, **Owes**, **Net = Paid − Owes**, a
|
|
269
|
+
spending breakdown, and a greedy **Settlement** (who pays whom):
|
|
270
|
+
|
|
271
|
+
```sh
|
|
272
|
+
spltty totals # one report per ledger (reference ledgers flagged)
|
|
273
|
+
spltty totals HOME TRIP # only these ledgers
|
|
274
|
+
spltty totals --combined # merge all non-reference ledgers into ONE settlement
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
A monthly ledger (HOME) merges all its `YYYY-MM.md` files into one per-ledger
|
|
278
|
+
report. Splits are resolved via split groups (above).
|
|
279
|
+
|
|
280
|
+
### Reference ledgers
|
|
281
|
+
|
|
282
|
+
A ledger that intentionally **duplicates** rows already recorded in another one — a
|
|
283
|
+
project tracker mirroring shared spend, so the project's cost is visible in one
|
|
284
|
+
place — must never be summed with the primaries; that double-counts. Mark it in its
|
|
285
|
+
notes header:
|
|
286
|
+
|
|
287
|
+
```yaml
|
|
288
|
+
---
|
|
289
|
+
spltty:
|
|
290
|
+
reference: true
|
|
291
|
+
---
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Reference ledgers are flagged in per-ledger `totals`, **excluded** from `--combined`
|
|
295
|
+
(and listed as skipped), and refused in a cross-ledger `settle` — settle them on
|
|
296
|
+
their own in cash mode.
|
|
297
|
+
|
|
298
|
+
Each payment method may define an optional **`slug`** — a short alias you can pass
|
|
299
|
+
to `-m` instead of the full name. Both are matched case-insensitively, and the
|
|
300
|
+
row always stores the full canonical name:
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
"Visa 5678": { "slug": "visa-5678", "paid_by": "Bruno", "responsible": "Bruno", "bill_day": "last" }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Then `spltty add "Coffee" -l HOME -v 12 -m visa-5678` fills Paid By / Responsible /
|
|
307
|
+
Date from that card. If a method has no default for a field, interactive mode
|
|
308
|
+
prompts for it (and `--yes` errors). `spltty methods` lists every method with its
|
|
309
|
+
slug.
|
|
310
|
+
|
|
311
|
+
## Settling up
|
|
312
|
+
|
|
313
|
+
`spltty settle` **records** a settlement (the `Settlement:` line in `totals` is
|
|
314
|
+
only a suggestion). A settlement is written as a normal table row — a transfer
|
|
315
|
+
where **Paid By = debtor** and **Responsible = creditor** — so it flows through
|
|
316
|
+
the totals math and offsets the debt exactly. No table is ever hand-edited.
|
|
317
|
+
|
|
318
|
+
```sh
|
|
319
|
+
spltty settle HOME # cash-settle HOME in full (Pix row in the current month file)
|
|
320
|
+
spltty settle TRIP -v 500 -y # partial cash settle, non-interactive
|
|
321
|
+
spltty settle # global offset mode: zero opposite-direction ledgers, rest stays open
|
|
322
|
+
spltty settle -v 2000 # global: offsets + R$ 2000 cash, smallest ledger first
|
|
323
|
+
spltty settle HOME WEDDING # offset mode restricted to these two ledgers
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Two modes, chosen by how many ledgers you pass:
|
|
327
|
+
|
|
328
|
+
- **One ledger — cash mode.** A real payment for that ledger's outstanding
|
|
329
|
+
balance (or `-v` part of it, titled `(partial)`), never touching other
|
|
330
|
+
ledgers. Payment method defaults to `Pix` (`-m` overrides).
|
|
331
|
+
- **None/several ledgers — offset mode.** None = all non-reference ledgers.
|
|
332
|
+
Ledgers whose debt points *opposite* to the combined direction are zeroed with
|
|
333
|
+
mirrored rows (payment method `offset` — no real money moves): e.g. HOME has
|
|
334
|
+
Bruno owing Ana 7000 and WEDDING has Ana owing Bruno 5000 ⇒
|
|
335
|
+
WEDDING gets an offset row Ana → Bruno 5000, HOME the mirror
|
|
336
|
+
Bruno → Ana 5000, leaving 2000 open in HOME. Without `-v` that is all
|
|
337
|
+
that happens — the remainder **stays open** (with nothing to offset, it just
|
|
338
|
+
prints the outstanding and writes nothing). With `-v X`, X is then recorded
|
|
339
|
+
as a cash payment against the remainder. Offsets and cash are both allocated
|
|
340
|
+
**smallest-outstanding ledger first**.
|
|
341
|
+
|
|
342
|
+
Rows are stamped with today's date (`-d` overrides; a card method's bill day is
|
|
343
|
+
ignored) and `Source = settle` (`-s` overrides). The plan is previewed and
|
|
344
|
+
confirmed before writing (`-y` skips). Ledgers marked `reference: true` never join
|
|
345
|
+
offset mode — settle them on their own in cash mode.
|
|
346
|
+
|
|
347
|
+
## Tests
|
|
348
|
+
|
|
349
|
+
```sh
|
|
350
|
+
bundle exec rake test
|
|
351
|
+
```
|
data/config.example.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "Reference for .spltty/config.json — normally written by `spltty install`, not by hand. Only payment_methods really needs hand-authoring.",
|
|
3
|
+
|
|
4
|
+
"accounts_dir": "../accounts",
|
|
5
|
+
"//accounts_dir": "Ledger root, resolved relative to this config file. Override at runtime with --accounts-dir or SPLTTY_ACCOUNTS_DIR.",
|
|
6
|
+
|
|
7
|
+
"default_ledger": "expenses",
|
|
8
|
+
"date_format": "%Y-%m-%d",
|
|
9
|
+
|
|
10
|
+
"participants": ["Ana", "Bruno"],
|
|
11
|
+
"//participants": "Everyone who can appear in Paid By / Responsible. Recorded by `spltty install` and used to render the workspace CLAUDE.md; the split groups below decide how a cost is divided.",
|
|
12
|
+
|
|
13
|
+
"payment_methods": {
|
|
14
|
+
"//": "name -> defaults applied when --method matches (by full name or slug, case-insensitive). bill_day is the day the bill is paid; it fills the Date column ('last' = last day of month). slug is an optional shortcut you can pass to -m (e.g. -m visa-1234).",
|
|
15
|
+
"Visa 1234": { "slug": "visa-1234", "paid_by": "Ana", "responsible": "Both", "bill_day": "last" },
|
|
16
|
+
"Visa 5678": { "slug": "visa-5678", "paid_by": "Bruno", "responsible": "Both", "bill_day": 21 }
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
"ledgers": {
|
|
20
|
+
"//": "OPTIONAL and auto-maintained. Discovery scans accounts/ on every run and writes this block. type/file/dir/schema are set automatically; title, default_responsible, default_currency, reference, and notes are user overrides that are preserved.",
|
|
21
|
+
"//sync": "The tunable fields (title, default_responsible, default_currency, reference, groups) also live in each ledger's *.notes.md YAML header under `spltty:`, and are two-way synced by `spltty sync` (and by `spltty list`). synced_at is a per-ledger timestamp written by the sync: if the notes file's mtime is newer, the header wins and updates config; otherwise config is copied into the header. The notes file has precedence. Do not hand-edit synced_at.",
|
|
22
|
+
"//groups": "ledgers.<NAME>.groups holds that ledger's split groups (name -> {participant: pct}), mirrored from the header's spltty.groups. Manage with `spltty groups add NAME -s Ana:70,Bruno:30 -l <NAME>`.",
|
|
23
|
+
"//reference": "reference: true marks a ledger whose rows duplicate another ledger's. It is flagged in `totals`, excluded from --combined, and refused in a cross-ledger `settle`.",
|
|
24
|
+
"expenses": { "type": "monthly", "dir": "expenses", "schema": "standard", "notes": "notes.md", "default_responsible": "Both", "groups": { "Both": { "Ana": 70, "Bruno": 30 } }, "synced_at": "2026-07-05T12:00:00Z" },
|
|
25
|
+
"TRIP": { "type": "single", "file": "TRIP.ledger.md", "schema": "montreal", "notes": "TRIP.notes.md", "default_currency": "CAD", "synced_at": "2026-07-05T12:00:00Z" },
|
|
26
|
+
"RENOVATION": { "type": "single", "file": "RENOVATION.ledger.md", "schema": "standard", "notes": "RENOVATION.notes.md", "reference": true, "synced_at": "2026-07-05T12:00:00Z" }
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"groups": {
|
|
30
|
+
"//": "GLOBAL split groups, shared across ledgers. Auto-populated when the identical definition (name + participants + percentages) appears in >=2 ledger headers; also editable via `spltty groups add NAME -s ... --global`. Never written back into any header. A ledger's own group shadows a global one of the same name.",
|
|
31
|
+
"Both": { "Ana": 50, "Bruno": 50 }
|
|
32
|
+
}
|
|
33
|
+
}
|
data/exe/spltty
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Gem entrypoint. Also works straight from a checkout (`exe/spltty …`) by
|
|
5
|
+
# putting the sibling lib/ on the load path when it exists.
|
|
6
|
+
lib = File.expand_path("../lib", __dir__)
|
|
7
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
|
8
|
+
|
|
9
|
+
require "spltty_cli"
|
|
10
|
+
|
|
11
|
+
SplttyCLI.run(ARGV)
|