# -*- coding: utf-8 -*- import base64 import io import os import time from dateutil.relativedelta import relativedelta from datetime import timedelta from reportlab.lib.utils import ImageReader from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.rl_config import TTFSearchPath from reportlab.pdfgen import canvas from reportlab.platypus import Paragraph from reportlab.lib.styles import ParagraphStyle from reportlab.pdfbase.pdfmetrics import stringWidth from PIL import UnidentifiedImageError from odoo import api, fields, models, _, Command from odoo.exceptions import UserError, ValidationError from odoo.tools.pdf import PdfFileReader, PdfFileWriter, PdfReadError, reshape_text import logging _logger = logging.getLogger(__name__) # Helper function copied from sign/models/sign_request.py def _fix_image_transparency(image): pixels = image.load() for x in range(image.size[0]): for y in range(image.size[1]): if pixels[x, y] == (0, 0, 0, 0): pixels[x, y] = (255, 255, 255, 0) class SignRequest(models.Model): _inherit = "sign.request" def _sign(self): """ Override to generate sequence numbers when request is signed """ # We perform sequence generation BEFORE calling super()._sign() # because super()._sign() triggers _send_completed_document() immediately. # We need the sequence values to be ready before the document is generated and sent. # We only generate if we are about to complete the signing process. # The logic in _post_fill_request_item calls _sign only when all items are completed. _logger.info("SignSequenceField: Starting sequence generation for sign.request %s", self.ids) for request in self: # Generate sequence numbers for sequence items # Use template_id.document_ids.sign_item_ids to be safe signer_items = request.template_id.document_ids.sign_item_ids _logger.info("SignSequenceField: Found %s total sign items on template documents for request %s", len(signer_items), request.id) for sign_item in signer_items: _logger.debug("SignSequenceField: Checking item %s: type=%s, sequence_id=%s", sign_item.id, sign_item.type_id.item_type, sign_item.sequence_id.id if sign_item.sequence_id else 'None') if sign_item.type_id.item_type == 'sequence' and sign_item.sequence_id: _logger.info("SignSequenceField: Processing sequence item %s (ID: %s) for request %s", sign_item.name, sign_item.id, request.id) # Find the responsible request item (signer) # Use role_id to match. # Fallback to the first signer if no specific matching signer is found (e.g. role is "Anyone") request_items = request.request_item_ids.filtered(lambda r: r.role_id == sign_item.responsible_id) if not request_items: _logger.info("SignSequenceField: No matching request items for role %s, falling back to first signer", sign_item.responsible_id.name) request_items = request.request_item_ids[:1] if not request_items: _logger.warning("SignSequenceField: No request items found at all for request %s", request.id) continue # We only generate if it hasn't been generated yet for this sign_item across the whole request # to prevent duplicate generation if multiple request_items were somehow matched. existing_value = request.env['sign.request.item.value'].search([ ('sign_request_id', '=', request.id), ('sign_item_id', '=', sign_item.id) ], limit=1) placeholder = (sign_item.type_id.placeholder or "Sequence Number").strip().lower() current_val = str(existing_value.value or "").strip().lower() _logger.debug("SignSequenceField: existing_value check for item %s: '%s' (placeholder: '%s')", sign_item.id, current_val, placeholder) # If it exists but value is empty/false OR it equals the placeholder # we must regenerate it. if not existing_value or not current_val or current_val == placeholder: _logger.info("SignSequenceField: Generating sequence for item %s", sign_item.id) new_seq = sign_item.sequence_id.next_by_id() _logger.info("SignSequenceField: Generated new sequence: %s", new_seq) if new_seq: if existing_value: # Update existing empty record _logger.debug("SignSequenceField: Updating existing value record %s", existing_value.id) existing_value.write({'value': new_seq}) else: # Create value for the first matching signer _logger.debug("SignSequenceField: Creating new value record for signer %s", request_items[0].id) request.env['sign.request.item.value'].create({ 'sign_request_item_id': request_items[0].id, 'sign_item_id': sign_item.id, 'value': new_seq }) # Rename the document if requested # User asked to put sequence in prefix of document name. # request.reference is the document name. # Check if already renamed to avoid double prefixing if not request.reference.startswith(f"{new_seq} - "): _logger.info("SignSequenceField: Prefixing document name with sequence: %s", new_seq) request.write({'reference': f"{new_seq} - {request.reference}"}) else: _logger.error("SignSequenceField: next_by_id() returned None for sequence %s", sign_item.sequence_id.id) else: _logger.info("SignSequenceField: Sequence already exists for item %s: %s", sign_item.id, existing_value.value) return super()._sign()