Skip to main content
The sessions service manages the complete lifecycle of tattoo appointments, from scheduling to completion with payment processing.

Overview

Location: services/sessions.py This module handles:
  • Tattoo session creation and scheduling
  • Schedule conflict detection
  • Session status management
  • Session completion with transaction creation
  • Commission calculations
  • Session listing and filtering

Session Status Values

Activa

Scheduled and confirmed session

Completada

Finished session with payment recorded

En espera

Tentative or pending confirmation

Cancelada

Cancelled session (includes no-shows)

Payment Methods

Efectivo

Cash payment

Tarjeta

Card payment

Transferencia

Bank transfer

Functions

create_session

Creates a new tattoo session after validating schedule conflicts.
payload
dict
required
Session data dictionary
Payload structure:
  • client_id (int): Client identifier
  • artist_id (int): Artist identifier
  • start (datetime): Session start time
  • end (datetime): Session end time
  • price (float): Session price
  • notes (Optional[str]): Additional notes
Returns: int - The ID of the created session Raises: ValueError - If schedule conflict detected or invalid time range
from services.sessions import create_session
from datetime import datetime, timedelta

# Schedule a new tattoo session
now = datetime.now()
session_data = {
    "client_id": 15,
    "artist_id": 3,
    "start": now.replace(hour=14, minute=0),
    "end": now.replace(hour=17, minute=0),
    "price": 5000.0,
    "notes": "Continuation of sleeve tattoo"
}

try:
    session_id = create_session(session_data)
    print(f"Session created with ID: {session_id}")
except ValueError as e:
    print(f"Cannot create session: {e}")
The function automatically sets the session status to “Activa” upon creation.

update_session

Updates an existing session’s details.
session_id
int
required
ID of the session to update
payload
dict
required
Dictionary with fields to update
Updatable fields:
  • start (datetime): New start time
  • end (datetime): New end time
  • price (float): Updated price
  • notes (str): Updated notes
  • status (str): New status (except “Completada”)
  • artist_id (int): Reassign to different artist
  • commission_override (float): Custom commission rate
Returns: None Raises: ValueError - If session not found, invalid status, or schedule conflict
from services.sessions import update_session
from datetime import datetime

# Update session time and price
update_session(session_id=42, payload={
    "start": datetime(2026, 3, 10, 11, 0),
    "end": datetime(2026, 3, 10, 14, 0),
    "price": 4000.0,
    "notes": "Client requested earlier time"
})

print("Session updated successfully")
Cannot use this function to mark a session as “Completada”. Use complete_session() instead to properly handle payment and transaction creation.

complete_session

Marks a session as complete and creates the associated financial transaction.
session_id
int
required
ID of the session to complete
payment
dict
required
Payment information dictionary
Payment structure:
  • method (str): Payment method - “Efectivo”, “Tarjeta”, or “Transferencia”
Returns: int - The ID of the created transaction Raises: ValueError - If session not found, already completed, or cancelled
from services.sessions import complete_session

# Complete session with cash payment
try:
    transaction_id = complete_session(
        session_id=42,
        payment={"method": "Efectivo"}
    )
    print(f"Session completed. Transaction ID: {transaction_id}")
except ValueError as e:
    print(f"Cannot complete session: {e}")
This function automatically calculates the artist’s commission based on their configured rate or a session-specific override.

Commission Calculation

The commission is calculated as follows:
  1. If commission_override is set on the session, use that rate
  2. Otherwise, use the artist’s rate_commission from their profile
  3. Default to 0.5 (50%) if neither is set
  4. Commission amount = session.price * commission_rate

cancel_session

Cancels a session and optionally marks it as a no-show.
session_id
int
required
ID of the session to cancel
as_no_show
bool
default:false
Whether to mark this as a client no-show
Returns: None Raises: ValueError - If session not found or already completed
from services.sessions import cancel_session

# Cancel a session
try:
    cancel_session(session_id=42)
    print("Session cancelled")
except ValueError as e:
    print(f"Cannot cancel: {e}")
When as_no_show=True, the function prepends “[No-show]” to the session notes to track client attendance issues.

list_sessions

Retrieves sessions with optional filtering.
filters
dict
required
Filter criteria dictionary
Filter options:
  • from (datetime): Start date filter
  • to (datetime): End date filter
  • artist_id (int): Filter by artist
  • status (str | list): Filter by status(es)
Returns: list[dict] - List of session dictionaries Each session dictionary contains:
  • id (int): Session ID
  • client_id (int): Client ID
  • artist_id (int): Artist ID
  • start (datetime): Start time
  • end (datetime): End time
  • status (str): Current status
  • price (float): Session price
  • notes (str): Notes
from services.sessions import list_sessions
from datetime import datetime, timedelta

# Get all sessions for next week
today = datetime.now()
next_week = today + timedelta(days=7)

sessions = list_sessions({
    "from": today,
    "to": next_week
})

for session in sessions:
    print(f"Session {session['id']}: {session['start']} - {session['status']}")
Results are automatically sorted by start time in ascending order, making it easy to display chronological schedules.

Schedule Conflict Detection

The service includes automatic overlap detection to prevent double-booking:

Overlap Rules

Two sessions overlap if:
new_start < existing_end AND new_end > existing_start

Conflict Detection Behavior

  • Checks only for the same artist
  • Ignores cancelled sessions
  • Allows updates to the same session (using exclude_session_id)
  • Raises ValueError with message “Choque de horario” on conflict
Existing session: 10:00 - 13:00CONFLICT:
  • 09:00 - 11:00 (starts before, ends during)
  • 11:00 - 14:00 (starts during, ends after)
  • 09:00 - 14:00 (completely overlaps)
  • 11:00 - 12:00 (completely inside)
NO CONFLICT:
  • 08:00 - 10:00 (ends exactly when existing starts)
  • 13:00 - 15:00 (starts exactly when existing ends)
  • 14:00 - 16:00 (completely after)

Typical Workflows

1

Check availability

Call list_sessions() to view existing schedule for the artist.
2

Create session

Call create_session() with client, artist, time, and price.
3

Handle conflicts

Catch ValueError and prompt user to select different time.

Best Practices

  • Always set the price before completing a session
  • If price is not set (None), it defaults to 0.0 on completion
  • Consider including deposit information in the notes field
  • Use commission_override for special pricing arrangements
  • Values should be between 0.0 and 1.0 (e.g., 0.6 for 60%)
  • Override persists if session is updated
Valid transitions:
  • “En espera” → “Activa” (confirmation)
  • “Activa” → “Completada” (via complete_session())
  • Any → “Cancelada” (via cancel_session())
Invalid:
  • “Completada” → Any other status
  • “Cancelada” → “Completada”
  • All datetime objects should be timezone-aware
  • Session end time must be after start time
  • Consider buffer time between sessions for setup/cleanup

Session Model

TattooSession data model and schema

Transaction Model

Financial transaction tracking

Managing Appointments

User guide for appointment scheduling

Artist Model

Artist data and commission rates

Build docs developers (and LLMs) love