package main import ( "fmt" "time" ) type BillService struct { db *DB bills map[int64]Bill } const paymentColumns = "id, billid, monthFor, paymentDate" const billColumns = "id, name" func (s *BillService) GetPaymentsForDate(date time.Time) ([]Payment, error) { res := []Payment{} if s == nil { return res, fmt.Errorf("calling GetPaymentsFor on nil BillService") } if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get payments, db is nil or not ready - %v", s.db) } rows, err := s.db.readConn.Query(fmt.Sprintf(` SELECT %s FROM Payment WHERE monthFor = date(strftime('%%Y-%%m-01', ?)); `, paymentColumns), date) if err != nil { return res, fmt.Errorf("query for payments of %s failed with error: %v", date, err) } for rows.Next() { payment := Payment{} err = rows.Scan(&payment.Id, &payment.BillId, &payment.MonthFor, &payment.PaymentDate) if err != nil { Error.Printf("failed to scan row: %v", err) continue } res = append(res, payment) } return res, nil } func (s *BillService) GetPaymentForBillAndDate(billid int64, date time.Time) (Payment, error) { res := Payment{} if s == nil { return res, fmt.Errorf("calling GetPaymentsFor on nil BillService") } if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get payments, db is nil or not ready - %v", s.db) } row := s.db.readConn.QueryRow(fmt.Sprintf(` SELECT %s FROM Payment WHERE billid = ? AND monthFor = date(strftime('%%Y-%%m-01', ?)); `, paymentColumns), billid, date) err := row.Scan(&res.Id, &res.BillId, &res.MonthFor, &res.PaymentDate) if err != nil { return res, fmt.Errorf("failed scanning row: %v", err) } return res, nil } func (s *BillService) GetAllBills() ([]Bill, error) { res := []Bill{} if s == nil { return res, fmt.Errorf("calling GetAllBills on nil BillService") } if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get bills, db is nil or not ready - %v", s.db) } rows, err := s.db.readConn.Query(fmt.Sprintf(`SELECT %s FROM Bill ORDER BY name`, billColumns)) if err != nil { return res, fmt.Errorf("failed to query for bills: %w", err) } for rows.Next() { bill := Bill{} err := rows.Scan(&bill.Id, &bill.Name) if err != nil { Error.Printf("failed to scan row: %v", err) continue } res = append(res, bill) } s.bills = make(map[int64]Bill) for _, bill := range res { s.bills[bill.Id] = bill } return res, nil } func (s *BillService) MarkPaid(billid int64, monthFor time.Time, when time.Time) (Payment, error) { res := Payment{} if s == nil { return res, fmt.Errorf("calling MarkPaid on nil BillService") } if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot mark bill paid, db is nil or not ready - %v", s.db) } qres, err := s.db.writeConn.Exec(` INSERT INTO Payment (billid, monthFor, paymentDate) VALUES (?, date(strftime('%Y-%m-01', ?)), ?) ON CONFLICT(billid, monthFor) DO UPDATE SET paymentDate = excluded.paymentDate WHERE Payment.paymentDate IS NULL `, billid, monthFor, when) if err != nil { return res, fmt.Errorf("failed upserting into payment with error: %w", err) } rows, err := qres.RowsAffected() if err != nil { return res, fmt.Errorf("failed to get rows affected: %w", err) } if rows == 0 { return res, fmt.Errorf("no rows affected") } return s.GetPaymentForBillAndDate(billid, monthFor) }