refactor building invoice

This commit is contained in:
Ruidy 2025-02-04 10:39:07 +01:00
parent 7796f71590
commit 85c65f2201
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
10 changed files with 56 additions and 138 deletions

File diff suppressed because one or more lines are too long

View file

@ -149,12 +149,12 @@
<body> <body>
<div class="header space-between"> <div class="header space-between">
<img class="logo" src="assets/img/logo.png" /> <img class="logo" src="/static/img/logo.png" />
<div class="payee"> <div class="payee">
<b>{{ .Host.Name }}</b><br /> <b>{{ .Host.Name }}</b><br />
{{ .Host.Address }}<br /> {{ .Host.Address }}<br />
{{ .Host.ZipCode }} {{ .Host.City }}<br /> {{ .Host.ZipCode }} {{ .Host.City }}<br />
<b>Tel :</b> {{ .Host.Phone }}<br /> <b>Tel :</b> {{ .Host.PhoneNumber }}<br />
<b>Mail :</b> {{ .Host.Email }}<br /> <b>Mail :</b> {{ .Host.Email }}<br />
</div> </div>
</div> </div>
@ -170,7 +170,7 @@
<td>{{ .PhoneNumber }}</td> <td>{{ .PhoneNumber }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Client :</strong></td> <td><strong>Nombre de clients :</strong></td>
<td>{{ .CustomersNumber }}</td> <td>{{ .CustomersNumber }}</td>
</tr> </tr>
<tr> <tr>

View file

@ -1,83 +0,0 @@
package main
import (
"bytes"
"html/template"
"log"
"os"
"time"
"github.com/rjNemo/rentease/internal/config"
"github.com/rjNemo/rentease/internal/service/booking"
)
func main() {
type Invoice struct {
Host struct {
Name string
Address string
ZipCode string
City string
Phone string
Email string
}
Name string
PhoneNumber string
CustomersNumber int
Platform string
ID string
From string
To string
Total string
AmountPaid string
BalanceDue string
Lines []struct {
Name string
Quantity int
Price string
Total string
}
Payments []struct {
Date string
Method string
Amount string
}
}
// Assume these values come from your application's context.
host := config.NewHost()
booking := booking.Booking{
Name: "Michel Le Corre",
PhoneNumber: "+590 690 44 15 30",
CustomerNumber: 2,
Platform: "Privée",
From: time.Date(2025, time.January, 15, 0, 0, 0, 0, time.UTC),
To: time.Date(2025, time.March, 15, 0, 0, 0, 0, time.UTC),
Items: []booking.Item{
{Item: "T2", Quantity: 59, Price: 35.00},
},
}
// Get dynamic invoice data from the booking via Serialize.
invoiceData := booking.Serialize(host)
// Parse the HTML template.
tmpl, err := template.ParseFiles("assets/html/invoice.html")
if err != nil {
log.Fatalf("Error parsing template: %v", err)
}
// Create a buffer to hold the rendered HTML.
var buf bytes.Buffer
if err := tmpl.Execute(&buf, invoiceData); err != nil {
log.Fatalf("Error executing template: %v", err)
}
// Write the rendered HTML to an output file.
outputPath := "output.html"
if err := os.WriteFile(outputPath, buf.Bytes(), 0644); err != nil {
log.Fatalf("Error writing HTML file: %v", err)
}
log.Printf("HTML file created successfully: %s", outputPath)
}

View file

@ -2,6 +2,7 @@ package server
import ( import (
"fmt" "fmt"
"log"
"net/http" "net/http"
"strconv" "strconv"
@ -17,17 +18,18 @@ func handlePdfCreateInvoice(bs *booking.Service, hc *config.Host) echo.HandlerFu
idStr := c.Param("id") idStr := c.Param("id")
id, err := strconv.Atoi(idStr) id, err := strconv.Atoi(idStr)
if err != nil { if err != nil {
log.Println(err)
return err return err
} }
b := bs.One(id) b := bs.One(id)
err = bs.BuildInvoice(b, hc) filePath, err := bs.BuildInvoice(b, hc)
if err != nil { if err != nil {
log.Println(err)
return err return err
} }
return c.File("output.html") return c.File(filePath)
// return c.File("output.html", fmt.Sprintf("VFNI-%s.pdf", b.InvoiceNumber(hc)))
} }
} }

View file

@ -22,7 +22,6 @@ func SentryTracingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
sentry.WithOpName("http.server"), sentry.WithOpName("http.server"),
sentry.ContinueFromRequest(c.Request()), sentry.ContinueFromRequest(c.Request()),
sentry.WithTransactionSource(sentry.SourceURL), sentry.WithTransactionSource(sentry.SourceURL),
} }
transaction := sentry.StartTransaction(ctx, transaction := sentry.StartTransaction(ctx,

View file

@ -1,5 +1,7 @@
package booking package booking
import "github.com/rjNemo/rentease/internal/config"
type InvoiceLine struct { type InvoiceLine struct {
Name string Name string
Quantity int Quantity int
@ -14,14 +16,7 @@ type PaymentLine struct {
} }
type Invoice struct { type Invoice struct {
Host struct { Host config.Host
Name string
Address string
ZipCode string
City string
Phone string
Email string
}
Name string Name string
PhoneNumber string PhoneNumber string
CustomersNumber int CustomersNumber int

View file

@ -58,21 +58,7 @@ func (b Booking) ToInvoice(hc *config.Host) Invoice {
}, 0.0) }, 0.0)
return Invoice{ return Invoice{
Host: struct { Host: *hc,
Name string
Address string
ZipCode string
City string
Phone string
Email string
}{
Name: hc.Name,
Address: hc.Address,
ZipCode: hc.ZipCode,
City: hc.City,
Phone: hc.PhoneNumber,
Email: hc.Email,
},
Name: b.Name, Name: b.Name,
PhoneNumber: b.PhoneNumber, PhoneNumber: b.PhoneNumber,
CustomersNumber: b.CustomerNumber, CustomersNumber: b.CustomerNumber,

View file

@ -2,6 +2,7 @@ package booking
import ( import (
"bytes" "bytes"
"fmt"
"log" "log"
"os" "os"
"text/template" "text/template"
@ -103,27 +104,26 @@ func (bs Service) Cancel(id int) {
} }
} }
func (bs Service) BuildInvoice(b *Booking, hc *config.Host) error { func (bs Service) BuildInvoice(b *Booking, hc *config.Host) (string, error) {
invoiceData := b.ToInvoice(hc) invoiceData := b.ToInvoice(hc)
log.Printf("%+v", invoiceData)
tmpl, err := template.ParseFiles("assets/html/invoice.html") tmpl, err := template.ParseFiles("assets/html/invoice.html")
if err != nil { if err != nil {
log.Fatalf("Error parsing template: %v", err) return "", fmt.Errorf("Error parsing template: %v", err)
} }
// Create a buffer to hold the rendered HTML. // Create a buffer to hold the rendered HTML.
var buf bytes.Buffer var buf bytes.Buffer
if err := tmpl.Execute(&buf, invoiceData); err != nil { if err := tmpl.Execute(&buf, invoiceData); err != nil {
log.Fatalf("Error executing template: %v", err) return "", fmt.Errorf("error executing template: %v", err)
} }
// Write the rendered HTML to an output file. outputPath := fmt.Sprintf("%s.html", b.InvoiceNumber(hc))
outputPath := "output.html"
if err := os.WriteFile(outputPath, buf.Bytes(), 0644); err != nil { if err := os.WriteFile(outputPath, buf.Bytes(), 0644); err != nil {
log.Fatalf("Error writing HTML file: %v", err) return "", fmt.Errorf("error writing HTML file: %v", err)
} }
log.Printf("HTML file created successfully: %s", outputPath) log.Printf("HTML file created successfully: %s", outputPath)
return nil return outputPath, nil
} }

View file

@ -14,16 +14,30 @@ templ BookingById(booking *BookingViewModel) {
</hgroup> </hgroup>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<a class="btn btn-primary btn-sm" href={ booking.PdfUrl } target="_blank">Create PDF</a> <a class="btn btn-primary btn-sm" href={ booking.PdfUrl } target="_blank">Create PDF</a>
<a href="https://web.whatsapp.com/" target="_blank" rel="noreferrer noopener" class="btn btn-ghost btn-sm btn-square"> <a
href="https://web.whatsapp.com/"
target="_blank"
rel="noreferrer noopener"
class="btn btn-ghost btn-sm btn-square"
>
<img src="/static/icons/whatsapp.png" class="w-6 h-6"/> <img src="/static/icons/whatsapp.png" class="w-6 h-6"/>
</a> </a>
<a href="https://dashboard.stripe.com/payments/new" target="_blank" rel="noreferrer noopener" class="btn btn-ghost btn-sm btn-square"> <a
href="https://dashboard.stripe.com/payments/new"
target="_blank"
rel="noreferrer noopener"
class="btn btn-ghost btn-sm btn-square"
>
<img src="/static/icons/stripe.png" class="w-6 h-6"/> <img src="/static/icons/stripe.png" class="w-6 h-6"/>
</a> </a>
if booking.Canceled { if booking.Canceled {
<span class="badge badge-error">Canceled</span> <span class="badge badge-error">Canceled</span>
} else { } else {
<button class="btn btn-outline btn-error btn-sm" hx-patch={ booking.CancelUrl } hx-swap="outerHTML">Cancel</button> <button
class="btn btn-outline btn-error btn-sm"
hx-patch={ booking.CancelUrl }
hx-swap="outerHTML"
>Cancel</button>
} }
</div> </div>
</section> </section>
@ -31,7 +45,9 @@ templ BookingById(booking *BookingViewModel) {
@BookingForm(*booking) @BookingForm(*booking)
</section> </section>
<section class="p-4 bg-base-100 rounded-lg shadow-sm"> <section class="p-4 bg-base-100 rounded-lg shadow-sm">
<h3 class="text-xl font-semibold mb-4 flex justify-between items-center">Line Items <button class="btn btn-sm btn-success" onclick="payment_modal.showModal()">Add Payment</button></h3> <h3 class="text-xl font-semibold mb-4 flex justify-between items-center">
Line Items <button class="btn btn-sm btn-success" onclick="payment_modal.showModal()">Add Payment</button>
</h3>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table table-zebra w-full"> <table class="table table-zebra w-full">
<thead> <thead>
@ -57,7 +73,14 @@ templ BookingById(booking *BookingViewModel) {
</section> </section>
<details class="collapse bg-base-200 mt-8"> <details class="collapse bg-base-200 mt-8">
<summary class="collapse-title text-xl font-medium flex items-center gap-2 hover:bg-base-300"> <summary class="collapse-title text-xl font-medium flex items-center gap-2 hover:bg-base-300">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"></path> <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"></path>
</svg> </svg>
Add New Line Item Add New Line Item

View file

@ -98,7 +98,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(booking.CancelUrl) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(booking.CancelUrl)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 26, Col: 82} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 38, Col: 34}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -132,7 +132,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var7 string var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Total) templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Total)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 51, Col: 26} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 67, Col: 26}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -145,7 +145,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var8 string var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s/items", booking.Url)) templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s/items", booking.Url))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 67, Col: 51} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 90, Col: 51}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -163,7 +163,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var9 string var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(item) templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(item)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 79, Col: 28} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 102, Col: 28}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -176,7 +176,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var10 string var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(item) templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(item)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 79, Col: 37} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 102, Col: 37}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -199,7 +199,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var11 string var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(paymentMethod) templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(paymentMethod)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 115, Col: 37} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 138, Col: 37}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -212,7 +212,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
var templ_7745c5c3_Var12 string var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(paymentMethod) templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(paymentMethod)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 115, Col: 55} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 138, Col: 55}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -269,7 +269,7 @@ func PaymentModal(paymentUrl string) templ.Component {
var templ_7745c5c3_Var14 string var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(paymentUrl) templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(paymentUrl)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 137, Col: 24} return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/view/booking_by_id.templ`, Line: 160, Col: 24}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {