create invoice

This commit is contained in:
Ruidy 2025-02-04 08:06:15 +01:00
parent bfde4eb601
commit 7796f71590
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
9 changed files with 302 additions and 227 deletions

View file

@ -1,5 +1,6 @@
<!doctype html>
<html lang="fr">
<head>
<style>
body {
@ -194,7 +195,7 @@
</tr>
<tr>
<td><strong>Montant Total :</strong></td>
<td>{{ .Total }}</td>
<td>{{ .Total }}</td>
</tr>
</tbody>
</table>
@ -281,8 +282,9 @@
<div></div>
<div class="total">
<div class="amount-due">Total</div>
<div class="amount-due-total">{{ .Total }}</div>
<div class="amount-due-total">{{ .Total }}</div>
</div>
</div>
</body>
</html>

View file

@ -5,12 +5,13 @@ import (
"html/template"
"log"
"os"
"time"
"github.com/rjNemo/rentease/internal/config"
"github.com/rjNemo/rentease/internal/service/booking"
)
const invoiceTemplate = "assets/html/invoice.html"
func main() {
// Define the invoice data structure
type Invoice struct {
Host struct {
Name string
@ -42,79 +43,37 @@ func main() {
Amount string
}
}
// Assume these values come from your application's context.
host := config.NewHost()
// Read the template file
tmpl, err := template.ParseFiles(invoiceTemplate)
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 sample invoice data
invoice := Invoice{
Host: struct {
Name string
Address string
ZipCode string
City string
Phone string
Email string
}{
Name: "VillaFleurie",
Address: "4 rue Gerty Archimede",
ZipCode: "97190",
City: "Le Gosier",
Phone: "+590 690 44 15 30",
Email: "location.villafleurie@gmail.com",
},
Name: "Michel Le Corre",
//PhoneNumber: "+590 690 44 15 30",
CustomersNumber: 2,
Platform: "Privée",
ID: "VFNI0332",
From: "15 Janvier 2025",
To: "15 Mars 2025",
Total: "2065.00 €",
AmountPaid: "1565.00 €",
BalanceDue: "500.00 €",
Lines: []struct {
Name string
Quantity int
Price string
Total string
}{
{
Name: "T2",
Quantity: 59,
Price: "35.00",
Total: "2065.00",
},
},
Payments: []struct {
Date string
Method string
Amount string
}{
{
"",
"Espèces",
"500.00",
},
{
"",
"Chèque",
"1065.00",
},
},
}
// Create a buffer to store the rendered HTML
// Create a buffer to hold the rendered HTML.
var buf bytes.Buffer
// Execute the template with the invoice data
if err := tmpl.Execute(&buf, invoice); err != nil {
if err := tmpl.Execute(&buf, invoiceData); err != nil {
log.Fatalf("Error executing template: %v", err)
}
// Write the rendered HTML to a file
// 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)

View file

@ -40,3 +40,32 @@ func handleSync(bs *booking.Service) echo.HandlerFunc {
return c.JSON(http.StatusCreated, "👍")
}
}
func handleCreateBooking(bs *booking.Service) echo.HandlerFunc {
type BookingInfo struct {
Content string `json:"content"`
}
return func(c echo.Context) error {
log.Info("received booking sync request from booking")
x := c.Request().Body
body, err := io.ReadAll(x)
if err != nil {
return c.String(http.StatusInternalServerError, err.Error())
}
log.Info(string(body))
bookingInfo := new(BookingInfo)
err = json.Unmarshal(body, bookingInfo)
if err != nil {
return c.String(http.StatusInternalServerError, fmt.Sprintf("error unmarshalling JSON: %s", err))
}
b, err := bs.ParseFromApi(bookingInfo.Content)
if err != nil {
return c.String(http.StatusInternalServerError, err.Error())
}
log.Infof("created booking %q from %q", b.Name, b.Platform)
return c.JSON(http.StatusCreated, "👍")
}
}

View file

@ -26,7 +26,8 @@ func handlePdfCreateInvoice(bs *booking.Service, hc *config.Host) echo.HandlerFu
if err != nil {
return err
}
return c.Attachment("tmp.pdf", fmt.Sprintf("VFNI-%s.pdf", b.InvoiceNumber(hc)))
return c.File("output.html")
// return c.File("output.html", fmt.Sprintf("VFNI-%s.pdf", b.InvoiceNumber(hc)))
}
}

View file

@ -19,6 +19,7 @@ func (s Server) MountHandlers() {
}))
api.POST("/sync", handleSync(s.bs))
api.GET("/bookings", handleBookingList(s.bs))
api.POST("/bookings", handleCreateBooking(s.bs))
private := s.Router.Group("")
private.Use(MakeAuthMiddleware(s.as))

View file

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

View file

@ -0,0 +1,37 @@
package booking
type InvoiceLine struct {
Name string
Quantity int
Price string
Total string
}
type PaymentLine struct {
Date string
Method string
Amount string
}
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 []InvoiceLine
Payments []PaymentLine
}

View file

@ -49,34 +49,55 @@ func (b Booking) InvoiceNumber(hc *config.Host) string {
return fmt.Sprintf("%s%04s", hc.InvoicePrefix, strconv.Itoa(b.Id+hc.CustomerSeed))
}
func (b Booking) Serialize(hc *config.Host) map[string]any {
return map[string]any{
"host": map[string]any{
"name": hc.Name,
"address": hc.Address,
"zip": hc.ZipCode,
"city": hc.City,
"phone": hc.PhoneNumber,
"email": hc.Email,
func (b Booking) ToInvoice(hc *config.Host) Invoice {
total := u.Reduce(b.Items, func(i Item, sum float64) float64 {
return sum + i.Price*float64(i.Quantity)
}, 0.0)
amountPaid := u.Reduce(b.Payments, func(i Payment, sum float64) float64 {
return sum + i.Amount
}, 0.0)
return Invoice{
Host: struct {
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,
},
"id": b.InvoiceNumber(hc),
"name": b.Name,
"phone_number": b.PhoneNumber,
"customers_number": b.CustomerNumber,
"platform": b.Platform,
"from": b.From.Format("02/01/2006"),
"to": b.To.Format("02/01/2006"),
"lines": u.Map(b.Items, func(i Item) map[string]any {
return map[string]any{
"name": i.ToFrench(),
"quantity": i.Quantity,
"price": i.Price,
"total": i.Price * float64(i.Quantity),
Name: b.Name,
PhoneNumber: b.PhoneNumber,
CustomersNumber: b.CustomerNumber,
Platform: b.Platform,
ID: b.InvoiceNumber(hc),
From: b.From.Format("02/01/2006"),
To: b.To.Format("02/01/2006"),
Total: strconv.FormatFloat(total, 'f', 2, 64),
AmountPaid: strconv.FormatFloat(amountPaid, 'f', 2, 64),
BalanceDue: strconv.FormatFloat(total-amountPaid, 'f', 2, 64),
Lines: u.Map(b.Items, func(i Item) InvoiceLine {
return InvoiceLine{
Name: i.ToFrench(),
Quantity: i.Quantity,
Price: strconv.FormatFloat(i.Price, 'f', 2, 64),
Total: strconv.FormatFloat(i.Price*float64(i.Quantity), 'f', 2, 64),
}
}),
Payments: u.Map(b.Payments, func(p Payment) PaymentLine {
return PaymentLine{
Date: p.CreatedAt.Format("02/01/2006"),
Method: p.PaymentMethod,
Amount: strconv.FormatFloat(p.Amount, 'f', 2, 64),
}
}),
"total": strconv.FormatFloat(u.Reduce(b.Items, func(i Item, sum float64) float64 {
return sum + i.Price*float64(i.Quantity)
}, 0.0), 'f', 2, 64),
}
}

View file

@ -1,7 +1,10 @@
package booking
import (
"bytes"
"log"
"os"
"text/template"
"time"
"github.com/rjNemo/rentease/internal/config"
@ -101,5 +104,26 @@ func (bs Service) Cancel(id int) {
}
func (bs Service) BuildInvoice(b *Booking, hc *config.Host) error {
return bs.pdf.BuildInvoice(b.Serialize(hc))
invoiceData := b.ToInvoice(hc)
log.Printf("%+v", invoiceData)
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)
return nil
}