correzioni finali
This commit is contained in:
parent
c49757aa13
commit
895860e906
4 changed files with 106 additions and 11 deletions
68
README.md
Normal file
68
README.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Backend – Gestione Rimborsi Spese Aziendali (Prova S5)
|
||||
|
||||
API REST per la gestione delle richieste di rimborso spese aziendali.
|
||||
|
||||
## URL produzione
|
||||
|
||||
| Risorsa | URL |
|
||||
|---------|-----|
|
||||
| API | https://provapraticabackend.andreavillari.it |
|
||||
| Health check | https://provapraticabackend.andreavillari.it/api/health |
|
||||
| Swagger UI | https://provapraticabackend.andreavillari.it/api/docs |
|
||||
| Frontend collegato | https://provapraticafrontend.andreavillari.it |
|
||||
|
||||
## Credenziali di test
|
||||
|
||||
Password comune per tutti gli utenti: `Password123!`
|
||||
|
||||
| Ruolo | Email |
|
||||
|-------|-------|
|
||||
| Responsabile amministrativo | `admin@azienda.it` |
|
||||
| Dipendente | `mario.rossi@azienda.it` |
|
||||
| Dipendente | `marco.bianchi@azienda.it` |
|
||||
| Dipendente | `anna.verdi@azienda.it` |
|
||||
|
||||
È possibile anche registrare nuovi utenti da `/register` sul frontend, scegliendo il ruolo.
|
||||
|
||||
## Avvio locale
|
||||
|
||||
Configurare il file `.env` (AUTH_SECRET, credenziali DB, URL frontend/backend, ecc.).
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Con Docker (app + MySQL + phpMyAdmin):
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
MySQL è esposto su `127.0.0.1:3307`, l'app su `127.0.0.1:3003`.
|
||||
|
||||
Gli script in `initdb/` vengono eseguiti **solo al primo avvio** del volume MySQL (database vuoto).
|
||||
|
||||
## Dati iniziali
|
||||
|
||||
Il seed (`initdb/02_seed.sql`) crea:
|
||||
|
||||
- 4 utenti di test (1 responsabile, 3 dipendenti)
|
||||
- 5 categorie di spesa
|
||||
- 10 richieste di rimborso in tutti gli stati (`IN_ATTESA`, `APPROVATA`, `RIFIUTATA`, `LIQUIDATA`)
|
||||
|
||||
Se il database di produzione è stato creato **prima** dell'aggiornamento del seed:
|
||||
|
||||
- ricreare il volume Docker (`docker compose down -v` poi `up`), oppure
|
||||
- eseguire `scripts/apply-missing-test-users.sql` via phpMyAdmin o CLI MySQL
|
||||
|
||||
## Stack
|
||||
|
||||
- Node.js 20, Express, TypeScript
|
||||
- MySQL 8
|
||||
- Auth.js (sessione JWT via cookie)
|
||||
- Swagger UI (`/api/docs`)
|
||||
|
||||
## Deploy
|
||||
|
||||
Push su `main` → Forgejo Actions → SSH sul VPS → `docker compose up -d --build` in `/root/provapraticabackend`.
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
-- ============================================================
|
||||
-- Dati iniziali di test – Gestione Rimborsi Spese Aziendali
|
||||
-- Utenti: già presenti in tabella utenti (id 1-4)
|
||||
-- 1 andrea villari – responsabile valutazioni
|
||||
-- 2 mario rossi
|
||||
-- 3 marco bianchi
|
||||
-- 4 anna verdi
|
||||
-- Password per tutti gli utenti: Password123!
|
||||
-- 1 Andrea Villari – admin@azienda.it (responsabile)
|
||||
-- 2 Mario Rossi – mario.rossi@azienda.it (dipendente)
|
||||
-- 3 Marco Bianchi – marco.bianchi@azienda.it (dipendente)
|
||||
-- 4 Anna Verdi – anna.verdi@azienda.it (dipendente)
|
||||
-- ============================================================
|
||||
|
||||
USE provapratica;
|
||||
|
||||
-- Ruoli corretti per gli utenti di test (id 1 = responsabile, 2-4 = dipendenti)
|
||||
UPDATE utenti SET ruolo = 'RESPONSABILE_AMMINISTRATIVO' WHERE utente_id = 1;
|
||||
UPDATE utenti SET ruolo = 'DIPENDENTE' WHERE utente_id IN (2, 3, 4);
|
||||
INSERT INTO utenti (nome, cognome, email, password_hash, ruolo) VALUES
|
||||
('Andrea', 'Villari', 'admin@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'RESPONSABILE_AMMINISTRATIVO'),
|
||||
('Mario', 'Rossi', 'mario.rossi@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'DIPENDENTE'),
|
||||
('Marco', 'Bianchi', 'marco.bianchi@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'DIPENDENTE'),
|
||||
('Anna', 'Verdi', 'anna.verdi@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'DIPENDENTE');
|
||||
|
||||
INSERT IGNORE INTO categorie_spesa (descrizione) VALUES
|
||||
('Trasferta'),
|
||||
|
|
|
|||
14
scripts/apply-missing-test-users.sql
Normal file
14
scripts/apply-missing-test-users.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- Eseguire manualmente su un database già esistente (es. produzione)
|
||||
-- se gli utenti di test non sono presenti.
|
||||
-- Password per tutti: Password123!
|
||||
|
||||
USE provapratica;
|
||||
|
||||
INSERT IGNORE INTO utenti (nome, cognome, email, password_hash, ruolo) VALUES
|
||||
('Andrea', 'Villari', 'admin@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'RESPONSABILE_AMMINISTRATIVO'),
|
||||
('Mario', 'Rossi', 'mario.rossi@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'DIPENDENTE'),
|
||||
('Marco', 'Bianchi', 'marco.bianchi@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'DIPENDENTE'),
|
||||
('Anna', 'Verdi', 'anna.verdi@azienda.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'DIPENDENTE');
|
||||
|
||||
UPDATE utenti SET ruolo = 'RESPONSABILE_AMMINISTRATIVO' WHERE email = 'admin@azienda.it';
|
||||
UPDATE utenti SET ruolo = 'DIPENDENTE' WHERE email IN ('mario.rossi@azienda.it', 'marco.bianchi@azienda.it', 'anna.verdi@azienda.it');
|
||||
|
|
@ -77,13 +77,18 @@ router.post("/", async (req, res) => {
|
|||
});
|
||||
|
||||
router.put("/:id", async (req, res) => {
|
||||
if (req.user!.ruolo !== "DIPENDENTE") {
|
||||
res.status(403).json({ error: "Solo i dipendenti possono modificare le proprie richieste" });
|
||||
return;
|
||||
}
|
||||
|
||||
const id = Number(req.params.id);
|
||||
const existing = await getRimborsoById(id);
|
||||
if (!existing) {
|
||||
res.status(404).json({ error: "Richiesta non trovata" });
|
||||
return;
|
||||
}
|
||||
if (req.user!.ruolo === "DIPENDENTE" && existing.dipendente_id !== req.user!.id) {
|
||||
if (existing.dipendente_id !== req.user!.id) {
|
||||
res.status(403).json({ error: "Accesso non consentito" });
|
||||
return;
|
||||
}
|
||||
|
|
@ -108,13 +113,18 @@ router.put("/:id", async (req, res) => {
|
|||
});
|
||||
|
||||
router.delete("/:id", async (req, res) => {
|
||||
if (req.user!.ruolo !== "DIPENDENTE") {
|
||||
res.status(403).json({ error: "Solo i dipendenti possono eliminare le proprie richieste" });
|
||||
return;
|
||||
}
|
||||
|
||||
const id = Number(req.params.id);
|
||||
const existing = await getRimborsoById(id);
|
||||
if (!existing) {
|
||||
res.status(404).json({ error: "Richiesta non trovata" });
|
||||
return;
|
||||
}
|
||||
if (req.user!.ruolo === "DIPENDENTE" && existing.dipendente_id !== req.user!.id) {
|
||||
if (existing.dipendente_id !== req.user!.id) {
|
||||
res.status(403).json({ error: "Accesso non consentito" });
|
||||
return;
|
||||
}
|
||||
|
|
@ -144,7 +154,8 @@ router.put("/:id/approva", requireRole("RESPONSABILE_AMMINISTRATIVO"), async (re
|
|||
|
||||
router.put("/:id/rifiuta", requireRole("RESPONSABILE_AMMINISTRATIVO"), async (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const motivazione = req.body.motivazione?.toString().trim() || null;
|
||||
const raw = req.body.motivazione?.toString().trim();
|
||||
const motivazione = raw || "Richiesta non approvata dal responsabile amministrativo";
|
||||
const ok = await rifiutaRimborso(id, req.user!.id, motivazione);
|
||||
if (!ok) {
|
||||
res.status(400).json({ error: "Impossibile rifiutare la richiesta" });
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue