verifyRole('viewProduct'); $data['page_title'] = $this->pageTitle; $Product = new Products(); $data['motos'] = $Product->getActiveProductData(); $data['stores'] = $Stores->getActiveStore(); return $this->render_template('products/index', $data); } public function assign_store() { // Vérifie que la requête est bien une requête AJAX if (!$this->request->isAJAX()) { $response = Services::response(); $response->setStatusCode(404, 'Page Not Found')->send(); exit; } // Récupère les données POST sous format JSON $data = $this->request->getJSON(true); // Décodage en tableau associatif if (!isset($data['product_id']) || !isset($data['store_id'])) { return $this->response->setJSON([ 'success' => false, 'message' => 'Paramètres manquants.' ])->setStatusCode(400); } $product_id = $data['product_id']; $store_id = $data['store_id']; $productsModel = new Products(); // Appeler la méthode assignToStore pour mettre à jour la base de données $result = $productsModel->assignToStore($product_id, $store_id); // Répondre en JSON avec le résultat if ($result) { return $this->response->setJSON(['success' => true]); } else { return $this->response->setJSON(['success' => false, 'message' => 'Échec de la mise à jour.']); } } public function fetchProductData() { // Initialize the response array $result = ['data' => []]; $Products = new Products(); $Stores = new Stores(); function convertString($name) { return "$name"; } // Fetch product data from the model $data = $Products->getProductData(); // Ensure this method exists in your ProductModel foreach ($data as $key => $value) { // Fetch store data $store_data = $Stores->getStoresData($value['store_id']); // Ensure this method exists in your StoreModel $store_data['name'] = $value['store_id'] == 0 ? "TOUS" : $Stores->getStoresData($value['store_id'])["name"]; // Construct buttons $buttons = ''; if (in_array('updateProduct', $this->permission ?? [])) { $buttons .= ''; } if (in_array('deleteProduct', $this->permission ?? [])) { $buttons .= ' '; } if (in_array('updateProduct', $this->permission ?? [])) { $buttons .= ' '; } if (in_array('updateProduct', $this->permission ?? [])) { $buttons .= ' '; } if (in_array('viewProduct', $this->permission ?? [])) { $buttons .= " "; } if (in_array('assignStore', $this->permission ?? [])) { $buttons .= ''; } // Image HTML $img = '' . $value['name'] . ''; // Availability Status $availability = ($value['availability'] == 1) ? 'Disponible' : 'Indisponible'; // Quantity Status $qty_status = ''; if ($value['qty'] <= 10 && $value['qty'] > 0) { $qty_status = 'Low!'; } elseif ($value['product_sold'] == false) { $qty_status = 'Rupture de stock!'; } // Populate the result data $result['data'][] = [ $img, $value['sku'], $value['name'], number_format($value['prix_vente'], 0, ',', ' '), $store_data['name'] ?? 'Unknown Store', $availability, $buttons ]; } // Return JSON response return $this->response->setJSON($result); } public function create() { $Products = new Products(); $Brands = new Brands(); $Category = new Category(); $Stores = new Stores(); $Notification = new NotificationController(); $this->verifyRole('createProduct'); $data['page_title'] = $this->pageTitle; // die(var_dump(json_encode($this->request->getPost('categorie[]')))); // Validate form inputs $validation = \Config\Services::validation(); $validation->setRules([ 'nom_de_produit' => 'required', 'marque' => 'required', 'numero_de_moteur' => 'required', 'prix' => 'required|numeric', 'price_vente' => 'required|numeric', 'puissance' => 'required', 'store' => 'required', 'availability' => 'required', 'price_min' => 'required|numeric', ]); if ($this->request->getMethod() === 'post' && $validation->withRequest($this->request)->run()) { // die(var_dump($this->request->getPost())); // Handle image upload $upload_image = $this->uploadImage(); // Prepare data for insertion $product_sold = false; $data = [ 'name' => $this->request->getPost('nom_de_produit'), 'sku' => $this->request->getPost('numero_de_serie'), 'price' => $this->request->getPost('prix'), 'qty' => 1, 'image' => $upload_image, 'description' => $this->request->getPost('description'), 'numero_de_moteur' => $this->request->getPost('numero_de_moteur'), 'marque' => $this->request->getPost('marque'), 'chasis' => $this->request->getPost('chasis'), 'store_id' => $this->request->getPost('store'), 'availability' => $this->request->getPost('availability'), 'prix_vente' => $this->request->getPost('price_vente'), 'date_arivage' => $this->request->getPost('datea'), 'puissance' => $this->request->getPost('puissance'), 'cler' => $this->request->getPost('cler'), 'categorie_id' => json_encode($this->request->getPost('categorie[]')), 'etats' => $this->request->getPost('etats'), 'infoManquekit' => $this->request->getPost('infoManquekit'), 'info' => $this->request->getPost('info'), 'infoManque' => $this->request->getPost('infoManque'), 'product_sold' => $product_sold, ]; $store_id1 = (int)$this->request->getPost('store'); // Insert data into the database if ($Products->create($data)) { $data = [ 'product_id' => $Products->insertID(), 'prix_minimal' => $this->request->getPost('price_min'), ]; $Fourchette = new FourchettePrix(); $Fourchette->createFourchettePrix($data); session()->setFlashdata('success', 'Créé avec succès'); $Notification->createNotification("Un nouveau Produit a été crée", "COMMERCIALE",$store_id1,'product/'); return redirect()->to('/products'); } else { session()->setFlashdata('errors', 'Error occurred while creating the product'); return redirect()->to('products/create'); } } else { $data = [ 'stores' => $Stores->getActiveStore(), 'validation' => $validation, // Pass validation errors to the view 'page_title' => $this->pageTitle, 'marque' => $Brands->getActiveBrands(), 'categorie' => $Category->getActiveCategory(), ]; // Render the form view return $this->render_template('products/create', $data); } } private function uploadImage() { // Define the upload directory $uploadPath = 'assets/images/product_image'; // Ensure the directory exists if (!is_dir($uploadPath)) { mkdir($uploadPath, 0777, true); } // Check if the file is uploaded via the form $file = $this->request->getFile('product_image'); if ($file && $file->isValid() && !$file->hasMoved()) { // Generate a unique file name $newName = uniqid() . '.' . $file->getExtension(); // Move the file to the target directory $file->move($uploadPath, $newName); // Return the actual file name return $newName; } // If an error occurs, return the error message return $file ? $file->getErrorString() : 'No file was uploaded.'; } public function update(int $id) { $Products = new Products(); $Stores = new Stores(); $Category = new Category(); $this->verifyRole('updateProduct'); $data['page_title'] = $this->pageTitle; $Brands = new Brands(); // Validate form inputs $validation = \Config\Services::validation(); $validation->setRules([ 'nom_de_produit' => 'required', 'marque' => 'required', ]); if ($this->request->getMethod() === 'post' && $validation->withRequest($this->request)->run()) { $data = [ 'name' => $this->request->getPost('nom_de_produit'), 'sku' => $this->request->getPost('numero_de_serie'), 'price' => $this->request->getPost('price'), 'qty' => 1, 'description' => $this->request->getPost('description'), 'numero_de_moteur' => $this->request->getPost('numero_de_moteur'), 'marque' => $this->request->getPost('marque'), 'chasis' => $this->request->getPost('chasis'), 'store_id' => $this->request->getPost('store'), 'availability' => $this->request->getPost('availability'), 'prix_vente' => $this->request->getPost('price_vente'), 'date_arivage' => $this->request->getPost('datea'), 'puissance' => $this->request->getPost('puissance'), 'cler' => $this->request->getPost('cler'), 'categorie_id' => json_encode($this->request->getPost('categorie[]')), 'etats' => $this->request->getPost('etats'), 'infoManquekit' => $this->request->getPost('infoManquekit'), 'info' => $this->request->getPost('info'), 'infoManque' => $this->request->getPost('infoManque'), ]; // Check if a product image is uploaded if ($this->request->getFile('product_image')->isValid()) { $uploadImage = $this->uploadImage(); // Use the previously provided upload function $uploadData = ['image' => $uploadImage]; // Update the product with the uploaded image $Products->update($id, $uploadData); } if ($Products->updateProduct($data, $id)) { // die(var_dump('tonga eto')); session()->setFlashdata('success', 'Successfully updated'); return redirect()->to('/products'); } else { session()->setFlashdata('errors', 'Error occurred!!'); return redirect()->to('/produtcs/update/' . $id); } } else { $data = [ 'stores' => $Stores->getActiveStore(), 'validation' => $validation, // Pass validation errors to the view 'page_title' => $this->pageTitle, 'product_data' => $Products->getProductData($id), 'categorie' => $Category->getActiveCategory(), 'marque' => $Brands->getActiveBrands() ]; return $this->render_template('products/editbackup', $data); } } public function remove() { $this->verifyRole('deleteProduct'); $product_id = $this->request->getPost('product_id'); $response = []; $Products = new Products(); if ($product_id) { if ($Products->remove($product_id)) { $response['success'] = true; $response['messages'] = "Successfully removed"; } else { $response['success'] = false; $response['messages'] = "Error in the database while removing the product information"; } } else { $response['success'] = false; $response['messages'] = "Refersh the page again!!"; } // Return JSON response return $this->response->setJSON($response); } public function createByExcel() { $this->verifyRole("createProduct"); // 1) Récupération et validation du fichier $file = $this->request->getFile('excel_product'); if (!$file || !$file->isValid() || $file->hasMoved()) { return $this->response->setJSON([ 'success' => false, 'messages' => "Aucun fichier valide reçu" ]); } $ext = strtolower($file->getClientExtension()); if (! in_array($ext, ['xls', 'xlsx'])) { return $this->response->setJSON([ 'success' => false, 'messages' => "Seuls les fichiers xls/xlsx sont autorisés" ]); } try { // 2) Chargement du fichier Excel $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file->getTempName()); $sheet = $spreadsheet->getActiveSheet(); // 3) Lecture des données brutes et mapping des en-têtes $allRows = $sheet->toArray(null, true, true, true); if (count($allRows) < 2) { return $this->response->setJSON([ 'success' => false, 'messages' => "Le fichier ne contient aucune donnée" ]); } $headerRow = array_shift($allRows); $map = []; foreach ($headerRow as $col => $heading) { $h = mb_strtolower(trim($heading)); $h = str_replace(['’', '‘', '“', '”'], '\'', $h); $h = iconv('UTF-8', 'ASCII//TRANSLIT', $h); $h = preg_replace('/[^a-z0-9_]/', '_', $h); $h = preg_replace('/_+/', '_', $h); $h = trim($h, '_'); switch ($h) { case 'designation': case 'nom': $map[$col] = 'name'; break; case 'n_serie': $map[$col] = 'sku'; break; case 'prix_ar': $map[$col] = 'prix_vente'; break; case 'prix_d_achat': case 'prix_dachat': case 'prixd_achat': $map[$col] = 'price'; break; case 'marque': $map[$col] = 'marque'; break; case 'description': $map[$col] = 'description'; break; case 'code_moteur': case 'n_moteur': $map[$col] = 'numero_de_moteur'; break; case 'chassis': case 'chasis': $map[$col] = 'chasis'; break; case 'date_arrivage': case 'date_d_arivage': $map[$col] = 'date_arrivage'; break; case 'puissance': $map[$col] = 'puissance'; break; case 'availability': case 'disponibilite': $map[$col] = 'availability'; break; case 'piece': case 'piece_manquant': $map[$col] = 'is_piece'; break; case 'cle': $map[$col] = 'cler'; break; case 'categories': case 'categorie_id': $map[$col] = 'categorie_id'; break; case 'etat': case 'etats': $map[$col] = 'etats'; break; case 'magasin': $map[$col] = 'store_id'; break; case 'info_manquekit': case 'infomanquekit': $map[$col] = 'infoManquekit'; break; case 'info': case 'info_piece': $map[$col] = 'info'; break; case 'info_manque': $map[$col] = 'infoManque'; break; case 'image': case 'image_s': $map[$col] = 'image'; break; default: // Non mappé break; } } // 4) Extraction des images intégrées, si présent $imagesMap = []; foreach ($sheet->getDrawingCollection() as $drawing) { if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) { $coord = $drawing->getCoordinates(); $extImg = pathinfo($drawing->getPath(), PATHINFO_EXTENSION); $name = uniqid('img_') . ".$extImg"; $dir = FCPATH . 'assets/images/product_image/'; if (! is_dir($dir)) { mkdir($dir, 0777, true); } file_put_contents($dir . $name, file_get_contents($drawing->getPath())); $imagesMap[$coord] = $name; } } // 5) Chargement des modèles $ProductsModel = new \App\Models\Products(); $ProductsModel->skipValidation(true); $BrandsModel = new \App\Models\Brands(); $CatModel = new \App\Models\Category(); $countInserted = 0; // 6) Boucle sur chaque ligne de données foreach ($allRows as $rowIndex => $row) { $data = []; // Lecture des cellules formatées pour chaque champ mappé foreach ($map as $col => $field) { $cellValue = $sheet ->getCell($col . ($rowIndex + 2)) ->getFormattedValue(); $data[$field] = trim((string)$cellValue); } if (empty($data['name'])) { continue; // champ désignation vide } // Conversion du prix AR : capture tous les groupes de chiffres if (! empty($data['prix_vente'])) { preg_match_all('/\d+/', $data['prix_vente'], $matches); $digits = implode('', $matches[0]); // ex. ["2","000","000"] => "2000000" $data['prix_vente'] = intval($digits); } else { $data['prix_vente'] = 0; } // Valeurs par défaut $data['qty'] = 1; $data['product_sold'] = 0; $data['availability'] = isset($data['availability']) ? (strtolower($data['availability']) === 'oui' ? 1 : 0) : 0; $data['is_piece'] = isset($data['is_piece']) ? (strtolower($data['is_piece']) === 'oui' ? 1 : 0) : 0; $data['cler'] = isset($data['cler']) ? (strtolower($data['cler']) === 'oui' ? 1 : 0) : 1; $data['etats'] = isset($data['etats']) ? (strtolower($data['etats']) === 'kit' ? 1 : 0) : 1; // Association d’image si présente foreach ($map as $col => $field) { if ($field === 'image') { $coordImg = $col . ($rowIndex + 2); if (isset($imagesMap[$coordImg])) { $data['image'] = $imagesMap[$coordImg]; } break; } } // Gestion des clés étrangères if (! empty($data['marque'])) { $data['marque'] = $BrandsModel->getOrCreateIdByName($data['marque']); } if (! empty($data['categorie_id'])) { $labels = array_map('trim', explode(',', $data['categorie_id'])); $catIds = []; foreach ($labels as $label) { if ($label !== '') { $catIds[] = $CatModel->getOrCreateIdByName($label); } } $data['categorie_id'] = $catIds; } if (! empty($data['store_id'])) { // store_id depuis la session $Store = new Stores(); $store = $Store->getIdStoreByName($data['store_id']); $data['store_id'] = $store; } // Insertion $id = $ProductsModel->insert($data); if ($id !== false) { $countInserted++; } } // 7) Notification et réponse $Notification = new \App\Controllers\NotificationController(); $user = session()->get('user'); $Notification->createNotification( "$countInserted produits ajoutés", "COMMERCIALE", (int)$user['store_id'], "avances" ); return $this->response->setJSON([ 'success' => true, 'messages' => "Produits importés avec succès ($countInserted)" ]); } catch (\Exception $e) { log_message('error', $e->getMessage()); return $this->response->setJSON([ 'success' => false, 'messages' => "Erreur pendant l’import : " . $e->getMessage() ]); } } }