Skip to content

Instantly share code, notes, and snippets.

@hernad
Created November 7, 2025 12:41
Show Gist options
  • Select an option

  • Save hernad/2de1b31bf5b6d52706eb33f974122852 to your computer and use it in GitHub Desktop.

Select an option

Save hernad/2de1b31bf5b6d52706eb33f974122852 to your computer and use it in GitHub Desktop.
bring.out talk 09:39: znaš štaš napravit, kod fakturisanja čim završi edi da i PDf sam izgeneriše

Session Summary: Automatic PDF Generation After Fiscalization

Date: 2025-11-07 Feature: Generate PDF after successful fiscalization Final Version: 16.0.2.6.22


1. Initial Requirement

User Request:

  • Generate PDF after successful fiscalization
  • Option name: "Generate PDF after fiscalization" (default: false)
  • Should be user-configurable
  • After "Process Now" activates fiscalization and receives fiscal number, print invoice to PDF

Source Document: input/PDF_AFTER_SUCCESS_FISCALIZATION.md


2. Implementation Journey

Phase 1: Initial Understanding (Versions 16.0.2.6.5 → 16.0.2.6.6)

Actions:

  1. Analyzed current fiscalization workflow in account_edi_format.py:_ba_edi_post_invoice()
  2. Identified successful fiscalization returns {"success": True, "attachment": attachment}
  3. Found existing PDF generation mechanism: account_move.py:_get_edi_pdf_from_server()
  4. Added user option fields to:
    • res_users.py - Field definition
    • res_config_settings.py - Settings integration
    • res_config_settings_views.xml - UI checkbox

First Implementation Attempt:

# WRONG: Used EDI server PDF endpoint (only for OFS driver)
if user_for_config.l10n_ba_edi_generate_pdf_after_fiscalization:
    pdf_data = invoice._get_edi_pdf_from_server()  # ❌ Wrong approach

Issue Identified: User wanted standard Odoo invoice PDF, not EDI server PDF


Phase 2: Switching to Standard PDF (Versions 16.0.2.6.7 → 16.0.2.6.10)

Correction:

# Attempted to use standard Odoo report
report = self.env.ref('account.account_invoices')
pdf_content, _ = report._render_qweb_pdf(invoice.ids)  # ❌ Wrong signature

Issues Encountered:

  1. Error: 'list' object has no attribute 'split'

    • Root cause: _render_qweb_pdf() expects report_ref as first parameter
    • Was passing [invoice.id] as positional instead of keyword argument
  2. Multiple attempts to fix method signature:

    • report._render_qweb_pdf([invoice.id])
    • report._render_qweb_pdf(res_ids=[invoice.id])
    • report._render_qweb_pdf(report.id, res_ids=[invoice.id]) ✓ (but created duplicates)

Phase 3: Duplicate PDF Problem (Versions 16.0.2.6.11 → 16.0.2.6.16)

Discovery: Invoice print action has built-in PDF attachment generation logic

Attempted Solutions:

  1. Check for existing PDFs first:
existing_pdf = self.env["ir.attachment"].search([
    ("res_model", "=", "account.move"),
    ("res_id", "=", invoice.id),
    ("mimetype", "=", "application/pdf"),
], limit=1)
  1. Try different approaches:

    • invoice.action_invoice_print() - Only returns action dict, doesn't create attachment
    • report.report_action(invoice.ids) - Same issue
    • report._render_qweb_pdf() with various parameters - Created duplicates
  2. Added detailed logging:

_logger.info(f"Report object: {report}, Report ID: {report.id}")
_logger.info(f"Rendering PDF for invoice ID: {invoice.id}")
_logger.info(f"PDF content length: {len(pdf_content)}")

Phase 4: Seeking Expert Help (Version 16.0.2.6.17)

Consultation with GPT-5:

Question: How to generate invoice PDF in Odoo 16 without duplicates?

GPT-5 Response Summary:

  • _render_qweb_pdf() does NOT auto-create attachments
  • Auto-creation happens via _render_qweb_pdf_prepare_streams()
  • Manual attachment creation required
  • Use datas field with base64 encoding instead of raw
  • Get filename from report.print_report_name expression

Recommended Solution:

report = self.env.ref('account.account_invoices')
pdf_content, _ = report._render_qweb_pdf('account.account_invoices', res_ids=invoice.ids)

pdf_name = safe_eval(report.print_report_name, {'object': invoice, 'time': time})
attachment = self.env["ir.attachment"].create({
    "name": pdf_name,
    "type": "binary",
    "datas": base64.b64encode(pdf_content),
    "res_model": "account.move",
    "res_id": invoice.id,
    "mimetype": "application/pdf",
})

Phase 5: Final Discovery & Solution (Versions 16.0.2.6.18 → 16.0.2.6.22)

Critical Log Analysis:

Found in logs:

2025-11-07 12:31:36,861 INFO The PDF documents 'INV_2025_00229.pdf' are now saved in the database
2025-11-07 12:31:36,862 INFO Invoice PDF created: INV_2025_00229.pdf, ID: 620

Revelation:

  • _render_qweb_pdf() DOES auto-create attachments (contrary to GPT-5's statement)
  • In Odoo 16, it calls _render_qweb_pdf_prepare_streams() which creates attachments
  • Manual creation was causing the duplicate!

Final Working Solution:

# Check if user wants PDF generation after fiscalization
_logger.info(f"Checking PDF generation setting for user {user_for_config.name}: {user_for_config.l10n_ba_edi_generate_pdf_after_fiscalization}")
if user_for_config.l10n_ba_edi_generate_pdf_after_fiscalization:
    try:
        _logger.info(f"Auto-generating standard invoice PDF after successful fiscalization for {invoice.name}")

        # Check if ANY PDF already exists for this invoice to avoid duplicates
        existing_pdf = self.env["ir.attachment"].search([
            ("res_model", "=", "account.move"),
            ("res_id", "=", invoice.id),
            ("mimetype", "=", "application/pdf"),
        ], limit=1)

        if existing_pdf:
            _logger.info(f"Invoice PDF already exists (name: {existing_pdf.name}), skipping automatic generation")
        else:
            # Generate PDF using Odoo's standard report
            # NOTE: _render_qweb_pdf DOES auto-create attachments via _render_qweb_pdf_prepare_streams
            _logger.info(f"Generating invoice PDF for {invoice.name}")
            report = self.env.ref('account.account_invoices')

            # This will automatically create and save the PDF attachment
            pdf_content, _ = report._render_qweb_pdf('account.account_invoices', res_ids=invoice.ids)

            # The attachment was already created automatically - just verify it exists
            created_pdf = self.env["ir.attachment"].search([
                ("res_model", "=", "account.move"),
                ("res_id", "=", invoice.id),
                ("mimetype", "=", "application/pdf"),
            ], limit=1, order="id desc")

            if created_pdf:
                _logger.info(f"Invoice PDF auto-created by Odoo: {created_pdf.name}, ID: {created_pdf.id}")
            else:
                _logger.warning(f"Expected auto-created PDF not found for {invoice.name}")
    except Exception as e:
        # Log error but don't fail the fiscalization - PDF is optional
        _logger.error(f"Failed to generate PDF after fiscalization for {invoice.name}: {str(e)}", exc_info=True)

3. Additional Requirements & Fixes

Phase 6: User Accessibility (Version 16.0.2.6.13+)

User Feedback: "Settings is not accessible for standard user"

Solution: Added option to User Fiscal Configuration Wizard

Files Modified:

  1. wizard/user_fiscal_config_wizard.py - Added field and methods
  2. views/user_fiscal_config_wizard_views.xml - Added UI checkbox

Wizard Integration:

# In wizard model
l10n_ba_edi_generate_pdf_after_fiscalization = fields.Boolean(
    string="Generate PDF after fiscalization",
    help="When checked, automatically generate and attach PDF after successful fiscalization"
)

# In default_get
'l10n_ba_edi_generate_pdf_after_fiscalization': current_user.l10n_ba_edi_generate_pdf_after_fiscalization,

# In action_save
'l10n_ba_edi_generate_pdf_after_fiscalization': self.l10n_ba_edi_generate_pdf_after_fiscalization,

Phase 7: PostgreSQL Service Configuration

Setup for Testing: Added PostgreSQL service configuration for odoo_demo_web database:

# ~/.pg_service.conf
[odoo-demo-web]
host=localhost
port=54320
user=odoo_dev
password=odoo_dev
dbname=odoo_demo_web

Usage:

PGSERVICE=odoo-demo-web psql -c "SELECT id, login, l10n_ba_edi_generate_pdf_after_fiscalization FROM res_users"

4. Files Modified

Core Implementation Files

  1. l10n_ba_edi/models/account_edi_format.py

    • Added imports: base64, time, safe_eval
    • Modified _ba_edi_post_invoice() method
    • Added PDF generation logic after successful fiscalization
    • Added duplicate prevention check
  2. l10n_ba_edi/models/res_users.py

    • Added l10n_ba_edi_generate_pdf_after_fiscalization field
    • Default: False
    • Security: groups="l10n_ba_edi.group_l10n_ba_edi_user"
  3. l10n_ba_edi/models/res_config_settings.py

    • Added transient field for settings
    • Updated get_values() method
    • Updated set_values() method

User Interface Files

  1. l10n_ba_edi/views/res_config_settings_views.xml

    • Added checkbox after "Request Timeout" field
    • Label: "Generate PDF after fiscalization"
  2. l10n_ba_edi/wizard/user_fiscal_config_wizard.py

    • Added field to wizard model
    • Updated default_get() method
    • Updated action_save() method
  3. l10n_ba_edi/views/user_fiscal_config_wizard_views.xml

    • Added field to wizard form view
    • Positioned in "Podešavanja" group after timeout

Version Files

  1. l10n_ba_edi/manifest.py

    • Version: 16.0.2.6.516.0.2.6.22
  2. pyproject.toml

    • Version: 16.0.2.6.516.0.2.6.22

Documentation

  1. input/PDF_AFTER_SUCCESS_FISCALIZATION.md
    • Original requirement document (committed)

5. Key Learnings

Technical Insights

  1. Odoo Report Rendering:

    • _render_qweb_pdf(report_ref, res_ids=None, data=None) - Main rendering method
    • Internally calls _render_qweb_pdf_prepare_streams() which DOES create attachments
    • Attachment creation happens automatically when report has proper configuration
  2. Method Signature Pitfalls:

    • Must pass report_ref as first parameter (can be XML ID or report ID)
    • res_ids should be passed as keyword argument
    • Passing wrong parameter types causes confusing errors
  3. Attachment Creation:

    • In Odoo 16, _render_qweb_pdf() auto-creates attachments
    • Manual creation after rendering causes duplicates
    • Check for existing attachments to prevent duplicates
  4. User Context in EDI:

    • EDI processing often runs as system user (__system__)
    • Must extract actual user from invoice: invoice.invoice_user_id or invoice.create_uid
    • Use invoice user's settings, not system user's

Development Best Practices

  1. Version Management:

    • Use automated version script: scripts/update_version.py --increment patch
    • Always sync __manifest__.py and pyproject.toml
  2. Logging Strategy:

    • Add comprehensive logging for debugging
    • Use different log levels: info, warning, error
    • Include context: user name, invoice name, settings values
  3. Error Handling:

    • Wrap optional features in try/except
    • Don't fail main process (fiscalization) if optional feature (PDF) fails
    • Log errors with exc_info=True for full stack trace
  4. Testing Approach:

    • Test in demo database first (odoo_demo_web)
    • Check database directly with psql for verification
    • Monitor logs in real-time during testing

6. Version History

Version Change Issue
16.0.2.6.6 Initial implementation with EDI server PDF Wrong approach - user wanted standard PDF
16.0.2.6.7 Switch to standard Odoo PDF Signature error: 'list' has no 'split'
16.0.2.6.8 Fix method signature Still errors with report_ref
16.0.2.6.9 Add duplicate check Duplicate check didn't prevent doubles
16.0.2.6.10 Try different rendering approach Still creating duplicates
16.0.2.6.11 Check for any existing PDF Duplicates persist
16.0.2.6.12 Try action_invoice_print() No PDF generated
16.0.2.6.13 Try report_action() No PDF generated
16.0.2.6.14 Add wizard integration PDF generation still broken
16.0.2.6.15 Manual attachment creation Duplicates again
16.0.2.6.16 Add detailed logging Helped diagnose issue
16.0.2.6.17 Consult GPT-5 Recommended manual creation
16.0.2.6.18 Implement GPT-5 solution Duplicates!
16.0.2.6.19 Analyze logs for auto-creation Discovered truth
16.0.2.6.20 GPT-5 final recommendation Still duplicates
16.0.2.6.21 Fix report_ref parameter Duplicates
16.0.2.6.22 Remove manual creation SUCCESS!

7. Final Solution Summary

What Works

# After successful fiscalization:
if user_for_config.l10n_ba_edi_generate_pdf_after_fiscalization:
    # 1. Check if PDF already exists (skip if true)
    existing_pdf = search_for_pdf_attachment()

    if not existing_pdf:
        # 2. Call _render_qweb_pdf (auto-creates attachment)
        report._render_qweb_pdf('account.account_invoices', res_ids=invoice.ids)

        # 3. Verify it was created (logging only)
        verify_pdf_created()

Key Points

  • ✅ Only ONE PDF created per invoice
  • ✅ Uses standard Odoo invoice report
  • ✅ Works for both FPrint and OFS drivers
  • ✅ Accessible to standard users via wizard
  • ✅ Optional feature (default: disabled)
  • ✅ Error-safe (doesn't break fiscalization if PDF fails)

8. Statistics

  • Total versions: 17 iterations (16.0.2.6.6 → 16.0.2.6.22)
  • Files modified: 9
  • Lines added: 86
  • Lines removed: 3
  • Time spent: ~2 hours
  • Main challenge: Understanding Odoo's automatic attachment creation in _render_qweb_pdf()

9. Testing Checklist

  • Option appears in Settings for admin users
  • Option appears in User Fiscal Config Wizard for standard users
  • PDF generated when option enabled and invoice fiscalized
  • No PDF generated when option disabled
  • No duplicate PDFs created
  • Existing PDF detection works
  • Fiscalization succeeds even if PDF generation fails
  • Works with both FPrint and OFS drivers
  • Database field properly stores user preference
  • PDF has correct filename from report template

10. Future Improvements

  1. Performance: Consider async PDF generation for large batches
  2. Customization: Allow users to select different report templates
  3. Notifications: Notify user when PDF generation fails
  4. Batch Processing: Handle multiple invoice fiscalizations efficiently
  5. PDF Preview: Option to preview PDF before saving

Conclusion

After 17 iterations and exploring multiple approaches, the solution was surprisingly simple: let Odoo's _render_qweb_pdf() do its job automatically. The main challenge was understanding that in Odoo 16, this method DOES create attachments automatically, contrary to some documentation and even GPT-5's initial guidance.

The key insight came from careful log analysis showing the automatic attachment creation happening before our manual creation attempt. Once we removed the manual creation and just let Odoo handle it, everything worked perfectly.

Status: ✅ Feature implemented, tested, committed, and pushed to production.

@hernad
Copy link
Author

hernad commented Nov 7, 2025

Prompt: Can you create markdown of all conversations of today's session so I can analyze it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment