Setting un invoice pdf microservice
This commit is contained in:
86
cmd/micro/invoice/helpers.go
Normal file
86
cmd/micro/invoice/helpers.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type JSONResponse struct {
|
||||
OK bool `json:"ok"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (app *application) readJSON(w http.ResponseWriter, r *http.Request, data interface{}) error {
|
||||
maxBytes := 1048576
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
err := dec.Decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure there is only one entry.
|
||||
err = dec.Decode(&struct{}{})
|
||||
if err != io.EOF {
|
||||
return errors.New("body must only have a single JSON value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeJSON writes arbitrary data out as JSON
|
||||
func (app *application) writeJSON(
|
||||
w http.ResponseWriter,
|
||||
status int, data interface{},
|
||||
headers ...http.Header,
|
||||
) error {
|
||||
out, err := json.MarshalIndent(data, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(headers) > 0 {
|
||||
for k, v := range headers[0] {
|
||||
w.Header()[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
w.Write(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) badRequest(w http.ResponseWriter, r *http.Request, err error) error {
|
||||
var payload JSONResponse
|
||||
|
||||
payload.OK = false
|
||||
payload.Message = err.Error()
|
||||
|
||||
out, err := json.MarshalIndent(payload, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
w.Write(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) CreateDirIfNotExist(path string) error {
|
||||
const mode = 0755
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
err := os.Mkdir(path, mode)
|
||||
if err != nil {
|
||||
app.errorLog.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
145
cmd/micro/invoice/invoice-handlers.go
Normal file
145
cmd/micro/invoice/invoice-handlers.go
Normal file
@ -0,0 +1,145 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/phpdave11/gofpdf"
|
||||
"github.com/phpdave11/gofpdf/contrib/gofpdi"
|
||||
)
|
||||
|
||||
type Order struct {
|
||||
ID int `json:"id"`
|
||||
Quantity int `json:"quantity"`
|
||||
Amount int `json:"amount"`
|
||||
Product string `json:"product"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Email string `json:"email"`
|
||||
CreatedAt time.Time `json:"-"`
|
||||
}
|
||||
|
||||
func (app *application) CreateAndSendInvoice(w http.ResponseWriter, r *http.Request) {
|
||||
// receive json
|
||||
var order Order
|
||||
|
||||
err := app.readJSON(w, r, &order)
|
||||
if err != nil {
|
||||
app.badRequest(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// generate a pdf invoice
|
||||
|
||||
// create mail
|
||||
|
||||
// send mail with attachment
|
||||
|
||||
// send response
|
||||
var resp JSONResponse
|
||||
resp.OK = true
|
||||
resp.Message = fmt.Sprintf("Invoice %d.pdf created and sent to %s", order.ID, order.Email)
|
||||
app.writeJSON(w, http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
func (app *application) createInvoicePDF(order Order) error {
|
||||
pdf := gofpdf.New("P", "mm", "Letter", "")
|
||||
pdf.SetMargins(10, 13, 10)
|
||||
pdf.SetAutoPageBreak(true, 0)
|
||||
|
||||
importer := gofpdi.NewImporter()
|
||||
|
||||
t := importer.ImportPage(pdf, "./pdf-templates/invoice.pdf", q, "/MediaBox")
|
||||
|
||||
pdf.AddPage()
|
||||
importer.UseImportedTemplate(pdf, t, 0, 0, 215.9, 0)
|
||||
|
||||
pdf.SetY(20)
|
||||
|
||||
// write info
|
||||
pdf.SetY(50)
|
||||
pdf.SetX(10)
|
||||
pdf.SetFont("Times", "", 11)
|
||||
pdf.CellFormat(
|
||||
97,
|
||||
8,
|
||||
fmt.Sprintf("Attention: %s %s", order.FirstName, order.LastName),
|
||||
"",
|
||||
0,
|
||||
"L",
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
)
|
||||
pdf.Ln(5)
|
||||
pdf.CellFormat(
|
||||
97,
|
||||
8,
|
||||
order.Email,
|
||||
"",
|
||||
0,
|
||||
"L",
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
)
|
||||
pdf.Ln(5)
|
||||
pdf.CellFormat(
|
||||
97,
|
||||
8,
|
||||
order.CreatedAt.Format("2005-01-02"),
|
||||
"",
|
||||
0,
|
||||
"L",
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
)
|
||||
|
||||
pdf.SetX(58)
|
||||
pdf.SetY(93)
|
||||
pdf.CellFormat(
|
||||
155,
|
||||
8,
|
||||
order.Product,
|
||||
"",
|
||||
0,
|
||||
"L",
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
)
|
||||
pdf.SetX(166)
|
||||
pdf.CellFormat(
|
||||
20,
|
||||
8,
|
||||
fmt.Sprintf("%d", order.Quantity),
|
||||
"",
|
||||
0,
|
||||
"C",
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
)
|
||||
|
||||
pdf.SetX(185)
|
||||
pdf.CellFormat(
|
||||
155,
|
||||
8,
|
||||
fmt.Sprintf("€%.2f", float32(order.Amount/100.0)),
|
||||
"",
|
||||
0,
|
||||
"R",
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
)
|
||||
|
||||
invoicePath := fmt.Sprintf("./invoices/%d.pdf", order.ID)
|
||||
err := pdf.OutputFileAndClose(invoicePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
23
cmd/micro/invoice/invoice-routes.go
Normal file
23
cmd/micro/invoice/invoice-routes.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
||||
func (app *application) routes() http.Handler {
|
||||
mux := chi.NewRouter()
|
||||
|
||||
mux.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{"https://*", "http://*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||
AllowCredentials: false,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
|
||||
mux.Post("/invoice/create-and-send", app.CreateAndSendInvoice)
|
||||
return mux
|
||||
}
|
79
cmd/micro/invoice/invoice.go
Normal file
79
cmd/micro/invoice/invoice.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
version = "1.0.0"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
port int
|
||||
smtp struct {
|
||||
host string
|
||||
port int
|
||||
username string
|
||||
password string
|
||||
}
|
||||
frontend string
|
||||
}
|
||||
|
||||
type application struct {
|
||||
config config
|
||||
infoLog *log.Logger
|
||||
errorLog *log.Logger
|
||||
version string
|
||||
}
|
||||
|
||||
func (app *application) serve() error {
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", app.config.port),
|
||||
Handler: app.routes(),
|
||||
IdleTimeout: 30 * time.Second,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
app.infoLog.Printf(
|
||||
"Starting invoice microservice on port %d",
|
||||
app.config.port,
|
||||
)
|
||||
return srv.ListenAndServe()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var cfg config
|
||||
|
||||
flag.IntVar(&cfg.port, "port", 5000, "Server port to listen on")
|
||||
flag.StringVar(&cfg.smtp.host, "smtphost", "0.0.0.0", "smtp host")
|
||||
flag.IntVar(&cfg.smtp.port, "smtpport", 1025, "smtp host")
|
||||
flag.StringVar(&cfg.smtp.username, "smtpuser", "user", "smtp user")
|
||||
flag.StringVar(&cfg.smtp.password, "smtppwd", "password", "smtp password")
|
||||
flag.StringVar(&cfg.frontend, "frontend", "http://localhost:4000", "frontend address")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
|
||||
errorLog := log.New(os.Stdout, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
app := &application{
|
||||
version: version,
|
||||
config: cfg,
|
||||
infoLog: infoLog,
|
||||
errorLog: errorLog,
|
||||
}
|
||||
|
||||
app.CreateDirIfNotExist("./invoices")
|
||||
|
||||
err := app.serve()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user