Filtres
+Toutes les actions
+| Date | +Utilisateur | +Action | +Module | +Description | +
|---|
diff --git a/app/Config/Routes.php b/app/Config/Routes.php index c3c64bdc..27fc28b3 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -49,6 +49,7 @@ $routes->group('', ['filter' => 'auth'], function ($routes) { * dashboard route */ $routes->get('/', [Dashboard::class, 'index']); + $routes->get('/dashboard/getTresorerieData', [Dashboard::class, 'getTresorerieData']); $routes->get('/ventes', [Auth::class, 'ventes']); $routes->get('/ventes/(:num)', [Auth::class, 'addImage']); $routes->get('/ventes/fetchProductVente/(:num)', [Auth::class, 'fetchProductVente']); @@ -388,6 +389,13 @@ $routes->group('avances', function ($routes) { $routes->post('validateAvance', [AvanceController::class, 'validateAvance']); }); + // Historique des actions (SuperAdmin) + $routes->group('action-log', ['filter' => 'auth'], static function ($routes) { + $routes->get('/', 'ActionLogController::index'); + $routes->get('fetchData', 'ActionLogController::fetchData'); + $routes->get('export', 'ActionLogController::export'); + }); + // historique $routes->group('historique', ['filter' => 'auth'], static function ($routes) { $routes->get('/', 'HistoriqueController::index'); diff --git a/app/Controllers/ActionLogController.php b/app/Controllers/ActionLogController.php new file mode 100644 index 00000000..162b309c --- /dev/null +++ b/app/Controllers/ActionLogController.php @@ -0,0 +1,154 @@ +get('user'); + + if ($user['group_name'] !== 'SuperAdmin') { + return redirect()->to('/'); + } + + $storesModel = new Stores(); + + $data['page_title'] = $this->pageTitle; + $data['stores'] = $storesModel->getActiveStore(); + + return $this->render_template('action_log/index', $data); + } + + public function fetchData() + { + $session = session(); + $user = $session->get('user'); + + if ($user['group_name'] !== 'SuperAdmin') { + return $this->response->setJSON(['data' => []]); + } + + $historiqueModel = new Historique(); + + $filters = [ + 'action' => $this->request->getGet('action'), + 'store_name' => $this->request->getGet('store_name'), + 'product_name' => $this->request->getGet('product'), + 'sku' => $this->request->getGet('sku'), + 'date_from' => $this->request->getGet('date_from'), + 'date_to' => $this->request->getGet('date_to'), + ]; + + $allData = $historiqueModel->getHistoriqueWithFilters($filters); + + $result = ['data' => []]; + + foreach ($allData as $row) { + $result['data'][] = [ + date('d/m/Y H:i', strtotime($row['created_at'])), + $row['user_name'] ?? 'Système', + $this->getActionBadge($row['action']), + $this->getTableLabel($row['table_name']), + $row['description'] ?? '', + ]; + } + + return $this->response->setJSON($result); + } + + private function getActionBadge($action) + { + $badges = [ + 'CREATE' => 'Création', + 'UPDATE' => 'Modification', + 'DELETE' => 'Suppression', + 'PAYMENT' => 'Paiement', + 'VALIDATE' => 'Validation', + 'REFUSE' => 'Refus', + 'DELIVERY' => 'Livraison', + 'ASSIGN_STORE' => 'Assignation', + 'ENTRER' => 'Entrée', + 'SORTIE' => 'Sortie', + 'IMPORT' => 'Import', + 'LOGIN' => 'Connexion', + ]; + + return $badges[$action] ?? '' . $action . ''; + } + + private function getTableLabel($tableName) + { + $labels = [ + 'orders' => 'Commande', + 'products' => 'Produit', + 'users' => 'Utilisateur', + 'groups' => 'Rôle', + 'avances' => 'Avance', + 'securite' => 'Sécurité', + 'remise' => 'Remise', + 'sortie_caisse' => 'Décaissement', + 'autres_encaissements' => 'Encaissement', + 'recouvrement' => 'Recouvrement', + 'stores' => 'Point de vente', + 'brands' => 'Marque', + 'categories' => 'Catégorie', + ]; + + return $labels[$tableName] ?? $tableName; + } + + public function export() + { + $session = session(); + $user = $session->get('user'); + + if ($user['group_name'] !== 'SuperAdmin') { + return redirect()->to('/'); + } + + $historiqueModel = new Historique(); + + $filters = [ + 'action' => $this->request->getGet('action'), + 'store_name' => $this->request->getGet('store_name'), + 'date_from' => $this->request->getGet('date_from'), + 'date_to' => $this->request->getGet('date_to'), + ]; + + $data = $historiqueModel->getHistoriqueWithFilters($filters); + + $csv = "\xEF\xBB\xBF"; // BOM UTF-8 pour Excel + $csv .= "Date;Heure;Utilisateur;Action;Module;Description\n"; + + foreach ($data as $row) { + $date = date('d-m-Y', strtotime($row['created_at'])); + $heure = date('H:i', strtotime($row['created_at'])); + $userName = $row['user_name'] ?? 'Système'; + $action = $row['action']; + $module = $this->getTableLabel($row['table_name']); + $description = str_replace('"', '""', $row['description'] ?? ''); + + $csv .= "{$date};{$heure};{$userName};{$action};{$module};\"{$description}\"\n"; + } + + $filename = 'historique_actions_' . date('Y-m-d_H-i') . '.csv'; + + return $this->response + ->setHeader('Content-Type', 'text/csv; charset=utf-8') + ->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"') + ->setBody($csv); + } +} diff --git a/app/Controllers/AdminController.php b/app/Controllers/AdminController.php index 897b7cb9..36e13482 100644 --- a/app/Controllers/AdminController.php +++ b/app/Controllers/AdminController.php @@ -44,6 +44,13 @@ abstract class AdminController extends BaseController protected function render_template($page = null, $data = []) { $data['user_permission'] = $this->permission; + // Charger le logo dynamiquement + if (!isset($data['company_logo'])) { + $companyModel = new Company(); + $companyData = $companyModel->getCompanyData(1); + $data['company_logo'] = $companyData['logo'] ?? 'assets/images/company_logo.jpg'; + $data['company_name'] = $companyData['company_name'] ?? 'MotorBike'; + } echo view('templates/header', $data); echo view('templates/header_menu', $data); echo view('templates/side_menubar', $data); @@ -51,6 +58,14 @@ abstract class AdminController extends BaseController echo view('templates/footer', $data); } + // Get company logo path + protected function getCompanyLogo() + { + $model = new Company(); + $data = $model->getCompanyData(1); + return $data['logo'] ?? 'assets/images/company_logo.jpg'; + } + // Get company currency using model public function company_currency() { diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php index 17117f3a..f990c17a 100644 --- a/app/Controllers/Auth.php +++ b/app/Controllers/Auth.php @@ -6,6 +6,7 @@ use App\Models\ProductImage; use App\Models\Users; use App\Models\Stores; use App\Models\Products; +use App\Models\Historique; class Auth extends AdminController { @@ -79,6 +80,10 @@ public function loginPost() 'logged_in' => true ]); + // Log connexion + $historique = new Historique(); + $historique->logAction('users', 'LOGIN', $user['id'], "Connexion de {$user['firstname']} {$user['lastname']} ({$user['group_name']})"); + // Redirect to dashboard return redirect()->to('/'); } diff --git a/app/Controllers/AutresEncaissementsController.php b/app/Controllers/AutresEncaissementsController.php index 2e394b01..53c5f980 100644 --- a/app/Controllers/AutresEncaissementsController.php +++ b/app/Controllers/AutresEncaissementsController.php @@ -5,6 +5,7 @@ namespace App\Controllers; use App\Models\AutresEncaissements; use App\Models\Stores; use App\Models\Users; +use App\Models\Historique; class AutresEncaissementsController extends AdminController { @@ -106,7 +107,11 @@ class AutresEncaissementsController extends AdminController $Notification->notifyGroupsByPermissionAllStores('notifEncaissement', $notificationMessage, 'encaissements'); log_message('info', "✅ Encaissement {$encaissementId} créé - Notifications envoyées"); - + + // Log de l'action + $historique = new Historique(); + $historique->logAction('autres_encaissements', 'CREATE', $encaissementId, "Encaissement {$finalType} - Montant: {$montantFormate} Ar"); + return $this->response->setJSON([ 'success' => true, 'messages' => 'Encaissement enregistré avec succès' diff --git a/app/Controllers/AvanceController.php b/app/Controllers/AvanceController.php index f4c801b3..96df98b7 100644 --- a/app/Controllers/AvanceController.php +++ b/app/Controllers/AvanceController.php @@ -195,7 +195,72 @@ private function fetchAvanceDataGeneric($methodName = 'getAllAvanceData') public function fetchAvanceBecameOrder() { - return $this->fetchAvanceDataGeneric('getCompletedAvances'); + helper(['url', 'form']); + $Avance = new Avance(); + $product = new Products(); + $result = ['data' => []]; + + $data = $Avance->getCompletedAvances(); + $session = session(); + $users = $session->get('user'); + + $isAdmin = $this->isAdmin($users); + $isCommerciale = $this->isCommerciale($users); + $isCaissier = $this->isCaissier($users); + + foreach ($data as $key => $value) { + $isOwner = $users['id'] === $value['user_id']; + + $buttons = $this->buildActionButtons( + $value, + $isAdmin, + $isOwner, + $isCaissier, + $isCommerciale + ); + + // Déterminer le statut + if ($value['is_order'] == 1) { + $status = ' Payé'; + } else { + $status = ' En attente de paiement'; + } + + $date_time = date('d-m-Y h:i a', strtotime($value['avance_date'])); + + if ($value['type_avance'] === 'mere') { + $productName = $value['product_name'] ?? 'Produit sur mer'; + } else { + $productName = !empty($value['product_name']) + ? $value['product_name'] + : $product->getProductNameById($value['product_id'] ?? 0); + } + + if ($isAdmin) { + $result['data'][] = [ + $value['customer_name'], + $value['customer_phone'], + $value['customer_address'], + $productName, + number_format((int)$value['gross_amount'], 0, ',', ' '), + number_format((int)$value['avance_amount'], 0, ',', ' '), + $status, + $date_time, + $buttons, + ]; + } elseif ($isCommerciale || $isCaissier) { + $result['data'][] = [ + $value['avance_id'], + $productName, + number_format((int)$value['avance_amount'], 0, ',', ' '), + $status, + $date_time, + $buttons, + ]; + } + } + + return $this->response->setJSON($result); } public function fetchExpiredAvance() @@ -998,24 +1063,29 @@ public function printInvoice($avance_id) return redirect()->back()->with('error', 'Accès non autorisé'); } - // ✅ CORRECTION SIMPLIFIÉE + // Récupérer les données de l'entreprise + $companyModel = new Company(); + $companyData = $companyModel->getCompanyData(1); + + // Récupérer les détails du produit + $product = null; + $brandName = ''; if ($avance['type_avance'] === 'mere' && !empty($avance['product_name'])) { - $productName = $avance['product_name']; $productDetails = [ 'marque' => $avance['product_name'], 'numero_moteur' => '', - 'puissance' => '' + 'puissance' => '', + 'couleur' => '', + 'chasis' => '', + 'type_avance' => 'mere', ]; } else { $product = $Products->find($avance['product_id']); - + if (!$product) { return redirect()->back()->with('error', 'Produit non trouvé'); } - - $productName = $product['name'] ?? 'N/A'; - - // ✅ Récupérer le nom de la marque + $brandName = 'N/A'; if (!empty($product['marque'])) { $db = \Config\Database::connect(); @@ -1028,15 +1098,18 @@ public function printInvoice($avance_id) $brandName = $brandResult['name']; } } - + $productDetails = [ 'marque' => $brandName, 'numero_moteur' => $product['numero_de_moteur'] ?? '', - 'puissance' => $product['puissance'] ?? '' + 'puissance' => $product['puissance'] ?? '', + 'couleur' => $product['cler'] ?? '', + 'chasis' => $product['chasis'] ?? '', + 'type_avance' => $avance['type_avance'] ?? 'terre', ]; } - - $html = $this->generatePrintableInvoiceHTML($avance, $productName, $productDetails); + + $html = $this->generatePrintableInvoiceHTML($avance, $productDetails, $companyData); return $this->response->setBody($html); @@ -1821,27 +1894,42 @@ public function getFullInvoiceForPrint($avance_id) /** * Générer le HTML optimisé pour l'impression (version identique à printInvoice) */ -private function generatePrintableInvoiceHTML($avance, $productName, $productDetails) +private function generatePrintableInvoiceHTML($avance, $productDetails, $companyData) { $avanceDate = date('d/m/Y', strtotime($avance['avance_date'])); $avanceNumber = str_pad($avance['avance_id'], 5, '0', STR_PAD_LEFT); $customerName = strtoupper(esc($avance['customer_name'])); - $customerPhone = esc($avance['customer_phone']); - $customerCin = esc($avance['customer_cin']); + $customerPhone = esc($avance['customer_phone'] ?? ''); $grossAmount = number_format($avance['gross_amount'], 0, ',', ' '); $avanceAmount = number_format($avance['avance_amount'], 0, ',', ' '); $amountDue = number_format($avance['amount_due'], 0, ',', ' '); - $marque = esc($productDetails['marque']) ?: $productName; + $marque = esc($productDetails['marque']); $numeroMoteur = esc($productDetails['numero_moteur']); $puissance = esc($productDetails['puissance']); - + $couleur = esc($productDetails['couleur'] ?? ''); + $chasis = esc($productDetails['chasis'] ?? ''); + $typeAvance = $productDetails['type_avance'] ?? 'terre'; + $nSerieOuArrivage = ($typeAvance === 'mere') ? 'Arrivage' : ($chasis ?: $numeroMoteur); + $typePayment = esc($avance['type_payment'] ?? ''); + $unite = '1'; + + // Company data + $companyName = esc($companyData['company_name'] ?? 'MOTORBIKE STORE'); + $companyNIF = esc($companyData['NIF'] ?? ''); + $companySTAT = esc($companyData['STAT'] ?? ''); + $companyPhone = esc($companyData['phone'] ?? ''); + $companyPhone2 = esc($companyData['phone2'] ?? ''); + $companyAddress = esc($companyData['address'] ?? ''); + + $year = date('Y'); + return <<
-| MARQUE | -N°MOTEUR | +N° Châssis/Arrivage | PUISSANCE | -RAP (Ariary) | +Reste à payer (Ariary) |
|---|---|---|---|---|---|
| {$marque} | -{$numeroMoteur} | +{$chasis} | {$puissance} | {$amountDue} |
Ry mpanjifa hajaina,
-Natao ity fifanekena ity mba hialana amin'ny fivadihana hampitokisana amin'ny andaniny sy ankilany.
- -Ny mpividy dia manao famandrahana amin'ny alalan'ny fandoavambola mihoatra ny 25 isan-jato amin'ny vidin'entana rehetra (avances).
-Rehefa tonga ny moto/pieces dia tsy maintsy mandoa ny 50 isan-jato ny vidin'entana ny mpamandrika.
-Manana 15 andro kosa adoavana ny 25 isan-jato raha misy tsy fahafahana alohan'ny famoahana ny entana.
-Raha toa ka misy antony tsy hakana ny entana indray dia tsy mamerina ny vola efa voaloha (avance) ny société.
-NY MPAMANDRIKA
-NY MPIVAROTRA
-Ny mpividy dia manao famandrihana amin'ny alalan'ny fandoavam-bola mihoatra ny 25 isan-jato amin'ny vidin'entana rehetra (avances).
+Rehefa tonga ny moto/pieces dia tsy maintsy mandoha ny 50 isan-jato ny vidin'entana ny mpamandrika.
+Manana 15 andro kosa andoaovana ny 25 isan-jato raha misy tsy fahafahana alohan'ny famoahana ny entana.
+Raha toa ka misy antony tsy hakana ny entana indray dia tsy mamerina ny vola efa voaloha (avance) ny société.
+NIF : '.esc($company['NIF']).'
-STAT : '.esc($company['STAT']).'
-Contact : '.esc($company['phone']).' | '.esc($company['phone2']).'
+
- '.$documentTitle.' N° '.esc($order['bill_no']).'
+DOIT Nom : '.esc($order['customer_name']).'
-Adresse : '.esc($order['customer_address']).'
-CIN : '.esc($order['customer_cin']).'
-Téléphone : '.esc($order['customer_phone'] ?? '').'
-Antananarivo, le '.$today.'
-| Produit | -Quantité | -Prix unitaire (Ar) | -Montant (Ar) | -
|---|---|---|---|
| '.esc($details['product_name']);
-
- if (!empty($details['commentaire'])) {
- $html .= ' '.esc($details['commentaire']).''; - } - - $html .= ' |
- '.esc($qty).' | -'.number_format($prixUnitaire, 0, '', ' ').' | -'.number_format($prixTotal, 0, '', ' ').' | -
| MARQUE | -Désignation | -N° Moteur | -N° Châssis | -Puissance (CC) | -Quantité | -Prix Unit. (Ar) | -Montant (Ar) | -
|---|---|---|---|---|---|---|---|
| '.esc($details['marque']).' | -'.esc($details['product_name']).' | -'.esc($details['numero_moteur']).' | -'.esc($details['numero_chassis']).' | -'.esc($details['puissance']).' | -'.esc($qty).' | -'.number_format($prixUnitaire, 0, '', ' ').' | -'.number_format($prixTotal, 0, '', ' ').' | -
| Prix (HT) : | -'.number_format($totalHT, 0, '', ' ').' Ar | -|||||||
| TVA (20%) : | -'.number_format($tva, 0, '', ' ').' Ar | -|||||||
| Total (TTC) : | -'.number_format($totalTTC, 0, '', ' ').' Ar | -
| MARQUE | +N° CHASSIS | +PUISSANCE | +PRIX (Ariary) | +
|---|
- NIF : '.esc($company['NIF']).'
-STAT : '.esc($company['STAT']).'
-Contact : '.esc($company['phone']).' | '.esc($company['phone2']).'
+ +
- '.$documentTitle.' N° '.esc($order['bill_no']).'
+ + +Client : '.esc($order['customer_name']).'
-Adresse : '.esc($order['customer_address']).'
-Téléphone : '.esc($order['customer_phone']).'
-CIN : '.esc($order['customer_cin']).'
-Antananarivo, le '.$today.'
-| Produit | -Quantité | -Prix Unitaire (Ar) | -Montant (Ar) | +MARQUE | +N°MOTEUR | +PUISSANCE | +PRIX (Ariary) |
|---|---|---|---|---|---|---|---|
| '.esc($details['product_name']);
-
- if (!empty($details['commentaire'])) {
- $html .= ' Remarque : '.esc($details['commentaire']).''; - } - - $html .= ' |
- '.esc($qty).' | -'.number_format($prixUnitaire, 0, '', ' ').' | -'.number_format($prixTotal, 0, '', ' ').' | -
| Nom | -Marque | -Catégorie | -N° Moteur | -Châssis | -Puissance (CC) | -Quantité | -Prix Unit. (Ar) | -Montant (Ar) | -
|---|---|---|---|---|---|---|---|---|
| '.esc($details['product_name']).' | -'.esc($details['marque']).' | -'.esc($categoryName).' | -'.esc($details['numero_moteur']).' | -'.esc($details['numero_chassis']).' | -'.esc($details['puissance']).' | -'.esc($qty).' | -'.number_format($prixUnitaire, 0, '', ' ').' | -'.number_format($prixTotal, 0, '', ' ').' | -
| Total HT : | -'.number_format($totalHT, 0, '', ' ').' Ar | -
| TVA (20%) : | -'.number_format($tva, 0, '', ' ').' Ar | -
| Total TTC : | -'.number_format($totalTTC, 0, '', ' ').' Ar | -
| Statut : | -'.$paidLabel.' | -
+
+
- Magasin : ' . esc($this->returnStore($order_data['store_id'])) . '
'; echo '
';
+ echo '' . $documentTitle . ' N° ' . esc($order_data['bill_no']) . '
'; echo 'Antananarivo, le ' . date('d/m/Y') . '
'; echo 'Contact : ' . esc($company_info['phone']) . ' | ' . esc($company_info['phone2']) . '
'; echo '
';
+ echo '' . $documentTitle . ' N° ' . esc($order_data['bill_no']) . '
'; echo '
';
+ echo '| Date | +Utilisateur | +Action | +Module | +Description | +
|---|