mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-12 13:46:51 +00:00
send data to render pdf
This commit is contained in:
parent
b42fe35831
commit
ec853f6b66
14 changed files with 62 additions and 27 deletions
|
|
@ -10,6 +10,7 @@ Manage your holiday rental
|
||||||
- [x] Build the pdf invoice
|
- [x] Build the pdf invoice
|
||||||
- [ ] Refactor the env variable calls to a Config struct with proper defaults
|
- [ ] Refactor the env variable calls to a Config struct with proper defaults
|
||||||
- [x] Refactor handlers to call their dependencies instead of taking them from the Server struct
|
- [x] Refactor handlers to call their dependencies instead of taking them from the Server struct
|
||||||
|
- [ ] Embed mandatory assets, css in the executable
|
||||||
|
|
||||||
## Built With
|
## Built With
|
||||||
|
|
||||||
|
|
|
||||||
7
config/host.go
Normal file
7
config/host.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
type Host struct{}
|
||||||
|
|
||||||
|
func NewHost() *Host {
|
||||||
|
return &Host{}
|
||||||
|
}
|
||||||
|
|
@ -7,26 +7,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Booking struct {
|
type Booking struct {
|
||||||
|
From time.Time
|
||||||
|
To time.Time
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Id int
|
|
||||||
Name string `gorm:"column:customer_name"`
|
Name string `gorm:"column:customer_name"`
|
||||||
PhoneNumber string
|
PhoneNumber string
|
||||||
CustomerNumber int `gorm:"column:customers"`
|
|
||||||
Email string
|
Email string
|
||||||
From time.Time
|
|
||||||
To time.Time
|
|
||||||
Platform string
|
Platform string
|
||||||
PlatformFees float64 `gorm:"type:decimal(10,2)"`
|
|
||||||
Items []Item
|
Items []Item
|
||||||
|
Id int
|
||||||
|
CustomerNumber int `gorm:"column:customers"`
|
||||||
|
PlatformFees float64 `gorm:"type:decimal(10,2)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Id int
|
|
||||||
BookingId int
|
|
||||||
Item string
|
Item string
|
||||||
Quantity int
|
|
||||||
Price float64 `gorm:"type:decimal(10,2)"`
|
|
||||||
PaymentMethod string
|
PaymentMethod string
|
||||||
PaymentStatus string `gorm:"default:Pending"`
|
PaymentStatus string `gorm:"default:Pending"`
|
||||||
|
Id int
|
||||||
|
BookingId int
|
||||||
|
Quantity int
|
||||||
|
Price float64 `gorm:"type:decimal(10,2)"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,16 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
|
|
||||||
|
"github.com/rjNemo/rentease/internal/booking"
|
||||||
|
u "github.com/rjNemo/underscore"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PdfService struct {
|
type PdfService struct {
|
||||||
|
|
@ -27,13 +32,25 @@ func NewPdfService() *PdfService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps PdfService) BuildInvoice() error {
|
func (ps PdfService) BuildInvoice(b *booking.Booking) error {
|
||||||
data := struct {
|
data := struct {
|
||||||
Context map[string]any `json:"context"`
|
Context map[string]any `json:"context"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
ProjectId string `json:"projectId"`
|
ProjectId string `json:"projectId"`
|
||||||
}{
|
}{
|
||||||
Context: map[string]any{},
|
Context: map[string]any{
|
||||||
|
"id": fmt.Sprintf("VFNI%04s", strconv.Itoa(b.Id)),
|
||||||
|
"name": b.Name,
|
||||||
|
"phone_number": b.PhoneNumber,
|
||||||
|
"custumers_number": b.CustomerNumber,
|
||||||
|
"platform": b.Platform,
|
||||||
|
"from": b.From.Format("Monday 02 January 2006"),
|
||||||
|
"to": b.To.Format("Monday 02 January 2006"),
|
||||||
|
"lines": b.Items,
|
||||||
|
"total": strconv.FormatFloat(u.Reduce(b.Items, func(i booking.Item, sum float64) float64 {
|
||||||
|
return sum + i.Price*float64(i.Quantity)
|
||||||
|
}, 0.0), 'f', 2, 64),
|
||||||
|
},
|
||||||
Path: ps.invoicePath,
|
Path: ps.invoicePath,
|
||||||
ProjectId: ps.projectId,
|
ProjectId: ps.projectId,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,25 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/rjNemo/rentease/internal/booking"
|
||||||
"github.com/rjNemo/rentease/internal/pdf"
|
"github.com/rjNemo/rentease/internal/pdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleCreateInvoicePdf(ps *pdf.PdfService) echo.HandlerFunc {
|
func handleCreateInvoicePdf(bs *booking.Service, ps *pdf.PdfService) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
err := ps.BuildInvoice()
|
idStr := c.Param("id")
|
||||||
|
c.Logger().Info(idStr)
|
||||||
|
id, err := strconv.Atoi(idStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b := bs.One(id)
|
||||||
|
|
||||||
|
err = ps.BuildInvoice(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
func (s Server) MountHandlers() {
|
func (s Server) MountHandlers() {
|
||||||
// landing page
|
|
||||||
s.Router.GET("/", handleHomePage())
|
s.Router.GET("/", handleHomePage())
|
||||||
s.Router.GET("/bookings", handleListBookingPage(s.bs))
|
s.Router.GET("/bookings", handleListBookingPage(s.bs))
|
||||||
s.Router.GET("/bookings/new", handleNewBookingPage())
|
s.Router.GET("/bookings/new", handleNewBookingPage())
|
||||||
s.Router.POST("/bookings/new", handleCreateBooking(s.bs))
|
s.Router.POST("/bookings/new", handleCreateBooking(s.bs))
|
||||||
s.Router.GET("/bookings/:id", handleBookingPage(s.bs))
|
s.Router.GET("/bookings/:id", handleBookingPage(s.bs))
|
||||||
s.Router.POST("bookings/:id/items", handleCreateItem(s.bs))
|
s.Router.POST("/bookings/:id/items", handleCreateItem(s.bs))
|
||||||
|
s.Router.GET("/bookings/pdf/:id", handleCreateInvoicePdf(s.bs, s.ps))
|
||||||
s.Router.GET("/reports", handleReportsPage())
|
s.Router.GET("/reports", handleReportsPage())
|
||||||
s.Router.GET("/reports/do", handleComputeReport(s.bs))
|
s.Router.GET("/reports/do", handleComputeReport(s.bs))
|
||||||
s.Router.GET("/pdf/:id", handleCreateInvoicePdf(s.bs, s.ps))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,20 @@ import (
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
|
"github.com/rjNemo/rentease/config"
|
||||||
"github.com/rjNemo/rentease/internal/booking"
|
"github.com/rjNemo/rentease/internal/booking"
|
||||||
"github.com/rjNemo/rentease/internal/pdf"
|
"github.com/rjNemo/rentease/internal/pdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Router *echo.Echo
|
Router *echo.Echo
|
||||||
db *gorm.DB
|
|
||||||
bs *booking.Service
|
bs *booking.Service
|
||||||
ps *pdf.PdfService
|
ps *pdf.PdfService
|
||||||
addr string
|
addr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(bs *booking.Service, ps *pdf.PdfService) *Server {
|
func New(bs *booking.Service, ps *pdf.PdfService, hc *config.Host) *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Router: NewRouter(),
|
Router: NewRouter(),
|
||||||
bs: bs,
|
bs: bs,
|
||||||
|
|
@ -91,7 +90,6 @@ func customHTTPErrorHandler(e *echo.Echo) echo.HTTPErrorHandler {
|
||||||
if errors.As(err, &he) {
|
if errors.As(err, &he) {
|
||||||
code = he.Code
|
code = he.Code
|
||||||
}
|
}
|
||||||
e.Logger.Error(err)
|
|
||||||
|
|
||||||
errorPage := fmt.Sprintf("assets/html/HTTP%d.html", code)
|
errorPage := fmt.Sprintf("assets/html/HTTP%d.html", code)
|
||||||
if err := c.File(errorPage); err != nil {
|
if err := c.File(errorPage); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ templ BookingById(booking *BookingViewModel) {
|
||||||
<section>
|
<section>
|
||||||
<h3>Line Items </h3>
|
<h3>Line Items </h3>
|
||||||
<div class="overflow-auto">
|
<div class="overflow-auto">
|
||||||
<table role="grid">
|
<table class="striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Item</th>
|
<th scope="col">Item</th>
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ func BookingById(booking *BookingViewModel) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div></fieldset><button type=\"submit\">Update</button></form></section><section><h3>Line Items </h3><div class=\"overflow-auto\"><table role=\"grid\"><thead><tr><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\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div></fieldset><button type=\"submit\">Update</button></form></section><section><h3>Line Items </h3><div class=\"overflow-auto\"><table class=\"striped\"><thead><tr><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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ templ ListBookings(bookings []*ListBookingsViewModel) {
|
||||||
<h2>Overview of the activity</h2>
|
<h2>Overview of the activity</h2>
|
||||||
</hgroup>
|
</hgroup>
|
||||||
<div class="overflow-auto">
|
<div class="overflow-auto">
|
||||||
<table role="grid">
|
<table class="striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">ID</th>
|
<th scope="col">ID</th>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ func ListBookings(bookings []*ListBookingsViewModel) templ.Component {
|
||||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<hgroup><h1>Bookings </h1><h2>Overview of the activity</h2></hgroup><div class=\"overflow-auto\"><table role=\"grid\"><thead><tr><th scope=\"col\">ID</th><th scope=\"col\">Name</th><th scope=\"col\">Total (€)</th><th scope=\"col\">From</th><th scope=\"col\">To</th><th scope=\"col\">Platform</th></tr></thead> <tbody>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<hgroup><h1>Bookings </h1><h2>Overview of the activity</h2></hgroup><div class=\"overflow-auto\"><table class=\"striped\"><thead><tr><th scope=\"col\">ID</th><th scope=\"col\">Name</th><th scope=\"col\">Total (€)</th><th scope=\"col\">From</th><th scope=\"col\">To</th><th scope=\"col\">Platform</th></tr></thead> <tbody>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ templ ReportSection(report []*ReportViewModel) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-auto">
|
<div class="overflow-auto">
|
||||||
<table role="grid">
|
<table class="striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">ID</th>
|
<th scope="col">ID</th>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func ReportSection(report []*ReportViewModel) templ.Component {
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid\"><h2>Your report </h2><div><button class=\"outline\">Create PDF</button></div></div><div class=\"overflow-auto\"><table role=\"grid\"><thead><tr><th scope=\"col\">ID</th><th scope=\"col\">Name</th><th scope=\"col\">From</th><th scope=\"col\">To</th><th scope=\"col\">Total (€)</th><th scope=\"col\">Platform</th><th scope=\"col\">Platform Fees</th></tr></thead> <tbody>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid\"><h2>Your report </h2><div><button class=\"outline\">Create PDF</button></div></div><div class=\"overflow-auto\"><table class=\"striped\"><thead><tr><th scope=\"col\">ID</th><th scope=\"col\">Name</th><th scope=\"col\">From</th><th scope=\"col\">To</th><th scope=\"col\">Total (€)</th><th scope=\"col\">Platform</th><th scope=\"col\">Platform Fees</th></tr></thead> <tbody>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
main.go
3
main.go
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"github.com/rjNemo/rentease/config"
|
||||||
"github.com/rjNemo/rentease/internal/booking"
|
"github.com/rjNemo/rentease/internal/booking"
|
||||||
"github.com/rjNemo/rentease/internal/pdf"
|
"github.com/rjNemo/rentease/internal/pdf"
|
||||||
"github.com/rjNemo/rentease/internal/server"
|
"github.com/rjNemo/rentease/internal/server"
|
||||||
|
|
@ -33,5 +34,5 @@ func main() {
|
||||||
log.Fatalf("error migrating the database %s\n", err)
|
log.Fatalf("error migrating the database %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server.New(booking.NewService(db), pdf.NewPdfService()).Start()
|
server.New(booking.NewService(db), pdf.NewPdfService(), config.NewHost()).Start()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue