pdf building script

This commit is contained in:
Ruidy 2025-01-20 17:26:19 +01:00
parent 7c3208c3b5
commit b8d5907f36
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
4 changed files with 213 additions and 503 deletions

3
.gitignore vendored
View file

@ -28,4 +28,5 @@ tmp.pdf
*.env *.env
*templ.txt *templ.txt
token.json token.json
.aider* .aider*
output.html

View file

@ -1,288 +1,288 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="fr">
<head> <head>
<style> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #f9f9f9; background-color: #f9f9f9;
color: #333; color: #333;
} }
.header { .header {
background-color: #007b8f; background-color: #007b8f;
color: #fff; color: #fff;
padding: 20px; padding: 20px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.logo { .logo {
height: 60px; height: 60px;
} }
.payee { .payee {
text-align: right; text-align: right;
} }
hr { hr {
border: none; border: none;
border-top: 2px solid #ddd; border-top: 2px solid #ddd;
margin: 20px 0; margin: 20px 0;
} }
.info-table { .info-table {
width: 48%; width: 48%;
margin-bottom: 20px; margin-bottom: 20px;
} }
.info-table td { .info-table td {
padding: 5px 10px; padding: 5px 10px;
} }
.billing-details { .billing-details {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.items-table { .items-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin-bottom: 20px; margin-bottom: 20px;
} }
.items-table th, .items-table th,
.items-table td { .items-table td {
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 10px; padding: 10px;
text-align: left; text-align: left;
} }
.items-table th { .items-table th {
background-color: #007b8f; background-color: #007b8f;
color: #fff; color: #fff;
} }
.items-table .align-right { .items-table .align-right {
text-align: right; text-align: right;
} }
.bg-gray { .bg-gray {
background-color: #f4f4f4; background-color: #f4f4f4;
} }
.payment-summary { .payment-summary {
margin-top: 20px; margin-top: 20px;
} }
.summary-table { .summary-table {
width: 100%; width: 100%;
} }
.summary-table td { .summary-table td {
padding: 10px; padding: 10px;
} }
.payment-history h3 { .payment-history h3 {
color: #007b8f; color: #007b8f;
} }
.history-table { .history-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
} }
.history-table th, .history-table th,
.history-table td { .history-table td {
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 10px; padding: 10px;
text-align: left; text-align: left;
} }
.history-table th { .history-table th {
background-color: #007b8f; background-color: #007b8f;
color: #fff; color: #fff;
} }
.card { .card {
background-color: #eef7f9; background-color: #eef7f9;
padding: 15px; padding: 15px;
border-radius: 5px; border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
} }
.order-total { .order-total {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
margin-top: 20px; margin-top: 20px;
} }
.total { .total {
text-align: right; text-align: right;
} }
.amount-due { .amount-due {
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
} }
.amount-due-total { .amount-due-total {
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
color: #007b8f; color: #007b8f;
} }
.space-between { .space-between {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.text-break { .text-break {
word-wrap: break-word; word-wrap: break-word;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="header space-between"> <div class="header space-between">
<img class="logo" src="assets/img/logo.png" /> <img class="logo" src="assets/img/logo.png"/>
<div class="payee"> <div class="payee">
<b>{{ .Host.Name }}</b><br /> <b>{{ .Host.Name }}</b><br/>
{{ .Host.Address }}<br /> {{ .Host.Address }}<br/>
{{ .Host.ZipCode }} {{ .Host.City }}<br /> {{ .Host.ZipCode }} {{ .Host.City }}<br/>
<b>Tel:</b> {{ .Host.Phone }}<br /> <b>Tel :</b> {{ .Host.Phone }}<br/>
<b>Mail:</b> {{ .Host.Email }}<br /> <b>Mail :</b> {{ .Host.Email }}<br/>
</div>
</div> </div>
<hr /> </div>
<div class="billing-details space-between"> <hr/>
<table class="info-table"> <div class="billing-details space-between">
<table class="info-table">
<tbody> <tbody>
<tr> <tr>
<td><strong>{{ .Name }}</strong></td> <td><strong>{{ .Name }}</strong></td>
</tr> </tr>
<tr> <tr>
<td><strong>Tel:</strong></td> <td><strong>Tel :</strong></td>
<td>{{ .PhoneNumber }}</td> <td>{{ .PhoneNumber }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Voyageurs:</strong></td> <td><strong>Client :</strong></td>
<td>{{ .CustomersNumber }}</td> <td>{{ .CustomersNumber }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Plateforme:</strong></td> <td><strong>Plateforme :</strong></td>
<td>{{ .Platform }}</td> <td>{{ .Platform }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<table class="info-table"> <table class="info-table">
<tbody> <tbody>
<tr> <tr>
<td><strong>Nº de facture:</strong></td> <td><strong>Nº de facture :</strong></td>
<td>{{ .ID }}</td> <td>{{ .ID }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Du:</strong></td> <td><strong>Du :</strong></td>
<td>{{ .From }}</td> <td>{{ .From }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Au:</strong></td> <td><strong>Au :</strong></td>
<td>{{ .To }}</td> <td>{{ .To }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Montant Total:</strong></td> <td><strong>Montant Total :</strong></td>
<td>{{ .Total }}</td> <td>{{ .Total }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<hr /> <hr/>
<div class="order-details"> <div class="order-details">
<table class="items-table"> <table class="items-table">
<thead> <thead>
<tr> <tr>
<th>Objet</th> <th>Objet</th>
<th>Quantité</th> <th>Quantité</th>
<th>Prix (€)</th> <th>Prix (€)</th>
<th class="align-right">Total (€)</th> <th class="align-right">Total (€)</th>
</tr> </tr>
</thead> </thead>
<tbody class="bg-gray rounded"> <tbody class="bg-gray rounded">
{{ range .Lines }} {{ range .Lines }}
<tr class="item-row"> <tr class="item-row">
<td class="text-break product_name">{{ .Name }}</td> <td class="text-break product_name">{{ .Name }}</td>
<td>{{ .Quantity }}</td> <td>{{ .Quantity }}</td>
<td>{{ .Price }}</td> <td>{{ .Price }}</td>
<td class="align-right">{{ .Total }}</td> <td class="align-right">{{ .Total }}</td>
</tr> </tr>
{{ end }} {{ end }}
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="payment-history"> <div class="payment-history">
<h3>Historique des Paiements</h3> <h3>Historique des Paiements</h3>
<table class="history-table"> <table class="history-table">
<thead> <thead>
<tr> <tr>
<th>Date</th> <th>Date</th>
<th>Mode de Paiement</th> <th>Mode de Paiement</th>
<th>Montant (€)</th> <th>Montant (€)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{ range .Payments }} {{ range .Payments }}
<tr> <tr>
<td>{{ .Date }}</td> <td>{{ .Date }}</td>
<td>{{ .Method }}</td> <td>{{ .Method }}</td>
<td>{{ .Amount }}</td> <td>{{ .Amount }}</td>
</tr> </tr>
{{ end }} {{ end }}
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="payment-summary space-between"> <div class="payment-summary space-between">
<table class="summary-table"> <table class="summary-table">
<tbody> <tbody>
<tr> <tr>
<td><strong>Montant Total:</strong></td> <td><strong>Montant Total :</strong></td>
<td>{{ .Total }}</td> <td>{{ .Total }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Montant Payé:</strong></td> <td><strong>Montant Payé :</strong></td>
<td>{{ .AmountPaid }}</td> <td>{{ .AmountPaid }}</td>
</tr> </tr>
<tr> <tr>
<td><strong>Solde Restant:</strong></td> <td><strong>Solde Restant :</strong></td>
<td>{{ .BalanceDue }}</td> <td>{{ .BalanceDue }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<hr /> <hr/>
<div class="order-summary space-between"> <div class="order-summary space-between">
<div class="card"> <div class="card">
<b>Notes</b> <br /> <b>Notes</b> <br/>
TVA non applicable, art. 293 B du CGI <br /> TVA non applicable, art. 293 B du CGI <br/>
Dispensé dimmatriculation au registre du commerce et des sociétés (RCS) Dispensé dimmatriculation au registre du commerce et des sociétés (RCS)
et au répertoire des métiers. <br /> et au répertoire des métiers. <br/>
Conditions de paiement : paiement à réception de facture. Aucun escompte Conditions de paiement : paiement à réception de facture. Aucun escompte
consenti pour règlement anticipé ou désistement. Tout incident de consenti pour règlement anticipé ou désistement. Tout incident de
paiement est passible d'intérêts de retard. Le montant des pénalités paiement est passible d'intérêts de retard. Le montant des pénalités
résulte de l'application aux sommes restant dues d'un taux d'intérêt résulte de l'application aux sommes restant dues d'un taux d'intérêt
légal en vigueur au moment de l'incident. <br /> légal en vigueur au moment de l'incident. <br/>
</div>
</div> </div>
<div class="order-total space-between"> </div>
<div></div> <div class="order-total space-between">
<div class="total"> <div></div>
<div class="total">
<div class="amount-due">Total</div> <div class="amount-due">Total</div>
<div class="amount-due-total">{{ .Total }}</div> <div class="amount-due-total">{{ .Total }}</div>
</div>
</div> </div>
</body> </div>
</body>
</html> </html>

View file

@ -7,6 +7,8 @@ import (
"os" "os"
) )
const invoiceTemplate = "assets/html/invoice.html"
func main() { func main() {
// Define the invoice data structure // Define the invoice data structure
type Invoice struct { type Invoice struct {
@ -42,7 +44,7 @@ func main() {
} }
// Read the template file // Read the template file
tmpl, err := template.ParseFiles("assets/html/invoice.html") tmpl, err := template.ParseFiles(invoiceTemplate)
if err != nil { if err != nil {
log.Fatalf("Error parsing template: %v", err) log.Fatalf("Error parsing template: %v", err)
} }
@ -64,8 +66,8 @@ func main() {
Phone: "+590 690 44 15 30", Phone: "+590 690 44 15 30",
Email: "location.villafleurie@gmail.com", Email: "location.villafleurie@gmail.com",
}, },
Name: "VillaFleurie", Name: "Michel Le Corre",
PhoneNumber: "+590 690 44 15 30", //PhoneNumber: "+590 690 44 15 30",
CustomersNumber: 2, CustomersNumber: 2,
Platform: "Privée", Platform: "Privée",
ID: "VFNI0332", ID: "VFNI0332",
@ -107,7 +109,6 @@ func main() {
// Create a buffer to store the rendered HTML // Create a buffer to store the rendered HTML
var buf bytes.Buffer var buf bytes.Buffer
// Execute the template with the invoice data // Execute the template with the invoice data
if err := tmpl.Execute(&buf, invoice); err != nil { if err := tmpl.Execute(&buf, invoice); err != nil {
log.Fatalf("Error executing template: %v", err) log.Fatalf("Error executing template: %v", err)

View file

@ -1,292 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f9f9f9;
color: #333;
}
.header {
background-color: #007b8f;
color: #fff;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
height: 60px;
}
.payee {
text-align: right;
}
hr {
border: none;
border-top: 2px solid #ddd;
margin: 20px 0;
}
.info-table {
width: 48%;
margin-bottom: 20px;
}
.info-table td {
padding: 5px 10px;
}
.billing-details {
display: flex;
justify-content: space-between;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.items-table th,
.items-table td {
border: 1px solid #ddd;
padding: 10px;
text-align: left;
}
.items-table th {
background-color: #007b8f;
color: #fff;
}
.items-table .align-right {
text-align: right;
}
.bg-gray {
background-color: #f4f4f4;
}
.payment-summary {
margin-top: 20px;
}
.summary-table {
width: 100%;
}
.summary-table td {
padding: 10px;
}
.payment-history h3 {
color: #007b8f;
}
.history-table {
width: 100%;
border-collapse: collapse;
}
.history-table th,
.history-table td {
border: 1px solid #ddd;
padding: 10px;
text-align: left;
}
.history-table th {
background-color: #007b8f;
color: #fff;
}
.card {
background-color: #eef7f9;
padding: 15px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.order-total {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 20px;
}
.total {
text-align: right;
}
.amount-due {
font-size: 18px;
font-weight: bold;
}
.amount-due-total {
font-size: 24px;
font-weight: bold;
color: #007b8f;
}
.space-between {
display: flex;
justify-content: space-between;
}
.text-break {
word-wrap: break-word;
}
</style>
</head>
<body>
<div class="header space-between">
<img class="logo" src="assets/img/logo.png" />
<div class="payee">
<b>VillaFleurie</b><br />
4 rue Gerty Archimede<br />
97190 Le Gosier<br />
<b>Tel:</b> &#43;590 690 44 15 30<br />
<b>Mail:</b> location.villafleurie@gmail.com<br />
</div>
</div>
<hr />
<div class="billing-details space-between">
<table class="info-table">
<tbody>
<tr>
<td><strong>VillaFleurie</strong></td>
</tr>
<tr>
<td><strong>Tel:</strong></td>
<td>&#43;590 690 44 15 30</td>
</tr>
<tr>
<td><strong>Voyageurs:</strong></td>
<td>2</td>
</tr>
<tr>
<td><strong>Plateforme:</strong></td>
<td>Privée</td>
</tr>
</tbody>
</table>
<table class="info-table">
<tbody>
<tr>
<td><strong>Nº de facture:</strong></td>
<td>VFNI0332</td>
</tr>
<tr>
<td><strong>Du:</strong></td>
<td>15 Janvier 2025</td>
</tr>
<tr>
<td><strong>Au:</strong></td>
<td>15 Mars 2025</td>
</tr>
<tr>
<td><strong>Montant Total:</strong></td>
<td>2065.00</td>
</tr>
</tbody>
</table>
</div>
<hr />
<div class="order-details">
<table class="items-table">
<thead>
<tr>
<th>Objet</th>
<th>Quantité</th>
<th>Prix</th>
<th class="align-right">Total</th>
</tr>
</thead>
<tbody class="bg-gray rounded">
<tr class="item-row">
<td class="text-break product_name">T2</td>
<td>59</td>
<td>35.00</td>
<td class="align-right">2065.00</td>
</tr>
</tbody>
</table>
</div>
<div class="payment-summary space-between">
<table class="summary-table">
<tbody>
<tr>
<td><strong>Montant Total:</strong></td>
<td>2065.00</td>
</tr>
<tr>
<td><strong>Montant Payé:</strong></td>
<td>1565.00</td>
</tr>
<tr>
<td><strong>Solde Restant:</strong></td>
<td>500.00</td>
</tr>
</tbody>
</table>
</div>
<div class="payment-history">
<h3>Historique des Paiements</h3>
<table class="history-table">
<thead>
<tr>
<th>Date</th>
<th>Mode de Paiement</th>
<th>Montant</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>Espèces</td>
<td>500.00</td>
</tr>
<tr>
<td></td>
<td>Cheque</td>
<td>1565.00</td>
</tr>
</tbody>
</table>
</div>
<hr />
<div class="order-summary space-between">
<div class="card">
<b>Notes</b> <br />
TVA non applicable, art. 293 B du CGI <br />
Dispensé dimmatriculation au registre du commerce et des sociétés (RCS)
et au répertoire des métiers. <br />
Conditions de paiement : paiement à réception de facture. Aucun escompte
consenti pour règlement anticipé ou désistement. Tout incident de
paiement est passible d'intérêts de retard. Le montant des pénalités
résulte de l'application aux sommes restant dues d'un taux d'intérêt
légal en vigueur au moment de l'incident. <br />
</div>
</div>
<div class="order-total space-between">
<div></div>
<div class="total">
<div class="amount-due">Total</div>
<div class="amount-due-total">2065.00</div>
</div>
</div>
</body>
</html>