requireOrgAccess(); $suppliers = Database::fetchAll( 'SELECT * FROM suppliers WHERE organization_id = ? ORDER BY criticality DESC, name', [$this->getCurrentOrgId()] ); $this->jsonSuccess($suppliers); } public function create(): void { $this->requireOrgRole(['org_admin', 'compliance_manager']); $this->validateRequired(['name', 'service_type']); $supplierId = Database::insert('suppliers', [ 'organization_id' => $this->getCurrentOrgId(), 'name' => trim($this->getParam('name')), 'vat_number' => $this->getParam('vat_number'), 'contact_email' => $this->getParam('contact_email'), 'contact_name' => $this->getParam('contact_name'), 'service_type' => $this->getParam('service_type'), 'service_description' => $this->getParam('service_description'), 'criticality' => $this->getParam('criticality', 'medium'), 'contract_start_date' => $this->getParam('contract_start_date'), 'contract_expiry_date' => $this->getParam('contract_expiry_date'), 'notes' => $this->getParam('notes'), ]); $this->logAudit('supplier_created', 'supplier', $supplierId); $this->jsonSuccess(['id' => $supplierId], 'Fornitore aggiunto', 201); } public function get(int $id): void { $this->requireOrgAccess(); $supplier = Database::fetchOne( 'SELECT * FROM suppliers WHERE id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()] ); if (!$supplier) { $this->jsonError('Fornitore non trovato', 404, 'SUPPLIER_NOT_FOUND'); } $this->jsonSuccess($supplier); } public function update(int $id): void { $this->requireOrgRole(['org_admin', 'compliance_manager']); $updates = []; $fields = ['name', 'vat_number', 'contact_email', 'contact_name', 'service_type', 'service_description', 'criticality', 'contract_start_date', 'contract_expiry_date', 'security_requirements_met', 'notes', 'status']; foreach ($fields as $field) { if ($this->hasParam($field)) { $updates[$field] = $this->getParam($field); } } if (!empty($updates)) { Database::update('suppliers', $updates, 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]); $this->logAudit('supplier_updated', 'supplier', $id, $updates); } $this->jsonSuccess($updates, 'Fornitore aggiornato'); } public function delete(int $id): void { $this->requireOrgRole(['org_admin']); $deleted = Database::delete('suppliers', 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]); if ($deleted === 0) { $this->jsonError('Fornitore non trovato', 404, 'SUPPLIER_NOT_FOUND'); } $this->logAudit('supplier_deleted', 'supplier', $id); $this->jsonSuccess(null, 'Fornitore eliminato'); } public function assessSupplier(int $id): void { $this->requireOrgRole(['org_admin', 'compliance_manager']); $this->validateRequired(['assessment_responses']); $responses = $this->getParam('assessment_responses'); $riskScore = $this->calculateSupplierRiskScore($responses); Database::update('suppliers', [ 'assessment_responses' => json_encode($responses), 'risk_score' => $riskScore, 'last_assessment_date' => date('Y-m-d'), 'next_assessment_date' => date('Y-m-d', strtotime('+6 months')), 'security_requirements_met' => $riskScore >= 70 ? 1 : 0, ], 'id = ? AND organization_id = ?', [$id, $this->getCurrentOrgId()]); $this->logAudit('supplier_assessed', 'supplier', $id, ['risk_score' => $riskScore]); $this->jsonSuccess(['risk_score' => $riskScore], 'Assessment fornitore completato'); } public function riskOverview(): void { $this->requireOrgAccess(); $overview = Database::fetchAll( 'SELECT criticality, status, COUNT(*) as count, AVG(risk_score) as avg_risk_score, SUM(CASE WHEN security_requirements_met = 0 THEN 1 ELSE 0 END) as non_compliant FROM suppliers WHERE organization_id = ? GROUP BY criticality, status', [$this->getCurrentOrgId()] ); $expiring = Database::fetchAll( 'SELECT id, name, contract_expiry_date, criticality FROM suppliers WHERE organization_id = ? AND contract_expiry_date IS NOT NULL AND contract_expiry_date <= DATE_ADD(NOW(), INTERVAL 90 DAY) AND status = "active" ORDER BY contract_expiry_date', [$this->getCurrentOrgId()] ); $this->jsonSuccess([ 'overview' => $overview, 'expiring_contracts' => $expiring, ]); } private function calculateSupplierRiskScore(array $responses): int { if (empty($responses)) return 0; $totalScore = 0; $totalWeight = 0; foreach ($responses as $resp) { $weight = $resp['weight'] ?? 1; $value = match ($resp['value'] ?? '') { 'yes', 'implemented' => 100, 'partial' => 50, default => 0, }; $totalScore += $value * $weight; $totalWeight += 100 * $weight; } return $totalWeight > 0 ? (int) round($totalScore / $totalWeight * 100) : 0; } }