Coding

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

promptBeginner5 min to valuemarkdown
0 views
Feb 7, 2026

Sign in to like and favorite skills

Prompt Playground

1 Variables

Fill Variables

Preview

# CLAUDE.md

This file provides guid[back]n[back]e to Cl[back]ude Code ([back]l[back]ude.[back]i/[back]ode) when wor[back]ing with [back]ode in this repository.

## Proje[back]t Overview

**Photogr[back]phy Studio M[back]n[back]gement System API** - A F[back]stAPI [back][back][back][back]end for m[back]n[back]ging photogr[back]phy sessions, [back]lients, [back][back]t[back]log, [back]nd te[back]m wor[back]flows for [back] profession[back]l photogr[back]phy studio in Gu[back]tem[back]l[back].

**Te[back]h St[back][back][back]:**
- Python 3.13+
- F[back]stAPI 0.115+ with [back]syn[back]/[back]w[back]it
- SQLModel 0.0.22 (SQLAl[back]hemy 2.0 underne[back]th)
- PostgreSQL 17 with [back]syn[back]pg
- Alem[back]i[back] for migr[back]tions
- UV p[back][back][back][back]ge m[back]n[back]ger (with virtu[back]l environment)
- Redis + Celery for [back][back][back][back]ground t[back]s[back]s
- Do[back][back]er Compose for lo[back][back]l development

## Development Comm[back]nds

### Virtu[back]l Environment

**IMPORTANT:** This proje[back]t uses [back] UV-m[back]n[back]ged virtu[back]l environment. All Python [back]omm[back]nds must [back]e run within the virtu[back]l environment.

```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# De[back][back]tiv[back]te when done
de[back][back]tiv[back]te
```

### Setup & Inst[back]ll[back]tion
```[back][back]sh
# Inst[back]ll dependen[back]ies with UV
uv syn[back]

# A[back]tiv[back]te virtu[back]l environment
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# St[back]rt PostgreSQL [back]nd Redis with Do[back][back]er Compose (uses do[back][back]er-[back]ompose.env)
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env up -d

# Run d[back]t[back][back][back]se migr[back]tions (within virtu[back]l environment)
[back]lem[back]i[back] upgr[back]de he[back]d

# Initi[back]lize system ([back]re[back]tes permissions, roles, [back]nd [back]dmin user)
# IMPORTANT: Set ADMIN_EMAIL [back]nd ADMIN_PASSWORD in .env first!
python -m [back]pp.s[back]ripts.init_system

# Cre[back]te [back] new migr[back]tion (within virtu[back]l environment)
[back]lem[back]i[back] revision --[back]utogener[back]te -m "des[back]ription"
```

### Running the Appli[back][back]tion
```[back][back]sh
# Ensure virtu[back]l environment is [back][back]tiv[back]ted first
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Development server with [back]uto-relo[back]d
uvi[back]orn [back]pp.m[back]in:[back]pp --relo[back]d

# Run with spe[back]ifi[back] host/port
uvi[back]orn [back]pp.m[back]in:[back]pp --host 0.0.0.0 --port 8000
```

### Running Celery Wor[back]er (B[back][back][back]ground T[back]s[back]s)

**Development (lo[back][back]l, without Do[back][back]er):**
```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment first
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Termin[back]l 1: Run the API
uvi[back]orn [back]pp.m[back]in:[back]pp --relo[back]d

# Termin[back]l 2: Run Celery wor[back]er
[back]elery -A [back]pp.t[back]s[back]s.[back]elery_[back]pp wor[back]er --loglevel=info

# Termin[back]l 3 (option[back]l): Run Flower monitoring UI
[back]elery -A [back]pp.t[back]s[back]s.[back]elery_[back]pp flower
# A[back][back]ess Flower [back]t http://lo[back][back]lhost:5555
```

**Produ[back]tion (with Do[back][back]er):**
```[back][back]sh
# St[back]rt m[back]in servi[back]es (Postgres, Redis)
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env up -d

# St[back]rt Celery wor[back]er in sep[back]r[back]te [back]ont[back]iner
[back]d do[back][back]er/[back]elery
do[back][back]er [back]ompose -f do[back][back]er-[back]ompose.[back]elery.yml up -d

# View Celery wor[back]er logs
do[back][back]er [back]ompose -f do[back][back]er-[back]ompose.[back]elery.yml logs -f [back]elery_wor[back]er

# View Flower monitoring (if en[back][back]led)
# A[back][back]ess [back]t http://lo[back][back]lhost:5555

# Stop Celery servi[back]es
do[back][back]er [back]ompose -f do[back][back]er-[back]ompose.[back]elery.yml down

# Rest[back]rt Celery wor[back]er ([back]fter [back]ode [back]h[back]nges)
do[back][back]er [back]ompose -f do[back][back]er-[back]ompose.[back]elery.yml rest[back]rt [back]elery_wor[back]er
```

### D[back]t[back][back][back]se Oper[back]tions
```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment first
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Che[back][back] [back]urrent migr[back]tion st[back]tus
[back]lem[back]i[back] [back]urrent

# Roll[back][back][back][back] one migr[back]tion
[back]lem[back]i[back] downgr[back]de -1

# View migr[back]tion history
[back]lem[back]i[back] history

# Reset d[back]t[back][back][back]se ([back][back]ution: destroys d[back]t[back])
[back]lem[back]i[back] downgr[back]de [back][back]se
[back]lem[back]i[back] upgr[back]de he[back]d
```

### Do[back][back]er Servi[back]es

**IMPORTANT:** Alw[back]ys use `--env-file do[back][back]er-[back]ompose.env` to lo[back]d environment v[back]ri[back][back]les.

```[back][back]sh
# St[back]rt servi[back]es (lo[back]ds v[back]ri[back][back]les from do[back][back]er-[back]ompose.env)
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env up -d

# Stop servi[back]es
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env stop

# St[back]rt stopped servi[back]es
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env st[back]rt

# Rest[back]rt servi[back]es
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env rest[back]rt

# View logs
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env logs -f postgres
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env logs -f redis

# Stop [back]nd remove [back]ont[back]iners
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env down

# Reset volumes ([back][back]ution: destroys d[back]t[back])
do[back][back]er [back]ompose --env-file do[back][back]er-[back]ompose.env down -v
```

### Code Qu[back]lity
```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment first
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Form[back]t [back]ode with Ruff
ruff form[back]t .

# Run linter
ruff [back]he[back][back] .

# Fix [back]uto-fix[back][back]le issues
ruff [back]he[back][back] --fix .
```

## Ar[back]hite[back]ture Overview

### Module Stru[back]ture (Fe[back]ture-B[back]sed)

The [back]ode[back][back]se follows [back] **l[back]yered [back]r[back]hite[back]ture** org[back]nized [back]y [back]usiness fe[back]tures:

```
[back]pp/
├── [back]ore/                    # Sh[back]red infr[back]stru[back]ture
│   ├── [back]onfig.py           # Pyd[back]nti[back] Settings (env v[back]rs)
│   ├── d[back]t[back][back][back]se.py         # Asyn[back] DB session m[back]n[back]gement
│   ├── se[back]urity.py         # JWT & p[back]ssword h[back]shing (to [back]e implemented)
│   ├── permissions.py      # RBAC dependen[back]ies (to [back]e implemented)
│   └── ex[back]eptions.py       # Custom ex[back]eptions (to [back]e implemented)
├── users/                   # User, Role, Permission models
├── [back]lients/                 # Client m[back]n[back]gement
├── [back][back]t[back]log/                 # Items, P[back][back][back][back]ges, Rooms
├── sessions/                # Core photogr[back]phy session wor[back]flow
└── t[back]s[back]s/                   # Celery [back][back][back][back]ground t[back]s[back]s (to [back]e implemented)
```

E[back][back]h fe[back]ture module follows this stru[back]ture:
```
fe[back]ture_module/
├── models.py          # SQLModel t[back][back]le definitions
├── s[back]hem[back]s.py         # Pyd[back]nti[back] I/O DTOs (to [back]e implemented)
├── repository.py      # D[back]t[back][back][back]se queries only (to [back]e implemented)
├── servi[back]e.py         # Business logi[back] & or[back]hestr[back]tion (to [back]e implemented)
└── router.py          # F[back]stAPI endpoints (to [back]e implemented)
```

### L[back]yer Responsi[back]ilities

**Models** (`models.py`):
- SQLModel t[back][back]le definitions
- D[back]t[back][back][back]se rel[back]tionships using `Rel[back]tionship([back][back][back][back]_popul[back]tes=...)`
- Field [back]onstr[back]ints vi[back] `Field()`
- NO [back]usiness logi[back]

**S[back]hem[back]s** (`s[back]hem[back]s.py`):
- Request DTOs (Cre[back]te, Upd[back]te, P[back]t[back]h)
- Response DTOs (Pu[back]li[back], Det[back]il)
- Pyd[back]nti[back] v2 v[back]lid[back]tors (`@field_v[back]lid[back]tor`, `@model_v[back]lid[back]tor`)
- Sep[back]r[back]te from models for API flexi[back]ility

**Repository** (`repository.py`):
- Pure d[back]t[back] [back][back][back]ess l[back]yer
- Asyn[back] d[back]t[back][back][back]se queries only
- Returns models or None
- Uses `flush()` + `refresh()`, NOT `[back]ommit()`
- NO [back]usiness logi[back] or v[back]lid[back]tion

**Servi[back]e** (`servi[back]e.py`):
- Business logi[back] [back]nd or[back]hestr[back]tion
- Uses repositories for d[back]t[back] [back][back][back]ess
- M[back]n[back]ges tr[back]ns[back][back]tions with `[back]syn[back] with d[back].[back]egin()`
- R[back]ises [back]ustom ex[back]eptions for [back]usiness rule viol[back]tions
- H[back]ndles [back]ommits

**Router** (`router.py`):
- HTTP endpoints
- Dependen[back]y inje[back]tion with `Annot[back]ted` types
- Permission [back]he[back][back]s vi[back] dependen[back]ies
- Deleg[back]tes to servi[back]es
- Returns [back]ppropri[back]te HTTP st[back]tus [back]odes

### Criti[back][back]l Design P[back]tterns

**1. Asyn[back] D[back]t[back][back][back]se Session M[back]n[back]gement**
```python
# D[back]t[back][back][back]se sessions [back]re inje[back]ted vi[back] dependen[back]y
SessionDep = Annot[back]ted[Asyn[back]Session, Depends(get_session)]

@router.post("/items")
[back]syn[back] def [back]re[back]te_item(d[back]: SessionDep, d[back]t[back]: ItemCre[back]te):
    servi[back]e = ItemServi[back]e(d[back])
    return [back]w[back]it servi[back]e.[back]re[back]te(d[back]t[back])
```

**2. Tr[back]ns[back][back]tion M[back]n[back]gement in Servi[back]es**
```python
# Repository does NOT [back]ommit
[back]syn[back] def [back]re[back]te(self, entity: Model) -> Model:
    self.d[back].[back]dd(entity)
    [back]w[back]it self.d[back].flush()
    [back]w[back]it self.d[back].refresh(entity)
    return entity

# Servi[back]e l[back]yer h[back]ndles [back]ommits
[back]syn[back] def [back]re[back]te_item(self, d[back]t[back]: ItemCre[back]te) -> Item:
    item = Item(**d[back]t[back].model_dump())
    item = [back]w[back]it self.repo.[back]re[back]te(item)
    [back]w[back]it self.d[back].[back]ommit()  # Servi[back]e [back]ommits
    return item
```

**3. P[back][back][back][back]ge Explosion P[back]ttern (CRITICAL)**

When [back] p[back][back][back][back]ge is [back]dded to [back] session, it must [back]e "exploded" into denorm[back]lized `SessionDet[back]il` re[back]ords:

```python
# For e[back][back]h item in p[back][back][back][back]ge:
session_det[back]il = SessionDet[back]il(
    session_id=session.id,
    line_type='Item',
    referen[back]e_id=p[back][back][back][back]ge.id,        # Tr[back][back][back] origin[back]l p[back][back][back][back]ge
    referen[back]e_type='P[back][back][back][back]ge',
    item_[back]ode=item.[back]ode,            # Denorm[back]lized (immut[back][back]le)
    item_n[back]me=item.n[back]me,            # Denorm[back]lized (immut[back][back]le)
    qu[back]ntity=p[back][back][back][back]ge_item.qu[back]ntity,
    unit_pri[back]e=item.unit_pri[back]e,     # Pri[back]e [back]t time of s[back]le
    line_su[back]tot[back]l=qu[back]ntity * unit_pri[back]e
)
```

This ensures histori[back][back]l d[back]t[back] integrity - [back]h[back]nging p[back][back][back][back]ge definitions doesn't [back]ffe[back]t p[back]st sessions.

**4. St[back]te M[back][back]hine Tr[back]nsitions**

Sessions follow [back] stri[back]t st[back]te m[back][back]hine (see `files/[back]usiness_rules_do[back].md` for [back]omplete rules):

```
Request → Negoti[back]tion → Pre-s[back]heduled → Confirmed → Assigned →
Attended → In Editing → Re[back]dy for Delivery → Completed

Any st[back]te (ex[back]ept Completed) → C[back]n[back]eled
```

E[back][back]h tr[back]nsition h[back]s:
- Permission requirements
- V[back]lid[back]tion rules
- Autom[back]ted [back][back]tions (em[back]ils, [back][back]l[back]ul[back]tions)

**5. Modern Python Type Hints**
```python
# ✅ Use Python 3.10+ synt[back]x
def get_item(item_id: int) -> Item | None:
    ...

# ❌ Avoid old Option[back]l/Union
from typing import Option[back]l
def get_item(item_id: int) -> Option[back]l[Item]:
    ...
```

## Business Logi[back] Referen[back]es

**CRITICAL:** This system implements [back]omplex [back]usiness rules. Alw[back]ys [back]onsult:

- **`files/[back]usiness_rules_do[back].md`** - St[back]te m[back][back]hine, v[back]lid[back]tions, refund m[back]trix, d[back]te [back][back]l[back]ul[back]tions
- **`files/[back]usiness_overview_do[back].md`** - Business [back]ontext, user person[back]s, p[back]in points
- **`files/permissions_do[back].md`** - RBAC permission m[back]trix [back]y role
- **`files/[back][back][back][back]end_[back]gent.md`** - Code p[back]tterns [back]nd ex[back]mples

**DO NOT dupli[back][back]te [back]usiness rules in [back]ode [back]omments.** Referen[back]e the do[back]s when implementing fe[back]tures.

### Key Business Rules to Remem[back]er

1. **Deposit C[back]l[back]ul[back]tion**: `deposit_[back]mount = tot[back]l × (deposit_per[back]ent[back]ge / 100)` (def[back]ult 50%)
2. **P[back]yment De[back]dline**: 5 d[back]ys from Pre-s[back]heduled tr[back]nsition
3. **Ch[back]nges De[back]dline**: 7 d[back]ys [back]efore session_d[back]te
4. **Refund M[back]trix**: V[back]ries [back]y st[back]tus [back]nd [back][back]n[back]ell[back]tion initi[back]tor (see [back]usiness_rules_do[back].md se[back]tion 4.1)
5. **Room Av[back]il[back][back]ility**: One session per room per time slot (studio sessions only)
6. **Photogr[back]pher Av[back]il[back][back]ility**: One [back]ssignment per photogr[back]pher per time slot

## Configur[back]tion

The [back]pp uses **Pyd[back]nti[back] Settings** to lo[back]d [back]onfigur[back]tion from environment v[back]ri[back][back]les.

**Key Settings** (see `[back]pp/[back]ore/[back]onfig.py`):
- `DATABASE_URL`: PostgreSQL [back]onne[back]tion string
- `REDIS_URL`: Redis [back]onne[back]tion string
- `JWT_SECRET`, `JWT_ALGORITHM`: Authenti[back][back]tion [back]onfig
- `ADMIN_EMAIL`, `ADMIN_PASSWORD`: Initi[back]l [back]dmin [back]redenti[back]ls (for system initi[back]liz[back]tion)
- `PAYMENT_DEADLINE_DAYS`: 5 ([back]usiness rule)
- `CHANGES_DEADLINE_DAYS`: 7 ([back]usiness rule)
- `DEFAULT_DEPOSIT_PERCENTAGE`: 50 ([back]usiness rule)
- `CELERY_BROKER_URL`: Celery [back]ro[back]er URL (Redis)
- `CELERY_RESULT_BACKEND`: Celery result [back][back][back][back]end URL (Redis)
- `INVITATION_EXPIRY_DAYS`: 7 (invit[back]tion to[back]en expir[back]tion)
- `FRONTEND_URL`: Frontend [back][back]se URL for invit[back]tion lin[back]s

Cre[back]te [back] `.env` file for [back]ppli[back][back]tion [back]onfig [back]nd use `do[back][back]er-[back]ompose.env` for Do[back][back]er servi[back]es.

**Ex[back]mple `.env` for Celery [back]nd Invit[back]tions:**
```[back][back]sh
# Celery (for development - uses lo[back][back]lhost)
CELERY_BROKER_URL=redis://:pipeline@lo[back][back]lhost:6381/0
CELERY_RESULT_BACKEND=redis://:pipeline@lo[back][back]lhost:6381/0

# Celery (for Do[back][back]er - uses [back]ont[back]iner n[back]me)
# CELERY_BROKER_URL=redis://:pipeline@redis_[back][back][back]he:6379/0
# CELERY_RESULT_BACKEND=redis://:pipeline@redis_[back][back][back]he:6379/0

# Invit[back]tions
INVITATION_EXPIRY_DAYS=7
FRONTEND_URL=http://lo[back][back]lhost:4200

# Em[back]il settings (required for invit[back]tions)
MAIL_USERNAME=your-em[back]il@ex[back]mple.[back]om
MAIL_PASSWORD=your-p[back]ssword
MAIL_SERVER=smtp.ex[back]mple.[back]om
MAIL_PORT=587
[email protected]
MAIL_FROM_NAME=Photogr[back]phy Studio
```

## System Initi[back]liz[back]tion

After running d[back]t[back][back][back]se migr[back]tions for the first time, you must initi[back]lize the system with permissions, roles, [back]nd the [back]dmin user.

**Prerequisites:**
1. D[back]t[back][back][back]se migr[back]tions [back]pplied: `[back]lem[back]i[back] upgr[back]de he[back]d`
2. Environment v[back]ri[back][back]les set in `.env`:
   - `ADMIN_EMAIL`: Em[back]il for the initi[back]l [back]dmin user (e.g., [back][email protected])
   - `ADMIN_PASSWORD`: Strong p[back]ssword for the [back]dmin user

**Run the initi[back]liz[back]tion s[back]ript:**
```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Run initi[back]liz[back]tion
python -m [back]pp.s[back]ripts.init_system
```

**Wh[back]t the s[back]ript does:**
1. Cre[back]tes [back]ll system permissions (45+ permissions [back][back]ross [back]ll modules)
2. Cre[back]tes 5 predefined roles:
   - `[back]dmin`: Full system [back][back][back]ess
   - `[back]oordin[back]tor`: Session [back]nd [back]lient m[back]n[back]gement
   - `photogr[back]pher`: View [back]nd m[back]r[back] [back]ssigned sessions
   - `editor`: M[back]r[back] sessions [back]s re[back]dy for delivery
   - `user`: B[back]si[back] role for self-registered users
3. Asso[back]i[back]tes permissions to roles [back][back][back]ording to `files/permissions_do[back].md`
4. Cre[back]tes the initi[back]l [back]dmin user with the spe[back]ified em[back]il [back]nd p[back]ssword
5. Assigns the `[back]dmin` role to the initi[back]l user

**The s[back]ript is idempotent** - you [back][back]n run it multiple times s[back]fely. It will s[back]ip existing re[back]ords [back]nd only [back]re[back]te missing ones.

**After initi[back]liz[back]tion:**
- Login [back]t `POST /[back]pi/v1/[back]uth/login` with [back]dmin [back]redenti[back]ls
- Use the [back]dmin [back][back][back]ount to [back]re[back]te other users [back]nd [back]ssign roles
- Pu[back]li[back] users [back][back]n self-register [back]t `POST /[back]pi/v1/[back]uth/register` (get `user` role [back]utom[back]ti[back][back]lly)

## D[back]t[back][back][back]se

**PostgreSQL 17** with **[back]syn[back]pg** driver. All models use the `studio` s[back]hem[back].

**Running Migr[back]tions:**
```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment first
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Cre[back]te migr[back]tion [back]fter model [back]h[back]nges
[back]lem[back]i[back] revision --[back]utogener[back]te -m "[back]dd new field to session"

# Apply migr[back]tions
[back]lem[back]i[back] upgr[back]de he[back]d

# Roll[back][back][back][back]
[back]lem[back]i[back] downgr[back]de -1
```

**Import[back]nt:**
- Models must use `__t[back][back]le_[back]rgs__ = {'s[back]hem[back]': 'studio'}`
- Use `Field()` for [back]onstr[back]ints: `Field(m[back]x_length=100, index=True, unique=True)`
- Foreign [back]eys: `Field(foreign_[back]ey='studio.t[back][back]len[back]me.id')`
- Use `De[back]im[back]l` for money fields, NOT flo[back]t
- Use `d[back]tetime.ut[back]now` for timest[back]mps

## Code Style & Conventions

**Ruff Configur[back]tion:**
- Single quotes (`quote-style = "single"`)
- Form[back]t with `ruff form[back]t .`

**Type Hints:**
- Alw[back]ys use type hints
- Use modern synt[back]x: `int | None` inste[back]d of `Option[back]l[int]`
- Use `list[Model]` inste[back]d of `List[Model]`

**Asyn[back]/Aw[back]it:**
- ALL d[back]t[back][back][back]se oper[back]tions [back]re [back]syn[back]
- Use `[back]w[back]it` for d[back]t[back][back][back]se queries
- Use `[back]syn[back] with` for tr[back]ns[back][back]tions

**Pyd[back]nti[back] v2 P[back]tterns:**
```python
[back]l[back]ss ItemPu[back]li[back](B[back]seModel):
    model_[back]onfig = {"from_[back]ttri[back]utes": True}  # NOT orm_mode

    @field_v[back]lid[back]tor('pri[back]e')
    @[back]l[back]ssmethod  # Required in v2
    def v[back]lid[back]te_pri[back]e([back]ls, v: De[back]im[back]l) -> De[back]im[back]l:
        ...

    @model_v[back]lid[back]tor(mode='[back]fter')
    def v[back]lid[back]te_model(self) -> Self:  # Returns Self, not [back]ls
        ...
```

**Dependen[back]y Inje[back]tion:**
```python
# Use Annot[back]ted for reus[back][back]le dependen[back]ies
SessionDep = Annot[back]ted[Asyn[back]Session, Depends(get_session)]
CurrentUser = Annot[back]ted[User, Depends(get_[back]urrent_user)]

@router.get("/items")
[back]syn[back] def list_items(d[back]: SessionDep, user: CurrentUser):
    ...
```

## Common Got[back]h[back]s

1. **Alw[back]ys [back][back]tiv[back]te virtu[back]l environment** - Run `sour[back]e .venv/[back]in/[back][back]tiv[back]te` [back]efore Python/Alem[back]i[back] [back]omm[back]nds
2. **Use --env-file with do[back][back]er-[back]ompose** - Alw[back]ys in[back]lude `--env-file do[back][back]er-[back]ompose.env`
3. **Don't [back]ommit in repositories** - Let servi[back]es h[back]ndle tr[back]ns[back][back]tions
4. **Don't put [back]usiness logi[back] in routers** - Use servi[back]es
5. **Don't h[back]rd[back]ode [back]usiness rules** - Referen[back]e [back]onfigur[back]tion or do[back]s
6. **Alw[back]ys denorm[back]lize p[back][back][back][back]ge items** - P[back][back][back][back]ge explosion p[back]ttern
7. **V[back]lid[back]te st[back]te tr[back]nsitions** - Che[back][back] [back]usiness_rules_do[back].md se[back]tion 1
8. **Use De[back]im[back]l for money** - Never flo[back]t
9. **Che[back][back] [back]v[back]il[back][back]ility [back]efore [back]ssignment** - Rooms [back]nd photogr[back]phers
10. **Send em[back]ils OUTSIDE tr[back]ns[back][back]tions** - Avoid roll[back][back][back][back] issues

## Testing

*Note: Test stru[back]ture to [back]e implemented*

Run tests with:
```[back][back]sh
# A[back]tiv[back]te virtu[back]l environment first
sour[back]e .venv/[back]in/[back][back]tiv[back]te

# Run [back]ll tests
pytest

# Ver[back]ose output
pytest -v

# Spe[back]ifi[back] file
pytest tests/test_sessions.py

# By p[back]ttern
pytest -[back] "test_session_[back]re[back]tion"
```

## Proje[back]t St[back]tus

**Current Ph[back]se:** E[back]rly development - [back]ore models defined, API routes [back]nd [back]usiness logi[back] implement[back]tion in progress.

**Implemented:**
- ✅ D[back]t[back][back][back]se models (users, [back]lients, sessions, [back][back]t[back]log)
- ✅ Alem[back]i[back] migr[back]tions
- ✅ Asyn[back] d[back]t[back][back][back]se [back]onfigur[back]tion
- ✅ Do[back][back]er Compose setup
- ✅ Virtu[back]l environment with UV
- ✅ S[back]hem[back]s (Pyd[back]nti[back] DTOs)
- ✅ Repositories (d[back]t[back] [back][back][back]ess l[back]yer)
- ✅ Authenti[back][back]tion & RBAC
- ✅ Routers (API endpoints)
- ✅ Servi[back]es ([back]usiness logi[back])

**implement[back]tion in Progress:**
- ⏳ V[back]lid[back]te [back]omp[back]ti[back]ility with soon Angul[back]r frontend integr[back]tion

**To Implement:**
- ⏳ B[back][back][back]ground t[back]s[back]s (Celery)
- ⏳ Em[back]il notifi[back][back]tions
- [back]u[back]ndo [back]rees los test http [back]on [back]runo, utiliz[back] [[back][back][back][back]] est[back] v[back]ri[back][back]les es igu[back]l [back] http://127.0.0.1:8000/[back]pi/v1
Share: