Compare commits

..

No commits in common. "046f2a9164e2a954b886b6dedb243f3d0aeb9b07" and "8540470c620056b62345e5f52c4a1fdd351c5ad5" have entirely different histories.

16 changed files with 129 additions and 346 deletions

View File

@ -17,8 +17,8 @@ RUN go mod download
COPY . .
# Build our application.
RUN #CGO_ENABLED=0 GOOS=linux go build -o bill-server ./cmd/bill-server/main.go
RUN go build -a -ldflags "-linkmode external -extldflags '-static' -s -w" -o bill-server ./cmd/bill-server/main.go
#RUN CGO_ENABLED=0 GOOS=linux go build -o docker-mariadb-clean-arch ./cmd/docker-mariadb-clean-arch/main.go
RUN go build -a -ldflags "-linkmode external -extldflags '-static' -s -w" -o docker-mariadb-clean-arch ./cmd/docker-mariadb-clean-arch/main.go
@ -29,8 +29,8 @@ FROM scratch AS prod
WORKDIR /production
# Copy our compiled executable from the last stage.
COPY --from=api /compiler/bill-server .
COPY --from=api /compiler/docker-mariadb-clean-arch .
# Run application and expose port 8080.
EXPOSE 8080
CMD ["./bill-server"]
CMD ["./docker-mariadb-clean-arch"]

View File

@ -1,16 +1,15 @@
version: "3.9"
services:
fiber-application:
restart: on-failure
image: bill-server:latest
restart: always
image: docker-mariadb-clean-arch:latest
build:
context: .
dockerfile: Dockerfile
target: prod
ports:
- "8080:8080"
- 8080:8080
environment:
- ENV=prod
- API_USERID=1
- API_USERNAME=fiber
- API_PASSWORD=fiber
@ -19,7 +18,7 @@ services:
- application
# depends_on:
# - mariadb
command: ./bill-server
command: ./docker-mariadb-clean-arch
# mariadb:
# image: mariadb:10.6.3

2
go.mod
View File

@ -22,14 +22,12 @@ require (
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.45.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/sys v0.7.0 // indirect
gorm.io/driver/sqlite v1.5.1 // indirect
gorm.io/gorm v1.25.1 // indirect

4
go.sum
View File

@ -37,8 +37,6 @@ github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
@ -63,8 +61,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=

View File

@ -5,24 +5,14 @@ import (
"bill-go-fiber/internal/label"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"log"
"os"
)
func ConnectToSqlite() (*gorm.DB, error) {
var dsn string
switch os.Getenv("ENV") {
case "prod":
dsn = "bill.db"
default:
dsn = "file::memory:?cache=shared"
}
dsn := "bill.db"
//dsn := "file::memory:?cache=shared"
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database.\n")
} else {
log.Println("Connecting to sqlite.")
}
err = db.AutoMigrate(&label.Label{})
@ -34,21 +24,7 @@ func ConnectToSqlite() (*gorm.DB, error) {
panic("failed to migrate database.\n")
}
// migrate data
if os.Getenv("ENV") != "prod" {
go func() {
sql, err := os.ReadFile("scripts/sqliteMigrations.sql")
if err != nil {
//panic("failed to read migration sql file: " + err.Error())
log.Fatal("failed to read migration sql file: " + err.Error())
}
res := db.Exec(string(sql))
if res.Error != nil {
log.Fatal("failed to migrate" + res.Error.Error())
}
log.Println("SQL migrated successfully!")
}()
}
db.Create(&label.Label{ID: 1, Name: "bill"})
return db, nil
}

View File

@ -12,22 +12,6 @@ type Label struct {
Count int
}
type LabelDTO struct {
ID int `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Count int `json:"count"`
}
func (l *Label) Dto() interface{} {
return LabelDTO{
ID: int(l.ID),
Type: l.Type,
Name: l.Name,
Count: l.Count,
}
}
type LabelRepository interface {
GetLabels(ctx context.Context) ([]Label, error)
GetLabelById(ctx context.Context, id int) (*Label, error)
@ -37,8 +21,8 @@ type LabelRepository interface {
}
type LabelService interface {
GetLabels(ctx context.Context) ([]interface{}, error)
GetLabelById(ctx context.Context, id int) (interface{}, error)
GetLabels(ctx context.Context) ([]Label, error)
GetLabelById(ctx context.Context, id int) (*Label, error)
CreateLabel(ctx context.Context, label *Label) error
UpdateLabel(ctx context.Context, label *Label) error
DeleteLabel(ctx context.Context, id int) error

34
internal/label/handle.go Normal file
View File

@ -0,0 +1,34 @@
package label
import (
"context"
"github.com/gofiber/fiber/v2"
)
type LabelHandler struct {
labelService LabelService
}
func NewLabelHandler(labelRoute fiber.Router, ls LabelService) {
handler := &LabelHandler{
labelService: ls,
}
labelRoute.Get("", handler.getLabels)
}
func (h *LabelHandler) getLabels(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
labels, err := h.labelService.GetLabels(customContext)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(&fiber.Map{
"status": "success",
"data": labels,
})
}

View File

@ -1,137 +0,0 @@
package label
import (
"context"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
type LabelHandler struct {
labelService LabelService
}
func NewLabelHandler(labelRoute fiber.Router, ls LabelService) {
handler := &LabelHandler{
labelService: ls,
}
labelRoute.Get("", handler.getLabels)
labelRoute.Post("", handler.createLabel)
labelRoute.Get("/:labelID", handler.getLabelById)
labelRoute.Put("/:labelID", handler.checkIfLabelExistsMiddleware, handler.updateLabel)
labelRoute.Delete("/:labelID", handler.checkIfLabelExistsMiddleware, handler.deleteLabel)
}
func (h *LabelHandler) getLabels(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
labels, err := h.labelService.GetLabels(customContext)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(&fiber.Map{
"status": "success",
"data": labels,
})
}
func (h *LabelHandler) createLabel(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
label := &Label{}
if err := c.BodyParser(label); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
err := h.labelService.CreateLabel(customContext, label)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(&fiber.Map{
"status": "success",
"message": "Label has been created successfully!",
})
}
func (h *LabelHandler) getLabelById(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
targetLabelId, err := c.ParamsInt("labelID")
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(&fiber.Map{
"status": "fail",
"message": "Please provide a valid label id",
})
}
label, err := h.labelService.GetLabelById(customContext, targetLabelId)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(&fiber.Map{
"status": "success",
"data": label,
})
}
func (h *LabelHandler) deleteLabel(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
targetLabelId := c.Locals("labelID").(int)
err := h.labelService.DeleteLabel(customContext, targetLabelId)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
return c.Status(fiber.StatusNoContent).JSON(&fiber.Map{
"status": "success",
"message": "Label has been deleted successfully",
})
}
func (h *LabelHandler) updateLabel(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
targetLabelId := c.Locals("labelID").(int)
label := &Label{Model: gorm.Model{ID: uint(targetLabelId)}}
if err := c.BodyParser(label); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
err := h.labelService.UpdateLabel(customContext, label)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(&fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(&fiber.Map{
"status": "success",
"message": "Label has been updated successfully!",
})
}

View File

@ -1,37 +0,0 @@
package label
import (
"context"
"github.com/gofiber/fiber/v2"
)
func (h *LabelHandler) checkIfLabelExistsMiddleware(c *fiber.Ctx) error {
customContext, cancel := context.WithCancel(context.Background())
defer cancel()
targetLabelID, err := c.ParamsInt("labelID")
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"status": "fail",
"message": "Please specify a valid label ID!",
})
}
searchLabel, err := h.labelService.GetLabelById(customContext, targetLabelID)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"status": "fail",
"message": err.Error(),
})
}
if searchLabel == nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"status": "fail",
"message": "These is no label with this ID!",
})
}
c.Locals("labelID", targetLabelID)
return c.Next()
}

View File

@ -25,23 +25,21 @@ func (s *sqliteRepository) GetLabels(ctx context.Context) ([]Label, error) {
}
func (s *sqliteRepository) GetLabelById(ctx context.Context, id int) (*Label, error) {
var label Label
res := s.First(&label, id)
if res.Error != nil {
return nil, res.Error
}
return &label, nil
//TODO implement me
panic("implement me")
}
func (s *sqliteRepository) CreateLabel(ctx context.Context, label *Label) error {
return s.Create(label).Error
//TODO implement me
panic("implement me")
}
func (s *sqliteRepository) UpdateLabel(ctx context.Context, label *Label) error {
return s.Save(label).Error
//TODO implement me
panic("implement me")
}
func (s *sqliteRepository) DeleteLabel(ctx context.Context, id int) error {
return s.Delete(&Label{}, id).Error
//TODO implement me
panic("implement me")
}

View File

@ -2,7 +2,6 @@ package label
import (
"context"
"github.com/samber/lo"
)
type labelService struct {
@ -13,29 +12,26 @@ func NewLabelService(labelRepository LabelRepository) LabelService {
return &labelService{labelRepository: labelRepository}
}
func (l *labelService) GetLabels(ctx context.Context) ([]interface{}, error) {
labels, err := l.labelRepository.GetLabels(ctx)
return lo.Map(labels, func(l Label, _ int) interface{} { return l.Dto() }), err
func (l *labelService) GetLabels(ctx context.Context) ([]Label, error) {
return l.labelRepository.GetLabels(ctx)
}
func (l *labelService) GetLabelById(ctx context.Context, id int) (interface{}, error) {
label, err := l.labelRepository.GetLabelById(ctx, id)
if err != nil {
return nil, err
}
return label.Dto(), nil
func (l *labelService) GetLabelById(ctx context.Context, id int) (*Label, error) {
//TODO implement me
panic("implement me")
}
func (l *labelService) CreateLabel(ctx context.Context, label *Label) error {
return l.labelRepository.CreateLabel(ctx, label)
//TODO implement me
panic("implement me")
}
func (l *labelService) UpdateLabel(ctx context.Context, label *Label) error {
return l.labelRepository.UpdateLabel(ctx, label)
//TODO implement me
panic("implement me")
}
func (l *labelService) DeleteLabel(ctx context.Context, id int) error {
return l.labelRepository.DeleteLabel(ctx, id)
//TODO implement me
panic("implement me")
}

View File

@ -1,10 +1,6 @@
package misc
import (
"github.com/gofiber/fiber/v2"
"os"
"time"
)
import "github.com/gofiber/fiber/v2"
// Create a handler. Leave this empty, as we have no domains nor use-cases.
type MiscHandler struct{}
@ -15,7 +11,6 @@ func NewMiscHandler(miscRoute fiber.Router) {
// Declare routing.
miscRoute.Get("", handler.healthCheck)
miscRoute.Get("/shutdown", handler.shutdown)
}
// Check for the health of the API.
@ -25,14 +20,3 @@ func (h *MiscHandler) healthCheck(c *fiber.Ctx) error {
"message": "Hello World!",
})
}
func (h *MiscHandler) shutdown(ctx *fiber.Ctx) error {
go func() {
time.Sleep(1 * time.Second)
os.Exit(0)
}()
return ctx.Status(fiber.StatusOK).JSON(&fiber.Map{
"status": "success",
"message": "server shutdown",
})
}

View File

@ -3,7 +3,7 @@
API_URL=http://localhost:8080
# Introduction to the script.
echo "Welcome to 'bill-server' application!"
echo "Welcome to 'docker-mariadb-clean-arch' application!"
echo "Before running the end-to-end tests, please ensure that you have run 'make start'!"; echo
# Testing '/api/v1'.
@ -12,73 +12,73 @@ echo "Running end-to-end testing..."
echo "Testing GET route '/api/v1'..."
curl $API_URL/api/v1; echo
# Testing '/api/v1/labels'.
# Testing '/api/v1/users'.
echo
echo "Testing GET route '/api/v1/labels'..."
curl $API_URL/api/v1/labels; echo
echo "Testing GET route '/api/v1/users'..."
curl $API_URL/api/v1/users; echo
echo
echo "Testing POST route '/api/v1/labels'..."
curl -X POST -H 'Content-Type: application/json' -d '{"name":"Lucy Heartfilia","type":"income"}' $API_URL/api/v1/labels; echo
echo "Testing POST route '/api/v1/users'..."
curl -X POST -H 'Content-Type: application/json' -d '{"name":"Lucy Heartfilia","address":"Shinhotaka, Japan"}' $API_URL/api/v1/users; echo
# Testing '/api/v1/labels/:labelID'.
# Testing '/api/v1/users/:userID'.
echo
echo "Using 'labelID' with value of 3 (the one created beforehand)."
echo "Testing GET route '/api/v1/labels/:labelID'..."
curl $API_URL/api/v1/labels/3; echo
echo "Using 'userID' with value of 11 (the one created beforehand)."
echo "Testing GET route '/api/v1/users/:userID'..."
curl $API_URL/api/v1/users/11; echo
echo
echo "Testing PUT route '/api/v1/labels/:labelID'..."
curl -X PUT -H 'Content-Type: application/json' -d '{"name":"Mirajane Strauss","type":"consume"}' $API_URL/api/v1/labels/3; echo
echo "Testing PUT route '/api/v1/users/:userID'..."
curl -X PUT -H 'Content-Type: application/json' -d '{"name":"Mirajane Strauss","address":"Osaka, Japan"}' $API_URL/api/v1/users/11; echo
echo
echo "Testing GET route after PUT '/api/v1/labels/:labelID'..."
curl $API_URL/api/v1/labels/3; echo
echo "Testing GET route after PUT '/api/v1/users/:userID'..."
curl $API_URL/api/v1/users/11; echo
echo
echo "Testing DELETE route '/api/v1/labels/:labelID'..."
curl -X DELETE $API_URL/api/v1/labels/3; echo
echo "Testing DELETE route '/api/v1/users/:userID'..."
curl -X DELETE $API_URL/api/v1/users/11; echo
echo
echo "Testing GET route after DELETE '/api/v1/labels/:labelID'..."
curl $API_URL/api/v1/labels/3; echo
echo "Testing GET route after DELETE '/api/v1/users/:userID'..."
curl $API_URL/api/v1/users/11; echo
## Testing '/api/v1/auth/login'.
#echo
#echo "Testing POST route '/api/v1/auth/login'..."
#curl -X POST -H 'Content-Type: application/json' -d '{"username":"fiber","password":"fiber"}' -c cookie.txt $API_URL/api/v1/auth/login; echo
#
## Testing '/api/v1/auth/private'.
#echo
#echo "Testing GET route '/api/v1/auth/private'..."
#curl -b cookie.txt $API_URL/api/v1/auth/private; echo
#
## Testing '/api/v1/cities'.
#echo
#echo "Testing GET route '/api/v1/cities'..."
#curl -b cookie.txt $API_URL/api/v1/cities; echo
#echo
#echo "Testing POST route '/api/v1/cities'..."
#curl -b cookie.txt -X POST -H 'Content-Type: application/json' -d '{"name":"Kyoto"}' $API_URL/api/v1/cities; echo
#
## Testing '/api/v1/cities/:cityID'.
#echo
#echo "Using 'cityID' with value of 6 (the one created beforehand)."
#echo "Testing GET route '/api/v1/cities/:cityID'..."
#curl -b cookie.txt $API_URL/api/v1/cities/6; echo
#echo
#echo "Testing PUT route '/api/v1/cities/:cityID'..."
#curl -b cookie.txt -X PUT -H 'Content-Type: application/json' -d '{"name":"Osaka"}' $API_URL/api/v1/cities/6; echo
#echo
#echo "Testing GET route after PUT '/api/v1/cities/:cityID'..."
#curl -b cookie.txt $API_URL/api/v1/cities/6; echo
#echo
#echo "Testing DELETE route '/api/v1/cities/:cityID'..."
#curl -b cookie.txt -X DELETE $API_URL/api/v1/cities/6; echo
#echo
#echo "Testing GET route after DELETE '/api/v1/cities/:cityID'..."
#curl -b cookie.txt $API_URL/api/v1/cities/6; echo
#
## Testing '/api/v1/auth/logout'.
#echo
#echo "Testing POST route '/api/v1/auth/logout'..."
#curl -X POST $API_URL/api/v1/auth/logout; echo
#
## Finish end-to-end testing.
#rm cookie.txt
#echo "Finished testing the application!"
# Testing '/api/v1/auth/login'.
echo
echo "Testing POST route '/api/v1/auth/login'..."
curl -X POST -H 'Content-Type: application/json' -d '{"username":"fiber","password":"fiber"}' -c cookie.txt $API_URL/api/v1/auth/login; echo
# Testing '/api/v1/auth/private'.
echo
echo "Testing GET route '/api/v1/auth/private'..."
curl -b cookie.txt $API_URL/api/v1/auth/private; echo
# Testing '/api/v1/cities'.
echo
echo "Testing GET route '/api/v1/cities'..."
curl -b cookie.txt $API_URL/api/v1/cities; echo
echo
echo "Testing POST route '/api/v1/cities'..."
curl -b cookie.txt -X POST -H 'Content-Type: application/json' -d '{"name":"Kyoto"}' $API_URL/api/v1/cities; echo
# Testing '/api/v1/cities/:cityID'.
echo
echo "Using 'cityID' with value of 6 (the one created beforehand)."
echo "Testing GET route '/api/v1/cities/:cityID'..."
curl -b cookie.txt $API_URL/api/v1/cities/6; echo
echo
echo "Testing PUT route '/api/v1/cities/:cityID'..."
curl -b cookie.txt -X PUT -H 'Content-Type: application/json' -d '{"name":"Osaka"}' $API_URL/api/v1/cities/6; echo
echo
echo "Testing GET route after PUT '/api/v1/cities/:cityID'..."
curl -b cookie.txt $API_URL/api/v1/cities/6; echo
echo
echo "Testing DELETE route '/api/v1/cities/:cityID'..."
curl -b cookie.txt -X DELETE $API_URL/api/v1/cities/6; echo
echo
echo "Testing GET route after DELETE '/api/v1/cities/:cityID'..."
curl -b cookie.txt $API_URL/api/v1/cities/6; echo
# Testing '/api/v1/auth/logout'.
echo
echo "Testing POST route '/api/v1/auth/logout'..."
curl -X POST $API_URL/api/v1/auth/logout; echo
# Finish end-to-end testing.
rm cookie.txt
echo "Finished testing the application!"

View File

@ -1,8 +0,0 @@
INSERT INTO labels (id, type, name, count)
VALUES (1, '', 'bill', 0),
(2, '', 'bill', 0),
(3, '', 'bill', 0),
(4, '', 'bill', 0),
(5, '', 'bill', 0),
(6, '', 'bill', 0),
(7, '', 'bill', 0);