master
bel 2021-08-03 21:32:09 -06:00
parent a901e41d25
commit ea83759be2
6 changed files with 94 additions and 9 deletions

View File

@ -52,9 +52,17 @@ func (ledger Ledger) SetTransactions(transactions []Transaction) error {
return os.Rename(f.Name(), ledger.path)
}
func (ledger Ledger) LossyBalances() (Balances, error) {
return ledger.balances(true)
}
func (ledger Ledger) Balances() (Balances, error) {
return ledger.balances(false)
}
func (ledger Ledger) balances(lossy bool) (Balances, error) {
transactions, err := ledger.Transactions()
if err != nil {
if !lossy && err != nil {
return nil, err
}
balances := make(Balances)
@ -65,14 +73,22 @@ func (ledger Ledger) Balances() (Balances, error) {
return balances, nil
}
func (ledger Ledger) LossyTransactions() ([]Transaction, error) {
return ledger.transactions(true)
}
func (ledger Ledger) Transactions() ([]Transaction, error) {
return ledger.transactions(false)
}
func (ledger Ledger) transactions(lossy bool) ([]Transaction, error) {
f, err := ledger.open()
if err != nil {
return nil, err
}
defer f.Close()
transactions := make([]Transaction, 0)
for err == nil {
for (!lossy && err == nil) || (lossy && err != io.EOF) {
var transaction Transaction
transaction, err = readTransaction(f)
if err == io.EOF {
@ -82,7 +98,7 @@ func (ledger Ledger) Transactions() ([]Transaction, error) {
err = fmt.Errorf("error parsing transaction #%d: %v", len(transactions), err)
}
}
if err == io.EOF {
if err == io.EOF || lossy {
err = nil
}
return transactions, err

View File

@ -9,6 +9,7 @@ import (
func main() {
as := args.NewArgSet()
as.Append(args.INT, "p", "port to listen on", "8101")
as.Append(args.BOOL, "debug", "debug mode", false)
as.Append(args.STRING, "f", "file to abuse", "/tmp/ledger-ui.dat")
if err := as.Parse(); err != nil {
panic(err)
@ -17,7 +18,7 @@ func main() {
if err != nil {
panic(err)
}
if err := http.ListenAndServe(":"+fmt.Sprint(as.GetInt("p")), Server{ledger: ledger}); err != nil {
if err := http.ListenAndServe(":"+fmt.Sprint(as.GetInt("p")), Server{ledger: ledger, debug: as.GetBool("debug")}); err != nil {
panic(err)
}
}

View File

@ -113,8 +113,8 @@
}
zachsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Zach")
belsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Bel")
zachsCharge = (x) => setRowKeyValue(x, "Payee", "DebtAccount:Zach")
belsCharge = (x) => setRowKeyValue(x, "belsCharge", "DebtAccount:Bel")
zachsCharge = (x) => setRowKeyValue(x, "Payee", "AssetAccount:Zach")
belsCharge = (x) => setRowKeyValue(x, "Payee", "AssetAccount:Bel")
function http(method, remote, callback, body) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {

View File

@ -2,7 +2,9 @@ package main
import (
_ "embed"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
@ -12,6 +14,7 @@ var index string
type Server struct {
ledger Ledger
debug bool
}
func (server Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -24,19 +27,50 @@ func (server Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "GET/api/balances":
server.getBalances(w, r)
default:
fmt.Fprint(w, index)
if server.debug {
b, _ := ioutil.ReadFile("./public/index.html")
w.Write(b)
} else {
fmt.Fprint(w, index)
}
}
}
func (server Server) err(w http.ResponseWriter, r *http.Request, err error) {
log.Println(r.Method, r.URL.Path, err)
http.Error(w, fmt.Sprint(err.Error()), http.StatusInternalServerError)
}
func (server Server) getTransactions(w http.ResponseWriter, r *http.Request) {
transactions, err := server.ledger.Transactions()
if err != nil {
server.err(w, r, err)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{"Transactions": transactions})
}
func (server Server) putTransactions(w http.ResponseWriter, r *http.Request) {
var request struct {
IDX int `json:"idx"`
Transaction
}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
server.err(w, r, err)
return
}
if err := server.ledger.SetTransaction(request.IDX, request.Transaction); err != nil {
server.err(w, r, err)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
}
func (server Server) getBalances(w http.ResponseWriter, r *http.Request) {
balances, err := server.ledger.Balances()
if err != nil {
server.err(w, r, err)
return
}
json.NewEncoder(w).Encode(map[string]interface{}{"Balances": balances})
}

21
testdata/ledger-groceries-custom.dat vendored Normal file
View File

@ -0,0 +1,21 @@
2021-01-01 groceries
Withdrawal:Target $10
AssetAccount:Chase:8824
2021-01-05 bel vidya
Withdrawal:Target $1
AssetAccount:Chase:8824
2021-01-06 qt vidya
Withdrawal:Target $100
AssetAccount:Chase:8824
2021-01-07 bel payment
AssetAccount:Chase:8824 $1
AssetAccount:Payment
2021-01-08 qt payment
AssetAccount:Chase:8824 $100
AssetAccount:Payment
2021-01-09 bel grocery payment
AssetAccount:Chase:8824 $5
AssetAccount:Payment
2021-01-10 qt grocery payment
AssetAccount:Chase:8824 $5
AssetAccount:Payment

View File

@ -3,6 +3,7 @@ package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"strconv"
@ -75,16 +76,25 @@ func words(b []byte) [][]byte {
}
func (transaction *Transaction) readDate(lines [][]byte) error {
if len(lines) < 1 || len(words(lines[0])) < 1 {
return errors.New("stub")
}
transaction.Date = string(words(lines[0])[0])
return nil
}
func (transaction *Transaction) readDescription(lines [][]byte) error {
if len(lines) < 1 || len(words(lines[0])) < 1 {
return errors.New("stub")
}
transaction.Description = string(bytes.Join(words(lines[0])[1:], []byte(" ")))
return nil
}
func (transaction *Transaction) readAmount(lines [][]byte) error {
if len(lines) < 2 || len(words(lines[1])) < 2 {
return errors.New("stub")
}
amount := string(words(lines[1])[1])
f, err := strconv.ParseFloat(strings.Trim(amount, "$"), 32)
transaction.Amount = float32(f)
@ -92,6 +102,9 @@ func (transaction *Transaction) readAmount(lines [][]byte) error {
}
func (transaction *Transaction) readPayerPayee(lines [][]byte) error {
if len(lines) < 3 || len(words(lines[1])) < 1 || len(words(lines[2])) < 1 {
return errors.New("stub")
}
payer := string(words(lines[1])[0])
payee := string(words(lines[2])[0])
if transaction.Amount >= 0 {
@ -108,7 +121,7 @@ func (transaction *Transaction) readPayerPayee(lines [][]byte) error {
func (transaction Transaction) Marshal() string {
return fmt.Sprintf(
"%-25s%s\n%25s%-50s$%.2f\n%25s%s",
"%-25s%s\n%25s%-49s $%.2f\n%25s%s",
transaction.Date,
transaction.Description,
"",