fix: PDF via JSON-RPC render, full-width table with nowrap columns
PDF download: - Drop HTTP Basic Auth approach (Odoo's /report/pdf/ endpoint rejects it) - Call ir.actions.report.render_qweb_pdf() via the already-working authenticated JSON-RPC connection; returns base64-encoded PDF bytes - Validate base64_decode result starts with %PDF before serving - Descriptive Spanish error messages for each failure point Table layout: - Remove table-layout:fixed which was squashing columns into WC's ~650px content column - Add min-width:820px so table never compresses below readable width (scrolls horizontally on small screens instead) - .woodoo-invoices breaks out 100px into page margins on desktop (margin: 0 -100px; width: calc(100% + 200px)) for full-width feel - Reverts to 100% width below 960px - All key columns use white-space:nowrap + min-width so invoice reference, dates and amounts never wrap to multiple lines Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -50,37 +50,55 @@
|
|||||||
.woodoo-badge--blue { background: #dbeafe; color: #1e40af; }
|
.woodoo-badge--blue { background: #dbeafe; color: #1e40af; }
|
||||||
.woodoo-badge--grey { background: #f3f4f6; color: #6b7280; }
|
.woodoo-badge--grey { background: #f3f4f6; color: #6b7280; }
|
||||||
|
|
||||||
|
/* ── Invoice table wrapper: break out of the narrow WC column ─────── */
|
||||||
|
.woodoo-invoices {
|
||||||
|
/* Push 100px into the page margin on each side on wide screens */
|
||||||
|
margin-left: -100px;
|
||||||
|
margin-right: -100px;
|
||||||
|
width: calc(100% + 200px);
|
||||||
|
}
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.woodoo-invoices {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Table ───────────────────────────────────────────────── */
|
/* ── Table ───────────────────────────────────────────────── */
|
||||||
.woodoo-table-wrap { overflow-x: auto; }
|
.woodoo-table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
.woodoo-table {
|
.woodoo-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-width: 820px; /* never compress below this — scroll horizontally instead */
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-size: .875rem;
|
font-size: .875rem;
|
||||||
table-layout: fixed; /* fixed layout so column widths are respected */
|
/* No table-layout:fixed — let the browser size columns to content */
|
||||||
}
|
}
|
||||||
.woodoo-table th,
|
.woodoo-table th,
|
||||||
.woodoo-table td {
|
.woodoo-table td {
|
||||||
padding: 10px 14px;
|
padding: 10px 16px;
|
||||||
border-bottom: 1px solid #e5e7eb;
|
border-bottom: 1px solid #e5e7eb;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.woodoo-table th { background: #f9fafb; font-weight: 600; }
|
.woodoo-table th { background: #f9fafb; font-weight: 600; white-space: nowrap; }
|
||||||
.woodoo-table tr:last-child td { border-bottom: none; }
|
.woodoo-table tr:last-child td { border-bottom: none; }
|
||||||
.woodoo-table .woodoo-amount {
|
.woodoo-table .woodoo-amount {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
/* Invoice-specific column widths */
|
/* Force key invoice columns to never wrap */
|
||||||
.woodoo-invoices-table .col-number { width: 180px; white-space: nowrap; font-weight: 600; }
|
.woodoo-invoices-table .col-number { white-space: nowrap; font-weight: 600; min-width: 160px; }
|
||||||
.woodoo-invoices-table .col-date { width: 100px; white-space: nowrap; }
|
.woodoo-invoices-table .col-date { white-space: nowrap; min-width: 90px; }
|
||||||
.woodoo-invoices-table .col-due { width: 130px; }
|
.woodoo-invoices-table .col-due { white-space: nowrap; min-width: 110px; }
|
||||||
.woodoo-invoices-table .col-amount { width: 110px; text-align: right; }
|
.woodoo-invoices-table .col-amount { white-space: nowrap; min-width: 100px; text-align: right; }
|
||||||
.woodoo-invoices-table .col-balance { width: 130px; text-align: right; }
|
.woodoo-invoices-table .col-balance { white-space: nowrap; min-width: 120px; text-align: right; }
|
||||||
.woodoo-invoices-table .col-status { width: 100px; white-space: nowrap; }
|
.woodoo-invoices-table .col-status { white-space: nowrap; min-width: 90px; }
|
||||||
.woodoo-invoices-table .col-download{ width: 70px; text-align: center; }
|
.woodoo-invoices-table .col-download{ min-width: 60px; text-align: center; }
|
||||||
|
|
||||||
/* Utility: never wrap content in a cell */
|
/* Utility: never wrap content in a cell */
|
||||||
.woodoo-nowrap { white-space: nowrap; }
|
.woodoo-nowrap { white-space: nowrap; }
|
||||||
|
|||||||
@@ -131,33 +131,30 @@ class WooDoo_Invoices {
|
|||||||
wp_die( esc_html__( 'Invoice not found.', 'woodoo' ) );
|
wp_die( esc_html__( 'Invoice not found.', 'woodoo' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the PDF via HTTP Basic Auth (API key as password – supported in Odoo 17+).
|
// Use the authenticated JSON-RPC connection to render the PDF.
|
||||||
// This avoids the fragile session-cookie approach entirely.
|
// Odoo's /report/pdf/ HTTP endpoint only accepts session cookies, not API keys.
|
||||||
$odoo_url = rtrim( get_option( 'woodoo_odoo_url', '' ), '/' );
|
// Calling ir.actions.report.render_qweb_pdf() via execute_kw works with any
|
||||||
$pdf_url = $odoo_url . '/report/pdf/account.report_invoice/' . $invoice_id;
|
// valid authenticated user and returns the PDF content base64-encoded.
|
||||||
$basic_auth = 'Basic ' . base64_encode(
|
$result = $api->execute_kw(
|
||||||
get_option( 'woodoo_odoo_username', '' ) . ':' . get_option( 'woodoo_odoo_api_key', '' )
|
'ir.actions.report',
|
||||||
|
'render_qweb_pdf',
|
||||||
|
[ 'account.report_invoice', [ $invoice_id ] ]
|
||||||
);
|
);
|
||||||
|
|
||||||
$pdf_response = wp_remote_get(
|
if ( is_wp_error( $result ) ) {
|
||||||
$pdf_url,
|
wp_die( 'Error de Odoo al generar el PDF: ' . esc_html( $result->get_error_message() ) );
|
||||||
[
|
|
||||||
'headers' => [ 'Authorization' => $basic_auth ],
|
|
||||||
'timeout' => 90,
|
|
||||||
'sslverify' => apply_filters( 'woodoo_ssl_verify', true ),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( is_wp_error( $pdf_response ) ) {
|
|
||||||
wp_die( 'No se pudo obtener el PDF de la factura: ' . esc_html( $pdf_response->get_error_message() ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$http_code = wp_remote_retrieve_response_code( $pdf_response );
|
// Result is [base64_pdf_string, 'pdf']
|
||||||
$pdf_body = wp_remote_retrieve_body( $pdf_response );
|
$b64 = is_array( $result ) ? ( $result[0] ?? '' ) : $result;
|
||||||
|
if ( empty( $b64 ) ) {
|
||||||
|
wp_die( 'Odoo devolvió una respuesta vacía al generar el PDF.' );
|
||||||
|
}
|
||||||
|
|
||||||
// Guard: Odoo may return a JSON error or HTML login page instead of a PDF
|
$pdf_body = base64_decode( $b64, true );
|
||||||
if ( $http_code !== 200 || substr( $pdf_body, 0, 4 ) !== '%PDF' ) {
|
|
||||||
wp_die( 'No se pudo generar el PDF. Código HTTP: ' . esc_html( $http_code ) . '. Comprueba los permisos del usuario de la API en Odoo.' );
|
if ( $pdf_body === false || substr( $pdf_body, 0, 4 ) !== '%PDF' ) {
|
||||||
|
wp_die( 'El contenido recibido de Odoo no es un PDF válido. Comprueba que el usuario de la API tenga permisos de impresión en Odoo.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
header( 'Content-Type: application/pdf' );
|
header( 'Content-Type: application/pdf' );
|
||||||
|
|||||||
Reference in New Issue
Block a user