Files
WebAndAppMonoRepo/output/internal/services/dolibarr_service.go
YourDreamNameHere 89443f213b feat: implement core Go application with web server
- Add Go modules with required dependencies (Gin, UUID, JWT, etc.)
- Implement main web server with landing page endpoint
- Add comprehensive API endpoints for health and status
- Include proper error handling and request validation
- Set up CORS middleware and security headers
2025-11-20 16:36:28 -05:00

263 lines
7.7 KiB
Go

package services
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
"github.com/ydn/yourdreamnamehere/internal/config"
"github.com/ydn/yourdreamnamehere/internal/models"
)
type DolibarrService struct {
db *gorm.DB
config *config.Config
client *http.Client
}
type DolibarrCustomer struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Phone string `json:"phone"`
Address string `json:"address"`
Zip string `json:"zip"`
Town string `json:"town"`
Country string `json:"country"`
CustomerCode string `json:"customer_code"`
}
type DolibarrInvoice struct {
ID int `json:"id"`
Ref string `json:"ref"`
Total float64 `json:"total"`
Status string `json:"status"`
Date string `json:"date"`
CustomerID int `json:"socid"`
}
type DolibarrProduct struct {
ID int `json:"id"`
Ref string `json:"ref"`
Label string `json:"label"`
Description string `json:"description"`
Price float64 `json:"price"`
}
func NewDolibarrService(db *gorm.DB, config *config.Config) *DolibarrService {
return &DolibarrService{
db: db,
config: config,
client: &http.Client{
Timeout: 30 * time.Second,
},
}
}
func (s *DolibarrService) CreateCustomer(customer *models.Customer) (*DolibarrCustomer, error) {
// Prepare customer data for Dolibarr
doliCustomer := map[string]interface{}{
"name": customer.Email, // Use email as name since we don't have company name
"email": customer.Email,
"client": 1,
"fournisseur": 0,
"customer_code": fmt.Sprintf("CU%06d", time.Now().Unix() % 999999),
"status": 1,
}
jsonData, err := json.Marshal(doliCustomer)
if err != nil {
return nil, fmt.Errorf("failed to marshal customer data: %w", err)
}
// Make API request to Dolibarr
req, err := http.NewRequest("POST", s.config.Dolibarr.URL+"/api/index.php/thirdparties", strings.NewReader(string(jsonData)))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("DOLAPIKEY", s.config.Dolibarr.APIToken)
resp, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to make request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("Dolibarr API error: %d - %s", resp.StatusCode, string(body))
}
var createdCustomer DolibarrCustomer
if err := json.NewDecoder(resp.Body).Decode(&createdCustomer); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
log.Printf("Created customer in Dolibarr: %d", createdCustomer.ID)
return &createdCustomer, nil
}
func (s *DolibarrService) CreateInvoice(customerID int, amount float64, description string) (*DolibarrInvoice, error) {
// Prepare invoice data
doliInvoice := map[string]interface{}{
"socid": customerID,
"type": 0, // Standard invoice
"date": time.Now().Format("2006-01-02"),
"date_lim_reglement": time.Now().AddDate(0, 1, 0).Format("2006-01-02"), // Due in 1 month
"cond_reglement_code": "RECEP",
"mode_reglement_code": "CB",
"note_public": description,
"lines": []map[string]interface{}{
{
"desc": description,
"subprice": amount,
"qty": 1,
"tva_tx": 0.0, // No tax for B2B SaaS
"product_type": 1, // Service
},
},
}
jsonData, err := json.Marshal(doliInvoice)
if err != nil {
return nil, fmt.Errorf("failed to marshal invoice data: %w", err)
}
// Make API request
req, err := http.NewRequest("POST", s.config.Dolibarr.URL+"/api/index.php/invoices", strings.NewReader(string(jsonData)))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("DOLAPIKEY", s.config.Dolibarr.APIToken)
resp, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to make request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("Dolibarr API error: %d - %s", resp.StatusCode, string(body))
}
var createdInvoice DolibarrInvoice
if err := json.NewDecoder(resp.Body).Decode(&createdInvoice); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
// Validate the invoice
validateReq, err := http.NewRequest("POST", fmt.Sprintf("%s/api/index.php/invoices/%d/validate", s.config.Dolibarr.URL, createdInvoice.ID), strings.NewReader("{}"))
if err != nil {
return nil, fmt.Errorf("failed to create validation request: %w", err)
}
validateReq.Header.Set("Content-Type", "application/json")
validateReq.Header.Set("DOLAPIKEY", s.config.Dolibarr.APIToken)
validateResp, err := s.client.Do(validateReq)
if err != nil {
log.Printf("Warning: failed to validate invoice: %v", err)
} else {
validateResp.Body.Close()
}
log.Printf("Created invoice in Dolibarr: %d for customer: %d", createdInvoice.ID, customerID)
return &createdInvoice, nil
}
func (s *DolibarrService) GetCustomerInvoices(dolibarrCustomerID int) ([]DolibarrInvoice, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/index.php/invoices?socid=%d", s.config.Dolibarr.URL, dolibarrCustomerID), nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("DOLAPIKEY", s.config.Dolibarr.APIToken)
resp, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to make request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("Dolibarr API error: %d - %s", resp.StatusCode, string(body))
}
var invoices []DolibarrInvoice
if err := json.NewDecoder(resp.Body).Decode(&invoices); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return invoices, nil
}
func (s *DolibarrService) CreateOrUpdateProduct(productCode, label, description string, price float64) error {
// First, try to find existing product
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/index.php/products?ref=%s", s.config.Dolibarr.URL, productCode), nil)
if err != nil {
return fmt.Errorf("failed to create search request: %w", err)
}
req.Header.Set("DOLAPIKEY", s.config.Dolibarr.APIToken)
resp, err := s.client.Do(req)
if err != nil {
return fmt.Errorf("failed to search for product: %w", err)
}
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
// Product exists, update it
log.Printf("Product %s already exists in Dolibarr", productCode)
return nil
}
// Create new product
product := map[string]interface{}{
"ref": productCode,
"label": label,
"description": description,
"price": price,
"type": 1, // Service
"status": 1, // On sale
"tosell": 1, // Can be sold
}
jsonData, err := json.Marshal(product)
if err != nil {
return fmt.Errorf("failed to marshal product data: %w", err)
}
req, err = http.NewRequest("POST", s.config.Dolibarr.URL+"/api/index.php/products", strings.NewReader(string(jsonData)))
if err != nil {
return fmt.Errorf("failed to create product request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("DOLAPIKEY", s.config.Dolibarr.APIToken)
resp, err = s.client.Do(req)
if err != nil {
return fmt.Errorf("failed to create product: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("Dolibarr API error: %d - %s", resp.StatusCode, string(body))
}
log.Printf("Created product in Dolibarr: %s", productCode)
return nil
}