Initial commit
This commit is contained in:
162
fusion_claims/models/pdf_template_inherit.py
Normal file
162
fusion_claims/models/pdf_template_inherit.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import logging
|
||||
from io import BytesIO
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FusionSaSignatureTemplate(models.Model):
|
||||
_name = 'fusion.sa.signature.template'
|
||||
_description = 'SA Mobility Signature Position Template'
|
||||
_order = 'name'
|
||||
|
||||
name = fields.Char(string='Template Name', required=True)
|
||||
active = fields.Boolean(default=True)
|
||||
notes = fields.Text(string='Notes')
|
||||
|
||||
sa_default_sig_page = fields.Integer(string='Default Signature Page', default=2)
|
||||
|
||||
# Absolute PDF point coordinates. Y = distance from top of page.
|
||||
sa_sig_name_x = fields.Integer(string='Name X', default=105)
|
||||
sa_sig_name_y = fields.Integer(string='Name Y from top', default=97)
|
||||
sa_sig_date_x = fields.Integer(string='Date X', default=430)
|
||||
sa_sig_date_y = fields.Integer(string='Date Y from top', default=97)
|
||||
sa_sig_x = fields.Integer(string='Signature X', default=72)
|
||||
sa_sig_y = fields.Integer(string='Signature Y from top', default=68)
|
||||
sa_sig_w = fields.Integer(string='Signature Width', default=190)
|
||||
sa_sig_h = fields.Integer(string='Signature Height', default=25)
|
||||
|
||||
preview_pdf = fields.Binary(
|
||||
string='Sample PDF',
|
||||
help='Upload a sample SA Mobility approval form to preview signature placement.',
|
||||
attachment=True,
|
||||
)
|
||||
preview_pdf_filename = fields.Char(string='PDF Filename')
|
||||
preview_pdf_page = fields.Integer(
|
||||
string='Preview Page', default=0,
|
||||
help='Page to render preview for. 0 = use Default Signature Page.',
|
||||
)
|
||||
preview_image = fields.Binary(
|
||||
string='Preview', readonly=True,
|
||||
compute='_compute_preview_image',
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
'preview_pdf', 'preview_pdf_page', 'sa_default_sig_page',
|
||||
'sa_sig_name_x', 'sa_sig_name_y',
|
||||
'sa_sig_date_x', 'sa_sig_date_y',
|
||||
'sa_sig_x', 'sa_sig_y', 'sa_sig_w', 'sa_sig_h',
|
||||
)
|
||||
def _compute_preview_image(self):
|
||||
for rec in self:
|
||||
if not rec.preview_pdf:
|
||||
rec.preview_image = False
|
||||
continue
|
||||
try:
|
||||
rec.preview_image = rec._render_preview()
|
||||
except Exception as e:
|
||||
_logger.warning("SA template preview render failed: %s", e)
|
||||
rec.preview_image = False
|
||||
|
||||
def _render_preview(self):
|
||||
"""Render the sample PDF page with colored markers for all signature positions."""
|
||||
self.ensure_one()
|
||||
from odoo.tools.pdf import PdfFileReader
|
||||
|
||||
pdf_bytes = base64.b64decode(self.preview_pdf)
|
||||
reader = PdfFileReader(BytesIO(pdf_bytes))
|
||||
num_pages = reader.getNumPages()
|
||||
|
||||
page_num = self.preview_pdf_page or self.sa_default_sig_page or 2
|
||||
page_idx = page_num - 1
|
||||
if page_idx < 0 or page_idx >= num_pages:
|
||||
return False
|
||||
|
||||
try:
|
||||
from pdf2image import convert_from_bytes
|
||||
except ImportError:
|
||||
_logger.warning("pdf2image not installed")
|
||||
return False
|
||||
|
||||
images = convert_from_bytes(
|
||||
pdf_bytes, first_page=page_idx + 1, last_page=page_idx + 1, dpi=150,
|
||||
)
|
||||
if not images:
|
||||
return False
|
||||
|
||||
from PIL import ImageDraw, ImageFont
|
||||
img = images[0]
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
page = reader.getPage(page_idx)
|
||||
page_w_pts = float(page.mediaBox.getWidth())
|
||||
page_h_pts = float(page.mediaBox.getHeight())
|
||||
img_w, img_h = img.size
|
||||
sx = img_w / page_w_pts
|
||||
sy = img_h / page_h_pts
|
||||
|
||||
try:
|
||||
font_b = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
|
||||
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12)
|
||||
font_sm = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 10)
|
||||
except Exception:
|
||||
font_b = font = font_sm = ImageFont.load_default()
|
||||
|
||||
def _draw_sample_text(label, x_pts, y_top_pts, color, sample_text):
|
||||
px_x = int(x_pts * sx)
|
||||
px_y = int(y_top_pts * sy)
|
||||
draw.text((px_x, px_y - 16), sample_text, fill=color, font=font_b)
|
||||
draw.text((px_x, px_y + 2), label, fill=color, font=font_sm)
|
||||
|
||||
def _draw_box(label, x_pts, y_top_pts, w_pts, h_pts, color):
|
||||
px_x = int(x_pts * sx)
|
||||
px_y = int(y_top_pts * sy)
|
||||
pw = int(w_pts * sx)
|
||||
ph = int(h_pts * sy)
|
||||
for off in range(3):
|
||||
draw.rectangle(
|
||||
[px_x - off, px_y - off, px_x + pw + off, px_y + ph + off],
|
||||
outline=color,
|
||||
)
|
||||
draw.text((px_x + 4, px_y + 4), label, fill=color, font=font_sm)
|
||||
|
||||
_draw_sample_text(
|
||||
"Name", self.sa_sig_name_x, self.sa_sig_name_y,
|
||||
'blue', "John Smith",
|
||||
)
|
||||
_draw_sample_text(
|
||||
"Date", self.sa_sig_date_x, self.sa_sig_date_y,
|
||||
'purple', "2026-02-17",
|
||||
)
|
||||
_draw_box(
|
||||
"Signature", self.sa_sig_x, self.sa_sig_y,
|
||||
self.sa_sig_w, self.sa_sig_h, 'red',
|
||||
)
|
||||
|
||||
buf = BytesIO()
|
||||
img.save(buf, format='PNG')
|
||||
return base64.b64encode(buf.getvalue())
|
||||
|
||||
def get_sa_coordinates(self, page_h=792):
|
||||
"""Convert to ReportLab bottom-origin coordinates.
|
||||
|
||||
Template stores Y as distance from TOP of page.
|
||||
ReportLab uses Y from BOTTOM.
|
||||
For text (name/date): baseline Y = page_h - y_from_top
|
||||
For signature image: drawImage Y is bottom-left corner,
|
||||
so Y = page_h - y_from_top - height
|
||||
"""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name_x': self.sa_sig_name_x,
|
||||
'name_y': page_h - self.sa_sig_name_y,
|
||||
'date_x': self.sa_sig_date_x,
|
||||
'date_y': page_h - self.sa_sig_date_y,
|
||||
'sig_x': self.sa_sig_x,
|
||||
'sig_y': page_h - self.sa_sig_y - self.sa_sig_h,
|
||||
'sig_w': self.sa_sig_w,
|
||||
'sig_h': self.sa_sig_h,
|
||||
}
|
||||
Reference in New Issue
Block a user