can create booking invoice

This commit is contained in:
Ruidy 2024-02-16 11:31:17 +01:00
parent 8f15fd4cfd
commit 3f6c5b6c99
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
4 changed files with 94 additions and 54 deletions

View file

@ -4,10 +4,12 @@ Manage your holiday rental
## To Do
- [ ] Create a booking
- [ ] Add line items
- [ ] Read from the database
- [x] Create a booking
- [x] Add line items
- [x] Read from the database
- [ ] Build the pdf invoice
- [ ] Refactor the env variable calls to a Config struct with proper defaults
- [ ] Refactor handlers to call their dependencies instead of taking them from the Server struct
## Built With
@ -15,4 +17,4 @@ Manage your holiday rental
- Htmx
- Templ
- PostgreSQL

View file

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
@ -58,13 +59,13 @@ func (s Server) handleNewBookingPage() echo.HandlerFunc {
func (s Server) handleCreateBooking() echo.HandlerFunc {
return func(c echo.Context) error {
type NewBooking struct {
Name string `form:"name"`
PhoneNumber string `form:"phone_number"`
CustomerNumber int `form:"customer_number"`
Email string `form:"email"`
From time.Time `json:"from"`
To time.Time `from:"to"`
Name string `form:"name"`
PhoneNumber string `form:"phone_number"`
Email string `form:"email"`
Platform string `form:"platform"`
CustomerNumber int `form:"customer_number"`
PlatformFees float64 `form:"platform_fees"`
}
nb := new(NewBooking)
@ -220,12 +221,12 @@ func (s Server) handleComputeReport() echo.HandlerFunc {
}
type Result struct {
Id string
Total float64
CustomerName string
From time.Time
To time.Time
Id string
CustomerName string
Platform string
Total float64
PlatformFees float64
}
res := make([]*Result, 0)
@ -271,26 +272,26 @@ func (s Server) handleComputeReport() echo.HandlerFunc {
}
func (s Server) handleCreateInvoicePdf() echo.HandlerFunc {
return func(_ echo.Context) error {
return func(c echo.Context) error {
data := struct {
Context map[string]any `json:"context"`
Path string `json:"path"`
ProjectId string `json:"projectId"`
}{
Context: map[string]interface{}{},
Path: "index.html",
Context: map[string]any{},
Path: "index.html", // TODO: put in a variable
ProjectId: os.Getenv("HTMLDOCS_PROJECT_ID"),
}
payload, err := json.Marshal(data)
if err != nil {
fmt.Println("Error marshalling JSON:", err)
log.Warnf("Error marshalling JSON:", err)
return err
}
req, err := http.NewRequest("POST", os.Getenv("HTMLDOCS_URL"), bytes.NewBuffer(payload))
if err != nil {
fmt.Println("Error creating request:", err)
log.Warnf("Error creating request:", err)
return err
}
@ -300,14 +301,51 @@ func (s Server) handleCreateInvoicePdf() echo.HandlerFunc {
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
log.Warnf("Error sending request:", err)
return err
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
fmt.Println("Response Status:", resp.Body)
log.Warnf("Response Status:", resp.Status)
type response struct {
Url string `json:"url"`
Error string `json:"error"`
}
res := new(response)
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(body, res)
if err != nil {
return err
}
log.Warnf("Response body:", res)
return nil
resp, err = http.Get(res.Url)
if err != nil {
log.Warnf("Error retrieving file")
return err
}
defer resp.Body.Close()
// Copy the file content to the response writer
file, err := os.Create("tmp.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
return err
}
_, err = file.Write(body)
if err != nil {
log.Error("Error copying file content")
return err
}
return c.Attachment("tmp.pdf", "tmp.pdf")
}
}

View file

@ -35,7 +35,7 @@ templ BookingById(booking *BookingViewModel) {
<h2>Manage a booking </h2>
</hgroup>
<div>
<button class="outline">Create PDF</button>
<a class="outline" role="button" href="/pdf" target="_blank">Create PDF</a>
</div>
</div>
<form method="POST">

View file

@ -56,7 +56,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid\"><hgroup><h1>Booking ID VFNI#")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -69,7 +69,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><h2>Manage a booking </h2></hgroup><div><a class=\"outline\" role=\"button\" href=\"/pdf\" target=\"_blank\">Create PDF</a></div></div><form method=\"POST\"><div class=\"grid\"><label for=\"name\">Customer name <input type=\"text\" id=\"name\" name=\"name\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -77,7 +77,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" required autofocus></label> <label for=\"phone_number\">Phone number <input type=\"tel\" id=\"phone_number\" name=\"phone_number\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -85,7 +85,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div><div class=\"grid\"><label for=\"customer_number\">Customer number <input type=\"number\" id=\"customer_number\" name=\"customer_number\" required value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -93,7 +93,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label> <label for=\"email\">Email <input type=\"email\" id=\"email\" name=\"email\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -101,7 +101,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 6)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div><div class=\"grid\"><label for=\"from\">From <input type=\"date\" id=\"from\" name=\"from\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -109,7 +109,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 7)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label> <label for=\"to\">To <input type=\"date\" id=\"to\" name=\"to\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -117,7 +117,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 8)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div><div class=\"grid\"><label for=\"platform\">Platform <select id=\"platform\" name=\"platform\"><option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -125,7 +125,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 9)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" selected>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -138,12 +138,12 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 10)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, platform := range booking.Platforms {
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 11)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -151,7 +151,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 12)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -164,12 +164,12 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 13)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 14)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></label> <label for=\"platform_fees\">Fees <input type=\"number\" id=\"platform_fees\" inputmode=\"decimal\" step=\"0.01\" name=\"platform_fees\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -177,12 +177,12 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 15)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div><button type=\"submit\">Update</button></form><h3>Line Items </h3><figure><table role=\"grid\"><thead><tr><th scope=\"col\">#</th><th scope=\"col\">Item</th><th scope=\"col\">Quantity</th><th scope=\"col\">Price (€)</th><th scope=\"col\">Payment Method</th><th scope=\"col\">Payment Status</th><th scope=\"col\">Sub-total (€)</th></tr></thead> <tbody id=\"line-items\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range booking.Items {
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 16)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><th scope=\"row\"></th><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -195,7 +195,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 17)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -208,7 +208,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 18)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -221,7 +221,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 19)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -234,7 +234,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 20)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -247,7 +247,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 21)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -260,12 +260,12 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 22)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 23)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody><tfoot><th scope=\"col\"></th><th scope=\"col\"></th><th scope=\"col\"></th><th scope=\"col\"></th><th scope=\"col\"></th><th scope=\"col\"></th><th scope=\"col\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -278,7 +278,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 24)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</th></tfoot></table></figure><details><summary role=\"button\" class=\"secondary\">Add line </summary><form hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -286,12 +286,12 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 25)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#line-items\" hx-swap=\"afterend\" hx-on::after-request=\" if(event.detail.successful) this.reset()\"><article><label for=\"new-line-item\">Item <select name=\"item\" id=\"new-line-item\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, item := range booking.ItemList {
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 26)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -299,7 +299,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 27)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -312,17 +312,17 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 28)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 29)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></label> <label for=\"new-line-quantity\">Quantity <input type=\"number\" name=\"quantity\" id=\"new-line-quantity\"></label> <label for=\"new-line-price\">Price <input type=\"number\" name=\"price\" inputmode=\"decimal\" step=\"0.01\" id=\"new-line-price\"></label> <label for=\"new-line-method\">Payment Method <select name=\"method\" id=\"new-line-method\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, paymentMethod := range booking.PaymentMethods {
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 30)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -330,7 +330,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 31)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -343,12 +343,12 @@ func BookingById(booking *BookingViewModel) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 32)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 33)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></label> <button type=\"submit\">Add</button></article></form></details>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}