## Recouvrement - Liste triée par date décroissante - Pop-up de confirmation anti-doublons - Affichage avec permission "Voir" seule ## Mise à jour produit - Correction erreur "Column count doesn't match" (triggers MySQL corrigés) - Formulaire corrigé (action, catégorie, champs null) - Catégorie et date d'arrivage correctement préremplis - Historique affiche le nom de l'utilisateur qui modifie ## Espace commercial - Colonne "Disponibilité" ajoutée avec statut "En attente de livraison" - Bouton panier caché pour les motos commandées - Surbrillance jaune pour les motos en attente ## Notifications - Caissière reçoit les notifications via notifCommande - notifSortieCaisse réservé à Direction/Admin ## Avances - Colonne "N° Série" ajoutée dans toutes les listes - Compteurs sur les boutons Incomplètes/Complètes ## Facture / BL - Total, Remise, Total à payer affichés - "Ariary" ne se répète plus - Prix individuel par moto - Impression automatique : 1 produit = Facture, 2+ = BL - Remise multiple : colonne product changée en TEXT ## Rapports - Filtre par date dans le rapport stock - Filtre par commercial et mécanicien dans les performances - Correction rapport stock (GROUP BY marque) - Liens absolus (correction erreur 404) ## Sidebar - Marge en haut supprimée (production) - Padding en bas ajouté pour scroll complet Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
347 lines
16 KiB
PHP
347 lines
16 KiB
PHP
<!-- Content Wrapper. Contains page content -->
|
||
<div class="content-wrapper">
|
||
<?php
|
||
function stringToArray($data)
|
||
{
|
||
// Ensure the string is properly formatted for JSON decoding
|
||
$data = str_replace("'", '"', $data); // Convert single quotes to double quotes
|
||
return json_decode($data, true);
|
||
}
|
||
?>
|
||
<!-- Content Header (Page header) -->
|
||
<section class="content-header">
|
||
<h1>
|
||
Gérer les
|
||
<small>Produits</small>
|
||
</h1>
|
||
<ol class="breadcrumb">
|
||
<li><a href="#"><i class="fa fa-dashboard"></i> Accueil</a></li>
|
||
<li class="active">Produits</li>
|
||
</ol>
|
||
</section>
|
||
|
||
<!-- Main content -->
|
||
<section class="content">
|
||
<div class="row">
|
||
<div class="col-md-12 col-xs-12">
|
||
<div id="messages"></div>
|
||
|
||
<?php if (session()->getFlashdata('success')): ?>
|
||
<div class="alert alert-success alert-dismissible" role="alert">
|
||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||
<?php echo session()->getFlashdata('success'); ?>
|
||
</div>
|
||
<?php elseif (session()->getFlashdata('error')): ?>
|
||
<div class="alert alert-error alert-dismissible" role="alert">
|
||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||
<?php echo session()->getFlashdata('error'); ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="box">
|
||
<div class="box-header">
|
||
<h3 class="box-title">Mise à jours Moto</h3>
|
||
</div>
|
||
|
||
<form role="form" action="<?php echo base_url('products/update/' . $product_data['id']); ?>" method="post" enctype="multipart/form-data">
|
||
<div class="box-body">
|
||
<!-- Image actuelle -->
|
||
<div class="form-group">
|
||
<label>Image: </label>
|
||
<img src="<?php echo base_url() . 'assets/images/product_image/' . $product_data['image'] ?>" width="150" height="150" class="img-circle">
|
||
</div>
|
||
|
||
<!-- Nouvelle Image -->
|
||
<div class="form-group">
|
||
<label for="product_image">Nouvelle Image</label>
|
||
<div class="kv-avatar">
|
||
<div class="file-loading">
|
||
<input id="product_image" name="product_image" type="file" accept="image/*">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Nom du produit -->
|
||
<div class="form-group">
|
||
<label for="product_name">Nom de Moto</label>
|
||
<input type="text" class="form-control" id="product_name" name="nom_de_produit" placeholder="Nom du moto" autocomplete="off" value="<?php echo $product_data['name']; ?>" />
|
||
</div>
|
||
|
||
<!-- Marque -->
|
||
<div class="form-group">
|
||
<label for="marque">Marque</label>
|
||
<select class="form-control" id="marque" name="marque">
|
||
<?php foreach ($marque as $k => $v): ?>
|
||
<option value="<?php echo $v['id'] ?>" <?php if ($product_data['marque'] == $v['id']) echo "selected"; ?>><?php echo $v['name'] ?></option>
|
||
<?php endforeach ?>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Numéro de moteur -->
|
||
<div class="form-group">
|
||
<label for="moteur">Numéro de moteur</label>
|
||
<input type="text" class="form-control" id="moteur" name="numero_de_moteur" placeholder="Numéro de série" autocomplete="off" value="<?php echo $product_data['numero_de_moteur']; ?>" />
|
||
</div>
|
||
|
||
<!-- Châssis -->
|
||
<div class="form-group">
|
||
<label for="chassis">Châssis</label>
|
||
<input type="text" class="form-control" id="chassis" name="chasis" placeholder="Chassis" autocomplete="off" value="<?php echo $product_data['chasis']; ?>" />
|
||
</div>
|
||
|
||
<!-- Prix d'achat -->
|
||
<div class="form-group">
|
||
<label for="price">Prix d'Achat</label>
|
||
<input type="number" min="0" class="form-control" id="price" name="price" placeholder="Prix d'achat" autocomplete="off" value="<?php echo $product_data['price']; ?>" />
|
||
</div>
|
||
|
||
<!-- Prix de vente -->
|
||
<div class="form-group">
|
||
<label for="price_vente">Prix de Vente</label>
|
||
<input type="number" min="0" class="form-control" id="price_vente" name="price_vente" placeholder="Prix de vente" autocomplete="off" value="<?php echo $product_data['prix_vente']; ?>" required />
|
||
</div>
|
||
|
||
<!-- Prix minimal -->
|
||
<div class="form-group">
|
||
<label for="price_min">Prix Minimal</label>
|
||
<input type="number" min="0" class="form-control" id="price_min" name="price_min" placeholder="Prix de vente minimal autorisé" autocomplete="off" value="<?= old('price_min') ?: $prix_minimal ?>" required />
|
||
<small id="price_min_error" style="color:red; display:none;">Le prix minimal ne peut pas être supérieur au prix de vente</small>
|
||
</div>
|
||
|
||
<!-- Date d'arrivage -->
|
||
<div class="form-group">
|
||
<label for="datea">Date d'arrivage</label>
|
||
<input type="date" class="form-control" id="datea" name="datea" autocomplete="off" value="<?php echo !empty($product_data['date_arivage']) ? date('Y-m-d', strtotime($product_data['date_arivage'])) : ''; ?>" />
|
||
</div>
|
||
|
||
<!-- Puissance -->
|
||
<div class="form-group">
|
||
<label for="puissance">Puissances</label>
|
||
<input type="text" class="form-control" id="puissance" name="puissance" placeholder="Puissances" autocomplete="off" value="<?php echo $product_data['puissance']; ?>" />
|
||
</div>
|
||
|
||
<!-- Clé -->
|
||
<div class="form-group">
|
||
<label for="cler">Clé</label>
|
||
<input type="text" class="form-control" id="cler" name="cler" placeholder="Type de clé" autocomplete="off" value="<?php echo $product_data['cler']; ?>" />
|
||
</div>
|
||
|
||
<!-- Description -->
|
||
<div class="form-group">
|
||
<label for="description">Description</label>
|
||
<textarea type="text" class="form-control" id="description" name="description" placeholder="Description" autocomplete="off"><?php echo $product_data['description']; ?></textarea>
|
||
</div>
|
||
|
||
<!-- Numéro de série -->
|
||
<div class="form-group">
|
||
<label for="sku">Numéro de série</label>
|
||
<input type="text" class="form-control" id="sku" name="numero_de_serie" placeholder="Numéro de série" autocomplete="off" value="<?php echo $product_data['sku']; ?>" />
|
||
</div>
|
||
|
||
<!-- Catégories -->
|
||
<div class="form-group">
|
||
<label for="categorie">Catégories</label>
|
||
<?php
|
||
$rawCats = $product_data['categorie_id'] ?? null;
|
||
$catIds = [];
|
||
if (is_array($rawCats)) {
|
||
$catIds = array_map('intval', $rawCats);
|
||
} elseif (is_string($rawCats)) {
|
||
$decoded = json_decode($rawCats, true);
|
||
if (is_array($decoded)) {
|
||
$catIds = array_map('intval', $decoded);
|
||
} else {
|
||
$catIds = array_filter(array_map('intval', explode(',', $rawCats)), fn($id) => $id > 0);
|
||
}
|
||
} elseif (is_int($rawCats) || ctype_digit((string)$rawCats)) {
|
||
$catIds = [(int) $rawCats];
|
||
}
|
||
?>
|
||
<select class="form-control select_group" id="categorie" name="categorie[]" multiple="multiple">
|
||
<?php foreach ($categorie as $k => $v): ?>
|
||
<option value="<?= $v['id']; ?>" <?= in_array((int)$v['id'], $catIds) ? 'selected="selected"' : '' ?>><?= esc($v['name']); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Magasin -->
|
||
<div class="form-group">
|
||
<label for="store">Magasin</label>
|
||
<select class="form-control select_group" id="store" name="store">
|
||
<?php foreach ($stores as $k => $v): ?>
|
||
<option value="<?php echo $v['id'] ?>" <?php if ($product_data['store_id'] == $v['id']) echo "selected"; ?>><?php echo $v['name'] ?></option>
|
||
<?php endforeach ?>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Disponibilité -->
|
||
<div class="form-group">
|
||
<label for="availability">Disponibilité</label>
|
||
<select class="form-control" id="availability" name="availability">
|
||
<option value="1" <?php if ($product_data['availability'] == 1) echo "selected"; ?>>Oui</option>
|
||
<option value="0" <?php if ($product_data['availability'] == 0) echo "selected"; ?>>Non</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Etats -->
|
||
<div class="form-group">
|
||
<label for="etat">Etats</label>
|
||
<select class="form-control" id="etat" name="etats">
|
||
<option value="1" <?php if ($product_data['etats'] == 1) echo "selected"; ?>>Kit</option>
|
||
<option value="2" <?php if ($product_data['etats'] != 1) echo "selected"; ?>>Non Kit</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group" id="boxsuplementairekit"></div>
|
||
|
||
<!-- Pièce manquante -->
|
||
<div class="form-group">
|
||
<label for="info">Pièce Manquant</label>
|
||
<select class="form-control" id="info" name="info">
|
||
<option value="2" <?php if ($product_data['info'] != 1) echo "selected"; ?>>Non</option>
|
||
<option value="1" <?php if ($product_data['info'] == 1) echo "selected"; ?>>Oui</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group" id="boxsuplementaire"></div>
|
||
|
||
<!-- Type -->
|
||
<div class="form-group">
|
||
<label for="type">Type</label>
|
||
<select class="form-control" id="type" name="type">
|
||
<option value="A" <?php if ($product_data['type'] == "A") echo "selected"; ?>>A</option>
|
||
<option value="B" <?php if ($product_data['type'] == "B") echo "selected"; ?>>B</option>
|
||
<option value="C" <?php if ($product_data['type'] == "C") echo "selected"; ?>>C</option>
|
||
<option value="D" <?php if ($product_data['type'] == "D") echo "selected"; ?>>D</option>
|
||
</select>
|
||
</div>
|
||
|
||
</div> <!-- /.box-body -->
|
||
|
||
<div class="box-footer">
|
||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||
<a href="<?php echo base_url('products/') ?>" class="btn btn-warning">Retour</a>
|
||
</div>
|
||
</form>
|
||
</div> <!-- /.box -->
|
||
</div> <!-- /.col-md-12 -->
|
||
</div> <!-- /.row -->
|
||
</section> <!-- /.content -->
|
||
</div> <!-- /.content-wrapper -->
|
||
|
||
<script type="text/javascript">
|
||
$(document).ready(function() {
|
||
// --- Initialisations ---
|
||
$(".select_group").select2();
|
||
$("#description").wysihtml5();
|
||
$("#mainProductNav").addClass('active');
|
||
$("#manageProductNav").addClass('active');
|
||
|
||
// --- Gestion image ---
|
||
var btnCust = '<button type="button" class="btn btn-secondary" title="Add picture tags" ' +
|
||
'onclick="alert(\'Call your custom code here.\')">' +
|
||
'<i class="glyphicon glyphicon-tag"></i>' +
|
||
'</button>';
|
||
$("#product_image").fileinput({
|
||
overwriteInitial: true,
|
||
maxFileSize: 1500,
|
||
showClose: false,
|
||
showCaption: false,
|
||
browseLabel: '',
|
||
removeLabel: '',
|
||
browseIcon: '<i class="glyphicon glyphicon-folder-open"></i>',
|
||
removeIcon: '<i class="glyphicon glyphicon-remove"></i>',
|
||
removeTitle: 'Cancel or reset changes',
|
||
elErrorContainer: '#kv-avatar-errors-1',
|
||
msgErrorClass: 'alert alert-block alert-danger',
|
||
layoutTemplates: { main2: '{preview} ' + btnCust + ' {remove} {browse}' },
|
||
allowedFileExtensions: ["jpg", "png", "gif", "jpeg", "webp"]
|
||
});
|
||
|
||
// --- Gestion champs supplémentaires ---
|
||
const info = document.getElementById('info');
|
||
const divSupp = document.getElementById('boxsuplementaire');
|
||
const etat = document.getElementById('etat');
|
||
const boxsuplementairekit = document.getElementById('boxsuplementairekit');
|
||
|
||
function afficherSupplements() {
|
||
if (info && info.value == 1) {
|
||
divSupp.innerHTML = `<label for="infoManque">Information</label>
|
||
<textarea type="text" class="form-control" id="infoManque" name="infoManque" placeholder="Description sur des trucs manquants" autocomplete="off"><?php echo $product_data['infoManque']; ?></textarea>`;
|
||
} else {
|
||
divSupp.innerHTML = '';
|
||
}
|
||
|
||
if (etat && etat.value == 1) {
|
||
boxsuplementairekit.innerHTML = `<label for="infoManquekit">Information sur le kit</label>
|
||
<textarea type="text" class="form-control" id="infoManquekit" name="infoManquekit" placeholder="Description sur le kit" autocomplete="off"><?php echo $product_data['infoManquekit']; ?></textarea>`;
|
||
} else {
|
||
boxsuplementairekit.innerHTML = '';
|
||
}
|
||
}
|
||
|
||
window.addEventListener('load', afficherSupplements);
|
||
if (info) info.addEventListener('change', afficherSupplements);
|
||
if (etat) etat.addEventListener('change', afficherSupplements);
|
||
|
||
// --- Vérification prix minimal < prix de vente ---
|
||
const priceMinInput = document.getElementById('price_min');
|
||
const priceVenteInput = document.getElementById('price_vente');
|
||
const priceMinError = document.getElementById('price_min_error');
|
||
|
||
function verifierPrix() {
|
||
let prixMin = parseFloat(priceMinInput.value.replace(/\s/g, '')) || 0;
|
||
let prixVente = parseFloat(priceVenteInput.value.replace(/\s/g, '')) || 0;
|
||
if (prixMin > prixVente) {
|
||
priceMinError.style.display = 'block';
|
||
} else {
|
||
priceMinError.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
if (priceMinInput) priceMinInput.addEventListener('input', verifierPrix);
|
||
if (priceVenteInput) priceVenteInput.addEventListener('input', verifierPrix);
|
||
|
||
// --- Formatage automatique des prix (avec affichage initial) ---
|
||
['price', 'price_vente', 'price_min'].forEach(id => {
|
||
const input = document.getElementById(id);
|
||
if (input) {
|
||
// Si c’est un input number, on le convertit en texte pour garder les espaces
|
||
if (input.type === "number") input.type = "text";
|
||
|
||
// 🟢 Formate dès la frappe
|
||
input.addEventListener('input', function(e) {
|
||
let val = e.target.value.replace(/\D/g, ''); // garde seulement les chiffres
|
||
e.target.value = val.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||
});
|
||
|
||
// 🟢 Formate aussi dès le chargement de la page (valeurs de la base)
|
||
let initialVal = input.value.replace(/\D/g, '');
|
||
if (initialVal) {
|
||
input.value = initialVal.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||
}
|
||
}
|
||
});
|
||
|
||
// --- Nettoyage des espaces avant soumission du formulaire ---
|
||
$("form").on('submit', function(e) {
|
||
let prixMin = parseFloat(priceMinInput.value.replace(/\s/g, '')) || 0;
|
||
let prixVente = parseFloat(priceVenteInput.value.replace(/\s/g, '')) || 0;
|
||
|
||
if (prixMin > prixVente) {
|
||
e.preventDefault();
|
||
alert("❌ Le prix minimal ne peut pas être supérieur au prix de vente !");
|
||
priceMinInput.focus();
|
||
return false;
|
||
}
|
||
|
||
// 🧹 Supprime les espaces avant envoi
|
||
['price', 'price_vente', 'price_min'].forEach(id => {
|
||
const input = document.getElementById(id);
|
||
if (input) {
|
||
input.value = input.value.replace(/\s/g, '');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
</script>
|