Supporting quote generation

This commit is contained in:
Finley Ghosh 2026-01-19 17:52:57 +11:00
parent 118a10b6f4
commit 3bfaa5b0fd
7 changed files with 155 additions and 77 deletions

View file

@ -610,12 +610,15 @@ func CountPages(w http.ResponseWriter, r *http.Request) {
return
}
log.Printf("CountPages: Attempting to count pages for file: %s", req.FilePath)
// Normalize path: remove double slashes
normalizedPath := strings.ReplaceAll(req.FilePath, "//", "/")
log.Printf("CountPages: Attempting to count pages for file: %s", normalizedPath)
// Count pages in the PDF file
pageCount, err := pdf.CountPDFPages(req.FilePath)
pageCount, err := pdf.CountPDFPages(normalizedPath)
if err != nil {
log.Printf("CountPages: error counting pages in %s: %v", req.FilePath, err)
log.Printf("CountPages: error counting pages in %s: %v", normalizedPath, err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]interface{}{
@ -625,7 +628,7 @@ func CountPages(w http.ResponseWriter, r *http.Request) {
return
}
log.Printf("CountPages: Successfully counted %d pages in %s", pageCount, req.FilePath)
log.Printf("CountPages: Successfully counted %d pages in %s", pageCount, normalizedPath)
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]int{"page_count": pageCount})
}

View file

@ -102,6 +102,7 @@ func (g *HTMLDocumentGenerator) GenerateInvoicePDF(data *InvoicePDFData) (string
}
filename := fmt.Sprintf("%s.pdf", filenameBase)
pdfPath := filepath.Join(g.outputDir, filename)
fmt.Printf("=== HTML Generator: Invoice filename generation - invoiceNumber='%s', filenameBase='%s', final filename='%s' ===\n", invoiceNumber, filenameBase, filename)
if err := g.htmlToPDF(tempHTML, pdfPath); err != nil {
return "", fmt.Errorf("failed to convert HTML to PDF (second pass): %w", err)
@ -195,6 +196,10 @@ func (g *HTMLDocumentGenerator) GenerateQuotePDF(data *QuotePDFData) (string, er
if data.Document != nil {
log.Printf("=== HTML Generator: Document not nil, CmcReference='%s'", data.Document.CmcReference)
quoteNumber = data.Document.CmcReference
if quoteNumber == "" {
// Strong fallback so we never emit a leading underscore filename
quoteNumber = fmt.Sprintf("Quote-%d", data.Document.ID)
}
if data.Document.Revision > 0 {
quoteNumber = fmt.Sprintf("%s_%d", quoteNumber, data.Document.Revision)
}
@ -224,11 +229,27 @@ func (g *HTMLDocumentGenerator) GenerateQuotePDF(data *QuotePDFData) (string, er
tempMergedPath := filepath.Join(g.outputDir, fmt.Sprintf("%s_merged_temp.pdf", filenameBase))
if err := MergePDFs(pdfPath, termsPath, tempMergedPath); err != nil {
fmt.Printf("=== HTML Generator: Warning - could not merge T&C PDF: %v. Returning quote without T&C.\n", err)
fmt.Printf("=== HTML Generator: Checking if pdfPath exists: %s\n", pdfPath)
if stat, err := os.Stat(pdfPath); err == nil {
fmt.Printf("=== HTML Generator: pdfPath exists, size=%d bytes\n", stat.Size())
}
return filename, nil
}
fmt.Printf("=== HTML Generator: Merge succeeded, replacing original PDF\n")
// 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)
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")
}
// 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)
}
}

View file

@ -875,11 +875,19 @@ ENDINSTRUCTIONS;
} else {
$filename = 'Quote-' . $document['Document']['id'];
}
$this->set('docTitle', $enquiry['Enquiry']['title']);
$this->set('job', $job);
$this->set('issue_date_string', date('d F Y', strtotime($document['Quote']['issue_date'])));
$this->set('ship_via', $document['Quote']['ship_via']);
$this->set('fob', $document['Quote']['fob']);
$template_name = 'pdf_quote';
break;
case "invoice":
$filename = $document['Invoice']['title'];
$this->set('docTitle', $document['Invoice']['title']);
$this->set('job', $this->Document->Invoice->Job->find('first', array('conditions'=>array('Job.id'=>$document['Invoice']['job_id']))));
$this->set('issue_date_string', date('d F Y', strtotime($document['Invoice']['issue_date'])));
$this->set('ship_via', $document['Invoice']['ship_via']);
$this->set('fob', $document['Invoice']['fob']);
$template_name = 'pdf_invoice';
@ -948,34 +956,15 @@ ENDINSTRUCTIONS;
break;
}
if($document['Document']['revision'] > 0) {
$filename = $filename.'_'.$document['Document']['revision'].'.pdf';
}
else {
$filename = $filename.'.pdf';
}
$this->set('filename', $filename);
$document['Document']['pdf_filename'] = $filename;
$document['Document']['pdf_created_at'] = date('Y-m-d H:i:s');
$document['Document']['pdf_created_by_user_id'] = $this->getCurrentUserID();
if($this->Document->save($document)) {
//echo "Set pdf_filename attritbute to: ".$filename;
// Count pages and update doc_page_count
App::import('Vendor','pagecounter');
$pageCounter = new PageCounter();
$pdfPath = $pdf_dir . $filename;
$pageCount = $pageCounter->count($pdfPath);
if ($pageCount > 0) {
$this->Document->id = $document['Document']['id'];
$this->Document->saveField('doc_page_count', $pageCount);
}
}
else {
//echo 'Failed to set pdf_filename to: '.$filename;
}
// Don't set filename here - let the Go service and view handle it
// The filename will be set by the pdf_* view after Go generates the PDF
$this->set('filename', '');
$this->set('docType', $docType);
$this->set('docTypeFullName', strtoupper($docTypeFullName));
$this->set('currency',$currency);
$this->set('currencyCode', isset($currencyCode) ? $currencyCode : 'AUD');
$this->set('currencySymbol', isset($currencySymbol) ? $currencySymbol : '$');
$colWidths = array(
'item' => '7%',

View file

@ -9,7 +9,7 @@ class PageCounter {
* Returns the page count or null if unable to determine.
*/
function count($file) {
error_log("PageCounter: Attempting to count pages for file: $file");
error_log("PageCounter: START - Attempting to count pages for file: $file");
if (!file_exists($file) || !is_readable($file)) {
error_log("PageCounter: File does not exist or is not readable: $file");
@ -17,6 +17,7 @@ class PageCounter {
}
try {
error_log("PageCounter: File exists and is readable, calling getGoBaseUrlOrFail()");
// Get the Go base URL from config
App::import('Controller', 'App');
$appController = new AppController();

View file

@ -42,39 +42,23 @@ $pagecounter = new PageCounter();
<?php foreach($attachments as $attachment) { ?>
<?php
// Debug log for page counter paths/results
$debugPath = APP.'webroot/pdf/pagecounter_debug.txt';
// Normalize attachment path: handle double slashes and ensure correct base path
// Normalize attachment path
// Database stores paths like /var/www/cmc-sales/app/webroot/attachments_files/...
// Docker mounts this at /var/www/cmc-sales/app/webroot/attachments_files/ so paths are correct as-is
$rawPath = $attachment['Attachment']['file'];
// Log the raw path from database
file_put_contents($debugPath, "Attachment: {$attachment['Attachment']['name']}\nRaw DB path: {$rawPath}\n", FILE_APPEND);
// If path starts with /webroot, prefix with APP; otherwise use as-is
if (strpos($rawPath, '/webroot/') === 0 || strpos($rawPath, '//webroot/') === 0) {
$attachmentPath = APP . ltrim($rawPath, '/');
} elseif (strpos($rawPath, APP) === 0) {
// Already has full APP path, use as-is
$attachmentPath = $rawPath;
} else {
// Default: prefix with APP
$attachmentPath = APP . ltrim($rawPath, '/');
}
// Remove any double slashes
$attachmentPath = preg_replace('#/+#', '/', $attachmentPath);
$exists = file_exists($attachmentPath) ? 'yes' : 'no';
$readable = is_readable($attachmentPath) ? 'yes' : 'no';
file_put_contents($debugPath, "Computed path: {$attachmentPath}\nExists: {$exists}, Readable: {$readable}\n", FILE_APPEND);
$attachmentPath = preg_replace('#/+#', '/', $rawPath);
$pagecount = '';
if($attachment['Attachment']['type'] == 'application/pdf') {
$count = $pagecounter->count($attachmentPath);
$pagecount = '('.$count.' pages)';
file_put_contents($debugPath, "Count result: {$count}\n---\n", FILE_APPEND);
if ($count !== null && $count > 0) {
$pagecount = '(' . $count . ' pages)';
} else {
file_put_contents($debugPath, "Skipped (non-pdf)\n---\n", FILE_APPEND);
$pagecount = '(page count unavailable)';
}
}
?>
<?= $attachment['Attachment']['name']; ?> <?= $pagecount ?><br>

View file

@ -1,8 +1,17 @@
<?php
// Generate the Invoice PDF by calling the Go service instead of TCPDF.
error_log("=== pdf_invoice.ctp: Starting invoice PDF generation ===");
error_log("=== pdf_invoice.ctp: Document ID: " . (isset($document['Document']['id']) ? $document['Document']['id'] : 'UNDEFINED') . " ===");
if (!isset($document) || !isset($document['Document'])) {
error_log("=== pdf_invoice.ctp: ERROR - Document not set in view! ===");
echo "ERROR: Document data not available in view";
exit;
}
$goBaseUrl = AppController::getGoBaseUrlOrFail();
$goEndpoint = $goBaseUrl . '/go/api/pdf/generate-invoice';
error_log("=== pdf_invoice.ctp: Go endpoint: " . $goEndpoint . " ===");
$outputDir = Configure::read('pdf_directory');
@ -106,9 +115,12 @@ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
error_log("=== pdf_invoice.ctp: Making curl request to Go service ===");
$response = curl_exec($ch);
error_log("=== pdf_invoice.ctp: Curl response received: " . substr($response, 0, 200) . " ===");
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
error_log("=== pdf_invoice.ctp: HTTP Code: " . $httpCode . ", Curl Error: " . $curlErr . " ===");
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
@ -123,9 +135,50 @@ if ($httpCode < 200 || $httpCode >= 300) {
echo "</p>";
exit;
}
?>
// PDF generated successfully - capture the filename from Go and save to database
$result = json_decode($response, true);
if (isset($result['filename'])) {
$Document = ClassRegistry::init('Document');
$Document->id = $document['Document']['id'];
$Document->saveField('pdf_filename', $result['filename']);
$Document->saveField('pdf_created_at', date('Y-m-d H:i:s'));
// Get user ID safely
$userId = null;
if (isset($this->Session)) {
$userId = $this->Session->read('Auth.User.id');
}
if ($userId) {
$Document->saveField('pdf_created_by_user_id', $userId);
}
// Count pages using the Go service
App::import('Vendor','pagecounter');
$pageCounter = new PageCounter();
$pdfPath = $outputDir . '/' . $result['filename'];
error_log("=== pdf_invoice.ctp: Counting pages for PDF: " . $pdfPath . " ===");
$pageCount = $pageCounter->count($pdfPath);
error_log("=== pdf_invoice.ctp: Page count result: " . var_export($pageCount, true) . " ===");
if ($pageCount !== null && $pageCount > 0) {
$Document->saveField('doc_page_count', $pageCount);
error_log("=== pdf_invoice.ctp: Saved page count: " . $pageCount . " ===");
} else {
error_log("=== pdf_invoice.ctp: Page count was null or 0, not saving ===");
}
}
error_log("=== pdf_invoice.ctp: About to redirect to /documents/view/" . $document['Document']['id'] . " ===");
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0;url=/documents/view/<?=$document['Document']['id']?>">
</head>
<body>
<p>PDF generated successfully. <a href="/documents/view/<?=$document['Document']['id']?>">Click here if you are not redirected</a>.</p>
<script type="text/javascript">
window.location.replace("/documents/view/<?=$document['Document']['id']?>");
</script>
</body>
</html>

View file

@ -92,10 +92,30 @@ if ($httpCode < 200 || $httpCode >= 300) {
exit;
}
// PDF generated successfully - now count pages and update database
// PDF generated successfully - now save metadata and count pages
$result = json_decode($response, true);
if (isset($result['filename'])) {
// Build path, removing any double slashes
$pdfPath = $outputDir . '/' . $result['filename'];
$pdfPath = preg_replace('#/+#', '/', $pdfPath);
// Update database with PDF metadata
$Document = ClassRegistry::init('Document');
$Document->id = $document['Document']['id'];
$Document->saveField('pdf_filename', $result['filename']);
$Document->saveField('pdf_created_at', date('Y-m-d H:i:s'));
// Get user ID safely (match invoice logic)
$userId = null;
if (isset($this->Session)) {
$userId = $this->Session->read('Auth.User.id');
}
if (!$userId && isset($_SESSION['Auth']['User']['id'])) {
$userId = $_SESSION['Auth']['User']['id'];
}
if ($userId) {
$Document->saveField('pdf_created_by_user_id', $userId);
}
// Count pages using the Go service
App::import('Vendor','pagecounter');
@ -103,14 +123,21 @@ if (isset($result['filename'])) {
$pageCount = $pageCounter->count($pdfPath);
if ($pageCount > 0) {
// Update the document with the page count
$Document = ClassRegistry::init('Document');
$Document->id = $document['Document']['id'];
$Document->saveField('doc_page_count', $pageCount);
}
}
?>
<script type="text/javascript">
window.location.replace("/documents/view/<?=$document['Document']['id']?>");
</script>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0;url=/documents/view/<?=$document['Document']['id']?>" />
<title>Redirecting...</title>
</head>
<body>
<p>PDF generated successfully. Redirecting back to quote...</p>
<p><a href="/documents/view/<?=$document['Document']['id']?>">Click here if not redirected</a></p>
<script type="text/javascript">
window.location.replace("/documents/view/<?=$document['Document']['id']?>");
</script>
</body>
</html>