motorbike/app/Controllers/OrderController.php
2025-10-13 09:59:40 +03:00

2116 lines
84 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');
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
if (in_array('updateOrder', $this->permission) && $users["store_id"] == $value['store_id']) {
$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 (SANS notification ici)
$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>';
}
}
// $Orders_items = new OrderItems();
// $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']);
$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);
}
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 (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>';
}
if (in_array('updateOrder', $this->permission)) {
$buttons .= ' <a href="' . site_url('orders/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>';
}
if (in_array('deleteOrder', $this->permission)) {
$buttons .= ' <button type="button" class="btn btn-danger" onclick="removeFunc(' . $value['id'] . ')" data-toggle="modal" data-target="#removeModal"><i class="fa fa-trash"></i></button>';
}
// 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 (SANS notification)
$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>';
}
}
// $Orders_items= new OrderItems();
// $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']);
$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);
}
else {
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>';
}
if (in_array('updateOrder', $this->permission) && $users["id"] == $value['user_id']) {
$buttons .= ' <a href="' . site_url('orders/update/' . $value['id']) . '" class="btn btn-default"><i class="fa fa-pencil"></i></a>';
}
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>';
}
if (in_array('deleteOrder', $this->permission) && $users["id"] == $value['user_id']) {
$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 (SANS notification)
$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>';
}
}
// $Orders_items= new OrderItems();
// $sum_order_item = $Orders_items->getSumOrdersItemData($value['id']);
$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 inférieur au prix minimal autorisé de {$prixMinimalFormatted} Ar."
]);
}
}
}
}
// ✅ 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/update/' . $order_id);
} else {
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();
// Règles de validation
$validation->setRules([
'product' => 'required'
]);
$validationData = [
'product' => $this->request->getPost('product')
];
$Orders = new Orders();
$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)) {
$current_order = $Orders->getOrdersData($id);
$old_paid_status = $current_order['paid_status'];
// ✅ Statut payé pour COMMERCIALE reste toujours "En attente"
if ($role === 'COMMERCIALE') {
$paid_status = 2; // ← mettre la valeur correspondant à "En attente" dans votre table
} 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/update/' . $id);
} 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);
// 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();
$Products = new Products();
$OrderItems = new OrderItems();
$Brand = new Brands();
$Category = new Category();
// Récupération des données
$order = $Orders->getOrdersData($id);
$items = $OrderItems->getOrdersItemData($id);
$company = $Company->getCompanyData(1);
$today = date('d/m/Y');
// ✅ LOGIQUE DE REMISE: Si discount existe, il devient le prix de vente
$discount = (float) $order['discount'];
$grossAmount = (float) $order['gross_amount'];
// Si remise existe, elle devient le montant TTC, sinon on prend gross_amount
$totalTTC = ($discount > 0) ? $discount : $grossAmount;
$totalHT = $totalTTC / 1.20;
$tva = $totalTTC - $totalHT;
$inWords = $this->numberToWords((int) round($totalTTC));
// Statut paiement
$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>
<table>
<thead>
<tr>
<th>Désignation</th>
<th>MARQUE</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) {
$p = $Products->getProductData($it['product_id']);
// ✅ Récupérer le nom de la marque correctement
$brandName = 'Non définie';
if (!empty($p['marque'])) {
$brandData = $Brand->find($p['marque']);
if ($brandData && isset($brandData['name'])) {
$brandName = $brandData['name'];
}
}
// ✅ LOGIQUE: Si discount existe pour la commande, on l'affiche comme prix
$prixAffiche = ($discount > 0) ? $discount : $p['prix_vente'];
$html .= '
<tr>
<td>'.esc($p['name']).'</td>
<td>'.esc($brandName).'</td>
<td>'.esc($p['numero_de_moteur']).'</td>
<td>'.esc($p['chasis'] ?? '').'</td>
<td>'.esc($p['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';
}
// ====================================
// PRINT7 - Bon de commande
// ====================================
public function print7(int $id)
{
$this->verifyRole('viewOrder');
if (! $id) {
throw new \CodeIgniter\Exceptions\PageNotFoundException();
}
// Modèles
$Orders = new Orders();
$Company = new Company();
$Products = new Products();
$OrderItems = new OrderItems();
$Brand = new Brands();
$Category = new Category();
// Récupération des données
$order = $Orders->getOrdersData($id);
$items = $OrderItems->getOrdersItemData($id);
$company = $Company->getCompanyData(1);
$today = date('d/m/Y');
// Calculs totaux
$totalTTC = (float) $order['net_amount'];
$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; }
}
</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>
<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>';
foreach ($items as $item) {
$p = $Products->getProductData($item['product_id']);
// ✅ Récupérer le nom de la marque correctement
$brandName = 'Non définie';
if (!empty($p['marque'])) {
$brandData = $Brand->find($p['marque']);
if ($brandData && isset($brandData['name'])) {
$brandName = $brandData['name'];
}
}
// ✅ Récupérer le nom de la catégorie
$categoryName = 'Non définie';
if (!empty($p['categorie_id'])) {
$categoryData = $Category->find($p['categorie_id']);
if ($categoryData && isset($categoryData['name'])) {
$categoryName = $categoryData['name'];
}
}
$html .= '<tr>
<td>'.esc($p['name']).'</td>
<td>'.esc($brandName).'</td>
<td>'.esc($categoryName).'</td>
<td>'.esc($p['numero_de_moteur']).'</td>
<td>'.esc($p['chasis'] ?? '').'</td>
<td>'.esc($p['puissance']).'</td>
<td class="right">'.number_format($p['prix_vente'], 0, '', ' ').'</td>
</tr>';
}
$html .= '
</tbody>
</table>
<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>';
}
$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>';
echo $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);
$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>";
// --- FACTURES : Une par produit ---
foreach ($items as $item) {
$p = $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;
$inWords = $this->numberToWords((int) round($subtotal));
// ✅ Récupérer le nom de la marque
$brandName = 'Non définie';
if (!empty($p['marque'])) {
$brandData = $Brand->find($p['marque']);
if ($brandData && isset($brandData['name'])) {
$brandName = $brandData['name'];
}
}
// ✅ Récupérer le nom de la catégorie
$categoryName = 'Non définie';
if (!empty($p['categorie_id'])) {
$categoryData = $Category->find($p['categorie_id']);
if ($categoryData && isset($categoryData['name'])) {
$categoryName = $categoryData['name'];
}
}
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; }
@media print {
body { font-size: 14px; }
@page { margin: 1cm; }
}
</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>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>';
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($p['name']) . '</td>';
echo '<td>' . esc($brandName) . '</td>';
echo '<td>' . esc($categoryName) . '</td>';
echo '<td>' . esc($p['numero_de_moteur']) . '</td>';
echo '<td>' . esc($p['chasis'] ?? '') . '</td>';
echo '<td>' . esc($p['puissance']) . '</td>';
echo '<td class="right">' . number_format($amount, 0, '', ' ') . '</td>';
echo '</tr>';
}
$tva = $total_ht * 0.2;
$total_ttc = $total_ht + $tva - (float) $order_data['discount'];
echo '</tbody></table>';
echo '<table>';
echo '<tr><td><strong>Total HT :</strong></td><td class="right">' . number_format($total_ht, 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($order_data['discount'], 0, '', ' ') . ' Ar</td></tr>';
echo '<tr><td><strong>Total TTC :</strong></td><td class="right">' . number_format($total_ttc, 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 style="page-break-before: always; break-before: page; padding:20px; line-height:1.5;">';
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 '</div>';
echo '</body></html>';
}
}