motorbike/app/Controllers/OrderController.php
2025-10-27 07:49:22 +03:00

2503 lines
100 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Controllers;
use DateTime;
use Mpdf\Mpdf;
use App\Models\Brands;
use App\Models\Caisse;
use App\Models\Orders;
use App\Models\Stores;
use App\Models\Company;
use App\Models\Category;
use App\Models\Products;
use App\Models\Attributes;
use App\Models\OrderItems;
use App\Models\Remise;
use App\Models\FourchettePrix;
use PhpParser\Node\Stmt\Else_;
class OrderController extends AdminController
{
public function __construct()
{
parent::__construct();
}
private $pageTitle = 'Orders';
public function index()
{
$this->verifyRole('viewOrder');
$data['page_title'] = $this->pageTitle;
return $this->render_template('orders/index', $data);
}
public function fetchOrdersData()
{
helper(['url', 'form']);
$Orders = new Orders();
$result = ['data' => []];
$data = $Orders->getOrdersData();
$session = session();
$users = $session->get('user');
// ========================================
// POUR CAISSIÈRE
// ========================================
if ($users['group_name'] == "Caissière") {
foreach ($data as $key => $value) {
$date_time = date('d-m-Y h:i a', strtotime($value['date_time']));
$buttons = '';
// Bouton imprimer (sauf pour SECURITE)
if (in_array('viewOrder', $this->permission) && $users['group_name'] != "SECURITE" && $users['group_name'] != "COMMERCIALE") {
$buttons .= '<a target="_blank" href="' . site_url('orders/printDiv/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-print"></i></a>';
}
// Bouton voir
if (in_array('viewOrder', $this->permission)) {
$buttons .= '
<a
href="#"
data-order-id="' . $value['id'] . '"
class="btn btn-default btn-view"
data-toggle="tooltip"
title="Voir">
<i class="fa fa-eye"></i>
</a>';
}
// ✅ Bouton modifier pour statuts 0 (Refusé) et 2 (En Attente)
if (in_array('updateOrder', $this->permission)
&& $users["store_id"] == $value['store_id']
&& in_array($value['paid_status'], [0, 2])) {
$buttons .= ' <a href="' . site_url('orders/update/' . $value['id']) . '" class="btn btn-primary"><i class="fa fa-pencil"></i></a>';
}
// Statut de paiement
if ($value['paid_status'] == 1) {
$paid_status = '<span class="label label-success">Validé</span>';
} elseif ($value['paid_status'] == 2) {
$paid_status = '<span class="label label-warning">En Attente</span>';
} elseif ($value['paid_status'] == 3) {
$paid_status = '<span class="label label-info">Validé et Livré</span>';
} else {
$paid_status = '<span class="label label-danger">Refusé</span>';
}
// Calcul délai
$date1 = new DateTime($date_time);
$date2 = new DateTime();
$interval = $date1->diff($date2);
$daysPassed = $interval->days;
$statuDate = '';
if ($value['paid_status'] == 2) {
if ($daysPassed < 8) {
$statuDate = '<span class="label label-success"> depuis ' . $daysPassed . ' Jours</span>';
} elseif ($daysPassed >= 8 && $daysPassed < 15) {
$statuDate = '<span class="label label-warning"> depuis ' . $daysPassed . ' Jours</span>';
} else {
$statuDate = '<span class="label label-danger"> depuis ' . $daysPassed . ' Jours</span>';
}
}
$result['data'][$key] = [
$value['product_names'],
$value['user_name'],
$date_time . " <br >" . $statuDate,
number_format((int) $value['discount'], 0, ',', ' '),
number_format((int) $value['gross_amount'], 0, ',', ' '),
$paid_status,
$buttons
];
}
return $this->response->setJSON($result);
}
// ========================================
// POUR DIRECTION OU DAF
// ========================================
elseif($users['group_name'] == "Direction" || $users['group_name'] == "DAF"){
foreach ($data as $key => $value) {
$date_time = date('d-m-Y h:i a', strtotime($value['date_time']));
$buttons = '';
// Bouton imprimer
if (in_array('viewOrder', $this->permission) && $users['group_name'] != "SECURITE" && $users['group_name'] != "COMMERCIALE") {
$buttons .= '<a target="_blank" href="' . site_url('orders/printDiv/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-print"></i></a>';
}
// ✅ Bouton modifier pour statuts 0 (Refusé) et 2 (En Attente)
if (in_array('updateOrder', $this->permission) && in_array($value['paid_status'], [0, 2])) {
$buttons .= ' <a href="' . site_url('orders/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>';
}
// ✅ Bouton supprimer pour statuts 0 et 2
if (in_array('deleteOrder', $this->permission) && in_array($value['paid_status'], [0, 2])) {
$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>';
}
// Statut de paiement
if ($value['paid_status'] == 1) {
$paid_status = '<span class="label label-success">Validé</span>';
} elseif ($value['paid_status'] == 2) {
$paid_status = '<span class="label label-warning">En Attente</span>';
} elseif ($value['paid_status'] == 3) {
$paid_status = '<span class="label label-info">Validé et Livré</span>';
} else {
$paid_status = '<span class="label label-danger">Refusé</span>';
}
// Calcul délai
$date1 = new DateTime($date_time);
$date2 = new DateTime();
$interval = $date1->diff($date2);
$daysPassed = $interval->days;
$statuDate = '';
if ($value['paid_status'] == 2) {
if ($daysPassed < 8) {
$statuDate = '<span class="label label-success"> depuis ' . $daysPassed . ' Jours</span>';
} elseif ($daysPassed >= 8 && $daysPassed < 15) {
$statuDate = '<span class="label label-warning"> depuis ' . $daysPassed . ' Jours</span>';
} else {
$statuDate = '<span class="label label-danger"> depuis ' . $daysPassed . ' Jours</span>';
}
}
$result['data'][$key] = [
$value['bill_no'],
$value['customer_name'],
$value['customer_phone'],
$date_time . " <br >" . $statuDate,
number_format((int) $value['discount'], 0, ',', ' '),
number_format((int) $value['gross_amount'], 0, ',', ' '),
$paid_status,
$buttons
];
}
return $this->response->setJSON($result);
}
// ========================================
// POUR LES AUTRES UTILISATEURS (COMMERCIALE, SECURITE, etc.)
// ========================================
else {
foreach ($data as $key => $value) {
$date_time = date('d-m-Y h:i a', strtotime($value['date_time']));
$buttons = '';
// Bouton imprimer
if (in_array('viewOrder', $this->permission) && $users['group_name'] != "SECURITE" && $users['group_name'] != "COMMERCIALE") {
$buttons .= '<a target="_blank" href="' . site_url('orders/printDiv/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-print"></i></a>';
}
// ✅ Bouton modifier pour statuts 0 et 2, ET si c'est l'utilisateur créateur
if (in_array('updateOrder', $this->permission)
&& $users["id"] == $value['user_id']
&& in_array($value['paid_status'], [0, 2])) {
$buttons .= ' <a href="' . site_url('orders/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>';
}
// Bouton voir
if (in_array('viewOrder', $this->permission)) {
$buttons .= '
<a
href="#"
data-order-id="' . $value['id'] . '"
class="btn btn-default btn-view"
data-toggle="tooltip"
title="Voir">
<i class="fa fa-eye"></i>
</a>';
}
// ✅ Bouton supprimer pour statuts 0 et 2, ET si c'est l'utilisateur créateur
if (in_array('deleteOrder', $this->permission)
&& $users["id"] == $value['user_id']
&& in_array($value['paid_status'], [0, 2])) {
$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>';
}
// Statut de paiement
if ($value['paid_status'] == 1) {
$paid_status = '<span class="label label-success">Validé</span>';
} elseif ($value['paid_status'] == 2) {
$paid_status = '<span class="label label-warning">En Attente</span>';
} elseif ($value['paid_status'] == 3) {
$paid_status = '<span class="label label-info">Validé et Livré</span>';
} else {
$paid_status = '<span class="label label-danger">Refusé</span>';
}
// Calcul délai
$date1 = new DateTime($date_time);
$date2 = new DateTime();
$interval = $date1->diff($date2);
$daysPassed = $interval->days;
$statuDate = '';
if ($value['paid_status'] == 2) {
if ($daysPassed < 8) {
$statuDate = '<span class="label label-success"> depuis ' . $daysPassed . ' Jours</span>';
} elseif ($daysPassed >= 8 && $daysPassed < 15) {
$statuDate = '<span class="label label-warning"> depuis ' . $daysPassed . ' Jours</span>';
} else {
$statuDate = '<span class="label label-danger"> depuis ' . $daysPassed . ' Jours</span>';
}
}
$result['data'][$key] = [
$value['product_names'],
$value['user_name'],
$date_time . " <br >" . $statuDate,
number_format((int) $value['discount'], 0, ',', ' '),
number_format((int) $value['gross_amount'], 0, ',', ' '),
$paid_status,
$buttons
];
}
return $this->response->setJSON($result);
}
}
/**
* Affiche le formulaire create avec les données d'une commande existante
* Pour le rôle COMMERCIALE
*/
/**
* function who check if the product is null
* and create notification about it
* @param array $product_id id of the product
* @param int $store_id id of the store
* @return void
*/
private function checkProductisNull(array $product_id, $store_id)
{
$notification = new NotificationController();
$product = new Products();
for ($i = 0; $i < count($product_id); $i++) {
$singleProduct = $product->getProductData($product_id[$i]);
if ($singleProduct['product_sold'] == true) {
$notification->createNotification("Produit en rupture de stock", "Direction", $store_id, "products");
}
}
}
private function calculGross($request)
{
$amount = $request;
$montant = 0;
for ($i = 0; $i < \count($amount); $i++) {
$montant += $amount[$i];
}
return $montant;
}
public function create()
{
$this->verifyRole('createOrder');
$data['page_title'] = $this->pageTitle;
$validation = \Config\Services::validation();
$products = $this->request->getPost('product[]');
if ($products !== null && (count($products) !== count(array_unique($products)))) {
return redirect()->back()->withInput()->with('errors', ['product' => 'Chaque produit sélectionné doit être unique.']);
}
$validation->setRules([
'product[]' => 'required'
]);
$validationData = [
'product[]' => $this->request->getPost('product[]')
];
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
if ($this->request->getMethod() === 'post' && $validation->run($validationData)) {
$session = session();
$users = $session->get('user');
$user_id = $users['id'];
$bill_no = 'BILPR-' . strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4));
// Récupération des produits
$posts = $this->request->getPost('product[]');
$rates = $this->request->getPost('rate_value[]');
$amounts = $this->request->getPost('amount_value[]');
$discount = (float)$this->request->getPost('discount') ?? 0;
$gross_amount = $this->calculGross($amounts);
// ✅ Vérification prix minimal SI rabais existe
if ($discount > 0) {
$FourchettePrix = new \App\Models\FourchettePrix();
foreach ($posts as $index => $productId) {
$productId = (int)$productId;
// Récupérer données produit + prix minimal
$productData = $Products->getProductData($productId);
$fourchette = $FourchettePrix->getFourchettePrixByProductId($productId);
if ($fourchette) {
$prixMinimal = (float)$fourchette['prix_minimal'];
// ✅ Le rabais devient le prix de vente
if ($discount < $prixMinimal) {
$prixMinimalFormatted = number_format($prixMinimal, 0, ',', ' ');
$discountFormatted = number_format($discount, 0, ',', ' ');
return redirect()->back()
->withInput()
->with('errors', [
"⚠️ Commande bloquée : Le rabais de {$discountFormatted} Ar pour « {$productData['name']} » est trop élevé."
]);
}
}
}
}
// ✅ NOUVELLE LOGIQUE : Calculer le montant à payer et net_amount
$montant_a_payer = ($discount > 0) ? $discount : $gross_amount;
// Récupérer les tranches
$tranche_1 = (float)$this->request->getPost('tranche_1') ?? 0;
$tranche_2 = (float)$this->request->getPost('tranche_2') ?? 0;
// Calculer net_amount selon les tranches
if ($tranche_1 > 0 && $tranche_2 > 0) {
// Paiement en 2 tranches
$net_amount = $tranche_1 + $tranche_2;
} else {
// Paiement en 1 tranche ou pas de tranches
$net_amount = $montant_a_payer;
}
// ✅ Création de la commande avec la nouvelle logique
$data = [
'bill_no' => $bill_no,
'customer_name' => $this->request->getPost('customer_name'),
'customer_address' => $this->request->getPost('customer_address'),
'customer_phone' => $this->request->getPost('customer_phone'),
'customer_cin' => $this->request->getPost('customer_cin'),
'date_time' => date('Y-m-d H:i:s'),
'service_charge_rate' => 0,
'vat_charge_rate' => 0,
'vat_charge' => 0,
'net_amount' => $net_amount, // ✅ Net amount = tranches OU montant_a_payer
'discount' => $discount,
'paid_status' => 2, // Toujours En Attente au départ
'user_id' => $user_id,
'amount_value' => $amounts,
'gross_amount' => $gross_amount,
'rate_value' => $rates,
'store_id' => $users['store_id'],
'tranche_1' => $tranche_1,
'tranche_2' => $tranche_2,
'order_payment_mode' => $this->request->getPost('order_payment_mode_1'),
'order_payment_mode_1' => $this->request->getPost('order_payment_mode_2')
];
$order_id = $Orders->create($data, $posts);
if ($order_id) {
session()->setFlashdata('success', 'Créé avec succès');
$Notification = new NotificationController();
// ✅ WORKFLOW SELON LA REMISE
if ($discount > 0) {
// AVEC REMISE : Créer demande + Notifier Conseil
$Order_item1 = new OrderItems();
$order_item_data = $Order_item1->getOrdersItemData($order_id);
$product_ids = array_column($order_item_data, 'product_id');
$productData = new Products();
$product_data_results = [];
foreach ($product_ids as $prod_id) {
$id = (int) $prod_id;
$product_data_results[] = $productData->getProductData($id);
}
$product_lines = [];
foreach ($product_data_results as $product) {
if (isset($product['sku'], $product['price'])) {
$sku = $product['sku'];
$price = $product['price'];
$product_lines[] = "{$sku}:{$price}";
}
}
$product_output = implode("\n", $product_lines);
$data1 = [
'date_demande' => date('Y-m-d H:i:s'),
'montant_demande' => $discount,
'total_price' => $amounts,
'id_store' => $users['store_id'],
'id_order' => $order_id,
'product' => $product_output,
'demande_status' => 'En attente'
];
$Remise = new Remise();
$id_remise = $Remise->addDemande($data1);
// Notification au CONSEIL
$Notification->createNotification(
"Nouvelle demande de remise à valider - Commande " . $bill_no,
"Direction",
(int)$users['store_id'],
"remise/"
);
} else {
// SANS REMISE : Notifier directement la Caissière
$Notification->createNotification(
"Nouvelle commande à valider - " . $bill_no,
"Caissière",
(int)$users['store_id'],
"orders"
);
}
// Redirection selon le rôle
if ($users["group_name"] != "COMMERCIALE") {
$this->checkProductisNull($posts, $users['store_id']);
}
return redirect()->to('orders/');
} else {
session()->setFlashdata('errors', 'Error occurred!!');
return redirect()->to('orders/create/');
}
} else {
// Affichage du formulaire
$company = $Company->getCompanyData(1);
$session = session();
$users = $session->get('user');
$store_id = $users['store_id'];
$data = [
'paid_status' => 2,
'company_data' => $company,
'is_vat_enabled' => ($company['vat_charge_value'] > 0),
'is_service_enabled' => ($company['service_charge_value'] > 0),
'products' => $Products->getProductData2($store_id),
'validation' => $validation,
'page_title' => $this->pageTitle,
];
return $this->render_template('orders/create', $data);
}
}
/**
* Marquer une commande comme livrée
* Accessible uniquement par le rôle SECURITE
*/
public function markAsDelivered()
{
// Activer le retour JSON même en cas d'erreur
ini_set('display_errors', 0);
$order_id = $this->request->getPost('order_id');
$response = ['success' => false, 'messages' => ''];
try {
$session = session();
$users = $session->get('user');
// Vérifier que l'utilisateur est SECURITE
if (!isset($users['group_name']) || $users['group_name'] !== 'SECURITE') {
$response['messages'] = "Accès refusé : seule la sécurité peut marquer une commande comme livrée.";
return $this->response->setJSON($response);
}
if (!$order_id) {
$response['messages'] = "ID de commande manquant.";
return $this->response->setJSON($response);
}
$Orders = new Orders();
// Récupérer la commande actuelle
$current_order = $Orders->getOrdersData((int)$order_id);
if (!$current_order) {
$response['messages'] = "Commande introuvable.";
return $this->response->setJSON($response);
}
// Vérifier que la commande est validée (paid_status = 1)
if ($current_order['paid_status'] != 1) {
$response['messages'] = "Cette commande doit être validée avant d'être marquée comme livrée.";
return $this->response->setJSON($response);
}
// Mettre à jour UNIQUEMENT le statut à 3 (Livré)
$updated = $Orders->update((int)$order_id, ['paid_status' => 3]);
if ($updated) {
// Créer une notification
try {
$Notification = new NotificationController();
$Notification->createNotification(
"Commande " . $current_order['bill_no'] . " livrée avec succès",
"Direction",
(int)$current_order['store_id'],
"orders"
);
} catch (\Exception $e) {
// Si la notification échoue, on continue quand même
log_message('error', 'Erreur notification: ' . $e->getMessage());
}
$response['success'] = true;
$response['messages'] = "La commande a été marquée comme livrée avec succès.";
} else {
$response['messages'] = "Erreur lors de la mise à jour du statut.";
}
} catch (\Exception $e) {
log_message('error', 'Erreur markAsDelivered: ' . $e->getMessage());
$response['messages'] = "Erreur serveur : " . $e->getMessage();
}
return $this->response->setJSON($response);
}
public function getProductValueById()
{
$product_id = $this->request->getPost('product_id');
if ($product_id) {
$Products = new \App\Models\Products();
$product_data = $Products->getProductData($product_id);
$FourchettePrix = new \App\Models\FourchettePrix();
$fourchette = $FourchettePrix->where('product_id', $product_id)->first();
$response = [
'id' => $product_data['id'],
'sku' => $product_data['sku'],
'name' => $product_data['name'],
'prix_vente' => $product_data['prix_vente'],
'prix_minimal' => $fourchette ? $fourchette['prix_minimal'] : 0,
];
return $this->response->setJSON($response);
}
}
public function getTableProductRow()
{
$Products = new Products();
$session = session();
$users = $session->get('user');
$store_id = $users['store_id'];
$product_data = $Products->getProductData2($store_id);
die(var_dump($product_data));
return $this->response->setJSON($product_data);
}
public function update(int $id)
{
$this->verifyRole('updateOrder');
$data['page_title'] = $this->pageTitle;
$validation = \Config\Services::validation();
// ✅ NOUVELLE VÉRIFICATION : Bloquer UNIQUEMENT si statut = Validé (1) ou Validé et Livré (3)
$Orders = new Orders();
$current_order = $Orders->getOrdersData($id);
if (!$current_order) {
session()->setFlashData('errors', 'Commande introuvable.');
return redirect()->to('orders/');
}
// ✅ Bloquer UNIQUEMENT les statuts 1 (Validé) et 3 (Validé et Livré)
// Le statut 0 (Refusé) et 2 (En Attente) restent modifiables
if (in_array($current_order['paid_status'], [1, 3])) {
session()->setFlashData('errors', 'Cette commande ne peut plus être modifiée car elle est déjà validée ou livrée.');
return redirect()->to('orders/');
}
// Règles de validation
$validation->setRules([
'product' => 'required'
]);
$validationData = [
'product' => $this->request->getPost('product')
];
$Company = new Company();
$Products = new Products();
$OrderItems = new OrderItems();
$session = session();
$user = $session->get('user');
$role = $user['group_name'] ?? null;
if ($this->request->getMethod() === 'post' && $validation->run($validationData)) {
// ✅ DOUBLE VÉRIFICATION avant l'update
$current_order_check = $Orders->getOrdersData($id);
if (in_array($current_order_check['paid_status'], [1, 3])) {
session()->setFlashData('errors', 'Cette commande ne peut plus être modifiée car elle est déjà validée ou livrée.');
return redirect()->to('orders/');
}
$old_paid_status = $current_order['paid_status'];
// ✅ Statut payé pour COMMERCIALE reste toujours "En attente"
if ($role === 'COMMERCIALE') {
$paid_status = 2;
} else {
$paid_status = $this->request->getPost('paid_status');
}
// Gestion remise
$discount = $this->request->getPost('discount');
$original_discount = $this->request->getPost('original_discount');
if ($discount === '' || $discount === null) {
$discount = $original_discount;
}
$dataUpdate = [
'customer_name' => $this->request->getPost('customer_name'),
'customer_address' => $this->request->getPost('customer_address'),
'customer_phone' => $this->request->getPost('customer_phone'),
'customer_cin' => $this->request->getPost('customer_cin'),
'gross_amount' => $this->request->getPost('gross_amount_value'),
'service_charge_rate' => $this->request->getPost('service_charge_rate'),
'service_charge' => max(0, (float)$this->request->getPost('service_charge_value')),
'vat_charge_rate' => $this->request->getPost('vat_charge_rate'),
'vat_charge' => max(0, (float)$this->request->getPost('vat_charge_value')),
'net_amount' => $this->request->getPost('net_amount_value'),
'discount' => (float)$discount,
'paid_status' => $paid_status,
'product' => $this->request->getPost('product'),
'product_sold' => true,
'rate_value' => $this->request->getPost('rate_value'),
'amount_value' => $this->request->getPost('amount_value'),
'tranche_1' => $role !== 'COMMERCIALE' ? $this->request->getPost('tranche_1') : null,
'tranche_2' => $role !== 'COMMERCIALE' ? $this->request->getPost('tranche_2') : null,
'order_payment_mode' => $role !== 'COMMERCIALE' ? $this->request->getPost('order_payment_mode_1') : null,
'order_payment_mode_1' => $role !== 'COMMERCIALE' ? $this->request->getPost('order_payment_mode_2') : null
];
if ($Orders->updates($id, $dataUpdate)) {
$order_item_data = $OrderItems->getOrdersItemData($id);
$product_ids = array_column($order_item_data, 'product_id');
$Notification = new NotificationController();
// Notification pour la sécurité si commande validée
if ($old_paid_status == 2 && $paid_status == 1) {
$customer_name = $this->request->getPost('customer_name');
$bill_no = $current_order['bill_no'];
$Notification->createNotification(
"Commande validée: {$bill_no} - Client: {$customer_name}",
"SECURITE",
(int)$user['store_id'],
'orders'
);
}
// Gestion demande de remise
if ((float)$discount > 0) {
$productData = new Products();
$product_data_results = [];
foreach ($product_ids as $product_id) {
$product_data_results[] = $productData->getProductData((int) $product_id);
}
$product_lines = [];
foreach ($product_data_results as $product) {
if (isset($product['sku'], $product['price'])) {
$product_lines[] = "{$product['sku']}:{$product['price']}";
}
}
$product_output = implode("\n", $product_lines);
$data1 = [
'date_demande' => date('Y-m-d H:i:s'),
'montant_demande' => $this->request->getPost('discount'),
'total_price' => $this->request->getPost('amount_value'),
'id_store' => $user['store_id'],
'id_order' => $id,
'product' => $product_output,
'demande_status' => 'En attente'
];
$Remise = new Remise();
$Remise->updateRemise1($id, $data1);
$Notification->createNotification(
"Une nouvelle demande de remise a été ajoutée",
"Direction",
(int)$user['store_id'] ?? null,
"remise/"
);
}
session()->setFlashData('success', 'Commande mise à jour avec succès.');
return redirect()->to('orders/');
} else {
session()->setFlashData('errors', 'Une erreur est survenue lors de la mise à jour.');
return redirect()->to('orders/update/' . $id);
}
}
// Si GET ou validation échoue
$company = $Company->getCompanyData(1);
$data['company_data'] = $company;
$data['is_vat_enabled'] = ($company['vat_charge_value'] > 0);
$data['is_service_enabled'] = ($company['service_charge_value'] > 0);
$orders_data = $Orders->getOrdersData($id);
// ✅ Ajouter un flag pour désactiver le formulaire UNIQUEMENT pour les statuts 1 et 3
$data['is_editable'] = !in_array($orders_data['paid_status'], [1, 3]);
// Montant tranches
$orders_data['montant_tranches'] = (!empty($orders_data['discount']) && $orders_data['discount'] > 0)
? $orders_data['discount']
: $orders_data['gross_amount'];
$result = ['order' => $orders_data];
$orders_item = $OrderItems->getOrdersItemData($orders_data['id']);
foreach ($orders_item as $item) {
$result['order_item'][] = $item;
}
$data['order_data'] = $result;
$data['products'] = $Products->getActiveProductData();
$data['validation'] = $validation;
return $this->render_template('orders/edit', $data);
}
public function lookOrder(int $id)
{
$this->verifyRole('viewOrder');
$data['page_title'] = $this->pageTitle;
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
$OrderItems = new OrderItems();
$Brands = new Brands();
// En cas déchec de la validation ou si GET
$company = $Company->getCompanyData(1);
$data['company_data'] = $company;
$data['is_vat_enabled'] = ($company['vat_charge_value'] > 0);
$data['is_service_enabled'] = ($company['service_charge_value'] > 0);
$orders_data = $Orders->getOrdersData($id);
// $sum_order_item = $OrderItems->getSumOrdersItemData($orders_data['id']);
$result = [
'order' => $orders_data,
// 'sum_order_data' => $sum_order_item
];
$orders_item = $OrderItems->getOrdersItemData($orders_data['id']);
foreach ($orders_item as $item) {
$result['order_item'][] = $item;
}
$data['order_data'] = $result;
$data['products'] = $Products->getActiveProductData();
$data['brands'] = $Brands->findAll();
return $this->response->setJSON($data);
}
/**
* return storename
* @param int $id
* @return string
*/
private function returnStore($id)
{
$Stores = new Stores();
$store = $Stores->getActiveStore();
$name = "";
foreach ($store as $key => $value) {
if ($value['id'] == $id) {
$name = $value['name'];
}
}
return $name;
}
public function print2(int $id)
{
$this->verifyRole('viewOrder');
if ($id) {
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
$OrderItems = new OrderItems();
// Récupération des données
$order_data = $Orders->getOrdersData($id);
$orders_items = $OrderItems->getOrdersItemData($id);
$company_info = $Company->getCompanyData(1);
// die(\var_dump($orders_items));
$html = '';
// Vérifier si l'utilisateur a payé
if ($order_data['paid_status'] == 1) {
$paid_status = "<span style='color: green; font-weight: bold;'>Validé</span>";
} elseif ($order_data['paid_status'] == 2) {
$paid_status = "<span style='color: orange; font-weight: bold;'>En Attente</span>";
} else {
$paid_status = "<span style='color: red; font-weight: bold;'>Refusé</span>";
}
// Génération du HTML
$html .= '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">
<style>
body { font-size: 14px; font-family: Arial, sans-serif; }
.invoice-container {
max-width: 750px; /* Réduire la largeur du cadre */
margin: 20px auto;
padding: 20px;
border: 2px solid #007bff; /* Bordure plus visible */
border-radius: 10px;
background: #f0f8ff; /* Couleur de fond plus douce */
}
.invoice-header {
background: #007bff;
color: white;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 10px;
border-radius: 10px 10px 0 0;
}
.invoice-footer {
background: #007bff;
color: white;
text-align: center;
font-size: 14px;
padding: 10px;
border-radius: 0 0 10px 10px;
margin-top: 12px;
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #e9ecef; }
p, strong { color: #333; }
@media print {
body { font-size: 14px; font-family: Arial, sans-serif, margin: 1cm; }
@page { margin: 0; }
.invoice-container {
max-width: 750px;
margin: 20px auto;
padding: 20px;
border: 2px solid #007bff;
border-radius: 10px;
background: #f0f8ff;
}
.invoice-header {
background: #007bff !important;
color: white !important;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 10px;
border-radius: 10px 10px 0 0;
}
input {
border: none;
outline: none;
width: 100%;
font-size: 14px;
}
.invoice-footer {
background: #007bff !important;
color: white !important;
text-align: center;
font-size: 14px;
padding: 10px;
border-radius: 0 0 10px 10px;
margin-top: 12px;
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #e9ecef; }
p, strong { color: #333; } a
/* Supprimer l\'ombre et les bordures non nécessaires */
.invoice-container { box-shadow: none !important; border: none !important; }
/* Éviter que les couleurs soient supprimées à l\'impression */
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
</style>
</head>
<body onload="window.print();">
<div class="invoice-container">
<div class="invoice-header" style="background: #007bff;color: white;text-align: center;font-size: 18px;font-weight: bold;padding: 10px;border-radius: 10px 10px 0 0; ">
' . esc($company_info['company_name']) . '
</div>
<div style="display: flex;justify-content: space-around;margin-top: 3%;">
<div>
<p><strong>Facture ID :</strong> ' . esc($order_data['bill_no']) . '</p>
<p><strong>NIF :</strong> ' . esc($company_info['NIF']) . '</p>
<p><strong>STAT :</strong> ' . esc($company_info['STAT']) . '</p>
<p style="display: flex; gap: 10px;">
<strong>Contact :</strong>
<span>' . esc($company_info['phone']) . '</span>
<span>' . esc($company_info['phone2']) . '</span>
</p>
<p><strong>Magasin :</strong> ' . esc($this->returnStore($order_data['store_id'])) . '</p>
</div>
<div>
<p><strong>Nom:</strong> ' . esc($order_data['customer_name']) . '</p>
<p><strong>Adresse:</strong> ' . esc($order_data['customer_address']) . '</p>
<p><strong>Téléphone:</strong> ' . esc($order_data['customer_phone']) . '</p>
<p><strong>CIN:</strong> ' . esc($order_data['customer_cin']) . '</p>
<br >
<br >
<p><strong>Antananarivo le</strong> ' . esc(date('d/m/Y')) . '</p>
</div>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>Marque</th>
<th>Moteur</th>
<th>Puissance</th>
<th>Prix</th>
</tr>
</thead>
<tbody>';
foreach ($orders_items as $item) {
$product_data = $Products->getProductData($item['product_id']);
$html .= '<tr>
<td>' . esc($product_data['sku']) . '</td>
<td>' . esc($product_data['numero_de_moteur']) . '</td>
<td><input type="text" name="customField" placeholder="Remplir ici" style="border: none; outline: none; width: 100%;" value="' . esc($product_data['puissance']) . '"></td>
<td style="text-align:right;">' . number_format((float) $item['amount'], 2, '.', ' ') . '</td>
</tr>';
}
$html .= ' </tbody>
</table>
<table class="table">
<tr>
<th>Total:</th>
<td>' . number_format(((float) $order_data['gross_amount'] - ((float) $order_data['gross_amount'] * 0.2)), 2, '.', ' ') . '</td>
</tr>
<tr>
<th>TVA:</th>
<td>' . number_format((((float) $order_data['gross_amount'] * 0.2)), 2, '.', ' ') . '</td>
</tr>';
$html .= '<tr>
<th>Réduction:</th>
<td>' . number_format((float) $order_data['discount'], 2, '.', ' ') . '</td>
</tr>
<tr>
<th>Total à payer:</th>
<td><strong>' . number_format((float) ($order_data['net_amount']), 2, '.', ' ') . '</strong></td>
</tr>
<tr>
<th>Statut:</th>
<td>' . $paid_status . '</td>
</tr>';
// Vérification et ajout des informations de paiement
if (!empty($order_data['order_payment_mode'])) {
$html .= '<tr>
<th>Mode de paiement:</th>
<td><strong>' . esc($order_data['order_payment_mode']) . '</strong></td>
</tr>';
}
if (!empty($order_data['tranche_1'])) {
$html .= '<tr>
<th>Tranche 1:</th>
<td><strong>' . number_format((float) $order_data['tranche_1'], 2, '.', ' ') . '</strong></td>
</tr>';
}
if (!empty($order_data['tranche_2'])) {
$html .= '<tr>
<th>Tranche 2:</th>
<td><strong>' . number_format((float) $order_data['tranche_2'], 2, '.', ' ') . '</strong></td>
</tr>';
}
$html .= '</table>
<div style="display: flex;align-items: center;justify-content: space-around;margin-bottom: 5%;">
<div>
<p style="font-weight:bold;">L\'acheteur</p>
</div>
<div>
<p style="font-weight:bold;">Le vendeur</p>
</div>
</div>
<div class="invoice-footer">
Merci pour votre achat !<br>
<strong style="color:white;">Original</strong>
</div>
</div>
</body>
</html>';
return $this->response->setBody($html);
}
}
public function print(int $id)
{
$this->verifyRole('viewOrder');
if ($id) {
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
$OrderItems = new OrderItems();
$order_data = $Orders->getOrdersData($id);
$orders_items = $OrderItems->getOrdersItemData($id);
$company_info = $Company->getCompanyData(1);
$paid_status = ($order_data['paid_status'] == 1)
? "<span style='color: green; font-weight: bold;'>Payé</span>"
: "<span style='color: red; font-weight: bold;'>Non payé</span>";
foreach ($orders_items as $index => $item) {
$product_data = $Products->getProductData($item['product_id']);
echo '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">
<style>
body { font-size: 14px; font-family: Arial, sans-serif; margin: 0; padding: 0; }
.invoice-container {
max-width: 750px;
margin: 20px auto;
padding: 20px;
border: 2px solid #007bff;
border-radius: 10px;
background: #f0f8ff;
page-break-after: always;
}
.invoice-header {
background: #007bff !important;
color: white;
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 10px;
border-radius: 10px 10px 0 0;
}
.invoice-footer {
background: #007bff !important;
color: white;
text-align: center;
font-size: 14px;
padding: 10px;
border-radius: 0 0 10px 10px;
margin-top: 12px;
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #e9ecef; }
input {
border: none;
outline: none;
width: 100%;
font-size: 14px;
}
@media print {
body { font-size: 14px; font-family: Arial, sans-serif, margin: 1cm; }
@page { margin: 0; }
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body, .invoice-container {
margin: 0;
padding: 0;
border: none;
box-shadow: none;
}
.invoice-container {
page-break-after: always;
}
}
</style>
</head>
<body onload="window.print()">
<div class="invoice-container">
<div class="invoice-header">' . esc($company_info['company_name']) . '</div>
<div style="display: flex; justify-content: space-around; margin-top: 3%;">
<div>
<p><strong>Facture ID :</strong> ' . esc($order_data['bill_no']) . '</p>
<p><strong>NIF :</strong> ' . esc($company_info['NIF']) . '</p>
<p><strong>STAT :</strong> ' . esc($company_info['STAT']) . '</p>
<p style="display: flex; gap: 10px;">
<strong>Contact :</strong>
<span>' . esc($company_info['phone']) . '</span>
<span>' . esc($company_info['phone2']) . '</span>
</p>
<p><strong>Magasin :</strong> ' . esc($this->returnStore($order_data['store_id'])) . '</p>
</div>
<div>
<p><strong>Nom:</strong> ' . esc($order_data['customer_name']) . '</p>
<p><strong>Adresse:</strong> ' . esc($order_data['customer_address']) . '</p>
<p><strong>Téléphone:</strong> ' . esc($order_data['customer_phone']) . '</p>
<p><strong>CIN:</strong> ' . esc($order_data['customer_cin']) . '</p>
<br><br>
<p><strong>Antananarivo le</strong> ' . esc(date('d/m/Y')) . '</p>
</div>
</div>
<table class="table">
<thead>
<tr>
<th>Marque</th>
<th>Moteur</th>
<th>Puissance</th>
<th>Prix</th>
</tr>
</thead>
<tbody>
<tr>
<td>' . esc($product_data['sku']) . '</td>
<td>' . esc($product_data['numero_de_moteur']) . '</td>
<td><input type="text" value="' . esc($product_data['puissance']) . '" placeholder="Remplir ici"></td>
<td style="text-align:right;">' . number_format((float)$item['amount'], 2, '.', ' ') . '</td>
</tr>
</tbody>
</table>
<table class="table">
<tr>
<th>Total:</th>
<td>' . number_format($item['amount'] - ($item['amount'] * 0.2), 2, '.', ' ') . '</td>
</tr>
<tr>
<th>TVA:</th>
<td>' . number_format($item['amount'] * 0.2, 2, '.', ' ') . '</td>
</tr>
<tr>
<th>Réduction:</th>
<td>' . number_format($order_data['discount'], 2, '.', ' ') . '</td>
</tr>
<tr>
<th>Total à payer:</th>
<td><strong>' . number_format($item['amount'] - $order_data['discount'], 2, '.', ' ') . '</strong></td>
</tr>
<tr>
<th>Statut:</th>
<td>' . $paid_status . '</td>
</tr>';
if (!empty($order_data['order_payment_mode'])) {
echo '<tr><th>Mode de paiement:</th><td><strong>' . esc($order_data['order_payment_mode']) . '</strong></td></tr>';
}
if (!empty($order_data['tranche_1'])) {
echo '<tr><th>Tranche 1:</th><td><strong>' . number_format((float)$order_data['tranche_1'], 2, '.', ' ') . '</strong></td></tr>';
}
if (!empty($order_data['tranche_2'])) {
echo '<tr><th>Tranche 2:</th><td><strong>' . number_format((float)$order_data['tranche_2'], 2, '.', ' ') . '</strong></td></tr>';
}
echo '</table>
<div style="display: flex; justify-content: space-around; margin-top: 5%;">
<div><p style="font-weight:bold;">L\'acheteur</p></div>
<div><p style="font-weight:bold;">Le vendeur</p></div>
</div>
<div class="invoice-footer">
Merci pour votre achat !<br>
<strong style="color:white;">Original</strong>
</div>
</div>
</body>
</html>';
}
}
}
public function remove()
{
$this->verifyRole('deleteOrder');
$order_id = $this->request->getPost('order_id');
$response = [];
if ($order_id) {
$Orders = new Orders();
if ($Orders->remove($order_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 $this->response->setJSON($response);
}
public function createById(int $id)
{
$this->verifyRole('createOrder');
$data['page_title'] = $this->pageTitle;
$Company = new Company();
$Products = new Products();
// If validation fails
$company = $Company->getCompanyData(1);
// Prepare data for the view
$data = [
'company_data' => $company,
'is_vat_enabled' => ($company['vat_charge_value'] > 0),
'is_service_enabled' => ($company['service_charge_value'] > 0),
'products' => $Products->getProductData($id),
// 'discount' => $Products->getProductData($id)['discount'],
'pu' => $Products->getProductData($id)['prix_vente'],
'page_title' => $this->pageTitle,
];
return $this->render_template('orders/createbyid', $data);
}
// update caisse
public function update_caisse($data)
{
$p1 = 0;
$p2 = 0;
$op = "";
$p3 = 0;
$dest = "";
if ($data['tranche2']) {
if ($data['order']) {
# code...
}
}
}
public function print3(int $id)
{
// Vérification du rôle
$this->verifyRole('viewOrder');
if (!$id) {
return $this->response->setStatusCode(400, 'ID manquant');
}
// Instanciation des modèles
$Orders = new Orders();
$Company = new Company();
$OrderItems = new OrderItems();
$Products = new Products();
// Récupération des données
$order_data = $Orders->getOrdersData($id);
$items = $OrderItems->getOrdersItemData($id);
$company_info = $Company->getCompanyData(1);
// Statut de paiement
$paid_status = $order_data['paid_status'] == 1
? "<span style='color: green; font-weight: bold;'>Payé</span>"
: "<span style='color: red; font-weight: bold;'>Non payé</span>";
// STYLE COMMUN
$style = '
<style>
body { font-family: Arial, sans-serif; font-size:14px; margin:0; padding:0; }
.invoice-container { max-width:750px; margin:20px auto; padding:20px;
border:2px solid #007bff; border-radius:10px; page-break-after:always; }
.invoice-header { background:#007bff !important; color:#fff; text-align:center;
font-size:18px; padding:10px; border-radius:10px 10px 0 0; }
.invoice-footer { background:#007bff !important; color:#fff; text-align:center;
font-size:14px; padding:10px; border-radius:0 0 10px 10px; margin-top:12px; }
.info { display:flex; justify-content:space-between; margin-top:15px; }
table { width:100%; border-collapse:collapse; margin-top:15px; }
th, td { border:1px solid #ddd; padding:8px; text-align:left; }
th { background:#e9ecef; }
.summary {
width: 80%;
margin: 20px 0;
margin-right: 0;
}
.summary th, .summary td { border:none; padding:4px; text-align:right; }
.summary th { text-align:left; }
@media print {
body { font-size: 14px; font-family: Arial, sans-serif, margin: 1cm; }
@page { margin: 0; }
* { -webkit-print-color-adjust:exact; print-color-adjust:exact; } }
</style>
';
// --- FACTURES : Une par produit ---
foreach ($items as $item) {
$product_data = $Products->getProductData($item['product_id']);
$unitPrice = (float)$item['amount'];
$quantity = isset($item['qty']) ? (int)$item['qty'] : 1;
$subtotal = $unitPrice * $quantity;
$vatAmount = $subtotal * 0.2;
$discount = (float)$order_data['discount'];
$totalNet = $subtotal + $vatAmount - $discount;
echo '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">
' . $style . '
</head>
<body onload="window.print()">
<div class="invoice-container">
<div class="invoice-header">FACTURE</div>
<div class="info">
<div>
<p><strong>Facture ID :</strong> ' . esc($order_data['bill_no']) . '</p>
<p><strong>NIF :</strong> ' . esc($company_info['NIF']) . '</p>
<p><strong>STAT :</strong> ' . esc($company_info['STAT']) . '</p>
<p><strong>Contact :</strong> ' . esc($company_info['phone']) . ' / ' . esc($company_info['phone2']) . '</p>
<p><strong>Magasin :</strong> ' . esc($this->returnStore($order_data['store_id'])) . '</p>
</div>
<div>
<p><strong>Nom:</strong> ' . esc($order_data['customer_name']) . '</p>
<p><strong>Adresse:</strong> ' . esc($order_data['customer_address']) . '</p>
<p><strong>Téléphone:</strong> ' . esc($order_data['customer_phone']) . '</p>
<p><strong>CIN:</strong> ' . esc($order_data['customer_cin']) . '</p>
</div>
</div>
<table>
<thead><tr><th>Marque</th><th>Moteur</th><th>Puissance</th><th>Prix unitaire</th></tr></thead>
<tbody>
<tr>
<td>' . esc($product_data['sku']) . '</td>
<td>' . esc($product_data['numero_de_moteur']) . '</td>
<td>' . esc($product_data['puissance']) . '</td>
<td>' . number_format($unitPrice, 2, '.', ' ') . '</td>
</tr>
</tbody>
</table>
<table class="table summary">
<tr><th>Total:</th><td></td></tr>
<tr><th>TVA (20%):</th><td></td></tr>
<tr><th>Réduction:</th><td></td></tr>
<tr><th>Total à payer:</th><td><strong></strong></td></tr>
<tr><th>Statut:</th><td></td></tr>';
if (!empty($order_data['order_payment_mode'])) {
echo '<tr><th>Mode de paiement:</th><td></td></tr>';
}
if (!empty($order_data['tranche_1'])) {
echo '<tr><th>Tranche 1:</th><td></td></tr>';
}
if (!empty($order_data['tranche_2'])) {
echo '<tr><th>Tranche 2:</th><td></td></tr>';
}
echo '</table>
<div style="display: flex; justify-content: space-around; margin-top: 5%;">
<div><p style="font-weight:bold;">L\'acheteur</p></div>
<div><p style="font-weight:bold;">Le vendeur</p></div>
</div>
<div class="invoice-footer">
Merci pour votre achat !<br>
<strong style="color:white;">Original</strong>
</div>
</div>
</body>
</html>';
}
// --- BON DE COMMANDE : une seule fois après la boucle ---
echo '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">
' . $style . '
</head>
<body onload="window.print()">
<div class="invoice-container">
<div class="invoice-header">BON DE COMMANDE</div>
<div class="info">
<div>
<p><strong>Commande ID :</strong> ' . esc($order_data['order_no'] ?? $order_data['bill_no']) . '</p>
<p><strong>Magasin :</strong> ' . esc($this->returnStore($order_data['store_id'])) . '</p>
</div>
<div>
<p><strong>Nom:</strong> ' . esc($order_data['customer_name']) . '</p>
<p><strong>Adresse:</strong> ' . esc($order_data['customer_address']) . '</p>
<p><strong>Téléphone:</strong> ' . esc($order_data['customer_phone']) . '</p>
<p><strong>CIN:</strong> ' . esc($order_data['customer_cin']) . '</p>
</div>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>Marque</th>
<th>Moteur</th>
<th>Puissance</th>
<th>Prix</th>
</tr>
</thead>
<tbody>';
$total_ht = 0;
foreach ($items as $item) {
$product_data = $Products->getProductData($item['product_id']);
$total_ht += (float)$item['amount'];
echo '<tr>
<td>' . esc($product_data['sku']) . '</td>
<td>' . esc($product_data['numero_de_moteur']) . '</td>
<td><input type="text" value="' . esc($product_data['puissance']) . '" style="border:none;width:100%;outline:none;"></td>
<td style="text-align:right;">' . number_format((float)$item['amount'], 2, '.', ' ') . '</td>
</tr>';
}
$tva = $total_ht * 0.2;
$total_ttc = $total_ht + $tva - $order_data['discount'];
echo '</tbody>';
echo ' </table>
<table class="table summary">
<tr><th>Total:</th><td>' . number_format($total_ht, 2, '.', ' ') . '</td></tr>
<tr><th>TVA:</th><td>' . number_format($tva, 2, '.', ' ') . '</td></tr>
<tr><th>Réduction:</th><td>' . number_format($order_data['discount'], 2, '.', ' ') . '</td></tr>
<tr><th>Total à payer:</th><td><strong>' . number_format($total_ttc, 2, '.', ' ') . '</strong></td></tr>
</table>';
echo '<div style="display: flex; justify-content: space-around; margin-top: 5%;">
<div><p style="font-weight:bold;">L\'acheteur</p></div>
<div><p style="font-weight:bold;">Le vendeur</p></div>
</div>';
echo'<div class="invoice-footer">
Merci pour votre commande !<br>
<strong style="color:white;">Original</strong>
</div>';
echo '</div>';
echo '</body>';
echo'</html>';
}
// PRINT5 - Facture détaillée avec conditions générales
// ====================================
public function print5(int $id)
{
$this->verifyRole('viewOrder');
if (! $id) {
throw new \CodeIgniter\Exceptions\PageNotFoundException();
}
// Modèles
$Orders = new Orders();
$Company = new Company();
$OrderItems = new OrderItems();
// Récupération des données
$order = $Orders->getOrdersData($id);
$items = $OrderItems->getOrdersItemData($id);
$company = $Company->getCompanyData(1);
$today = date('d/m/Y');
// ✅ Vérifier si c'est une avance "sur mer"
$isAvanceMere = false;
foreach ($items as $item) {
if (!empty($item['product_name']) && empty($item['product_id'])) {
$isAvanceMere = true;
break;
}
}
// Calculs
$discount = (float) $order['discount'];
$grossAmount = (float) $order['gross_amount'];
$totalTTC = ($discount > 0) ? $discount : $grossAmount;
$totalHT = $totalTTC / 1.20;
$tva = $totalTTC - $totalHT;
$inWords = $this->numberToWords((int) round($totalTTC));
$paidLabel = $order['paid_status'] == 1 ? 'Payé' : 'Non payé';
// Début du HTML
$html = '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Facture '.$order['bill_no'].'</title>
<style>
body { font-family: Arial, sans-serif; font-size:14px; color:#000;margin:0; padding:0; }
.header { display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; }
.header .infos { line-height:1.4; }
.header img { max-height:80px; }
.client { margin-bottom:20px; }
table { width:100%; border-collapse:collapse; margin-bottom:20px; }
th, td { border:1px solid #000; padding:6px; }
th { background:#f0f0f0; }
.right { text-align:right; }
.signature { display:flex; justify-content:space-between; margin-top:50px; }
.signature div { text-align:center; }
.conditions { page-break-before: always; padding:20px; line-height:1.5; }
</style>
</head>
<body onload="window.print()">
<div class="header">
<div class="infos">
<h2 style="margin:0;">'.esc($company['company_name']).'</h2>
<p style="margin:2px 0;"><strong>NIF :</strong> '.esc($company['NIF']).'</p>
<p style="margin:2px 0;"><strong>STAT :</strong> '.esc($company['STAT']).'</p>
<p style="margin:2px 0;"><strong>Contact :</strong> '.esc($company['phone']).' | '.esc($company['phone2']).'</p>
</div>
<div style="text-align:center;">
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo">
<p style="margin:5px 0; font-weight:bold;">Facture N° '.esc($order['bill_no']).'</p>
</div>
</div>
<div class="client">
<p><strong>DOIT Nom :</strong> '.esc($order['customer_name']).'</p>
<p><strong>Adresse :</strong> '.esc($order['customer_address']).'</p>
<p><strong>CIN :</strong> '.esc($order['customer_cin']).'</p>
<p><strong>Téléphone :</strong> '.esc($order['customer_phone'] ?? '').'</p>
<p style="text-align:right;"><em>Antananarivo, le '.$today.'</em></p>
</div>';
// ✅ TABLEAU ADAPTÉ SELON LE TYPE
if ($isAvanceMere) {
// ========================================
// TABLE SIMPLIFIÉE POUR AVANCE "SUR MER"
// ========================================
$html .= '
<table>
<thead>
<tr>
<th>Produit</th>
<th class="right">Prix (Ar)</th>
</tr>
</thead>
<tbody>';
foreach ($items as $it) {
$details = $this->getOrderItemDetails($it);
if (!$details) continue;
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
$html .= '
<tr>
<td>'.esc($details['product_name']);
// Afficher le commentaire s'il existe
if (!empty($details['commentaire'])) {
$html .= '<br><em style="font-size:12px; color:#666;">'.esc($details['commentaire']).'</em>';
}
$html .= '</td>
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
</tr>';
}
} else {
// ========================================
// TABLE COMPLÈTE POUR AVANCE "SUR TERRE" OU COMMANDE NORMALE
// ========================================
$html .= '
<table>
<thead>
<tr>
<th>MARQUE</th>
<th>Désignation</th>
<th>N° Moteur</th>
<th>N° Châssis</th>
<th>Puissance (CC)</th>
<th class="right">PRIX (Ar)</th>
</tr>
</thead>
<tbody>';
foreach ($items as $it) {
$details = $this->getOrderItemDetails($it);
if (!$details) continue;
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
$html .= '
<tr>
<td>'.esc($details['marque']).'</td>
<td>'.esc($details['product_name']).'</td>
<td>'.esc($details['numero_moteur']).'</td>
<td>'.esc($details['numero_chassis']).'</td>
<td>'.esc($details['puissance']).'</td>
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
</tr>';
}
}
$html .= '
</tbody>
</table>
<table>
<tr>
<td><strong>Prix (HT) :</strong></td>
<td class="right">'.number_format($totalHT, 0, '', ' ').' Ar</td>
</tr>
<tr>
<td><strong>TVA (20%) :</strong></td>
<td class="right">'.number_format($tva, 0, '', ' ').' Ar</td>
</tr>
<tr>
<td><strong>Total (TTC) :</strong></td>
<td class="right">'.number_format($totalTTC, 0, '', ' ').' Ar</td>
</tr>
</table>
<div style="border:1px solid #000; padding:10px; margin-bottom:30px;">
<strong>Arrêté à la somme de :</strong><br>
'.$inWords.'
</div>
<div class="signature">
<div>L\'Acheteur<br><br>__________________</div>
<div>Le Vendeur<br><br>__________________</div>
</div>
<!-- Conditions Générales avec saut de page -->
<div class="conditions">
<div style="display:flex; justify-content:space-between; align-items:center;">
<h3 style="margin:0;">Conditions Générales</h3>
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo" style="height:60px;">
</div>
<ul>
<li>Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.</li>
<li>Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.</li>
<li>Aucun service après-vente n\'est fourni.</li>
<li>La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.</li>
<li>La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation dans le délai prévu par la loi.</li>
</ul>
<div style="text-align:center; margin-top:50px;">L\'Acheteur</div>
</div>
</body>
</html>';
return $this->response->setBody($html);
}
/**
* Convertit un nombre entier en texte (français, sans décimales).
* Usage basique, pour Ariary.
*/
private function numberToWords(int $num): string
{
// Cas zéro
if ($num === 0) {
return 'zéro ariary';
}
// Tableaux de base
$units = [
'', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six',
'sept', 'huit', 'neuf', 'dix', 'onze', 'douze',
'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf'
];
$tens = [
2 => 'vingt', 3 => 'trente', 4 => 'quarante',
5 => 'cinquante', 6 => 'soixante',
7 => 'soixante-dix', 8 => 'quatre-vingt', 9 => 'quatre-vingt-dix'
];
// Fonction récursive interne (sans la monnaie)
$convert = function(int $n) use (&$convert, $units, $tens): string {
if ($n < 20) {
return $units[$n];
}
if ($n < 100) {
$d = intdiv($n, 10);
$r = $n % 10;
// 7079 et 9099
if ($d === 7 || $d === 9) {
$base = $d === 7 ? 60 : 80;
return $tens[$d] . ($r ? '-' . $units[$n - $base] : '');
}
// 2069 ou 8089
return $tens[$d] . ($r ? '-' . $units[$r] : '');
}
if ($n < 1000) {
$h = intdiv($n, 100);
$rest = $n % 100;
$hundredText = $h > 1 ? $units[$h] . ' cent' : 'cent';
// « deux cents » prend un « s » si pas de reste
if ($h > 1 && $rest === 0) {
$hundredText .= 's';
}
return $hundredText . ($rest ? ' ' . $convert($rest) : '');
}
if ($n < 1000000) {
$k = intdiv($n, 1000);
$rest = $n % 1000;
$thousandText = $k > 1 ? $convert($k) . ' mille' : 'mille';
return $thousandText . ($rest ? ' ' . $convert($rest) : '');
}
// millions et plus
$m = intdiv($n, 1000000);
$rest = $n % 1000000;
$millionText = $m > 1 ? $convert($m) . ' million' : 'million';
// pas de 's' à million en francais
return $millionText . ($rest ? ' ' . $convert($rest) : '');
};
// Construit le texte sans la monnaie, puis ajoute 'ariary' à la fin
$words = $convert($num);
return trim($words) . ' ariary';
}
/**
* ✅ NOUVELLE MÉTHODE : Vérifier si une commande provient d'une avance "sur mer"
*/
private function isFromAvanceMere($order_id)
{
$db = \Config\Database::connect();
// Vérifier s'il existe une avance "sur mer" liée à cette commande
$avance = $db->table('avances')
->select('type_avance, product_name')
->where('is_order', 1)
->where('customer_name',
$db->table('orders')
->select('customer_name')
->where('id', $order_id)
->get()
->getRow()->customer_name ?? ''
)
->where('type_avance', 'mere')
->get()
->getRowArray();
return $avance !== null;
}
/**
* ✅ NOUVELLE MÉTHODE : Récupérer les détails d'un item de commande
* Gère à la fois les produits normaux et ceux des avances "sur mer"
*/
private function getOrderItemDetails($item)
{
$Products = new Products();
$Brand = new Brands();
// Si l'item a un product_name (avance sur mer), on l'utilise directement
if (!empty($item['product_name']) && empty($item['product_id'])) {
return [
'type' => 'mere',
'product_name' => $item['product_name'],
'marque' => $item['marque'] ?? $item['product_name'],
'numero_moteur' => $item['numero_moteur'] ?? '',
'numero_chassis' => $item['numero_chassis'] ?? '',
'puissance' => $item['puissance'] ?? '',
'commentaire' => $item['commentaire'] ?? '',
'prix' => (float)$item['amount']
];
}
// Sinon, récupérer depuis la table products (avance sur terre ou commande normale)
if (empty($item['product_id'])) {
return null;
}
$product = $Products->getProductData($item['product_id']);
if (!$product) {
return null;
}
// Récupérer le nom de la marque
$brandName = 'N/A';
if (!empty($product['marque'])) {
$brandData = $Brand->find($product['marque']);
if ($brandData && isset($brandData['name'])) {
$brandName = $brandData['name'];
}
}
return [
'type' => 'terre',
'product_name' => $product['name'],
'marque' => $brandName,
'numero_moteur' => $product['numero_de_moteur'] ?? '',
'numero_chassis' => $product['chasis'] ?? $product['sku'] ?? '',
'puissance' => $product['puissance'] ?? '',
'commentaire' => '',
'prix' => (float)$item['amount']
];
}
/**
* ✅ PRINT7 - Bon de commande adapté pour avances "sur mer" et "sur terre"
*/
public function print7(int $id)
{
$this->verifyRole('viewOrder');
if (!$id) {
throw new \CodeIgniter\Exceptions\PageNotFoundException();
}
// Modèles
$Orders = new Orders();
$Company = new Company();
$OrderItems = new OrderItems();
// Récupération des données
$order = $Orders->getOrdersData($id);
$items = $OrderItems->getOrdersItemData($id);
$company = $Company->getCompanyData(1);
$today = date('d/m/Y');
// ✅ VÉRIFIER SI C'EST UNE AVANCE "SUR MER"
$isAvanceMere = false;
foreach ($items as $item) {
if (!empty($item['product_name']) && empty($item['product_id'])) {
$isAvanceMere = true;
break;
}
}
// ✅ LOGIQUE DE REMISE
$discount = (float) $order['discount'];
$grossAmount = (float) $order['gross_amount'];
$totalTTC = ($discount > 0) ? $discount : $grossAmount;
$totalHT = $totalTTC / 1.20;
$tva = $totalTTC - $totalHT;
$paidLabel = $order['paid_status'] == 1 ? 'Payé' : 'Non payé';
// Démarrage du HTML
$html = '<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Bon de commande '.$order['bill_no'].'</title>
<style>
body { font-family: Arial, sans-serif; font-size:14px; color:#000; margin:0; padding:0; }
.header { display:flex; justify-content:space-between; align-items:center; margin:20px; }
.header .infos { line-height:1.4; }
.header img { max-height:80px; }
.client { margin:20px; }
table { width:calc(100% - 40px); margin:0 20px 20px; border-collapse:collapse; }
th, td { border:1px solid #000; padding:8px; }
th { background:#f0f0f0; }
.right { text-align:right; }
.signature { display:flex; justify-content:space-between; margin:50px 20px 20px; }
.signature div { text-align:center; }
.footer { text-align:center; margin:20px; font-size:12px; color:#666; }
.conditions { page-break-before: always; padding:20px; line-height:1.5; }
@media print {
@page { margin:1cm; }
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
</style>
</head>
<body onload="window.print()">
<div class="header">
<div class="infos">
<h2 style="margin:0;">'.esc($company['company_name']).'</h2>
<p style="margin:2px 0;"><strong>NIF :</strong> '.esc($company['NIF']).'</p>
<p style="margin:2px 0;"><strong>STAT :</strong> '.esc($company['STAT']).'</p>
<p style="margin:2px 0;"><strong>Contact :</strong> '.esc($company['phone']).' | '.esc($company['phone2']).'</p>
</div>
<div style="text-align:center;">
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo">
<p style="margin:5px 0; font-weight:bold;">Bon de commande N° '.esc($order['bill_no']).'</p>
</div>
</div>
<div class="client">
<p><strong>Client :</strong> '.esc($order['customer_name']).'</p>
<p><strong>Adresse :</strong> '.esc($order['customer_address']).'</p>
<p><strong>Téléphone :</strong> '.esc($order['customer_phone']).'</p>
<p><strong>CIN :</strong> '.esc($order['customer_cin']).'</p>
<p style="text-align:right;"><em>Antananarivo, le '.$today.'</em></p>
</div>';
// ========================================
// ✅ TABLEAU ADAPTÉ SELON LE TYPE
// ========================================
if ($isAvanceMere) {
// --- TABLE SIMPLIFIÉE POUR AVANCE "SUR MER" (2-3 COLONNES) ---
$html .= '
<table>
<thead>
<tr>
<th>Produit</th>
<th class="right">Prix Unitaire (Ar)</th>
</tr>
</thead>
<tbody>';
foreach ($items as $item) {
$details = $this->getOrderItemDetails($item);
if (!$details) continue;
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
$html .= '
<tr>
<td>'.esc($details['product_name']);
// Afficher le commentaire s'il existe
if (!empty($details['commentaire'])) {
$html .= '<br><em style="font-size:12px; color:#666;">Remarque : '.esc($details['commentaire']).'</em>';
}
$html .= '</td>
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
</tr>';
}
$html .= '
</tbody>
</table>';
} else {
// --- TABLE COMPLÈTE POUR AVANCE "SUR TERRE" OU COMMANDE NORMALE (7 COLONNES) ---
$html .= '
<table>
<thead>
<tr>
<th>Nom</th>
<th>Marque</th>
<th>Catégorie</th>
<th>N° Moteur</th>
<th>Châssis</th>
<th>Puissance (CC)</th>
<th class="right">Prix Unitaire (Ar)</th>
</tr>
</thead>
<tbody>';
$Products = new Products();
$Brand = new Brands();
$Category = new Category();
foreach ($items as $item) {
$details = $this->getOrderItemDetails($item);
if (!$details) continue;
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
// Récupérer la catégorie si c'est un produit terre
$categoryName = 'Non définie';
if ($details['type'] === 'terre' && !empty($item['product_id'])) {
$p = $Products->getProductData($item['product_id']);
if (!empty($p['categorie_id'])) {
$categoryData = $Category->find($p['categorie_id']);
if ($categoryData && isset($categoryData['name'])) {
$categoryName = $categoryData['name'];
}
}
}
$html .= '
<tr>
<td>'.esc($details['product_name']).'</td>
<td>'.esc($details['marque']).'</td>
<td>'.esc($categoryName).'</td>
<td>'.esc($details['numero_moteur']).'</td>
<td>'.esc($details['numero_chassis']).'</td>
<td>'.esc($details['puissance']).'</td>
<td class="right">'.number_format($prixAffiche, 0, '', ' ').'</td>
</tr>';
}
$html .= '
</tbody>
</table>';
}
// ========================================
// TABLEAU RÉCAPITULATIF (IDENTIQUE POUR TOUS)
// ========================================
$html .= '
<table style="width:calc(100% - 40px); margin:0 20px 20px;">
<tr>
<td><strong>Total HT :</strong></td>
<td class="right">'.number_format($totalHT, 0, '', ' ').' Ar</td>
</tr>
<tr>
<td><strong>TVA (20%) :</strong></td>
<td class="right">'.number_format($tva, 0, '', ' ').' Ar</td>
</tr>
<tr>
<td><strong>Total TTC :</strong></td>
<td class="right">'.number_format($totalTTC, 0, '', ' ').' Ar</td>
</tr>
<tr>
<td><strong>Statut :</strong></td>
<td class="right">'.$paidLabel.'</td>
</tr>';
if (!empty($order['order_payment_mode'])) {
$html .= '
<tr>
<td><strong>Mode de paiement :</strong></td>
<td class="right">'.esc($order['order_payment_mode']).'</td>
</tr>';
}
if (!empty($order['tranche_1'])) {
$html .= '
<tr>
<td><strong>Tranche 1 :</strong></td>
<td class="right">'.number_format((float)$order['tranche_1'], 0, '', ' ').' Ar</td>
</tr>';
}
if (!empty($order['tranche_2'])) {
$html .= '
<tr>
<td><strong>Tranche 2 :</strong></td>
<td class="right">'.number_format((float)$order['tranche_2'], 0, '', ' ').' Ar</td>
</tr>';
}
$html .= '
</table>
<div class="signature">
<div>L\'acheteur<br><br>__________________</div>
<div>Le vendeur<br><br>__________________</div>
</div>
<div class="footer">
Merci pour votre confiance
</div>
<!-- Conditions Générales avec saut de page -->
<div class="conditions">
<div style="display:flex; justify-content:space-between; align-items:center;">
<h3 style="margin:0;">Conditions Générales</h3>
<img src="'.base_url('assets/images/company_logo.jpg').'" alt="Logo" style="height:60px;">
</div>
<ul>
<li>Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.</li>
<li>Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.</li>
<li>Aucun service après-vente n\'est fourni.</li>
<li>La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.</li>
<li>La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation dans le délai prévu par la loi.</li>
</ul>
<div style="text-align:center; margin-top:50px;">L\'Acheteur</div>
</div>
</body>
</html>';
return $this->response->setBody($html);
}
// ====================================
// PRINT31 - Facture + Bon de commande (pages séparées)
// ====================================
public function print31(int $id)
{
$this->verifyRole('viewOrder');
if (!$id) {
return $this->response->setStatusCode(400, 'ID manquant');
}
$Orders = new Orders();
$Company = new Company();
$OrderItems = new OrderItems();
$Products = new Products();
$Brand = new Brands();
$Category = new Category();
$order_data = $Orders->getOrdersData($id);
$items = $OrderItems->getOrdersItemData($id);
$company_info = $Company->getCompanyData(1);
// ✅ Vérifier si c'est une avance "sur mer"
$isAvanceMere = false;
foreach ($items as $item) {
if (!empty($item['product_name']) && empty($item['product_id'])) {
$isAvanceMere = true;
break;
}
}
$paid_status = $order_data['paid_status'] === 1
? "<span style='color: green; font-weight: bold;'>Validé</span>"
: "<span style='color: red; font-weight: bold;'>Refusé</span>";
// Calculs globaux
$discount = (float) $order_data['discount'];
$grossAmount = (float) $order_data['gross_amount'];
$totalTTC = ($discount > 0) ? $discount : $grossAmount;
$totalHT = $totalTTC / 1.20;
$tva = $totalTTC - $totalHT;
// --- FACTURES : Une par produit ---
foreach ($items as $item) {
// ✅ Utiliser getOrderItemDetails au lieu de getProductData directement
$details = $this->getOrderItemDetails($item);
if (!$details) continue;
$unitPrice = $details['prix'];
$quantity = isset($item['qty']) ? (int) $item['qty'] : 1;
$subtotal = $unitPrice * $quantity;
// ✅ Pour avance sur mer avec remise, utiliser la remise comme prix
$prixAffiche = ($discount > 0 && $isAvanceMere) ? $discount : $unitPrice;
echo '<!DOCTYPE html>';
echo '<html lang="fr">';
echo '<head><meta charset="utf-8">';
echo '<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">';
echo "<style>
body { font-family: Arial, sans-serif; font-size:14px; color:#000; margin:0; padding:0; }
.header { display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; }
.header .infos { line-height:1.4; }
.header img { max-height:80px; }
.client { margin-bottom:20px; }
table { width:100%; border-collapse:collapse; margin-bottom:20px; }
th, td { border:1px solid #000; padding:6px; text-align:left; }
th { background:#f0f0f0; }
.right { text-align:right; }
.signature { display:flex; justify-content:space-between; margin-top:50px; }
.signature div { text-align:center; }
.page-break { page-break-after: always; }
.to-fill { border-bottom: 1px dotted #000; min-width: 150px; display: inline-block; }
@media print {
body { font-size: 14px; }
@page { margin: 1cm; }
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
</style>";
echo '</head><body onload="window.print()">';
echo '<div class="page-break">';
echo '<div class="header">';
echo '<div class="infos">';
echo '<h2 style="margin:0;">' . esc($company_info['company_name']) . '</h2>';
echo '<p style="margin:2px 0;"><strong>NIF :</strong> ' . esc($company_info['NIF']) . '</p>';
echo '<p style="margin:2px 0;"><strong>STAT :</strong> ' . esc($company_info['STAT']) . '</p>';
echo '<p style="margin:2px 0;"><strong>Contact :</strong> ' . esc($company_info['phone']) . ' | ' . esc($company_info['phone2']) . '</p>';
echo '<p style="margin:2px 0;"><strong>Magasin :</strong> ' . esc($this->returnStore($order_data['store_id'])) . '</p>';
echo '</div>';
echo '<div style="text-align:center;">';
echo '<img src="' . base_url('assets/images/company_logo.jpg') . '" alt="Logo">';
echo '<p style="margin:5px 0; font-weight:bold;">Facture N° ' . esc($order_data['bill_no']) . '</p>';
echo '<p style="margin:5px 0;"><em>Antananarivo, le ' . date('d/m/Y') . '</em></p>';
echo '</div>';
echo '</div>';
echo '<div class="client">';
echo '<p><strong>DOIT :</strong> ' . esc($order_data['customer_name']) . '</p>';
echo '<p><strong>Adresse :</strong> ' . esc($order_data['customer_address']) . '</p>';
echo '<p><strong>Téléphone :</strong> ' . esc($order_data['customer_phone']) . '</p>';
echo '<p><strong>CIN :</strong> ' . esc($order_data['customer_cin']) . '</p>';
echo '</div>';
// ✅ TABLEAU ADAPTÉ SELON LE TYPE
if ($isAvanceMere) {
// TABLE SIMPLIFIÉE POUR AVANCE "SUR MER"
echo '<table><thead><tr>';
echo '<th>Désignation</th>';
echo '<th>Produit à compléter</th>';
echo '<th class="right">Prix (Ar)</th>';
echo '</tr></thead><tbody>';
echo '<tr>';
echo '<td>' . esc($details['product_name']) . '</td>';
echo '<td><span class="to-fill">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td>';
echo '<td class="right">' . number_format($prixAffiche, 0, '', ' ') . '</td>';
echo '</tr>';
// Afficher commentaire si existant
if (!empty($details['commentaire'])) {
echo '<tr><td colspan="3"><em style="font-size:12px; color:#666;">Remarque : ' . esc($details['commentaire']) . '</em></td></tr>';
}
echo '</tbody></table>';
} else {
// TABLE COMPLÈTE POUR AVANCE "SUR TERRE" OU COMMANDE NORMALE
// Récupérer catégorie
$categoryName = 'Non définie';
if ($details['type'] === 'terre' && !empty($item['product_id'])) {
$p = $Products->getProductData($item['product_id']);
if (!empty($p['categorie_id'])) {
$categoryData = $Category->find($p['categorie_id']);
if ($categoryData && isset($categoryData['name'])) {
$categoryName = $categoryData['name'];
}
}
}
echo '<table><thead><tr>';
echo '<th>Nom</th><th>Marque</th><th>Catégorie</th><th>N° Moteur</th><th>Châssis</th><th>Puissance (CC)</th><th class="right">Prix Unit. (Ar)</th>';
echo '</tr></thead><tbody>';
echo '<tr>';
echo '<td>' . esc($details['product_name']) . '</td>';
echo '<td>' . esc($details['marque']) . '</td>';
echo '<td>' . esc($categoryName) . '</td>';
echo '<td>' . esc($details['numero_moteur']) . '</td>';
echo '<td>' . esc($details['numero_chassis']) . '</td>';
echo '<td>' . esc($details['puissance']) . '</td>';
echo '<td class="right">' . number_format($prixAffiche, 0, '', ' ') . '</td>';
echo '</tr>';
echo '</tbody></table>';
}
// Récapitulatif pour cette facture
$itemHT = $prixAffiche / 1.20;
$itemTVA = $prixAffiche - $itemHT;
echo '<table>';
echo '<tr><td><strong>Total HT :</strong></td><td class="right">' . number_format($itemHT, 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>TVA (20%) :</strong></td><td class="right">' . number_format($itemTVA, 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>Total TTC :</strong></td><td class="right">' . number_format($prixAffiche, 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>Statut :</strong></td><td class="right">' . $paid_status . '</td></tr>';
if (!empty($order_data['order_payment_mode'])) {
echo '<tr><td><strong>Mode de paiement :</strong></td><td class="right">' . esc($order_data['order_payment_mode']) . '</td></tr>';
}
if (!empty($order_data['tranche_1'])) {
echo '<tr><td><strong>Tranche 1 :</strong></td><td class="right">' . number_format((float)$order_data['tranche_1'], 0, '', ' ') . ' Ar</td></tr>';
}
if (!empty($order_data['tranche_2'])) {
echo '<tr><td><strong>Tranche 2 :</strong></td><td class="right">' . number_format((float)$order_data['tranche_2'], 0, '', ' ') . ' Ar</td></tr>';
}
echo '</table>';
echo '<div class="signature">';
echo '<div>L\'Acheteur<br><br>__________________</div>';
echo '<div>Le Vendeur<br><br>__________________</div>';
echo '</div>';
echo '</div>'; // fin page-break
echo '</body></html>';
}
// --- BON DE COMMANDE (UNE SEULE PAGE) ---
echo '<!DOCTYPE html>';
echo '<html lang="fr">';
echo '<head><meta charset="utf-8">';
echo '<link rel="stylesheet" href="' . base_url('assets/bower_components/bootstrap/dist/css/bootstrap.min.css') . '">';
echo "<style>
body { font-family: Arial, sans-serif; font-size:14px; color:#000; margin:0; padding:0; }
.header { display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; }
.header .infos { line-height:1.4; }
.header img { max-height:80px; }
.client { margin-bottom:20px; }
table { width:100%; border-collapse:collapse; margin-bottom:20px; }
th, td { border:1px solid #000; padding:6px; text-align:left; }
th { background:#f0f0f0; }
.right { text-align:right; }
.signature { display:flex; justify-content:space-between; margin-top:50px; }
.signature div { text-align:center; }
.to-fill { border-bottom: 1px dotted #000; min-width: 150px; display: inline-block; }
.conditions { page-break-before: always; padding:20px; line-height:1.5; }
@media print {
body { font-size: 14px; }
@page { margin: 1cm; }
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
</style>";
echo '</head><body>';
echo '<div class="header">';
echo '<div class="infos">';
echo '<h2 style="margin:0;">' . esc($company_info['company_name']) . '</h2>';
echo '<p style="margin:2px 0;"><strong>NIF :</strong> ' . esc($company_info['NIF']) . '</p>';
echo '<p style="margin:2px 0;"><strong>STAT :</strong> ' . esc($company_info['STAT']) . '</p>';
echo '<p style="margin:2px 0;"><strong>Contact :</strong> ' . esc($company_info['phone']) . ' | ' . esc($company_info['phone2']) . '</p>';
echo '</div>';
echo '<div style="text-align:center;">';
echo '<img src="' . base_url('assets/images/company_logo.jpg') . '" alt="Logo">';
echo '<p style="margin:5px 0; font-weight:bold;">Bon de Commande N° ' . esc($order_data['bill_no']) . '</p>';
echo '</div>';
echo '</div>';
echo '<div class="client">';
echo '<p><strong>Client :</strong> ' . esc($order_data['customer_name']) . '</p>';
echo '<p><strong>Adresse :</strong> ' . esc($order_data['customer_address']) . '</p>';
echo '<p><strong>Téléphone :</strong> ' . esc($order_data['customer_phone']) . '</p>';
echo '<p><strong>CIN :</strong> ' . esc($order_data['customer_cin']) . '</p>';
echo '</div>';
// ✅ TABLEAU RÉCAPITULATIF SELON LE TYPE
if ($isAvanceMere) {
echo '<table><thead><tr>';
echo '<th>Désignation</th>';
echo '<th>Produit à compléter</th>';
echo '<th class="right">Prix (Ar)</th>';
echo '</tr></thead><tbody>';
foreach ($items as $item) {
$details = $this->getOrderItemDetails($item);
if (!$details) continue;
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
echo '<tr>';
echo '<td>' . esc($details['product_name']) . '</td>';
echo '<td><span class="to-fill">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td>';
echo '<td class="right">' . number_format($prixAffiche, 0, '', ' ') . '</td>';
echo '</tr>';
}
echo '</tbody></table>';
} else {
echo '<table><thead><tr>';
echo '<th>Nom</th><th>Marque</th><th>Catégorie</th><th>N° Moteur</th><th>Châssis</th><th>Puissance</th><th class="right">Prix (Ar)</th>';
echo '</tr></thead><tbody>';
foreach ($items as $item) {
$details = $this->getOrderItemDetails($item);
if (!$details) continue;
$prixAffiche = ($discount > 0) ? $discount : $details['prix'];
// Catégorie
$categoryName = 'Non définie';
if ($details['type'] === 'terre' && !empty($item['product_id'])) {
$p = $Products->getProductData($item['product_id']);
if (!empty($p['categorie_id'])) {
$categoryData = $Category->find($p['categorie_id']);
if ($categoryData) $categoryName = $categoryData['name'];
}
}
echo '<tr>';
echo '<td>' . esc($details['product_name']) . '</td>';
echo '<td>' . esc($details['marque']) . '</td>';
echo '<td>' . esc($categoryName) . '</td>';
echo '<td>' . esc($details['numero_moteur']) . '</td>';
echo '<td>' . esc($details['numero_chassis']) . '</td>';
echo '<td>' . esc($details['puissance']) . '</td>';
echo '<td class="right">' . number_format($prixAffiche, 0, '', ' ') . '</td>';
echo '</tr>';
}
echo '</tbody></table>';
}
echo '<table>';
echo '<tr><td><strong>Total HT :</strong></td><td class="right">' . number_format($totalHT, 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>TVA :</strong></td><td class="right">' . number_format($tva, 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>Réduction :</strong></td><td class="right">' . number_format($discount, 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>Total TTC :</strong></td><td class="right">' . number_format($totalTTC, 0, '', ' ') . ' Ar</td></tr>';
echo '</table>';
echo '<div class="signature">';
echo '<div>L\'Acheteur<br><br>__________________</div>';
echo '<div>Le Vendeur<br><br>__________________</div>';
echo '</div>';
// --- CONDITIONS GÉNÉRALES ---
echo '<div class="conditions">';
echo '<div style="display:flex; justify-content:space-between; align-items:center;">';
echo '<h3 style="margin:0;">Conditions Générales</h3>';
echo '<img src="' . base_url('assets/images/company_logo.jpg') . '" alt="Logo" style="height:60px;">';
echo '</div>';
echo '<ul>';
echo '<li>Aucun accessoire (casque, rétroviseur, batterie, etc.) n\'est inclus avec la moto. Si le client en a besoin, il doit les acheter séparément.</li>';
echo '<li>Le client doit vérifier soigneusement la marchandise avant de quitter notre établissement.</li>';
echo '<li>Aucun service après-vente n\'est fourni.</li>';
echo '<li>La moto est vendue sans garantie, car il s\'agit d\'un modèle d\'occasion.</li>';
echo '<li>La facture étant un document provisoire ne peut se substituer au certificat modèle (si requis) délivré au client au moment de l\'achat. Il appartient à ce dernier de procéder à l\'immatriculation dans le délai prévu par la loi.</li>';
echo '</ul>';
echo '<div style="text-align:center; margin-top:50px;">L\'Acheteur</div>';
echo '</div>';
echo '</body></html>';
}
}