From 4a4f372605d56f2241699da6a5bf8dae7eda2b86 Mon Sep 17 00:00:00 2001 From: Nikolaus Gotsche Date: Wed, 5 Sep 2018 04:10:54 +0200 Subject: TOML Configuration added. texification working. outsourced some functions to my utilitys. billed tasks not yet checked --- config.go | 129 +++++++++++++++++++++++++++++++++++++++++++++++++ interact.go | 82 +++++++++++++++++++++++++++---- main.go | 11 ++++- sqlite.go | 96 +++++++++++++++++++++++++++++++++--- templates/_invoice.tex | 2 +- templates/_main.tex | 4 +- texify.go | 101 +++++++++++++++++++++++++++++++++++++- utils.go | 97 +++++++++++++++++++++++++++++++++++++ 8 files changed, 498 insertions(+), 24 deletions(-) create mode 100644 config.go create mode 100644 utils.go diff --git a/config.go b/config.go new file mode 100644 index 0000000..e03a196 --- /dev/null +++ b/config.go @@ -0,0 +1,129 @@ +package main + +import ( + "bytes" + "fmt" + //"os" + "io/ioutil" + //"strconv" + "github.com/BurntSushi/toml" +) + +type Config struct { + Database string //`toml:"database"` + Name string //`toml:"name"` + Street string //`toml:"street"` + Zip string //`toml:"zip"` + City string //`toml:"city"` + Country string //`toml:"country"` + Telefon string //`toml:"telefon"` + Mobile string //`toml:"mobile"` + Mail string //`toml:"mail"` + Url string //`toml:"url"` + Taxid string //`toml:"taxid"` + Bankacc string //`toml:"bankacc"` + Banklz string //`toml:"banklz"` + Bankname string //`toml:"bankname"` + Iban string //`toml:"iban"` + Bic string //`toml:"bic"` +} + +var conffile string +var config Config + +func init() { + conffile = "laboravi.config.toml" +} + +func editConf() { + for { + if _, err := toml.DecodeFile(conffile, &config); err != nil { + //fmt.Println(err) + fmt.Println("Configuration file not found") + makeNewTOML(false) + }else{ + showConfig() + if isSure("Is this Configuration Correct?") { + break + }else{ + //if isSure("Enter new Configuration?"){ + makeNewTOML(true) + //}else{ + // fmt.Println("I Really Dont think I can Help You then...!") + //} + } + } + } +} + +func initConf() { + for { + if _, err := toml.DecodeFile(conffile, &config); err != nil { + //fmt.Println(err) + fmt.Println("Configuration file not found") + makeNewTOML(false) + }else{ + fmt.Println("Configuration loaded Successfully") + break + } + } +} + +func makeNewTOML(gibts bool) { + if gibts { + fmt.Println("Editing Configuration File\nPress [Enter] to keep the Current setting") + }else{ + fmt.Println("Making new Configuration File") + + //arr := []string{"", "", ""} + //ma0 := MailAccount{"","","",0,"",""} + //mm0 := MassMail{"","","",arr,""} + //Examplenames Coming soon + n := "" + config = Config{"mytimes.db",n,n,n,n,n,n,n,n,n,n,n,n,n,n,n} + } + //fmt.Println("Whats Your Namecount\n") + database := getNewInput("DB File Path: ",config.Database) + name := getNewInput("Whats your Name?: ",config.Name) + street := getNewInput("Street: ",config.Street) + zip := getNewInput("Zip Code: ",config.Zip) + city := getNewInput("City: ",config.City) + country := getNewInput("Country: ",config.City) + telefon := getNewInput("Telefone number: ",config.Telefon) + mobile := getNewInput("Mobile number: ",config.Mobile) + mail := getNewInput("Email: ",config.Mail) + url := getNewInput("URL: ",config.Url) + taxid := getNewInput("Tax ID: ",config.Taxid) + bankname := getNewInput("Bank Name: ",config.Bankname) + bankacc := getNewInput("Bank Account: ",config.Bankacc) + banklz := getNewInput("BLZ: ",config.Banklz) + iban := getNewInput("IBAN: ",config.Iban) + bic := getNewInput("BIC: ",config.Bic) + + conf := Config{database,name,street,zip,city,country,telefon,mobile,mail,url,taxid,bankacc,banklz,bankname,iban,bic} + + buf := new(bytes.Buffer) + err = toml.NewEncoder(buf).Encode(conf) + if err != nil { + panic(err) + }else{ + //fmt.Println("Conffile:\n",buf.String()) + fmt.Println("Writing Configuration File") + err = ioutil.WriteFile(conffile, buf.Bytes(), 0644) + checkErr(err) + } + +} + +func showConfig() { + buf := new(bytes.Buffer) + err := toml.NewEncoder(buf).Encode(config) + if err != nil { + panic(err) + }else{ + fmt.Println("Configuration:\n",buf.String()) + //fmt.Println(config) + } +} + + diff --git a/interact.go b/interact.go index f555b55..918b2a9 100644 --- a/interact.go +++ b/interact.go @@ -5,6 +5,7 @@ import ( "strings" "strconv" "fmt" + "time" "github.com/abiosoft/ishell" "github.com/fatih/color" @@ -34,7 +35,27 @@ func interact() { showLastProject() }, }) - + + shell.AddCmd(&ishell.Cmd{ + Name: "config", + Help: "View and Edit Configuration", + Func: func(c *ishell.Context) { + //c.Print("\033[H\033[2J") + //c.Println(boldGreen("Start New Project")) + editConf() + }, + }) + + shell.AddCmd(&ishell.Cmd{ + Name: "show", + Help: "Show Current State", + Func: func(c *ishell.Context) { + //c.Print("\033[H\033[2J") + //c.Println(boldGreen("Start New Project")) + stdOut() + }, + }) + shell.AddCmd(&ishell.Cmd{ Name: "add", Help: "Add new Customer", @@ -319,19 +340,22 @@ func interact() { //c.Printf("%v Tasks Selected. Totaling %.2f (h) - Dates: %s\n",count,hours,dur) proj,cust := getProject(billprojid) - billid := newBill(billprojid) + billid,billident := newBill(billprojid) //prs,cus := getProjectName(billprojid) - c.Println("For",cust.company,"-",cust.name) - c.Println("Project:",proj.name,"- ID:",proj.id) - c.Println("Projected Income:",hours*cust.satz,"€") + //c.Println("For",cust.company,"-",cust.name) + //c.Println("Project:",proj.name,"- ID:",proj.id) + //c.Println("Projected Income:",hours*cust.satz,"€") - c.Println("Create New Bill:",billid) - c.ReadLine() //Make NEW BILL WITH ID and INV No + //c.Println("Create New Bill:",billid) + //c.ReadLine() //Make NEW BILL WITH ID and INV No + c.ShowPrompt(false) - fullbillinfo := fmt.Sprintf("For: %s - %s - Invoice: %s\nProject: %s - Id:%v\n%v Tasks Selected. Totaling %.2f (h) - Dates: %s\nProjected Income: %.2f(€)\n",cust.company,cust.name,billid,proj.name,proj.id,count,hours,dur,hours*cust.satz) + fullbillinfo := fmt.Sprintf("For: %s - %s - %.2f(€/h) - Invoice: %s Id:%v\nProject: %s - Id:%v\n%v Tasks Selected. Totaling %.2f (h) - Dates: %s\nProjected Income: %.2f(€)\n",cust.company,cust.name,cust.satz,billident,billid,proj.name,proj.id,count,hours,dur,hours*cust.satz) sep := "-------------------------\n" restinfo := "Here some Info about the rest" restids := selids + var allitems []billitem + //allaccounted := false //resttasks := seltasks //SELECT SUBSET AND NAME BILLITEM @@ -340,8 +364,12 @@ func interact() { if len(restids) == 0 { break } + resttasks := getSelectedTasks(restids) + rcount,rhours,_ := analyzeTasks(resttasks) + rids,rstr := getTaskList(restids,false) - restinfo = "Select Tasks to Group as Billitem" + qu := "Select Tasks to Group as Billitem" + restinfo = fmt.Sprintf("%v Tasks Left, Total %.2f(h)\n%s",rcount,rhours,qu) pre := fmt.Sprintf("%s%s%s",fullbillinfo,sep,restinfo) choices2 := c.Checklist(rstr,pre,nil) @@ -353,9 +381,43 @@ func interact() { } taskids := out() restids = removeItems(restids,taskids) - c.Println("Full",selids,"Your choices are",taskids,"Rest:",removeItems(selids,taskids)) + + if len(taskids)>0 { + ittasks := getSelectedTasks(taskids) + itcount,ithours,itdur := analyzeTasks(ittasks) + c.Printf("\n%v Tasks Selected, Total %.2f(h), Date: %s\n",itcount,ithours,itdur) + c.ShowPrompt(false) + c.Print("Name your Task for the Bill: ") + tsk := c.ReadLine() + var hrf float64 + for { + c.Print("How Many Hours: ") + hr := c.ReadLine() + hrf,err = strconv.ParseFloat(hr,64) + if err != nil { + c.Println(hr,boldRed("can not be Parsed as a Float."),"Try a shape of X.X") + }else{break} + } + c.Printf("%T %v - %T %v\n",tsk,tsk,hrf,hrf) + allitems = append(allitems,billitem{tsk,itdur,hrf,hrf*cust.satz}) + c.Print("<>") + c.ReadLine() + //c.ShowPrompt(true) + } } + c.Println(green("Bill Completed")) + fullbill := bill{billid,billident,dur,proj.id,proj.name,time.Time{},time.Time{},allitems} + saveBill(fullbill) + testid := []int{billid} + testbill := loadBills(testid) + //c.Println(testbill[0].projectname,testbill[0].items) + files := billTemplate(testbill[0],cust) + c.Println(files) + c.Print("<>") + c.ReadLine() + stdOut() } + c.ShowPrompt(true) }, }) diff --git a/main.go b/main.go index 68a2397..695d0de 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ var starttime,stoptime string var projectid, edittaskid, editprojectid, editcustomerid int var deltask int var addcustomer bool -var newproject, newtask, stoptask, allproj, runinter, test bool +var newproject, newtask, stoptask, allproj, runinter, test, newconfig bool //var red, green, yellow, cyan color //var boldRed, boldGreen color @@ -37,6 +37,10 @@ func init() { "Test Some Functions") + flag.BoolVar(&newconfig, + "config", + false, + "View and Edit Configuration") flag.BoolVar(&addcustomer, "addcustomer", false, @@ -111,6 +115,7 @@ func stdOut() { func main() { fmt.Println("Laboravi Started") + initConf() dbname := "./.mytimes.db" if len(flag.Args())>0 { @@ -154,7 +159,9 @@ func main() { if runinter { interact() } - + if newconfig { + editConf() + } if newproject { newProject() os.Exit(0) diff --git a/sqlite.go b/sqlite.go index 79ef4a7..f51a459 100644 --- a/sqlite.go +++ b/sqlite.go @@ -5,7 +5,7 @@ import ( "fmt" "time" "os" - "bufio" + //"bufio" "strings" "regexp" "strconv" @@ -42,6 +42,24 @@ type customer struct{ lastbill time.Time } +type billitem struct{ + Task string + Time string + Hours float64 + Money float64 +} + +type bill struct{ + id int + identity string //invoice number + timerange string + project int + projectname string + date time.Time + paid time.Time + items []billitem +} + var db *sql.DB var err error var currproject project @@ -160,7 +178,7 @@ func newTask(proj int) { } } -func newBill(proj int) (int) { +func newBill(proj int) (int,string) { boldGreen := color.New(color.FgGreen, color.Bold).SprintFunc() //boldRed := color.New(color.FgRed, color.Bold).SprintFunc() fmt.Println(boldGreen("Creating New Bill")) @@ -173,7 +191,65 @@ func newBill(proj int) (int) { checkErr(err) lid,_ := answ.LastInsertId() fmt.Println("Bill",invno,"Created with ID",lid) - return int(lid) + return int(lid), invno +} + +func saveBill(in bill) { + boldGreen := color.New(color.FgGreen, color.Bold).SprintFunc() + tasks,times,hours,moneys := items2strings(in.items) + fmt.Println(boldGreen("Saving Bill"),in.id) + stmt, err := db.Prepare("UPDATE bills SET identity = ?, timerange = ?, tasks = ?, times = ?, hours = ?, moneys = ? WHERE id = ?") + checkErr(err) + _, err = stmt.Exec(in.identity,in.timerange,tasks,times,hours,moneys,in.id) + checkErr(err) +} + +func loadBills(in []int) (out []bill){ + ins := strings.Trim(strings.Replace(fmt.Sprint(in)," "," , ",-1),"[]") + 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 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) + checkErr(err) + itms := strings2items(taskstr,timestr,hourstr,moneystr) + prname,_ := getProjectName(proj) + bi := bill{id,ident,timerange,proj,prname,date,paid,itms} + out = append(out,bi) + } + return +} +func strings2items(tasks, times, hours, moneys string) (out []billitem) { + ta := string2StringArray(tasks,";") + ti := string2StringArray(times,";") + ho := string2FloatArray(hours,";") + mo := string2FloatArray(moneys,";") + for i,_ := range ta{ + out = append(out,billitem{ta[i],ti[i],ho[i],mo[i]}) + } + return +} + +func items2strings(in []billitem) (tasks, times, hours, moneys string) { + var tsk,tim []string + var hrs,mny []float64 + for _,item := range in { + tsk = append(tsk,item.Task) + tim = append(tim,item.Time) + hrs = append(hrs,item.Hours) + mny = append(mny,item.Money) + } + tasks = stringArray2String(tsk,";") + times = stringArray2String(tim,";") + hours = strings.Trim(strings.Replace(fmt.Sprint(hrs)," ",";",-1),"[]") + moneys = strings.Trim(strings.Replace(fmt.Sprint(mny)," ",";",-1),"[]") + return } func closeTaskTime(tim string) { @@ -385,7 +461,8 @@ func analyzeTasks(in []task) (count int, hours float64, duration string) { } //txt := fmt.Sprintf("%s - (%v) - %.2f h",task, durstr, dur) } - duration = fmt.Sprintf("%v - %v",lstart.Local().Format("01.02.2006"),hstop.Local().Format("01.02.2006")) + duration = fmt.Sprintf("%v - %v",lstart.Local().Format("01.02."),hstop.Local().Format("01.02.2006")) + //duration = fmt.Sprintf("%v - %v",lstart.Local().Format("01.02.2006"),hstop.Local().Format("01.02.2006")) return } @@ -728,7 +805,7 @@ func deleteTask(id int) { fmt.Println(boldGreen("Delete Task", id)) dur := float64(stop.Sub(start))/(1000000000*60*60) fmt.Printf("%v: %v (%v-%v) - %.2f h\n",prj,task,start.Local().Format("2006 Mon Jan _2 15:04"),stop.Local().Format("15:04"),dur) - if isSure() { + if isSure("Are You Sure?") { stmt, err := db.Prepare("DELETE FROM timetable WHERE id = ?") checkErr(err) _, err = stmt.Exec(id) @@ -991,6 +1068,7 @@ func isTime(in string) bool { return match } +/* func getInput(quest string) string { fmt.Print(quest) in := bufio.NewReader(os.Stdin) @@ -998,8 +1076,9 @@ func getInput(quest string) string { line = strings.TrimSuffix(line, "\n") checkErr(err) return line -} +}*/ +/* func isSure() bool { fmt.Print("Are You Sure ? (type 'yes' to confirm) : ") in := bufio.NewReader(os.Stdin) @@ -1012,7 +1091,7 @@ func isSure() bool { } else { return false } -} +}*/ func isElement(some int, group []int) (bool) { for _,e := range group { @@ -1033,8 +1112,9 @@ func removeItems(master, rem []int) ([]int) { return out } +/* func checkErr(err error) { if err != nil { panic(err) } -} +}*/ diff --git a/templates/_invoice.tex b/templates/_invoice.tex index ae899fc..cb1d9f7 100644 --- a/templates/_invoice.tex +++ b/templates/_invoice.tex @@ -1,6 +1,6 @@ \ProjectTitle{EinBeliebeigerTitel} % Muss gesetzt bleiben, scheint nirgends auf [[range .]] -\FeeTwo{[[.Task]]}{[[.Timeframe]]}{[[.Hours]]}{[[.Price]]} +\FeeTwo{[[.Task]]}{[[.Time]]}{[[.Hours]]}{[[.Money]]} [[end]] % Auslagen diff --git a/templates/_main.tex b/templates/_main.tex index b07b221..8fd1eb2 100644 --- a/templates/_main.tex +++ b/templates/_main.tex @@ -15,7 +15,7 @@ \renewcommand{\familydefault}{\sfdefault} % include meta data -\include{_data} +\include{[[.Data]]} \setkomavar{fromname}{\senderName} \setkomavar{fromaddress}{\senderStreet \\ \senderZIP \ \senderCity} @@ -70,7 +70,7 @@ \fcolorbox{lightgray}{lightgray}{\parbox{\textwidth}{\large{Projekt: \textbf{\invoiceTitle}}}} \begin{invoice}{Euro}{0} - \input{_invoice} + \input{[[.Invoice]]} \end{invoice} \ps Im ausgewiesenen Betrag ist gemäß § 19 UStG keine Umsatzsteuer enthalten. diff --git a/texify.go b/texify.go index 01ccd8f..71a9988 100644 --- a/texify.go +++ b/texify.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "text/template" ) @@ -8,29 +9,40 @@ import ( type FeeItem struct{ Task string Timeframe string - Hours int + Hours float64 Price float64 } +type Filenames struct{ + Data string + Invoice string + Main string +} + type Data struct { InvDate string WorkDate string InvNo string ProjName string + CustComp string CustName string CustStr string CustZip string CustCity string + CustCountry string + TaxID string MyName string MyStr string MyZip string MyCity string + MyCountry string MyTel string MyMob string MyMail string MyUrl string + BankNum string BankLZ string BankName string @@ -38,6 +50,91 @@ type Data struct { Bic string } +func billTemplate(billdata bill, cust customer) (Filenames) { + billid := billdata.id + custr,cuzip,cucity,cucou := "","","","" + addr := string2StringArray(cust.address,";") + for i,a := range addr { + switch i { + case 0: + custr=a + case 1: + cuzip=a + case 2: + cucity=a + case 3: + cucou=a + default: + break + } + } + dat := Data{billdata.date.UTC().Format("2006-01-02"), + billdata.timerange, + billdata.identity, + billdata.projectname, + + cust.company, + cust.name, + custr, + cuzip, + cucity, + cucou, + + config.Taxid, + config.Name, + config.Street, + config.Zip, + config.City, + config.Country, + config.Telefon, + config.Mobile, + config.Mail, + config.Url, + + config.Bankacc, + config.Banklz, + config.Bankname, + config.Iban, + config.Bic, + } + + inv := billdata.items + + filenamedata := fmt.Sprintf("%v_data",billid) + filenameinv := fmt.Sprintf("%v_invoice",billid) + filenamemain := fmt.Sprintf("outtex/%v_main.tex",billid) + fnames := Filenames{filenamedata,filenameinv,filenamemain} + + //Parse Templates + tmpl, err := template.New("invoice").Delims("[[","]]").ParseGlob("templates/*.tex") + checkErr(err) + filenamedata = fmt.Sprintf("outtex/%v_data.tex",billid) + filenameinv = fmt.Sprintf("outtex/%v_invoice.tex",billid) + filenamemain = fmt.Sprintf("outtex/%v_main.tex",billid) + + //Open File + datf, err := os.Create(filenamedata) + checkErr(err) + invf, err := os.Create(filenameinv) + checkErr(err) + maif, err := os.Create(filenamemain) + checkErr(err) + defer datf.Close() + defer invf.Close() + defer maif.Close() + + //Execute Template + //err = tmpl.ExecuteTemplate(os.Stdout, "invoice.tex", td) + err = tmpl.ExecuteTemplate(datf, "_data.tex", dat) + checkErr(err) + err = tmpl.ExecuteTemplate(invf, "_invoice.tex", inv) + checkErr(err) + err = tmpl.ExecuteTemplate(maif, "_main.tex", fnames) + checkErr(err) + + return fnames +} + func tmpltest() { //Sampledata @@ -50,11 +147,13 @@ func tmpltest() { "Fakestreet 123", "6996", "Busenhausen", + "Tittania", "6575-6544", "Meinam Sauluschtig", "Saubastrasse 86", "1111", "Nicetown", + "Niceland", "+43 (1) 655 555 23", "+43 (0) 650 555 32", "Meinam@sauluscht.ig", diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..c94f94b --- /dev/null +++ b/utils.go @@ -0,0 +1,97 @@ +package main + +import ( + "fmt" + "bufio" + "os" + "strings" + "strconv" + "runtime" +) + +func isSure(quest string) bool { + fmt.Printf("%s (type 'y/Y/yes' to confirm) : ",quest) + in := bufio.NewReader(os.Stdin) + line, err := in.ReadString('\n') + if(runtime.GOOS == "windows"){ + line = strings.TrimSuffix(line, "\r\n") //for Windows + }else{ + line = strings.TrimSuffix(line, "\n") + } + checkErr(err) + + if ( line == "yes" || line == "y" || line == "Y") { + return true + } else { + return false + } +} + +func checkErr(err error) { + if err != nil { + panic(err) + } +} + +func getInput(quest string) string { + fmt.Print(quest) + in := bufio.NewReader(os.Stdin) + line, err := in.ReadString('\n') + if(runtime.GOOS == "windows"){ + line = strings.TrimSuffix(line, "\r\n") //for Windows + }else{ + line = strings.TrimSuffix(line, "\n") + } + checkErr(err) + return line +} + +func getNewInput(quest,old string) string { + if old != "" { + fmt.Println("Current:",old) + } + fmt.Print(quest) + in := bufio.NewReader(os.Stdin) + line, err := in.ReadString('\n') + if(runtime.GOOS == "windows"){ + line = strings.TrimSuffix(line, "\r\n") //for Windows + }else{ + line = strings.TrimSuffix(line, "\n") + } + checkErr(err) + if line == "" { + return old + }else{ + return line + } +} + +func stringArray2String(in []string, delim string) (string) { + var out string + for i,a := range in { + if i==0 { + out=a + }else{ + out=fmt.Sprintf("%s%s%s",out,delim,a) + } + } + return out +} + +func string2FloatArray(in string,delim string)(out []float64) { + read := strings.Split(in,delim) + for _,s := range read{ + fs,err := strconv.ParseFloat(s,64) + checkErr(err) + out = append(out,fs) + } + return +} + +func string2StringArray(in string,delim string)(out []string) { + read := strings.Split(in,delim) + for _,s := range read{ + out = append(out,s) + } + return +} -- cgit v1.2.3