This commit is contained in:
admin.suherdy 2025-11-27 10:11:39 +07:00
parent 7e5a51fa2d
commit e279e84501
29 changed files with 1140 additions and 1140 deletions

124
README.md
View File

@ -1,63 +1,63 @@
# Web Direct Print Module for Odoo 18 # 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. 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 ## Features
- Direct print from web interface to local printer - Direct print from web interface to local printer
- Integration with existing report actions - Integration with existing report actions
- Browser-based printing without downloads - Browser-based printing without downloads
- Support for all standard Odoo reports - Support for all standard Odoo reports
- Direct print button in report dialogs - Direct print button in report dialogs
## Installation ## Installation
1. Place the `web_direct_print` folder in your Odoo addons directory 1. Place the `web_direct_print` folder in your Odoo addons directory
2. Update your Odoo configuration to include this directory in `addons_path` 2. Update your Odoo configuration to include this directory in `addons_path`
3. Restart your Odoo server 3. Restart your Odoo server
4. Install the module from Apps menu (search for "Web Direct Print") 4. Install the module from Apps menu (search for "Web Direct Print")
## Usage ## Usage
Once installed, the module works automatically with existing reports: Once installed, the module works automatically with existing reports:
1. When viewing a document (Invoice, Sale Order, Purchase Order, etc.), click the print button 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 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 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 4. The browser's print dialog will appear, allowing you to select your printer and print settings
## Technical Details ## Technical Details
The module works by: The module works by:
1. Intercepting report generation requests 1. Intercepting report generation requests
2. Converting reports to PDF in the backend 2. Converting reports to PDF in the backend
3. Sending the PDF data to the browser as a blob 3. Sending the PDF data to the browser as a blob
4. Using JavaScript to create a temporary iframe with the PDF 4. Using JavaScript to create a temporary iframe with the PDF
5. Calling the browser's print function on the iframe 5. Calling the browser's print function on the iframe
## Browser Compatibility ## 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. 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 ## Troubleshooting
### Browser Settings ### Browser Settings
Make sure your browser allows popups from your Odoo instance, as some browsers may block the print dialog. Make sure your browser allows popups from your Odoo instance, as some browsers may block the print dialog.
### Printer Access ### Printer Access
The module sends print jobs to the browser's default printer. Users can change printer settings in the browser's print dialog. The module sends print jobs to the browser's default printer. Users can change printer settings in the browser's print dialog.
### Security Restrictions ### 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. 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 ## Limitations
- Requires user to have a local printer configured - Requires user to have a local printer configured
- Browser-dependent functionality - Browser-dependent functionality
- May not work in all network configurations - May not work in all network configurations
- Users need to confirm print dialog (cannot print silently) - Users need to confirm print dialog (cannot print silently)
## Security ## Security
The module follows Odoo's security model and only allows users to print documents they have access to. The module follows Odoo's security model and only allows users to print documents they have access to.

View File

@ -1,2 +1,2 @@
from . import models from . import models
from . import controllers from . import controllers

View File

@ -1,58 +1,58 @@
{ {
'name': 'Web Direct Print', 'name': 'Web Direct Print',
'version': '18.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Extra Tools', 'category': 'Extra Tools',
'summary': 'Enable direct printing from web browser to local printers', 'summary': 'Enable direct printing from web browser to local printers',
'description': """ 'description': """
This module enables direct printing of reports to local printers This module enables direct printing of reports to local printers
connected to the user's computer without downloading PDF files. connected to the user's computer without downloading PDF files.
Uses browser printing capabilities for seamless printing experience. Uses browser printing capabilities for seamless printing experience.
Features: Features:
Sales: Direct print quotations and sales orders Sales: Direct print quotations and sales orders
Purchase: Direct print purchase orders and vendor bills Purchase: Direct print purchase orders and vendor bills
Inventory: Direct print delivery slips, picking operations, internal transfers, receipts Inventory: Direct print delivery slips, picking operations, internal transfers, receipts
Accounting: Direct print customer invoices, vendor bills, payment receipts, account statements Accounting: Direct print customer invoices, vendor bills, payment receipts, account statements
Stock Management: Direct print stock moves, inventory valuation, package contents Stock Management: Direct print stock moves, inventory valuation, package contents
Supported Reports: Supported Reports:
- Sales Orders & Quotations - Sales Orders & Quotations
- Purchase Orders & RFQs - Purchase Orders & RFQs
- Customer Invoices & Credit Notes - Customer Invoices & Credit Notes
- Vendor Bills & Credit Notes - Vendor Bills & Credit Notes
- Delivery Orders & Picking Lists - Delivery Orders & Picking Lists
- Internal Transfers & Receipts - Internal Transfers & Receipts
- Payment Receipts & Statements - Payment Receipts & Statements
- Stock Moves & Inventory Reports - Stock Moves & Inventory Reports
- Package & Lot Tracking Labels - Package & Lot Tracking Labels
""", """,
'author': 'Suherdy Yacob', 'author': 'Suherdy Yacob',
'depends': [ 'depends': [
'base', 'base',
'web', 'web',
'account', 'account',
'sale', 'sale',
'purchase', 'purchase',
'stock', 'stock',
], ],
'data': [ 'data': [
'data/ir_actions_server.xml', 'data/ir_actions_server.xml',
'views/direct_print_templates.xml', 'views/direct_print_templates.xml',
'views/sale_order_views.xml', 'views/sale_order_views.xml',
'views/sale_order_form_views.xml', 'views/sale_order_form_views.xml',
'views/purchase_order_views.xml', 'views/purchase_order_views.xml',
'views/purchase_order_form_views.xml', 'views/purchase_order_form_views.xml',
'views/stock_picking_views.xml', 'views/stock_picking_views.xml',
'views/stock_picking_form_views.xml', 'views/stock_picking_form_views.xml',
'views/account_move_views.xml', 'views/account_move_views.xml',
], ],
'assets': { 'assets': {
'web.assets_backend': [ 'web.assets_backend': [
'web_direct_print/static/src/js/direct_print.js', 'web_direct_print/static/src/js/direct_print.js',
'web_direct_print/static/src/xml/direct_print.xml', 'web_direct_print/static/src/xml/direct_print.xml',
], ],
}, },
'installable': True, 'installable': True,
'auto_install': False, 'auto_install': False,
'license': 'LGPL-3', 'license': 'LGPL-3',
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data noupdate="1"> <data noupdate="1">
<!-- Server Action to trigger direct print --> <!-- Server Action to trigger direct print -->
<record id="action_direct_print" model="ir.actions.server"> <record id="action_direct_print" model="ir.actions.server">
<field name="name">Direct Print</field> <field name="name">Direct Print</field>
<field name="model_id" ref="base.model_ir_actions_report"/> <field name="model_id" ref="base.model_ir_actions_report"/>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if object: if object:
action = env['web.direct.print'].direct_print_action(object.report_name, env.context.get('active_ids', []), context=env.context) action = env['web.direct.print'].direct_print_action(object.report_name, env.context.get('active_ids', []), context=env.context)
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,4 +1,4 @@
from . import direct_print from . import direct_print
from . import stock_picking from . import stock_picking
from . import sale_order from . import sale_order
from . import purchase_order from . import purchase_order

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,212 +1,212 @@
from odoo import models, fields, api from odoo import models, fields, api
import base64 import base64
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class DirectPrint(models.Model): class DirectPrint(models.Model):
_name = 'web.direct.print' _name = 'web.direct.print'
_description = 'Web Direct Print' _description = 'Web Direct Print'
@api.model @api.model
def get_report_data(self, report_name, docids, data=None): def get_report_data(self, report_name, docids, data=None):
""" """
Generate report data for direct printing Generate report data for direct printing
:param report_name: Name of the report :param report_name: Name of the report
:param docids: Document IDs to print :param docids: Document IDs to print
:param data: Additional report data :param data: Additional report data
:return: Report content in base64 :return: Report content in base64
""" """
try: try:
_logger.info(f"get_report_data called with report_name={report_name} (type: {type(report_name)}), docids={docids} (type: {type(docids)})") _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 # Handle report_name parameter - it could be a string, list, or ID
if isinstance(report_name, list): if isinstance(report_name, list):
if report_name and isinstance(report_name[0], int): if report_name and isinstance(report_name[0], int):
# It's a list of IDs, get the report by ID # It's a list of IDs, get the report by ID
report = self.env['ir.actions.report'].browse(report_name[0]) report = self.env['ir.actions.report'].browse(report_name[0])
else: else:
# It's a list with string names # It's a list with string names
report_name_str = report_name[0] if report_name else '' report_name_str = report_name[0] if report_name else ''
report = self.env['ir.actions.report'].search([('report_name', '=', report_name_str)], limit=1) report = self.env['ir.actions.report'].search([('report_name', '=', report_name_str)], limit=1)
elif isinstance(report_name, int): elif isinstance(report_name, int):
# It's an ID # It's an ID
report = self.env['ir.actions.report'].browse(report_name) report = self.env['ir.actions.report'].browse(report_name)
elif isinstance(report_name, str): elif isinstance(report_name, str):
# It's a string name - try to find by report_name or use env.ref # It's a string name - try to find by report_name or use env.ref
if '.' in report_name: if '.' in report_name:
# It looks like a module.xml_id format, try env.ref first # It looks like a module.xml_id format, try env.ref first
try: try:
ref_obj = self.env.ref(report_name) ref_obj = self.env.ref(report_name)
# Check if the referenced object is actually a report # Check if the referenced object is actually a report
if hasattr(ref_obj, '_name') and ref_obj._name == 'ir.actions.report': if hasattr(ref_obj, '_name') and ref_obj._name == 'ir.actions.report':
report = ref_obj report = ref_obj
else: else:
# It's not a report (probably a view), search by report_name instead # 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) report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1)
except ValueError: except ValueError:
# If env.ref fails, try searching by report_name # If env.ref fails, try searching by report_name
report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1)
else: else:
# Search by report_name # Search by report_name
report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1) report = self.env['ir.actions.report'].search([('report_name', '=', report_name)], limit=1)
else: else:
report = None report = None
# Generate the report content # Generate the report content
if report: if report:
# Generate the pdf content with proper context # Generate the pdf content with proper context
current_context = self.env.context.copy() current_context = self.env.context.copy()
# Handle docids parameter - ensure it's a list of integers # Handle docids parameter - ensure it's a list of integers
if isinstance(docids, str): if isinstance(docids, str):
# If it's a string, try to convert to int # If it's a string, try to convert to int
try: try:
docids = [int(docids)] docids = [int(docids)]
except ValueError: except ValueError:
# If it contains commas, split and convert # If it contains commas, split and convert
if ',' in docids: if ',' in docids:
docids = [int(d.strip()) for d in docids.split(',') if d.strip()] docids = [int(d.strip()) for d in docids.split(',') if d.strip()]
else: else:
docids = [int(docids)] docids = [int(docids)]
elif isinstance(docids, int): elif isinstance(docids, int):
docids = [docids] docids = [docids]
elif isinstance(docids, list): elif isinstance(docids, list):
# Ensure all elements are integers # Ensure all elements are integers
processed_ids = [] processed_ids = []
for d in docids: for d in docids:
if isinstance(d, str): if isinstance(d, str):
try: try:
processed_ids.append(int(d)) processed_ids.append(int(d))
except ValueError: except ValueError:
continue continue
elif isinstance(d, int): elif isinstance(d, int):
processed_ids.append(d) processed_ids.append(d)
docids = processed_ids docids = processed_ids
else: else:
docids = [] docids = []
_logger.info(f"Processed docids: {docids}") _logger.info(f"Processed docids: {docids}")
if not docids: if not docids:
return { return {
'success': False, 'success': False,
'error': 'No valid document IDs provided' 'error': 'No valid document IDs provided'
} }
# Use the standard Odoo report rendering approach # Use the standard Odoo report rendering approach
# Ensure report_name is a string, not a list # Ensure report_name is a string, not a list
report_name_str = str(report.report_name) if report.report_name else report.name 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)})") _logger.info(f"Using report_name_str: {report_name_str} (type: {type(report_name_str)})")
# Generate PDF using the standard method # 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 {}) 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 # Encode the content in base64 for transmission
encoded_content = base64.b64encode(pdf_content).decode('utf-8') encoded_content = base64.b64encode(pdf_content).decode('utf-8')
return { return {
'success': True, 'success': True,
'content': encoded_content, 'content': encoded_content,
'report_name': report_name, 'report_name': report_name,
'docids': docids, 'docids': docids,
'content_type': 'application/pdf' 'content_type': 'application/pdf'
} }
else: else:
return { return {
'success': False, 'success': False,
'error': f'Report "{report_name}" not found' 'error': f'Report "{report_name}" not found'
} }
except Exception as e: except Exception as e:
_logger.error(f"Error generating report for direct print: {str(e)}") _logger.error(f"Error generating report for direct print: {str(e)}")
_logger.exception("Full traceback:") _logger.exception("Full traceback:")
return { return {
'success': False, 'success': False,
'error': str(e) 'error': str(e)
} }
@api.model @api.model
def prepare_print_data(self, report_name, docids, data=None): def prepare_print_data(self, report_name, docids, data=None):
""" """
Prepare data for direct printing Prepare data for direct printing
:param report_name: Name of the report to print :param report_name: Name of the report to print
:param docids: IDs of documents to print :param docids: IDs of documents to print
:param data: Additional data for the report :param data: Additional data for the report
:return: Dictionary with report data :return: Dictionary with report data
""" """
try: try:
result = self.get_report_data(report_name, docids, data) result = self.get_report_data(report_name, docids, data)
return result return result
except Exception as e: except Exception as e:
_logger.error(f"Error preparing print data: {str(e)}") _logger.error(f"Error preparing print data: {str(e)}")
return { return {
'success': False, 'success': False,
'error': str(e) 'error': str(e)
} }
@api.model @api.model
def get_available_reports(self): def get_available_reports(self):
""" """
Get list of available reports for direct printing Get list of available reports for direct printing
:return: List of available reports :return: List of available reports
""" """
try: try:
reports = self.env['ir.actions.report'].search([ reports = self.env['ir.actions.report'].search([
('active', '=', True), ('active', '=', True),
('report_type', 'in', ['qweb-pdf', 'qweb-html']) ('report_type', 'in', ['qweb-pdf', 'qweb-html'])
]) ])
report_list = [] report_list = []
for report in reports: for report in reports:
report_list.append({ report_list.append({
'id': report.id, 'id': report.id,
'name': report.name, 'name': report.name,
'report_name': report.report_name, 'report_name': report.report_name,
'model': report.model, 'model': report.model,
}) })
return { return {
'success': True, 'success': True,
'reports': report_list 'reports': report_list
} }
except Exception as e: except Exception as e:
_logger.error(f"Error getting available reports: {str(e)}") _logger.error(f"Error getting available reports: {str(e)}")
return { return {
'success': False, 'success': False,
'error': str(e) 'error': str(e)
} }
@api.model @api.model
def direct_print_action(self, report_name, docids, data=None, context=None): def direct_print_action(self, report_name, docids, data=None, context=None):
""" """
Execute direct print action for a given report Execute direct print action for a given report
:param report_name: Name of the report to print :param report_name: Name of the report to print
:param docids: IDs of documents to print :param docids: IDs of documents to print
:param data: Additional data for the report :param data: Additional data for the report
:param context: Context data :param context: Context data
:return: Result of the print action :return: Result of the print action
""" """
try: try:
# Update the environment context if provided # Update the environment context if provided
if context: if context:
self = self.with_context(**context) self = self.with_context(**context)
# Prepare the print data # Prepare the print data
print_data = self.prepare_print_data(report_name, docids, data) print_data = self.prepare_print_data(report_name, docids, data)
if print_data['success']: if print_data['success']:
# In a real implementation, we might want to add additional # In a real implementation, we might want to add additional
# processing here, such as tracking print jobs or handling # processing here, such as tracking print jobs or handling
# special printer configurations # special printer configurations
# Return the print data for client-side processing # Return the print data for client-side processing
return print_data return print_data
else: else:
return print_data return print_data
except Exception as e: except Exception as e:
_logger.error(f"Error in direct print action: {str(e)}") _logger.error(f"Error in direct print action: {str(e)}")
return { return {
'success': False, 'success': False,
'error': str(e) 'error': str(e)
} }

View File

@ -1,18 +1,18 @@
from odoo import models, fields, api from odoo import models, fields, api
class PurchaseOrder(models.Model): class PurchaseOrder(models.Model):
_inherit = 'purchase.order' _inherit = 'purchase.order'
def action_direct_print_purchase_order(self): def action_direct_print_purchase_order(self):
""" """
Direct print action for purchase orders and RFQs Direct print action for purchase orders and RFQs
""" """
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'name': 'Direct Print Purchase Order', 'name': 'Direct Print Purchase Order',
'report_name': 'purchase.report_purchaseorder', 'report_name': 'purchase.report_purchaseorder',
'docids': self.ids, 'docids': self.ids,
'context': self.env.context, 'context': self.env.context,
} }

View File

@ -1,18 +1,18 @@
from odoo import models, fields, api from odoo import models, fields, api
class SaleOrder(models.Model): class SaleOrder(models.Model):
_inherit = 'sale.order' _inherit = 'sale.order'
def action_direct_print_quotation(self): def action_direct_print_quotation(self):
""" """
Direct print action for sale orders and quotations Direct print action for sale orders and quotations
""" """
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'name': 'Direct Print Quotation/Order', 'name': 'Direct Print Quotation/Order',
'report_name': 'sale.report_saleorder', 'report_name': 'sale.report_saleorder',
'docids': self.ids, 'docids': self.ids,
'context': self.env.context, 'context': self.env.context,
} }

View File

@ -1,56 +1,56 @@
from odoo import models, fields, api from odoo import models, fields, api
import json import json
class StockPicking(models.Model): class StockPicking(models.Model):
_inherit = 'stock.picking' _inherit = 'stock.picking'
def action_direct_print_receipt(self): def action_direct_print_receipt(self):
""" """
Direct print action for stock picking receipts Direct print action for stock picking receipts
""" """
# Determine the appropriate report based on picking type # Determine the appropriate report based on picking type
if self.picking_type_id.code == 'incoming': if self.picking_type_id.code == 'incoming':
report_name = 'stock.action_report_delivery' report_name = 'stock.action_report_delivery'
elif self.picking_type_id.code == 'outgoing': elif self.picking_type_id.code == 'outgoing':
report_name = 'stock.action_report_delivery' report_name = 'stock.action_report_delivery'
elif self.picking_type_id.code == 'internal': elif self.picking_type_id.code == 'internal':
report_name = 'stock.action_report_picking' report_name = 'stock.action_report_picking'
else: else:
report_name = 'stock.action_report_picking' report_name = 'stock.action_report_picking'
# Return client action for direct printing # Return client action for direct printing
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'name': 'Direct Print', 'name': 'Direct Print',
'report_name': report_name, 'report_name': report_name,
'docids': self.ids, 'docids': self.ids,
'context': self.env.context, 'context': self.env.context,
} }
def action_direct_print_delivery(self): def action_direct_print_delivery(self):
""" """
Direct print delivery slip Direct print delivery slip
""" """
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'name': 'Direct Print Delivery', 'name': 'Direct Print Delivery',
'report_name': 'stock.action_report_delivery', 'report_name': 'stock.action_report_delivery',
'docids': self.ids, 'docids': self.ids,
'context': self.env.context, 'context': self.env.context,
} }
def action_direct_print_picking_operations(self): def action_direct_print_picking_operations(self):
""" """
Direct print picking operations Direct print picking operations
""" """
return { return {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'name': 'Direct Print Operations', 'name': 'Direct Print Operations',
'report_name': 'stock.action_report_picking', 'report_name': 'stock.action_report_picking',
'docids': self.ids, 'docids': self.ids,
'context': self.env.context, 'context': self.env.context,
} }

View File

@ -1,216 +1,216 @@
/** @odoo-module **/ /** @odoo-module **/
import { rpc } from "@web/core/network/rpc"; import { rpc } from "@web/core/network/rpc";
import { _t } from "@web/core/l10n/translation"; import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry"; import { registry } from "@web/core/registry";
import { Component } from "@odoo/owl"; import { Component } from "@odoo/owl";
const actionRegistry = registry.category("actions"); const actionRegistry = registry.category("actions");
// Direct Print Action Handler // Direct Print Action Handler
class DirectPrintAction extends Component { class DirectPrintAction extends Component {
static template = "web_direct_print.DirectPrintAction"; static template = "web_direct_print.DirectPrintAction";
async setup() { async setup() {
// This action will handle direct print requests // This action will handle direct print requests
const action = this.props.action; const action = this.props.action;
console.log("DirectPrintAction received action:", action); console.log("DirectPrintAction received action:", action);
const report_name = action.report_name || action.context?.report_name || ''; const report_name = action.report_name || action.context?.report_name || '';
const docids = action.docids || action.context?.active_ids || []; const docids = action.docids || action.context?.active_ids || [];
const data = action.data || {}; const data = action.data || {};
console.log("Extracted params:", { report_name, docids, data }); console.log("Extracted params:", { report_name, docids, data });
await this.directPrint(report_name, docids, data); await this.directPrint(report_name, docids, data);
} }
async directPrint(reportName, docIds, data) { async directPrint(reportName, docIds, data) {
try { try {
// Call the controller endpoint to get report data // Call the controller endpoint to get report data
const result = await rpc("/web/direct_print", { const result = await rpc("/web/direct_print", {
report_name: reportName, report_name: reportName,
docids: docIds, docids: docIds,
data: data || {} data: data || {}
}); });
if (result.success) { if (result.success) {
this.printReport(result); this.printReport(result);
} else { } else {
this.env.services.notification.add( this.env.services.notification.add(
_t("Error: ") + (result.error || _t("Unknown error occurred")), _t("Error: ") + (result.error || _t("Unknown error occurred")),
{ type: "danger" } { type: "danger" }
); );
} }
} catch (error) { } catch (error) {
console.error("Direct print error:", error); console.error("Direct print error:", error);
this.env.services.notification.add( this.env.services.notification.add(
_t("Error occurred while preparing print: ") + (error.message || error), _t("Error occurred while preparing print: ") + (error.message || error),
{ type: "danger" } { type: "danger" }
); );
} }
} }
printReport(printData) { printReport(printData) {
// Create a blob from the base64 content // Create a blob from the base64 content
const binaryString = atob(printData.content); const binaryString = atob(printData.content);
const bytes = new Uint8Array(binaryString.length); const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) { for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i); bytes[i] = binaryString.charCodeAt(i);
} }
const blob = new Blob([bytes], { type: printData.content_type }); const blob = new Blob([bytes], { type: printData.content_type });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
// Create a new window with HTML that embeds the PDF and has print functionality // Create a new window with HTML that embeds the PDF and has print functionality
const printWindow = window.open('', '_blank', 'width=800,height=600'); const printWindow = window.open('', '_blank', 'width=800,height=600');
if (!printWindow) { if (!printWindow) {
// If popup is blocked, show download option // If popup is blocked, show download option
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = printData.report_name + '.pdf'; a.download = printData.report_name + '.pdf';
a.click(); a.click();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
return; return;
} }
// Write HTML content that will embed the PDF and automatically trigger print // Write HTML content that will embed the PDF and automatically trigger print
var htmlContent = '<!DOCTYPE html>' + var htmlContent = '<!DOCTYPE html>' +
'<html>' + '<html>' +
'<head>' + '<head>' +
'<title>Printing Document</title>' + '<title>Printing Document</title>' +
'<style>' + '<style>' +
'body, html {' + 'body, html {' +
'margin: 0;' + 'margin: 0;' +
'padding: 0;' + 'padding: 0;' +
'height: 100%;' + 'height: 100%;' +
'overflow: hidden;' + 'overflow: hidden;' +
'}' + '}' +
'#pdfContainer {' + '#pdfContainer {' +
'width: 100%;' + 'width: 100%;' +
'height: 100vh;' + 'height: 100vh;' +
'border: none;' + 'border: none;' +
'}' + '}' +
'</style>' + '</style>' +
'</head>' + '</head>' +
'<body>' + '<body>' +
'<div id="pdfContainer">' + '<div id="pdfContainer">' +
'<embed id="pdfEmbed" src="' + url + '" type="application/pdf" width="100%" height="100%">' + '<embed id="pdfEmbed" src="' + url + '" type="application/pdf" width="100%" height="100%">' +
'</div>' + '</div>' +
'<script>' + '<script>' +
'var pdfEmbed = document.getElementById("pdfEmbed");' + 'var pdfEmbed = document.getElementById("pdfEmbed");' +
'var printWindow = window;' + 'var printWindow = window;' +
'' + '' +
'// Wait for the PDF to load and then print' + '// Wait for the PDF to load and then print' +
'function attemptPrint() {' + 'function attemptPrint() {' +
'try {' + 'try {' +
'// Method 1: Try to print directly' + '// Method 1: Try to print directly' +
'if (printWindow.print) {' + 'if (printWindow.print) {' +
'printWindow.print();' + 'printWindow.print();' +
'}' + '}' +
'} catch (e) {' + '} catch (e) {' +
'console.log("Direct print failed:", e);' + 'console.log("Direct print failed:", e);' +
'' + '' +
'// Method 2: Try to access the embedded PDF\'s print functionality' + '// Method 2: Try to access the embedded PDF\'s print functionality' +
'try {' + 'try {' +
'if (pdfEmbed && pdfEmbed.print) {' + 'if (pdfEmbed && pdfEmbed.print) {' +
'pdfEmbed.print();' + 'pdfEmbed.print();' +
'} else {' + '} else {' +
'// Method 3: Try to print using document.execCommand (older method)' + '// Method 3: Try to print using document.execCommand (older method)' +
'if (document.execCommand) {' + 'if (document.execCommand) {' +
'document.execCommand("print", false, null);' + 'document.execCommand("print", false, null);' +
'}' + '}' +
'}' + '}' +
'} catch (e2) {' + '} catch (e2) {' +
'console.log("Alternative print methods failed:", e2);' + 'console.log("Alternative print methods failed:", e2);' +
'// As a last resort, show user instructions' + '// As a last resort, show user instructions' +
'printWindow.document.body.innerHTML = \'<div style="padding: 20px;"><p>PDF loaded. Press Ctrl+P to print.</p><embed src="' + url + '" type="application/pdf" width="100%" height="90%"></div>\';' + 'printWindow.document.body.innerHTML = \'<div style="padding: 20px;"><p>PDF loaded. Press Ctrl+P to print.</p><embed src="' + url + '" type="application/pdf" width="100%" height="90%"></div>\';' +
'}' + '}' +
'}' + '}' +
'}' + '}' +
'' + '' +
'// Try to print immediately and at intervals' + '// Try to print immediately and at intervals' +
'setTimeout(attemptPrint, 1000); // Wait 1 second then try to print' + 'setTimeout(attemptPrint, 1000); // Wait 1 second then try to print' +
'setTimeout(attemptPrint, 3000); // Try again after 3 seconds' + 'setTimeout(attemptPrint, 3000); // Try again after 3 seconds' +
'setTimeout(attemptPrint, 5000); // Try again after 5 seconds' + 'setTimeout(attemptPrint, 5000); // Try again after 5 seconds' +
'' + '' +
'// Also try to print when the window is focused' + '// Also try to print when the window is focused' +
'printWindow.onfocus = function() {' + 'printWindow.onfocus = function() {' +
'setTimeout(attemptPrint, 500);' + 'setTimeout(attemptPrint, 500);' +
'};' + '};' +
'' + '' +
'// Listen for afterprint to close the window if possible' + '// Listen for afterprint to close the window if possible' +
'if (window.matchMedia) {' + 'if (window.matchMedia) {' +
'var mediaQueryList = window.matchMedia("print");' + 'var mediaQueryList = window.matchMedia("print");' +
'mediaQueryList.onchange = function(mql) {' + 'mediaQueryList.onchange = function(mql) {' +
'if (!mql.matches) {' + 'if (!mql.matches) {' +
'// Print dialog was closed, close the window after a delay' + '// Print dialog was closed, close the window after a delay' +
'setTimeout(function() {' + 'setTimeout(function() {' +
'window.close();' + 'window.close();' +
'}, 1000);' + '}, 1000);' +
'}' + '}' +
'};' + '};' +
'}' + '}' +
'' + '' +
'</scr' + 'ipt>' + // Split to avoid issues with closing script tag '</scr' + 'ipt>' + // Split to avoid issues with closing script tag
'</body>' + '</body>' +
'</html>'; '</html>';
printWindow.document.write(htmlContent); printWindow.document.write(htmlContent);
printWindow.document.close(); // Important: Close the document to finish loading printWindow.document.close(); // Important: Close the document to finish loading
// Clean up the URL object after some time // Clean up the URL object after some time
setTimeout(() => { setTimeout(() => {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
}, 5000); }, 5000);
} }
} }
// Register the action // Register the action
actionRegistry.add("direct_print", DirectPrintAction); actionRegistry.add("direct_print", DirectPrintAction);
// Global function for backward compatibility // Global function for backward compatibility
window.directPrint = async function(reportName, docIds, data) { window.directPrint = async function(reportName, docIds, data) {
try { try {
const result = await rpc("/web/direct_print", { const result = await rpc("/web/direct_print", {
report_name: reportName, report_name: reportName,
docids: docIds, docids: docIds,
data: data || {} data: data || {}
}); });
if (result.success) { if (result.success) {
// Create a blob from the base64 content // Create a blob from the base64 content
const binaryString = atob(result.content); const binaryString = atob(result.content);
const bytes = new Uint8Array(binaryString.length); const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) { for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i); bytes[i] = binaryString.charCodeAt(i);
} }
const blob = new Blob([bytes], { type: result.content_type }); const blob = new Blob([bytes], { type: result.content_type });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
// Create a new window for printing // Create a new window for printing
const printWindow = window.open('', '_blank', 'width=800,height=600'); const printWindow = window.open('', '_blank', 'width=800,height=600');
if (printWindow) { if (printWindow) {
printWindow.document.write(` printWindow.document.write(`
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head><title>Print</title></head> <head><title>Print</title></head>
<body style="margin:0;"> <body style="margin:0;">
<embed src="${url}" type="application/pdf" width="100%" height="100%"> <embed src="${url}" type="application/pdf" width="100%" height="100%">
<script> <script>
setTimeout(() => { window.print(); }, 1000); setTimeout(() => { window.print(); }, 1000);
setTimeout(() => { window.close(); }, 5000); setTimeout(() => { window.close(); }, 5000);
</script> </script>
</body> </body>
</html> </html>
`); `);
printWindow.document.close(); printWindow.document.close();
} }
// Clean up // Clean up
setTimeout(() => URL.revokeObjectURL(url), 10000); setTimeout(() => URL.revokeObjectURL(url), 10000);
} }
} catch (error) { } catch (error) {
console.error("Direct print error:", error); console.error("Direct print error:", error);
} }
}; };

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve"> <templates id="template" xml:space="preserve">
<t t-name="web_direct_print.DirectPrintAction"> <t t-name="web_direct_print.DirectPrintAction">
<div class="o_direct_print_action"> <div class="o_direct_print_action">
<!-- This template is minimal as the printing happens in setup() --> <!-- This template is minimal as the printing happens in setup() -->
<div class="text-center"> <div class="text-center">
<i class="fa fa-spinner fa-spin fa-2x"/> <i class="fa fa-spinner fa-spin fa-2x"/>
<div class="mt-2">Preparing print...</div> <div class="mt-2">Preparing print...</div>
</div> </div>
</div> </div>
</t> </t>
</templates> </templates>

View File

@ -1,115 +1,115 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<!-- Customer Invoice Direct Print --> <!-- Customer Invoice Direct Print -->
<record id="action_direct_print_customer_invoice" model="ir.actions.server"> <record id="action_direct_print_customer_invoice" model="ir.actions.server">
<field name="name">Direct Print Customer Invoice</field> <field name="name">Direct Print Customer Invoice</field>
<field name="model_id" ref="account.model_account_move"/> <field name="model_id" ref="account.model_account_move"/>
<field name="binding_model_id" ref="account.model_account_move"/> <field name="binding_model_id" ref="account.model_account_move"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for customer invoices # Filter for customer invoices
invoices = records.filtered(lambda r: r.move_type in ['out_invoice', 'out_refund']) invoices = records.filtered(lambda r: r.move_type in ['out_invoice', 'out_refund'])
if invoices: if invoices:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'account.report_invoice', 'report_name': 'account.report_invoice',
'docids': invoices.ids, 'docids': invoices.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Vendor Bill Direct Print --> <!-- Vendor Bill Direct Print -->
<record id="action_direct_print_vendor_bill" model="ir.actions.server"> <record id="action_direct_print_vendor_bill" model="ir.actions.server">
<field name="name">Direct Print Vendor Bill</field> <field name="name">Direct Print Vendor Bill</field>
<field name="model_id" ref="account.model_account_move"/> <field name="model_id" ref="account.model_account_move"/>
<field name="binding_model_id" ref="account.model_account_move"/> <field name="binding_model_id" ref="account.model_account_move"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for vendor bills # Filter for vendor bills
bills = records.filtered(lambda r: r.move_type in ['in_invoice', 'in_refund']) bills = records.filtered(lambda r: r.move_type in ['in_invoice', 'in_refund'])
if bills: if bills:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'account.report_invoice', 'report_name': 'account.report_invoice',
'docids': bills.ids, 'docids': bills.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Invoice with Payment Receipt Direct Print --> <!-- Invoice with Payment Receipt Direct Print -->
<record id="action_direct_print_invoice_with_payments" model="ir.actions.server"> <record id="action_direct_print_invoice_with_payments" model="ir.actions.server">
<field name="name">Direct Print Invoice with Payments</field> <field name="name">Direct Print Invoice with Payments</field>
<field name="model_id" ref="account.model_account_move"/> <field name="model_id" ref="account.model_account_move"/>
<field name="binding_model_id" ref="account.model_account_move"/> <field name="binding_model_id" ref="account.model_account_move"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for customer invoices # Filter for customer invoices
invoices = records.filtered(lambda r: r.move_type in ['out_invoice']) invoices = records.filtered(lambda r: r.move_type in ['out_invoice'])
if invoices: if invoices:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'account.report_invoice_with_payments', 'report_name': 'account.report_invoice_with_payments',
'docids': invoices.ids, 'docids': invoices.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Payment Receipt Direct Print --> <!-- Payment Receipt Direct Print -->
<record id="action_direct_print_payment_receipt" model="ir.actions.server"> <record id="action_direct_print_payment_receipt" model="ir.actions.server">
<field name="name">Direct Print Payment Receipt</field> <field name="name">Direct Print Payment Receipt</field>
<field name="model_id" ref="account.model_account_payment"/> <field name="model_id" ref="account.model_account_payment"/>
<field name="binding_model_id" ref="account.model_account_payment"/> <field name="binding_model_id" ref="account.model_account_payment"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for payments # Filter for payments
payments = records.filtered(lambda r: r.state == 'posted') payments = records.filtered(lambda r: r.state == 'posted')
if payments: if payments:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'account.action_report_payment_receipt', 'report_name': 'account.action_report_payment_receipt',
'docids': payments.ids, 'docids': payments.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Account Statement Direct Print --> <!-- Account Statement Direct Print -->
<record id="action_direct_print_account_statement" model="ir.actions.server"> <record id="action_direct_print_account_statement" model="ir.actions.server">
<field name="name">Direct Print Account Statement</field> <field name="name">Direct Print Account Statement</field>
<field name="model_id" ref="base.model_res_partner"/> <field name="model_id" ref="base.model_res_partner"/>
<field name="binding_model_id" ref="base.model_res_partner"/> <field name="binding_model_id" ref="base.model_res_partner"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for partners with accounting entries # Filter for partners with accounting entries
partners = records.filtered(lambda r: r.customer_rank > 0 or r.supplier_rank > 0) partners = records.filtered(lambda r: r.customer_rank > 0 or r.supplier_rank > 0)
if partners: if partners:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'account.report_partnerledger', 'report_name': 'account.report_partnerledger',
'docids': partners.ids, 'docids': partners.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
</odoo> </odoo>

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<!-- Template for direct print button that can be added to forms --> <!-- Template for direct print button that can be added to forms -->
<template id="direct_print_button" name="Direct Print Button"> <template id="direct_print_button" name="Direct Print Button">
<button type="button" class="btn btn-primary o_direct_print_btn" <button type="button" class="btn btn-primary o_direct_print_btn"
title="Print directly to local printer"> title="Print directly to local printer">
<i class="fa fa-print"/> Direct Print <i class="fa fa-print"/> Direct Print
</button> </button>
</template> </template>
</odoo> </odoo>

View File

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<!-- Add Direct Print button to Purchase Order form view --> <!-- Add Direct Print button to Purchase Order form view -->
<record id="purchase_order_form_direct_print" model="ir.ui.view"> <record id="purchase_order_form_direct_print" model="ir.ui.view">
<field name="name">purchase.order.form.direct.print</field> <field name="name">purchase.order.form.direct.print</field>
<field name="model">purchase.order</field> <field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/> <field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//header" position="inside"> <xpath expr="//header" position="inside">
<button name="action_direct_print_purchase_order" <button name="action_direct_print_purchase_order"
type="object" type="object"
string="Direct Print" string="Direct Print"
class="btn-primary" class="btn-primary"
invisible="state == 'cancel'" invisible="state == 'cancel'"
groups="purchase.group_purchase_user"/> groups="purchase.group_purchase_user"/>
</xpath> </xpath>
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<record id="action_direct_print_purchase_order" model="ir.actions.server"> <record id="action_direct_print_purchase_order" model="ir.actions.server">
<field name="name">Direct Print Request for Quotation / Order</field> <field name="name">Direct Print Request for Quotation / Order</field>
<field name="model_id" ref="purchase.model_purchase_order"/> <field name="model_id" ref="purchase.model_purchase_order"/>
<field name="binding_model_id" ref="purchase.model_purchase_order"/> <field name="binding_model_id" ref="purchase.model_purchase_order"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'purchase.report_purchaseorder', 'report_name': 'purchase.report_purchaseorder',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<!-- Add Direct Print button to Sale Order form view --> <!-- Add Direct Print button to Sale Order form view -->
<record id="view_order_form_direct_print" model="ir.ui.view"> <record id="view_order_form_direct_print" model="ir.ui.view">
<field name="name">sale.order.form.direct.print</field> <field name="name">sale.order.form.direct.print</field>
<field name="model">sale.order</field> <field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/> <field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//header" position="inside"> <xpath expr="//header" position="inside">
<button name="action_direct_print_quotation" <button name="action_direct_print_quotation"
type="object" type="object"
string="Direct Print" string="Direct Print"
class="btn-primary" class="btn-primary"
invisible="state == 'cancel'" invisible="state == 'cancel'"
groups="sales_team.group_sale_salesman"/> groups="sales_team.group_sale_salesman"/>
</xpath> </xpath>
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<record id="action_direct_print_sale_order" model="ir.actions.server"> <record id="action_direct_print_sale_order" model="ir.actions.server">
<field name="name">Direct Print Quotation / Order</field> <field name="name">Direct Print Quotation / Order</field>
<field name="model_id" ref="sale.model_sale_order"/> <field name="model_id" ref="sale.model_sale_order"/>
<field name="binding_model_id" ref="sale.model_sale_order"/> <field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'sale.report_saleorder', 'report_name': 'sale.report_saleorder',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<!-- Add Direct Print button to Stock Picking form view --> <!-- Add Direct Print button to Stock Picking form view -->
<record id="view_picking_form_direct_print" model="ir.ui.view"> <record id="view_picking_form_direct_print" model="ir.ui.view">
<field name="name">stock.picking.form.direct.print</field> <field name="name">stock.picking.form.direct.print</field>
<field name="model">stock.picking</field> <field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/> <field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//header" position="inside"> <xpath expr="//header" position="inside">
<button name="action_direct_print_receipt" <button name="action_direct_print_receipt"
type="object" type="object"
string="Direct Print" string="Direct Print"
class="btn-primary" class="btn-primary"
invisible="state not in ['done', 'assigned']" invisible="state not in ['done', 'assigned']"
groups="stock.group_stock_user"/> groups="stock.group_stock_user"/>
</xpath> </xpath>
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@ -1,160 +1,160 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data> <data>
<record id="action_direct_print_delivery_slip" model="ir.actions.server"> <record id="action_direct_print_delivery_slip" model="ir.actions.server">
<field name="name">Direct Print Delivery Slip</field> <field name="name">Direct Print Delivery Slip</field>
<field name="model_id" ref="stock.model_stock_picking"/> <field name="model_id" ref="stock.model_stock_picking"/>
<field name="binding_model_id" ref="stock.model_stock_picking"/> <field name="binding_model_id" ref="stock.model_stock_picking"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_delivery', 'report_name': 'stock.action_report_delivery',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<record id="action_direct_print_picking_operations" model="ir.actions.server"> <record id="action_direct_print_picking_operations" model="ir.actions.server">
<field name="name">Direct Print Picking Operations</field> <field name="name">Direct Print Picking Operations</field>
<field name="model_id" ref="stock.model_stock_picking"/> <field name="model_id" ref="stock.model_stock_picking"/>
<field name="binding_model_id" ref="stock.model_stock_picking"/> <field name="binding_model_id" ref="stock.model_stock_picking"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_picking', 'report_name': 'stock.action_report_picking',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Internal Transfer Direct Print --> <!-- Internal Transfer Direct Print -->
<record id="action_direct_print_internal_transfer" model="ir.actions.server"> <record id="action_direct_print_internal_transfer" model="ir.actions.server">
<field name="name">Direct Print Internal Transfer</field> <field name="name">Direct Print Internal Transfer</field>
<field name="model_id" ref="stock.model_stock_picking"/> <field name="model_id" ref="stock.model_stock_picking"/>
<field name="binding_model_id" ref="stock.model_stock_picking"/> <field name="binding_model_id" ref="stock.model_stock_picking"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for internal transfers # Filter for internal transfers
internal_transfers = records.filtered(lambda r: r.picking_type_id.code == 'internal') internal_transfers = records.filtered(lambda r: r.picking_type_id.code == 'internal')
if internal_transfers: if internal_transfers:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_picking', 'report_name': 'stock.action_report_picking',
'docids': internal_transfers.ids, 'docids': internal_transfers.ids,
'context': env.context, 'context': env.context,
} }
else: else:
# All records if none are internal transfers # All records if none are internal transfers
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_picking', 'report_name': 'stock.action_report_picking',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Receipt/Incoming Shipment Direct Print --> <!-- Receipt/Incoming Shipment Direct Print -->
<record id="action_direct_print_receipt" model="ir.actions.server"> <record id="action_direct_print_receipt" model="ir.actions.server">
<field name="name">Direct Print Receipt</field> <field name="name">Direct Print Receipt</field>
<field name="model_id" ref="stock.model_stock_picking"/> <field name="model_id" ref="stock.model_stock_picking"/>
<field name="binding_model_id" ref="stock.model_stock_picking"/> <field name="binding_model_id" ref="stock.model_stock_picking"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
# Filter for incoming/receipt operations # Filter for incoming/receipt operations
receipts = records.filtered(lambda r: r.picking_type_id.code == 'incoming') receipts = records.filtered(lambda r: r.picking_type_id.code == 'incoming')
if receipts: if receipts:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_picking', 'report_name': 'stock.action_report_picking',
'docids': receipts.ids, 'docids': receipts.ids,
'context': env.context, 'context': env.context,
} }
else: else:
# All records if none are receipts # All records if none are receipts
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_picking', 'report_name': 'stock.action_report_picking',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Stock Move Direct Print --> <!-- Stock Move Direct Print -->
<record id="action_direct_print_stock_move" model="ir.actions.server"> <record id="action_direct_print_stock_move" model="ir.actions.server">
<field name="name">Direct Print Stock Move</field> <field name="name">Direct Print Stock Move</field>
<field name="model_id" ref="stock.model_stock_move"/> <field name="model_id" ref="stock.model_stock_move"/>
<field name="binding_model_id" ref="stock.model_stock_move"/> <field name="binding_model_id" ref="stock.model_stock_move"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.report_stock_move', 'report_name': 'stock.report_stock_move',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Inventory Valuation Direct Print --> <!-- Inventory Valuation Direct Print -->
<record id="action_direct_print_inventory_valuation" model="ir.actions.server"> <record id="action_direct_print_inventory_valuation" model="ir.actions.server">
<field name="name">Direct Print Inventory Valuation</field> <field name="name">Direct Print Inventory Valuation</field>
<field name="model_id" ref="stock.model_stock_quant"/> <field name="model_id" ref="stock.model_stock_quant"/>
<field name="binding_model_id" ref="stock.model_stock_quant"/> <field name="binding_model_id" ref="stock.model_stock_quant"/>
<field name="binding_view_types">list</field> <field name="binding_view_types">list</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_inventory', 'report_name': 'stock.action_report_inventory',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
<!-- Package/Lot Tracking Direct Print --> <!-- Package/Lot Tracking Direct Print -->
<record id="action_direct_print_package_content" model="ir.actions.server"> <record id="action_direct_print_package_content" model="ir.actions.server">
<field name="name">Direct Print Package Content</field> <field name="name">Direct Print Package Content</field>
<field name="model_id" ref="stock.model_stock_quant_package"/> <field name="model_id" ref="stock.model_stock_quant_package"/>
<field name="binding_model_id" ref="stock.model_stock_quant_package"/> <field name="binding_model_id" ref="stock.model_stock_quant_package"/>
<field name="binding_view_types">list,form</field> <field name="binding_view_types">list,form</field>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
action = { action = {
'type': 'ir.actions.client', 'type': 'ir.actions.client',
'tag': 'direct_print', 'tag': 'direct_print',
'report_name': 'stock.action_report_quant_package_barcode', 'report_name': 'stock.action_report_quant_package_barcode',
'docids': records.ids, 'docids': records.ids,
'context': env.context, 'context': env.context,
} }
</field> </field>
</record> </record>
</data> </data>
</odoo> </odoo>