mirror of
https://github.com/hazemKrimi/crimson-vault.git
synced 2026-05-01 18:20:27 +00:00
wip: improve invoice generation logic
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@ to quickly create a Cobra application.`,
|
|||||||
// Uncomment the following line if your bare application
|
// Uncomment the following line if your bare application
|
||||||
// has an action associated with it:
|
// has an action associated with it:
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
dir, err := lib.GetConfigDirectory()
|
dir, err := lib.GetConfigDirectoryPath()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.26.0
|
github.com/go-playground/validator/v10 v10.26.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/sessions v1.4.0
|
github.com/gorilla/sessions v1.4.0
|
||||||
|
github.com/johnfercher/maroto/v2 v2.3.1
|
||||||
github.com/labstack/echo-contrib v0.17.4
|
github.com/labstack/echo-contrib v0.17.4
|
||||||
github.com/labstack/echo/v4 v4.13.4
|
github.com/labstack/echo/v4 v4.13.4
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
@@ -38,7 +39,6 @@ require (
|
|||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/johnfercher/go-tree v1.0.5 // indirect
|
github.com/johnfercher/go-tree v1.0.5 // indirect
|
||||||
github.com/johnfercher/maroto/v2 v2.3.1 // indirect
|
|
||||||
github.com/jung-kurt/gofpdf v1.16.2 // indirect
|
github.com/jung-kurt/gofpdf v1.16.2 // indirect
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ github.com/johnfercher/maroto/v2 v2.3.1/go.mod h1:/LfW6AQGZzsG6xUixcfyxkKztDoszd
|
|||||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
|
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
|
||||||
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
|
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/labstack/echo-contrib v0.17.4 h1:g5mfsrJfJTKv+F5uNKCyrjLK7js+ZW6HTjg4FnDxxgk=
|
github.com/labstack/echo-contrib v0.17.4 h1:g5mfsrJfJTKv+F5uNKCyrjLK7js+ZW6HTjg4FnDxxgk=
|
||||||
@@ -142,6 +146,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
@@ -149,6 +155,8 @@ github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wx
|
|||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
|
||||||
|
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@@ -258,6 +266,8 @@ golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
|||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
+83
-2
@@ -85,9 +85,26 @@ func (api *API) CreateInvoiceHandler(context echo.Context) error {
|
|||||||
return types.Error{Code: http.StatusInternalServerError, Cause: errors.New("Unexpected error getting User."), Messages: []string{"Unexpected error getting User!"}}
|
return types.Error{Code: http.StatusInternalServerError, Cause: errors.New("Unexpected error getting User."), Messages: []string{"Unexpected error getting User!"}}
|
||||||
}
|
}
|
||||||
|
|
||||||
lib.GenerateInvoice(invoice, user, client)
|
path, err := lib.GenerateInvoice(invoice, user, client)
|
||||||
|
|
||||||
return context.Attachment(fmt.Sprintf("invoices/%s.pdf", invoice.ID), fmt.Sprintf("invoices/%s.pdf", invoice.ID))
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: err, Messages: []string{"Unexpected error generating Invoice!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
invoiceFileName := fmt.Sprintf("%s.pdf", invoice.ID)
|
||||||
|
invoiceId, err := uuid.Parse(invoice.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusBadRequest, Messages: []string{"Could not parse Invoice ID!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = api.db.UpdateInvoicePDF(userId, invoiceId, path, &invoice)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: err, Messages: []string{"Unexpected error generating Invoice!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.Attachment(path, invoiceFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetAllItemsHandler(context echo.Context) error {
|
func (api *API) GetAllItemsHandler(context echo.Context) error {
|
||||||
@@ -172,6 +189,70 @@ func (api *API) GetInvoiceHandler(context echo.Context) error {
|
|||||||
return context.JSON(http.StatusOK, invoice)
|
return context.JSON(http.StatusOK, invoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) DownloadInvoiceHandler(context echo.Context) error {
|
||||||
|
userId, err := uuid.Parse(context.Get("id").(string))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: errors.New("Session ID not found after authorization."), Messages: []string{"Unexpected error getting User!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := uuid.Parse(context.Param("id"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusBadRequest, Messages: []string{"ID is required to download an Invoice!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var invoice types.Invoice
|
||||||
|
|
||||||
|
if err := api.db.GetInvoiceById(userId, id, &invoice); err != nil {
|
||||||
|
return types.Error{Code: http.StatusNotFound, Messages: []string{"Invoice not found!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
if invoice.PDF != "" {
|
||||||
|
invoiceFileName := fmt.Sprintf("%s.pdf", invoice.ID)
|
||||||
|
return context.Attachment(invoice.PDF, invoiceFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var user types.User
|
||||||
|
|
||||||
|
if err := api.db.GetUserById(userId, &user); err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: errors.New("Unexpected error getting User."), Messages: []string{"Unexpected error getting User!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientId, err := uuid.Parse(invoice.ClientID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: errors.New("Invalid Client ID."), Messages: []string{"Unexpected error getting User!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var client types.Client
|
||||||
|
|
||||||
|
if err := api.db.GetClientById(userId, clientId, &client); err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: errors.New("Unexpected error getting Client."), Messages: []string{"Unexpected error getting User!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := lib.GenerateInvoice(invoice, user, client)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: err, Messages: []string{"Unexpected error generating Invoice!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
invoiceFileName := fmt.Sprintf("%s.pdf", invoice.ID)
|
||||||
|
invoiceId, err := uuid.Parse(invoice.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusBadRequest, Messages: []string{"Could not parse Invoice ID!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = api.db.UpdateInvoicePDF(userId, invoiceId, path, &invoice)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return types.Error{Code: http.StatusInternalServerError, Cause: err, Messages: []string{"Unexpected error generating Invoice!"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.Attachment(path, invoiceFileName)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) UpdateItemHandler(context echo.Context) error {
|
func (api *API) UpdateItemHandler(context echo.Context) error {
|
||||||
userId, err := uuid.Parse(context.Get("id").(string))
|
userId, err := uuid.Parse(context.Get("id").(string))
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ func (api *API) InvoiceRoutes() {
|
|||||||
invoices.POST("/", api.CreateInvoiceHandler)
|
invoices.POST("/", api.CreateInvoiceHandler)
|
||||||
invoices.POST("/:id/items/", api.CreateItemHandler)
|
invoices.POST("/:id/items/", api.CreateItemHandler)
|
||||||
invoices.GET("/:id/", api.GetInvoiceHandler)
|
invoices.GET("/:id/", api.GetInvoiceHandler)
|
||||||
|
invoices.GET("/:id/download/", api.DownloadInvoiceHandler)
|
||||||
invoices.GET("/:id/items/", api.GetAllItemsHandler)
|
invoices.GET("/:id/items/", api.GetAllItemsHandler)
|
||||||
invoices.GET("/items/:id/", api.GetItemHandler)
|
invoices.GET("/items/:id/", api.GetItemHandler)
|
||||||
invoices.PUT("/:id/", api.UpdateInvoiceHandler)
|
invoices.PUT("/:id/", api.UpdateInvoiceHandler)
|
||||||
|
|||||||
+22
-34
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/johnfercher/maroto/v2"
|
"github.com/johnfercher/maroto/v2"
|
||||||
"github.com/johnfercher/maroto/v2/pkg/components/code"
|
|
||||||
"github.com/johnfercher/maroto/v2/pkg/components/col"
|
"github.com/johnfercher/maroto/v2/pkg/components/col"
|
||||||
"github.com/johnfercher/maroto/v2/pkg/components/image"
|
"github.com/johnfercher/maroto/v2/pkg/components/image"
|
||||||
"github.com/johnfercher/maroto/v2/pkg/components/row"
|
"github.com/johnfercher/maroto/v2/pkg/components/row"
|
||||||
@@ -25,7 +24,7 @@ import (
|
|||||||
"github.com/hazemKrimi/crimson-vault/internal/types"
|
"github.com/hazemKrimi/crimson-vault/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetConfigDirectory() (string, error) {
|
func GetConfigDirectoryPath() (string, error) {
|
||||||
home, err := os.UserHomeDir()
|
home, err := os.UserHomeDir()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -172,7 +171,7 @@ func getTransactions(items []types.Item) []core.Row {
|
|||||||
col.New(3),
|
col.New(3),
|
||||||
text.NewCol(4, item.Name, props.Text{Size: 8, Align: align.Center}),
|
text.NewCol(4, item.Name, props.Text{Size: 8, Align: align.Center}),
|
||||||
text.NewCol(2, fmt.Sprintf("%d", item.Quantity), props.Text{Size: 8, Align: align.Center}),
|
text.NewCol(2, fmt.Sprintf("%d", item.Quantity), props.Text{Size: 8, Align: align.Center}),
|
||||||
text.NewCol(3, fmt.Sprintf("%d", item.Price), props.Text{Size: 8, Align: align.Center}),
|
text.NewCol(3, fmt.Sprintf("%.2f", item.Price), props.Text{Size: 8, Align: align.Center}),
|
||||||
)
|
)
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
gray := getGrayColor()
|
gray := getGrayColor()
|
||||||
@@ -203,7 +202,7 @@ func getTransactions(items []types.Item) []core.Row {
|
|||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateInvoice(invoice types.Invoice, user types.User, client types.Client) error {
|
func GenerateInvoice(invoice types.Invoice, user types.User, client types.Client) (string, error) {
|
||||||
cfg := config.NewBuilder().WithPageNumber().WithLeftMargin(10).WithRightMargin(10).WithTopMargin(15).Build()
|
cfg := config.NewBuilder().WithPageNumber().WithLeftMargin(10).WithRightMargin(10).WithTopMargin(15).Build()
|
||||||
darkGray := getDarkGrayColor()
|
darkGray := getDarkGrayColor()
|
||||||
mrt := maroto.New(cfg)
|
mrt := maroto.New(cfg)
|
||||||
@@ -212,23 +211,23 @@ func GenerateInvoice(invoice types.Invoice, user types.User, client types.Client
|
|||||||
err := m.RegisterHeader(getPageHeader(client, user.Logo))
|
err := m.RegisterHeader(getPageHeader(client, user.Logo))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.RegisterFooter(getPageFooter(user))
|
err = m.RegisterFooter(getPageFooter(user))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.AddRows(text.NewRow(10, "Invoice ABC123456789", props.Text{
|
m.AddRows(text.NewRow(10, fmt.Sprintf("Invoice %s", invoice.ID), props.Text{
|
||||||
Top: 3,
|
Top: 3,
|
||||||
Style: fontstyle.Bold,
|
Style: fontstyle.Bold,
|
||||||
Align: align.Center,
|
Align: align.Center,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
m.AddRow(7,
|
m.AddRow(7,
|
||||||
text.NewCol(3, "Transactions", props.Text{
|
text.NewCol(3, "Items", props.Text{
|
||||||
Top: 1.5,
|
Top: 1.5,
|
||||||
Size: 9,
|
Size: 9,
|
||||||
Style: fontstyle.Bold,
|
Style: fontstyle.Bold,
|
||||||
@@ -239,43 +238,32 @@ func GenerateInvoice(invoice types.Invoice, user types.User, client types.Client
|
|||||||
|
|
||||||
m.AddRows(getTransactions(invoice.Items)...)
|
m.AddRows(getTransactions(invoice.Items)...)
|
||||||
|
|
||||||
m.AddRow(15,
|
|
||||||
col.New(6).Add(
|
|
||||||
code.NewBar("5123.151231.512314.1251251.123215", props.Barcode{
|
|
||||||
Percent: 0,
|
|
||||||
Proportion: props.Proportion{
|
|
||||||
Width: 20,
|
|
||||||
Height: 2,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
text.New("5123.151231.512314.1251251.123215", props.Text{
|
|
||||||
Top: 12,
|
|
||||||
Family: "",
|
|
||||||
Style: fontstyle.Bold,
|
|
||||||
Size: 9,
|
|
||||||
Align: align.Center,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
col.New(6),
|
|
||||||
)
|
|
||||||
|
|
||||||
document, err := m.Generate()
|
document, err := m.Generate()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = document.Save(fmt.Sprintf("invoices/%s.pdf", invoice.ID))
|
configDir, err := GetConfigDirectoryPath()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = document.GetReport().Save(fmt.Sprintf("invoices/%s.txt", invoice.ID))
|
invoicesDir := filepath.Join(configDir, user.Username, client.ID)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(invoicesDir, os.ModePerm); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
invoiceFileName := fmt.Sprintf("%s.pdf", invoice.ID)
|
||||||
|
invoicePath := filepath.Join(invoicesDir, invoiceFileName)
|
||||||
|
|
||||||
|
err = document.Save(invoicePath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return invoicePath, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -170,6 +171,24 @@ func (db *DB) UpdateInvoice(userId, id uuid.UUID, body types.UpdateInvoiceReques
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) UpdateInvoicePDF(userId, id uuid.UUID, pdf string, invoice *types.Invoice) error {
|
||||||
|
if pdf == "" {
|
||||||
|
return errors.New("PDF path is empty!")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := db.instance.Where("user_id = ?", userId).Where("id = ?", id).First(invoice)
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
result = db.instance.Model(invoice).Updates(types.Invoice{
|
||||||
|
PDF: pdf,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) DeleteItem(userId, id uuid.UUID) error {
|
func (db *DB) DeleteItem(userId, id uuid.UUID) error {
|
||||||
result := db.instance.Unscoped().Where("user_id = ?", userId).Where("id = ?", id).Delete(&types.Item{})
|
result := db.instance.Unscoped().Where("user_id = ?", userId).Where("id = ?", id).Delete(&types.Item{})
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type Item struct {
|
|||||||
UserID string `json:"userId" gorm:"type:varchar(255)"`
|
UserID string `json:"userId" gorm:"type:varchar(255)"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Price uint32 `json:"price"`
|
Price float32 `json:"price"`
|
||||||
Quantity uint32 `json:"quantity"`
|
Quantity uint32 `json:"quantity"`
|
||||||
Tax uint32 `json:"tax"`
|
Tax uint32 `json:"tax"`
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ type CreateItemRequestBody struct {
|
|||||||
Name string `json:"name" validate:"alpha,required"`
|
Name string `json:"name" validate:"alpha,required"`
|
||||||
Type string `json:"type" validate:"alpha,required"`
|
Type string `json:"type" validate:"alpha,required"`
|
||||||
Quantity uint32 `json:"quantity" validate:"number,required"`
|
Quantity uint32 `json:"quantity" validate:"number,required"`
|
||||||
Price uint32 `json:"price" validate:"number,required"`
|
Price float32 `json:"price" validate:"number,required"`
|
||||||
Tax uint32 `json:"tax" validate:"number,omitempty"`
|
Tax uint32 `json:"tax" validate:"number,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ type UpdateItemRequestBody struct {
|
|||||||
Name string `json:"name" validate:"alpha,omitempty"`
|
Name string `json:"name" validate:"alpha,omitempty"`
|
||||||
Type string `json:"type" validate:"alpha,omitempty"`
|
Type string `json:"type" validate:"alpha,omitempty"`
|
||||||
Quantity uint32 `json:"quantity" validate:"number,omitempty"`
|
Quantity uint32 `json:"quantity" validate:"number,omitempty"`
|
||||||
Price uint32 `json:"price" validate:"number,omitempty"`
|
Price float32 `json:"price" validate:"number,omitempty"`
|
||||||
Tax uint32 `json:"tax" validate:"number,omitempty"`
|
Tax uint32 `json:"tax" validate:"number,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- Disable the enforcement of foreign-keys constraints
|
||||||
|
PRAGMA foreign_keys = off;
|
||||||
|
-- Create "new_items" table
|
||||||
|
CREATE TABLE `new_items` (
|
||||||
|
`id` varchar NULL,
|
||||||
|
`invoice_id` varchar NULL,
|
||||||
|
`user_id` varchar NULL,
|
||||||
|
`name` text NULL,
|
||||||
|
`type` text NULL,
|
||||||
|
`price` real NULL,
|
||||||
|
`quantity` integer NULL,
|
||||||
|
`tax` integer NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
CONSTRAINT `fk_invoices_items` FOREIGN KEY (`invoice_id`) REFERENCES `invoices` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION
|
||||||
|
);
|
||||||
|
-- Copy rows from old table "items" to new temporary table "new_items"
|
||||||
|
INSERT INTO `new_items` (`id`, `invoice_id`, `user_id`, `name`, `type`, `price`, `quantity`, `tax`) SELECT `id`, `invoice_id`, `user_id`, `name`, `type`, `price`, `quantity`, `tax` FROM `items`;
|
||||||
|
-- Drop "items" table after copying rows
|
||||||
|
DROP TABLE `items`;
|
||||||
|
-- Rename temporary table "new_items" to "items"
|
||||||
|
ALTER TABLE `new_items` RENAME TO `items`;
|
||||||
|
-- Enable back the enforcement of foreign-keys constraints
|
||||||
|
PRAGMA foreign_keys = on;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
h1:dDD/MWKqjFIkCAuR4i/9LtH4ibg9wZR03FApuHHYCos=
|
h1:wDAImuaIK9lhzbnQ3++MNijLAxUnoaLdlysqqYjOfoU=
|
||||||
20250611102124.sql h1:HkDgtWXUxfAR9gIObTMzP98pVEIakerASGGSufW695k=
|
20250611102124.sql h1:HkDgtWXUxfAR9gIObTMzP98pVEIakerASGGSufW695k=
|
||||||
20250616102420.sql h1:7C1kEskaDwdHmQOu3t48oZcky0WaNiFeIugJcJFkjKM=
|
20250616102420.sql h1:7C1kEskaDwdHmQOu3t48oZcky0WaNiFeIugJcJFkjKM=
|
||||||
20250616150439.sql h1:nx8CvH5om7lbeEezo7roNcTV+f0agch0gBjYxKtTDn8=
|
20250616150439.sql h1:nx8CvH5om7lbeEezo7roNcTV+f0agch0gBjYxKtTDn8=
|
||||||
20250616151658.sql h1:O4hbYFj4bwMDML0mR9PtSn7GUbXQVCbh+lGkwTNBwnU=
|
20250616151658.sql h1:O4hbYFj4bwMDML0mR9PtSn7GUbXQVCbh+lGkwTNBwnU=
|
||||||
|
20250814173803.sql h1:xRBhN15wPcJJOfzzPuMoUJsUqEYNCdLmEBiYUJBF1FA=
|
||||||
|
|||||||
Reference in New Issue
Block a user