---
title: "Integrazione HR"
url: "https://www.4hse.com/it/news/2024-03-04"
description: "Esploreremo come sviluppare un software che, sfruttando le API fornite da 4hse, consenta a qualsiasi software HR di integrarsi con 4HSE mantenendo i dati anagrafici del personale costantemente sincronizzati. Non ci concentreremo su un software HR specifico, ma adotteremo un approccio generico al problema, sviluppando uno script di integrazione facilmente adattabile alle diverse soluzioni HR presenti sul mercato."
---

10 dic 2024

# Integrazione HR

Esploreremo come sviluppare un software che, sfruttando le API fornite da 4hse, consenta a qualsiasi software HR di integrarsi con 4HSE mantenendo i dati anagrafici del personale costantemente sincronizzati. Non ci concentreremo su un software HR specifico, ma adotteremo un approccio generico al problema, sviluppando uno script di integrazione facilmente adattabile alle diverse soluzioni HR presenti sul mercato.

![Integrazione HR](/_astro/hero.Ch7BhWJ3_ZtCFC3.webp)

## Introduzione

4HSE offre strumenti per la gestione delle anagrafiche del personale. Tuttavia, è sempre più comune che aziende consolidate desiderino continuare a usare il proprio software HR e **integrarlo** con un sistema di gestione della sicurezza come 4HSE. Questa esigenza si presenta sia nella fase di avvio del progetto che nelle successive fasi operative quotidiane, dove è fondamentale mantenere un allineamento solido tra i due sistemi.

Esploreremo come sviluppare un software che, sfruttando le **API** messe a disposizione da 4hse, consenta a qualsiasi **software HR di integrarsi con 4HSE**, mantenendo i dati anagrafici del personale sempre sincronizzati.

Non ci concentreremo su un software HR specifico, ma adotteremo un approccio generico al problema, sviluppando uno script di integrazione facilmente adattabile alle diverse soluzioni HR presenti sul mercato.

## Prerequisiti

Cosa ci serve per sviluppare uno script di integrazione HR?

-   **Fonte dei dati**: useremo un semplice file CSV come sorgente, che immaginiamo venga esportato periodicamente dal database del software HR per allinearsi con 4hse ogni 24 ore.
-   `project_id`: Dobbiamo avere un progetto attivo in 4hse e conoscerne l’ID. Puoi trovare l’ID nella sezione “info” della finestra del progetto.
-   `access_token`: Ci serve un utente attivo con ruolo di amministratore per il progetto indicato, poiché sarà lui a eseguire la sincronizzazione. Per autenticare questo utente tramite API, abbiamo bisogno del suo access token. Il token viene fornito da 4hse su richiesta del cliente.

### Fonte dei dati

Per preparare il file CSV estratto dalla fonte dati, dobbiamo sempre considerare queste semplici regole:

1.  Il file segue una struttura di colonne predefinita da 4hse.
2.  Il file contiene sempre l’intero dataset aggiornato dei dipendenti.
3.  I valori dei campi per ciascun dipendente nel dataset sovrascrivono i valori dello stesso dipendente in 4hse.
4.  I dipendenti presenti in 4hse ma non nella sorgente verranno storicizzati.
5.  I dipendenti presenti nella sorgente ma non in 4hse verranno aggiunti in 4hse.

The CSV file consists of the following columns:

field

required

code

no

first\_name

yes

last\_name

yes

street

no

locality

no

postal\_code

no

region

no

sex

no

country

no

birth\_date

no

birth\_place

no

tax\_code

no

note

no

is\_employee

yes

Tra questi campi è necessario identificare un campo chiave, cioè un campo il cui valore identifica univocamente un dipendente. Può essere uno qualsiasi di questi campi, di solito il `code` o il codice fiscale `tax_code`.

### REST API di 4HSE

4hse fornisce una REST API standard OpenAPI per eseguire e monitorare la sincronizzazione. L’endpoint `create` riceve una richiesta contenente un file CSV di input e crea un’attività sui server di elaborazione di 4hse. Il servizio risponde immediatamente al successo della creazione dell’attività, fornendo l’identificativo univoco dell’attività. L’elaborazione dell’attività verrà eseguita il prima possibile. L’endpoint `view` fornisce lo stato di elaborazione dell’attività identificata dal suo ID.

La documentazione completa delle API è disponibile qui: [PeopleSync API](https://docs.4hse.com/it/api/peoplesync/).

#### Esempio

In questo esempio vogliamo stabilire una sincronizzazione periodica tra i dati estratti dal nostro gestionale HR e il progetto “La mia azienda” in 4hse. Supponiamo che il file CSV `employees-1.csv` sia stato estratto dal nostro software HR e trasformato per seguire la struttura prevista da 4hse. Il file contiene 7 dipendenti.

code

first\_name

last\_name

street

locality

postal\_code

region

sex

country

birth\_date

birth\_place

tax\_code

note

is\_employee

1

John

Doe

123 Main St

Anytown

12345

CA

M

USA

1980-05-15

Anytown

ABC123456

Note for John Doe

1

2

Jane

Smith

456 Oak St

Someville

67890

NY

F

USA

1985-08-21

Someville

DEF789012

Note for Jane Smith

1

3

Robert

Johnson

789 Pine St

Another City

54321

TX

M

USA

1972-11-03

Another City

GHI345678

Note for Robert Johnson

1

4

Emily

Davis

101 Elm St

Yourtown

98765

FL

F

USA

1992-04-18

Yourtown

JKL901234

Note for Emily Davis

1

5

Michael

Miller

202 Cedar St

Cityville

13579

IL

M

USA

1988-07-27

Cityville

MNO567890

Note for Michael Miller

1

6

Sophia

Brown

303 Maple St

Smalltown

24680

GA

F

USA

1995-02-09

Smalltown

PQR123456

Note for Sophia Brown

1

7

Daniel

Wilson

404 Birch St

Newtown

86420

WA

M

USA

1983-09-12

Newtown

STU789012

Note for Daniel Wilson

1

-   `ACCESS_TOKEN`: È il token che identifica l’utente di 4hse che eseguirà l’operazione, un amministratore del progetto.
-   `PROJECT_ID`: È l’ID del progetto che abbiamo appena creato.
-   `PRIMARY_KEY`: È la colonna nel CSV che identifica univocamente un dipendente. Abbiamo scelto “tax\_code.”
-   `EMULATION`: Permette di eseguire la richiesta in modalità di emulazione. Questo significa che possiamo effettuare la sincronizzazione e ottenere i risultati senza applicare effettivamente le modifiche. Questo ci consente di testare l’esito dell’operazione prima di applicarla. Inizialmente imposteremo questo valore su `TRUE`.
-   `MAX_CHANGES`: È un parametro di sicurezza che controlla il numero massimo di modifiche applicabili nella richiesta. Se il numero totale di modifiche effettuate supera questo valore, verremo avvisati e la richiesta verrà rifiutata. Inizialmente lo impostiamo a `150`.

Ora definiamo il metodo `createTask`, che verrà usato per creare l’attività di sincronizzazione presso il servizio `people-sync` di 4hse.  
The creation of the task is a simple `HTTP POST` request to the server, which responds with a `task_id`.  
The task ID is an identifier for the task that allows us to query the server to know its progress (queued, executed, etc.).

The method only requires the input of the file name to synchronize and provides the output of the created task ID.

```
private static String createTask(String filePath) throws IOException {
    ...
}
```

La prima cosa da fare è istanziare un client HTTP per inviare la richiesta:

```
private static String createTask(String filePath) throws IOException {
    HttpClient httpClient = HttpClients.createDefault();
    HttpPost httpPost = new HttpPost("http://localhost:8081/people-sync/create");
}
```

Poiché dobbiamo inviare un file, i dati devono essere trasmessi in formato multipart.  
Popoliamo il corpo della richiesta multipart fornendo il file e i restanti parametri di input:

```
private static String createTask(String filePath) throws IOException {
    HttpClient httpClient = HttpClients.createDefault();
    HttpPost httpPost = new HttpPost("http://localhost:8081/people-sync/create");

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();

    // Add file parameter
    builder.addBinaryBody("file", new File(filePath),
        ContentType.APPLICATION_OCTET_STREAM, "file.csv");

    // Add other parameters
    builder.addTextBody("access-token", ACCESS_TOKEN);
    builder.addTextBody("project_id", PROJECT_ID);
    builder.addTextBody("pk", PRIMARY_KEY);
    builder.addTextBody("emulation", String.valueOf(EMULATION));
    builder.addTextBody("max_changes", String.valueOf(MAX_CHANGES));

    HttpEntity multipart = builder.build();
    httpPost.setEntity(multipart);
}
```

Infine, eseguiamo la richiesta.  
In case of success, the server will respond with a JSON that needs to be parsed to obtain the `task_id`, which we will return as output.  
In case of failure, we print the error on the screen.

```
private static String createTask(String filePath) throws IOException {
    HttpClient httpClient = HttpClients.createDefault();
    HttpPost httpPost = new HttpPost("http://localhost:8081/people-sync/create");

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();

    // Add file parameter
    builder.addBinaryBody("file", new File(filePath), 
    ContentType.APPLICATION_OCTET_STREAM,"file.csv");

    // Add other parameters
    builder.addTextBody("access-token", ACCESS_TOKEN);
    builder.addTextBody("project_id", PROJECT_ID);
    builder.addTextBody("pk", PRIMARY_KEY);
    builder.addTextBody("emulation", String.valueOf(EMULATION));
    builder.addTextBody("max_changes", String.valueOf(MAX_CHANGES));

    HttpEntity multipart = builder.build();
    httpPost.setEntity(multipart);

    // Execute the request
    HttpResponse response = httpClient.execute(httpPost);

    // Parse the response JSON
    JSONParser parser = new JSONParser();
    try (InputStream content = response.getEntity().getContent()) {
        JSONObject jsonResponse = (JSONObject) parser.parse(new InputStreamReader(content));
        return (String) jsonResponse.get("task_id");
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
```

Definiamo ora un metodo `readTask` che, utilizzando il `task_id`, otterrà lo stato corrente dell’attività e lo stamperà a video.

```
    private static void readTask(String taskId) throws IOException {
    ...
    }
```

Analogamente al metodo precedente, instanziamo un client HTTP che, questa volta, effettua una richiesta utilizzando il metodo `GET`, fornendo il `task_id` e il `access-token` per l’identificazione dell’utente.

```
    private static void readTask(String taskId) throws IOException {
        HttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://localhost:8081/people-sync/view?id=" + taskId + "&access-token=" + ACCESS_TOKEN);
    }
```

Eseguiamo la richiesta, analizziamo la risposta JSON e stampiamo a schermo il `task_id`.

```
private static void readTask(String taskId) throws IOException {
    HttpClient httpClient = HttpClients.createDefault();
    HttpGet httpGet = new HttpGet("http://localhost:8081/people-sync/view?id=" + taskId + "&access-token=" + ACCESS_TOKEN);

    // Execute the request
    HttpResponse response = httpClient.execute(httpGet);

    // Parse and print the response JSON
    JSONParser parser = new JSONParser();
    try (InputStream content = response.getEntity().getContent()) {
        JSONObject jsonResponse = (JSONObject) parser.parse(new InputStreamReader(content));
        System.out.println("Task: " + jsonResponse.toJSONString());
    } catch (Exception e) {
        e.printStackTrace();
    }
}
```

Infine definiamo il metodo `main`, che sarà il punto di ingresso del nostro script. I comandi disponibili saranno:

Ecco la conversione in Markdown:

-   `create` to create the task
    
    ```
    java -jar your-jar-file.jar create filename.csv
    ```
    
-   `read` to read the task
    
    ```
    java -jar your-jar-file.jar read task-id
    ```
    

Verifichiamo quindi che siano stati forniti i parametri di input, altrimenti mostriamo un messaggio a schermo.

```
public static void main(String[] args) {
    String command = args[0]; //create or read

    if (args.length < 2) {
        System.out.println("Usage:");
        System.out.println("java -jar your-jar-file.jar create file.csv");
        System.out.println("java -jar your-jar-file.jar read task-id");
        return;
    }
}
```

Verifichiamo quindi se il primo parametro passato è `create` oppure `read` e chiamiamo i rispettivi metodi, passando il nome del file CSV oppure l’ID dell’attività.

```
    public static void main(String[] args) {
        String command = args[0]; //create or read

        if (args.length < 2) {
            System.out.println("Usage:");
            System.out.println("java -jar your-jar-file.jar create file.csv");
            System.out.println("java -jar your-jar-file.jar read task-id");
            return;
        }

        try {
            if ("create".equals(args[0])) {
            String taskId = createTask(args[1]);
            System.out.println("Created Task ID: " + taskId);
            }
            else if ("read".equals(args[0])) {
                readTask(args[1]);
            }
            else {
                System.out.println("Action must be 'create' or 'read'");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
```

Eseguiamo ora lo script avviando la creazione dell’attività con il comando:

```
java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar 
create employees2-1.csv
```

In risposta, otteniamo l’ID dell’attività creata:

```
Created Task ID: be9eec92-621c-31b1-a525-7df391f599bf
```

Dopo qualche secondo, eseguiamo il comando per leggere lo stato dell’attività creata:

```
java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar 
read be9eec92-621c-31b1-a525-7df391f599bf
```

In risposta, otteniamo il report completo delle azioni eseguite dal server:

-   `task_id`: ID dell’attività
-   `entity_id`: ID del progetto
-   `user_id`: l’utente che ha creato l’attività
-   `status`: indica se l’attività è stata eseguita con successo o meno
-   `created_at`, `updated_at`: forniscono riferimenti temporali alla creazione e all’aggiornamento dell’attività
-   `response.result`: è una cronologia delle operazioni eseguite per ogni record nel nostro file CSV. In questo caso, essendo nuovi dipendenti, ciascun dipendente, identificato dal proprio `tax_code`, è stato aggiunto al progetto.
-   `response.changes`: è un riepilogo di tutte le operazioni eseguite. In questo caso, l’attività ha effettuato un totale di 7 modifiche, tutte “aggiunte.”
-   `log`: il registro del server registrato durante l’esecuzione dell’attività. È utile per identificare problemi in caso di errori.

```
{
    "task_id": "a386a5bf-5bc4-3be7-9392-c81c69918bfc",
    "entity_id": "41ec7395-eeda-4c8e-bc73-5585d48e5161",
    "user_id": "john@4hse.com",
    "status": "DONE",
    "created_at": "2024-01-11 08:14:53",
    "updated_at": "2024-01-11 08:15:07",
    "response": {
        "result": {
            "ABC123456": [
                {
                    "action": "add",
                    "id": "ABC123456"
                }
            ],
            "PQR123456": [
                {
                    "action": "add",
                    "id": "PQR123456"
                }
            ],
            "DEF789012": [
                {
                    "action": "add",
                    "id": "DEF789012"
                }
            ],
            "STU789012": [
                {
                    "action": "add",
                    "id": "STU789012"
                }
            ],
            "MNO567890": [
                {
                    "action": "add",
                    "id": "MNO567890"
                }
            ],
            "GHI345678": [
                {
                    "action": "add",
                    "id": "GHI345678"
                }
            ],
            "JKL901234": [
                {
                    "action": "add",
                    "id": "JKL901234"
                }
            ]
        },
        "log": [
            {
                "level": "INFO",
                "text": "Task a386a5bf-5bc4-3be7-9392-c81c69918bfc executed",
                "timestamp": 1704960907
            },
            {
                "level": "INFO",
                "text": "Executing task a386a5bf-5bc4-3be7-9392-c81c69918bfc in emulation mode",
                "timestamp": 1704960906
            }
        ],
        "changes": {
            "add": 7,
            "total": 7,
            "activate": 0,
            "update": 0,
            "deactivate": 0
        }
    }
}
```

A questo punto cambiamo il parametro di emulazione in `false` e rieseguiamo il comando di creazione.

```
private final static boolean EMULATION = false;
```

Riceveremo la stessa risposta di prima, ma in questo caso, le modifiche sono state effettivamente eseguite e, di conseguenza, avremo 7 dipendenti nel progetto “La mia azienda”.

Supponiamo ora che in azienda 2 persone siano andate in pensione. Consequently, our HR has created a new file `employees2-2.csv` containing only the remaining 5 individuals. Additionally, for “John Doe” the `NOTE` field has been modified. In total, there are 3 differences compared to the `employees2-1.csv` file.

code

first\_name

last\_name

street

locality

postal\_code

region

sex

country

birth\_date

birth\_place

tax\_code

note

is\_employee

1

John

Doe

123 Main St

Anytown

12345

CA

M

USA

1980-05-15

Anytown

ABC123456

Note for John Doe

1

2

Jane

Smith

456 Oak St

Someville

67890

NY

F

USA

1985-08-21

Someville

DEF789012

Note for Jane Smith

1

3

Robert

Johnson

789 Pine St

Another City

54321

TX

M

USA

1972-11-03

Another City

GHI345678

Note for Robert Johnson

1

4

Emily

Davis

101 Elm St

Yourtown

98765

FL

F

USA

1992-04-18

Yourtown

JKL901234

Note for Emily Davis

1

5

Michael

Miller

202 Cedar St

Cityville

13579

IL

M

USA

1988-07-27

Cityville

MNO567890

Note for Michael Miller

1

Modifichiamo lo script, in particolare il parametro `MAX_CHANGES`, riducendolo a 2.

```
private final static int MAX_CHANGES = 2;
```

Let’s run the create command again, this time sending the `employees2-2.csv` file:

```
java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar create 
employees2-2.csv
```

Leggiamo lo stato dell’attività appena creata e riscontriamo un errore: il numero effettivo di modifiche (3) supera il massimo consentito dalla richiesta (2).

```
{
    "task_id": "b9819d5e-a0a7-34e0-844d-cde54ed91858",
    "entity_id": "41ec7395-eeda-4c8e-bc73-5585d48e5161",
    "user_id": "john@4hse.com",
    "status": "FAILED",
    "created_at": "2024-01-11 09:19:03",
    "updated_at": "2024-01-11 09:19:21",
    "response": {
        "log": [
            {
                "level": "ERROR",
                "text": "Out of max changes",
                "timestamp": 1704964761
            }
        ]
    }
}
```

Modifichiamo di nuovo il valore di `MAX_CHANGES`, impostandolo a `150`.

```
private final static int MAX_CHANGES = 150;
```

Rieseguiamo il comando per la creazione dell’attività e controlliamo il suo stato:

```
{
    "task_id": "b7ede071-e5c2-37c9-8075-9cd1b5580f26",
    "entity_id": "41ec7395-eeda-4c8e-bc73-5585d48e5161",
    "user_id": "john@4hse.com",
    "created_at": "2024-01-11 08:22:36",
    "updated_at": "2024-01-11 08:23:16",
    "status": "DONE",
    "response": {
        "result": {
            "ABC123456": [
                {
                    "changes": {
                        "note": "Another note for John Doe"
                    },
                    "action": "update",
                    "id": "ABC123456"
                }
            ],
            "PQR123456": [
                {
                    "action": "deactivate",
                    "periods": [
                        {
                            "end_date": "2024-01-11 00:00:00",
                            "start_date": "1970-01-01 00:00:00"
                        }
                    ],
                    "id": "PQR123456"
                }
            ],
            "STU789012": [
                {
                    "action": "deactivate",
                    "periods": [
                        {
                            "end_date": "2024-01-11 00:00:00",
                            "start_date": "1970-01-01 00:00:00"
                        }
                    ],
                    "id": "STU789012"
                }
            ]
        },
        "log": [
            {
                "level": "INFO",
                "text": "Task b7ede071-e5c2-37c9-8075-9cd1b5580f26 executed",
                "timestamp": 1704961396
            },
            {
                "level": "INFO",
                "text": "Executing task b7ede071-e5c2-37c9-8075-9cd1b5580f26 in emulation mode",
                "timestamp": 1704961396
            }
        ],
        "changes": {
            "add": 0,
            "total": 3,
            "activate": 0,
            "update": 1,
            "deactivate": 2
        }
    }
}
```

La risposta mostra le tre modifiche attese. I due dipendenti pensionati sono stati storicizzati e la nota del dipendente è stata aggiornata.

## Conclusioni

Abbiamo visto quanto sia semplice sincronizzare il database HR con 4hse, mantenendo entrambi i sistemi perfettamente allineati. Nel prossimo tutorial estenderemo questo esempio per sfruttare meccanismi di sincronizzazione più approfonditi.

Fonti: [https://github.com/4hse/example-people-sync](https://github.com/4hse/example-people-sync)