Documentation ¶
Overview ¶
Copyright 2023 PraserX
Copyright 2023 PraserX ¶
Copyright 2023 PraserX
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var Billing = cli.Command{ Name: "billing", Aliases: []string{"b"}, Usage: "Billing periods and bills management", Flags: []cli.Flag{ &flags.FlagPlainPrint, }, Subcommands: []*cli.Command{ &BillingPeriods, &BillingBills, }, Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } bills, err := database.SelectAllBills() if err != nil { return fmt.Errorf("error: cannot get bills: %v", err.Error()) } if !ctx.Bool("plain") { t := table.NewWriter() t.AppendHeader(table.Row{"Bill ID", "Period ID", "Amount", "Issued", "Paid", "Confirmed"}) for _, bill := range bills { t.AppendRow(table.Row{bill.ID, bill.PeriodID, bill.Amount, bill.Issued, bill.Paid, bill.PaymentConfirmation}) } fmt.Println(t.Render()) } else { for _, bill := range bills { fmt.Printf("%d %d %.2f %t %t %t\n", bill.ID, bill.PeriodID, bill.Amount, bill.Issued, bill.Paid, bill.PaymentConfirmation) } } return nil }, }
View Source
var BillingBills = cli.Command{ Name: "bills", Aliases: []string{"b"}, Usage: "Bills management section (add, issue, pay, confirm)", Subcommands: []*cli.Command{ &BillingBillsAdd, &BillingBillsIssue, &BillingBillsPay, &BillingBillsPayCSV, &BillingBillsConfirmPayment, &BillingBillsUnpaidNotification, }, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "all", Aliases: []string{"a"}, Usage: "print all bills overtime", }, &cli.BoolFlag{ Name: "paid", Aliases: []string{"p"}, Usage: "print all bills which were already paid", }, &cli.BoolFlag{ Name: "unpaid", Aliases: []string{"u"}, Usage: "print all bills which waiting for payment", }, &cli.IntFlag{ Name: "sort", Aliases: []string{"s"}, Usage: "sort output by quantity (1: high-low, 2: low-high) or amount (3: high-low, 4:low-high)", }, }, Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } bills, err := database.SelectAllBills() if err != nil { return fmt.Errorf("error: cannot get billing periods: %v", err.Error()) } var display []models.Bill var quantity int var amount float32 for _, bill := range bills { if ctx.Bool("all") { quantity += bill.Quantity amount += bill.Amount display = append(display, bill) } else if ctx.Bool("paid") && bill.Paid { quantity += bill.Quantity amount += bill.Amount display = append(display, bill) } else if ctx.Bool("unpaid") && !bill.Paid { quantity += bill.Quantity amount += bill.Amount display = append(display, bill) } } switch variant := ctx.Int("sort"); variant { case 1: sort.Slice(display, func(i, j int) bool { return display[i].Quantity > display[j].Quantity }) case 2: sort.Slice(display, func(i, j int) bool { return display[i].Quantity < display[j].Quantity }) case 3: sort.Slice(display, func(i, j int) bool { return display[i].Amount > display[j].Amount }) case 4: sort.Slice(display, func(i, j int) bool { return display[i].Amount < display[j].Amount }) default: } t := table.NewWriter() t.AppendHeader(table.Row{"ID", "PID", "UID", "Name", "Quantity", "Amount", "Issued", "Paid", "Payment Confirmed"}) for _, bill := range display { user, err := database.SelectUserByID(bill.UserID) if err != nil { return fmt.Errorf("error: cannot get user: %v", err.Error()) } t.AppendRow(table.Row{bill.ID, bill.PeriodID, bill.UserID, user.Firstname + " " + user.Lastname, fmt.Sprintf("%d", bill.Quantity), fmt.Sprintf("%.2f", bill.Amount), fmt.Sprintf("%t", bill.Issued), fmt.Sprintf("%t", bill.Paid), fmt.Sprintf("%t", bill.PaymentConfirmation)}) } t.AppendFooter(table.Row{"", "", "", "", fmt.Sprintf("%d", quantity), fmt.Sprintf("%.2f", amount), "", "", ""}) fmt.Println(t.Render()) return nil }, }
View Source
var BillingBillsAdd = cli.Command{ Name: "add", Aliases: []string{"a"}, Usage: "Add bill for given user and period with specified quantity", ArgsUsage: "[period_id user_id quantity]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 3 { return fmt.Errorf("error: too few arguments: requires (4), get (%d)", ctx.NArg()) } pid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } uid, err := strconv.ParseUint(ctx.Args().Get(1), 10, 64) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } quantity, err := strconv.ParseInt(ctx.Args().Get(2), 10, 64) if err != nil { return fmt.Errorf("error: cannot parse int: %v", err) } user, err := database.SelectUserByID(uint(uid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: user not found") } else if err != nil { return fmt.Errorf("error: cannot get user: %v", err) } period, err := database.SelectPeriodByID(uint(pid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: billing period not found") } else if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } if period.Closed { return fmt.Errorf("error: billing period is already closed") } bill := models.Bill{ Quantity: int(quantity), UserID: uint(uid), PeriodID: uint(pid), } id, err := database.InsertBill(bill) if err != nil { return fmt.Errorf("error: cannot add new bill to billing period: %v", err.Error()) } logger.Info(fmt.Sprintf("new bill successfully added to billing period for user_id=%d, user_name=%s: new bill id: %d", uid, user.Firstname+" "+user.Lastname, id)) return nil }, }
View Source
var BillingBillsConfirmPayment = cli.Command{ Name: "confirm", Aliases: []string{"c"}, Usage: "Send payment confirmation for all paid bills for given period (e-mail will not be sent for already notified users)", ArgsUsage: "[period_id]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if err = helpers.SetupMail(ctx); err != nil { return err } if ctx.NArg() != 1 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } pid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } period, err := database.SelectPeriodByID(uint(pid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: billing period not found") } else if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } if !period.Closed { return fmt.Errorf("error: cannot send payment confirmation: period is not closed") } bills, err := database.SelectAllBillsForPeriod(uint(pid)) if err != nil { return fmt.Errorf("error: cannot get bills for given period: %v", err) } for _, bill := range bills { user, err := database.SelectUserByID(bill.UserID) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: user for giver bill is not found: user_id=%d", bill.UserID) } else if err != nil { return fmt.Errorf("error: cannot get user: user_id=%d: %v", bill.UserID, err) } if !bill.PaymentConfirmation && bill.Paid { if err = mail.SendPaymentConfirmation(user, period, bill); err != nil { logger.Error(fmt.Sprintf("error: billing e-mail has not been sent for bill_id=%d user_id=%d user_name='%s' user_email:'%s'", bill.ID, user.ID, user.Firstname+" "+user.Lastname, user.Email)) } else { logger.Info(fmt.Sprintf("payment confirmation has been sent for bill_id=%d user_id=%d user_name='%s' user_email:'%s'", bill.ID, user.ID, user.Firstname+" "+user.Lastname, user.Email)) err = database.UpdateBillOnPaymentConfirmation(bill.ID) if err != nil { return fmt.Errorf("error: cannot update bill: bill_id=%d: %v", bill.ID, err) } } } } return nil }, }
View Source
var BillingBillsIssue = cli.Command{ Name: "issue", Aliases: []string{"i"}, Usage: "Issue all bills for giver billing period", ArgsUsage: "[period_id]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if err = helpers.SetupMail(ctx); err != nil { return err } if ctx.NArg() != 1 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } pid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } period, err := database.SelectPeriodByID(uint(pid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: billing period not found") } else if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } if !period.Closed { return fmt.Errorf("error: cannot issue bills: period is not closed") } bills, err := database.SelectAllBillsForPeriod(uint(pid)) if err != nil { return fmt.Errorf("error: cannot get bills for given period: %v", err) } for _, bill := range bills { user, err := database.SelectUserByID(bill.UserID) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: user for giver bill is not found: user_id=%d", bill.UserID) } else if err != nil { return fmt.Errorf("error: cannot get user: user_id=%d: %v", bill.UserID, err) } if err = mail.SendBill(user, period, bill, len(bills)); err != nil { logger.Error(fmt.Sprintf("error: billing e-mail has not been sent for bill_id=%d user_id=%d user_name='%s' user_email:'%s'", bill.ID, user.ID, user.Firstname+" "+user.Lastname, user.Email)) } else { logger.Info(fmt.Sprintf("billing e-mail has been sent for bill_id=%d user_id=%d user_name='%s' user_email:'%s'", bill.ID, user.ID, user.Firstname+" "+user.Lastname, user.Email)) err = database.UpdateBillOnIssued(bill.ID) if err != nil { return fmt.Errorf("error: cannot update bill: bill_id=%d: %v", bill.ID, err) } } } return nil }, }
View Source
var BillingBillsPay = cli.Command{ Name: "pay", Aliases: []string{"p"}, Usage: "Pay (add payment tag) bills for giver billing period", ArgsUsage: "[bill_id]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 1 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } bid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } err = database.UpdateBillOnPaid(uint(bid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: bill not found: bill_id=%d", bid) } else if err != nil { return fmt.Errorf("error: cannot update bill_id=%d: %v", bid, err) } logger.Info(fmt.Sprintf("bill successfully marked as paid: bill_id=%d", bid)) return nil }, }
View Source
var BillingBillsPayCSV = cli.Command{ Name: "pay-csv", Usage: "Bulk pay (add payment tag) by CSV with appropriate variable symbol", ArgsUsage: "[csv]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } var fr *os.File if fr, err = os.Open(ctx.Args().Get(0)); err != nil { fmt.Fprintf(os.Stderr, "cannot read file") os.Exit(1) } reader := bufio.NewReader(fr) defer func() { fr.Close() }() csv := [][]string{} for { line, _, err := reader.ReadLine() if err != nil { break } csv = append(csv, strings.Split(string(line), ";")) } csv = csv[1:] for i, line := range csv { unbid := strings.ReplaceAll(line[12], "\"", "") if len(unbid) == 0 { logger.Info(fmt.Sprintf("missing variable symbol, skipping: line=%d", i)) continue } bid, err := strconv.ParseUint(unbid, 10, 64) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } bill, err := database.SelectBillByID(uint(bid)) if errors.Is(err, gorm.ErrRecordNotFound) { logger.Error(fmt.Sprintf("error: bill not found: bill_id=%d", bid)) } else if err != nil { return fmt.Errorf("error: get bill with bill_id=%d: %v", bid, err) } if !bill.Paid { logger.Info(fmt.Sprintf("bill is not paid: bill_id=%d", bid)) } } return nil }, }
View Source
var BillingBillsUnpaidNotification = cli.Command{ Name: "notify", Aliases: []string{"n"}, Usage: "Send notification e-mail for unpaid debt for all unpaid bills for given period", ArgsUsage: "[period_id]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if err = helpers.SetupMail(ctx); err != nil { return err } if ctx.NArg() != 1 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } pid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } period, err := database.SelectPeriodByID(uint(pid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: billing period not found") } else if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } if !period.Closed { return fmt.Errorf("error: cannot send notification: period is not closed") } bills, err := database.SelectAllBillsForPeriod(uint(pid)) if err != nil { return fmt.Errorf("error: cannot get bills for given period: %v", err) } for _, bill := range bills { user, err := database.SelectUserByID(bill.UserID) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: user for giver bill is not found: user_id=%d", bill.UserID) } else if err != nil { return fmt.Errorf("error: cannot get user: user_id=%d: %v", bill.UserID, err) } if !bill.Paid { if err = mail.SendUnpaidNotification(user, period, bill); err != nil { logger.Error(fmt.Sprintf("error: notification e-mail has not been sent for bill_id=%d user_id=%d user_name='%s' user_email:'%s'", bill.ID, user.ID, user.Firstname+" "+user.Lastname, user.Email)) } else { logger.Info(fmt.Sprintf("debt notification has been sent for bill_id=%d user_id=%d user_name='%s' user_email:'%s'", bill.ID, user.ID, user.Firstname+" "+user.Lastname, user.Email)) } } } return nil }, }
View Source
var BillingPeriods = cli.Command{ Name: "periods", Aliases: []string{"p"}, Usage: "Billing periods management (create, close, summary)", Subcommands: []*cli.Command{ &BillingPeriodsNew, &BillingPeriodsClose, &BillingPeriodsSummary, }, Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } periods, err := database.SelectAllPeriods() if err != nil { return fmt.Errorf("error: cannot get billing periods: %v", err.Error()) } t := table.NewWriter() t.AppendHeader(table.Row{"ID", "From", "To", "UnitPrice", "Total Amount", "Total Quantity", "Avg. Package Price", "Cash"}) for _, period := range periods { t.AppendRow(table.Row{period.ID, period.DateFrom.Format("2006-01-02"), period.DateTo.Format("2006-01-02"), fmt.Sprintf("%.2f", period.UnitPrice), fmt.Sprintf("%.2f", period.TotalAmount), fmt.Sprintf("%d", period.TotalQuantity), fmt.Sprintf("%.2f", period.AmountPerPackage), fmt.Sprintf("%.2f", period.Cash)}) } fmt.Println(t.Render()) return nil }, }
View Source
var BillingPeriodsClose = cli.Command{ Name: "close", Aliases: []string{"c"}, Usage: "Close billing period and calculate remaining values such as total quantity or unit price", ArgsUsage: "[period_id]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 1 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } pid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } period, err := database.SelectPeriodByID(uint(pid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: billing period not found") } else if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } bills, err := database.SelectAllBillsForPeriod(uint(pid)) if err != nil { return fmt.Errorf("error: cannot get bills for given period: %v", err) } totalQuantity := 0 for _, bill := range bills { totalQuantity += bill.Quantity } unitPrice := (period.TotalAmount - period.Cash) / float32(totalQuantity) err = database.UpdatePeriodOnClose(uint(pid), totalQuantity, unitPrice) if err != nil { return fmt.Errorf("error: cannot create new billing period: %v", err.Error()) } logger.Info(fmt.Sprintf("billing period updated and closed successfully: period_id=%d, total_quantity=%d, unit_price=%.2f", pid, totalQuantity, unitPrice)) for _, bill := range bills { amount := float32(bill.Quantity) * unitPrice payment := amount user, err := database.SelectUserByID(bill.UserID) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: user for giver bill is not found: user_id=%d", bill.UserID) } else if err != nil { return fmt.Errorf("error: cannot get user: user_id=%d: %v", bill.UserID, err) } if user.Credit != 0 { var ballance float32 if float32(user.Credit) <= amount { payment = amount - float32(user.Credit) ballance = amount - payment } else { payment = 0 ballance = amount } err = database.WithdrawMoney(bill.UserID, ballance) if err != nil { logger.Error(fmt.Sprintf("error: cannot withdraw credit for user: bill_id=%d user_id=%d ballance=%.2f: %v", bill.ID, bill.UserID, ballance, err.Error())) } else { logger.Info(fmt.Sprintf("credit has been reduced for user: bill_id=%d user_id=%d reduction=%.2f", bill.ID, bill.UserID, ballance)) } _, err = database.InsertTransaction(models.Transaction{Type: models.WITHDRAW, Amount: ballance, UserID: bill.UserID}) if err != nil { logger.Error(fmt.Sprintf("error: cannot insert withdraw transaction for user: bill_id=%d user_id=%d ballance=%.2f: %v", bill.ID, bill.UserID, ballance, err.Error())) } } err = database.UpdateBillOnPeriodClose(bill.ID, amount, payment) if err != nil { logger.Error(fmt.Sprintf("error: cannot update bill with bill_id=%d: %v", bill.ID, err.Error())) } else { logger.Info(fmt.Sprintf("bill has been updated successfully: bill_id=%d amount=%.2f", bill.ID, amount)) } } return nil }, }
View Source
var BillingPeriodsNew = cli.Command{ Name: "new", Aliases: []string{"n"}, Usage: "Add new billing period", ArgsUsage: "[from(2006-01-02) to(2006-01-02) issued(2006-01-02) total_amount amount_per_package cash]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 6 { return fmt.Errorf("error: too few arguments: requires (6), get (%d)", ctx.NArg()) } DateFrom, err := time.Parse("2006-01-02", ctx.Args().Get(0)) if err != nil { return fmt.Errorf("error: cannot parse date: %v", err) } DateTo, err := time.Parse("2006-01-02", ctx.Args().Get(1)) if err != nil { return fmt.Errorf("error: cannot parse date: %v", err) } DateOfIssue, err := time.Parse("2006-01-02", ctx.Args().Get(2)) if err != nil { return fmt.Errorf("error: cannot parse date: %v", err) } TotalAmount, err := strconv.ParseFloat(ctx.Args().Get(3), 32) if err != nil { return fmt.Errorf("error: cannot parse float: %v", err) } AmountPerPackage, err := strconv.ParseFloat(ctx.Args().Get(4), 32) if err != nil { return fmt.Errorf("error: cannot parse float: %v", err) } Cash, err := strconv.ParseFloat(ctx.Args().Get(5), 32) if err != nil { return fmt.Errorf("error: cannot parse float: %v", err) } totalMonths := 1 if int(DateTo.Sub(DateFrom).Hours()/24/30) > 0 { totalMonths = int(DateTo.Sub(DateFrom).Hours() / 24 / 30) } period := models.Period{ DateFrom: DateFrom, DateTo: DateTo, DateOfIssue: DateOfIssue, TotalMonths: totalMonths, TotalAmount: float32(TotalAmount), AmountPerPackage: float32(AmountPerPackage), Cash: float32(Cash), } id, err := database.InsertPeriod(period) if err != nil { return fmt.Errorf("error: cannot create new billing period: %v", err.Error()) } logger.Info(fmt.Sprintf("new billing period successfully created: new period id: %d", id)) return nil }, }
View Source
var BillingPeriodsSummary = cli.Command{ Name: "summary", Aliases: []string{"s"}, Usage: "Get numbers and statistics for given billing period", ArgsUsage: "[period_id]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 1 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } pid, err := strconv.ParseUint(ctx.Args().Get(0), 10, 32) if err != nil { return fmt.Errorf("error: cannot parse uint: %v", err) } period, err := database.SelectPeriodByID(uint(pid)) if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("error: billing period not found") } else if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } bills, err := database.SelectAllBillsForPeriod(uint(pid)) if err != nil { return fmt.Errorf("error: cannot get bills for given period: %v", err) } fmt.Printf("ID: %d\n", period.ID) fmt.Printf("Period: %s - %s\n", period.DateFrom.Format("2006-01-02"), period.DateTo.Format("2006-01-02")) fmt.Printf("Unit price: %.2f\n", period.UnitPrice) fmt.Printf("Total quantity: %d\n", period.TotalQuantity) fmt.Printf("Total amount: %.2f\n", period.TotalAmount) fmt.Printf("Total amount (wc): %.2f (total - cash)\n", period.TotalAmount-period.Cash) fmt.Printf("Total months: %d\n", period.TotalMonths) fmt.Printf("Cash: %.2f\n", period.Cash) fmt.Printf("Closed: %t\n", period.Closed) fmt.Printf("Total bills: %d\n", len(bills)) return nil }, }
View Source
var Database = cli.Command{ Name: "database", Aliases: []string{"d"}, Usage: "Database initialization and checks", Subcommands: []*cli.Command{ &DatabaseInitialize, &DatabaseMigrate, }, }
View Source
var DatabaseCheck = cli.Command{ Name: "check", Usage: "Check SQLite database", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } schema, err := database.SelectVersion() if err != nil { return fmt.Errorf("cannot get schema from database for version check: %v", err) } if schema.Version != models.VERSION { return fmt.Errorf("check failed: version miss match") } return nil }, }
View Source
var DatabaseInitialize = cli.Command{ Name: "initialize", Usage: "Initialize SQLite database", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } database.RunAutoMigration() _, err = database.InsertSchema(models.Schema{Version: models.VERSION}) if err != nil { return fmt.Errorf("cannot update schema version for database: %v", err) } return nil }, }
View Source
var DatabaseMigrate = cli.Command{ Name: "migrate", Usage: "Migrate SQLite database", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } database.RunAutoMigration() schema, err := database.SelectVersion() if err != nil { return fmt.Errorf("cannot get schema from database for version check: %v", err) } if schema.Version != models.VERSION { err = database.UpdateVersion(models.VERSION) if err != nil { return fmt.Errorf("cannot update schema version for database: %v", err) } } return nil }, }
View Source
var Users = cli.Command{ Name: "users", Aliases: []string{"u"}, Usage: "User management operations", Flags: []cli.Flag{ &flags.FlagPlainPrint, }, Subcommands: []*cli.Command{ &UsersAdd, &UsersAddBulk, &UsersContacts, }, Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } users, err := database.SelectAllUsers() if err != nil { return fmt.Errorf("error: cannot get users: %v", err.Error()) } if !ctx.Bool("plain") { t := table.NewWriter() t.AppendHeader(table.Row{"ID", "Name", "E-mail", "Location"}) for _, user := range users { t.AppendRow(table.Row{user.ID, user.Firstname + " " + user.Lastname, user.Email, user.Location}) } fmt.Println(t.Render()) } else { for _, user := range users { fmt.Printf("%d %s %s, %s, %s\n", user.ID, user.Firstname, user.Lastname, user.Email, user.Location) } } return nil }, }
View Source
var UsersAdd = cli.Command{ Name: "add", Usage: "Add new user", ArgsUsage: "[employee_id firstname lastname e-mail location]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 5 { return fmt.Errorf("error: too few arguments: requires (5), get (%d)", ctx.NArg()) } user := models.User{ EID: ctx.Args().Get(0), Firstname: ctx.Args().Get(1), Lastname: ctx.Args().Get(2), Email: ctx.Args().Get(3), Location: ctx.Args().Get(4), } if _, err = database.SelectUserByEID(user.EID); err != nil { id, err := database.InsertUser(user) if err != nil { return fmt.Errorf("error: cannot create user: %v", err.Error()) } logger.Info(fmt.Sprintf("user successfully created: new user id: %d", id)) } else { logger.Info("user already exists") } return nil }, }
View Source
var UsersAddBulk = cli.Command{ Name: "add-bulk", Usage: "Add multiple users at once by csv file (eid,\"lastname firstname\",e-mail,location,...)", ArgsUsage: "[file.csv]", Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 1 { fmt.Println("Too few arguments") } fmt.Println(ctx.Args().Get(0)) const ( CSV_EID = iota CSV_NAME CSV_EMAIL CSV_LOCATION CSV_COFFEES ) var fd *os.File if fd, err = os.Open(ctx.Args().Get(0)); err != nil { return err } ioReader := bufio.NewReader(fd) reader := csv.NewReader(ioReader) for { var record []string if record, err = reader.Read(); err == io.EOF { break } else if err != nil { return err } user := models.User{ EID: record[CSV_EID], Firstname: strings.Fields(record[CSV_NAME])[1], Lastname: strings.Fields(record[CSV_NAME])[0], Email: record[CSV_EMAIL], Location: record[CSV_LOCATION], } if _, err = database.SelectUserByEID(user.EID); err != nil { id, err := database.InsertUser(user) if err != nil { return fmt.Errorf("error: cannot create user: %v", err.Error()) } logger.Info(fmt.Sprintf("user successfully created: new user id: %d", id)) } else { logger.Info("user already exists") } } return nil }, }
View Source
var UsersContacts = cli.Command{ Name: "contacts", Aliases: []string{"c"}, Usage: "Get e-mail contacts of all customers", Flags: []cli.Flag{ &flags.FlagPlainPrint, }, Action: func(ctx *cli.Context) (err error) { if err = helpers.SetupDatabase(ctx); err != nil { return err } if ctx.NArg() != 0 { return fmt.Errorf("error: too few arguments: requires (1), get (%d)", ctx.NArg()) } bills, err := database.SelectAllBills() if err != nil { return fmt.Errorf("error: cannot get billing period: %v", err) } var contacts []string for _, bill := range bills { user, err := database.SelectUserByID(bill.UserID) if err != nil { logger.Error(fmt.Sprintf("error: cannot get user by id: user_id=%d: %v", bill.UserID, err.Error())) } if !slices.Contains(contacts, user.Email) { contacts = append(contacts, user.Email) } } if !ctx.Bool("plain") { t := table.NewWriter() t.AppendHeader(table.Row{"E-mail"}) for _, contact := range contacts { t.AppendRow(table.Row{contact}) } fmt.Println(t.Render()) } else { for _, contact := range contacts { fmt.Println(contact) } } return nil }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.