diff --git a/README.md b/README.md index 9e9ef55..5d16fe3 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,63 @@ -# Web Direct Print Module for Odoo 18 - -This module enables direct printing of reports to local printers connected to the user's computer without downloading PDF files. It uses browser printing capabilities for a seamless printing experience. - -## Features - -- Direct print from web interface to local printer -- Integration with existing report actions -- Browser-based printing without downloads -- Support for all standard Odoo reports -- Direct print button in report dialogs - -## Installation - -1. Place the `web_direct_print` folder in your Odoo addons directory -2. Update your Odoo configuration to include this directory in `addons_path` -3. Restart your Odoo server -4. Install the module from Apps menu (search for "Web Direct Print") - -## Usage - -Once installed, the module works automatically with existing reports: - -1. When viewing a document (Invoice, Sale Order, Purchase Order, etc.), click the print button -2. Instead of downloading a PDF, you'll see options to print directly -3. Select "Direct Print" to send the report directly to your default printer -4. The browser's print dialog will appear, allowing you to select your printer and print settings - -## Technical Details - -The module works by: - -1. Intercepting report generation requests -2. Converting reports to PDF in the backend -3. Sending the PDF data to the browser as a blob -4. Using JavaScript to create a temporary iframe with the PDF -5. Calling the browser's print function on the iframe - -## Browser Compatibility - -This module relies on browser printing capabilities, which are available in all modern browsers (Chrome, Firefox, Safari, Edge). For best results, use the latest version of your preferred browser. - -## Troubleshooting - -### Browser Settings -Make sure your browser allows popups from your Odoo instance, as some browsers may block the print dialog. - -### Printer Access -The module sends print jobs to the browser's default printer. Users can change printer settings in the browser's print dialog. - -### Security Restrictions -Some browsers may have security restrictions that prevent direct printing. If direct print doesn't work, the module will fall back to opening the report in a new tab where users can manually print using Ctrl+P. - -## Limitations - -- Requires user to have a local printer configured -- Browser-dependent functionality -- May not work in all network configurations -- Users need to confirm print dialog (cannot print silently) - -## Security - +# Web Direct Print Module for Odoo 18 + +This module enables direct printing of reports to local printers connected to the user's computer without downloading PDF files. It uses browser printing capabilities for a seamless printing experience. + +## Features + +- Direct print from web interface to local printer +- Integration with existing report actions +- Browser-based printing without downloads +- Support for all standard Odoo reports +- Direct print button in report dialogs + +## Installation + +1. Place the `web_direct_print` folder in your Odoo addons directory +2. Update your Odoo configuration to include this directory in `addons_path` +3. Restart your Odoo server +4. Install the module from Apps menu (search for "Web Direct Print") + +## Usage + +Once installed, the module works automatically with existing reports: + +1. When viewing a document (Invoice, Sale Order, Purchase Order, etc.), click the print button +2. Instead of downloading a PDF, you'll see options to print directly +3. Select "Direct Print" to send the report directly to your default printer +4. The browser's print dialog will appear, allowing you to select your printer and print settings + +## Technical Details + +The module works by: + +1. Intercepting report generation requests +2. Converting reports to PDF in the backend +3. Sending the PDF data to the browser as a blob +4. Using JavaScript to create a temporary iframe with the PDF +5. Calling the browser's print function on the iframe + +## Browser Compatibility + +This module relies on browser printing capabilities, which are available in all modern browsers (Chrome, Firefox, Safari, Edge). For best results, use the latest version of your preferred browser. + +## Troubleshooting + +### Browser Settings +Make sure your browser allows popups from your Odoo instance, as some browsers may block the print dialog. + +### Printer Access +The module sends print jobs to the browser's default printer. Users can change printer settings in the browser's print dialog. + +### Security Restrictions +Some browsers may have security restrictions that prevent direct printing. If direct print doesn't work, the module will fall back to opening the report in a new tab where users can manually print using Ctrl+P. + +## Limitations + +- Requires user to have a local printer configured +- Browser-dependent functionality +- May not work in all network configurations +- Users need to confirm print dialog (cannot print silently) + +## Security + The module follows Odoo's security model and only allows users to print documents they have access to. \ No newline at end of file diff --git a/__init__.py b/__init__.py index 38718f0..af57e79 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -from . import models +from . import models from . import controllers \ No newline at end of file diff --git a/__manifest__.py b/__manifest__.py index 83fea89..5167361 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,58 +1,58 @@ -{ - 'name': 'Web Direct Print', - 'version': '18.0.1.0.0', - 'category': 'Extra Tools', - 'summary': 'Enable direct printing from web browser to local printers', - 'description': """ - This module enables direct printing of reports to local printers - connected to the user's computer without downloading PDF files. - Uses browser printing capabilities for seamless printing experience. - - Features: - • Sales: Direct print quotations and sales orders - • Purchase: Direct print purchase orders and vendor bills - • Inventory: Direct print delivery slips, picking operations, internal transfers, receipts - • Accounting: Direct print customer invoices, vendor bills, payment receipts, account statements - • Stock Management: Direct print stock moves, inventory valuation, package contents - - Supported Reports: - - Sales Orders & Quotations - - Purchase Orders & RFQs - - Customer Invoices & Credit Notes - - Vendor Bills & Credit Notes - - Delivery Orders & Picking Lists - - Internal Transfers & Receipts - - Payment Receipts & Statements - - Stock Moves & Inventory Reports - - Package & Lot Tracking Labels - """, - 'author': 'Suherdy Yacob', - 'depends': [ - 'base', - 'web', - 'account', - 'sale', - 'purchase', - 'stock', - ], - 'data': [ - 'data/ir_actions_server.xml', - 'views/direct_print_templates.xml', - 'views/sale_order_views.xml', - 'views/sale_order_form_views.xml', - 'views/purchase_order_views.xml', - 'views/purchase_order_form_views.xml', - 'views/stock_picking_views.xml', - 'views/stock_picking_form_views.xml', - 'views/account_move_views.xml', - ], - 'assets': { - 'web.assets_backend': [ - 'web_direct_print/static/src/js/direct_print.js', - 'web_direct_print/static/src/xml/direct_print.xml', - ], - }, - 'installable': True, - 'auto_install': False, - 'license': 'LGPL-3', +{ + 'name': 'Web Direct Print', + 'version': '18.0.1.0.0', + 'category': 'Extra Tools', + 'summary': 'Enable direct printing from web browser to local printers', + 'description': """ + This module enables direct printing of reports to local printers + connected to the user's computer without downloading PDF files. + Uses browser printing capabilities for seamless printing experience. + + Features: + • Sales: Direct print quotations and sales orders + • Purchase: Direct print purchase orders and vendor bills + • Inventory: Direct print delivery slips, picking operations, internal transfers, receipts + • Accounting: Direct print customer invoices, vendor bills, payment receipts, account statements + • Stock Management: Direct print stock moves, inventory valuation, package contents + + Supported Reports: + - Sales Orders & Quotations + - Purchase Orders & RFQs + - Customer Invoices & Credit Notes + - Vendor Bills & Credit Notes + - Delivery Orders & Picking Lists + - Internal Transfers & Receipts + - Payment Receipts & Statements + - Stock Moves & Inventory Reports + - Package & Lot Tracking Labels + """, + 'author': 'Suherdy Yacob', + 'depends': [ + 'base', + 'web', + 'account', + 'sale', + 'purchase', + 'stock', + ], + 'data': [ + 'data/ir_actions_server.xml', + 'views/direct_print_templates.xml', + 'views/sale_order_views.xml', + 'views/sale_order_form_views.xml', + 'views/purchase_order_views.xml', + 'views/purchase_order_form_views.xml', + 'views/stock_picking_views.xml', + 'views/stock_picking_form_views.xml', + 'views/account_move_views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'web_direct_print/static/src/js/direct_print.js', + 'web_direct_print/static/src/xml/direct_print.xml', + ], + }, + 'installable': True, + 'auto_install': False, + 'license': 'LGPL-3', } \ No newline at end of file diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e1fb1db Binary files /dev/null and b/__pycache__/__init__.cpython-310.pyc differ diff --git a/controllers/__pycache__/__init__.cpython-310.pyc b/controllers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..04a9dea Binary files /dev/null and b/controllers/__pycache__/__init__.cpython-310.pyc differ diff --git a/controllers/__pycache__/main.cpython-310.pyc b/controllers/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000..551114e Binary files /dev/null and b/controllers/__pycache__/main.cpython-310.pyc differ diff --git a/controllers/main.py b/controllers/main.py index 668804b..01b6140 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -1,90 +1,90 @@ -from odoo import http -from odoo.http import request -import json -import base64 - - -class DirectPrintController(http.Controller): - - @http.route('/web/direct_print', type='json', auth='user') - def direct_print(self, report_name, docids, data=None): - """ - Controller method to handle direct print requests - :param report_name: Name of the report to print - :param docids: IDs of documents to print - :param data: Additional data for the report - :return: JSON response with print data - """ - try: - import logging - _logger = logging.getLogger(__name__) - _logger.info(f"Controller received: report_name={report_name} (type: {type(report_name)}), docids={docids} (type: {type(docids)}), data={data}") - - # Handle parameters that might come as different types - if isinstance(report_name, list): - report_name = report_name[0] if report_name else '' - - if isinstance(docids, str): - try: - # Try to convert string to list of integers - if ',' in docids: - docids = [int(x.strip()) for x in docids.split(',') if x.strip()] - else: - docids = [int(docids)] - except ValueError: - docids = [] - elif not isinstance(docids, list): - docids = [docids] if docids else [] - - _logger.info(f"Processed parameters: report_name={report_name}, docids={docids}") - - # Call the direct print model method with proper context - result = request.env['web.direct.print'].sudo().direct_print_action( - report_name, docids, data, context=request.context - ) - return result - except Exception as e: - import logging - _logger = logging.getLogger(__name__) - _logger.error(f"Controller error: {str(e)}") - _logger.exception("Full traceback:") - return { - 'success': False, - 'error': str(e) - } - - @http.route('/web/direct_print/get_reports', type='json', auth='user') - def get_available_reports(self): - """ - Controller method to get available reports for direct printing - :return: JSON response with available reports - """ - try: - result = request.env['web.direct.print'].sudo().get_available_reports() - return result - except Exception as e: - return { - 'success': False, - 'error': str(e) - } - - @http.route('/web/direct_print/test', type='http', auth='user', website=True) - def test_direct_print(self, **kwargs): - """ - Test endpoint for direct printing functionality - """ - # This could be used for testing purposes - html_content = """ - - - - Direct Print Test - - -

Direct Print Test Page

-

This page demonstrates the direct print functionality.

- - - - """ +from odoo import http +from odoo.http import request +import json +import base64 + + +class DirectPrintController(http.Controller): + + @http.route('/web/direct_print', type='json', auth='user') + def direct_print(self, report_name, docids, data=None): + """ + Controller method to handle direct print requests + :param report_name: Name of the report to print + :param docids: IDs of documents to print + :param data: Additional data for the report + :return: JSON response with print data + """ + try: + import logging + _logger = logging.getLogger(__name__) + _logger.info(f"Controller received: report_name={report_name} (type: {type(report_name)}), docids={docids} (type: {type(docids)}), data={data}") + + # Handle parameters that might come as different types + if isinstance(report_name, list): + report_name = report_name[0] if report_name else '' + + if isinstance(docids, str): + try: + # Try to convert string to list of integers + if ',' in docids: + docids = [int(x.strip()) for x in docids.split(',') if x.strip()] + else: + docids = [int(docids)] + except ValueError: + docids = [] + elif not isinstance(docids, list): + docids = [docids] if docids else [] + + _logger.info(f"Processed parameters: report_name={report_name}, docids={docids}") + + # Call the direct print model method with proper context + result = request.env['web.direct.print'].sudo().direct_print_action( + report_name, docids, data, context=request.context + ) + return result + except Exception as e: + import logging + _logger = logging.getLogger(__name__) + _logger.error(f"Controller error: {str(e)}") + _logger.exception("Full traceback:") + return { + 'success': False, + 'error': str(e) + } + + @http.route('/web/direct_print/get_reports', type='json', auth='user') + def get_available_reports(self): + """ + Controller method to get available reports for direct printing + :return: JSON response with available reports + """ + try: + result = request.env['web.direct.print'].sudo().get_available_reports() + return result + except Exception as e: + return { + 'success': False, + 'error': str(e) + } + + @http.route('/web/direct_print/test', type='http', auth='user', website=True) + def test_direct_print(self, **kwargs): + """ + Test endpoint for direct printing functionality + """ + # This could be used for testing purposes + html_content = """ + + + + Direct Print Test + + +

Direct Print Test Page

+

This page demonstrates the direct print functionality.

+ + + + """ return html_content \ No newline at end of file diff --git a/data/ir_actions_server.xml b/data/ir_actions_server.xml index 756d11a..eb37416 100644 --- a/data/ir_actions_server.xml +++ b/data/ir_actions_server.xml @@ -1,16 +1,16 @@ - - - - - - Direct Print - - code - -if object: - action = env['web.direct.print'].direct_print_action(object.report_name, env.context.get('active_ids', []), context=env.context) - - - - + + + + + + Direct Print + + code + +if object: + action = env['web.direct.print'].direct_print_action(object.report_name, env.context.get('active_ids', []), context=env.context) + + + + \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py index bd0fadf..d50d46a 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,4 +1,4 @@ -from . import direct_print -from . import stock_picking -from . import sale_order +from . import direct_print +from . import stock_picking +from . import sale_order from . import purchase_order \ No newline at end of file diff --git a/models/__pycache__/__init__.cpython-310.pyc b/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7d75823 Binary files /dev/null and b/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/models/__pycache__/direct_print.cpython-310.pyc b/models/__pycache__/direct_print.cpython-310.pyc new file mode 100644 index 0000000..09955f4 Binary files /dev/null and b/models/__pycache__/direct_print.cpython-310.pyc differ diff --git a/models/__pycache__/purchase_order.cpython-310.pyc b/models/__pycache__/purchase_order.cpython-310.pyc new file mode 100644 index 0000000..6961c43 Binary files /dev/null and b/models/__pycache__/purchase_order.cpython-310.pyc differ diff --git a/models/__pycache__/sale_order.cpython-310.pyc b/models/__pycache__/sale_order.cpython-310.pyc new file mode 100644 index 0000000..f01ca14 Binary files /dev/null and b/models/__pycache__/sale_order.cpython-310.pyc differ diff --git a/models/__pycache__/stock_picking.cpython-310.pyc b/models/__pycache__/stock_picking.cpython-310.pyc new file mode 100644 index 0000000..e7d19d9 Binary files /dev/null and b/models/__pycache__/stock_picking.cpython-310.pyc differ diff --git a/models/direct_print.py b/models/direct_print.py index 0175913..6116a58 100644 --- a/models/direct_print.py +++ b/models/direct_print.py @@ -1,212 +1,212 @@ -from odoo import models, fields, api -import base64 -import logging - -_logger = logging.getLogger(__name__) - - -class DirectPrint(models.Model): - _name = 'web.direct.print' - _description = 'Web Direct Print' - - @api.model - def get_report_data(self, report_name, docids, data=None): - """ - Generate report data for direct printing - :param report_name: Name of the report - :param docids: Document IDs to print - :param data: Additional report data - :return: Report content in base64 - """ - try: - _logger.info(f"get_report_data called with report_name={report_name} (type: {type(report_name)}), docids={docids} (type: {type(docids)})") - - # Handle report_name parameter - it could be a string, list, or ID - if isinstance(report_name, list): - if report_name and isinstance(report_name[0], int): - # It's a list of IDs, get the report by ID - report = self.env['ir.actions.report'].browse(report_name[0]) - else: - # It's a list with string names - report_name_str = report_name[0] if report_name else '' - report = self.env['ir.actions.report'].search([('report_name', '=', report_name_str)], limit=1) - elif isinstance(report_name, int): - # It's an ID - report = self.env['ir.actions.report'].browse(report_name) - elif isinstance(report_name, str): - # It's a string name - try to find by report_name or use env.ref - if '.' in report_name: - # It looks like a module.xml_id format, try env.ref first - try: - ref_obj = self.env.ref(report_name) - # Check if the referenced object is actually a report - if hasattr(ref_obj, '_name') and ref_obj._name == 'ir.actions.report': - report = ref_obj - else: - # It's not a report (probably a view), search by report_name instead - report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) - except ValueError: - # If env.ref fails, try searching by report_name - report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) - else: - # Search by report_name - report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) - else: - report = None - - # Generate the report content - if report: - # Generate the pdf content with proper context - current_context = self.env.context.copy() - - # Handle docids parameter - ensure it's a list of integers - if isinstance(docids, str): - # If it's a string, try to convert to int - try: - docids = [int(docids)] - except ValueError: - # If it contains commas, split and convert - if ',' in docids: - docids = [int(d.strip()) for d in docids.split(',') if d.strip()] - else: - docids = [int(docids)] - elif isinstance(docids, int): - docids = [docids] - elif isinstance(docids, list): - # Ensure all elements are integers - processed_ids = [] - for d in docids: - if isinstance(d, str): - try: - processed_ids.append(int(d)) - except ValueError: - continue - elif isinstance(d, int): - processed_ids.append(d) - docids = processed_ids - else: - docids = [] - - _logger.info(f"Processed docids: {docids}") - - if not docids: - return { - 'success': False, - 'error': 'No valid document IDs provided' - } - - # Use the standard Odoo report rendering approach - # Ensure report_name is a string, not a list - report_name_str = str(report.report_name) if report.report_name else report.name - _logger.info(f"Using report_name_str: {report_name_str} (type: {type(report_name_str)})") - - # Generate PDF using the standard method - pdf_content, report_format = self.env['ir.actions.report'].with_context(**current_context)._render_qweb_pdf(report_name_str, docids, data or {}) - - # Encode the content in base64 for transmission - encoded_content = base64.b64encode(pdf_content).decode('utf-8') - - return { - 'success': True, - 'content': encoded_content, - 'report_name': report_name, - 'docids': docids, - 'content_type': 'application/pdf' - } - else: - return { - 'success': False, - 'error': f'Report "{report_name}" not found' - } - except Exception as e: - _logger.error(f"Error generating report for direct print: {str(e)}") - _logger.exception("Full traceback:") - return { - 'success': False, - 'error': str(e) - } - - @api.model - def prepare_print_data(self, report_name, docids, data=None): - """ - Prepare data for direct printing - :param report_name: Name of the report to print - :param docids: IDs of documents to print - :param data: Additional data for the report - :return: Dictionary with report data - """ - try: - result = self.get_report_data(report_name, docids, data) - return result - except Exception as e: - _logger.error(f"Error preparing print data: {str(e)}") - return { - 'success': False, - 'error': str(e) - } - - @api.model - def get_available_reports(self): - """ - Get list of available reports for direct printing - :return: List of available reports - """ - try: - reports = self.env['ir.actions.report'].search([ - ('active', '=', True), - ('report_type', 'in', ['qweb-pdf', 'qweb-html']) - ]) - - report_list = [] - for report in reports: - report_list.append({ - 'id': report.id, - 'name': report.name, - 'report_name': report.report_name, - 'model': report.model, - }) - - return { - 'success': True, - 'reports': report_list - } - except Exception as e: - _logger.error(f"Error getting available reports: {str(e)}") - return { - 'success': False, - 'error': str(e) - } - - @api.model - def direct_print_action(self, report_name, docids, data=None, context=None): - """ - Execute direct print action for a given report - :param report_name: Name of the report to print - :param docids: IDs of documents to print - :param data: Additional data for the report - :param context: Context data - :return: Result of the print action - """ - try: - # Update the environment context if provided - if context: - self = self.with_context(**context) - - # Prepare the print data - print_data = self.prepare_print_data(report_name, docids, data) - - if print_data['success']: - # In a real implementation, we might want to add additional - # processing here, such as tracking print jobs or handling - # special printer configurations - - # Return the print data for client-side processing - return print_data - else: - return print_data - except Exception as e: - _logger.error(f"Error in direct print action: {str(e)}") - return { - 'success': False, - 'error': str(e) +from odoo import models, fields, api +import base64 +import logging + +_logger = logging.getLogger(__name__) + + +class DirectPrint(models.Model): + _name = 'web.direct.print' + _description = 'Web Direct Print' + + @api.model + def get_report_data(self, report_name, docids, data=None): + """ + Generate report data for direct printing + :param report_name: Name of the report + :param docids: Document IDs to print + :param data: Additional report data + :return: Report content in base64 + """ + try: + _logger.info(f"get_report_data called with report_name={report_name} (type: {type(report_name)}), docids={docids} (type: {type(docids)})") + + # Handle report_name parameter - it could be a string, list, or ID + if isinstance(report_name, list): + if report_name and isinstance(report_name[0], int): + # It's a list of IDs, get the report by ID + report = self.env['ir.actions.report'].browse(report_name[0]) + else: + # It's a list with string names + report_name_str = report_name[0] if report_name else '' + report = self.env['ir.actions.report'].search([('report_name', '=', report_name_str)], limit=1) + elif isinstance(report_name, int): + # It's an ID + report = self.env['ir.actions.report'].browse(report_name) + elif isinstance(report_name, str): + # It's a string name - try to find by report_name or use env.ref + if '.' in report_name: + # It looks like a module.xml_id format, try env.ref first + try: + ref_obj = self.env.ref(report_name) + # Check if the referenced object is actually a report + if hasattr(ref_obj, '_name') and ref_obj._name == 'ir.actions.report': + report = ref_obj + else: + # It's not a report (probably a view), search by report_name instead + report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) + except ValueError: + # If env.ref fails, try searching by report_name + report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) + else: + # Search by report_name + report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) + else: + report = None + + # Generate the report content + if report: + # Generate the pdf content with proper context + current_context = self.env.context.copy() + + # Handle docids parameter - ensure it's a list of integers + if isinstance(docids, str): + # If it's a string, try to convert to int + try: + docids = [int(docids)] + except ValueError: + # If it contains commas, split and convert + if ',' in docids: + docids = [int(d.strip()) for d in docids.split(',') if d.strip()] + else: + docids = [int(docids)] + elif isinstance(docids, int): + docids = [docids] + elif isinstance(docids, list): + # Ensure all elements are integers + processed_ids = [] + for d in docids: + if isinstance(d, str): + try: + processed_ids.append(int(d)) + except ValueError: + continue + elif isinstance(d, int): + processed_ids.append(d) + docids = processed_ids + else: + docids = [] + + _logger.info(f"Processed docids: {docids}") + + if not docids: + return { + 'success': False, + 'error': 'No valid document IDs provided' + } + + # Use the standard Odoo report rendering approach + # Ensure report_name is a string, not a list + report_name_str = str(report.report_name) if report.report_name else report.name + _logger.info(f"Using report_name_str: {report_name_str} (type: {type(report_name_str)})") + + # Generate PDF using the standard method + pdf_content, report_format = self.env['ir.actions.report'].with_context(**current_context)._render_qweb_pdf(report_name_str, docids, data or {}) + + # Encode the content in base64 for transmission + encoded_content = base64.b64encode(pdf_content).decode('utf-8') + + return { + 'success': True, + 'content': encoded_content, + 'report_name': report_name, + 'docids': docids, + 'content_type': 'application/pdf' + } + else: + return { + 'success': False, + 'error': f'Report "{report_name}" not found' + } + except Exception as e: + _logger.error(f"Error generating report for direct print: {str(e)}") + _logger.exception("Full traceback:") + return { + 'success': False, + 'error': str(e) + } + + @api.model + def prepare_print_data(self, report_name, docids, data=None): + """ + Prepare data for direct printing + :param report_name: Name of the report to print + :param docids: IDs of documents to print + :param data: Additional data for the report + :return: Dictionary with report data + """ + try: + result = self.get_report_data(report_name, docids, data) + return result + except Exception as e: + _logger.error(f"Error preparing print data: {str(e)}") + return { + 'success': False, + 'error': str(e) + } + + @api.model + def get_available_reports(self): + """ + Get list of available reports for direct printing + :return: List of available reports + """ + try: + reports = self.env['ir.actions.report'].search([ + ('active', '=', True), + ('report_type', 'in', ['qweb-pdf', 'qweb-html']) + ]) + + report_list = [] + for report in reports: + report_list.append({ + 'id': report.id, + 'name': report.name, + 'report_name': report.report_name, + 'model': report.model, + }) + + return { + 'success': True, + 'reports': report_list + } + except Exception as e: + _logger.error(f"Error getting available reports: {str(e)}") + return { + 'success': False, + 'error': str(e) + } + + @api.model + def direct_print_action(self, report_name, docids, data=None, context=None): + """ + Execute direct print action for a given report + :param report_name: Name of the report to print + :param docids: IDs of documents to print + :param data: Additional data for the report + :param context: Context data + :return: Result of the print action + """ + try: + # Update the environment context if provided + if context: + self = self.with_context(**context) + + # Prepare the print data + print_data = self.prepare_print_data(report_name, docids, data) + + if print_data['success']: + # In a real implementation, we might want to add additional + # processing here, such as tracking print jobs or handling + # special printer configurations + + # Return the print data for client-side processing + return print_data + else: + return print_data + except Exception as e: + _logger.error(f"Error in direct print action: {str(e)}") + return { + 'success': False, + 'error': str(e) } \ No newline at end of file diff --git a/models/purchase_order.py b/models/purchase_order.py index 7dc7b49..d8dde40 100644 --- a/models/purchase_order.py +++ b/models/purchase_order.py @@ -1,18 +1,18 @@ -from odoo import models, fields, api - - -class PurchaseOrder(models.Model): - _inherit = 'purchase.order' - - def action_direct_print_purchase_order(self): - """ - Direct print action for purchase orders and RFQs - """ - return { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'name': 'Direct Print Purchase Order', - 'report_name': 'purchase.report_purchaseorder', - 'docids': self.ids, - 'context': self.env.context, +from odoo import models, fields, api + + +class PurchaseOrder(models.Model): + _inherit = 'purchase.order' + + def action_direct_print_purchase_order(self): + """ + Direct print action for purchase orders and RFQs + """ + return { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'name': 'Direct Print Purchase Order', + 'report_name': 'purchase.report_purchaseorder', + 'docids': self.ids, + 'context': self.env.context, } \ No newline at end of file diff --git a/models/sale_order.py b/models/sale_order.py index 8f98cf6..b84c398 100644 --- a/models/sale_order.py +++ b/models/sale_order.py @@ -1,18 +1,18 @@ -from odoo import models, fields, api - - -class SaleOrder(models.Model): - _inherit = 'sale.order' - - def action_direct_print_quotation(self): - """ - Direct print action for sale orders and quotations - """ - return { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'name': 'Direct Print Quotation/Order', - 'report_name': 'sale.report_saleorder', - 'docids': self.ids, - 'context': self.env.context, +from odoo import models, fields, api + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + def action_direct_print_quotation(self): + """ + Direct print action for sale orders and quotations + """ + return { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'name': 'Direct Print Quotation/Order', + 'report_name': 'sale.report_saleorder', + 'docids': self.ids, + 'context': self.env.context, } \ No newline at end of file diff --git a/models/stock_picking.py b/models/stock_picking.py index febce5d..caee3f6 100644 --- a/models/stock_picking.py +++ b/models/stock_picking.py @@ -1,56 +1,56 @@ -from odoo import models, fields, api -import json - - -class StockPicking(models.Model): - _inherit = 'stock.picking' - - def action_direct_print_receipt(self): - """ - Direct print action for stock picking receipts - """ - # Determine the appropriate report based on picking type - if self.picking_type_id.code == 'incoming': - report_name = 'stock.action_report_delivery' - elif self.picking_type_id.code == 'outgoing': - report_name = 'stock.action_report_delivery' - elif self.picking_type_id.code == 'internal': - report_name = 'stock.action_report_picking' - else: - report_name = 'stock.action_report_picking' - - # Return client action for direct printing - return { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'name': 'Direct Print', - 'report_name': report_name, - 'docids': self.ids, - 'context': self.env.context, - } - - def action_direct_print_delivery(self): - """ - Direct print delivery slip - """ - return { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'name': 'Direct Print Delivery', - 'report_name': 'stock.action_report_delivery', - 'docids': self.ids, - 'context': self.env.context, - } - - def action_direct_print_picking_operations(self): - """ - Direct print picking operations - """ - return { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'name': 'Direct Print Operations', - 'report_name': 'stock.action_report_picking', - 'docids': self.ids, - 'context': self.env.context, +from odoo import models, fields, api +import json + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + def action_direct_print_receipt(self): + """ + Direct print action for stock picking receipts + """ + # Determine the appropriate report based on picking type + if self.picking_type_id.code == 'incoming': + report_name = 'stock.action_report_delivery' + elif self.picking_type_id.code == 'outgoing': + report_name = 'stock.action_report_delivery' + elif self.picking_type_id.code == 'internal': + report_name = 'stock.action_report_picking' + else: + report_name = 'stock.action_report_picking' + + # Return client action for direct printing + return { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'name': 'Direct Print', + 'report_name': report_name, + 'docids': self.ids, + 'context': self.env.context, + } + + def action_direct_print_delivery(self): + """ + Direct print delivery slip + """ + return { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'name': 'Direct Print Delivery', + 'report_name': 'stock.action_report_delivery', + 'docids': self.ids, + 'context': self.env.context, + } + + def action_direct_print_picking_operations(self): + """ + Direct print picking operations + """ + return { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'name': 'Direct Print Operations', + 'report_name': 'stock.action_report_picking', + 'docids': self.ids, + 'context': self.env.context, } \ No newline at end of file diff --git a/static/src/js/direct_print.js b/static/src/js/direct_print.js index 1c30e35..22ae1ea 100644 --- a/static/src/js/direct_print.js +++ b/static/src/js/direct_print.js @@ -1,216 +1,216 @@ -/** @odoo-module **/ - -import { rpc } from "@web/core/network/rpc"; -import { _t } from "@web/core/l10n/translation"; -import { registry } from "@web/core/registry"; -import { Component } from "@odoo/owl"; - -const actionRegistry = registry.category("actions"); - -// Direct Print Action Handler -class DirectPrintAction extends Component { - static template = "web_direct_print.DirectPrintAction"; - - async setup() { - // This action will handle direct print requests - const action = this.props.action; - console.log("DirectPrintAction received action:", action); - - const report_name = action.report_name || action.context?.report_name || ''; - const docids = action.docids || action.context?.active_ids || []; - const data = action.data || {}; - - console.log("Extracted params:", { report_name, docids, data }); - await this.directPrint(report_name, docids, data); - } - - async directPrint(reportName, docIds, data) { - try { - // Call the controller endpoint to get report data - const result = await rpc("/web/direct_print", { - report_name: reportName, - docids: docIds, - data: data || {} - }); - - if (result.success) { - this.printReport(result); - } else { - this.env.services.notification.add( - _t("Error: ") + (result.error || _t("Unknown error occurred")), - { type: "danger" } - ); - } - } catch (error) { - console.error("Direct print error:", error); - this.env.services.notification.add( - _t("Error occurred while preparing print: ") + (error.message || error), - { type: "danger" } - ); - } - } - - printReport(printData) { - // Create a blob from the base64 content - const binaryString = atob(printData.content); - const bytes = new Uint8Array(binaryString.length); - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - - const blob = new Blob([bytes], { type: printData.content_type }); - const url = URL.createObjectURL(blob); - - // Create a new window with HTML that embeds the PDF and has print functionality - const printWindow = window.open('', '_blank', 'width=800,height=600'); - if (!printWindow) { - // If popup is blocked, show download option - const a = document.createElement('a'); - a.href = url; - a.download = printData.report_name + '.pdf'; - a.click(); - URL.revokeObjectURL(url); - return; - } - - // Write HTML content that will embed the PDF and automatically trigger print - var htmlContent = '' + - '' + - '' + - 'Printing Document' + - '' + - '' + - '' + - '
' + - '' + - '
' + - ' - - - `); - printWindow.document.close(); - } - - // Clean up - setTimeout(() => URL.revokeObjectURL(url), 10000); - } - } catch (error) { - console.error("Direct print error:", error); - } +/** @odoo-module **/ + +import { rpc } from "@web/core/network/rpc"; +import { _t } from "@web/core/l10n/translation"; +import { registry } from "@web/core/registry"; +import { Component } from "@odoo/owl"; + +const actionRegistry = registry.category("actions"); + +// Direct Print Action Handler +class DirectPrintAction extends Component { + static template = "web_direct_print.DirectPrintAction"; + + async setup() { + // This action will handle direct print requests + const action = this.props.action; + console.log("DirectPrintAction received action:", action); + + const report_name = action.report_name || action.context?.report_name || ''; + const docids = action.docids || action.context?.active_ids || []; + const data = action.data || {}; + + console.log("Extracted params:", { report_name, docids, data }); + await this.directPrint(report_name, docids, data); + } + + async directPrint(reportName, docIds, data) { + try { + // Call the controller endpoint to get report data + const result = await rpc("/web/direct_print", { + report_name: reportName, + docids: docIds, + data: data || {} + }); + + if (result.success) { + this.printReport(result); + } else { + this.env.services.notification.add( + _t("Error: ") + (result.error || _t("Unknown error occurred")), + { type: "danger" } + ); + } + } catch (error) { + console.error("Direct print error:", error); + this.env.services.notification.add( + _t("Error occurred while preparing print: ") + (error.message || error), + { type: "danger" } + ); + } + } + + printReport(printData) { + // Create a blob from the base64 content + const binaryString = atob(printData.content); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + const blob = new Blob([bytes], { type: printData.content_type }); + const url = URL.createObjectURL(blob); + + // Create a new window with HTML that embeds the PDF and has print functionality + const printWindow = window.open('', '_blank', 'width=800,height=600'); + if (!printWindow) { + // If popup is blocked, show download option + const a = document.createElement('a'); + a.href = url; + a.download = printData.report_name + '.pdf'; + a.click(); + URL.revokeObjectURL(url); + return; + } + + // Write HTML content that will embed the PDF and automatically trigger print + var htmlContent = '' + + '' + + '' + + 'Printing Document' + + '' + + '' + + '' + + '
' + + '' + + '
' + + ' + + + `); + printWindow.document.close(); + } + + // Clean up + setTimeout(() => URL.revokeObjectURL(url), 10000); + } + } catch (error) { + console.error("Direct print error:", error); + } }; \ No newline at end of file diff --git a/static/src/xml/direct_print.xml b/static/src/xml/direct_print.xml index eff485f..69a99bf 100644 --- a/static/src/xml/direct_print.xml +++ b/static/src/xml/direct_print.xml @@ -1,12 +1,12 @@ - - - -
- -
- -
Preparing print...
-
-
-
+ + + +
+ +
+ +
Preparing print...
+
+
+
\ No newline at end of file diff --git a/views/account_move_views.xml b/views/account_move_views.xml index 0643c0d..9ccea3c 100644 --- a/views/account_move_views.xml +++ b/views/account_move_views.xml @@ -1,115 +1,115 @@ - - - - - - Direct Print Customer Invoice - - - list,form - code - -if records: - # Filter for customer invoices - invoices = records.filtered(lambda r: r.move_type in ['out_invoice', 'out_refund']) - if invoices: - action = { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'report_name': 'account.report_invoice', - 'docids': invoices.ids, - 'context': env.context, - } - - - - - - Direct Print Vendor Bill - - - list,form - code - -if records: - # Filter for vendor bills - bills = records.filtered(lambda r: r.move_type in ['in_invoice', 'in_refund']) - if bills: - action = { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'report_name': 'account.report_invoice', - 'docids': bills.ids, - 'context': env.context, - } - - - - - - Direct Print Invoice with Payments - - - list,form - code - -if records: - # Filter for customer invoices - invoices = records.filtered(lambda r: r.move_type in ['out_invoice']) - if invoices: - action = { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'report_name': 'account.report_invoice_with_payments', - 'docids': invoices.ids, - 'context': env.context, - } - - - - - - Direct Print Payment Receipt - - - list,form - code - -if records: - # Filter for payments - payments = records.filtered(lambda r: r.state == 'posted') - if payments: - action = { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'report_name': 'account.action_report_payment_receipt', - 'docids': payments.ids, - 'context': env.context, - } - - - - - - Direct Print Account Statement - - - list,form - code - -if records: - # Filter for partners with accounting entries - partners = records.filtered(lambda r: r.customer_rank > 0 or r.supplier_rank > 0) - if partners: - action = { - 'type': 'ir.actions.client', - 'tag': 'direct_print', - 'report_name': 'account.report_partnerledger', - 'docids': partners.ids, - 'context': env.context, - } - - - - + + + + + + Direct Print Customer Invoice + + + list,form + code + +if records: + # Filter for customer invoices + invoices = records.filtered(lambda r: r.move_type in ['out_invoice', 'out_refund']) + if invoices: + action = { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'report_name': 'account.report_invoice', + 'docids': invoices.ids, + 'context': env.context, + } + + + + + + Direct Print Vendor Bill + + + list,form + code + +if records: + # Filter for vendor bills + bills = records.filtered(lambda r: r.move_type in ['in_invoice', 'in_refund']) + if bills: + action = { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'report_name': 'account.report_invoice', + 'docids': bills.ids, + 'context': env.context, + } + + + + + + Direct Print Invoice with Payments + + + list,form + code + +if records: + # Filter for customer invoices + invoices = records.filtered(lambda r: r.move_type in ['out_invoice']) + if invoices: + action = { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'report_name': 'account.report_invoice_with_payments', + 'docids': invoices.ids, + 'context': env.context, + } + + + + + + Direct Print Payment Receipt + + + list,form + code + +if records: + # Filter for payments + payments = records.filtered(lambda r: r.state == 'posted') + if payments: + action = { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'report_name': 'account.action_report_payment_receipt', + 'docids': payments.ids, + 'context': env.context, + } + + + + + + Direct Print Account Statement + + + list,form + code + +if records: + # Filter for partners with accounting entries + partners = records.filtered(lambda r: r.customer_rank > 0 or r.supplier_rank > 0) + if partners: + action = { + 'type': 'ir.actions.client', + 'tag': 'direct_print', + 'report_name': 'account.report_partnerledger', + 'docids': partners.ids, + 'context': env.context, + } + + + + \ No newline at end of file diff --git a/views/assets.xml b/views/assets.xml index bc404c8..a8d032f 100644 --- a/views/assets.xml +++ b/views/assets.xml @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/views/direct_print_templates.xml b/views/direct_print_templates.xml index 7aebd43..c5c2bbd 100644 --- a/views/direct_print_templates.xml +++ b/views/direct_print_templates.xml @@ -1,10 +1,10 @@ - - - - + + + + \ No newline at end of file diff --git a/views/purchase_order_form_views.xml b/views/purchase_order_form_views.xml index 64af6e6..36f72bc 100644 --- a/views/purchase_order_form_views.xml +++ b/views/purchase_order_form_views.xml @@ -1,21 +1,21 @@ - - - - - - purchase.order.form.direct.print - purchase.order - - - -