mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-12 13:46:51 +00:00
create pdf driver
This commit is contained in:
parent
85c65f2201
commit
c712b487ce
8 changed files with 46 additions and 300 deletions
|
|
@ -7,7 +7,7 @@ tmp_dir = "tmp"
|
||||||
delay = 1000
|
delay = 1000
|
||||||
exclude_dir = ["assets", "tmp", "vendor"]
|
exclude_dir = ["assets", "tmp", "vendor"]
|
||||||
exclude_file = []
|
exclude_file = []
|
||||||
exclude_regex = [".*_templ.go"]
|
exclude_regex = [".*_templ.go", "VFNI*html"]
|
||||||
exclude_unchanged = false
|
exclude_unchanged = false
|
||||||
follow_symlink = false
|
follow_symlink = false
|
||||||
full_bin = ""
|
full_bin = ""
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -29,4 +29,4 @@ tmp.pdf
|
||||||
*templ.txt
|
*templ.txt
|
||||||
token.json
|
token.json
|
||||||
.aider*
|
.aider*
|
||||||
output.html
|
VFNI*.html
|
||||||
40
internal/driver/pdf/html.go
Normal file
40
internal/driver/pdf/html.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package pdf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/rjNemo/rentease/internal/service/booking"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HtmlPdfClient struct{}
|
||||||
|
|
||||||
|
func NewPdfClient() (*HtmlPdfClient, error) {
|
||||||
|
return &HtmlPdfClient{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *HtmlPdfClient) BuildInvoice(data booking.Invoice) (string, error) {
|
||||||
|
tmpl, err := template.ParseFiles("assets/html/invoice.html")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error parsing template: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a buffer to hold the rendered HTML.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&buf, data); err != nil {
|
||||||
|
return "", fmt.Errorf("error executing template: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPath := fmt.Sprintf("%s.html", data.ID)
|
||||||
|
if err := os.WriteFile(outputPath, buf.Bytes(), 0644); err != nil {
|
||||||
|
return "", fmt.Errorf("error writing HTML file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *HtmlPdfClient) BuildReport(context map[string]any, period string, month int, year int) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ type PdfClient struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPdfClient(pid, rid, url, key string) (*PdfClient, error) {
|
func NewApiPdfClient(pid, rid, url, key string) (*PdfClient, error) {
|
||||||
if pid == "" || rid == "" || url == "" || key == "" {
|
if pid == "" || rid == "" || url == "" || key == "" {
|
||||||
return nil, errors.New("error building Pdf service. Verify your env variables")
|
return nil, errors.New("error building Pdf service. Verify your env variables")
|
||||||
}
|
}
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
package booking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockStore is a mock implementation of the Store interface
|
|
||||||
type MockStore struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) All() []*Line {
|
|
||||||
args := m.Called()
|
|
||||||
return args.Get(0).([]*Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) Search(value string) []*Line {
|
|
||||||
args := m.Called(value)
|
|
||||||
return args.Get(0).([]*Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) List(from, to time.Time) ([]*Line, error) {
|
|
||||||
args := m.Called(from, to)
|
|
||||||
return args.Get(0).([]*Line), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) CardTotal(from, to time.Time) (float64, error) {
|
|
||||||
args := m.Called(from, to)
|
|
||||||
return args.Get(0).(float64), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) Get(id int) *Booking {
|
|
||||||
args := m.Called(id)
|
|
||||||
return args.Get(0).(*Booking)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) Create(b *Booking) error {
|
|
||||||
args := m.Called(b)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) Update(b *Booking) error {
|
|
||||||
args := m.Called(b)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) Cancel(id int) error {
|
|
||||||
args := m.Called(id)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) CreateItem(i *Item) error {
|
|
||||||
args := m.Called(i)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) PayItem(id int) (*Item, error) {
|
|
||||||
args := m.Called(id)
|
|
||||||
return args.Get(0).(*Item), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) GetItem(id int) (*Item, error) {
|
|
||||||
args := m.Called(id)
|
|
||||||
return args.Get(0).(*Item), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) UpdateItem(id int, item string, paymentMethod string, paymentStatus string, qty int, price float64) (*Item, error) {
|
|
||||||
args := m.Called(id, item, paymentMethod, paymentStatus, qty, price)
|
|
||||||
return args.Get(0).(*Item), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) CreatePayment(p *Payment) (*Payment, error) {
|
|
||||||
args := m.Called(p)
|
|
||||||
return args.Get(0).(*Payment), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) GetPayment(id int) (*Payment, error) {
|
|
||||||
args := m.Called(id)
|
|
||||||
return args.Get(0).(*Payment), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockStore) UpdatePayment(id int, amount float64, paymentMethod string) (*Payment, error) {
|
|
||||||
args := m.Called(id, amount, paymentMethod)
|
|
||||||
return args.Get(0).(*Payment), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockParserClient is a mock implementation of the parser.Client interface
|
|
||||||
type MockParserClient struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockCalendarClient is a mock implementation of the calendar.Client interface
|
|
||||||
type MockCalendarClient struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockCalendarClient) Create(calendarId, name, description string, from, to time.Time) error {
|
|
||||||
args := m.Called(calendarId, name, description, from, to)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPDFClient is a mock implementation of the pdf.Client interface
|
|
||||||
type MockPDFClient struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockPDFClient) BuildInvoice(context map[string]any) error {
|
|
||||||
args := m.Called(context)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockPDFClient) BuildReport(context map[string]any, period string, month, year int) error {
|
|
||||||
args := m.Called(context, period, month, year)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
package booking
|
package booking
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rjNemo/rentease/internal/config"
|
"github.com/rjNemo/rentease/internal/config"
|
||||||
|
|
@ -34,7 +30,7 @@ type Store interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PdfClient interface {
|
type PdfClient interface {
|
||||||
BuildInvoice(context map[string]any) error
|
BuildInvoice(invoice Invoice) (string, error)
|
||||||
BuildReport(context map[string]any, period string, month, year int) error
|
BuildReport(context map[string]any, period string, month, year int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,25 +101,5 @@ func (bs Service) Cancel(id int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs Service) BuildInvoice(b *Booking, hc *config.Host) (string, error) {
|
func (bs Service) BuildInvoice(b *Booking, hc *config.Host) (string, error) {
|
||||||
invoiceData := b.ToInvoice(hc)
|
return bs.pdf.BuildInvoice(b.ToInvoice(hc))
|
||||||
|
|
||||||
tmpl, err := template.ParseFiles("assets/html/invoice.html")
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Error parsing template: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a buffer to hold the rendered HTML.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := tmpl.Execute(&buf, invoiceData); err != nil {
|
|
||||||
return "", fmt.Errorf("error executing template: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
outputPath := fmt.Sprintf("%s.html", b.InvoiceNumber(hc))
|
|
||||||
|
|
||||||
if err := os.WriteFile(outputPath, buf.Bytes(), 0644); err != nil {
|
|
||||||
return "", fmt.Errorf("error writing HTML file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("HTML file created successfully: %s", outputPath)
|
|
||||||
return outputPath, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
package booking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestService_All(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
mockData []*Line
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "returns empty list when no bookings",
|
|
||||||
mockData: []*Line{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "returns list of bookings",
|
|
||||||
mockData: []*Line{
|
|
||||||
{
|
|
||||||
Id: 1,
|
|
||||||
CustomerName: "John Doe",
|
|
||||||
From: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
||||||
To: time.Date(2024, 1, 5, 0, 0, 0, 0, time.UTC),
|
|
||||||
Platform: "Airbnb",
|
|
||||||
Total: 500.0,
|
|
||||||
Canceled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: 2,
|
|
||||||
CustomerName: "Jane Smith",
|
|
||||||
From: time.Date(2024, 2, 1, 0, 0, 0, 0, time.UTC),
|
|
||||||
To: time.Date(2024, 2, 3, 0, 0, 0, 0, time.UTC),
|
|
||||||
Platform: "Booking.com",
|
|
||||||
Total: 300.0,
|
|
||||||
Canceled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// Create mocks
|
|
||||||
mockStore := new(MockStore)
|
|
||||||
mockCalendar := new(MockCalendarClient)
|
|
||||||
mockPDF := new(MockPDFClient)
|
|
||||||
|
|
||||||
// Set up expectations
|
|
||||||
mockStore.On("All").Return(tt.mockData)
|
|
||||||
|
|
||||||
// Create service with mocks
|
|
||||||
service, err := NewService(mockStore, nil, mockCalendar, mockPDF)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Call the method
|
|
||||||
result := service.All()
|
|
||||||
|
|
||||||
// Assert expectations
|
|
||||||
assert.Equal(t, tt.mockData, result)
|
|
||||||
mockStore.AssertExpectations(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestService_Search(t *testing.T) {
|
|
||||||
sampleBookings := []*Line{
|
|
||||||
{
|
|
||||||
Id: 1,
|
|
||||||
CustomerName: "John Doe",
|
|
||||||
From: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
||||||
To: time.Date(2024, 1, 5, 0, 0, 0, 0, time.UTC),
|
|
||||||
Platform: "Airbnb",
|
|
||||||
Total: 500.0,
|
|
||||||
Canceled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: 2,
|
|
||||||
CustomerName: "Jane Smith",
|
|
||||||
From: time.Date(2024, 2, 1, 0, 0, 0, 0, time.UTC),
|
|
||||||
To: time.Date(2024, 2, 3, 0, 0, 0, 0, time.UTC),
|
|
||||||
Platform: "Booking.com",
|
|
||||||
Total: 300.0,
|
|
||||||
Canceled: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
searchQuery string
|
|
||||||
expectedLines []*Line
|
|
||||||
expectedCalled bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty search query returns no results",
|
|
||||||
searchQuery: "",
|
|
||||||
expectedLines: []*Line{},
|
|
||||||
expectedCalled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "search for 'John' returns matching booking",
|
|
||||||
searchQuery: "John",
|
|
||||||
expectedLines: []*Line{
|
|
||||||
sampleBookings[0],
|
|
||||||
},
|
|
||||||
expectedCalled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "search for 'Smith' returns matching booking",
|
|
||||||
searchQuery: "Smith",
|
|
||||||
expectedLines: []*Line{
|
|
||||||
sampleBookings[1],
|
|
||||||
},
|
|
||||||
expectedCalled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "search for non-existent name returns empty list",
|
|
||||||
searchQuery: "NonExistent",
|
|
||||||
expectedLines: []*Line{},
|
|
||||||
expectedCalled: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// Create mocks
|
|
||||||
mockStore := new(MockStore)
|
|
||||||
mockCalendar := new(MockCalendarClient)
|
|
||||||
mockPDF := new(MockPDFClient)
|
|
||||||
|
|
||||||
// Set up expectations
|
|
||||||
mockStore.On("Search", tt.searchQuery).Return(tt.expectedLines)
|
|
||||||
|
|
||||||
// Create service with mocks
|
|
||||||
service, err := NewService(mockStore, nil, mockCalendar, mockPDF)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Call the method
|
|
||||||
result := service.Search(tt.searchQuery)
|
|
||||||
|
|
||||||
// Assert expectations
|
|
||||||
assert.Equal(t, tt.expectedLines, result)
|
|
||||||
mockStore.AssertExpectations(t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
main.go
7
main.go
|
|
@ -65,12 +65,7 @@ func run(c context.Context, getEnv func(string) string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// build pdf client
|
// build pdf client
|
||||||
pc, err := pdf.NewPdfClient(
|
pc, err := pdf.NewPdfClient()
|
||||||
getEnv("HTMLDOCS_PROJECT_ID"),
|
|
||||||
getEnv("HTMLDOCS_REPORT_PROJECT_ID"),
|
|
||||||
getEnv("HTMLDOCS_URL"),
|
|
||||||
getEnv("HTMLDOCS_KEY"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting pdf client %w", err)
|
return fmt.Errorf("error starting pdf client %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue