# CLAUDE.md

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

## Architecture Overview

Vanilla PHP application (no framework). Two distinct parts:

1. **Web portal** (`public/*.php`) — Client-facing portal for stock search, cart, and invoice history.
2. **REST API v1** (`public/api/v1/`) — Stateless JSON API for external integrations (e-commerce, ERP).

Legacy API endpoints exist in `public/api/` (single-file handlers, older pattern).

## Two Database Connections — Critical Distinction

This is the most important architectural detail:

| Variable / Class | DB | Host | Purpose |
|---|---|---|---|
| `$pdo` (global, from `config/database.php`) | `clientes_app` | localhost | Web portal + agencias/access_clientes_tmp tables |
| `TornoDatabase::getConnection()` | `torno_app` | 192.168.111.111 (PROD) / .100 (STAGE) | Web portal reads of products/prices from tornoweb |
| `TornoApiDatabase::getConnection()` | `torno_app` | same as above | API REST reads only |

**Rule:** `agencias` and `access_clientes_tmp` live in `clientes_app` (localhost). Always use `$pdo` for those tables, never `TornoDatabase`. This was a production bug (1.3.6.1).

The `estadodeposicion`, `precioarticulo`, and `product_sync_log` tables are queried via the global `$pdo` in API services (these are in `clientes_app` as well, synced from tornoweb).

## Environment

`config/env.php` controls `APP_ENV` ('STAGE' or 'PROD') and `APP_VER`. The APP_ENV value:
- Controls tornoweb host (111.100 vs 111.111)
- Controls email recipients (STAGE sends only to internal address)
- Controls HTTPS enforcement (in `config/site.php`)

Server IPs: clientes STAGE = .102, clientes PROD = .101, tornoweb STAGE = .100, tornoweb PROD = .111.

## API v1 Structure

```
public/api/v1/
├── index.php          # Router: parses path, enforces auth, dispatches to controllers
├── middleware/auth.php
├── controllers/       # Handle HTTP, validate params, call services, return JSON
├── services/          # SQL queries and business logic
└── utils/Response.php # JSON envelope helper
```

The router matches routes like `GET /products/changes` before `GET /products/{sku}` — order matters in `index.php`.

## Client ID Normalization

`cliente_id` can have leading zeros (e.g., `4126` vs `04126`). Always compare numerically:

```sql
WHERE CAST(TRIM(c.cl_codigo) AS UNSIGNED) = CAST(:cliente_id AS UNSIGNED)
```

Never use string equality for `cl_codigo` comparisons.

## Price Tiers

Client pricing is determined by `access_clientes_tmp.lista_precio` (one of: `PUBLICO`, `DISTRIBUIDOR`, `IDENTIFICADO`, `FLOTA`). The `precioarticulo` table has a column for each tier. All product queries do a `CROSS JOIN access_clientes_tmp` to pick the right price column via a `CASE` expression.

## Stock

Only `estadodeposicion.Burghi` (main warehouse) is used for stock in the API. The web portal `logic/Stock.php` may combine warehouses.

## Order Flow (Current State)

- Web portal: cart → email only. No DB write. `OrdersService::create()` exists but is **intentionally commented out** pending server migration.
- REST API: `POST /orders` endpoint exists but is disabled (returns 501). Do not re-enable without explicit instruction.

## Authentication (Portal)

Session guards in `config/site.php`: idle timeout 30 min, absolute 8 hours, session regeneration every 5 min. Login throttling: after 2 failures reCAPTCHA required; after 5 failures, 30-min lockout.

## Key Config Files (Not in git)

- `config/api.php` — API key for REST v1
- `config/mail.php` — SMTP credentials
- `config/recaptcha.php` — reCAPTCHA keys

## Delta Sync (product_sync_log)

MySQL triggers on `estadodeposicion` and `precioarticulo` write to `product_sync_log` when products change. The `GET /products/changes?since=...` endpoint reads this table. Schema in `database/migrations/product_sync_log.sql`.

## Error Logging

API v1 errors → `logs/api_errors.log`. This directory must exist on the server (`mkdir -p logs` if missing on a fresh deploy).

## Versioning

Bump `APP_VER` in `config/env.php` with each deploy. Format: `major.minor.patch.hotfix` (e.g., `1.3.6.2`). Used for asset cache-busting in frontend pages.
