Replica il workflow del foglio Excel originale dell'istruttoria Gepafin. Pattern preso da DomandaEditPreInstructor/components/ListOfFiles. Pagina IstruttoriaPratica riscritta (858 righe): RIEPILOGO FINANZIARIO esteso: - Totale dichiarato (dal beneficiario) - Totale verificato (somma AMMESSA + PARZIALE istruttore) - Cap remissione (min(50% erogato, 12500)) - Remissione da riconoscere (da verificato) - Residuo da restituire (erogato - remissione) VERIFICA FATTURE per categoria con appPageSection__list: - Ogni fattura come row con numero, fornitore, date, descrizione, Tag stato - Tag rosso 'Date fuori periodo' se invoice_in_period=false O payment_in_period=false - Riga dichiarato + riga verificato (se presente) - Note rettifica evidenziate con barra arancione - 5 pulsanti icona: eye (anteprima PDF) + download + pencil (rettifica con dialog) + thumbs-up AMMESSA + thumbs-down RESPINTA - Thumbs up/down = ammissione/rifiuto rapido senza rettifica - Pencil = dialog con dichiarato readonly + verificato editabile + note obbligatorie -> PARZIALE VERIFICA ULA: - Stesso pattern: eye/download/pencil/up/down - Rettifica FTE (0-1) con note VERIFICA DOCUMENTI: - eye/download/thumbs-up VALIDO - clock SCADUTO (apre dialog con motivazione) - thumbs-down NON_VALIDO (apre dialog con motivazione) VERBALE ISTRUTTORIA finale (visibile in UNDER_REVIEW/AWAITING_AMENDMENT): - 3 checkbox: documentazione completa, ULA>1, erogato in range - Textarea note sintetiche con save onBlur Approva disabilitato finché tutte le righe hanno status != PENDING. Anteprima PDF: dialog con placeholder sandbox (file reale sarà in prod). Download: toast stub (in prod scarica dal storage).
276 lines
12 KiB
JavaScript
276 lines
12 KiB
JavaScript
/**
|
|
* Client HTTP per rendicontazione-api (microservizio BFLOWS).
|
|
* Usa fetch nativa come NetworkService. Il microservizio valida lo stesso JWT di GEPAFIN-BE.
|
|
*
|
|
* Env var: REACT_APP_RENDICONTAZIONE_API_URL (es. http://78.46.41.91:18090)
|
|
*/
|
|
import { storeGet } from '../../../store';
|
|
|
|
const BASE_URL = process.env.REACT_APP_RENDICONTAZIONE_API_URL || '';
|
|
|
|
const buildHeaders = () => {
|
|
const token = storeGet('getToken');
|
|
const h = { 'Content-Type': 'application/json' };
|
|
if (token) {
|
|
h['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
return h;
|
|
};
|
|
|
|
const handleResponse = async (response, onSuccess, onError) => {
|
|
let body = null;
|
|
try {
|
|
body = await response.json();
|
|
} catch (e) {
|
|
body = { detail: response.statusText };
|
|
}
|
|
if (response.status >= 200 && response.status < 300) {
|
|
if (onSuccess) onSuccess(body);
|
|
} else {
|
|
if (onError) onError({ status: response.status, ...body });
|
|
}
|
|
};
|
|
|
|
const handleError = (err, onError) => {
|
|
if (onError) onError({ status: 0, detail: err.message });
|
|
};
|
|
|
|
const RendicontazioneService = {
|
|
getSchemaByCallId(callId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/rendicontazione-schemas/${callId}`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
})
|
|
.then(r => handleResponse(r, onSuccess, onError))
|
|
.catch(e => handleError(e, onError));
|
|
},
|
|
|
|
initializeRestartTemplate(callId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/rendicontazione-schemas/${callId}/initialize-restart`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders()
|
|
})
|
|
.then(r => handleResponse(r, onSuccess, onError))
|
|
.catch(e => handleError(e, onError));
|
|
},
|
|
|
|
updateSchema(callId, schemaJson, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/rendicontazione-schemas/${callId}`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify({ schema_json: schemaJson })
|
|
})
|
|
.then(r => handleResponse(r, onSuccess, onError))
|
|
.catch(e => handleError(e, onError));
|
|
},
|
|
|
|
publishSchema(callId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/rendicontazione-schemas/${callId}/publish`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders()
|
|
})
|
|
.then(r => handleResponse(r, onSuccess, onError))
|
|
.catch(e => handleError(e, onError));
|
|
},
|
|
|
|
deleteSchema(callId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/rendicontazione-schemas/${callId}`, {
|
|
method: 'DELETE', mode: 'cors', headers: buildHeaders()
|
|
})
|
|
.then(r => handleResponse(r, onSuccess, onError))
|
|
.catch(e => handleError(e, onError));
|
|
},
|
|
|
|
getRestartTemplatePreview(onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/rendicontazione-schemas/templates/restart`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
})
|
|
.then(r => handleResponse(r, onSuccess, onError))
|
|
.catch(e => handleError(e, onError));
|
|
}
|
|
};
|
|
|
|
export default RendicontazioneService;
|
|
|
|
// ====================== PRATICHE BENEFICIARIO ======================
|
|
|
|
const extendPractice = {
|
|
listMine(onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/mine`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
startPractice(applicationId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/start`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify({ application_id: applicationId })
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
getPractice(practiceId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
updatePractice(practiceId, patch, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(patch)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
addInvoice(practiceId, invoice, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/invoices`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(invoice)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
deleteInvoice(practiceId, invoiceId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/invoices/${invoiceId}`, {
|
|
method: 'DELETE', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
addUlaEmployee(practiceId, emp, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/ula-employees`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(emp)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
deleteUlaEmployee(practiceId, empId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/ula-employees/${empId}`, {
|
|
method: 'DELETE', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
upsertDocument(practiceId, docCode, payload, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/documents/${docCode}`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify({ doc_code: docCode, ...payload })
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
clearDocument(practiceId, docCode, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/documents/${docCode}`, {
|
|
method: 'DELETE', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
gateCheck(practiceId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/gate-check`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
submitPractice(practiceId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/${practiceId}/submit`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
// dev-only: impersonation per test beneficiary
|
|
impersonate(email, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/debug/impersonate`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify({ email })
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
}
|
|
};
|
|
|
|
// Attach to main export
|
|
Object.assign(RendicontazioneService, extendPractice);
|
|
|
|
|
|
// ====================== ISTRUTTORE ======================
|
|
|
|
const extendInstructor = {
|
|
instructorQueue(onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/queue`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
instructorViewPractice(practiceId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}`, {
|
|
method: 'GET', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
claimPractice(practiceId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/claim`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
approvePractice(practiceId, body, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/approve`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(body || {})
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
rejectPractice(practiceId, reason, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/reject`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify({ rejection_reason: reason })
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
createAmendment(practiceId, body, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/amendment`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(body)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
closeAmendment(practiceId, amendmentId, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/amendment/${amendmentId}/close`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders()
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
respondAmendmentBeneficiary(practiceId, amendmentId, responseText, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/amendment/${amendmentId}/respond-beneficiary`, {
|
|
method: 'POST', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify({ response_text: responseText })
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
}
|
|
};
|
|
|
|
Object.assign(RendicontazioneService, extendInstructor);
|
|
|
|
|
|
// ====================== VERIFICA SINGOLA RIGA ISTRUTTORE ======================
|
|
|
|
const extendVerify = {
|
|
verifyInvoice(practiceId, invoiceId, body, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/invoices/${invoiceId}/verify`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(body)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
verifyUlaEmployee(practiceId, empId, body, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/ula-employees/${empId}/verify`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(body)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
verifyDocument(practiceId, docCode, body, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/documents/${docCode}/verify`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(body)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
},
|
|
|
|
setInstructorFinalNotes(practiceId, body, onSuccess, onError) {
|
|
fetch(`${BASE_URL}/api/remission-practices/instructor/${practiceId}/final-notes`, {
|
|
method: 'PUT', mode: 'cors', headers: buildHeaders(),
|
|
body: JSON.stringify(body)
|
|
}).then(r => handleResponse(r, onSuccess, onError)).catch(e => handleError(e, onError));
|
|
}
|
|
};
|
|
|
|
Object.assign(RendicontazioneService, extendVerify);
|