first commit
This commit is contained in:
commit
7e5a51fa2d
63
README.md
Normal file
63
README.md
Normal file
@ -0,0 +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
|
||||||
|
|
||||||
|
The module follows Odoo's security model and only allows users to print documents they have access to.
|
||||||
2
__init__.py
Normal file
2
__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from . import models
|
||||||
|
from . import controllers
|
||||||
58
__manifest__.py
Normal file
58
__manifest__.py
Normal file
@ -0,0 +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',
|
||||||
|
}
|
||||||
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
1
controllers/__init__.py
Normal file
1
controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import main
|
||||||
BIN
controllers/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
controllers/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
controllers/__pycache__/main.cpython-312.pyc
Normal file
BIN
controllers/__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
90
controllers/main.py
Normal file
90
controllers/main.py
Normal file
@ -0,0 +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 = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Direct Print Test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Direct Print Test Page</h1>
|
||||||
|
<p>This page demonstrates the direct print functionality.</p>
|
||||||
|
<button onclick="window.print()">Print this page</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
return html_content
|
||||||
16
data/ir_actions_server.xml
Normal file
16
data/ir_actions_server.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- Server Action to trigger direct print -->
|
||||||
|
<record id="action_direct_print" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print</field>
|
||||||
|
<field name="model_id" ref="base.model_ir_actions_report"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if object:
|
||||||
|
action = env['web.direct.print'].direct_print_action(object.report_name, env.context.get('active_ids', []), context=env.context)
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
4
models/__init__.py
Normal file
4
models/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from . import direct_print
|
||||||
|
from . import stock_picking
|
||||||
|
from . import sale_order
|
||||||
|
from . import purchase_order
|
||||||
BIN
models/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
models/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/direct_print.cpython-312.pyc
Normal file
BIN
models/__pycache__/direct_print.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/purchase_order.cpython-312.pyc
Normal file
BIN
models/__pycache__/purchase_order.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/sale_order.cpython-312.pyc
Normal file
BIN
models/__pycache__/sale_order.cpython-312.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/stock_picking.cpython-312.pyc
Normal file
BIN
models/__pycache__/stock_picking.cpython-312.pyc
Normal file
Binary file not shown.
212
models/direct_print.py
Normal file
212
models/direct_print.py
Normal file
@ -0,0 +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)
|
||||||
|
}
|
||||||
18
models/purchase_order.py
Normal file
18
models/purchase_order.py
Normal file
@ -0,0 +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,
|
||||||
|
}
|
||||||
18
models/sale_order.py
Normal file
18
models/sale_order.py
Normal file
@ -0,0 +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,
|
||||||
|
}
|
||||||
56
models/stock_picking.py
Normal file
56
models/stock_picking.py
Normal file
@ -0,0 +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,
|
||||||
|
}
|
||||||
216
static/src/js/direct_print.js
Normal file
216
static/src/js/direct_print.js
Normal file
@ -0,0 +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 = '<!DOCTYPE html>' +
|
||||||
|
'<html>' +
|
||||||
|
'<head>' +
|
||||||
|
'<title>Printing Document</title>' +
|
||||||
|
'<style>' +
|
||||||
|
'body, html {' +
|
||||||
|
'margin: 0;' +
|
||||||
|
'padding: 0;' +
|
||||||
|
'height: 100%;' +
|
||||||
|
'overflow: hidden;' +
|
||||||
|
'}' +
|
||||||
|
'#pdfContainer {' +
|
||||||
|
'width: 100%;' +
|
||||||
|
'height: 100vh;' +
|
||||||
|
'border: none;' +
|
||||||
|
'}' +
|
||||||
|
'</style>' +
|
||||||
|
'</head>' +
|
||||||
|
'<body>' +
|
||||||
|
'<div id="pdfContainer">' +
|
||||||
|
'<embed id="pdfEmbed" src="' + url + '" type="application/pdf" width="100%" height="100%">' +
|
||||||
|
'</div>' +
|
||||||
|
'<script>' +
|
||||||
|
'var pdfEmbed = document.getElementById("pdfEmbed");' +
|
||||||
|
'var printWindow = window;' +
|
||||||
|
'' +
|
||||||
|
'// Wait for the PDF to load and then print' +
|
||||||
|
'function attemptPrint() {' +
|
||||||
|
'try {' +
|
||||||
|
'// Method 1: Try to print directly' +
|
||||||
|
'if (printWindow.print) {' +
|
||||||
|
'printWindow.print();' +
|
||||||
|
'}' +
|
||||||
|
'} catch (e) {' +
|
||||||
|
'console.log("Direct print failed:", e);' +
|
||||||
|
'' +
|
||||||
|
'// Method 2: Try to access the embedded PDF\'s print functionality' +
|
||||||
|
'try {' +
|
||||||
|
'if (pdfEmbed && pdfEmbed.print) {' +
|
||||||
|
'pdfEmbed.print();' +
|
||||||
|
'} else {' +
|
||||||
|
'// Method 3: Try to print using document.execCommand (older method)' +
|
||||||
|
'if (document.execCommand) {' +
|
||||||
|
'document.execCommand("print", false, null);' +
|
||||||
|
'}' +
|
||||||
|
'}' +
|
||||||
|
'} catch (e2) {' +
|
||||||
|
'console.log("Alternative print methods failed:", e2);' +
|
||||||
|
'// 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>\';' +
|
||||||
|
'}' +
|
||||||
|
'}' +
|
||||||
|
'}' +
|
||||||
|
'' +
|
||||||
|
'// Try to print immediately and at intervals' +
|
||||||
|
'setTimeout(attemptPrint, 1000); // Wait 1 second then try to print' +
|
||||||
|
'setTimeout(attemptPrint, 3000); // Try again after 3 seconds' +
|
||||||
|
'setTimeout(attemptPrint, 5000); // Try again after 5 seconds' +
|
||||||
|
'' +
|
||||||
|
'// Also try to print when the window is focused' +
|
||||||
|
'printWindow.onfocus = function() {' +
|
||||||
|
'setTimeout(attemptPrint, 500);' +
|
||||||
|
'};' +
|
||||||
|
'' +
|
||||||
|
'// Listen for afterprint to close the window if possible' +
|
||||||
|
'if (window.matchMedia) {' +
|
||||||
|
'var mediaQueryList = window.matchMedia("print");' +
|
||||||
|
'mediaQueryList.onchange = function(mql) {' +
|
||||||
|
'if (!mql.matches) {' +
|
||||||
|
'// Print dialog was closed, close the window after a delay' +
|
||||||
|
'setTimeout(function() {' +
|
||||||
|
'window.close();' +
|
||||||
|
'}, 1000);' +
|
||||||
|
'}' +
|
||||||
|
'};' +
|
||||||
|
'}' +
|
||||||
|
'' +
|
||||||
|
'</scr' + 'ipt>' + // Split to avoid issues with closing script tag
|
||||||
|
'</body>' +
|
||||||
|
'</html>';
|
||||||
|
|
||||||
|
printWindow.document.write(htmlContent);
|
||||||
|
printWindow.document.close(); // Important: Close the document to finish loading
|
||||||
|
|
||||||
|
// Clean up the URL object after some time
|
||||||
|
setTimeout(() => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the action
|
||||||
|
actionRegistry.add("direct_print", DirectPrintAction);
|
||||||
|
|
||||||
|
// Global function for backward compatibility
|
||||||
|
window.directPrint = async function(reportName, docIds, data) {
|
||||||
|
try {
|
||||||
|
const result = await rpc("/web/direct_print", {
|
||||||
|
report_name: reportName,
|
||||||
|
docids: docIds,
|
||||||
|
data: data || {}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Create a blob from the base64 content
|
||||||
|
const binaryString = atob(result.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: result.content_type });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Create a new window for printing
|
||||||
|
const printWindow = window.open('', '_blank', 'width=800,height=600');
|
||||||
|
if (printWindow) {
|
||||||
|
printWindow.document.write(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Print</title></head>
|
||||||
|
<body style="margin:0;">
|
||||||
|
<embed src="${url}" type="application/pdf" width="100%" height="100%">
|
||||||
|
<script>
|
||||||
|
setTimeout(() => { window.print(); }, 1000);
|
||||||
|
setTimeout(() => { window.close(); }, 5000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
printWindow.document.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 10000);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Direct print error:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
12
static/src/xml/direct_print.xml
Normal file
12
static/src/xml/direct_print.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates id="template" xml:space="preserve">
|
||||||
|
<t t-name="web_direct_print.DirectPrintAction">
|
||||||
|
<div class="o_direct_print_action">
|
||||||
|
<!-- This template is minimal as the printing happens in setup() -->
|
||||||
|
<div class="text-center">
|
||||||
|
<i class="fa fa-spinner fa-spin fa-2x"/>
|
||||||
|
<div class="mt-2">Preparing print...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
0
views/__init__.py
Normal file
0
views/__init__.py
Normal file
115
views/account_move_views.xml
Normal file
115
views/account_move_views.xml
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Customer Invoice Direct Print -->
|
||||||
|
<record id="action_direct_print_customer_invoice" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Customer Invoice</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="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,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Vendor Bill Direct Print -->
|
||||||
|
<record id="action_direct_print_vendor_bill" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Vendor Bill</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="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,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Invoice with Payment Receipt Direct Print -->
|
||||||
|
<record id="action_direct_print_invoice_with_payments" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Invoice with Payments</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="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,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Payment Receipt Direct Print -->
|
||||||
|
<record id="action_direct_print_payment_receipt" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Payment Receipt</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="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,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Account Statement Direct Print -->
|
||||||
|
<record id="action_direct_print_account_statement" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Account Statement</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="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,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
3
views/assets.xml
Normal file
3
views/assets.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
</odoo>
|
||||||
10
views/direct_print_templates.xml
Normal file
10
views/direct_print_templates.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Template for direct print button that can be added to forms -->
|
||||||
|
<template id="direct_print_button" name="Direct Print Button">
|
||||||
|
<button type="button" class="btn btn-primary o_direct_print_btn"
|
||||||
|
title="Print directly to local printer">
|
||||||
|
<i class="fa fa-print"/> Direct Print
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
21
views/purchase_order_form_views.xml
Normal file
21
views/purchase_order_form_views.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Add Direct Print button to Purchase Order form view -->
|
||||||
|
<record id="purchase_order_form_direct_print" model="ir.ui.view">
|
||||||
|
<field name="name">purchase.order.form.direct.print</field>
|
||||||
|
<field name="model">purchase.order</field>
|
||||||
|
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//header" position="inside">
|
||||||
|
<button name="action_direct_print_purchase_order"
|
||||||
|
type="object"
|
||||||
|
string="Direct Print"
|
||||||
|
class="btn-primary"
|
||||||
|
invisible="state == 'cancel'"
|
||||||
|
groups="purchase.group_purchase_user"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
22
views/purchase_order_views.xml
Normal file
22
views/purchase_order_views.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="action_direct_print_purchase_order" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Request for Quotation / Order</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'purchase.report_purchaseorder',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
21
views/sale_order_form_views.xml
Normal file
21
views/sale_order_form_views.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Add Direct Print button to Sale Order form view -->
|
||||||
|
<record id="view_order_form_direct_print" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.form.direct.print</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//header" position="inside">
|
||||||
|
<button name="action_direct_print_quotation"
|
||||||
|
type="object"
|
||||||
|
string="Direct Print"
|
||||||
|
class="btn-primary"
|
||||||
|
invisible="state == 'cancel'"
|
||||||
|
groups="sales_team.group_sale_salesman"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
22
views/sale_order_views.xml
Normal file
22
views/sale_order_views.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="action_direct_print_sale_order" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Quotation / Order</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'sale.report_saleorder',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
22
views/stock_picking_form_views.xml
Normal file
22
views/stock_picking_form_views.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Add Direct Print button to Stock Picking form view -->
|
||||||
|
<record id="view_picking_form_direct_print" model="ir.ui.view">
|
||||||
|
<field name="name">stock.picking.form.direct.print</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//header" position="inside">
|
||||||
|
<button name="action_direct_print_receipt"
|
||||||
|
type="object"
|
||||||
|
string="Direct Print"
|
||||||
|
class="btn-primary"
|
||||||
|
invisible="state not in ['done', 'assigned']"
|
||||||
|
groups="stock.group_stock_user"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
160
views/stock_picking_views.xml
Normal file
160
views/stock_picking_views.xml
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="action_direct_print_delivery_slip" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Delivery Slip</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_delivery',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_direct_print_picking_operations" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Picking Operations</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_picking',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Internal Transfer Direct Print -->
|
||||||
|
<record id="action_direct_print_internal_transfer" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Internal Transfer</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
# Filter for internal transfers
|
||||||
|
internal_transfers = records.filtered(lambda r: r.picking_type_id.code == 'internal')
|
||||||
|
if internal_transfers:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_picking',
|
||||||
|
'docids': internal_transfers.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# All records if none are internal transfers
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_picking',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Receipt/Incoming Shipment Direct Print -->
|
||||||
|
<record id="action_direct_print_receipt" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Receipt</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
# Filter for incoming/receipt operations
|
||||||
|
receipts = records.filtered(lambda r: r.picking_type_id.code == 'incoming')
|
||||||
|
if receipts:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_picking',
|
||||||
|
'docids': receipts.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# All records if none are receipts
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_picking',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Stock Move Direct Print -->
|
||||||
|
<record id="action_direct_print_stock_move" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Stock Move</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.report_stock_move',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Inventory Valuation Direct Print -->
|
||||||
|
<record id="action_direct_print_inventory_valuation" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Inventory Valuation</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_inventory',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Package/Lot Tracking Direct Print -->
|
||||||
|
<record id="action_direct_print_package_content" model="ir.actions.server">
|
||||||
|
<field name="name">Direct Print Package Content</field>
|
||||||
|
<field name="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="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
if records:
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'direct_print',
|
||||||
|
'report_name': 'stock.action_report_quant_package_barcode',
|
||||||
|
'docids': records.ids,
|
||||||
|
'context': env.context,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in New Issue
Block a user