Untitled

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