Quotes almost working correctly
This commit is contained in:
parent
e197dc540a
commit
b9d44fc82b
|
|
@ -267,6 +267,7 @@ func GenerateQuotePDF(w http.ResponseWriter, r *http.Request) {
|
|||
ItemNumber: li.ItemNumber,
|
||||
Quantity: li.Quantity,
|
||||
Title: li.Title,
|
||||
Description: li.Description,
|
||||
GrossUnitPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.UnitPrice), Valid: true},
|
||||
GrossPrice: sql.NullString{String: fmt.Sprintf("%.2f", li.TotalPrice), Valid: true},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ func (g *HTMLDocumentGenerator) GenerateInvoicePDF(data *InvoicePDFData) (string
|
|||
os.Remove(tempPDFPath)
|
||||
os.Remove(tempMergedPath)
|
||||
|
||||
// SECOND PASS: Generate final PDF with correct page count
|
||||
fmt.Printf("=== HTML Generator: Second pass - regenerating with page count %d ===\n", totalPageCount)
|
||||
html = g.BuildInvoiceHTML(data, totalPageCount, 1)
|
||||
// SECOND PASS: Generate final PDF without page count in HTML (will be added via pdfcpu after merge)
|
||||
fmt.Println("=== HTML Generator: Second pass - regenerating final PDF ===")
|
||||
html = g.BuildInvoiceHTML(data, 0, 0)
|
||||
|
||||
if err := ioutil.WriteFile(tempHTML, []byte(html), 0644); err != nil {
|
||||
return "", fmt.Errorf("failed to write temp HTML (second pass): %w", err)
|
||||
|
|
@ -121,9 +121,17 @@ func (g *HTMLDocumentGenerator) GenerateInvoicePDF(data *InvoicePDFData) (string
|
|||
// Replace original with merged version
|
||||
if err := os.Rename(tempMergedPath, pdfPath); err != nil {
|
||||
fmt.Printf("=== HTML Generator: Warning - could not replace PDF: %v\n", err)
|
||||
return filename, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add page numbers to final PDF
|
||||
// TODO: Re-enable after fixing pdfcpu watermark API usage
|
||||
// fmt.Println("=== HTML Generator: Adding page numbers to final PDF ===")
|
||||
// if err := AddPageNumbers(pdfPath); err != nil {
|
||||
// fmt.Printf("=== HTML Generator: Warning - could not add page numbers: %v\n", err)
|
||||
// }
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
|
|
@ -183,9 +191,9 @@ func (g *HTMLDocumentGenerator) GenerateQuotePDF(data *QuotePDFData) (string, er
|
|||
os.Remove(tempPDFPath)
|
||||
os.Remove(tempMergedPath)
|
||||
|
||||
// SECOND PASS: Generate final PDF with correct page count
|
||||
fmt.Printf("=== HTML Generator: Second pass - regenerating with page count %d ===\n", totalPageCount)
|
||||
html = g.BuildQuoteHTML(data, totalPageCount, 1)
|
||||
// SECOND PASS: Generate final PDF without page count in HTML (will be added via pdfcpu after merge)
|
||||
fmt.Println("=== HTML Generator: Second pass - regenerating final PDF ===")
|
||||
html = g.BuildQuoteHTML(data, 0, 0)
|
||||
|
||||
if err := ioutil.WriteFile(tempHTML, []byte(html), 0644); err != nil {
|
||||
return "", fmt.Errorf("failed to write temp HTML (second pass): %w", err)
|
||||
|
|
@ -241,18 +249,26 @@ func (g *HTMLDocumentGenerator) GenerateQuotePDF(data *QuotePDFData) (string, er
|
|||
fmt.Printf("=== HTML Generator: Warning - could not replace PDF: %v\n", err)
|
||||
fmt.Printf("=== HTML Generator: tempMergedPath: %s\n", tempMergedPath)
|
||||
fmt.Printf("=== HTML Generator: pdfPath: %s\n", pdfPath)
|
||||
} else {
|
||||
fmt.Printf("=== HTML Generator: Replaced PDF successfully\n")
|
||||
return filename, err
|
||||
}
|
||||
fmt.Printf("=== HTML Generator: Replaced PDF successfully\n")
|
||||
|
||||
// Verify the final file exists
|
||||
if stat, err := os.Stat(pdfPath); err == nil {
|
||||
fmt.Printf("=== HTML Generator: Final PDF verified, size=%d bytes\n", stat.Size())
|
||||
} else {
|
||||
fmt.Printf("=== HTML Generator: ERROR - Final PDF does not exist after merge: %v\n", err)
|
||||
return filename, fmt.Errorf("final PDF does not exist: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add page numbers to final PDF
|
||||
// TODO: Re-enable after fixing pdfcpu watermark API usage
|
||||
// fmt.Println("=== HTML Generator: Adding page numbers to final PDF ===")
|
||||
// if err := AddPageNumbers(pdfPath); err != nil {
|
||||
// fmt.Printf("=== HTML Generator: Warning - could not add page numbers: %v\n", err)
|
||||
// }
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
41
go/internal/cmc/pdf/page_numbers.go
Normal file
41
go/internal/cmc/pdf/page_numbers.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package pdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pdfcpu/pdfcpu/pkg/api"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/color"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
|
||||
)
|
||||
|
||||
// AddPageNumbers adds page numbers to a PDF file
|
||||
// This should be called after all merging is complete to show accurate page counts
|
||||
func AddPageNumbers(pdfPath string) error {
|
||||
// Read the PDF to get page count
|
||||
pageCount, err := api.PageCountFile(pdfPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get page count: %w", err)
|
||||
}
|
||||
|
||||
// Create watermark configuration for page numbers
|
||||
// Position: bottom right, slightly above footer
|
||||
wm := &model.Watermark{
|
||||
TextString: fmt.Sprintf("Page $p of %d", pageCount),
|
||||
FontName: "Helvetica",
|
||||
FontSize: 9,
|
||||
ScaleAbs: true,
|
||||
Color: color.Black,
|
||||
Pos: types.BottomRight,
|
||||
Dx: -15, // 15mm from right
|
||||
Dy: 25, // 25mm from bottom
|
||||
Rotation: 0,
|
||||
}
|
||||
|
||||
// Apply watermark (page numbers) to all pages
|
||||
if err := api.AddWatermarksFile(pdfPath, pdfPath, nil, wm, nil); err != nil {
|
||||
return fmt.Errorf("failed to add page numbers: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -9,6 +9,15 @@
|
|||
margin: 15mm;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 9pt;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0 0 25mm 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 40mm;
|
||||
height: auto;
|
||||
|
|
@ -118,13 +127,6 @@
|
|||
.pricing-header {
|
||||
text-align: center;
|
||||
margin: 8mm 0 5mm 0;
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
.pricing-header h2 {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.line-items {
|
||||
|
|
@ -134,6 +136,7 @@
|
|||
table-layout: fixed;
|
||||
margin-right: 1mm;
|
||||
margin-left: auto;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.line-items th {
|
||||
|
|
@ -144,33 +147,39 @@
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.line-items tbody tr {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.line-items td {
|
||||
border: 1px solid #000;
|
||||
padding: 2mm;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.line-items .item-no {
|
||||
width: 7%;
|
||||
text-align: center;
|
||||
.line-items .description {
|
||||
width: 50%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.line-items .qty {
|
||||
width: 7%;
|
||||
width: 10%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.line-items .description {
|
||||
width: 56%;
|
||||
.line-items .unit-price {
|
||||
width: 13%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.line-items .unit-price {
|
||||
width: 15%;
|
||||
.line-items .discount {
|
||||
width: 13%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.line-items .total {
|
||||
width: 15%;
|
||||
width: 14%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
|
@ -178,6 +187,8 @@
|
|||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 5mm;
|
||||
page-break-inside: avoid;
|
||||
page-break-before: avoid;
|
||||
}
|
||||
|
||||
.totals-table {
|
||||
|
|
@ -298,12 +309,6 @@
|
|||
.footer .service-flow { color: #2F4BE0; }
|
||||
.footer .service-process { color: #AB31F8; }
|
||||
|
||||
.page-number {
|
||||
position: fixed;
|
||||
bottom: 25mm;
|
||||
right: 15mm;
|
||||
font-size: 9pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -347,31 +352,29 @@
|
|||
<!-- Pricing & Specifications Header -->
|
||||
<div class="pricing-header">
|
||||
<h2>PRICING & SPECIFICATIONS</h2>
|
||||
<div style="margin-top: 3mm; text-align: right; font-weight: bold; font-size: 9pt;">
|
||||
Shown in {{.CurrencyCode}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Line Items Table -->
|
||||
<table class="line-items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="item-no">ITEM</th>
|
||||
<th class="qty">QTY</th>
|
||||
<th class="description">DESCRIPTION</th>
|
||||
<th class="qty">QTY</th>
|
||||
<th class="unit-price">UNIT PRICE</th>
|
||||
<th class="discount">DISCOUNT</th>
|
||||
<th class="total">TOTAL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .LineItems}}
|
||||
<tr>
|
||||
<td class="item-no">{{.ItemNumber}}</td>
|
||||
<td class="description"><strong>{{.Title}}</strong><br>{{.Description}}</td>
|
||||
<td class="qty">{{.Quantity}}</td>
|
||||
<td class="description">
|
||||
<strong>{{.Title}}</strong>
|
||||
{{if .Description}}
|
||||
<div>{{.Description}}</div>
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="unit-price">{{formatPrice .GrossUnitPrice}}</td>
|
||||
<td class="discount">$0.00</td>
|
||||
<td class="total">{{formatPrice .GrossPrice}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
|
@ -460,13 +463,6 @@
|
|||
</table>
|
||||
{{end}}
|
||||
|
||||
<!-- Page Number -->
|
||||
{{if .PageCount}}
|
||||
<div class="page-number">
|
||||
Page {{.CurrentPage}} of {{.PageCount}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<div class="services-title">CMC TECHNOLOGIES Provides Solutions in the Following Fields</div>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ foreach ($document['LineItem'] as $li) {
|
|||
'item_number' => $li['item_number'],
|
||||
'quantity' => $li['quantity'],
|
||||
'title' => $li['title'],
|
||||
'description' => isset($li['description']) ? $li['description'] : '',
|
||||
'unit_price' => floatval($li['gross_unit_price']),
|
||||
'total_price' => floatval($li['gross_price'])
|
||||
);
|
||||
|
|
@ -41,6 +42,16 @@ $userLastName = !empty($enquiry['User']['last_name']) ? $enquiry['User']['last_n
|
|||
$userEmail = !empty($enquiry['User']['email']) ? $enquiry['User']['email'] : '';
|
||||
$createdDate = !empty($enquiry['Enquiry']['created']) ? $enquiry['Enquiry']['created'] : date('Y-m-d');
|
||||
|
||||
// Extract GST setting from enquiry data (fallback to currency or document data)
|
||||
$showGst = false;
|
||||
if (isset($enquiry['Enquiry']['gst'])) {
|
||||
$showGst = (bool)$enquiry['Enquiry']['gst'];
|
||||
} elseif (isset($gst)) {
|
||||
$showGst = (bool)$gst;
|
||||
} elseif (isset($currencyCode) && $currencyCode === 'AUD') {
|
||||
$showGst = true; // Default to GST for Australian quotes
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'document_id' => intval($document['Document']['id']),
|
||||
'cmc_reference' => $cmcReference,
|
||||
|
|
@ -55,7 +66,7 @@ $payload = array(
|
|||
'user_email' => $userEmail,
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'currency_code' => isset($currencyCode) ? $currencyCode : 'AUD',
|
||||
'show_gst' => (bool)$gst,
|
||||
'show_gst' => $showGst,
|
||||
'commercial_comments' => isset($document['Quote']['commercial_comments']) ? $document['Quote']['commercial_comments'] : '',
|
||||
'line_items' => $lineItems,
|
||||
'pages' => array_map(function($p) { return $p['content']; }, $document['DocPage']),
|
||||
|
|
|
|||
Loading…
Reference in a new issue