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:
+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!"}}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -172,6 +189,70 @@ func (api *API) GetInvoiceHandler(context echo.Context) error {
|
||||
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 {
|
||||
userId, err := uuid.Parse(context.Get("id").(string))
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ func (api *API) InvoiceRoutes() {
|
||||
invoices.POST("/", api.CreateInvoiceHandler)
|
||||
invoices.POST("/:id/items/", api.CreateItemHandler)
|
||||
invoices.GET("/:id/", api.GetInvoiceHandler)
|
||||
invoices.GET("/:id/download/", api.DownloadInvoiceHandler)
|
||||
invoices.GET("/:id/items/", api.GetAllItemsHandler)
|
||||
invoices.GET("/items/:id/", api.GetItemHandler)
|
||||
invoices.PUT("/:id/", api.UpdateInvoiceHandler)
|
||||
|
||||
+22
-34
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/sessions"
|
||||
"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/image"
|
||||
"github.com/johnfercher/maroto/v2/pkg/components/row"
|
||||
@@ -25,7 +24,7 @@ import (
|
||||
"github.com/hazemKrimi/crimson-vault/internal/types"
|
||||
)
|
||||
|
||||
func GetConfigDirectory() (string, error) {
|
||||
func GetConfigDirectoryPath() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
|
||||
if err != nil {
|
||||
@@ -172,7 +171,7 @@ func getTransactions(items []types.Item) []core.Row {
|
||||
col.New(3),
|
||||
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(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 {
|
||||
gray := getGrayColor()
|
||||
@@ -203,7 +202,7 @@ func getTransactions(items []types.Item) []core.Row {
|
||||
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()
|
||||
darkGray := getDarkGrayColor()
|
||||
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))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = m.RegisterFooter(getPageFooter(user))
|
||||
|
||||
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,
|
||||
Style: fontstyle.Bold,
|
||||
Align: align.Center,
|
||||
}))
|
||||
|
||||
m.AddRow(7,
|
||||
text.NewCol(3, "Transactions", props.Text{
|
||||
text.NewCol(3, "Items", props.Text{
|
||||
Top: 1.5,
|
||||
Size: 9,
|
||||
Style: fontstyle.Bold,
|
||||
@@ -239,43 +238,32 @@ func GenerateInvoice(invoice types.Invoice, user types.User, client types.Client
|
||||
|
||||
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()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = document.Save(fmt.Sprintf("invoices/%s.pdf", invoice.ID))
|
||||
configDir, err := GetConfigDirectoryPath()
|
||||
|
||||
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 {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nil
|
||||
return invoicePath, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -170,6 +171,24 @@ func (db *DB) UpdateInvoice(userId, id uuid.UUID, body types.UpdateInvoiceReques
|
||||
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 {
|
||||
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)"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Price uint32 `json:"price"`
|
||||
Price float32 `json:"price"`
|
||||
Quantity uint32 `json:"quantity"`
|
||||
Tax uint32 `json:"tax"`
|
||||
}
|
||||
@@ -79,7 +79,7 @@ type CreateItemRequestBody struct {
|
||||
Name string `json:"name" validate:"alpha,required"`
|
||||
Type string `json:"type" validate:"alpha,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"`
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ type UpdateItemRequestBody struct {
|
||||
Name string `json:"name" validate:"alpha,omitempty"`
|
||||
Type string `json:"type" validate:"alpha,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"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user