Index.php

PHP Guest 19 Views Size: 19.97 KB Posted on: Jun 2, 26 @ 9:24 PM
  1. 1<?php
  2. 2session_start();
  3. 3ini_set('display_errors', 1);
  4. 4ini_set('display_startup_errors', 1);
  5. 5error_reporting(E_ALL);
  6. 6
  7. 7require_once __DIR__ . '/../app/Support/helpers.php';
  8. 8require_once __DIR__ . '/../app/Core/Database.php';
  9. 9require_once __DIR__ . '/../app/Core/View.php';
  10. 10require_once __DIR__ . '/../app/Repositories/InvoiceRepository.php';
  11. 11require_once __DIR__ . '/../app/Services/VatCalculator.php';
  12. 12require_once __DIR__ . '/../app/Services/InvoiceBuilder.php';
  13. 13require_once __DIR__ . '/../app/Services/PdfService.php';
  14. 14require_once __DIR__ . '/../app/Services/ExcelGenerator.php';
  15. 15require_once __DIR__ . '/../app/Controllers/DashboardController.php';
  16. 16require_once __DIR__ . '/../app/Controllers/InvoiceController.php';
  17. 17
  18. 18use App\Core\Database;
  19. 19use App\Repositories\InvoiceRepository;
  20. 20use App\Services\VatCalculator;
  21. 21use App\Services\InvoiceBuilder;
  22. 22use App\Services\PdfService;
  23. 23use App\Services\ExcelGenerator;
  24. 24use App\Controllers\DashboardController;
  25. 25use App\Controllers\InvoiceController;
  26. 26
  27. 27$config = require __DIR__ . '/../config/database.php';
  28. 28$pdo = Database::connect($config);
  29. 29
  30. 30$repository = new InvoiceRepository($pdo);
  31. 31$calculator = new VatCalculator(0.20);
  32. 32$builder = new InvoiceBuilder($calculator);
  33. 33$pdfService = new PdfService();
  34. 34$excelService = new ExcelGenerator();
  35. 35$dashboardController = new DashboardController($repository);
  36. 36$invoiceController = new InvoiceController($repository, $calculator, $builder, $pdfService, $excelService);
  37. 37
  38. 38$action = $_GET['action'] ?? 'dashboard';
  39. 39$dashboardYear = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
  40. 40
  41. 41if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  42. 42 if ($action === 'store') $invoiceController->store();
  43. 43 if ($action === 'update') $invoiceController->update();
  44. 44 if ($action === 'form') {
  45. 45 include __DIR__ . '/../app/Views/invoices/form.php';
  46. 46 exit;
  47. 47}
  48. 48
  49. 49if ($action === 'edit') {
  50. 50 $edit = $repository->find((int)($_GET['id'] ?? 0));
  51. 51 include __DIR__ . '/../app/Views/invoices/form.php';
  52. 52 exit;
  53. 53}
  54. 54
  55. 55
  56. 56 if ($action === 'delete') $invoiceController->delete();
  57. 57 if ($action === 'tva') $invoiceController->selectedVat();
  58. 58}
  59. 59
  60. 60if ($action === 'pdf') {
  61. 61 $invoiceController->pdf();
  62. 62 exit;
  63. 63}
  64. 64
  65. 65if ($action === 'csv') {
  66. 66 // Filtres liste
  67. 67$listYear = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
  68. 68$listPage = isset($_GET['page']) ? max(1,(int)$_GET['page']) : 1;
  69. 69$listSort = isset($_GET['sort']) ? $_GET['sort'] : 'date_desc';
  70. 70$listQuarter = isset($_GET['quarter']) ? trim($_GET['quarter']) : '';
  71. 71$listQ = isset($_GET['q']) ? trim($_GET['q']) : '';
  72. 72$perPage = 20;
  73. 73$totalInv = $repository->countSearch($listYear, $listQuarter, $listQ);
  74. 74$pages = max(1, (int)ceil($totalInv / $perPage));
  75. 75if ($listPage > $pages) $listPage = $pages;
  76. 76$invoices = $repository->search($listYear, $listPage, $perPage, $listSort, $listQuarter, $listQ);
  77. 77$availYears = $repository->availableYears();
  78. 78 header('Content-Type: text/csv; charset=UTF-8');
  79. 79 header('Content-Disposition: attachment; filename="factures.csv"');
  80. 80 $out = fopen('php://output', 'w');
  81. 81 fputcsv($out, ['FACTURE', 'NUMERO', 'DATE', 'TRIMESTRE', 'ANNEE'], ';');
  82. 82 fputcsv($out, ['LIGNE', 'DESIGNATION', 'QTE', 'PU HT', 'TOTAL HT'], ';');
  83. 83 fputcsv($out, [], ';');
  84. 84 foreach ($invoices as $inv) {
  85. 85 fputcsv($out, ['FACTURE', $inv['invoice_number'], $inv['invoice_date'], $inv['quarter'], $inv['year']], ';');
  86. 86 $it = $pdo->prepare('SELECT * FROM invoice_items WHERE invoice_id=:id ORDER BY id ASC');
  87. 87 $it->execute(['id' => $inv['id']]);
  88. 88 foreach ($it->fetchAll() as $item) {
  89. 89 fputcsv($out, ['LIGNE', $item['description'], $item['quantity'], $item['unit_price_ht'], $item['line_total_ht']], ';');
  90. 90 }
  91. 91 fputcsv($out, ['TOTAL HT', $inv['total_ht']], ';');
  92. 92 fputcsv($out, ['TVA 20%', $inv['total_vat']], ';');
  93. 93 fputcsv($out, ['TOTAL TTC', $inv['total_ttc']], ';');
  94. 94 fputcsv($out, [], ';');
  95. 95 }
  96. 96 fclose($out);
  97. 97 exit;
  98. 98}
  99. 99
  100. 100if ($action === 'excel') {
  101. 101 $xid = isset($_GET['id']) ? (int)$_GET['id'] : 0;
  102. 102 if ($xid > 0) {
  103. 103 ob_end_clean();
  104. 104 header('Location: /TV@/dlxls.php?id=' . $xid);
  105. 105 exit;
  106. 106 }
  107. 107 if (!empty($_POST['invoice_ids'])) {
  108. 108 ob_end_clean();
  109. 109 require_once dirname(__DIR__) . '/dlxls.php';
  110. 110 exit;
  111. 111 }
  112. 112 echo '<div class="container py-4"><div class="alert alert-warning">Selectionnez au moins une facture.</div></div>';
  113. 113 exit;
  114. 114}
  115. 115
  116. 116$invoices = $repository->all();
  117. 117$selectedTotals = $_SESSION['selected_totals'] ?? null;
  118. 118unset($_SESSION['selected_totals']);
  119. 119
  120. 120$edit = null;
  121. 121if ($action === 'edit') {
  122. 122 $edit = $repository->find((int)($_GET['id'] ?? 0));
  123. 123}
  124. 124
  125. 125$yearInvoices = array_values(array_filter($invoices, fn($inv) => (int)$inv['year'] === $dashboardYear));
  126. 126$yearTotals = ['c' => count($yearInvoices), 'ht' => 0, 'vat' => 0, 'ttc' => 0];
  127. 127foreach ($yearInvoices as $inv) {
  128. 128 $yearTotals['ht'] += (float)$inv['total_ht'];
  129. 129 $yearTotals['vat'] += (float)$inv['total_vat'];
  130. 130 $yearTotals['ttc'] += (float)$inv['total_ttc'];
  131. 131}
  132. 132$quarters = $repository->quarterSummary($dashboardYear);
  133. 133$latest = array_slice($yearInvoices, 0, 10);
  134. 134$nextNumber = $repository->nextNumber();
  135. 135?>
  136. 136<!doctype html>
  137. 137<html lang="fr">
  138. 138<head>
  139. 139<meta charset="utf-8">
  140. 140<meta name="viewport" content="width=device-width, initial-scale=1">
  141. 141<title>Facturation TVA</title>
  142. 142<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
  143. 143<link rel="stylesheet" href="assets/css/style.css">
  144. 144</head>
  145. 145<body class="bg-light">
  146. 146<div class="container py-4">
  147. 147 <div class="card card-soft mb-4">
  148. 148 <div class="card-body d-flex justify-content-between align-items-center flex-wrap gap-2">
  149. 149 <div>
  150. 150 <h1 class="h4 mb-1">Facturation TVA</h1>
  151. 151 <div class="text-muted">Dashboard, factures, TVA, PDF, CSV, Excel</div>
  152. 152 </div>
  153. 153 <div class="d-flex gap-2 flex-wrap">
  154. 154 <a href="?action=dashboard&year=<?= (int)$dashboardYear ?>" class="btn btn-outline-primary btn-sm">Dashboard</a>
  155. 155 <a href="?action=list" class="btn btn-outline-primary btn-sm">Factures</a>
  156. 156 <a href="?action=form" class="btn btn-primary btn-sm">Nouvelle facture</a>
  157. 157 <a href="?action=csv" class="btn btn-outline-success btn-sm">Export CSV</a>
  158. 158 <a href="?action=excel" class="btn btn-success btn-sm">Export Excel</a>
  159. 159 </div>
  160. 160 </div>
  161. 161 </div>
  162. 162
  163. 163 <?php if (!empty($_SESSION['success'])): ?>
  164. 164 <div class="alert alert-success"><?= e($_SESSION['success']); unset($_SESSION['success']); ?></div>
  165. 165 <?php endif; ?>
  166. 166
  167. 167 <?php if (!empty($_SESSION['error'])): ?>
  168. 168 <div class="alert alert-danger"><?= e($_SESSION['error']); unset($_SESSION['error']); ?></div>
  169. 169 <?php endif; ?>
  170. 170
  171. 171 <?php if ($action === 'dashboard'): ?>
  172. 172 <div class="card card-soft mb-4">
  173. 173 <div class="card-body">
  174. 174 <div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
  175. 175 <h2 class="h5 mb-0">Dashboard <?= (int)$dashboardYear ?></h2>
  176. 176 <form method="get" class="d-flex gap-2 align-items-center">
  177. 177 <input type="hidden" name="action" value="dashboard">
  178. 178 <select name="year" class="form-select form-select-sm" style="width:auto">
  179. 179 <?php for ($y = date('Y'); $y >= date('Y') - 5; $y--): ?>
  180. 180 <option value="<?= (int)$y ?>" <?= $dashboardYear == $y ? 'selected' : '' ?>><?= (int)$y ?></option>
  181. 181 <?php endfor; ?>
  182. 182 </select>
  183. 183 <button class="btn btn-sm btn-primary" type="submit">Voir</button>
  184. 184 </form>
  185. 185 </div>
  186. 186
  187. 187 <div class="row g-3 mb-4">
  188. 188 <div class="col-md-3"><div class="card card-soft"><div class="card-body"><div class="text-muted">Factures <?= (int)$dashboardYear ?></div><div class="fs-4 fw-bold"><?= (int)$yearTotals['c'] ?></div></div></div></div>
  189. 189 <div class="col-md-3"><div class="card card-soft"><div class="card-body"><div class="text-muted">Total HT</div><div class="fs-4 fw-bold"><?= money($yearTotals['ht']) ?></div></div></div></div>
  190. 190 <div class="col-md-3"><div class="card card-soft"><div class="card-body"><div class="text-muted">TVA</div><div class="fs-4 fw-bold"><?= money($yearTotals['vat']) ?></div></div></div></div>
  191. 191 <div class="col-md-3"><div class="card card-soft"><div class="card-body"><div class="text-muted">TTC</div><div class="fs-4 fw-bold"><?= money($yearTotals['ttc']) ?></div></div></div></div>
  192. 192 </div>
  193. 193
  194. 194 <div class="table-responsive">
  195. 195 <table class="table table-sm align-middle mb-0">
  196. 196 <thead><tr><th>Trimestre</th><th>Factures</th><th>HT</th><th>TVA</th><th>TTC</th></tr></thead>
  197. 197 <tbody>
  198. 198 <?php foreach($quarters as $q): ?>
  199. 199 <tr>
  200. 200 <td><?= e($q['quarter']) ?></td>
  201. 201 <td><?= (int)$q['c'] ?></td>
  202. 202 <td><?= money($q['ht']) ?></td>
  203. 203 <td><?= money($q['vat']) ?></td>
  204. 204 <td><?= money($q['ttc']) ?></td>
  205. 205 </tr>
  206. 206 <?php endforeach; ?>
  207. 207 </tbody>
  208. 208 </table>
  209. 209 </div>
  210. 210 </div>
  211. 211 </div>
  212. 212
  213. 213 <div class="card card-soft">
  214. 214 <div class="card-body">
  215. 215 <h2 class="h5 mb-3">Dernières factures de lannée <?= (int)$dashboardYear ?></h2>
  216. 216 <div class="table-responsive">
  217. 217 <table class="table table-sm align-middle">
  218. 218 <thead><tr><th>N°</th><th>Date</th><th>Trimestre</th><th>TVA</th><th>TTC</th></tr></thead>
  219. 219 <tbody>
  220. 220 <?php foreach($latest as $inv): ?>
  221. 221 <tr>
  222. 222 <td><?= e($inv['invoice_number']) ?></td>
  223. 223 <td><?= e($inv['invoice_date']) ?></td>
  224. 224 <td><?= e($inv['quarter']) ?></td>
  225. 225 <td><?= money($inv['total_vat']) ?></td>
  226. 226 <td><?= money($inv['total_ttc']) ?></td>
  227. 227 </tr>
  228. 228 <?php endforeach; ?>
  229. 229 </tbody>
  230. 230 </table>
  231. 231 </div>
  232. 232 </div>
  233. 233 </div>
  234. 234
  235. 235<?php elseif ($action === 'list'): ?>
  236. 236<?php
  237. 237$listYear = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
  238. 238$listPage = isset($_GET['page']) ? max(1,(int)$_GET['page']) : 1;
  239. 239$listSort = isset($_GET['sort']) ? $_GET['sort'] : 'date_desc';
  240. 240$listQuarter = isset($_GET['quarter']) ? trim($_GET['quarter']) : '';
  241. 241$listQ = isset($_GET['q']) ? trim($_GET['q']) : '';
  242. 242$perPage = 20;
  243. 243$totalInv = $repository->countSearch($listYear, $listQuarter, $listQ);
  244. 244$pages = max(1,(int)ceil($totalInv / $perPage));
  245. 245if ($listPage > $pages) $listPage = $pages;
  246. 246$invoices = $repository->search($listYear, $listPage, $perPage, $listSort, $listQuarter, $listQ);
  247. 247$availYears = $repository->availableYears();
  248. 248$baseParams = http_build_query(array_filter(["action"=>"list","year"=>$listYear,"sort"=>$listSort,"quarter"=>$listQuarter,"q"=>$listQ],fn($v)=>$v!==" "&&$v!==null&&$v!==0));
  249. 249?>
  250. 250
  251. 251<div class="card card-soft mb-3">
  252. 252 <div class="card-body py-2">
  253. 253 <form method="get" id="filterForm" class="row g-2 align-items-end">
  254. 254 <input type="hidden" name="action" value="list">
  255. 255 <div class="col-auto">
  256. 256 <label class="form-label mb-1 small text-muted">Ann&eacute;e</label>
  257. 257 <select name="year" class="form-select form-select-sm" style="width:100px" onchange="document.getElementById(&apos;filterForm&apos;).submit()">
  258. 258 <?php foreach ($availYears as $y): ?>
  259. 259 <option value="<?= (int)$y ?>" <?= (int)$y===$listYear?"selected":"" ?>><?= (int)$y ?></option>
  260. 260 <?php endforeach; ?>
  261. 261 </select>
  262. 262 </div>
  263. 263 <div class="col-auto">
  264. 264 <label class="form-label mb-1 small text-muted">Trimestre</label>
  265. 265 <select name="quarter" class="form-select form-select-sm" style="width:120px" onchange="document.getElementById(&apos;filterForm&apos;).submit()">
  266. 266 <option value="" <?= $listQuarter===""?"selected":"" ?>>Tous</option>
  267. 267 <option value="Q1" <?= $listQuarter==="Q1"?"selected":"" ?>>T1 (Jan-Mar)</option>
  268. 268 <option value="Q2" <?= $listQuarter==="Q2"?"selected":"" ?>>T2 (Avr-Jun)</option>
  269. 269 <option value="Q3" <?= $listQuarter==="Q3"?"selected":"" ?>>T3 (Jul-Sep)</option>
  270. 270 <option value="Q4" <?= $listQuarter==="Q4"?"selected":"" ?>>T4 (Oct-Dec)</option>
  271. 271 </select>
  272. 272 </div>
  273. 273 <div class="col-auto">
  274. 274 <label class="form-label mb-1 small text-muted">Trier</label>
  275. 275 <select name="sort" class="form-select form-select-sm" style="width:150px" onchange="document.getElementById(&apos;filterForm&apos;).submit()">
  276. 276 <option value="date_desc" <?= $listSort==="date_desc"?"selected":"" ?>>Date d&eacute;croissante</option>
  277. 277 <option value="date_asc" <?= $listSort==="date_asc"?"selected":"" ?>>Date croissante</option>
  278. 278 </select>
  279. 279 </div>
  280. 280 <div class="col-auto">
  281. 281 <label class="form-label mb-1 small text-muted">Recherche article</label>
  282. 282 <div class="input-group input-group-sm">
  283. 283 <input type="text" name="q" class="form-control" placeholder="Ex: b&eacute;ton, dalle..." value="<?= htmlspecialchars($listQ) ?>" style="width:180px">
  284. 284 <button type="submit" class="btn btn-primary btn-sm">OK</button>
  285. 285 </div>
  286. 286 </div>
  287. 287 <div class="col-auto">
  288. 288 <label class="form-label mb-1 d-block">&nbsp;</label>
  289. 289 <?php if ($listQuarter!==" "||$listQ!==" "||$listSort!=="date_desc"): ?>
  290. 290 <a href="?action=list&amp;year=<?= $listYear ?>" class="btn btn-sm btn-outline-danger">Effacer</a>
  291. 291 <?php endif; ?>
  292. 292 </div>
  293. 293 <div class="col-auto ms-auto">
  294. 294 <label class="form-label mb-1 d-block">&nbsp;</label>
  295. 295 <span class="text-muted small"><?= $totalInv ?> facture(s)</span>
  296. 296 </div>
  297. 297 </form>
  298. 298 </div>
  299. 299</div>
  300. 300
  301. 301<div class="card card-soft">
  302. 302 <div class="card-body p-0">
  303. 303 <form method="post" action="?action=tva" id="invForm">
  304. 304 <div class="d-flex align-items-center gap-2 px-3 py-2 border-bottom bg-light rounded-top">
  305. 305 <div class="form-check mb-0">
  306. 306 <input class="form-check-input" type="checkbox" id="chkAll" onchange="toggleAll(this)">
  307. 307 <label class="form-check-label small" for="chkAll">Tout s&eacute;lectionner</label>
  308. 308 </div>
  309. 309 <button type="button" class="btn btn-sm btn-outline-secondary" onclick="deselectAll()">D&eacute;s&eacute;lectionner tout</button>
  310. 310 <span class="badge bg-secondary" id="selCount">0 s&eacute;lectionn&eacute;(s)</span>
  311. 311 <div class="ms-auto d-flex gap-2">
  312. 312 <button type="submit" formaction="?action=tva" class="btn btn-sm btn-outline-info">TVA</button>
  313. 313 <button type="submit" formaction="/TV@/dlxls.php" class="btn btn-sm btn-success">Export Excel</button>
  314. 314 </div>
  315. 315 </div>
  316. 316 <div class="table-responsive">
  317. 317 <table class="table table-sm align-middle mb-0">
  318. 318 <thead class="table-light">
  319. 319 <tr>
  320. 320 <th style="width:36px"></th>
  321. 321 <th>N&deg;</th>
  322. 322 <th>Client</th>
  323. 323 <th>Date</th>
  324. 324 <th>Trimestre</th>
  325. 325 <th class="text-end">HT</th>
  326. 326 <th class="text-end">TVA</th>
  327. 327 <th class="text-end">TTC</th>
  328. 328 <th class="text-center">Actions</th>
  329. 329 </tr>
  330. 330 </thead>
  331. 331 <tbody>
  332. 332 <?php if (empty($invoices)): ?>
  333. 333 <tr><td colspan="9" class="text-center text-muted py-4">Aucune facture trouv&eacute;e</td></tr>
  334. 334 <?php else: ?>
  335. 335 <?php foreach ($invoices as $inv): ?>
  336. 336 <tr>
  337. 337 <td><input type="checkbox" name="invoice_ids[]" value="<?= $inv["id"] ?>" class="form-check-input chk" onchange="saveSelection();updateCount()"></td>
  338. 338 <td><strong><?= e($inv["invoice_number"]) ?></strong></td>
  339. 339 <td class="text-truncate" style="max-width:150px"><?= e($inv["client_name"]??"") ?></td>
  340. 340 <td><?= e($inv["invoice_date"]) ?></td>
  341. 341 <td><span class="badge bg-light text-dark border"><?= e($inv["quarter"]) ?></span></td>
  342. 342 <td class="text-end"><?= money($inv["total_ht"]) ?></td>
  343. 343 <td class="text-end"><?= money($inv["total_vat"]) ?></td>
  344. 344 <td class="text-end fw-semibold"><?= money($inv["total_ttc"]) ?></td>
  345. 345 <td class="text-center">
  346. 346 <a href="?action=edit&amp;id=<?= $inv["id"] ?>" class="btn btn-sm btn-outline-primary py-0 px-1">Modifier</a>
  347. 347 <a href="/TV@/genpdf.php?id=<?= $inv["id"] ?>" target="_blank" class="btn btn-sm btn-outline-danger py-0 px-1">PDF</a>
  348. 348 <a href="/TV@/dlxls.php?id=<?= $inv["id"] ?>" class="btn btn-sm btn-outline-success py-0 px-1">XLS</a>
  349. 349 <a href="?action=delete&amp;id=<?= $inv["id"] ?>" class="btn btn-sm btn-outline-secondary py-0 px-1" onclick="return confirm(&apos;Supprimer ?&apos;)">Suppr.</a>
  350. 350 </td>
  351. 351 </tr>
  352. 352 <?php endforeach; ?>
  353. 353 <?php endif; ?>
  354. 354 </tbody>
  355. 355 </table>
  356. 356 </div>
  357. 357 <?php if ($pages>1): ?>
  358. 358 <nav class="d-flex justify-content-center align-items-center py-3 gap-1 flex-wrap">
  359. 359 <?php if ($listPage>1): ?><a href="?<?= $baseParams ?>&amp;page=<?= $listPage-1 ?>" class="btn btn-sm btn-outline-secondary">&laquo;</a><?php endif; ?>
  360. 360 <?php $ps=max(1,$listPage-3);$pe=min($pages,$listPage+3); ?>
  361. 361 <?php if($ps>1): ?><a href="?<?= $baseParams ?>&amp;page=1" class="btn btn-sm btn-outline-secondary">1</a><?php if($ps>2): ?><span class="px-1 text-muted">...</span><?php endif; endif; ?>
  362. 362 <?php for($p=$ps;$p<=$pe;$p++): ?>
  363. 363 <a href="?<?= $baseParams ?>&amp;page=<?= $p ?>" class="btn btn-sm <?= $p===$listPage?"btn-primary":"btn-outline-secondary" ?>"><?= $p ?></a>
  364. 364 <?php endfor; ?>
  365. 365 <?php if($pe<$pages): ?><?php if($pe<$pages-1): ?><span class="px-1 text-muted">...</span><?php endif; ?><a href="?<?= $baseParams ?>&amp;page=<?= $pages ?>" class="btn btn-sm btn-outline-secondary"><?= $pages ?></a><?php endif; ?>
  366. 366 <?php if($listPage<$pages): ?><a href="?<?= $baseParams ?>&amp;page=<?= $listPage+1 ?>" class="btn btn-sm btn-outline-secondary">&raquo;</a><?php endif; ?>
  367. 367 <small class="text-muted ms-2">Page <?= $listPage ?>/<?= $pages ?></small>
  368. 368 </nav>
  369. 369 <?php endif; ?>
  370. 370 </form>
  371. 371 </div>
  372. 372</div>
  373. 373<script>
  374. 374const _SK="inv_sel";
  375. 375function _getS(){try{return JSON.parse(sessionStorage.getItem(_SK)||"[]")}catch(e){return[]}}
  376. 376function saveSelection(){const s=_getS();document.querySelectorAll(".chk").forEach(c=>{const id=c.value;if(c.checked&&!s.includes(id))s.push(id);else if(!c.checked){const i=s.indexOf(id);if(i>-1)s.splice(i,1);}});sessionStorage.setItem(_SK,JSON.stringify(s));updateCount();}
  377. 377function restoreSelection(){const s=_getS();document.querySelectorAll(".chk").forEach(c=>c.checked=s.includes(c.value));updateCount();syncChkAll();}
  378. 378function updateCount(){document.getElementById("selCount").textContent=_getS().length+" sélectionné(s)";}
  379. 379function toggleAll(m){document.querySelectorAll(".chk").forEach(c=>c.checked=m.checked);saveSelection();}
  380. 380function deselectAll(){sessionStorage.removeItem(_SK);document.querySelectorAll(".chk").forEach(c=>c.checked=false);document.getElementById("chkAll").checked=false;updateCount();}
  381. 381function syncChkAll(){const a=document.querySelectorAll(".chk").length,c=document.querySelectorAll(".chk:checked").length;const el=document.getElementById("chkAll");el.checked=a>0&&c===a;el.indeterminate=c>0&&c<a;}
  382. 382document.getElementById("invForm").addEventListener("submit",function(e){const btn=e.submitter;const s=_getS();this.querySelectorAll("input[name=\"invoice_ids[]\"]").forEach(e=>e.remove());s.forEach(id=>{const i=document.createElement("input");i.type="hidden";i.name="invoice_ids[]";i.value=id;this.appendChild(i);});if(btn&&btn.getAttribute("formaction")){this.action=btn.getAttribute("formaction");}});
  383. 383document.addEventListener("DOMContentLoaded",()=>restoreSelection());
  384. 384</script>
  385. 385<?php endif; ?>
  386. 386</div>
  387. 387</body>
  388. 388</html>

Paste brut

Comments 0
Login to join the discussion.
  • No comments yet — be the first.
Login to post a comment. Se connecter/S'enregistrer
We use cookies. To comply with GDPR in the EU and the UK we have to show you these.

We use cookies and similar technologies to keep this website functional (including spam protection via Google reCAPTCHA or Cloudflare Turnstile), and — with your consent — to measure usage and show ads. See Privacy.