You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
588 lines
23 KiB
588 lines
23 KiB
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Models\Attributes;
|
|
use App\Models\Brands;
|
|
use App\Models\Category;
|
|
use App\Models\FourchettePrix;
|
|
use App\Models\Notification;
|
|
use App\Models\Products;
|
|
use App\Models\Stores;
|
|
use Config\Services;
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
|
|
|
class ProductCOntroller extends AdminController
|
|
{
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
// Assuming permission is being set from a session
|
|
helper(['form', 'url']);
|
|
}
|
|
|
|
|
|
private $pageTitle = 'Produits';
|
|
|
|
public function index()
|
|
{
|
|
$Stores = new Stores();
|
|
$this->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 .= '<a href="' . base_url('products/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>';
|
|
}
|
|
|
|
if (in_array('deleteProduct', $this->permission ?? [])) {
|
|
$buttons .= ' <button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['id'] . ')" data-toggle="modal" data-target="#removeModal"><i class="fa fa-trash"></i></button>';
|
|
}
|
|
|
|
if (in_array('updateProduct', $this->permission ?? [])) {
|
|
$buttons .= ' <a href="ventes/' . $value['id'] . '" class="btn btn-default"><i class="fa fa-image"></i></a>';
|
|
}
|
|
|
|
if (in_array('updateProduct', $this->permission ?? [])) {
|
|
$buttons .= ' <button class="btn btn-default" onclick="generateQrPdf(' . $value["id"] . ')"><i class="fa fa-qrcode"></i></button>';
|
|
}
|
|
|
|
if (in_array('viewProduct', $this->permission ?? [])) {
|
|
$buttons .= " <a href='/ventes/show/" . $value['id'] . "' class='btn btn-default'><i class='fa fa-eye'></i></a>";
|
|
}
|
|
if (in_array('assignStore', $this->permission ?? [])) {
|
|
$buttons .=
|
|
'<button type="button" class="btn btn-info assignbtn" title="Assigner sur un magasin" data-magasin="' . $store_data['name'] . '" data-products-id="' . $value["id"] . '" data-toggle="modal" data-target="#assignStoreModal">
|
|
<i class="fa fa-forward"></i>
|
|
</button>';
|
|
}
|
|
|
|
|
|
// Image HTML
|
|
$img = '<img src="' . base_url('assets/images/product_image/' . $value['image']) . '" alt="' . $value['name'] . '" class="img-circle" width="50" height="50" />';
|
|
|
|
// Availability Status
|
|
$availability = ($value['availability'] == 1) ? '<span class="label label-success">Disponible</span>' : '<span class="label label-warning">Indisponible</span>';
|
|
|
|
// Quantity Status
|
|
$qty_status = '';
|
|
if ($value['qty'] <= 10 && $value['qty'] > 0) {
|
|
$qty_status = '<span class="label label-warning">Low!</span>';
|
|
} elseif ($value['product_sold'] == false) {
|
|
$qty_status = '<span class="label label-danger">Rupture de stock!</span>';
|
|
}
|
|
|
|
// 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()
|
|
]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|