diff options
| -rw-r--r-- | interact.go | 67 | ||||
| -rw-r--r-- | sqlite.go | 166 | ||||
| -rw-r--r-- | utils.go | 21 |
3 files changed, 230 insertions, 24 deletions
diff --git a/interact.go b/interact.go index ba4fa67..4aae3ac 100644 --- a/interact.go +++ b/interact.go @@ -72,6 +72,43 @@ func interact(fulldb bool) { c.Println(promptcol("______________________")) }, }) + + paycmd := &ishell.Cmd{ + Name: "payment", + Help: "<Date>/now - Enter a Payment",// at a specific Time 'YYYY-MM-DD' Or 'now'", + LongHelp: ` Usage: new payment <Date>/now + Enter a new Payment at <Date>. + Use Format "YYYY-MM-DD" for date. + 'now' will start the Task at current local date.`, + Func: func(c *ishell.Context) { + arg := "none" + if len(c.Args) > 0 { + arg = strings.Join(c.Args, " ") + if isDate(arg){ + AddPayment(arg) + }else{ + c.Println(boldRed("new payment <Date> - Please enter a valid Date of Format 'YYYY-MM-DD'")) + } + } else { + c.Println(boldRed("new payment <Date> - Please enter a Date")) + } + stdOut() + c.Println(promptcol("______________________")) + }, + } + paycmd.AddCmd(&ishell.Cmd{ + Name: "now", + Help: "Enter a new Payment dated today", + LongHelp: ` Usage: new payment now + Enter a new Payment with current local date`, + Func: func(c *ishell.Context) { + AddPayment("jetzt") + stdOut() + c.Println(promptcol("______________________")) + }, + }) + + newcmd.AddCmd(paycmd) shell.AddCmd(newcmd) } @@ -120,7 +157,7 @@ func interact(fulldb bool) { - + /* OLD CHECKBILL shell.AddCmd(&ishell.Cmd{ Name: "checkbill", Help: "<id> check a Bill with the following id as paid", @@ -144,6 +181,7 @@ func interact(fulldb bool) { c.Println(promptcol("______________________")) }, }) + */ // Delete Commands: Bill / Task { @@ -360,7 +398,7 @@ func interact(fulldb bool) { c.Println(boldRed(arg, "is not a valid id!")) } } else { - tids := GetBillIds() + tids := GetBillIds(false) selids, lids := GetBillList(tids) choice := c.MultiChoice(lids, "Select a Bill to Edit") //c.Println(tids) @@ -462,10 +500,10 @@ func interact(fulldb bool) { LongHelp: ` Usage: print bills Show all Projects with a small summary, sorted by Customer.`, Func: func(c *ishell.Context) { - tids := GetBillIds() + tids := GetBillIds(false) ids, str := GetBillList(tids) choices := c.Checklist(str, - "Which Tasks should be charged in the new bill ?", + "Which Bills should be Printed again ?", nil) out := func() (c []int) { for _, v := range choices { @@ -495,7 +533,6 @@ func interact(fulldb bool) { }else{ c.Println(boldRed("Charging Aborted")) } - c.Println(promptcol("______________________")) }, @@ -588,7 +625,7 @@ func interact(fulldb bool) { c.Println(boldRed(arg, "is not a valid integer!")) } } else { - tids := GetBillIds() + tids := GetBillIds(false) selids, lids := GetBillList(tids) choice := c.MultiChoice(lids, "Select a Bill to Edit") //c.Println(tids) @@ -977,12 +1014,12 @@ func interact(fulldb bool) { //c.ShowPrompt(true) } } - halfbill := bill{0, "None", dur, proj.Id, proj.Name, time.Time{}, time.Time{}, allitems} + halfbill := bill{0, "None", dur, proj.Id, proj.Name, time.Time{}, time.Time{}, allitems, 0} ShowBill(halfbill,false) if isInterSure("Is this bill Correct?") { billid, billident := newBill(billprojid) //c.Println(green("Bill Completed")) - fullbill := bill{billid, billident, dur, proj.Id, proj.Name, time.Time{}, time.Time{}, allitems} + fullbill := bill{billid, billident, dur, proj.Id, proj.Name, time.Time{}, time.Time{}, allitems, 0} saveBill(fullbill) checkTasks(selids, billid) c.ProgressBar().Indeterminate(true) @@ -1177,3 +1214,17 @@ func Multichoice(question string,list []string) (int) { shell.Process(str) shell.Println(about)*/ } + +// Invoke a Checklist Question with a leading Question +// line and return an int slice of the selected rows of the list +func Checklist(question string, list []string) ([]int) { + shell := ishell.New() + marker := li+li+">" + shell.SetMultiChoicePrompt(marker,nli) + shell.SetChecklistOptions("[ ] ", "[X] ") + qu1 := line(marker,false) + qu2 := frame(question,true) + quest := qu1 + strings.TrimLeft(qu2,"\n") + choices := shell.Checklist(list,quest,nil) + return choices +} @@ -47,6 +47,15 @@ type Customer struct { Lastbill time.Time // Last time a bill was paid } +// Datatype for SQL payments +type Payment struct { + Id int + Customerid int + Amount float64 + Date time.Time + Billids []int +} + type billitem struct { Task string Time string @@ -63,6 +72,7 @@ type bill struct { date time.Time paid time.Time items []billitem + paymentid int } // Useful to Map some old ids to New ones @@ -120,7 +130,14 @@ func initDB(filename string) { hours VARCHAR(240), moneys VARCHAR(240), paid TIMESTAMP DEFAULT '1791-09-30 19:07', - date TIMESTAMP DEFAULT '1791-09-30 19:07' ); + date TIMESTAMP DEFAULT '1791-09-30 19:07', + paymentid INTEGER DEFAULT 0); + CREATE TABLE payments( + id INTEGER PRIMARY KEY AUTOINCREMENT, + customerid INTEGER DEFAULT NULL, + amount REAL DEFAULT 0, + bills VARCHAR(240), + date TIMESTAMP DEFAULT '1791-09-30 19:07' ); CREATE TABLE vars( id INTEGER PRIMARY KEY AUTOINCREMENT, pauseid INTEGER DEFAULT NULL, @@ -389,7 +406,7 @@ func showLastBills(count int) { rows, err = db.Query("SELECT * FROM bills ORDER BY date DESC LIMIT ?", count) } checkErr(err) - var id, proj int + var id, proj, payid int var ident, timerange string var date, paid time.Time var taskstr, timestr, hourstr, moneystr string @@ -406,16 +423,16 @@ func showLastBills(count int) { i := 0 for rows.Next() { i++ - err = rows.Scan(&id, &ident, &timerange, &proj, &taskstr, ×tr, &hourstr, &moneystr, &paid, &date) + err = rows.Scan(&id, &ident, &timerange, &proj, &taskstr, ×tr, &hourstr, &moneystr, &paid, &date, &payid) checkErr(err) prn, _ := getProjectName(proj) hsum := sumFloatArray(string2FloatArray(hourstr, ";")) msum := sumFloatArray(string2FloatArray(moneystr, ";")) fmt.Printf("%s %v:%s - %s (%v) %.1f[h]: %.2f[€] - ",nli, id, ident, prn, date.Local().Format("2006.01.02"), hsum, msum) - p := fmt.Sprintf("%v", paid.Local().Format("2006.01.02")) + //p := fmt.Sprintf("%v", paid.Local().Format("2006.01.02")) //fmt.Println(p) - //if p == "1791-09-30 19:07:00 +0000 UTC" { - if p == "1791.09.30" { + //if p == "1791.09.30" { + if payid == 0 { fmt.Print(boldRed("OPEN\n")) } else { fmt.Printf(boldGreen("%v\n"), paid.Local().Format("2006.01.02")) @@ -475,18 +492,18 @@ func loadBills(in []int) (out []bill) { que := fmt.Sprintf("SELECT * FROM bills WHERE id IN (%s) ORDER BY project DESC", ins) rows, err := db.Query(que) - var id, proj int + var id, proj, payid int var ident, timerange string var date, paid time.Time var taskstr, timestr, hourstr, moneystr string defer rows.Close() for rows.Next() { - err = rows.Scan(&id, &ident, &timerange, &proj, &taskstr, ×tr, &hourstr, &moneystr, &paid, &date) + err = rows.Scan(&id, &ident, &timerange, &proj, &taskstr, ×tr, &hourstr, &moneystr, &paid, &date, &payid) checkErr(err) itms := strings2items(taskstr, timestr, hourstr, moneystr) prname, _ := getProjectName(proj) - bi := bill{id, ident, timerange, proj, prname, date, paid, itms} + bi := bill{id, ident, timerange, proj, prname, date, paid, itms, payid} out = append(out, bi) } return @@ -505,6 +522,27 @@ func SaveCustomer(in Customer) (int) { return int(newid) } +// Save Payment in DB and return its new id +func SavePayment(in Payment) (int) { + tila := in.Date.Local().Format("2006-01-02 15:04") + billids := IntArray2String(in.Billids,";") + if in.Id == 0 { + stmt, err := db.Prepare("INSERT INTO payments (customerid, amount ,bills ,date) values(?, ?, ?, ?)") + checkErr(err) + res, err := stmt.Exec(in.Customerid,in.Amount,billids,tila) + checkErr(err) + newid, err := res.LastInsertId() + checkErr(err) + return int(newid) + } else { + stmt, err := db.Prepare("UPDATE payments SET customerid = ? , amount = ? , bills = ? , date = ? ) WHERE id = ?") + checkErr(err) + _, err = stmt.Exec(in.Customerid,in.Amount,billids,tila,in.Id) + checkErr(err) + return in.Id + } +} + // Save Project in DB and return its new id func SaveProject(in Project) (int) { tifi := in.First.Local().Format("2006-01-02 15:04") @@ -784,7 +822,8 @@ func closeTask(loud bool) { } } } - +//TODO Adapt Function to Save paymentid Or dump it completely +// TODO Make an uncheck function func checkBill(bid int) { boldGreen := color.New(color.FgGreen, color.Bold).SprintFunc() boldRed := color.New(color.FgRed, color.Bold).SprintFunc() @@ -1229,10 +1268,15 @@ func GetTaskIds() []int { return ids } -// Returns slice of ids of all Bills -func GetBillIds() []int { - var ids []int +// Returns slice of ids of Bills if parameter open is true only open bills are included +func GetBillIds(open bool) []int { rows, err := db.Query("SELECT id FROM bills") // ORDER BY id DESC") + var ids []int + if open{ + rows, err = db.Query("SELECT id FROM bills WHERE paymentid == 0") // ORDER BY id DESC") + }else{ + rows, err = db.Query("SELECT id FROM bills") // ORDER BY id DESC") + } checkErr(err) var id int @@ -1250,7 +1294,7 @@ func GetAllBills() (out []bill) { //var ids []int rows, err := db.Query("SELECT * FROM bills") // ORDER BY id DESC") checkErr(err) - var id,prj int + var id,prj,payid int var ide,trg,prn string var dat,pai time.Time var itms []billitem @@ -1262,7 +1306,7 @@ func GetAllBills() (out []bill) { checkErr(err) prn,_ = getProjectName(id) itms = strings2items(tsks,tims,hrs,mos) - out = append(out, bill{id,ide,trg,prj,prn,dat,pai,itms}) + out = append(out, bill{id,ide,trg,prj,prn,dat,pai,itms,payid}) } return } @@ -1418,6 +1462,96 @@ func showOpenProject(alone bool) { //fmt.Println(frame("Current Project",true)) } +// Unser Input to create a new Payment +func AddPayment(dat string) { + //TODO + boldRed := color.New(color.FgRed, color.Bold).SprintFunc() + boldGreen := color.New(color.FgGreen, color.Bold).SprintFunc() + const form = "2006-01-02" + date,_ := time.Parse(form,getDate(dat)) + if dat == "jetzt" { + date = time.Now().Local()//.Format("2006-01-02") + } + fmt.Println(frame(boldGreen("Adding new Payment"),true)) + amtfl := 0.0 + for{ + amtstr := getInterInput(sli+"Enter Amount: ") + amtfl, err = strconv.ParseFloat(amtstr, 64) + if err != nil { + fmt.Println(nli,amtstr, boldRed("can not be Parsed as a Float."), "Try a shape of X.X") + } else { + break + } + } + allCustomers(true) + icust := 0 + for { + cust := getInterInput(sli+"Enter Customer id: ") + icust, err = strconv.Atoi(cust) + if err == nil && (isCustomer(icust) || icust == 0) { + break + } else { + fmt.Println(nli,cust, boldRed("is an invalid ID or Not a known Customer")) + } + } + billids := GetBillIds(true) + listids,billlist := GetBillList(billids) + var selids []int + for{ + res := Checklist("What Bills are includes in this Payment",billlist) + out := func() (c []int) { + for _,v := range res { + if v > -1 { + c = append(c, listids[v]) + } + } + return + } + selids = out() + if len(selids)>0{ + break + }else{ + fmt.Println(boldRed("At least one Bill should Correspont to a payment!")) + getInterInput("<continue>") + } + } + mybills := loadBills(selids) + sum := 0.0 + for _,bill := range mybills { + for _,it := range bill.items { + sum += it.Money + } + } + + fmt.Println(frame(boldGreen("New Payment"),true)) + fmt.Println(nli," Amount:",amtfl) + fmt.Println(nli," Date:",date.Format("2006-01-02")) + fmt.Println(nli,"Customer:",getCustomerName(icust)) + fmt.Println(sub("Invoices")) + for _,bi := range mybills{ + bsum := 0.0 + for _,it := range bi.items { + bsum += it.Money + } + fmt.Printf("%s %s : %s (%s) %.2f[€]\n",nli,bi.identity,bi.projectname,bi.date.Format("2006-01-02 15:04"),bsum) + } + if amtfl != sum { + fmt.Println(sli,boldRed(" The sum of the billsi (",sum,"€) does not match Payment amount!")) + if amtfl > sum { + fmt.Printf("%s %.2f € Overpaid\n",nli,(amtfl-sum) ) + } + if amtfl < sum { + fmt.Printf("%s %.2f € Underpaid\n",nli,(amtfl-sum) ) + } + } + if isInterSure(sli+"Do you want to continue?") { + paym := Payment{0,icust,amtfl,date,selids} + SavePayment(paym) + fmt.Println(frame(posR(),false)) + }else{ + fmt.Println(frame("Closing payment",false)) + }//fmt.Println(paym) +} func addCustomer() { boldRed := color.New(color.FgRed, color.Bold).SprintFunc() boldGreen := color.New(color.FgGreen, color.Bold).SprintFunc() @@ -2204,7 +2338,7 @@ func isCustomer(id int) bool { return false } } - +//TODO Change get Funcs to time.Parse() func getDateTime(in string) string { r := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})`) //r := regexp.MustCompile(`(\d{4})-(((0)[0-9])|((1)[0-2]))-([0-2][0-9]|(3)[0-1]) ([01]?[0-9]|2[0-3]):[0-5][0-9]`) @@ -223,6 +223,27 @@ func stringArray2String(in []string, delim string) string { return out } +func IntArray2String(in []int, delim string) (out string) { + for i,a := range in { + if i == 0 { + out = fmt.Sprintf("%v",a) + } else { + out = fmt.Sprintf("%s%s%v", out, delim, a) + } + } + return +} + +func String2IntArray(in, delim string) (out []int) { + read := strings.Split(in, delim) + for _, s := range read { + is, err := strconv.Atoi(s) + checkErr(err) + out = append(out, is) + } + return +} + func string2FloatArray(in string, delim string) (out []float64) { read := strings.Split(in, delim) for _, s := range read { |
