סקירה כללית

מודול החתימה הדיגיטלית מאפשר לאסוף חתימות אלקטרוניות על כל סוג של ישות במערכת TechLab Pro, כולל חשבוניות, חוזים, הצעות מחיר, תיקונים ועוד.

Polymorphic Design

חיבור לכל ישות במערכת דרך entity_type + entity_id

מספר חותמים

תמיכה בחתימות מרובות עם סדר חתימה מוגדר

חתימה חיצונית

שליחת קישור לחתימה ללקוחות ללא צורך בהרשמה

סטטיסטיקות המודול

5
טבלאות DB
28
API Endpoints
10
סוגי ישויות
4
סוגי חתימה

תכונות עיקריות

סוגי חתימה
  • חתימה מצוירת (drawn) - ציור על Canvas עם עכבר או מגע
  • חתימה מודפסת (typed) - הקלדת שם בפונט חתימה
  • תמונת חתימה (image) - העלאת תמונה של חתימה
  • חתימה דיגיטלית (certificate) - חתימה עם תעודה דיגיטלית
ישויות נתמכות
  • invoice - חשבונית
  • contract - חוזה
  • quote - הצעת מחיר
  • repair - תיקון
  • customer - לקוח
  • document - מסמך
  • project - פרויקט
  • purchase_order - הזמנת רכש
תכונות מתקדמות
  • תבניות חתימה מותאמות אישית
  • סדר חתימות (parallel/sequential)
  • תאריך תפוגה אוטומטי
  • תזכורות אוטומטיות
  • אימות חתימה ציבורי
  • Hash verification
  • לוג פעולות מלא (Audit)
  • תמיכה ב-RTL (עברית)
  • Responsive למובייל

מסד הנתונים

דיאגרמת ישויות (ERD)

┌─────────────────────────┐     ┌─────────────────────────┐
│ digital_signature_      │     │ digital_signature_      │
│ templates               │────▶│ requests                │
├─────────────────────────┤     ├─────────────────────────┤
│ id (PK)                 │     │ id (PK)                 │
│ template_name           │     │ request_number (UNIQUE) │
│ template_code (UNIQUE)  │     │ template_id (FK)        │
│ entity_type             │     │ entity_type ◀──────┐    │
│ required_signatures     │     │ entity_id   ◀──────┤    │
│ signature_order         │     │ title               │    │  Polymorphic
│ allow_drawn_signature   │     │ status              │    │  Relationship
│ ...                     │     │ customer_id (FK)    │    │
└─────────────────────────┘     │ requested_by (FK)   │    │
                                └─────────────────────────┘
                                          │
                    ┌─────────────────────┼─────────────────────┐
                    ▼                     ▼                     ▼
┌─────────────────────────┐  ┌─────────────────────────┐  ┌─────────────────────────┐
│ digital_signature_      │  │ digital_signatures      │  │ digital_signature_      │
│ signers                 │  │                         │  │ audit                   │
├─────────────────────────┤  ├─────────────────────────┤  ├─────────────────────────┤
│ id (PK)                 │  │ id (PK)                 │  │ id (PK)                 │
│ request_id (FK)         │  │ signature_number (UNQ)  │  │ request_id (FK)         │
│ signer_name             │──│ request_id (FK)         │  │ signature_id (FK)       │
│ signer_email            │  │ signer_id (FK)          │──│ entity_type             │
│ signer_type             │  │ entity_type             │  │ entity_id               │
│ signing_order           │  │ entity_id               │  │ action                  │
│ status                  │  │ signature_type          │  │ actor_type              │
│ access_token            │  │ signature_data (Base64) │  │ ip_address              │
│ signature_id (FK)       │◀─│ status                  │  │ created_at              │
└─────────────────────────┘  │ ip_address              │  └─────────────────────────┘
                             │ signed_at               │
                             └─────────────────────────┘

טבלת digital_signature_templates

תבניות חתימה מגדירות את הדרישות לחתימה עבור כל סוג ישות.

עמודה סוג תיאור
id INTEGER PK מזהה ייחודי
template_name VARCHAR(100) שם התבנית
template_code VARCHAR(50) UNIQUE קוד ייחודי (INV_APPROVAL)
entity_type VARCHAR(50) סוג הישות (invoice, contract...)
required_signatures_count INTEGER מספר חתימות נדרשות
signature_order VARCHAR(20) סדר חתימות: any / sequential
allow_drawn_signature BOOLEAN האם לאפשר חתימה מצוירת
signature_valid_days INTEGER תוקף החתימה בימים (365)
request_expires_hours INTEGER תפוגת בקשה בשעות (72)

טבלת digital_signature_requests

בקשות לחתימה - מייצגות בקשה ספציפית לחתום על ישות.

עמודה סוג תיאור
request_number VARCHAR(50) UNIQUE מספר בקשה: SIG-REQ-2025-00001
entity_type VARCHAR(50) Polymorphic: סוג הישות
entity_id INTEGER Polymorphic: מזהה הישות
status VARCHAR(30) pending, sent, partially_signed, completed, expired, cancelled
access_token VARCHAR(128) Token לגישה חיצונית

אינדקסים

-- אינדקס לשאילתות פולימורפיות
CREATE INDEX idx_signature_request_entity ON digital_signature_requests (entity_type, entity_id);
CREATE INDEX idx_digital_signature_entity ON digital_signatures (entity_type, entity_id);
CREATE INDEX idx_signature_audit_entity ON digital_signature_audit (entity_type, entity_id);

-- אינדקס ללוג פעולות
CREATE INDEX idx_signature_audit_request ON digital_signature_audit (request_id);
CREATE INDEX idx_signature_audit_action ON digital_signature_audit (action);

מודלים (Models)

DigitalSignatureTemplate

תבנית המגדירה את דרישות החתימה לסוג ישות מסוים.

class DigitalSignatureTemplate(db.Model):
    __tablename__ = 'digital_signature_templates'

    id = db.Column(db.Integer, primary_key=True)
    template_name = db.Column(db.String(100), nullable=False)
    template_code = db.Column(db.String(50), unique=True, nullable=False)
    entity_type = db.Column(db.String(50), nullable=False)

    # Signature Requirements
    required_signatures_count = db.Column(db.Integer, default=1)
    signature_order = db.Column(db.String(20), default='any')  # any, sequential

    # Allowed Signature Types
    allow_drawn_signature = db.Column(db.Boolean, default=True)
    allow_typed_signature = db.Column(db.Boolean, default=True)
    allow_image_signature = db.Column(db.Boolean, default=True)

    # Methods
    def to_dict(self) -> dict

DigitalSignatureRequest

בקשה לחתימה על ישות ספציפית - הליבה של המודול.

class DigitalSignatureRequest(db.Model):
    __tablename__ = 'digital_signature_requests'

    # Polymorphic Relationship - התחברות לכל ישות
    entity_type = db.Column(db.String(50), nullable=False)  # invoice, contract, repair...
    entity_id = db.Column(db.Integer, nullable=False)       # ID of the entity

    # Composite Index for fast lookups
    __table_args__ = (
        db.Index('idx_signature_request_entity', 'entity_type', 'entity_id'),
    )

    # Properties
    @property
    def is_expired(self) -> bool

    @property
    def completion_percentage(self) -> int

    # Methods
    def generate_access_token(self, expires_hours=72) -> str
    def to_dict(self, include_signatures=False, include_signers=False) -> dict

DigitalSignature

החתימה עצמה - מכילה את נתוני החתימה ומידע על החותם.

class DigitalSignature(db.Model):
    __tablename__ = 'digital_signatures'

    signature_number = db.Column(db.String(50), unique=True)  # SIG-2025-00001
    signature_type = db.Column(db.String(30))  # drawn, typed, image, certificate
    signature_data = db.Column(db.Text)        # Base64 encoded signature
    signature_hash = db.Column(db.String(128)) # SHA-512 for verification

    # Signer Information
    signer_name = db.Column(db.String(200), nullable=False)
    signer_email = db.Column(db.String(200))
    signer_type = db.Column(db.String(30))  # customer, company, witness

    # Legal Information
    ip_address = db.Column(db.String(45))
    user_agent = db.Column(db.String(500))
    geolocation = db.Column(db.JSON)
    consent_given = db.Column(db.Boolean, default=True)

    # Methods
    def generate_hash(self, document_hash=None) -> str
    def verify_hash(self, document_hash=None) -> bool

Helper Functions

# יצירת מספר בקשה ייחודי
def generate_signature_request_number() -> str:
    # Returns: SIG-REQ-2025-00001

# יצירת מספר חתימה ייחודי
def generate_signature_number() -> str:
    # Returns: SIG-2025-00001

# קבלת חתימות לישות
def get_entity_signatures(entity_type: str, entity_id: int) -> List[DigitalSignature]

# קבלת בקשות חתימה לישות
def get_entity_signature_requests(entity_type: str, entity_id: int) -> List[DigitalSignatureRequest]

# יצירת רשומת Audit
def create_audit_log(request_id=None, action=None, ...) -> DigitalSignatureAudit

API Reference

כל ה-API endpoints נמצאים תחת /api/digital-signature/

Templates API

GET /api/digital-signature/templates

קבלת רשימת תבניות חתימה

Query params: entity_type, is_active, search, page, per_page
POST /api/digital-signature/templates

יצירת תבנית חדשה

Required: template_name, template_code, entity_type
PUT /api/digital-signature/templates/{id}

עדכון תבנית

Signature Requests API

GET /api/digital-signature/requests

קבלת רשימת בקשות חתימה

Query params: entity_type, entity_id, status, customer_id, priority, search
POST /api/digital-signature/requests

יצירת בקשת חתימה חדשה

{
  "entity_type": "invoice",
  "entity_id": 123,
  "title": "אישור חשבונית #123",
  "description": "נא לחתום על החשבונית המצורפת",
  "template_id": 1,
  "priority": "normal",
  "signers": [
    {
      "signer_name": "ישראל ישראלי",
      "signer_email": "israel@example.com",
      "signer_type": "customer"
    }
  ]
}
POST /api/digital-signature/requests/{id}/send

שליחת בקשה לחתימה (email/sms)

POST /api/digital-signature/requests/{id}/cancel

ביטול בקשת חתימה

Signatures API

POST /api/digital-signature/sign

ביצוע חתימה

{
  "request_id": 1,
  "signer_id": 1,
  "signature_type": "drawn",
  "signature_data": "data:image/png;base64,iVBORw0...",
  "signer_name": "ישראל ישראלי",
  "signer_email": "israel@example.com",
  "consent_given": true,
  "consent_text": "אני מאשר/ת שקראתי והבנתי את המסמך"
}
POST /api/digital-signature/signatures/{id}/revoke

ביטול חתימה

Entity-Based API (Polymorphic)

GET /api/digital-signature/entity/{type}/{id}/signatures

קבלת כל החתימות לישות מסוימת

GET /api/digital-signature/entity/{type}/{id}/requests

קבלת כל בקשות החתימה לישות

POST /api/digital-signature/entity/{type}/{id}/request

יצירת בקשת חתימה לישות

Public Endpoints (ללא אימות)

נקודות קצה אלו לא דורשות התחברות ומיועדות לשימוש ציבורי
GET /api/digital-signature/verify/{signature_number}

אימות חתימה ציבורי

// Response
{
  "valid": true,
  "signature_number": "SIG-2025-00001",
  "signer_name": "ישראל ישראלי",
  "signed_at": "2025-12-01T14:30:00",
  "signature_type": "drawn",
  "entity_type": "invoice"
}
GET /api/digital-signature/constants

קבלת קבועים (entity_types, statuses)

GET /api/digital-signature/signing-page/{token}

קבלת נתוני דף חתימה ציבורי

Use Cases

Use Case 1: חתימה על חשבונית

תרחיש: לקוח צריך לאשר חשבונית לפני שליחה

  1. יצירת בקשת חתימה לחשבונית #123
  2. הגדרת הלקוח כחותם
  3. שליחת קישור חתימה במייל
  4. הלקוח פותח את הקישור וחותם
  5. החשבונית מסומנת כ"מאושרת"
// יצירת בקשת חתימה לחשבונית
const response = await fetch('/api/digital-signature/requests', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        entity_type: 'invoice',
        entity_id: 123,
        title: 'אישור חשבונית #123',
        signers: [{
            signer_name: 'ישראל ישראלי',
            signer_email: 'customer@example.com',
            signer_type: 'customer'
        }]
    })
});
Use Case 2: חתימה על חוזה עם מספר צדדים

תרחיש: חוזה שירות דורש חתימת לקוח + נציג חברה

  1. יצירת תבנית "חוזה שירות" עם 2 חתימות
  2. יצירת בקשת חתימה עם 2 חותמים
  3. הגדרת סדר חתימות (לקוח ראשון)
  4. שליחה ללקוח
  5. לאחר חתימת הלקוח - שליחה לנציג החברה
  6. החוזה מושלם כשכולם חתמו
// יצירת בקשה עם מספר חותמים
const response = await fetch('/api/digital-signature/requests', {
    method: 'POST',
    body: JSON.stringify({
        entity_type: 'contract',
        entity_id: 456,
        title: 'חוזה שירות שנתי',
        template_id: 2, // תבנית חוזה
        signers: [
            {
                signer_name: 'ישראל ישראלי',
                signer_email: 'customer@example.com',
                signer_type: 'customer',
                signing_order: 1
            },
            {
                signer_name: 'מנהל החברה',
                signer_email: 'manager@company.com',
                signer_type: 'company',
                signing_order: 2
            }
        ]
    })
});
Use Case 3: אישור קבלת תיקון

תרחיש: לקוח מאשר קבלת מכשיר מתוקן

  1. כשהתיקון מוכן, נוצרת בקשת חתימה אוטומטית
  2. הלקוח מגיע לאסוף את המכשיר
  3. חותם על טאבלט בחנות
  4. מקבל SMS עם קישור לאימות
Use Case 4: אימות חתימה חיצוני

תרחיש: צד שלישי רוצה לוודא תקינות חתימה

// אימות חתימה ציבורי (ללא התחברות)
const response = await fetch('/api/digital-signature/verify/SIG-2025-00001');
const result = await response.json();

if (result.valid) {
    console.log(`חתימה תקינה של: ${result.signer_name}`);
    console.log(`נחתם בתאריך: ${result.signed_at}`);
} else {
    console.log(`חתימה לא תקינה: ${result.reason_he}`);
}

Workflows

תהליך חתימה סטנדרטי

1
יצירת בקשת חתימה

משתמש מורשה יוצר בקשה לחתימה על ישות (חשבונית, חוזה וכו')

POST /api/digital-signature/requests
2
הוספת חותמים

הגדרת מי צריך לחתום (לקוח, נציג חברה, עד)

POST /api/digital-signature/requests/{id}/signers
3
שליחת בקשה

שליחת הבקשה בדוא"ל/SMS עם קישור לחתימה

POST /api/digital-signature/requests/{id}/send
4
חתימה על ידי החותם

החותם פותח את הקישור, קורא את המסמך וחותם

POST /api/digital-signature/sign
5
השלמה ואימות

כל החותמים חתמו - הבקשה מושלמת. ניתן לאמת בכל עת.

GET /api/digital-signature/verify/{signature_number}

סטטוסים של בקשת חתימה

┌─────────┐     ┌──────┐     ┌─────────────────┐     ┌───────────┐
│ pending │────▶│ sent │────▶│ partially_signed│────▶│ completed │
└─────────┘     └──────┘     └─────────────────┘     └───────────┘
     │              │                │
     │              │                │
     ▼              ▼                ▼
┌───────────┐  ┌─────────┐    ┌──────────┐
│ cancelled │  │ expired │    │ rejected │
└───────────┘  └─────────┘    └──────────┘

אינטגרציה עם מודולים אחרים

אינטגרציה עם מודול חשבוניות

# בקובץ api_invoicing.py - לאחר יצירת חשבונית

from app.models_digital_signature import (
    DigitalSignatureRequest,
    generate_signature_request_number
)

@invoicing_bp.route('/invoices/<int:id>/request-signature', methods=['POST'])
@login_required
def request_invoice_signature(id):
    """בקשת חתימה על חשבונית"""
    invoice = Invoice.query.get_or_404(id)

    # יצירת בקשת חתימה
    sig_request = DigitalSignatureRequest(
        request_number=generate_signature_request_number(),
        entity_type='invoice',
        entity_id=invoice.id,
        title=f'אישור חשבונית #{invoice.invoice_number}',
        customer_id=invoice.customer_id,
        requested_by=current_user.id
    )

    db.session.add(sig_request)
    db.session.commit()

    return jsonify(sig_request.to_dict())

אינטגרציה עם מודול תיקונים

# בקובץ api_routes.py - כשתיקון מושלם

@api_bp.route('/repairs/<int:id>/complete', methods=['POST'])
@login_required
def complete_repair(id):
    """השלמת תיקון ובקשת חתימה"""
    repair = Repair.query.get_or_404(id)
    repair.status = 'completed'

    # יצירת בקשת חתימה אוטומטית
    sig_request = DigitalSignatureRequest(
        request_number=generate_signature_request_number(),
        entity_type='repair',
        entity_id=repair.id,
        title=f'אישור קבלת תיקון #{repair.repair_number}',
        customer_id=repair.customer_id,
        requested_by=current_user.id
    )

    # הוספת הלקוח כחותם
    signer = DigitalSignatureSigner(
        request=sig_request,
        signer_name=repair.customer.name,
        signer_email=repair.customer.email,
        signer_phone=repair.customer.phone,
        signer_type='customer'
    )

    db.session.add_all([sig_request, signer])
    db.session.commit()

    return jsonify({'repair': repair.to_dict(), 'signature_request': sig_request.to_dict()})

שאילתות פולימורפיות

# קבלת כל החתימות לישות מסוימת
from app.models_digital_signature import get_entity_signatures, get_entity_signature_requests

# בכל מקום באפליקציה
invoice_signatures = get_entity_signatures('invoice', 123)
contract_signatures = get_entity_signatures('contract', 456)

# או עם שאילתה ישירה
signatures = DigitalSignature.query.filter_by(
    entity_type='invoice',
    entity_id=123,
    status='completed'
).all()

אבטחה

אימות וגישה

הרשאות API
  • Endpoints מוגנים: רוב ה-endpoints דורשים התחברות (@login_required)
  • Endpoints ציבוריים: verify, constants, signing-page - ללא אימות
  • Access Tokens: קישורי חתימה חיצוניים משתמשים ב-tokens חד-פעמיים עם תפוגה

אימות חתימות

Hash Verification
  • כל חתימה מקבלת hash ייחודי (SHA-512)
  • ה-hash כולל: signature_data + document_hash + timestamp + signer_name
  • ניתן לאמת תקינות חתימה בכל עת
def generate_hash(self, document_hash=None):
    hash_input = f"{self.signature_data}{document_hash}{self.signed_at}{self.signer_name}"
    return hashlib.sha512(hash_input.encode()).hexdigest()

def verify_hash(self, document_hash=None):
    expected_hash = self.generate_hash(document_hash)
    return self.signature_hash == expected_hash

מידע משפטי

Legal Compliance
  • IP Address: נשמרת כתובת IP של החותם
  • User Agent: נשמר מידע על הדפדפן/מכשיר
  • Timestamp: זמן חתימה מדויק
  • Consent: אישור מפורש שהחותם קרא והבין
  • Audit Log: לוג מלא של כל הפעולות
חשוב: מודול זה מספק חתימה אלקטרונית בסיסית. לחתימה דיגיטלית מאושרת לפי חוק, יש לשלב עם ספק חתימה דיגיטלית מורשה (כמו קומסיין).

דוגמאות קוד

JavaScript - יצירת בקשת חתימה

// יצירת בקשת חתימה מלאה
async function createSignatureRequest(entityType, entityId, title, signers) {
    try {
        const response = await fetch('/api/digital-signature/requests', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                entity_type: entityType,
                entity_id: entityId,
                title: title,
                priority: 'normal',
                signers: signers
            })
        });

        if (!response.ok) {
            throw new Error('Failed to create request');
        }

        const data = await response.json();
        console.log('Created request:', data.request_number);
        return data;

    } catch (error) {
        console.error('Error:', error);
        throw error;
    }
}

// שימוש
createSignatureRequest('invoice', 123, 'אישור חשבונית', [
    {
        signer_name: 'ישראל ישראלי',
        signer_email: 'israel@example.com',
        signer_type: 'customer'
    }
]);

JavaScript - Canvas Signature

// איתחול Canvas לחתימה
class SignatureCanvas {
    constructor(canvasElement) {
        this.canvas = canvasElement;
        this.ctx = canvas.getContext('2d');
        this.isDrawing = false;

        // הגדרות
        this.ctx.strokeStyle = '#000';
        this.ctx.lineWidth = 2;
        this.ctx.lineCap = 'round';
        this.ctx.lineJoin = 'round';

        this.setupEvents();
    }

    setupEvents() {
        // Mouse events
        this.canvas.addEventListener('mousedown', (e) => this.startDrawing(e));
        this.canvas.addEventListener('mousemove', (e) => this.draw(e));
        this.canvas.addEventListener('mouseup', () => this.stopDrawing());
        this.canvas.addEventListener('mouseout', () => this.stopDrawing());

        // Touch events
        this.canvas.addEventListener('touchstart', (e) => this.startDrawing(e));
        this.canvas.addEventListener('touchmove', (e) => this.draw(e));
        this.canvas.addEventListener('touchend', () => this.stopDrawing());
    }

    startDrawing(e) {
        this.isDrawing = true;
        const pos = this.getPosition(e);
        this.ctx.beginPath();
        this.ctx.moveTo(pos.x, pos.y);
    }

    draw(e) {
        if (!this.isDrawing) return;
        e.preventDefault();
        const pos = this.getPosition(e);
        this.ctx.lineTo(pos.x, pos.y);
        this.ctx.stroke();
    }

    stopDrawing() {
        this.isDrawing = false;
    }

    getPosition(e) {
        const rect = this.canvas.getBoundingClientRect();
        const clientX = e.touches ? e.touches[0].clientX : e.clientX;
        const clientY = e.touches ? e.touches[0].clientY : e.clientY;
        return {
            x: clientX - rect.left,
            y: clientY - rect.top
        };
    }

    clear() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }

    getSignatureData() {
        return this.canvas.toDataURL('image/png');
    }

    isEmpty() {
        const blank = document.createElement('canvas');
        blank.width = this.canvas.width;
        blank.height = this.canvas.height;
        return this.canvas.toDataURL() === blank.toDataURL();
    }
}

// שימוש
const sigCanvas = new SignatureCanvas(document.getElementById('signatureCanvas'));

// שליחת החתימה
async function submitSignature() {
    if (sigCanvas.isEmpty()) {
        alert('נא לחתום לפני השליחה');
        return;
    }

    const signatureData = sigCanvas.getSignatureData();

    await fetch('/api/digital-signature/sign', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            request_id: currentRequestId,
            signature_type: 'drawn',
            signature_data: signatureData,
            signer_name: 'ישראל ישראלי',
            consent_given: true
        })
    });
}

Python - אינטגרציה בקוד Backend

from app.models_digital_signature import (
    DigitalSignatureRequest, DigitalSignatureSigner, DigitalSignature,
    generate_signature_request_number, create_audit_log
)
from app import db
from datetime import datetime, timedelta

def create_signature_request_for_entity(entity_type, entity_id, title, signers_info, requested_by_user):
    """
    יצירת בקשת חתימה לישות

    Args:
        entity_type: סוג הישות (invoice, contract, etc.)
        entity_id: מזהה הישות
        title: כותרת הבקשה
        signers_info: רשימת מילונים עם פרטי החותמים
        requested_by_user: אובייקט User של המבקש

    Returns:
        DigitalSignatureRequest: אובייקט הבקשה שנוצרה
    """

    # יצירת הבקשה
    sig_request = DigitalSignatureRequest(
        request_number=generate_signature_request_number(),
        entity_type=entity_type,
        entity_id=entity_id,
        title=title,
        status='pending',
        expires_at=datetime.utcnow() + timedelta(hours=72),
        requested_by=requested_by_user.id
    )

    db.session.add(sig_request)
    db.session.flush()  # קבלת ID

    # הוספת חותמים
    for idx, signer_info in enumerate(signers_info):
        signer = DigitalSignatureSigner(
            request_id=sig_request.id,
            signer_name=signer_info['name'],
            signer_email=signer_info.get('email'),
            signer_phone=signer_info.get('phone'),
            signer_type=signer_info.get('type', 'customer'),
            signing_order=idx + 1
        )
        signer.generate_access_token(72)  # Token לשעות 72
        db.session.add(signer)

    # יצירת רשומת audit
    create_audit_log(
        request_id=sig_request.id,
        entity_type=entity_type,
        entity_id=entity_id,
        action='created',
        action_details=f'Signature request created: {title}',
        actor_type='user',
        actor_id=requested_by_user.id,
        actor_name=requested_by_user.username
    )

    db.session.commit()

    return sig_request


# שימוש לדוגמה
request = create_signature_request_for_entity(
    entity_type='invoice',
    entity_id=123,
    title='אישור חשבונית #INV-2025-123',
    signers_info=[
        {'name': 'ישראל ישראלי', 'email': 'israel@example.com', 'type': 'customer'},
        {'name': 'מנהל חברה', 'email': 'manager@company.com', 'type': 'company'}
    ],
    requested_by_user=current_user
)