Metadata-Version: 2.4
Name: py_restfull_gazie
Version: 0.1.8
Summary: Api Restfull for GAzie
Home-page: https://www.newstechnology.eu
Author: Daniele Frulla
Author-email: daniele.frulla@newstechnology.eu
Keywords: python,GAzie,ERP,Gestione Aziendale,py-gazie,gazie,gazie database,php-crud-api,api gazie,restfull gazie
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: py_gazie
Requires-Dist: fastapi>=0.100.0
Requires-Dist: uvicorn[standard]>=0.20.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: PyJWT>=2.8.0
Requires-Dist: typing-inspect
Requires-Dist: fastapi-utils
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: httpx2; extra == "dev"
Dynamic: author
Dynamic: author-email
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# Python Restfull API GAzie DB 

## Installazione

1. **PIP**:
    ```python
    pip install py_restfull_gazie
    ```

## Adattare GAzie a CRUD PHP API

#### Procedure da Eseguire Su DB GAzie:

Prima di procedere occorre che il DB GAzie si
pronto ad usare CRUD PHP API inserendo in 
tabella una chiave primaria 'id'.

Utilizzare lo script seguento per farlo.

```
DELIMITER //

DROP PROCEDURE IF EXISTS AddIdPrimaryKeyToGazie //

CREATE PROCEDURE AddIdPrimaryKeyToGazie()
BEGIN
    -- =========================================================================
    -- 1. TUTTE LE DICHIARAZIONI (Devono stare in cima)
    -- =========================================================================
    DECLARE done INT DEFAULT FALSE;
    DECLARE current_table VARCHAR(255);
    DECLARE old_pk_columns VARCHAR(512);
    
    -- Cursore per escludere tabelle con 'id' o con qualsiasi altro AUTO_INCREMENT
    DECLARE table_cursor CURSOR FOR 
        SELECT t.TABLE_NAME 
        FROM information_schema.TABLES t
        WHERE t.TABLE_SCHEMA = DATABASE()
          AND t.TABLE_NAME LIKE 'gaz_%'
          AND NOT EXISTS (
              SELECT 1 
              FROM information_schema.COLUMNS c 
              WHERE c.TABLE_SCHEMA = DATABASE() 
                AND c.TABLE_NAME = t.TABLE_NAME 
                AND c.COLUMN_NAME = 'id'
          )
          AND NOT EXISTS (
              SELECT 1 
              FROM information_schema.COLUMNS c 
              WHERE c.TABLE_SCHEMA = DATABASE() 
                AND c.TABLE_NAME = t.TABLE_NAME 
                AND c.EXTRA LIKE '%auto_increment%'
          );

    -- L'handler deve essere l'ultima cosa dichiarata
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;


    -- =========================================================================
    -- 2. ISTRUZIONI ESEGUIBILI
    -- =========================================================================
    
    -- Creazione della tabella temporanea per il LOG
    DROP TEMPORARY TABLE IF EXISTS temp_log_alter_tables;
    CREATE TEMPORARY TABLE temp_log_alter_tables (
        id INT AUTO_INCREMENT PRIMARY KEY,
        eseguito_alle TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        tabella_modificata VARCHAR(255),
        query_eseguita TEXT
    );

    OPEN table_cursor;

    read_loop: LOOP
        FETCH table_cursor INTO current_table;
        
        IF done THEN
            LEAVE read_loop;
        END IF;

        -- Recupera dinamicamente le colonne che compongono l'attuale chiave primaria
        SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ', ')
        INTO old_pk_columns
        FROM information_schema.KEY_COLUMN_USAGE
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = current_table
          AND CONSTRAINT_NAME = 'PRIMARY';

        -- Se esiste una vecchia chiave primaria, procediamo
        IF old_pk_columns IS NOT NULL THEN
            
            SET @dynamic_sql = CONCAT(
                'ALTER TABLE `', current_table, '` ',
                'DROP PRIMARY KEY, ',
                'ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST, ',
                'ADD UNIQUE KEY `uk_', current_table, '_old_pk` (', old_pk_columns, ');'
            );

            -- Scrittura nel LOG
            INSERT INTO temp_log_alter_tables (tabella_modificata, query_eseguita) 
            VALUES (current_table, @dynamic_sql);

            -- Esecuzione della query
            PREPARE stmt FROM @dynamic_sql;
            EXECUTE stmt;
            DEALLOCATE PREPARE stmt;
            
        END IF;

    END LOOP;

    CLOSE table_cursor;
    
    -- 3. Stampa a schermo del risultato finale
    SELECT * FROM temp_log_alter_tables;
    
    -- Pulizia finale
    DROP TEMPORARY TABLE IF EXISTS temp_log_alter_tables;

END //

DELIMITER ;
```

#### Chiamare la Procedura

```
CALL AddIdPrimaryKeyToGazie();
```


## Uso

Il server espone le tabelle GAzie come risorse JSON sotto il prefisso
`PREFIX_API` (default `/contabile`). In modalità `DEVELOPMENT=true` la
documentazione interattiva (Swagger) è disponibile su `/docs`.

| Endpoint | Metodi | Chiave del record singolo |
|---|---|---|
| `/health/` | GET | — |
| `/products/` | GET, POST | `/products/{codice}` — stringa con zeri iniziali (`00100010001`) |
| `/anagra/` | GET, POST | `/anagra/{id}` |
| `/companies/` | GET, POST | `/companies/{codice}` |
| `/customers/` | GET, POST | `/customers/{codice}` |
| `/documents/` | GET | `/documents/{id_tes}` — sola lettura |

Ogni risorsa con lista accetta i parametri `page` (default 1), `limit`
(1-100, default 10), `company` (azienda GAzie, default 1) e `search`:
ricerca sui campi della risorsa (`codice`/`descri` per i prodotti,
`ragso1`, `ragso2`, `pariva`, `e_mail`, `telefo` sull'anagrafica per i
clienti, `numfat`/`numdoc`/`tipdoc` per i documenti). Senza `search`
non viene applicato alcun filtro.

Le liste rispondono con la struttura standard:

```json
{"data": [ ... ],
 "pagination": {"page": 1, "limit": 10, "total_records": 8507,
                "total_pages": 851, "has_next": true, "has_previous": false}}
```

Codici di errore: `422` parametri non validi · `400` operazione fallita
su Gazie · `404` record non trovato · `500` errore imprevisto.

### Cliente di un'anagrafica (`/customers/by_anagra`)

Cerca i clienti collegati a una scheda anagrafica. In `gaz_001clfoco` non
c'è la ragione sociale, solo il puntatore `id_anagra` verso `anagra`:
serve per stabilire se un'anagrafica ha già un cliente aperto prima di
crearne uno.

```
GET /contabile/customers/by_anagra?id_anagra=42&company=1&page=1&limit=10
```

`id_anagra` è obbligatorio; `page`, `limit` e `company` hanno gli stessi
significato e limiti delle altre risorse. La risposta usa la struttura
standard `{"data": [...], "pagination": {...}}`: un'anagrafica senza
clienti risponde `200` con `data` vuota, non un errore.

### Documenti (`/documents/`)

Ogni testata riporta il tipo di documento tradotto dal campo `tipdoc`
più i flag `e_fattura` ed `e_ricevuta`:

```json
{"id_tes": 6, "tipdoc": "FAI", "numdoc": 1, "numfat": "1",
 "datfat": "2011-01-22", "tipo_documento": "Fattura",
 "e_fattura": true, "e_ricevuta": false}
```

| `tipdoc` | `tipo_documento` |
|---|---|
| `FAI`, `AFA`, `VRI`, `AFC`, `AFT` | Fattura |
| `ADT`, `DDT`, `DDR` | DDT |
| `FNC` | Nota di credito |
| `RIC`, `RF`, `RFR`, `RFI` | Ricevuta *(sigle preventive: assenti nel DB attuale, appena emesse sono già gestite)* |
| qualsiasi altro codice | resta il codice grezzo (trasparente) |

- `GET /documents/` — elenco paginato (8507 testate); `search` prova su
  numero fattura, numero documento e tipo.
- `GET /documents/{id_tes}` — testata singola per `id_tes` (la chiave di
  `tesdoc` è `id_tes`, non `id`); `404` se assente.
- Sola lettura: la scrittura dei documenti (testata + righe) non è
  ancora esposta.

## Configurazione

Per la configurazione occore un file *.env*:

```
# Configurazione generica 
PREFIX_API=/contabile
GAZIE_URL=http://127.0.0.1:8082/modules/api/index.php
APP_TOKEN=test-token
PORT_EXPOSE=5002
DEVELOPMENT=true
```

## Eseguire lo script api:

```
python -m py_restfull_gazie.main
```

Sia avviera' il server restfull api con swagger.
