This commit is contained in:
gsinghpal
2026-03-13 12:38:28 -04:00
parent db4b9aa278
commit fc3c966484
2975 changed files with 1614 additions and 498 deletions

View File

@@ -3,157 +3,41 @@
<h4 class="oe_slogan">
<span class="label label-info orders_label_text_align">
<span class="fa fa-star-o fa-spin"></span>
Pdf Print Preview
Fusion PDF Preview
</span>
</h4>
<section style="text-align: center;font-size: 24px;">
<b> Preview and print PDF report in your browser </b>
<b>Preview and print PDF reports directly in your browser</b>
<p style="font-size: 16px;">by Nexa Systems Inc</p>
</section>
<h4 class="oe_slogan">
<span class="label label-success orders_label_text_align">
<span class="fa fa-certificate fa-spin"></span>
Feature
Features
</span>
</h4>
<ul class="list-unstyled" style="font-size: 21px;">
<li><i class="fa fa-check text-primary"></i> <b>PDF report preview, in current tab.</b></li>
<li><i class="fa fa-check text-primary"></i> <b>PDF report preview, in another tab.</b></li>
<li><i class="fa fa-check text-primary"></i> <b>Load report in another tab with print by default.</b></li>
<li><i class="fa fa-check text-primary"></i> <b>Pos, point of sale pdf report support. All odoo app like sale, purchase support.</b></li>
<li><i class="fa fa-check text-primary"></i> <b>In-browser PDF report preview dialog</b></li>
<li><i class="fa fa-check text-primary"></i> <b>Full-screen document viewing</b></li>
<li><i class="fa fa-check text-primary"></i> <b>One-click printing without download</b></li>
<li><i class="fa fa-check text-primary"></i> <b>Per-user configurable preview settings</b></li>
<li><i class="fa fa-check text-primary"></i> <b>Works with all Odoo apps (Sale, Purchase, POS, Stock, etc.)</b></li>
<li><i class="fa fa-check text-primary"></i> <b>Compatible with Odoo 19 Community and Enterprise</b></li>
</ul>
<h4 class="oe_slogan">
<span class="label label-warning orders_label_text_align">
<span class="fa fa-cog fa-spin"></span>
Configuration
</span>
</h4>
<div class="oe_span12">
<h4><i class="fa fa-star-o fa-spin"></i> <b>When a user is administrator he can update report preview for each user.</b></h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_1.PNG">
</div>
<h4><i class="fa fa-star-o fa-spin"></i> <b>When a user is administrator Or not administrator he can update report preview with this shortcut.</b></h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_2.PNG" />
</div>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_3.PNG" />
</div>
<h4><i class="fa fa-star-o fa-spin"></i><b>Try to allow <b style='color: red;'>pop up</b> in your browser before preview report in another tab.</b>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_12.png" />
</div>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_11.png" />
</div>
</div>
<h4 class="oe_slogan">
<span class="label label-success orders_label_text_align">
Demo test
</span>
</h4>
<div class="oe_span12">
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_4.PNG">
</div>
<h4 class="oe_slogan">
<span class="orders_label_text_align">
<b> Report preview </b>
</span>
</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_5.PNG">
</div>
<h4 class="oe_slogan">
<span class="orders_label_text_align">
<b> Full screen 1 </b>
</span>
</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="report.png">
</div>
<h4 class="oe_slogan">
<span class="orders_label_text_align">
<b> Full screen 2 </b>
</span>
</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_7.png">
</div>
<h4 class="oe_slogan">
<span class="orders_label_text_align">
<b> Print report </b>
</span>
</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="print_report.png">
</div>
<h4 class="oe_slogan">
<span class="orders_label_text_align">
<b> Load report in another tab with print by default </b>
</span>
</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_9.PNG">
</div>
<h4 class="oe_slogan">
<span class="orders_label_text_align">
<b> Load report in another tab </b>
</span>
</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="config_10.PNG">
</div>
<h4 class="oe_slogan">
<span class="label label-warning orders_label_text_align">
HTML5 Browser Support
</span>
</h4>
<h4>
<span>
<i class="fa fa-check text-primary"> Print by default( <b>Automatic printing</b> ) is tested just with Chrome and Opera.
</span>
</h4>
<h4>
<span>
<i class="fa fa-check text-primary"> Pdf print preview Compatible with all browser.
</span>
</h4>
<div class="oe_row oe_spaced text-center">
<a class="btn btn-warning btn-lg" href="https://www.youtube.com/watch?v=hEXqt4moKts" target="_blank"><i class="fa fa-youtube" style="padding: 3px"></i>Click here to see it on youtube
</a>
<img src="https://www.itechgroup.info/web/image/product.product/10/image_1024/Pdf%20Print%20Preview%20%2817.0%29?unique=64031cc&utm_source=odoo-apps&utm_medium=link&utm_campaign=pdf-preview-17" style="display: none;"/>
</div>
<center>
<img src="youtube.gif">
</center>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced text-center">
<div class="oe_span12">
<h2 class="oe_slogan">Technical Help & Support</h2>
<h2 class="oe_slogan">Technical Help &amp; Support</h2>
</div>
<div class="col-md-12 pad0">
<div class="oe_mt16">
<p><h4>For any type of technical help & support requests, Feel free to contact us</h4></p>
<p><h4><span class="fa fa-language fa-spin"></span> <b>For custom translate please contact us</b></h4></p>
<a class="btn btn-warning btn-lg" rel="nofollow" href="mailto:support@itechgroup.info"><span
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
<i class="fa fa-envelope" style="padding: 3px"></i> support@itechgroup.info
</a>
<a class="btn btn-warning btn-lg" rel="nofollow" href="mailto:contact@itechgroup.info"><span
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
<i class="fa fa-envelope" style="padding: 3px"></i> contact@itechgroup.info
<p><h4>For any type of technical help &amp; support requests, feel free to contact us</h4></p>
<a class="btn btn-warning btn-lg" rel="nofollow" href="mailto:support@nexasystemsinc.com">
<i class="fa fa-envelope" style="padding: 3px"></i> support&#64;nexasystemsinc.com
</a>
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,12 +1,13 @@
<section>
<div class="row justify-content-center pt-4">
<div class="col-12 text-center">
<h4>Boost Efficiency with Odoo's Pdf Print Preview Module</h4>
<h4>Fusion PDF Preview for Odoo</h4>
<p class="text-muted">by Nexa Systems Inc</p>
</div>
<div class="col align-self-center">
<p>
Streamline your Odoo workflow and save time with the Pdf Print Preview
module. This powerful tool allows you to conveniently preview reports
Streamline your Odoo workflow and save time with Fusion PDF Preview.
This powerful module allows you to conveniently preview reports
directly within your browser, eliminating the need for constant
downloads.
</p>
@@ -209,7 +210,7 @@
No matter your role in Odoo, a convenient shortcut allows you to
personalize your report preview. This means everyone can quickly
adjust how they see reports, ensuring they have the information they
need in the clearest format. this handy feature empowers you to take
need in the clearest format. This handy feature empowers you to take
control and optimize your Odoo experience.
</div>
<div class="col-sm-8">
@@ -231,7 +232,7 @@
<section class="oe_container pb-2">
<div class="row">
<div class="col-sm-4 pt-sm-4 pb-sm-4 pl-sm-1 pr-sm-5 pl-5 pr-5">
<h5>Open PDF report in other tab & print</h5>
<h5>Open PDF report in other tab &amp; print</h5>
This means the report wouldn't download to your computer, but instead,
it would open within another browser tab, separate from the original
webpage where you found the link or button.
@@ -274,7 +275,7 @@
opacity: 0.3;
"
></span>
<b class="ps-2 pe-2">Demo test</b>
<b class="ps-2 pe-2">Demo</b>
<span
class="d-none d-sm-inline-block"
style="
@@ -330,11 +331,9 @@
<div class="row">
<div class="col-sm-4 pt-sm-4 pb-sm-4 pl-sm-1 pr-sm-5 pl-5 pr-5">
<h5>Full Screen</h5>
The <b>Full Screen</b> option in our module allows you to maximize the
The Full Screen option allows you to maximize the
document view for improved readability, focus, and clarity when
reviewing documents. This is a convenient feature that enhances the
user experience and facilitates better interaction with document
content.
reviewing documents.
</div>
<div class="col-sm-8">
<div class="oe_row_img oe_centered">
@@ -356,11 +355,9 @@
<div class="row">
<div class="col-sm-4 pt-sm-4 pb-sm-4 pl-sm-1 pr-sm-5 pl-5 pr-5">
<h5>Print report</h5>
<b>Print report</b> within document previews allow users to see how a
Print report within document previews allows users to see how a
document will appear when printed, facilitating verification of
layout, formatting, and page breaks before sending it to the printer.
This helps save time, reduce paper waste, and ensure a more
professional-looking final document.
</div>
<div class="col-sm-8">
<div class="oe_row_img oe_centered">
@@ -397,75 +394,36 @@
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<section class="oe_container pb-2">
<div class="oe_row oe_spaced text-center">
<div class="oe_span12">
<div class="oe_row oe_spaced text-center">
<a
class="btn btn-warning btn-lg"
href="https://youtu.be/aPYgzADUDKs"
target="_blank"
><i class="fa fa-youtube" style="padding: 3px"></i>Watch demo on
YouTube!
</a>
<img
src="https://tech58.odoo.com/r/uFs"
style="display: none"
src="assets/youtube.gif" class="oe_screenshot"
/>
</div>
</div>
</div>
<div class="text-center pb-2">
<img src="assets/youtube.gif" class="oe_screenshot "/>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced text-center">
<div class="oe_span12">
<h2 class="oe_slogan">Technical Help & Support</h2>
<h2 class="oe_slogan">Technical Help &amp; Support</h2>
</div>
<div class="col-md-12 pad0">
<div class="oe_mt16">
<h4>
For any type of technical help & support requests, Feel free to
For any type of technical help &amp; support requests, feel free to
contact us
</h4>
<h4>
<span class="fa fa-language fa-spin"></span>
<b>For custom translate please contact us</b>
</h4>
<a
class="btn btn-warning btn-lg"
rel="nofollow"
href="mailto:support@itechgroup.info"
><span
style="
height: 354px;
width: 354px;
top: -147.433px;
left: -6.93335px;
"
class="o_ripple"
></span>
href="mailto:support@nexasystemsinc.com"
>
<i class="fa fa-envelope" style="padding: 3px"></i>
support&#64;itechgroup.info
</a>
<a
class="btn btn-warning btn-lg"
rel="nofollow"
href="mailto:contact@itechgroup.info"
><span
style="
height: 354px;
width: 354px;
top: -147.433px;
left: -6.93335px;
"
class="o_ripple"
></span>
<i class="fa fa-envelope" style="padding: 3px"></i>
contact&#64;itechgroup.info
support&#64;nexasystemsinc.com
</a>
</div>
</div>

View File

@@ -5,9 +5,27 @@ import { _t } from "@web/core/l10n/translation";
import { session } from "@web/session";
import { rpc } from "@web/core/network/rpc";
import { user } from "@web/core/user";
import { Component, useState, onMounted } from "@odoo/owl";
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
/**
* Fire-and-forget audit log for PDF preview/print/download actions.
*/
async function logPreviewAction(reportName, actionType, recordIds, modelName) {
try {
await rpc("/fusion_pdf_preview/log_action", {
report_name: reportName,
action_type: actionType,
record_ids: recordIds || '',
model_name: modelName || '',
});
} catch (err) {
console.warn("Failed to log PDF preview action:", err);
}
}
export class PDFViewerDialog extends Component {
setup() {
this.state = useState({
@@ -15,10 +33,20 @@ export class PDFViewerDialog extends Component {
viewerUrl: this.getViewerUrl(),
isMaximized: false
});
this._onKeyDown = this._onKeyDown.bind(this);
onMounted(() => {
document.addEventListener('keydown', this._onKeyDown);
});
onWillUnmount(() => {
document.removeEventListener('keydown', this._onKeyDown);
});
}
getViewerUrl() {
const baseUrl = '/pdf_print_preview/static/lib/pdfjs/web/viewer.html';
const baseUrl = '/fusion_pdf_preview/static/lib/pdfjs/web/viewer.html';
return `${baseUrl}?file=${this.props.url}`;
}
@@ -43,20 +71,83 @@ export class PDFViewerDialog extends Component {
}
return 'height: calc(90vh - 100px) !important;';
}
downloadPDF() {
const link = document.createElement('a');
link.href = this.props.url;
link.download = this.props.title ? `${this.props.title}.pdf` : 'document.pdf';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
if (this.props.reportName) {
logPreviewAction(this.props.reportName, 'download',
this.props.recordIds, this.props.modelName);
}
}
openInNewTab() {
window.open(this.props.url, '_blank');
}
printPDF() {
try {
const iframe = this.el?.querySelector('iframe');
if (iframe && iframe.contentWindow) {
iframe.contentWindow.print();
}
} catch (err) {
window.open(this.props.url, '_blank');
}
if (this.props.reportName) {
logPreviewAction(this.props.reportName, 'print',
this.props.recordIds, this.props.modelName);
}
}
_onKeyDown(ev) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const ctrlOrCmd = isMac ? ev.metaKey : ev.ctrlKey;
// F key (no modifiers) - toggle fullscreen
if ((ev.key === 'f' || ev.key === 'F') && !ctrlOrCmd && !ev.altKey && !ev.shiftKey) {
const activeEl = document.activeElement;
if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable)) {
return;
}
ev.preventDefault();
this.toggle();
}
// Ctrl+P / Cmd+P - print
if (ctrlOrCmd && (ev.key === 'p' || ev.key === 'P')) {
ev.preventDefault();
this.printPDF();
}
// Ctrl+D / Cmd+D - download
if (ctrlOrCmd && (ev.key === 'd' || ev.key === 'D')) {
ev.preventDefault();
this.downloadPDF();
}
}
}
PDFViewerDialog.template = 'pdf_print_preview.PDFViewerDialog';
PDFViewerDialog.template = 'fusion_pdf_preview.PDFViewerDialog';
PDFViewerDialog.components = { Dialog };
// Register for use in actions
registry.category("dialog").add("PDFViewerDialog", PDFViewerDialog);
export function openPDFViewer(env, url, title = "PDF Document") {
export function openPDFViewer(env, url, title = "PDF Document", meta = {}) {
const dialog = env.services.dialog;
return dialog.add(PDFViewerDialog, {
url: url,
title: title
title: title,
reportName: meta.reportName || '',
recordIds: meta.recordIds || '',
modelName: meta.modelName || '',
});
}
@@ -70,7 +161,7 @@ function handleAutomaticPrinting(url, env) {
const printFrame = document.createElement('iframe');
printFrame.style.display = 'none';
printFrame.src = url;
printFrame.onload = function() {
try {
printFrame.contentWindow.print();
@@ -88,12 +179,14 @@ function handleAutomaticPrinting(url, env) {
};
const cleanup = () => {
document.body.removeChild(printFrame);
if (printFrame.parentNode) {
document.body.removeChild(printFrame);
}
window.removeEventListener('focus', cleanup);
};
window.addEventListener('focus', cleanup);
document.body.appendChild(printFrame);
}
@@ -103,15 +196,17 @@ function handleAutomaticPrinting(url, env) {
*
* @private
* @param {ReportAction} action
* @param {env} env
* @param {Object} env
* @param {string} filename
* @returns {string}
*/
function _getReportUrl(action, env, filename) {
let url = `/report/pdf/${action.report_name}`;
const actionContext = action.context || {};
filename = filename || action.name;
if(filename !== undefined)
if (filename !== undefined) {
filename = filename.replace(/[/?%#&=]/g, "_") + ".pdf";
}
if (action.data && JSON.stringify(action.data) !== "{}") {
const options = encodeURIComponent(JSON.stringify(action.data));
const context = encodeURIComponent(JSON.stringify(actionContext));
@@ -125,7 +220,7 @@ function _getReportUrl(action, env, filename) {
return url;
}
async function PdfPrintPreview(action, options, env) {
async function FusionPdfPreview(action, options, env) {
const link = '<br><br><a href="http://wkhtmltopdf.org/" target="_blank">wkhtmltopdf.org</a>';
const WKHTMLTOPDF_MESSAGES = {
broken:
@@ -149,15 +244,17 @@ async function PdfPrintPreview(action, options, env) {
),
};
if (action.report_type === "qweb-pdf" && env.services.menu.getCurrentApp() !== undefined && (session.preview_print || session.automatic_printing)) {
let getReportResult = rpc("/pdf_print_preview/get_report_name", {
if (action.report_type === "qweb-pdf" && env.services.menu.getCurrentApp() !== undefined) {
const userWantsPreview = session.preview_print;
const userWantsAutoPrint = session.automatic_printing;
const result = await rpc("/fusion_pdf_preview/get_report_name", {
report_name: action.report_name,
data: JSON.stringify(action.context)
});
const result = await getReportResult;
const state = result["wkhtmltopdf_state"];
const previewMode = result["fusion_preview_mode"] || "default";
// display a notification according to wkhtmltopdf's state
if (state in WKHTMLTOPDF_MESSAGES) {
env.services.notification.add(WKHTMLTOPDF_MESSAGES[state], {
sticky: true,
@@ -166,14 +263,40 @@ async function PdfPrintPreview(action, options, env) {
}
if (state === "upgrade" || state === "ok") {
let url = _getReportUrl(action, env, result["file_name"]);
if(session.preview_print) {
// PreviewDialog.createPreviewDialog(self, url, action.name);
openPDFViewer(env, url, action.name);
// Determine effective behavior based on report-level override
let shouldPreview = userWantsPreview;
let shouldAutoPrint = userWantsAutoPrint;
if (previewMode === 'preview') {
shouldPreview = true;
shouldAutoPrint = false;
} else if (previewMode === 'download') {
return false;
} else if (previewMode === 'auto_print') {
shouldPreview = false;
shouldAutoPrint = true;
}
if (session.automatic_printing) {
if (!shouldPreview && !shouldAutoPrint) {
return false;
}
const url = _getReportUrl(action, env, result["file_name"]);
const actionContext = action.context || {};
const meta = {
reportName: action.report_name,
recordIds: (actionContext.active_ids || []).join(','),
modelName: actionContext.active_model || '',
};
if (shouldPreview) {
openPDFViewer(env, url, action.name, meta);
logPreviewAction(meta.reportName, 'preview', meta.recordIds, meta.modelName);
}
if (shouldAutoPrint) {
handleAutomaticPrinting(url, env);
logPreviewAction(meta.reportName, 'print', meta.recordIds, meta.modelName);
}
return true;
}
@@ -182,4 +305,4 @@ async function PdfPrintPreview(action, options, env) {
registry
.category("ir.actions.report handlers")
.add("pdf_print_preview", PdfPrintPreview);
.add("fusion_pdf_preview", FusionPdfPreview);

View File

@@ -13,7 +13,7 @@ function reportPreviewConfigItem(env) {
description: _t("Report preview"),
callback: async function () {
const actionDescription = await rpc("/web/action/load", {
action_id: "pdf_print_preview.action_short_preview_print"
action_id: "fusion_pdf_preview.action_short_preview_print"
});
actionDescription.res_id = user.userId;
env.services.action.doAction(actionDescription);

View File

@@ -1,15 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="pdf_print_preview.PDFViewerDialog">
<t t-name="fusion_pdf_preview.PDFViewerDialog">
<Dialog size="getDialogSize()" footer="false">
<t t-set-slot="header">
<div class="d-flex align-items-center justify-content-between flex w-100">
<div style="width: 90px"></div>
<div style="width: 135px"></div>
<h4 class="modal-title text-break fw-normal">
<b><t t-esc="props.title" /></b>
</h4>
<div class="d-flex align-items-center" style="width: 45px">
<button type="button" class="btn" t-on-click="toggle">
<div class="d-flex align-items-center" style="width: 135px; justify-content: flex-end;">
<button type="button" class="btn" t-on-click="downloadPDF" title="Download (Ctrl+D)">
<i class="fa fa-download"/>
</button>
<button type="button" class="btn" t-on-click="openInNewTab" title="Open in new tab">
<i class="fa fa-external-link"/>
</button>
<button type="button" class="btn" t-on-click="toggle" title="Toggle fullscreen (F)">
<i t-if="!state.isMaximized" class="fa fa-square-o" />
<i t-else="" class="fa fa-clone" />
</button>
@@ -17,17 +23,17 @@
</div>
</div>
</t>
<div class="o_pdf_viewer position-relative">
<!-- Loading spinner -->
<div t-if="state.isLoading"
class="position-absolute w-100 h-100 d-flex justify-content-center align-items-center"
<div t-if="state.isLoading"
class="position-absolute w-100 h-100 d-flex justify-content-center align-items-center"
style="z-index: 1;">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<!-- PDF.js viewer iframe -->
<iframe t-att-src="state.viewerUrl"
class="w-100 border-0"