MVP w UI
parent
a901e41d25
commit
ea83759be2
22
ledger.go
22
ledger.go
|
|
@ -52,9 +52,17 @@ func (ledger Ledger) SetTransactions(transactions []Transaction) error {
|
||||||
return os.Rename(f.Name(), ledger.path)
|
return os.Rename(f.Name(), ledger.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ledger Ledger) LossyBalances() (Balances, error) {
|
||||||
|
return ledger.balances(true)
|
||||||
|
}
|
||||||
|
|
||||||
func (ledger Ledger) Balances() (Balances, error) {
|
func (ledger Ledger) Balances() (Balances, error) {
|
||||||
|
return ledger.balances(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ledger Ledger) balances(lossy bool) (Balances, error) {
|
||||||
transactions, err := ledger.Transactions()
|
transactions, err := ledger.Transactions()
|
||||||
if err != nil {
|
if !lossy && err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
balances := make(Balances)
|
balances := make(Balances)
|
||||||
|
|
@ -65,14 +73,22 @@ func (ledger Ledger) Balances() (Balances, error) {
|
||||||
return balances, nil
|
return balances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ledger Ledger) LossyTransactions() ([]Transaction, error) {
|
||||||
|
return ledger.transactions(true)
|
||||||
|
}
|
||||||
|
|
||||||
func (ledger Ledger) Transactions() ([]Transaction, error) {
|
func (ledger Ledger) Transactions() ([]Transaction, error) {
|
||||||
|
return ledger.transactions(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ledger Ledger) transactions(lossy bool) ([]Transaction, error) {
|
||||||
f, err := ledger.open()
|
f, err := ledger.open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
transactions := make([]Transaction, 0)
|
transactions := make([]Transaction, 0)
|
||||||
for err == nil {
|
for (!lossy && err == nil) || (lossy && err != io.EOF) {
|
||||||
var transaction Transaction
|
var transaction Transaction
|
||||||
transaction, err = readTransaction(f)
|
transaction, err = readTransaction(f)
|
||||||
if err == io.EOF {
|
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)
|
err = fmt.Errorf("error parsing transaction #%d: %v", len(transactions), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF || lossy {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
return transactions, err
|
return transactions, err
|
||||||
|
|
|
||||||
3
main.go
3
main.go
|
|
@ -9,6 +9,7 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
as := args.NewArgSet()
|
as := args.NewArgSet()
|
||||||
as.Append(args.INT, "p", "port to listen on", "8101")
|
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")
|
as.Append(args.STRING, "f", "file to abuse", "/tmp/ledger-ui.dat")
|
||||||
if err := as.Parse(); err != nil {
|
if err := as.Parse(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -17,7 +18,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,8 @@
|
||||||
}
|
}
|
||||||
zachsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Zach")
|
zachsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Zach")
|
||||||
belsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Bel")
|
belsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Bel")
|
||||||
zachsCharge = (x) => setRowKeyValue(x, "Payee", "DebtAccount:Zach")
|
zachsCharge = (x) => setRowKeyValue(x, "Payee", "AssetAccount:Zach")
|
||||||
belsCharge = (x) => setRowKeyValue(x, "belsCharge", "DebtAccount:Bel")
|
belsCharge = (x) => setRowKeyValue(x, "Payee", "AssetAccount:Bel")
|
||||||
function http(method, remote, callback, body) {
|
function http(method, remote, callback, body) {
|
||||||
var xmlhttp = new XMLHttpRequest();
|
var xmlhttp = new XMLHttpRequest();
|
||||||
xmlhttp.onreadystatechange = function() {
|
xmlhttp.onreadystatechange = function() {
|
||||||
|
|
|
||||||
36
server.go
36
server.go
|
|
@ -2,7 +2,9 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
@ -12,6 +14,7 @@ var index string
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
ledger Ledger
|
ledger Ledger
|
||||||
|
debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
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":
|
case "GET/api/balances":
|
||||||
server.getBalances(w, r)
|
server.getBalances(w, r)
|
||||||
default:
|
default:
|
||||||
|
if server.debug {
|
||||||
|
b, _ := ioutil.ReadFile("./public/index.html")
|
||||||
|
w.Write(b)
|
||||||
|
} else {
|
||||||
fmt.Fprint(w, index)
|
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) {
|
func (server Server) getTransactions(w http.ResponseWriter, r *http.Request) {
|
||||||
transactions, err := server.ledger.Transactions()
|
transactions, err := server.ledger.Transactions()
|
||||||
if err != nil {
|
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) {
|
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) {
|
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})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -75,16 +76,25 @@ func words(b []byte) [][]byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (transaction *Transaction) readDate(lines [][]byte) error {
|
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])
|
transaction.Date = string(words(lines[0])[0])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (transaction *Transaction) readDescription(lines [][]byte) error {
|
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(" ")))
|
transaction.Description = string(bytes.Join(words(lines[0])[1:], []byte(" ")))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (transaction *Transaction) readAmount(lines [][]byte) error {
|
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])
|
amount := string(words(lines[1])[1])
|
||||||
f, err := strconv.ParseFloat(strings.Trim(amount, "$"), 32)
|
f, err := strconv.ParseFloat(strings.Trim(amount, "$"), 32)
|
||||||
transaction.Amount = float32(f)
|
transaction.Amount = float32(f)
|
||||||
|
|
@ -92,6 +102,9 @@ func (transaction *Transaction) readAmount(lines [][]byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (transaction *Transaction) readPayerPayee(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])
|
payer := string(words(lines[1])[0])
|
||||||
payee := string(words(lines[2])[0])
|
payee := string(words(lines[2])[0])
|
||||||
if transaction.Amount >= 0 {
|
if transaction.Amount >= 0 {
|
||||||
|
|
@ -108,7 +121,7 @@ func (transaction *Transaction) readPayerPayee(lines [][]byte) error {
|
||||||
|
|
||||||
func (transaction Transaction) Marshal() string {
|
func (transaction Transaction) Marshal() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"%-25s%s\n%25s%-50s$%.2f\n%25s%s",
|
"%-25s%s\n%25s%-49s $%.2f\n%25s%s",
|
||||||
transaction.Date,
|
transaction.Date,
|
||||||
transaction.Description,
|
transaction.Description,
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue