Skip to content

Commit

Permalink
Work on discount and tax
Browse files Browse the repository at this point in the history
  • Loading branch information
angelodlfrtr committed Mar 1, 2021
1 parent 3f4417b commit 1a53d4f
Show file tree
Hide file tree
Showing 14 changed files with 552 additions and 464 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2018 angelodlfrtr
Copyright 2018 The contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
lint:
misspell ./...
golint ./...
ineffassign ./*
find . -name "*.go" | xargs misspell -error
golint -set_exit_status ./...
go vet ./...
staticcheck ./...

Expand Down
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
go-invoice-generator
Copyright 2018 angelodlfrtr
Copyright 2018 The contributors
10 changes: 5 additions & 5 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package generator

// Address represent an address
type Address struct {
Address string `validate:"required" json:"address"`
Address2 string `json:"address2"`
PostalCode string `json:"postalCode"`
City string `json:"city"`
Country string `json:"country"`
Address string `json:"address,omitempty" validate:"required"`
Address2 string `json:"address_2,omitempty"`
PostalCode string `json:"postal_code,omitempty"`
City string `json:"city,omitempty"`
Country string `json:"country,omitempty"`
}

// ToString output address as string
Expand Down
312 changes: 312 additions & 0 deletions build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
package generator

import (
"fmt"
"time"

"github.com/jung-kurt/gofpdf"
"github.com/leekchan/accounting"
"github.com/shopspring/decimal"
)

// Build pdf document from data provided
func (d *Document) Build() (*gofpdf.Fpdf, error) {
// Validate document data
err := d.Validate()
if err != nil {
return nil, err
}

// Build base doc
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.SetMargins(BaseMargin, BaseMarginTop, BaseMargin)
pdf.SetXY(10, 10)
pdf.SetTextColor(BaseTextColor[0], BaseTextColor[1], BaseTextColor[2])

// Set header
if d.Header != nil {
err = d.Header.applyHeader(d, pdf)

if err != nil {
return nil, err
}
}

// Set footer
if d.Footer != nil {
err = d.Footer.applyFooter(d, pdf)

if err != nil {
return nil, err
}
}

// Add first page
pdf.AddPage()

// Load font
pdf.SetFont("Helvetica", "", 12)

// Appenf document title
d.appendTitle(pdf)

// Appenf document metas (ref & version)
d.appendMetas(pdf)

// Append company contact to doc
companyBottom := d.Company.appendCompanyContactToDoc(pdf)

// Append customer contact to doc
customerBottom := d.Customer.appendCustomerContactToDoc(pdf)

if customerBottom > companyBottom {
pdf.SetXY(10, customerBottom)
} else {
pdf.SetXY(10, companyBottom)
}

// Append description
d.appendDescription(pdf)

// Append items
d.appendItems(pdf)

// Append notes
d.appendNotes(pdf)

// Append total
d.appendTotal(pdf)

// Append payment term
d.appendPaymentTerm(pdf)

// Append js to autoprint if AutoPrint == true
if d.Options.AutoPrint {
pdf.SetJavascript("print(true);")
}

return pdf, nil
}

func (d *Document) appendTitle(pdf *gofpdf.Fpdf) {
title := d.typeAsString()

// Set x y
pdf.SetXY(120, BaseMarginTop)

// Draw rect
pdf.SetFillColor(DarkBgColor[0], DarkBgColor[1], DarkBgColor[2])
pdf.Rect(120, BaseMarginTop, 80, 10, "F")

// Draw text
pdf.SetFont("Helvetica", "", 14)
pdf.CellFormat(80, 10, encodeString(title), "0", 0, "C", false, 0, "")
}

func (d *Document) appendMetas(pdf *gofpdf.Fpdf) {
// Append ref
refString := fmt.Sprintf("%s: %s", encodeString(d.Options.TextRefTitle), d.Ref)

pdf.SetXY(120, BaseMarginTop+11)
pdf.SetFont("Helvetica", "", 8)
pdf.CellFormat(80, 4, encodeString(refString), "0", 0, "R", false, 0, "")

// Append version
if len(d.Version) > 0 {
versionString := fmt.Sprintf("%s: %s", encodeString(d.Options.TextVersionTitle), d.Version)
pdf.SetXY(120, BaseMarginTop+15)
pdf.SetFont("Helvetica", "", 8)
pdf.CellFormat(80, 4, encodeString(versionString), "0", 0, "R", false, 0, "")
}

// Append date
date := time.Now().Format("02/01/2006")
if len(d.Date) > 0 {
date = d.Date
}
dateString := fmt.Sprintf("%s: %s", encodeString(d.Options.TextDateTitle), date)
pdf.SetXY(120, BaseMarginTop+19)
pdf.SetFont("Helvetica", "", 8)
pdf.CellFormat(80, 4, encodeString(dateString), "0", 0, "R", false, 0, "")
}

func (d *Document) appendDescription(pdf *gofpdf.Fpdf) {
if len(d.Description) > 0 {
pdf.SetY(pdf.GetY() + 10)
pdf.SetFont("Helvetica", "", 10)
pdf.MultiCell(190, 5, encodeString(d.Description), "B", "L", false)
}
}

func (d *Document) drawsTableTitles(pdf *gofpdf.Fpdf) {
// Draw table titles
pdf.SetX(10)
pdf.SetY(pdf.GetY() + 5)
pdf.SetFont("Helvetica", "B", 8)

// Draw rec
pdf.SetFillColor(GreyBgColor[0], GreyBgColor[1], GreyBgColor[2])
pdf.Rect(10, pdf.GetY(), 190, 6, "F")

// Description
pdf.CellFormat(80, 6, encodeString(d.Options.TextItemsDescriptionTitle), "0", 0, "", false, 0, "")

// Unit price
pdf.SetX(90)
pdf.CellFormat(30, 6, encodeString(d.Options.TextItemsUnitCostTitle), "0", 0, "", false, 0, "")

// Quantity
pdf.SetX(120)
pdf.CellFormat(15, 6, encodeString(d.Options.TextItemsQuantityTitle), "0", 0, "", false, 0, "")

// Total HT
pdf.SetX(135)
pdf.CellFormat(20, 6, encodeString(d.Options.TextItemsTotalHTTitle), "0", 0, "", false, 0, "")

// Tax
pdf.SetX(155)
pdf.CellFormat(20, 6, encodeString(d.Options.TextItemsTaxTitle), "0", 0, "", false, 0, "")

// TOTAL TTC
pdf.SetX(175)
pdf.CellFormat(25, 6, encodeString(d.Options.TextItemsTotalTTCTitle), "0", 0, "", false, 0, "")
}

func (d *Document) appendItems(pdf *gofpdf.Fpdf) {
d.drawsTableTitles(pdf)

pdf.SetX(10)
pdf.SetY(pdf.GetY() + 6)
pdf.SetFont("Helvetica", "", 8)

for i := 0; i < len(d.Items); i++ {
item := d.Items[i]
item.appendColTo(d.Options, pdf)

if pdf.GetY() > MaxPageHeight {
// Add page
pdf.AddPage()
d.drawsTableTitles(pdf)
pdf.SetFont("Helvetica", "", 8)
}

pdf.SetX(10)
pdf.SetY(pdf.GetY() + 6)
}
}

func (d *Document) appendNotes(pdf *gofpdf.Fpdf) {
if len(d.Notes) == 0 {
return
}

currentY := pdf.GetY()

if currentY+30 > MaxPageHeight {
pdf.AddPage()
currentY = pdf.GetY()
}

pdf.SetFont("Helvetica", "", 9)
pdf.SetX(BaseMargin)
pdf.SetRightMargin(100)
pdf.SetY(currentY + 10)

_, lineHt := pdf.GetFontSize()
html := pdf.HTMLBasicNew()
html.Write(lineHt, d.Notes)

pdf.SetRightMargin(BaseMargin)
pdf.SetY(currentY)
}

func (d *Document) appendTotal(pdf *gofpdf.Fpdf) {
ac := accounting.Accounting{
Symbol: encodeString(d.Options.CurrencySymbol),
Precision: d.Options.CurrencyPrecision,
Thousand: d.Options.CurrencyThousand,
Decimal: d.Options.CurrencyDecimal,
}

// Get total HT & totalTTC
totalHT, _ := decimal.NewFromString("0")
totalTTC, _ := decimal.NewFromString("0")

for i := 0; i < len(d.Items); i++ {
item := d.Items[i]
totalHT = totalHT.Add(item.totalHT())
totalTTC = totalTTC.Add(item.totalTTC(d.Tax))
}

totalTax := totalTTC.Sub(totalHT)

// Check page height (total bloc height = 30) // @TOOD: include discount
if pdf.GetY()+30 > MaxPageHeight {
pdf.AddPage()
}

pdf.SetY(pdf.GetY() + 10)
pdf.SetFont("Helvetica", "", 10)

// Draw TOTAL HT title
pdf.SetX(120)
pdf.SetFillColor(DarkBgColor[0], DarkBgColor[1], DarkBgColor[2])
pdf.Rect(120, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(38, 10, encodeString(d.Options.TextTotalTotal), "0", 0, "R", false, 0, "")

// Draw TOTAL HT amount
pdf.SetX(162)
pdf.SetFillColor(GreyBgColor[0], GreyBgColor[1], GreyBgColor[2])
pdf.Rect(160, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(40, 10, ac.FormatMoneyDecimal(totalHT), "0", 0, "L", false, 0, "")

// Draw DISCOUNT title
pdf.SetY(pdf.GetY() + 10)
pdf.SetX(120)
pdf.SetFillColor(DarkBgColor[0], DarkBgColor[1], DarkBgColor[2])
pdf.Rect(120, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(38, 10, encodeString(d.Options.TextTotalDiscount), "0", 0, "R", false, 0, "")

// Draw DISCOUNT amount
pdf.SetX(162)
pdf.SetFillColor(GreyBgColor[0], GreyBgColor[1], GreyBgColor[2])
pdf.Rect(160, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(40, 10, ac.FormatMoneyDecimal(totalHT), "0", 0, "L", false, 0, "")

// Draw TAX title
pdf.SetY(pdf.GetY() + 10)
pdf.SetX(120)
pdf.SetFillColor(DarkBgColor[0], DarkBgColor[1], DarkBgColor[2])
pdf.Rect(120, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(38, 10, encodeString(d.Options.TextTotalTax), "0", 0, "R", false, 0, "")

// Draw TAX amount
pdf.SetX(162)
pdf.SetFillColor(GreyBgColor[0], GreyBgColor[1], GreyBgColor[2])
pdf.Rect(160, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(40, 10, ac.FormatMoneyDecimal(totalTax), "0", 0, "L", false, 0, "")

// Draw TOTAL TTC title
pdf.SetY(pdf.GetY() + 10)
pdf.SetX(120)
pdf.SetFillColor(DarkBgColor[0], DarkBgColor[1], DarkBgColor[2])
pdf.Rect(120, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(38, 10, encodeString(d.Options.TextTotalWithTax), "0", 0, "R", false, 0, "")

// Draw TOTAL TTC amount
pdf.SetX(162)
pdf.SetFillColor(GreyBgColor[0], GreyBgColor[1], GreyBgColor[2])
pdf.Rect(160, pdf.GetY(), 40, 10, "F")
pdf.CellFormat(40, 10, ac.FormatMoneyDecimal(totalTTC), "0", 0, "L", false, 0, "")
}

func (d *Document) appendPaymentTerm(pdf *gofpdf.Fpdf) {
if len(d.PaymentTerm) > 0 {
paymentTermString := fmt.Sprintf("%s: %s", encodeString(d.Options.TextPaymentTermTitle), encodeString(d.PaymentTerm))
pdf.SetY(pdf.GetY() + 15)

pdf.SetX(120)
pdf.SetFont("Helvetica", "B", 10)
pdf.CellFormat(80, 4, paymentTermString, "0", 0, "R", false, 0, "")
}
}
6 changes: 3 additions & 3 deletions contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (

// Contact contact a company informations
type Contact struct {
Name string `validate:"required,min=1,max=256"`
Logo *[]byte // Logo byte array
Address *Address
Name string `json:"name,omitempty" validate:"required,min=1,max=256"`
Logo *[]byte `json:"logo,omitempty"` // Logo byte array
Address *Address `json:"address,omitempty"`
}

func (c *Contact) appendContactTODoc(x float64, y float64, fill bool, logoAlign string, pdf *gofpdf.Fpdf) float64 {
Expand Down
10 changes: 5 additions & 5 deletions discount.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (

// Discount define discount as percent or fixed amount
type Discount struct {
Percent string // Discount in percent ex 17
Amount string // Discount in amount ex 123.40
Percent string `json:"percent,omitempty"` // Discount in percent ex 17
Amount string `json:"amount,omitempty"` // Discount in amount ex 123.40
}

func (t *Tax) getDiscount() (string, decimal.Decimal) {
var tax string
var taxType string = "percent"
func (t *Discount) getDiscount() (string, decimal.Decimal) {
tax := "0"
taxType := "percent"

if len(t.Percent) > 0 {
tax = t.Percent
Expand Down
Loading

0 comments on commit 1a53d4f

Please sign in to comment.