{"id":338,"date":"2026-07-06T09:54:41","date_gmt":"2026-07-06T07:54:41","guid":{"rendered":"https:\/\/lab.pharmavitale.net\/wp-cepi\/?page_id=338"},"modified":"2026-07-16T13:14:45","modified_gmt":"2026-07-16T11:14:45","slug":"intranet-pharmag8","status":"publish","type":"page","link":"https:\/\/lab.pharmavitale.net\/wp-cepi\/intranet-pharmag8\/","title":{"rendered":"Intranet PharmaG8"},"content":{"rendered":"\n\n<!doctype html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Contr\u00f4le des remises \u2014 Pharma G8 (groupement)<\/title>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Archivo:wght@500;600;700;800&#038;family=Inter:wght@400;500;600&#038;family=IBM+Plex+Mono:wght@400;500;600&#038;display=swap\" rel=\"stylesheet\">\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/Chart.js\/4.4.1\/chart.umd.min.js\"><\/script>\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/xlsx\/0.18.5\/xlsx.full.min.js\"><\/script>\n<style>\n:root{\n  --ink:#16221d; --ink-soft:#566159; --line:#e0e6e0; --bg:#edf0ea;\n  --surface:#ffffff; --surface-2:#f5f8f3;\n  --brand:#15715a; --brand-deep:#0e4a3a;\n  --alert:#bf3b2b; --alert-bg:#fbeae6; --alert-line:#eec6bf;\n  --ok:#15715a; --ok-bg:#e7f2ec;\n  --amber:#9a6b0d; --verse:#2456c8;\n  --bg-tint:#e3ece4; --hero1:#15241e; --hero2:#0e1c17;\n  --font-d:'Archivo',system-ui,sans-serif;\n  --font-u:'Inter',system-ui,sans-serif;\n  --font-m:'IBM Plex Mono',ui-monospace,monospace;\n  --r:14px; --sh:0 1px 2px rgba(16,40,30,.05),0 8px 24px -16px rgba(16,40,30,.18);\n}\n*{box-sizing:border-box}\nhtml,body{margin:0}\nbody{font-family:var(--font-u);color:var(--ink);background:\n  radial-gradient(1200px 500px at 80% -10%,var(--bg-tint) 0,transparent 60%),var(--bg);\n  -webkit-font-smoothing:antialiased;line-height:1.45}\n.wrap{max-width:1120px;margin:0 auto;padding:28px 20px 80px}\nh1,h2,h3{font-family:var(--font-d);margin:0}\n.mono{font-family:var(--font-m);font-variant-numeric:tabular-nums}\nbutton{font-family:var(--font-u);cursor:pointer}\nbutton:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid var(--brand);outline-offset:2px}\n\n\/* header *\/\nheader.top{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;flex-wrap:wrap;margin-bottom:22px}\n.brandmark{display:flex;align-items:center;gap:13px}\n.rx{width:42px;height:42px;border-radius:11px;background:var(--brand-deep);color:#dff3ea;\n  display:grid;place-items:center;font-family:var(--font-d);font-weight:800;font-size:20px;letter-spacing:-1px;box-shadow:var(--sh)}\n.brandmark .t1{font-family:var(--font-d);font-weight:800;font-size:21px;letter-spacing:-.4px;line-height:1.05}\n.brandmark .t2{font-size:12.5px;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.14em;font-weight:600}\n.toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center}\n.btn{border:1px solid var(--line);background:var(--surface);color:var(--ink);padding:8px 13px;border-radius:10px;font-size:13.5px;font-weight:600;display:inline-flex;align-items:center;gap:7px;transition:.15s}\n.btn:hover{border-color:#c6d0c6;background:var(--surface-2)}\n.btn.primary{background:var(--brand-deep);border-color:var(--brand-deep);color:#eafaf3}\n.btn.primary:hover{background:#0c3f31}\n.btn.ghost{background:transparent;border-color:transparent;color:var(--ink-soft)}\n.btn.ghost:hover{color:var(--alert);background:var(--alert-bg)}\n\n\/* hero *\/\n.hero{background:linear-gradient(160deg,var(--hero1) 0,var(--hero2) 100%);color:#eaf3ee;border-radius:18px;\n  padding:26px 28px;display:flex;justify-content:space-between;gap:26px;flex-wrap:wrap;align-items:center;\n  box-shadow:0 20px 50px -28px rgba(8,28,20,.7);position:relative;overflow:hidden;margin-bottom:22px}\n.hero::after{content:\"\";position:absolute;inset:0;pointer-events:none;\n  background:repeating-linear-gradient(0deg,rgba(255,255,255,.025) 0,rgba(255,255,255,.025) 1px,transparent 1px,transparent 3px);opacity:.5}\n.hero .lab{font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:#7fa996;font-weight:600;margin-bottom:8px}\n.hero .verdict{font-family:var(--font-d);font-weight:600;font-size:15px;color:#cfe3da;max-width:34ch}\n.hero .amount{font-family:var(--font-m);font-weight:600;font-size:clamp(38px,7vw,62px);letter-spacing:-1.5px;line-height:1;font-variant-numeric:tabular-nums}\n.hero .amount.owed{color:#ff8a72}\n.hero .amount.ok{color:#7ff0bf}\n.hero .right{text-align:right;position:relative;z-index:1}\n.hero .sub{font-family:var(--font-m);font-size:13px;color:#88b09e;margin-top:9px}\n.hero .left{position:relative;z-index:1}\n\n\/* kpis *\/\n.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:24px}\n.kpi{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:15px 16px;box-shadow:var(--sh)}\n.kpi .k{font-size:11.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-soft);font-weight:600;display:flex;align-items:center;gap:6px}\n.kpi .v{font-family:var(--font-m);font-weight:600;font-size:23px;letter-spacing:-.5px;margin-top:7px}\n.kpi .v small{font-size:14px;color:var(--ink-soft);font-weight:400}\n.dot{width:7px;height:7px;border-radius:50%;display:inline-block}\n\n\/* sections *\/\n.grid2{display:grid;grid-template-columns:1.25fr 1fr;gap:16px;margin-bottom:16px}\n.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh)}\n.card .hd{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:15px 18px;border-bottom:1px solid var(--line)}\n.card .hd h2{font-size:15px;font-weight:700;letter-spacing:-.2px}\n.card .hd .eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink-soft);font-weight:600}\n.card .bd{padding:16px 18px}\n.chartbox{position:relative;height:260px}\n.muted{color:var(--ink-soft)}\n\n\/* toggle *\/\n.switch{display:inline-flex;align-items:center;gap:9px;font-size:13px;font-weight:600;color:var(--ink);user-select:none;cursor:pointer}\n.switch input{display:none}\n.track{width:38px;height:22px;border-radius:11px;background:#cdd6cd;position:relative;transition:.18s}\n.track::after{content:\"\";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:.18s}\n.switch input:checked + .track{background:var(--brand)}\n.switch input:checked + .track::after{transform:translateX(16px)}\n\n\/* tables *\/\ntable{width:100%;border-collapse:collapse}\nth,td{text-align:right;padding:10px 12px;font-size:13.5px;border-bottom:1px solid var(--line)}\nth:first-child,td:first-child{text-align:left}\nthead th{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);font-weight:600;background:var(--surface-2)}\ntbody tr:hover{background:var(--surface-2)}\ntd.num,th.num{font-family:var(--font-m);font-variant-numeric:tabular-nums}\n.period-row{cursor:pointer}\n.chev{display:inline-block;transition:.15s;color:var(--ink-soft);margin-right:6px}\n.chev.open{transform:rotate(90deg)}\n.detail td{background:var(--surface-2);padding:0}\n.detail-inner{padding:14px 18px}\n.subt{width:100%;border-collapse:collapse;margin-top:4px}\n.subt th,.subt td{border-bottom:1px dashed var(--line);padding:7px 10px;font-size:12.5px}\n.tag{display:inline-block;font-size:11px;font-weight:600;padding:2px 8px;border-radius:20px;letter-spacing:.02em}\n.tag.owed{background:var(--alert-bg);color:var(--alert)}\n.tag.ok{background:var(--ok-bg);color:var(--ok)}\n.tag.warn{background:#fbf2dd;color:var(--amber)}\n.iconbtn{border:none;background:transparent;color:var(--ink-soft);padding:4px 7px;border-radius:7px;font-size:13px}\n.iconbtn:hover{background:var(--alert-bg);color:var(--alert)}\n\n\/* empty *\/\n.empty{text-align:center;padding:48px 24px}\n.empty h2{font-size:19px;margin-bottom:8px}\n.empty p{color:var(--ink-soft);max-width:46ch;margin:0 auto 18px}\n\n\/* modal *\/\n.modal-bg{position:fixed;inset:0;background:rgba(14,28,22,.42);backdrop-filter:blur(2px);display:none;align-items:flex-start;justify-content:center;padding:40px 16px;z-index:50;overflow:auto}\n.modal-bg.show{display:flex}\n.modal{background:var(--surface);border-radius:18px;width:100%;max-width:600px;box-shadow:0 30px 80px -20px rgba(8,28,20,.5);overflow:hidden}\n.modal .mhd{padding:18px 22px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}\n.modal .mhd h2{font-size:17px}\n.modal .mbd{padding:20px 22px}\n.modal .mft{padding:16px 22px;border-top:1px solid var(--line);display:flex;justify-content:space-between;gap:10px;align-items:center;background:var(--surface-2)}\nlabel.fld{display:block;margin-bottom:14px}\nlabel.fld .lt{font-size:12.5px;font-weight:600;color:var(--ink-soft);margin-bottom:6px;display:block}\ntextarea,input[type=text],input[type=month],input[type=number],select{width:100%;border:1px solid var(--line);border-radius:10px;padding:10px 12px;font-size:13.5px;font-family:var(--font-u);background:#fff;color:var(--ink)}\ntextarea{min-height:96px;font-family:var(--font-m);font-size:12.5px;resize:vertical;white-space:pre;overflow:auto}\n.row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n.preview{border:1px solid var(--line);border-radius:11px;background:var(--surface-2);padding:13px 15px;margin-top:4px;display:none}\n.preview.show{display:block}\n.preview .pgrid{display:grid;grid-template-columns:1fr 1fr;gap:6px 18px;margin-top:8px}\n.preview .pgrid div{display:flex;justify-content:space-between;font-size:12.5px}\n.preview .pgrid .mono{font-weight:600}\n.perr{color:var(--alert);font-size:12.5px;margin-top:8px;display:none}\n.hint{font-size:12px;color:var(--ink-soft);margin-top:-8px;margin-bottom:14px}\n.dropzone{border:1.5px dashed #b9c8bd;border-radius:13px;background:var(--surface-2);padding:22px 16px;text-align:center;transition:.15s;cursor:pointer}\n.dropzone:hover{border-color:var(--brand);background:#eef5f0}\n.dropzone.drag{border-color:var(--brand);background:#e3f1ea;transform:scale(1.005)}\n.dropzone.loaded{border-style:solid;border-color:var(--brand);background:var(--ok-bg)}\n.dz-ic{font-size:22px;color:var(--brand);line-height:1}\n.dz-main{font-weight:600;font-size:14px;margin-top:6px}\n.dz-sub{font-size:12px;color:var(--ink-soft);margin-top:3px}\n.linklike{border:none;background:none;color:var(--brand);font-weight:600;text-decoration:underline;cursor:pointer;font-size:12px;padding:0}\n.orsep{display:flex;align-items:center;gap:12px;color:var(--ink-soft);font-size:11.5px;text-transform:uppercase;letter-spacing:.1em;margin:16px 0 14px}\n.orsep::before,.orsep::after{content:\"\";flex:1;height:1px;background:var(--line)}\n.steplabel{display:flex;align-items:center;gap:9px;font-family:var(--font-d);font-weight:700;font-size:14.5px;color:var(--ink);margin:0 0 12px}\n.stepnum{width:23px;height:23px;border-radius:50%;background:var(--brand-deep);color:#eafaf3;display:grid;place-items:center;font-size:13px;font-weight:700;flex:none}\n.pastefold{margin-top:12px;border-top:1px solid var(--line);padding-top:10px}\n.pastefold summary{cursor:pointer;font-size:12.5px;color:var(--brand);font-weight:600;list-style:none}\n.pastefold summary::-webkit-details-marker{display:none}\n.pastefold summary::before{content:\"\uff0b \";font-weight:700}\n.pastefold[open] summary::before{content:\"\uff0d \"}\n.monthpick{background:var(--ok-bg);border:1px solid #bfe0d0;border-radius:12px;padding:14px 16px;margin-bottom:14px}\n.monthpick input[type=month]{font-size:16px;font-weight:600;padding:11px 13px;max-width:230px;background:#fff}\n.monthhelp{font-size:12px;color:var(--ink-soft);margin-top:8px}\n#step2{animation:fadeIn .25s ease}\n@keyframes fadeIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}\n.toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--ink);color:#eafaf3;padding:11px 18px;border-radius:11px;font-size:13.5px;font-weight:600;opacity:0;pointer-events:none;transition:.25s;z-index:80;box-shadow:var(--sh)}\n.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}\n\n.note{font-size:12.5px;color:var(--ink-soft);background:var(--surface-2);border:1px solid var(--line);border-left:3px solid var(--brand);border-radius:8px;padding:10px 13px;margin-top:14px}\n.tabs{display:inline-flex;gap:4px;background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:4px;box-shadow:var(--sh);margin-bottom:22px}\n.tab{border:none;background:transparent;color:var(--ink-soft);font-family:var(--font-d);font-weight:700;font-size:14px;padding:9px 18px;border-radius:9px;transition:.15s;letter-spacing:-.2px}\n.tab:hover{color:var(--ink)}\n.tab.active{background:var(--brand-deep);color:#eafaf3}\n.tab .cnt{font-family:var(--font-m);font-weight:500;font-size:11px;opacity:.7;margin-left:6px}\n[hidden]{display:none!important}\n.offlead{font-size:13.5px;color:var(--ink-soft);margin:0 0 16px;max-width:70ch}\n.share{font-family:var(--font-m);font-size:12px;color:var(--ink-soft)}\n.addrow{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;margin-top:6px}\n.addrow .fld{margin-bottom:0}\n.addrow .grow{flex:1;min-width:140px}\n\n@media(max-width:860px){.kpis{grid-template-columns:1fr 1fr}.grid2{grid-template-columns:1fr}}\n@media(max-width:560px){.kpis{grid-template-columns:1fr 1fr}.hero .amount{font-size:42px}.row2{grid-template-columns:1fr}}\n@media(prefers-reduced-motion:reduce){*{transition:none!important}}\n<\/style>\n<\/head>\n<body>\n<div class=\"wrap\">\n\n  <header class=\"top\">\n    <div class=\"brandmark\">\n      <div class=\"rx\" id=\"brandIcon\">Rx<\/div>\n      <div>\n        <div class=\"t1\">Contr\u00f4le des remises compl\u00e9mentaires<\/div>\n        <div class=\"t2\">Arrow G\u00e9n\u00e9riques \u00b7 v\u00e9rification des reversements<\/div>\n      <\/div>\n    <\/div>\n    <div class=\"toolbar\">\n      <label class=\"switch\" title=\"Inclure la prestation g8 (16,5 %) dans le total d\u00fb\">\n        <input type=\"checkbox\" id=\"g8toggle\" checked>\n        <span class=\"track\"><\/span>\n        <span>Prestation g8 (16,5 %)<\/span>\n      <\/label>\n      <label class=\"switch\" id=\"commSwitch\" style=\"display:none\" title=\"Commission groupement 3 % sur le brut hors 0 % et 2,5 %\">\n        <input type=\"checkbox\" id=\"commtoggle\" checked>\n        <span class=\"track\"><\/span>\n        <span>Commission groupement (3 %)<\/span>\n      <\/label>\n      <button class=\"btn primary\" id=\"btnImportPharma\" style=\"display:none\">Importer une pharmacie<\/button>\n      <button class=\"btn primary\" id=\"btnExportGrp\" style=\"display:none\">Exporter pour le groupement<\/button>\n      <button class=\"btn\" id=\"btnBackup\" title=\"T\u00e9l\u00e9charge un fichier de sauvegarde que vous conservez\">Sauvegarder<\/button>\n      <button class=\"btn\" id=\"btnRestore\" title=\"Recharge une sauvegarde .json\">Restaurer<\/button>\n      <button class=\"btn\" id=\"btnExport\">Exporter CSV<\/button>\n      <button class=\"btn ghost\" id=\"btnReset\">Tout effacer<\/button>\n      <input type=\"file\" id=\"restoreInput\" accept=\"application\/json,.json\" style=\"display:none\">\n      <input type=\"file\" id=\"pharmaInput\" accept=\"application\/json,.json\" style=\"display:none\">\n    <\/div>\n  <\/header>\n  <div style=\"display:flex;justify-content:flex-end;margin:-12px 0 16px\">\n    <span id=\"storageBadge\" class=\"mono\" style=\"font-size:11.5px;color:var(--ink-soft)\">Sauvegarde auto : \u2014<\/span>\n  <\/div>\n\n  <div class=\"tabs\" role=\"tablist\">\n    <button class=\"tab active\" id=\"tabDashboard\" role=\"tab\">Tableau de bord<\/button>\n    <button class=\"tab\" id=\"tabOfficines\" role=\"tab\">Par officine<span class=\"cnt\" id=\"offCount\"><\/span><\/button>\n  <\/div>\n\n  <div id=\"view-dashboard\">\n  <!-- HERO -->\n  <section class=\"hero\" id=\"hero\">\n    <div class=\"left\">\n      <div class=\"lab\" id=\"heroLab\">Solde de contr\u00f4le \u00b7 cumul\u00e9<\/div>\n      <div class=\"amount\" id=\"heroAmount\">\u2014<\/div>\n      <div class=\"verdict\" id=\"heroVerdict\">Ajoutez une p\u00e9riode pour d\u00e9marrer le contr\u00f4le.<\/div>\n    <\/div>\n    <div class=\"right\">\n      <div class=\"lab\">D\u00fb \/ Vers\u00e9<\/div>\n      <div class=\"sub\" id=\"heroSub\">\u2014<\/div>\n    <\/div>\n  <\/section>\n\n  <!-- KPIS -->\n  <div class=\"kpis\">\n    <div class=\"kpi\"><div class=\"k\"><span class=\"dot\" style=\"background:var(--brand)\"><\/span>Total d\u00fb (cumul\u00e9)<\/div><div class=\"v\" id=\"kpiDu\">\u2014<\/div><\/div>\n    <div class=\"kpi\"><div class=\"k\"><span class=\"dot\" style=\"background:var(--verse)\"><\/span>Total vers\u00e9 (cumul\u00e9)<\/div><div class=\"v\" id=\"kpiVerse\">\u2014<\/div><\/div>\n    <div class=\"kpi\"><div class=\"k\"><span class=\"dot\" style=\"background:var(--amber)\"><\/span>Rattrapage 40 % (cumul\u00e9)<\/div><div class=\"v\" id=\"kpi40\">\u2014<\/div><\/div>\n    <div class=\"kpi\"><div class=\"k\"><span class=\"dot\" style=\"background:#6b7d72\"><\/span>Remise g8 (cumul\u00e9e)<\/div><div class=\"v\" id=\"kpiG8\">\u2014<\/div><\/div>\n  <\/div>\n\n  <div class=\"card\" id=\"commCard\" style=\"display:none;margin-bottom:24px\">\n    <div class=\"hd\">\n      <h2>Commission groupement <span class=\"muted\" style=\"font-weight:500;font-size:13px\">\u00b7 3 % du brut hors 0 % et 2,5 %<\/span><\/h2>\n      <span class=\"eyebrow\" id=\"commBase\">\u2014<\/span>\n    <\/div>\n    <div class=\"bd\">\n      <div style=\"display:grid;grid-template-columns:repeat(3,1fr);gap:12px\">\n        <div style=\"background:var(--surface-2);border-radius:11px;padding:13px 15px\"><div class=\"k\" style=\"font-size:11.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-soft);font-weight:600\">D\u00fb<\/div><div class=\"mono\" id=\"commDu\" style=\"font-size:22px;font-weight:600;margin-top:6px\">\u2014<\/div><\/div>\n        <div style=\"background:var(--surface-2);border-radius:11px;padding:13px 15px\"><div class=\"k\" style=\"font-size:11.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-soft);font-weight:600\">Vers\u00e9<\/div><div class=\"mono\" id=\"commVerse\" style=\"font-size:22px;font-weight:600;margin-top:6px;color:var(--verse)\">\u2014<\/div><\/div>\n        <div style=\"background:var(--surface-2);border-radius:11px;padding:13px 15px\"><div class=\"k\" style=\"font-size:11.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink-soft);font-weight:600\">\u00c0 r\u00e9cup\u00e9rer<\/div><div class=\"mono\" id=\"commReste\" style=\"font-size:22px;font-weight:600;margin-top:6px\">\u2014<\/div><\/div>\n      <\/div>\n      <div class=\"note\">Vers\u00e9e en plus par le laboratoire au groupement. Pour la pointer, saisissez un versement en choisissant l&rsquo;officine \u00ab Groupement \u2014 commission 3 % \u00bb.<\/div>\n    <\/div>\n  <\/div>\n\n  <!-- CHARTS -->\n  <div class=\"grid2\">\n    <div class=\"card\">\n      <div class=\"hd\"><h2>Suivi mensuel<\/h2><span class=\"eyebrow\">D\u00fb \u00b7 Vers\u00e9 \u00b7 Solde cumul\u00e9<\/span><\/div>\n      <div class=\"bd\"><div class=\"chartbox\"><canvas id=\"chartMonthly\"><\/canvas><\/div><\/div>\n    <\/div>\n    <div class=\"card\">\n      <div class=\"hd\"><h2>O\u00f9 se joue le rattrapage<\/h2><span class=\"eyebrow\">Cumul\u00e9 par palier<\/span><\/div>\n      <div class=\"bd\"><div class=\"chartbox\"><canvas id=\"chartTier\"><\/canvas><\/div><\/div>\n    <\/div>\n  <\/div>\n\n  <!-- PERIODS -->\n  <div class=\"card\" style=\"margin-bottom:16px\">\n    <div class=\"hd\">\n      <h2>P\u00e9riodes<\/h2>\n      <button class=\"btn primary\" id=\"btnAddPeriod\">+ Ajouter une p\u00e9riode<\/button>\n    <\/div>\n    <div class=\"bd\" id=\"periodsBody\" style=\"padding:0\"><\/div>\n  <\/div>\n\n  <!-- PAYMENTS -->\n  <div class=\"card\">\n    <div class=\"hd\">\n      <h2>Versements Arrow <span class=\"muted\" style=\"font-weight:500;font-size:13px\">(HT)<\/span><\/h2>\n      <span class=\"eyebrow\">Saisie manuelle<\/span>\n    <\/div>\n    <div class=\"bd\">\n      <div id=\"paymentsTable\"><\/div>\n      <div class=\"addrow\">\n        <label class=\"fld\"><span class=\"lt\">Mois<\/span><input type=\"month\" id=\"payMonth\"><\/label>\n        <label class=\"fld grow\"><span class=\"lt\">Libell\u00e9<\/span><input type=\"text\" id=\"payLabel\" placeholder=\"Avoir, prestation, pharminfo\u2026\"><\/label>\n        <label class=\"fld\"><span class=\"lt\">Officine<\/span><select id=\"payOfficine\"><\/select><\/label>\n        <label class=\"fld\"><span class=\"lt\">Montant \u20ac HT<\/span><input type=\"number\" id=\"payAmount\" step=\"0.01\" placeholder=\"0,00\"><\/label>\n        <button class=\"btn primary\" id=\"btnAddPay\">Ajouter<\/button>\n      <\/div>\n      <div class=\"note\">Chaque versement d&rsquo;Arrow se saisit ici au fil de l&rsquo;eau, <strong>rattach\u00e9 \u00e0 son officine<\/strong>. Le \u00ab \u00c0 r\u00e9cup\u00e9rer \u00bb de l&rsquo;onglet \u00ab Par officine \u00bb se met \u00e0 jour automatiquement. Tant qu&rsquo;aucun versement n&rsquo;est saisi, le solde refl\u00e8te l&rsquo;int\u00e9gralit\u00e9 du d\u00fb.<\/div>\n    <\/div>\n  <\/div>\n  <\/div><!-- \/view-dashboard -->\n\n  <div id=\"view-officines\" hidden>\n    <p class=\"offlead\">D\u00e9tail du d\u00fb par officine, <strong>cumul\u00e9 sur toutes les p\u00e9riodes<\/strong> enregistr\u00e9es. Le tableau de bord reste la vue d&rsquo;ensemble (toutes officines confondues) ; cette vue isole la contribution de chaque pharmacie. D\u00e9pliez une ligne pour voir son d\u00e9tail mois par mois.<\/p>\n    <div class=\"grid2\">\n      <div class=\"card\">\n        <div class=\"hd\"><h2>Total d\u00fb par officine<\/h2><span class=\"eyebrow\">Cumul\u00e9<\/span><\/div>\n        <div class=\"bd\"><div class=\"chartbox\" style=\"height:300px\"><canvas id=\"chartOfficines\"><\/canvas><\/div><\/div>\n      <\/div>\n      <div class=\"card\">\n        <div class=\"hd\"><h2>R\u00e9partition<\/h2><span class=\"eyebrow\">Part du d\u00fb total<\/span><\/div>\n        <div class=\"bd\"><div id=\"offShare\"><\/div><\/div>\n      <\/div>\n    <\/div>\n    <div class=\"card\" style=\"margin-top:16px\">\n      <div class=\"hd\"><h2>Officines<\/h2><span class=\"eyebrow\" id=\"offSub\"><\/span><\/div>\n      <div class=\"bd\" id=\"officinesBody\" style=\"padding:0\"><\/div>\n    <\/div>\n  <\/div>\n\n<\/div>\n\n<!-- ADD PERIOD MODAL -->\n<div class=\"modal-bg\" id=\"modalPeriod\">\n  <div class=\"modal\">\n    <div class=\"mhd\"><h2>Ajouter une p\u00e9riode<\/h2><button class=\"btn ghost\" id=\"closePeriod\">Fermer<\/button><\/div>\n    <div class=\"mbd\">\n      <div class=\"steplabel\"><span class=\"stepnum\" id=\"snum1\">1<\/span>Importer l&rsquo;extraction ospharm<\/div>\n      <div class=\"dropzone\" id=\"dropzone\">\n        <div class=\"dz-ic\">\u2b07\ufe0e<\/div>\n        <div class=\"dz-main\">D\u00e9posez le fichier Excel ospharm ici<\/div>\n        <div class=\"dz-sub\">ou <button type=\"button\" class=\"linklike\" id=\"dzBrowse\">parcourir<\/button> \u00b7 formats .xlsx \/ .xls \/ .csv<\/div>\n        <input type=\"file\" id=\"excelInput\" accept=\".xlsx,.xls,.csv,application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application\/vnd.ms-excel,text\/csv\" style=\"display:none\">\n      <\/div>\n      <details class=\"pastefold\">\n        <summary>ou coller les donn\u00e9es manuellement<\/summary>\n        <label class=\"fld\" style=\"margin-top:10px\">\n          <textarea id=\"pasteArea\" placeholder=\"Collez ici la ligne d'extraction.&#10;Le CIP \/ nom de pharmacie sont ignor\u00e9s automatiquement, et plusieurs officines sont additionn\u00e9es.\"><\/textarea>\n        <\/label>\n        <div class=\"hint\">Les 22 valeurs Catalogue\/Factur\u00e9, de \u00ab Global \u00bb \u00e0 \u00ab 40 % \u00bb. D\u00e9cimales avec point ou virgule, peu importe.<\/div>\n      <\/details>\n      <div class=\"perr\" id=\"pErr\"><\/div>\n\n      <div id=\"step2\" hidden>\n        <div class=\"steplabel\" style=\"margin-top:20px\"><span class=\"stepnum\">2<\/span>De quel mois proviennent ces donn\u00e9es ?<\/div>\n        <div class=\"monthpick\">\n          <input type=\"month\" id=\"periodMonth\">\n          <div class=\"monthhelp\">S\u00e9lectionnez le mois sur lequel porte cette extraction. <span id=\"monthGuess\" class=\"muted\"><\/span><\/div>\n        <\/div>\n        <label class=\"fld\"><span class=\"lt\">Libell\u00e9 affich\u00e9 (optionnel)<\/span><input type=\"text\" id=\"periodLabel\" placeholder=\"ex. Janvier 2025\"><\/label>\n        <div class=\"preview show\" id=\"pPreview\">\n          <strong style=\"font-size:12.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft)\">Aper\u00e7u du calcul<\/strong>\n          <div class=\"pgrid\">\n            <div><span>CA catalogue<\/span><span class=\"mono\" id=\"pvCat\">\u2014<\/span><\/div>\n            <div><span>CA factur\u00e9<\/span><span class=\"mono\" id=\"pvFac\">\u2014<\/span><\/div>\n            <div><span>Rattrapage 40 %<\/span><span class=\"mono\" id=\"pv40\">\u2014<\/span><\/div>\n            <div><span>Remise g8 (16,5 %)<\/span><span class=\"mono\" id=\"pvG8\">\u2014<\/span><\/div>\n            <div style=\"grid-column:1\/-1;border-top:1px solid var(--line);padding-top:6px;margin-top:2px\"><span style=\"font-weight:600\">Total d\u00fb<\/span><span class=\"mono\" id=\"pvDu\" style=\"font-weight:700\">\u2014<\/span><\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n    <div class=\"mft\">\n      <span class=\"muted\" style=\"font-size:12.5px\" id=\"pMeta\"><\/span>\n      <button class=\"btn primary\" id=\"confirmPeriod\" disabled>Ajouter ce mois<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<div class=\"toast\" id=\"toast\"><\/div>\n\n<script>\n\"use strict\";\n\/* ====== MODE : 'groupement' (vue multi-officines) ou 'perso' (une pharmacie) ====== *\/\nconst APP_MODE = 'groupement';\nconst IS_GRP = APP_MODE === 'groupement';\nconst GRP_KEY = '__groupement__';   \/\/ cible de versement pour la commission groupement\nconst COMM_RATE = 0.03;             \/\/ 3 % du brut catalogue hors 0 % et 2,5 %\nconst PHARMA_G8_LOGO = \"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAA2G0lEQVR4nO2dd5wcV5Xvv+dWdZqkGWmUc7Bl2XK2nHACJ7CXaJIBkwzL57GwwGOBXS8s6RF3WZb04IFZFhsewez6mQVjHHGUA7ZlHGVJVrCyNBqNJnSocN4f91ZPjzQzmtAzPbbn9\/m01NNdfet21a9PuuecK6rKJCZRK5haT2ASL21MEnASNcUkAQ\/BWJokOsbjv\/Dg13oCEw\/yAh37hYkJJgEVJSLWkFhDVCOUeFzPXow6xk5ExSUIu8Zs+EOhoBFo6B4R6Phdz6FgQhBQUVQjQBA8jPgY8RHxEIx9n2gMz2\/H3tZ9l\/58w\/EUojZN5lWdE4T2vye\/rnr7JZqcdezgiIeAeCC+e3ggpuL92qPmKliJEQyIRzHar3sKa+gobSDUIvX+LFqzx9GcXibg9R5b7TmoIgK7Cn9mZ34z+4vrmVU3DYgBrwpncKp37\/2w514IOiHViCVhldWyxpZk4llpu+9R5cCzEPVAZha0rIQpKwTx3LFS\/TkMAzUloBIheOTDPfrnvV9n7YFf01naQqzu1ghkvHrm5F6mJ7d+nIUNF4mi7nJV76KJG2tv4THAsL+0jll1p5WJOTqoJYMq5HdAKYaOp5TW08QSoBoET04V2fGCA+hT31A2XANdm+zvKOF6Kg2tp6qs+BgseIOU51gjEtaMgFaaeezouVdv3PoO9hU3kTZCymRBBEFQVVRLbOy6mU1dN7Nq+if07JlfF0tcQ3UumiLiEWvAnsJjGInZH2yowrgHITygFHZCDNr2INJ6GlVVwwmZ9\/9F9c7Loe0pSAG+j71OYs+nIey8B91xDyy\/UuW0Hwgm0SrjT8Ka2ICJKt2df1j\/a\/MlHAg2UefX40kaJUbVOiLWNjNkvBxpL8d9u\/6Zu3Z+TAUPrZIxndh5+0sbtKO0AU+gq\/S8e7cKNyRZaSrsgdJ+e8X33Fe98aFX7XauU73lQmh\/CnIZ8Hz7XtkRcXZhKgXpNDz1Y\/S+dypIzZyTGhDQivsg7uaP295JMTpA2tQRazCA0W8dFNWYhlQdD+z5N57tuE5FPOe4jHI27sLvzD9AMerGF+iJ9gAgo9e\/lKVccS+EeWtStj0IUcGp3ypJwThA73sndO+GTAbiYABSVXjGdRl49hfo2u+qNRPG3zEZdwLGGiEIT7b\/WHfmnyLj1RM7L3FwKKqKL4b793yWSAtI4tGNAgnJtnXfaUcSCOJO+141VVLQCZFaldi5CTqetBMfreTREMSgW36jbL\/fSrY4GNpn4whSBh7\/EpT2a1V\/EEPEuBPQiPVmn+n4Gb7IsFSpEpEyafYWnmZr910KMso4oSJ4RFpiR341niNjEHe796soAcNu6wyYFAQxuuuuvu+PGO4WbviJ82iHM14Mngddu9Btv3PTGV8pOK4EtGQRuoKt2l5ci2fMsAkkYogUtvfcY8ccRTZPcu69hb9oe3EdvsmCQqRDlCDDOVdcdE+sycWOm907o7kFam2\/UofS\/hfwdAQSVUAFdt\/d+\/c4YpwloCVLd7iDUtyJwWf4EsCGYbqCbcDo1GRC3i1dt1CKI0w55jcGaqj8Q1Ebe9h7PxR2K6MxI8oOzk4I2kd4NxVEoWeb+\/tFTcBejPoWy+hJYlwMblPXH\/Ck1yM2MgbRqSTep7FVwz370V1\/onqrErUNKI8U40xAe4Hq\/dmkTSMxIcO\/aIICDf5cYOTLZYk5sL+0XnflH8Y3KRsCAnzJlY8aPVzY3M9VfFWxQz9\/A6MiTuKl52ZCagrEI5mvU8F1c93f4xuOGVcC2uCx0uDPk5b0kURxPGwVqhrjCcyue5kdc4ShksT52dj5e\/JRD56krDACUqbRHlNNVew3uqvtJJ4P7LwVSu1aXikZNlz8LtUstBwHsbi13uHApYhNP2sE5x89ahOGEcNRzW8nVLsKMVQIHqGWaM0sZ179uW5ZbmRfwbiQw\/oDv7HqVxURQRWyXos9qCrlCu4Hkm4Gz\/Q6CV4KunbD9puxJBipGnbjLXm3k4DD+UEaG4ppnIHM+ys33SouDQ5tBuMLcTf+mJYrZWbuKIpR9xBtLkFECOKY02Z8Dl9yTooNXwIm6ndv4QndkX+QlEmXM2IUqE\/Nds+rSMBMK\/i5Q7xU3fR\/3TEjvBUuqUAWvkmYfSqUStbGHAqMZ9emV14F6RYpr5SMI8afgAiKkjaNXDT3p6RNA6W4ByMpBvrySVpWV9DDqtYPsXzKW0U1LjsRw0Wiftd2\/JJiVOrzAxCgKbVwROP2i+QrpacK6dbeLAuNwRfYcRt0b7be8IiC0u4EJo2c+VPItUKpaEk4kHmSpGj1FOGINyFH\/a2UExnGGTXxggWDasys3Kny+kW\/p9GfT0\/YTawBgkHEw4hXVs\/FKE8xznP6jI9x3uxv22SEES+TKUY8gribdQd+ScoYYnfjFcUITEktKc909HDBYT8HdXMqbHy1JCl0oxt\/7l4boQOQkLfpKOHCm2HKcsgXIYp6U7OSfECAIIBiCVa8C3nZz6Q8Rg1QszCMiEGJmFt3jrxt6YOcOv2j1PmzKcUFCmGefJinGOYBw8L6V\/CGhTdy7qx\/FUicmZGRI3ZqZlPnH7St8BwpkwGnkmMNSZssU9JL3RyrmCwA0LDEcSy57LFdG37uGpstPZqlMDGgETL1RJFL7ocTr4K6BRCEUHSEKwXW451xOvKKXyFn\/odg0skAo\/mGI4bUui64Msm0EO3T3fmH6ShtJKZEzptBa\/ZYpmZWyMHHjvZ812++SDd23kLay6FufTrUIlPSi7li6RPimzqqlienIYiPPvXPygOfhEy6nCVtVzJC5IIbYN5rRq8Kk8wYsHmBbQ8rneshykNmOtK8ElqOld5jX8IJqUA55R6NyXpTZUHDhf0cpeX8wdEgSYDdmX9IN3fdYZ2PJAgshiiGqZkV+KauytnXTsu1nIB60FfVOi6s\/Q4y7zWMmgzllPsYUk3IrJcLs15+6HE1svkOxoSoCREEEa9cG5IUJcXloiQZNfmAsnZ7rO3bhHF4kPMhxAozs6fZQ6uaH+dI1bwSsk0QVwTgNYKUB9tvh70P2BTsUa+MSK86H6goaQKQD0ZMQC0njvZWro1elSdETIqSjPN+qwElRsTQXlyr6w78Fxkv1ScNTDXGF5hTf6adS7XsP+iVSrnZQtPRNuRXafSLB1GMPv0vVFcdDlSUVAWUE13dY4T3f5izUXfTpOytSpkk4rKYJ2jhtVp77uG2b1CIkrCPhSBEWqIhNZsZ2ZPdq1VWDolUm3mO08AVRNPQSsHNN0D7XzRxKCYkNHIZPYl37R7Jqsww5z1kGzCxiYz4qMbsK63V7nAbqjF1\/kymZo4STzLu2Kg6KrNKqJR+T+\/\/OWkv3Uf6iRjCKGZ27nSyXouMTfWdswNnXYiar3NIyEUMBAH6xBeRs6+r8rmrAXXEc\/e1Z5vS9RwadiOpRmhcBtmZw3ZuhkTAhFCluJM1bd\/VtR2\/oKO0nlKct4OYFE2pxbq08XWc1PoR6v05MqFI6H6xD+z5IsWoh5xfd1AWtk1wWNx4qTs8dtnWVUQyXuvpQuMcpXu7XYlINIZGkPJh03\/BUauV6WfULDh8KFw0QAS2\/lb1me\/ZdLLSAVtk5QGZFph+jspRH4bZ5w+52u6wYZiESLvyD+lN297F7vzT+AZ8STmCCUpEpCWCGJpSs7lw7vdZ0vhaiTUa8WpFtaAaIeKxM\/+A\/vK5s\/DK80m+t6CEpKSBK5Y9QUNq3hhJQMqep973HmXtTyGT6g3HgCVbKYB55yEX3DFBCOhIFOXR+z+grLvWvuwDJqm4U+tYJb7Vig8hq\/5Neu3NgUk46FVOQh87ex7Q32y8kLbC09T59aQkCxiXvmR1vi9Z6vx6CtEubtj8etYd+I0aqV712uig3Lvr74k1LGfkJBAxBHHEvPqXjy35KiAL3gBGOUQNawTpFGz9E7rl+poVClVMyKrTqIj+6fXKM9fampN0qrwGXfasxbOvp3x4\/LvoPW9XNMnQHljIDXil1TG\/ELXrH7ZdTinuIOMl1WuHDqrExBrgSQYjHjdvew\/txbUqMtq6jZEjkX7PdPxf3dj5JzJejoNbfAhWQx815W3uM2M410QizDxPaJoHUciht0DBCDz6CVtHkkiYWsARS9f8vbLpj1CX7Q3nHDIn7SVjXRae\/RX61Ne1TNQBMDAB3erAmrZvsbewccjVa0qEbzLkwy7u3\/N5rHc0\/hdQURChEO3Te3ddhW+8Q+pHBEMQF5mWWcyixlcJMMYmg4vxpRph\/hudyjroFmhsK+f2bUCf+FLtPOJEqnU8qTz9Xcj6w6i2CyDjweNfhp6tg5JwAAIqRnzCuIdnOn7mFuyHfhFiDcl4KTZ2\/jcHgi22hnecpaD9ARnu23UV7cUtpEzmkDlY9RtzVPMVpExDOcQ0HpCl77Hqqr8ftcaQ9uGJb0D7GkX8GpDQJWhs+A+7njys2hW11XY9nbDpF33GOxj9EjC5UW3Fp7WjtBFf\/GESSBHxyUdd7OhZbV8ZRymoGmHEZ3PXH\/WxfT8k62X7kd429lfvN7Gy5Ur7ynhkhCTSoOU4Yc4rIOivP4yV3kQl9P4PVJB0HDVJspKy+25rEgz33GpNCd11h3uh\/2vbPwHduTqDjYQ6sg5RtrcLHAieS0Yd9hgjgWJjUMWoXW\/b\/sEBSWXEoxSFLJ9yOY2pBZJIzPGBvRZy1MecwO3n2mhkW2jseNCp4vF0SJznG+ahZ6t1mIZtG6v9XPfWigSJQ7\/noFe8XB87Qq2kgA6p60H1oGq92Dt2fJh9xef6Vb3Jqk3Wq+OkaR+jLHHGC4kUnH2RMOt0p+L6+ZFrDBkf1vwvdPfdNVDFw1\/Z6HeM4XrByb2o9+e4OpqRSS8B6vxZI\/rsSBCrTTB4ov2H+kT7z8n5uX4dJyMehajEiuYraMksH5fQy6Fwy1krP9P7d7\/HYFXwve9yBUwjzZweAbycLSVIsriHBVf5l26ht+jq0DEGuOr25WnZY6jzW4kJ+v3wYIg1JGV8ZuZW2emMsX2lWLtvV\/7PeseOj5Lx0gOEVIRIA+r9JlZN\/weGEq0fEyS1HHMvEea+HEqDSEE\/Bfs3oquvxM61Oskfg0yuHDRn6gkQjaRmRew0p61yf\/cvSfuXgG51I+e1ysKGiylFw1vREPEI4oBZuVNozR435hImCZjnwz1649bLXeMir9\/ECCMexSjgpGkfpSm1UGKthfRL4GzBE75mvcYBpAQa2STWDdejj3\/RqeLxMW1k0duwTQCGS3ib7S2L3pqM1O9RA195tf+c0vop0l7GdbUayo2yzSVjlFOnf\/qQlYdqwyaz2lT7P2x9G\/uK60mZXL+t25K43\/TsEZzU+glJkhRqhsSxaF0lLH\/fwFIQHAlT8Mg\/oVt+rUhqbElYtlMvFuadZ9P6h1xtl4JCCIvfDNNOHXRJccCrn8TuWrPHyrmz\/pl8VHRFOwNLQttg3KMr6OHU1o+yuPFSsU7BWAV31a12GG7b\/gHd0HkrWe\/gRIM+X4pII86Z9Q3SpgFUqWoLtpHAVcjJ8V+Cptl2daTfH4Xr4eJ5cPe7Ye\/q8ZGEIshpV0P9jN5quwGvmdj3i0VoWYKs+ja9mTH9Y9Cfv+ChRBw\/9cNy\/uyvE8YlilHeZgOWk0ZdR3sMgebJhz2cNv3DnDPrm07CjN0Njl28777dn9Y1+35M3SFZLr0w4lMI86xsfjtLGl8tyTJd7eE0RGaayKpvQxQz8PpAbGNyUR69\/XVw4Jmx9YzFWLOgcanIBTdC42JbbZdItHKiq98rzfNFmLYSOf+m3vSsQWg2pKKkxIZ7vvsOvX\/359necw+l2H7pJETpG5iRPZ5VrVdx5JQ3S2W2yVgg8Xgf2fsNvX3H35H1+1e7dgaGUEMa\/Nm8bemfyfmtFdV1EwTlTJl3Kc9cA7m0S93vB+LZ0sqmJchFt0H9orHNnEnieIVdqo99Djb+AgodvbdYsRyra4Wl70GO+0dITZE+BVIDYMhVcZX5fbvyD+v2nnvpCragxNT7s5mVO505dWdKorrH8uYm5Hts3\/f01m0fIuNlncPR\/3exuYx53rDw9yxqvGQCSb8KqJt\/0Kn6h1Oh41m7JjxQyEV8CErQvAIuugXJzR0fEgL0PK\/svBPteNImTKSakObjbLZ3bpYccvwgGFZZ5lC604\/tzdWy2l2z77t62\/YPkzFZrCvS\/\/cw4tMT9nDGjE9y1syvSULeCYmEQG0Pqv7hbCB2qxAD3CPj8gdbjkYuuAnq5ktSAjpGE+xNUhjwEFcMP0TNN6K6YFuQdPDCvhyWnKODukxljz\/v\/breufNTpE3GmQADky8f9bCk4QJet\/AmEWGM51gFJDXE669W7nm\/zb8bzNEwvi06b1mOnH8jNCwZYxLipPKhq0tghh2wrnlh+lBgVbqtMbh311W6es9XyB5G8tlYZIEpqUW8Zcl91PszR9VNa1yRkPCRjytr\/tVuuTBYKlSijhvmIS+\/AaaeJMShy1ie2JjwBExsz1gDbt3+AX1s30+o83LEg6wGCIZIQ3zJ8eYldzI9e+LEqlE5LHpVnd79ZmX9dZA9HAk9CANINyPn\/ALmvNJJQlexNkExoQmY2Gs94Q79w\/Pv4Lmu2wcNtYAlX0xMrDGvW3gDixoukZjQ9aN+IcHZfnGA3nGJ8vztkB3EMwZLtigAfDjtW8iRH5TyOLUMuA+CCUlAq1hjDB7be+7Vm7a+m\/bierJDIJ+iBHGRS+f\/nOVT3jaxnY7DIfEkg070tlcpO+4dAgld7C6I4Oi\/QU75pmBSjLldOEJMOAJWetFr2r6jd+36JJEW3G5KQyPfxXN\/xDEt73thky9BmYT7VW9\/LWy\/6\/DqGNeqtxjA7LOQl\/0YGo+U4Xqo44EJQ8CkQZGIR3e4U\/+04yM8vf\/XpL00hsH3E7FqNyKKQy6aezXHtLz3xUG+BAkJwy70zjcqm\/84eKA6QeIh56Yiq\/4NllzhYnQTodzTYkIQsFLqrT9wvd6542O0lza7AvLB+47YZpNFDCleNe8ajpjy5hcX+RIkJIxL6OorlbU\/g2wKmzQ6yD0Uz0rLEDjiCuTkf4HsDOltUlRb27CmBKz0TLuCbXrv7qt4sv0ajAgp038yaSWM+JSiHnJ+K5fO\/yXz689\/cZIvQUXLC13zaWXNl2ybX3OY9WBxMbpiAE3z4KSvIYvfViENa6eWa0LARJ3aVr0Rj7f\/SO\/f8wUOlHaQ9bJ9jukfghGPfNjDzNyxXDr\/V0zNrHhxk6+MXq9Wn7tWuf9\/QNDtNik8jEpOvOQIWPga5MQvQfNKR8TahGzGlYBJQXsi9TZ33az37\/48z3ffR8oYfNNf9Vpf2M2lQ0pRwIrmt3DBnB+Q8ZonRBuQcUXi1e57RPXe98Kex2ztLgyesp9Iu1IA6To46sPI0R+H7PSaEHFcCGi7EUh5FWJn\/kF9aO9XWX\/gelSxHQs0HnBVw03VZTP3kDI5zpr5FU6c9pGqte59QSIhYdiFPvxJ5ZnvW96khigN4xACtWr56I8hR7xf8Bt7x8Yb9tLacDGGBFRijTEV9sXO\/IP6aNu3WHfg1wRxSMZksD1mBs9nM+ITa0AhCphXdzqvmPMdZuZOkd7uqRMnrDDuqMw62fpb1Yc+Bu3PQcZw+FJO18AyKlknpeUIWPERZMk7hVRCRLd3yBg5K1UmoA0g2yrHXnW4petWXbPvu2zs\/D2BhmRMmqHseC6umqoQF8h5Daxq\/QdOaf07MZLmpWHvDRUVWSqldtXHPg9rvwdhaDsswGHUsgAehCVrHzYvhmXvR5a9C3JzelutqUuWraJUrAIB7bqF3TimlxCFqE3XH7ieJ9v\/ne09q4mBtEljxDtsm4\/ERizFeQQ4YsobOXPGF6raLf9Ficr43t4HVR\/7LDx\/k1PLKXpJNAASbRU6R6V+Gix8M7Ls3ba2o\/I8lcePAiMkoJZttkrSKRHbe+7TZzt+yYYDv2V\/aStGIG2yIHIYiScYMagqpbiAAgvqz+HU6f\/IwoaLLPFc\/cdEiuRPPPTN2dMt1ytPfA12P+CI6Hr6DdY2TQx2H7kSBEAKmH4OsuQdMO\/SCqlIRWqWGZGaHiIBe6WciOkjfRRlT2GNbuz8HesP\/D\/2FB4hjCFlBN9kUdVBQipJr2kh0iKlKMIzsKD+fE6c9rcsaXxNWeLZoyel3pCR7KPnip50y3XK09+B3fdavvhgN6mJB5GK0mtHhpH9XH0zzDofFlyGzDrfBrX7nDdp8CTlmOVgGJCASuR2kDSH3PhitF935f\/Mlu6b2dJ1K3uLj1OMQnyBlMmU7bv+vdpe0sUaEMQBsUK938LixktZ2fI+5tWfW7Y7qrE\/yEsaBy276Y5blHU\/hG03QqHH1nL4xiUqDNJQMhkjWVUBqJ8OM86Gua9CZp4HjcsOZVuykjVAaGdIEjCIu2krPqU7e1aztecuduUf5EDpeUK1gXjfpMvNy\/tKu8RDFbcVakykJUKN3baoWWbmVrGs6Y0sa3odjakFvcRz68KTqBIOXvHoXK+6+TrYfB20P2pVrcH2oSqXXlbajBVFZomqTYLaCmSy0LLS7js842xk6knQsFBGIAGtut3UeZPuyK+mrfA4bcUn6AyepxSXEMA3Hp5kyhkovSqywjRQ27430oBI7Va2nkC9P50ZuZNZ1HAxCxsuYmrmaOk9c1JpN0m8MUPZgai4xvseUd12o927uP1RKHT1VroZwJh+JJjr4pCQUYNeNW2ATBM0HmlbezQfj8y7FBoWS\/lzDgcR0D4PtcivnjtTt3Y\/Wj7cE9cmbqDvRd818cT5qE\/NoSW9nJm5k5mVO4MZuZOp92dK5SeTTawnbbxxROI8HBzK6t6s7HkQ3Xsf7HsEOp+Fwh6bX1hJFTno\/z5j26FdmNY+zvrfyJH\/45B6lQFVcKQlDpQ2aUewnv2lDXSWttAT7aIUdRDE3URacucXPJMlZRrJeFOo82bQlFpEU3oRTalFNKbmu43\/KucX9+vQTKIWSKru4v7ttOAAdG22e4J0b4aeLY6QHbYks5yXKOBlINVkO2Ll5kDDUqRxcd89RA7COC3FOcI5kf2SXrmY6Cg7IQM7DtXEIAR0FWfaW3kmFa71wdZA4jiUB3bHSlkGT+KFiURCHlz47zRX5apIv8cNXq45IRJSJ\/HSxaQBNomaYpKAk6gpJgk4iZpikoCTqCkmCTiJmmKSgJOoKSYJOImaYpKAk6gpJgk4iZpikoCTqCkmCTiJmmKSgJOoKSYJOImaYpKAk6gpJgk4iZpiCL0tKvZjUj3otUq4hMM+taCTiagvFrjNU8sbY+jBbya3n97\/7d4xg2PghFSNKDc2HDGSoufKIuVJUk502AIzJXbEMoAZRT+YSNXmRfczxuEzorWEhvtUgx1ouA+iDjTqBC1YkpoUmDrENIHfjPitiD8d8Vqk\/x3Aq9dXZBLVQ6xaLmLz+iFKKYppL5V0V77A3kKRA6WAriCkJ4yIVEkbQ53v0ZRO0ZxJMzObZXouw5R0atCb3G9ZJhpR2v55jbvuRsM9EO5Foy6IewZvRywCJgdeAyY1G0kvQrIrMHXHY+pOxGSWHUTKuNztc5KM44+EdN5BqrInjHi2o1Mf2bOPNXvbeXJ\/J1u6utmZL9AdhGgYQVxRsF5WwU7T+R4536cpnWJuXZZ5uQz\/88RjOHf2dIlV+0jCg2zA5I0YLW1A80+h0T40ispji1cHkiof16cIRWMggnAfcbAb7X4M9AbXYqQOySxVr\/5UTNP5eA1nI+l5vd+8v4LpSVQdiiVeohITA2tdR6fesX03t2zdyYN79rGls9tuCavYIm9jXIG6YFJ+rzElyThCpEoQ2T4y+WKJfN7QXirRmU+zO18on78Sg6tgDYmL6zXuWUPctZq4+wHiwtNoeMB+2AhIzkowraxEdjMr231qm9bExbIEldQ0vIaX4TW\/Fm\/Kq5DU7L7tvyalYlWREK9SvW7s7NIbNm7jvzZu5YE9+ygVCtj6Xg88YyWjWCWV2IUJjJOaYRy7jggx+B6zG+o5ZXoLZ85s5YxZ0zmmpYnWbGbAGzkIAfvfuVuLGzXqWk3ceStR591ocb3VogKYOkfG\/rZWSDomuR3C4xLqippNajpmykX4067Aa7p4kohVxMHEi1S5cct2\/cnajdz8\/E66e\/JWsvk+vpGyPov74YVxhIyiGIIQUNLZDCdPn8oFc2fxijkzOGFaM82Z9CE37GDVm+AwToibTlLveXAbh7iHqPsBjfb\/jujATVZlK4jxwGTdEAP1BKzoK5KQUcCrPx1\/+l\/jT71c+owxqZqHjaiCeF1hyM+e3aQ\/eGo9j+1qs\/c05eN5xsoDHaCXmVgbMYzVki6Oqa\/LcvbsGbx64VwumDeTI6c09mFW7DxoETDIoA1VR1AXXBlaqSBFXCDqukej9t8QddxIXHzeScUcvb2KBzpXhWSMrKNj6laSmvER\/NZ3CuL62AGTsfPDI1Ytx+C6w5Crn35Ov\/P4Wja07QfPK9twA5EOelVsFEWWeKkUZ8xq5S1LF\/DaRXNZ1Fhf0VQKolgt4YYQ+6vEKAvTK9p3VfagC9s02v9bwrZriLruhjhCvDRI+jBEhHIVfdRtiVh\/Euk5\/4TX\/Fr7vSal4YA4WN1e8+wm\/fLDT7B2bzv4Hp7vD6heEyRqMg5CiCJmNjXypqXzeefyxayaPrXMrcSDHm2MsIqdEfonY9R1j4Z7ryZqvx4NDyAmbTtzDomIBqIuFPCb30Bq3lcw2SOl1\/OelIYJKtXt6l1t+g\/3r+HO53dYZyLl251fh0S8AFRZOX0a71+xhMuXLWJ6zjoR6s4zWtJVYoxacyR9insdiLiwVsM9PyBsuxYN2iok4mH2s8COoVE34reQmvNZUjM\/MikNK5CQrysI+fyfn9Bv\/uUZojDES6eHJPEEiFzIZdXs6Xz02OW8ael8SRlTHn+glYzRYhx6wyTBZkuUuLhRw93fJdx7tZWIXhLGGbxzPuLbVZmohNf8KtILvofJLD6k39xLCQmxjAh37dijH7zrIZ7c3QaZNJ7YuNxAEMAzQhhEEEWsmj2dT554NJctmV+mWeiIPZYxiHFsTnQQEQtrNdjxZaK2n6NEiFc\/cH\/iMqzjo2EXkp5FZuEPrG1YsYnfSwWVKvd\/PfKkfvbBvxDHip9O2djcIPBEiOIYSgFLpzXzDycdw7uXL5bKUM1wnYmRogbdsfoSMeq8S4NtnyHqvAsxKTCZw6tl8SEuoBqSnvN5UnP+ScpjvwTswjBWfCPszhf1vX+6n9+v24Jk067x+yBSz618RMUSuWyajx9\/FH93\/Iryem0lqccLNWzP1peIwe7va7jjc8Sl3Yg\/FGloQEDDHvxpbye96EciJseLnYQJ+R7e265vvfke1rftx89liOLBd9rzjBCFMYQhlyyZz9fPOIFjWqbUjHgJat8fsGK1Iy5u0mDrJwj3\/cZ5y6nD2IZOJQddeFPOI7P014g\/XWzr9hefc5KQ74ZN2\/Qdt95HVykYusotlphWn+Orp5\/A+1YsFRgfG+9wqD0BE1Q4E+Hef9fS838HUTt4DUNSyRp2YeqOJ3vE72ySw4vMQ07I96NnntO\/vuN+EIPnmUEdDSNiNwoqlnjlkvl875xTWNLYIJXOS60xcQgIVK52xIWntbTpA0QH7kZSQ1DJCQlzK8geeROSXvCiIWFCvn99fK1+\/K6HMKkUHMbe84wQBRFGhC+fdjyfOnFFWer5E4B4CSYYAR0SaagBpa2f0GDntxAv45b0Bttsz4eoC8keTXb5LUhqjrzQbcKEMF9\/7Bn91N0P4WXSxAiD3TffCGGhxPwpjfzk\/DM4f86MCSX1KjExCQhUbjIR7v2JlrZ8CLRokxwGswsTSdhwMtkjb0W85hcsCRPJ9+0n1ulH\/vQAXjrF4VwzX4SwUOS8BXO49oIzmVefk4km9SoxgQkIvSsqHlHn3Vp87nI02IYczi50JPSmXEj2iN8J+C+4OGFCvmvXbdJ33nwvXsonloElX7JSERWKvPvYI\/nhOaskZcyEU7kHY4KLBZdxoyFe49mSXX4HJncMGnYNvvqhIeI3Eu2\/heLmD+rAOYoTE5Fa8t22fZe+97bVGN8bnHzJclqxxGfOOIGfnHea+MYQT3DywagI6HbXKT\/GEOKDhpjsEZI98jZM45lDIGGApBoId\/+YYNe\/qB3jMMt9EwBJNsuzHZ36lj\/eQ6iAMYOTT5U4CPnWeafyhVOOlWgc7L1kjTlSHTSt63AYpgo+NMmg79shY7u7jo3vadRJccMbNOq4FfEHU8dW7WpcIHvkH\/GaLpjQnnGS9l6MYs74f7foY7va8NKpAUMtZfKFEVeffwZXLl8sieoeKyRk6y9wPZKkhWEQsK8hr8FO1WAnoLYMMz1PBjq2qnAE0riH4vrXadRxy2FIaEBLkJpNbsVDSGqGTNRUroQ8773zQf3JY2vx67IDBplFBKNKFIb85IIzefeRiyWIldQYkS\/5cSTk6gpCtnR1a3cY0ZjyWdhQLznfLa8OY2VliAR0hNKIsO2nGrZd6yrm2u3UvCmY7BF4zW\/Cn\/4+Ea9pjFOl7Hw07qa47hKND9wFg5HQOSX+1MvILP3NhJSCyU275tlN+q6b78HPpAkHcziAKAi5+oIzuXL5YgnimCR9qtqoaHzA6l179ftPrOP2HbvZkS8SRxG+5zGvPseFc2fyNyuP5PhpzVL5mcEwhMJ0q3LjwrNa2vgeos77nAZOVajbEOLAZjDnjiK96Id4DWeP7Y1289KwXYvrLiLq+vPgklB8NOgis+Rq\/NYrJxQJkxT6zZ3desJ1N9FZClBPBqzB9kUIi0X+7bzT+MjKI8dc8iVz\/PjqNfqtx56BMISUD8YkNY8Q2XXmVDrFVSev5HOnrJTK0oCBcBgCWkkT55\/WwrMXoKXtiN9Ib11I8tmk4s2DqBNMjsyy3zqbK7EZxwCJOi5t1cLac9HiJvByAzgbxpLTayR39CNIev6EUcWJ9HvVH+7SmzZswcumieL+74tvhDBf5KrTj+dLq44bc\/IlNcRvvW21XvfkOkxd1oZ7XK1mIulEBCO2NkTzBT5w0jH84OxT5HDqeJCr74aOuyk+dzkk5NOgn5CG84g1cGu3JYrPvQMtPW8rVcbKS3bFTpKeJ5ll14M3xe1f29\/XisGk0aCN0tZP2u82AWKgyQ362brNetOGLfhDIN8VK4\/gS6uOGxeHwxPhC488qdc9uY50fR2q1lZNaoWhl6ihm3eqvo7\/8+hTfP+p9Xq4xNiBCagRIAS7\/7fG3Y9BQr7DQUMwdWiwi2D7FxnzG+3ihCZ3nGQWX+NU8AAWiIaIX0e479dEHTdpb7VebZDMcn8p0E89sAbxvQHXdz0jhMUSZ8yfzdXnnioJOcaKfsn46w906lceeQovlyWI48OGW8p1I+k0\/\/jgX9idL6o3SAxzAAK6GuC4SNj2U8QMIWW+z8dDxEsT7v9PNNhhb\/RYBoFdnNBr\/itJzfsqGuYHtu8UBCHY9o\/WO5ayFTPuSIq1v7rmaba3H8DzPfoTfkaEOIyY1dDAry88k7RzNsYyxpzM4z\/WbqJUKCKuaH1on1WMb2jv7OaXG7YAEA3w4f4J6NgaF55RLTxrq9iGpUYVJIUG+4i6VruXxjpYbSVhatYnxJ\/2pkEC1TF4dUTdjxC2\/UytbTjGc+tvFo58Gzu79Tt\/WYvJpPpVvQIYVVDlmgvOYF59nSQp82MJz6n227fvRowZthKz3TKEm7fuBAb+sQwsAQEtPWfV7oi8RVe4VlzfZ8yxQ1LcHpNe+ANMbinE+f4dII0R8Qh2fg3ibspF8eOIRP1+Zc3T9OQLGGP6nYFnDGGxxGdOPY4L586UMB777OVkbvkoYktXN+qZYVvxCqgx9vPYwHV\/329QF1DjwujMN8WquXGDAVXEnyrphT8GHcj+jMHLEeefJWy7Vq2dOn5SMJF+z3V26bVrNyLp\/qVfYvedvXAunz3pGAlVy5JpPBDGSqmyDdsIEMQ6eFnoYB8Wf6YzkUbIQgHxW0f22ZGinLxwrvizP4lG+f5VscaIGILd34G4MK5SMJEw331iHYV8Ac87VPoJoFFMYybNv593armV2nimFtR5HlMzafujHuaZbSW70pxOlaVffyP0T0CntkzuaPCnAcEAHx8EGoHxMXUn9xlzXOC82\/Tsz4ipPxGinn7MCCcFe54i7PhvJwXH3iNWrArdVyzpNes2Ian+pZ8xQlwK+MoZJ7KsqWFc7L4EggsPGeH4qc1IFA87WioCEsec1NoCMGBoaYBxbexOUrPEazwPjYZrB3oQF\/HqjsPUnSgDhkXGDO5cJkt6wXfo7V\/Yz5EihHt+4P4Y+x9J4g3+asMW2jq6MP6h0i8pIjpr4Vw+ePSywwZzxwLJnN6ydIF9Plz5o6Cex1uXLgCG7YRQVrv+rE+C8enbfPIwEEE1xp\/5KRciGcZnqwUnBb2Gl4k\/\/a9daOYgVawRmAxx5z3EPY9qst49lvCw6uinz25y4a2Dpo1d9E\/7Pt972UljGmoZDJ6rOXn1wjly8tyZhMUS\/hDXmlPGEBWKvHLxPM52bXkH+gENPGJyA+tPlfScz6KlHishBpWExoVfuvCnvxd\/6pslyWiuCZxXnJrzOUxmNsRFDvkhiI\/GJcK2n7sXxs4OtGuj8Je2\/frQrr2Q8g9ZJTBGiIsl\/ub4ozhuWrOENZB+QHmNN2UM\/37OKhoyGcIgGDThQdzxQanEzKYGvn\/Wye5qjmgpDkeciNTsT0t63t+jYTdELmwhvnt4vf9rEQ068VvfQWbhD2wtRk0zcsV5xdPFn\/0Z1wTzoK+sMWIMUcd\/u7CNz1iRMDGDrt+0lbgUHOLRGoE4jJjZ3MinTzjaestjMpOhIZGCx01rlhsuOYfWbIbA9Xr2jeAbwRMpP1cgyBeY31jP7y89j0WN9WJTuAY+xxC+nwfEpOZ+RbLLfoPkTkCjHjTsco\/u8v+SXkh68ffJLLlWbCPzCVCH4SrpUtOvFK\/+eIjy9P3aMZgscWEdUdd9LgA6NiEZz9gbesOm7eAdGtwVETQIuerEY5iaTUusta9iSxIPXjFnptx\/2cW8cflifIUwXyTMF4mKpfLzjAjvWnkE9192MSe3tgzJcRp+QqqGRJ13aNx5N3Gw1UqQ1Cy8htMxjeeLeI30zZKZAHBlnmH7f2px\/Rtd648KW8\/lC6ZmfZT0\/G+OScetJPb3ZHuHHn\/dTYdUt3kixEHIEdOaeeyNF0vaeNaTrOosRo5KR+jJ9g699fmdPLm\/g84gojnlc+zUZi6cP4sjXLvegXpCH4xhXGVnoIuP13SheE0X9n\/YBMqzK0OsE+U3v07CxjM07rofvLpeEmqMiBAduM2t\/KSotudupRnctm0XUbGEn80cknCqccSnTzqarOe5RpAThX696hjgmJYpckzLlH6Pi13McKiSe3gmRpJUoJGVKhr1fY5OPPIlcM5QatanXGZG5c2PwWTQ4rPEhWfcG9W1A5P7ceu2XTZKUPGecdJvxYxW3rJ0wYRQvf3BiCVWknoVuqKk0P2dSL3hTH0ENq70dTwqn1flF6sHPaoEJwW9KZeK17AKokLfH4v4aFQkHoPkiWQttDsIedj1a678ZnbVI+J\/HrectDHEaNVkX\/lKjqJy7WCYxPEQ54S4v0fyo6l9OnAZscvlk4MecT8JsCOExiA+\/owPoYcQzI4fdTsCVlH9JXb2M\/s7dUdXD+JqdsEZ+WHIwmnNXL5sgSRkHS0SKVW+ki53MHYSq\/apuBYTgICKLbc0ZSmlYZtqsEOJe9zriXQdpVRyJoTffJmY3DK7BpxcAhej056\/VN2OTcIva9ra0SDAVMQljABhxF+vWEq975dLG0eDxPtMsqX3FUq6vTuv3UFoX08K2SdARniNmysnhr5H3P2whvt+Stx5DxpsRzVCvCY1uWPwmi\/Dn3a5lLd5GDE5xEpZrx5\/2rsobf0M4ifLdDaHMS5tQsPdarcOq5Ij4oZY07bfJcT2vhxFMY31dbx7+WJgdBKhfDVFeHzffv2PZzZy+\/ZdbOnOE8QxDSlfj2lu4vWL53HFkYul0QXCa9WcEoYVhqk23OXSIqWtn9Rw9\/fROHAtXHzc7UFdOpBXfwLphd\/H1J8+yoo2V9JZ3KT5p44HLWBjnUkNTJHMUXfiNZxVtcq5xDi\/8Pd\/0ls3bcVLp8vtN8J8kbevPIKfvfz0Ua35luszVPnMQ4\/rvzz6FKVSYPd9MyZZ47PVa3HMEdOa+fY5q3jlvFk1WWtOUCMV7EzjOE9h\/es12PFtkJQtqzRuN07xQTKIX4\/4DcQ9ayg88wqijj+OspbDSjzJLBKv6WI0KvWSTDxUY7S4sWKeo\/+mRuxWV1u7e2ybDfderIDn8f7lS0Z1pqQoKIpj3nLrffrl1WsoieDnsnZXJCPWBvQMXjqFn8uyrqOTS357Oz9bv1krQyzjjRoS0FB8\/iMatf8BSTXZ1zSk7xawFSEfrwEIKD73NuLiek3WeUcGew5\/2hUV86mYXZmAVYAben+ppDt6CuU+L0noZeWMqZw1e7rAyJ2PZLH\/qoce1988tZ50Q668k2XSSsN6wtbuC+MYz\/fBM7znttX8ec8+NTUi4fgTUK3DEXXeoeGeH7nupyUOK200BJNDw30EW\/+eUdlmzqnxml4hJrfErgH3aTuydeRjH4TE32wrFOkKEi\/fOR9RxFuWzHfbJozs5ifkW9O2X7\/xqKteiw5fvRap4nmGMAj52\/sedeQbfzVcAwlov2S4+3sMO9FMA8TLEnX8jrjwtE2fGpEUdM6Iqceb8mprZ1ZkRGu41x02+suTEKGtWCKKonKQNoqVVDbNG5fMBxh0wX7Q8d0JvvfkOuIgHFb1WhgrJpNm9fZd3LtrrxoZf894nAmo2HYa+zXuWo2YEbRMcwHjqONmN+RI1bC9417z65GKhFUR0OhAn2OqgQOlAKLYdRCwSQenzGjlqOYmSezE4UKxCQ6lOOb2bbvB9\/st6xwMRkDCiBu37LBjjrMWHn8CAlrahEZ7R7Xgr8Vn3bMRksRJN6\/hNJHsES4m6MaKe0Y3dgWS+9kTRjbWiFuWi2Net2gOMHC6+mHHdmzZ3p3XHT15MIP3ju5\/DFARntlvf3Tj7QyPLwGTixPnXQuNJO1xWIPYf+PuUU7G1YBIFq\/pfLcy0ruBdrVRjKykF7GE8zJpXjXfEnC0hW49YWir10bKHoF8GCVPxxXjS8DkAnlNttn4iAK9dnFJvJaqTcs0vdI9KwdIqjb2IedC0DBixdQpHN0ycvVbiSnpNDnfG7n+VJiSSSVPxxXjrIKdB5hZLJKew8hrhhWTO678fOTTSdTwmZj0TNCie736C0RJKrsRgSjiorkzR+X9JmMpMLsuK4sa6hFnYw4HSdntCdOagRe9DejUnqnDa7zAeZ\/DudkCWkJSzXhNF7mXRvMVXPWfP02k7uRe1Wuy7v3R342EDjnfduqPVMEYXjF3pn1\/lDovim1M8dWL5qBhOGx1HseKl07xmoVzgdGbA8NFzcIw\/oy\/tXsF6zBqjsVHwxL+tPcg6bmSxBRHBef9eo3nln\/9YhqTN0c3dgUaUzbwG0QRzfU5Tp0xDRi9+vVc2OWDK5bR2FhHHMVDHjNlDHGhxGVLF3BMy5RxrT1OMP4EdFsmmNzRkprzBTRM8vIOMxVJoWEnpv5YUnM+gy14qsb0nVnQcCZiUpaEiX1ZBX2U3M6WTArf9yCMOH5aC9OzGVEdvdGfpFjNa6iTb555EnGphBlCQmtSvTa7uZF\/PePEca\/cTlCbpTi3lpua9XeSmvspNOiySQF9klwrkl0RNOjE5I4iu+x6xGtx16oamSpJF4iVSHqu5XV6jnuzGgS0c5yRy9KYSkEU8bJZVvpVK+ibNIG8cvkS+eJZpxCWSsRhWK5Y86T34RubsRzkC8ypr+O3l5zL3PqcaI2ysGuXDyi22i4996uSWXotkl5crq4jsg8Nux05Y\/zp7yF71N1IZqmtNa7a1J0d6DWLyR4NCia9uEpjU\/6NtGTSMj2bAeCMGbZfTjXvd0LCT594tPznpeexvLmpXLEWlQL3sH9rGHPZkYu577KLOaVcvVa9uQwHNc4HtEtp\/rR3iNf8GqL9v9Wo8w60+By2m+k0TP0peFNeg6k7LokSV0n1VsD1sTZ1J8K+m5DsEe6N0d8VqyIhbQxz63Osz6TKHme1f\/1JVssbFs+TV86fzW83bdXbtu7imQOdlKKYlkyak1tbeM3CuZw2c1q5eq2W+YA1JiAk1XbiNeFPe4f4094xwHFJe4+xENr2BkjuWJAYk1nqXq7OjUnyARc01NGYyTC3oU60iuNXIqnjrfM93rpsobx12cIB55QcX0tMoM0KXeoVQu\/GglqWTmNrLVgTXMM2jQ\/cgtfyZqmmlE1KMh\/ft183d\/XwVwvmDHkfjZEiyRGE3poQVS3PpdbESzCBCDiJlyImQFHSREIihccGyeZ+k+jFpAScRE0xKQEnUVNMEnASNcX\/BycahOQuoQ3lAAAAAElFTkSuQmCC\";\n\/* ---------- constants \/ model ---------- *\/\nconst G8_RATE=0.165, TARGET_FACTOR=0.6; \/\/ 40% target => catalogue * 0.6\n\/\/ indices into the 22-value vector (pairs: catalogue, factur\u00e9)\nconst CAT={GLOBAL:0,AD:2,AG:4,T0:6,T25:8,T5:10,T10:12,T20:14,T20b:14}; \/\/ see TIERS for clarity\nconst IDX={GLOBAL_nr:0,GLOBAL_r:1,T0_nr:6,T0_r:7,T2_5_nr:8,T2_5_r:9};\nconst RATTRAP=[ \/\/ tiers brought up to 40%\n  {key:'5 %',  nr:10, r:11},\n  {key:'10 %', nr:12, r:13},\n  {key:'20 %', nr:14, r:15},\n  {key:'30 %', nr:18, r:19}\n];\nconst fmtEUR=n=>new Intl.NumberFormat('fr-FR',{style:'currency',currency:'EUR',maximumFractionDigits:0}).format(n||0);\nconst fmtEUR2=n=>new Intl.NumberFormat('fr-FR',{style:'currency',currency:'EUR',minimumFractionDigits:2,maximumFractionDigits:2}).format(n||0);\nconst monthLabel=m=>{ if(!m)return''; const[y,mo]=m.split('-'); const names=['janv.','f\u00e9vr.','mars','avr.','mai','juin','juil.','ao\u00fbt','sept.','oct.','nov.','d\u00e9c.']; return names[(+mo)-1]+' '+y; };\n\n\/* ---------- state + storage ----------\n   Persistance \u00e0 3 niveaux, dans l'ordre :\n   1. window.storage  -> stockage de l'aper\u00e7u Claude (pr\u00e9sence en ligne)\n   2. localStorage    -> stockage du navigateur quand le fichier est ouvert en local\n   3. m\u00e9moire         -> dernier recours (session uniquement)\n   + sauvegarde\/restauration manuelle dans un fichier .json (la plus fiable).        *\/\nlet state={periods:[],payments:[],settings:{g8:true}};\nlet storageMode='m\u00e9moire';   \/\/ affich\u00e9 \u00e0 l'utilisateur\nlet memCache=null;\nconst KEY='arrow:state';\n\nconst hasClaudeStorage=()=>typeof window!=='undefined'&&window.storage&&typeof window.storage.get==='function';\nfunction localGet(){ try{ return window.localStorage.getItem(KEY); }catch(e){ return null; } }\nfunction localSet(v){ try{ window.localStorage.setItem(KEY,v); return true; }catch(e){ return false; } }\n\nasync function loadState(){\n  let raw=null;\n  if(hasClaudeStorage()){\n    try{ const r=await window.storage.get(KEY); if(r&&r.value){ raw=r.value; storageMode='aper\u00e7u Claude'; } }catch(e){}\n  }\n  if(raw===null){ const l=localGet(); if(l!==null){ raw=l; storageMode='navigateur'; } }\n  if(raw===null) raw=memCache;\n  if(raw){ try{ state=JSON.parse(raw); }catch(e){} }\n  if(!state.periods)state.periods=[];\n  if(!state.payments)state.payments=[];\n  if(!state.settings)state.settings={g8:true};\n}\nasync function saveState(){\n  const v=JSON.stringify(state); memCache=v;\n  let claudeOk=false, localOk=localSet(v);\n  if(hasClaudeStorage()){ try{ await window.storage.set(KEY,v); claudeOk=true; }catch(e){} }\n  storageMode = claudeOk ? 'aper\u00e7u Claude' : (localOk ? 'navigateur' : 'm\u00e9moire (session)');\n  updateStorageBadge();\n}\nfunction updateStorageBadge(){\n  const el=document.getElementById('storageBadge'); if(!el)return;\n  const warn = storageMode.indexOf('m\u00e9moire')===0;\n  el.textContent='Sauvegarde auto : '+storageMode;\n  el.style.color = warn ? 'var(--alert)' : 'var(--ink-soft)';\n  el.title = warn ? 'Le stockage local est indisponible \u2014 exportez une sauvegarde pour ne rien perdre.' : 'Vos donn\u00e9es sont enregistr\u00e9es automatiquement.';\n}\n\n\/* ---------- backup file (.json) ---------- *\/\nfunction exportBackup(){\n  const data=JSON.stringify({app:'controle-remises-arrow',version:1,exportedAt:new Date().toISOString(),state},null,2);\n  const blob=new Blob([data],{type:'application\/json'});\n  const a=document.createElement('a');\n  a.href=URL.createObjectURL(blob);\n  a.download='sauvegarde_remises_arrow_'+new Date().toISOString().slice(0,10)+'.json';\n  a.click();\n  toast('Sauvegarde t\u00e9l\u00e9charg\u00e9e');\n}\nfunction importBackup(file){\n  const reader=new FileReader();\n  reader.onload=async()=>{\n    try{\n      const obj=JSON.parse(reader.result);\n      const s=obj.state||obj;\n      if(!s||!Array.isArray(s.periods)||!Array.isArray(s.payments)) throw 0;\n      if(!confirm('Restaurer cette sauvegarde ? Les donn\u00e9es actuellement saisies seront remplac\u00e9es.'))return;\n      state={periods:s.periods,payments:s.payments,settings:s.settings||{g8:true}};\n      document.getElementById('g8toggle').checked=state.settings.g8!==false;\n      document.getElementById('commtoggle').checked=state.settings.comm!==false;\n      await saveState(); render(); toast('Sauvegarde restaur\u00e9e');\n    }catch(e){ toast('Fichier de sauvegarde invalide'); }\n  };\n  reader.readAsText(file);\n}\n\n\/* ---------- \u00e9change perso <-> groupement ---------- *\/\nfunction pharmacyLabel(){\n  const offs=officineAggregates();\n  if(offs.length===1)return offs[0].name;\n  return 'pharmacie';\n}\nfunction exportForGroupement(){\n  if(state.periods.length===0){ toast('Importez d\u2019abord une p\u00e9riode'); return; }\n  const data=JSON.stringify({app:'controle-remises-arrow',kind:'perso-export',exportedAt:new Date().toISOString(),\n    periods:state.periods,payments:state.payments},null,2);\n  const blob=new Blob([data],{type:'application\/json'});\n  const a=document.createElement('a');\n  const who=pharmacyLabel().replace(\/[^a-zA-Z0-9]+\/g,'_').slice(0,30)||'pharmacie';\n  a.href=URL.createObjectURL(blob);\n  a.download='export_groupement_'+who+'_'+new Date().toISOString().slice(0,10)+'.json';\n  a.click();\n  toast('Export pour le groupement g\u00e9n\u00e9r\u00e9');\n}\nfunction offKey(o){ return (o.cip&&o.cip.trim())||o.name||'\u2014'; }\nfunction mergePharmacyData(obj){\n  const periods=obj.periods||(obj.state&&obj.state.periods)||[];\n  const payments=obj.payments||(obj.state&&obj.state.payments)||[];\n  if(!Array.isArray(periods)) throw 0;\n  const incomingKeys=new Set();\n  let nOff=0;\n  periods.forEach(ip=>{\n    const offs = (ip.officines&&ip.officines.length) ? ip.officines : (ip.vals?[{cip:'',name:ip.label||'Officine',vals:ip.vals}]:[]);\n    let gp=state.periods.find(p=>p.month===ip.month);\n    if(!gp){ gp={id:uid(),month:ip.month,label:ip.label||monthLabel(ip.month),officines:[]}; state.periods.push(gp); }\n    if(!gp.officines||!gp.officines.length) gp.officines=periodOfficines(gp);\n    offs.forEach(o=>{\n      const key=offKey(o); incomingKeys.add(key);\n      const idx=gp.officines.findIndex(x=>offKey(x)===key);\n      const rec={cip:o.cip||'',name:o.name||key,vals:o.vals};\n      if(idx>=0)gp.officines[idx]=rec; else { gp.officines.push(rec); nOff++; }\n    });\n  });\n  \/\/ versements : on remplace les imports pr\u00e9c\u00e9dents de ces officines, puis on ajoute\n  state.payments=state.payments.filter(p=>!(p._imp && incomingKeys.has(p.officine)));\n  const fallbackKey=[...incomingKeys][0]||'';\n  payments.forEach(pm=>{\n    state.payments.push({id:uid(),date:pm.date,label:pm.label,officine:pm.officine||fallbackKey,amount:+pm.amount||0,_imp:true});\n  });\n  return {officines:incomingKeys.size, periods:periods.length};\n}\nfunction importPharmacy(file){\n  const reader=new FileReader();\n  reader.onload=async()=>{\n    try{\n      const obj=JSON.parse(reader.result);\n      const r=mergePharmacyData(obj);\n      await saveState(); render(); switchTab('officines');\n      toast(`Import\u00e9 : ${r.officines} officine(s), ${r.periods} p\u00e9riode(s)`);\n    }catch(e){ toast('Fichier pharmacie invalide'); }\n  };\n  reader.readAsText(file);\n}\n\n\/* ---------- parsing ---------- *\/\nfunction parseNum(tok){\n  if(tok==null)return null;\n  let s=String(tok).replace(\/[\\s\\u00a0\\u202f\\u2009]\/g,'').trim();\n  if(s==='')return null;\n  if(s.indexOf(',')>-1 && s.indexOf('.')>-1){\n    \/\/ last separator is decimal\n    if(s.lastIndexOf(',')>s.lastIndexOf('.')) s=s.replace(\/\\.\/g,'').replace(',', '.');\n    else s=s.replace(\/,\/g,'');\n  } else if(s.indexOf(',')>-1){\n    s=s.replace(',', '.');\n  }\n  const n=parseFloat(s);\n  return isNaN(n)?null:n;\n}\n\/\/ --- read 22 values from a row given the column indices; blank\/non-numeric -> 0 ---\nfunction readVals(cells, valCols){\n  const vals=new Array(22).fill(0);\n  for(let i=0;i<22;i++){\n    const c=cells[valCols[i]];\n    const n=(typeof c==='number')?c:parseNum(c);\n    vals[i]=(n==null||!isFinite(n))?0:n;\n  }\n  return vals;\n}\nfunction isTotalsRow(cells){\n  const lead=String(cells&#038;&#038;cells[0]!=null?cells[0]:'')+' '+String(cells&#038;&#038;cells[1]!=null?cells[1]:'');\n  return \/\\btotal\\b|cumul|g[\u00e9e]n[\u00e9e]ral\/i.test(lead) &#038;&#038; !\/^\\d\/.test(String((cells&#038;&#038;cells[0])||'').trim());\n}\nfunction hasContent(vals){ return vals.some(v=>Math.abs(v)>1e-9); }\nconst ZEROS=()=>new Array(22).fill(0);\nfunction sumVals(list){ const s=ZEROS(); for(const v of list){ for(let i=0;i<22;i++)s[i]+=(v[i]||0); } return s; }\n\n\/\/ locate the value columns from a header row (the 22 \"CA non remis\u00e9\/CA remis\u00e9\" cells)\nfunction layoutFromHeader(rows){\n  for(let h=0;h<rows.length;h++){\n    const cells=rows[h]||[]; const valCols=[];\n    for(let j=0;j<cells.length;j++){ if(\/CA\\s*(non\\s*)?remis\/i.test(String(cells[j]||'')))valCols.push(j); }\n    if(valCols.length>=22){\n      let cipCol=null,nameCol=null;\n      for(let j=0;j<cells.length;j++){ const t=String(cells[j]||''); if(\/^\\s*CIP\\s*$\/i.test(t))cipCol=j; if(\/pharmacie\/i.test(t))nameCol=j; }\n      return {headerRow:h, valCols:valCols.slice(0,22), cipCol, nameCol};\n    }\n  }\n  return null;\n}\n\n\/\/ Excel workbook (Uint8Array) -> {officines:[{cip,name,vals}], rows, sheet} | {error}\nfunction parseWorkbook(buf){\n  if(typeof XLSX==='undefined')return{error:'Le lecteur Excel n\u2019a pas pu se charger (connexion ?). Utilisez le collage.'};\n  let wb;\n  try{ wb=XLSX.read(buf,{type:'array'}); }catch(e){ return{error:'Fichier Excel illisible.'}; }\n  const order=[...wb.SheetNames].sort((a,b)=>(\/d[\u00e9e]tail\/i.test(b)?1:0)-(\/d[\u00e9e]tail\/i.test(a)?1:0));\n  for(const name of order){\n    const rows=XLSX.utils.sheet_to_json(wb.Sheets[name],{header:1,raw:true,blankrows:false});\n    const lay=layoutFromHeader(rows); if(!lay)continue;\n    const officines=[];\n    for(let r=lay.headerRow+1;r<rows.length;r++){\n      const cells=rows[r]||[]; if(isTotalsRow(cells))continue;\n      const vals=readVals(cells,lay.valCols);\n      const nm=(lay.nameCol!=null&#038;&#038;cells[lay.nameCol]!=null)?String(cells[lay.nameCol]).trim():'';\n      const cip=(lay.cipCol!=null&#038;&#038;cells[lay.cipCol]!=null)?String(cells[lay.cipCol]).trim():'';\n      if(!hasContent(vals)&#038;&#038;!nm)continue;\n      officines.push({cip,name:nm||('Officine '+(officines.length+1)),vals});\n    }\n    if(officines.length)return{officines,rows:officines.length,sheet:name};\n  }\n  return{error:'Aucune ligne d\u2019officine trouv\u00e9e. V\u00e9rifiez qu\u2019il s\u2019agit bien de l\u2019extraction ospharm.'};\n}\n\n\/\/ pasted text -> {officines, rows} | {error}. Tab-split preserves empty (blank tier) columns.\nfunction parsePaste(text){\n  if(!text||!text.trim())return{error:'Collez d\u2019abord une extraction.'};\n  const lines=text.split(\/\\r?\\n\/).filter(l=>l.trim().length);\n  const officines=[];\n  for(const line of lines){\n    if(\/CA non remis|CA remis|Laboratoire|^\\s*CIP\\b\/i.test(line))continue;\n    let toks=line.split('\\t');\n    if(toks.length<22){ const alt=line.split(\/;|\\|\/); toks=(alt.length>=22)?alt:line.trim().split(\/\\s+\/); }\n    if(toks.length<22)continue;\n    const offset=(toks.length>=25)?3:(toks.length-22); \/\/ standard ospharm row: CIP,Pharmacie,Source,+22\n    const vals=ZEROS();\n    for(let i=0;i<22;i++){ const n=parseNum(toks[offset+i]); vals[i]=(n==null)?0:n; }\n    if(!hasContent(vals))continue;\n    let cip='',nm='';\n    for(let k=0;k<offset;k++){ const t=String(toks[k]).trim(); if(!t)continue;\n      if(\/^\\d[\\d\\s]*$\/.test(t)&#038;&#038;!cip)cip=t; else if(!\/^\\d\/.test(t)&#038;&#038;!nm)nm=t; }\n    officines.push({cip,name:nm||('Officine '+(officines.length+1)),vals});\n  }\n  if(!officines.length)return{error:'Impossible de rep\u00e9rer 22 valeurs (de \u00ab Global \u00bb \u00e0 \u00ab 40 % \u00bb). V\u00e9rifiez le collage.'};\n  return{officines,rows:officines.length};\n}\n\n\/* ---------- calc engine ---------- *\/\nfunction compute(vals,g8on){\n  const caCat=vals[IDX.GLOBAL_nr], caFac=vals[IDX.GLOBAL_r];\n  const tiers=RATTRAP.map(t=>{\n    const nr=vals[t.nr], r=vals[t.r], at40=nr*TARGET_FACTOR, comp=r-at40;\n    return {key:t.key,nr,r,at40,comp};\n  });\n  const total40=tiers.reduce((a,t)=>a+t.comp,0);\n  const remunerable=vals[IDX.GLOBAL_nr]-vals[IDX.T0_nr]-vals[IDX.T2_5_nr];\n  const g8=remunerable*G8_RATE;\n  const totalDu=total40+(g8on?g8:0);\n  return {caCat,caFac,tiers,total40,remunerable,g8,totalDu};\n}\n\n\/* ---------- aggregation ---------- *\/\n\/\/ a period stores per-officine rows; older saves may have a flat .vals -> wrap it\nfunction periodOfficines(p){\n  if(p.officines&&p.officines.length)return p.officines;\n  if(p.vals)return [{cip:'',name:p.label||'Global',vals:p.vals}];\n  return [];\n}\nfunction periodVals(p){ return sumVals(periodOfficines(p).map(o=>o.vals)); }\n\/\/ cumul par officine sur toutes les p\u00e9riodes, regroup\u00e9 par CIP (ou nom)\nfunction officineAggregates(){\n  const g=new Map();\n  for(const p of periodsSorted()){\n    for(const o of periodOfficines(p)){\n      const key=(o.cip&&o.cip.trim())||o.name||'\u2014';\n      if(!g.has(key))g.set(key,{key,cip:o.cip||'',name:o.name||key,totalVals:ZEROS(),perPeriod:[]});\n      const e=g.get(key);\n      for(let i=0;i<22;i++)e.totalVals[i]+=(o.vals[i]||0);\n      if(!e.name&#038;&#038;o.name)e.name=o.name;\n      e.perPeriod.push({month:p.month,label:p.label||monthLabel(p.month),vals:o.vals});\n    }\n  }\n  return [...g.values()];\n}\nfunction periodsSorted(){ return [...state.periods].sort((a,b)=>(a.month||'').localeCompare(b.month||'')); }\nfunction totalVerse(){ return state.payments.reduce((a,p)=>a+(+p.amount||0),0); }\nfunction commissionOn(){ return IS_GRP && state.settings.comm!==false; }\nfunction cumulative(){\n  const g8on=state.settings.g8;\n  let duOff=0,r40=0,g8=0,remun=0;\n  for(const p of state.periods){ const c=compute(periodVals(p),g8on); duOff+=c.totalDu; r40+=c.total40; g8+=c.g8; remun+=c.remunerable; }\n  const comm = commissionOn() ? remun*COMM_RATE : 0;\n  const verseComm = state.payments.filter(p=>p.officine===GRP_KEY).reduce((a,p)=>a+(+p.amount||0),0);\n  const du = duOff + comm;\n  const verse = totalVerse();   \/\/ inclut les versements officines + la commission\n  return {du,duOff,r40,g8,remun,comm,verseComm,commReste:comm-verseComm,verse,balance:verse-du};\n}\n\n\/* ---------- render ---------- *\/\nlet chartMonthly=null,chartTier=null;\n\nfunction render(){\n  const g8on=state.settings.g8;\n  const cum=cumulative();\n  \/\/ hero\n  const owed=cum.balance<-0.5;\n  const amt=document.getElementById('heroAmount');\n  if(state.periods.length===0){\n    amt.textContent='\u2014';amt.className='amount';\n    document.getElementById('heroVerdict').textContent='Ajoutez une p\u00e9riode pour d\u00e9marrer le contr\u00f4le.';\n    document.getElementById('heroSub').textContent='\u2014';\n    document.getElementById('heroLab').textContent='Solde de contr\u00f4le \u00b7 cumul\u00e9';\n  }else{\n    amt.textContent=fmtEUR2(Math.abs(cum.balance));\n    amt.className='amount '+(owed?'owed':'ok');\n    document.getElementById('heroLab').textContent=owed?'Arrow vous doit \u00b7 cumul\u00e9':'Compte \u00e0 jour \u00b7 cumul\u00e9';\n    document.getElementById('heroVerdict').textContent= owed\n      ? 'Le total vers\u00e9 est inf\u00e9rieur au total d\u00fb. Ce montant reste \u00e0 r\u00e9cup\u00e9rer aupr\u00e8s du laboratoire.'\n      : (cum.balance>0.5?'Trop-per\u00e7u : les versements d\u00e9passent le d\u00fb calcul\u00e9 sur la p\u00e9riode.':'Versements et d\u00fb s\u2019\u00e9quilibrent sur la p\u00e9riode.');\n    document.getElementById('heroSub').textContent=fmtEUR(cum.du)+'  \/  '+fmtEUR(cum.verse);\n  }\n  \/\/ kpis\n  document.getElementById('kpiDu').textContent=fmtEUR(cum.du);\n  document.getElementById('kpiVerse').textContent=fmtEUR(cum.verse);\n  document.getElementById('kpi40').textContent=fmtEUR(cum.r40);\n  document.getElementById('kpiG8').innerHTML=fmtEUR(cum.g8)+(g8on?'':' <small>(d\u00e9sactiv\u00e9e)<\/small>');\n\n  \/\/ carte commission groupement\n  const commCard=document.getElementById('commCard');\n  if(commCard){\n    if(commissionOn() && state.periods.length){\n      commCard.style.display='';\n      document.getElementById('commBase').textContent='base '+fmtEUR(cum.remun);\n      document.getElementById('commDu').textContent=fmtEUR2(cum.comm);\n      document.getElementById('commVerse').textContent=fmtEUR2(cum.verseComm);\n      const cr=document.getElementById('commReste');\n      cr.textContent = cum.commReste>0.5?fmtEUR2(cum.commReste):(cum.commReste<-0.5?'trop-per\u00e7u':'sold\u00e9');\n      cr.style.color = cum.commReste>0.5?'var(--alert)':'var(--ink-soft)';\n    } else commCard.style.display='none';\n  }\n\n  renderPeriods(g8on);\n  renderPayments();\n  renderOfficines(g8on);\n  document.getElementById('offCount').textContent = officineAggregates().length ? '\u00b7 '+officineAggregates().length : '';\n  drawActiveCharts(g8on);\n}\n\nlet activeTab='dashboard';\nfunction drawActiveCharts(g8on){\n  g8on=(g8on===undefined)?state.settings.g8:g8on;\n  if(typeof Chart==='undefined')return;\n  if(activeTab==='dashboard') renderCharts(g8on); else renderOfficineChart(g8on);\n}\nfunction switchTab(name){\n  activeTab=name;\n  document.getElementById('view-dashboard').hidden = name!=='dashboard';\n  document.getElementById('view-officines').hidden = name!=='officines';\n  document.getElementById('tabDashboard').classList.toggle('active',name==='dashboard');\n  document.getElementById('tabOfficines').classList.toggle('active',name==='officines');\n  drawActiveCharts();\n}\n\nconst OFF_COLORS = IS_GRP\n  ? ['#0098A8','#7FB800','#F8A000','#E8B400','#3f5a6b','#27b4ac','#cf7a3a','#9a6db0','#5d8a2f','#d99a00','#4a6fa5','#b0506b']\n  : ['#15715a','#2f8f6f','#7aa996','#9a6b0d','#5a7d8f','#8a5a9a','#b8763a','#3a8f73','#6b7d72','#a04a4a','#4a6ba0','#7d9a3a'];\n\/\/ r\u00e9partit les versements par officine : affect\u00e9s directement + part au prorata du d\u00fb pour les versements globaux\nfunction officineVerseMap(aggs){\n  const keySet=new Set(aggs.map(a=>a.key));\n  const duTotal=aggs.reduce((s,a)=>s+a.c.totalDu,0);\n  const direct={}; let pool=0;\n  for(const p of state.payments){\n    if(p.officine===GRP_KEY) continue;   \/\/ la commission groupement n'est pas ventil\u00e9e aux officines\n    const amt=+p.amount||0;\n    if(p.officine && keySet.has(p.officine)) direct[p.officine]=(direct[p.officine]||0)+amt;\n    else pool+=amt;\n  }\n  const map={};\n  for(const a of aggs){\n    const share = duTotal>0 ? pool*(a.c.totalDu\/duTotal) : pool\/(aggs.length||1);\n    map[a.key]=(direct[a.key]||0)+share;\n  }\n  return {map,pool};\n}\nfunction resteCell(reste){\n  if(reste>0.5) return `<span class=\"mono\" style=\"color:var(--alert);font-weight:700\">${fmtEUR(reste)}<\/span>`;\n  if(reste<-0.5) return `<span class=\"mono ok-txt\" style=\"color:var(--ok)\">trop-per\u00e7u<\/span>`;\n  return `<span class=\"mono muted\">sold\u00e9<\/span>`;\n}\nfunction renderOfficines(g8on){\n  const host=document.getElementById('officinesBody');\n  const aggs=officineAggregates().map(a=>({...a,c:compute(a.totalVals,g8on)})).sort((x,y)=>y.c.totalDu-x.c.totalDu);\n  document.getElementById('offSub').textContent = aggs.length? (aggs.length+(aggs.length>1?' officines':' officine')) : '';\n  if(aggs.length===0){\n    host.innerHTML=`<div class=\"empty\"><h2>Aucune officine<\/h2><p>Importez une extraction (id\u00e9alement votre fichier comportant plusieurs pharmacies) pour voir ici la contribution de chacune.<\/p><button class=\"btn primary\" onclick=\"openPeriod()\">+ Ajouter une p\u00e9riode<\/button><\/div>`;\n    document.getElementById('offShare').innerHTML='<p class=\"muted\" style=\"margin:0\">\u2014<\/p>';\n    return;\n  }\n  const {map:vmap,pool}=officineVerseMap(aggs);\n  const totalDu=aggs.reduce((a,x)=>a+x.c.totalDu,0)||1;\n  \/\/ share list\n  document.getElementById('offShare').innerHTML = aggs.map((a,i)=>{\n    const pct=a.c.totalDu\/totalDu*100;\n    return `<div style=\"margin:0 0 11px\">\n      <div style=\"display:flex;justify-content:space-between;font-size:13px;margin-bottom:4px\">\n        <span style=\"display:flex;align-items:center;gap:7px\"><span class=\"dot\" style=\"background:${OFF_COLORS[i%OFF_COLORS.length]}\"><\/span>${escapeHtml(a.name)}<\/span>\n        <span class=\"mono\">${pct.toFixed(1)} %<\/span><\/div>\n      <div style=\"height:7px;border-radius:4px;background:var(--surface-2);overflow:hidden\"><div style=\"height:100%;width:${pct.toFixed(1)}%;background:${OFF_COLORS[i%OFF_COLORS.length]}\"><\/div><\/div>\n    <\/div>`;\n  }).join('');\n  \/\/ table with expandable per-period detail\n  const rows=aggs.map((a,i)=>{\n    const verse=vmap[a.key]||0, reste=a.c.totalDu-verse;\n    const per=[...a.perPeriod].sort((x,y)=>(x.month||'').localeCompare(y.month||'')).map(pp=>{\n      const cc=compute(pp.vals,g8on);\n      return `<tr><td>${escapeHtml(pp.label||monthLabel(pp.month))}<\/td><td class=\"num\">${fmtEUR2(cc.caCat)}<\/td><td class=\"num\">${fmtEUR2(cc.caFac)}<\/td><td class=\"num\">${fmtEUR2(cc.total40)}<\/td><td class=\"num\">${g8on?fmtEUR2(cc.g8):'\u2014'}<\/td><td class=\"num\" style=\"font-weight:600\">${fmtEUR2(cc.totalDu)}<\/td><\/tr>`;\n    }).join('');\n    return `\n    <tr class=\"period-row\" data-off=\"${i}\">\n      <td><span class=\"chev\">\u25b8<\/span><span class=\"dot\" style=\"background:${OFF_COLORS[i%OFF_COLORS.length]};margin-right:7px\"><\/span>${escapeHtml(a.name)}${a.cip?` <span class=\"muted\" style=\"font-size:12px\">\u00b7 ${escapeHtml(a.cip)}<\/span>`:''}<\/td>\n      <td class=\"num\">${fmtEUR(a.c.caCat)}<\/td>\n      <td class=\"num\">${fmtEUR(a.c.caFac)}<\/td>\n      <td class=\"num\">${fmtEUR(a.c.total40)}<\/td>\n      <td class=\"num\">${g8on?fmtEUR(a.c.g8):'<span class=\"muted\">\u2014<\/span>'}<\/td>\n      <td class=\"num\" style=\"font-weight:600\">${fmtEUR(a.c.totalDu)}<\/td>\n      <td class=\"num\" style=\"color:var(--verse)\">${fmtEUR(verse)}<\/td>\n      <td class=\"num\">${resteCell(reste)}<\/td>\n    <\/tr>\n    <tr class=\"detail\" data-offdetail=\"${i}\" style=\"display:none\"><td colspan=\"8\"><div class=\"detail-inner\">\n      <table class=\"subt\"><thead><tr><th>P\u00e9riode<\/th><th class=\"num\">Catalogue<\/th><th class=\"num\">Factur\u00e9<\/th><th class=\"num\">Rattrapage 40 %<\/th><th class=\"num\">g8<\/th><th class=\"num\">Total d\u00fb<\/th><\/tr><\/thead>\n      <tbody>${per}<\/tbody><\/table>\n    <\/div><\/td><\/tr>`;\n  }).join('');\n  const tot=aggs.reduce((a,x)=>({cat:a.cat+x.c.caCat,fac:a.fac+x.c.caFac,r40:a.r40+x.c.total40,g8:a.g8+x.c.g8,du:a.du+x.c.totalDu}),{cat:0,fac:0,r40:0,g8:0,du:0});\n  const totVerse=aggs.reduce((s,a)=>s+(vmap[a.key]||0),0), totReste=tot.du-totVerse;\n  host.innerHTML=`<table>\n    <thead><tr><th>Officine<\/th><th class=\"num\">CA catalogue<\/th><th class=\"num\">CA factur\u00e9<\/th><th class=\"num\">Rattrapage 40 %<\/th><th class=\"num\">Remise g8<\/th><th class=\"num\">Total d\u00fb<\/th><th class=\"num\">Vers\u00e9<\/th><th class=\"num\">\u00c0 r\u00e9cup\u00e9rer<\/th><\/tr><\/thead>\n    <tbody>${rows}\n      <tr style=\"background:var(--surface-2)\"><td style=\"font-weight:700\">Toutes officines<\/td><td class=\"num\" style=\"font-weight:600\">${fmtEUR(tot.cat)}<\/td><td class=\"num\" style=\"font-weight:600\">${fmtEUR(tot.fac)}<\/td><td class=\"num\" style=\"font-weight:600\">${fmtEUR(tot.r40)}<\/td><td class=\"num\" style=\"font-weight:600\">${g8on?fmtEUR(tot.g8):'\u2014'}<\/td><td class=\"num\" style=\"font-weight:700\">${fmtEUR(tot.du)}<\/td><td class=\"num\" style=\"font-weight:700;color:var(--verse)\">${fmtEUR(totVerse)}<\/td><td class=\"num\" style=\"font-weight:700\">${resteCell(totReste)}<\/td><\/tr>\n    <\/tbody><\/table>\n    ${pool>0?`<p class=\"muted\" style=\"font-size:12px;padding:10px 18px;margin:0\">Les versements non affect\u00e9s \u00e0 une officine (${fmtEUR(pool)}) sont r\u00e9partis au prorata du d\u00fb de chaque pharmacie. Affectez un versement \u00e0 une officine pr\u00e9cise depuis l\u2019onglet \u00ab Tableau de bord \u00bb pour un suivi exact.<\/p>`:''}`;\n  host.querySelectorAll('.period-row').forEach(tr=>tr.addEventListener('click',()=>{\n    const i=tr.dataset.off; const det=host.querySelector(`[data-offdetail=\"${i}\"]`); const ch=tr.querySelector('.chev');\n    const open=det.style.display==='none'; det.style.display=open?'table-row':'none'; ch.classList.toggle('open',open);\n  }));\n}\n\nlet chartOff=null;\nfunction renderOfficineChart(g8on){\n  if(typeof Chart==='undefined')return;\n  const css=getComputedStyle(document.documentElement);\n  const line=css.getPropertyValue('--line').trim(), brand=css.getPropertyValue('--brand').trim();\n  Chart.defaults.font.family=\"'IBM Plex Mono','Inter',monospace\";\n  const aggs=officineAggregates().map(a=>({...a,c:compute(a.totalVals,g8on)})).sort((x,y)=>y.c.totalDu-x.c.totalDu).slice(0,14);\n  const {map:vmap}=officineVerseMap(aggs);\n  if(chartOff)chartOff.destroy();\n  const cv=document.getElementById('chartOfficines'); if(!cv)return;\n  chartOff=new Chart(cv,{type:'bar',data:{labels:aggs.map(a=>a.name),\n    datasets:[\n      {label:'D\u00fb',data:aggs.map(a=>a.c.totalDu),backgroundColor:brand,borderRadius:4,maxBarThickness:18},\n      {label:'Vers\u00e9',data:aggs.map(a=>vmap[a.key]||0),backgroundColor:css.getPropertyValue('--verse').trim(),borderRadius:4,maxBarThickness:18}\n    ]},\n    options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,\n      plugins:{legend:{position:'bottom',labels:{boxWidth:10,boxHeight:10,usePointStyle:true,font:{size:11}}},tooltip:{callbacks:{label:c=>c.dataset.label+': '+fmtEUR2(c.parsed.x)}}},\n      scales:{x:{grid:{color:line},ticks:{callback:v=>fmtEUR(v),font:{size:10}}},y:{grid:{display:false},ticks:{font:{size:11}}}}}});\n}\n\nfunction renderPeriods(g8on){\n  const host=document.getElementById('periodsBody');\n  if(state.periods.length===0){\n    host.innerHTML=`<div class=\"empty\">\n      <h2>Aucune p\u00e9riode enregistr\u00e9e<\/h2>\n      <p>Collez votre extraction mensuelle pour calculer automatiquement le rattrapage \u00e0 40 % et la prestation g8, puis suivez l\u2019\u00e9volution mois apr\u00e8s mois.<\/p>\n      <button class=\"btn primary\" onclick=\"openPeriod()\">+ Ajouter une p\u00e9riode<\/button>\n      <button class=\"btn\" onclick=\"loadDemo()\" style=\"margin-left:8px\">Charger l\u2019exemple Arrow 2025<\/button>\n    <\/div>`;\n    return;\n  }\n  const rows=periodsSorted().map(p=>{\n    const c=compute(periodVals(p),g8on);\n    const nOff=periodOfficines(p).length;\n    const offNote=nOff>1?` <span class=\"muted\" style=\"font-weight:400;font-size:12px\">\u00b7 ${nOff} officines<\/span>`:'';\n    const sub=c.tiers.map(t=>`<tr><td>Palier ${t.key} \u2192 40 %<\/td><td class=\"num\">${fmtEUR2(t.nr)}<\/td><td class=\"num\">${fmtEUR2(t.r)}<\/td><td class=\"num\">${fmtEUR2(t.at40)}<\/td><td class=\"num\">${fmtEUR2(t.comp)}<\/td><\/tr>`).join('');\n    return `\n    <tr class=\"period-row\" data-id=\"${p.id}\">\n      <td><span class=\"chev\">\u25b8<\/span>${p.label||monthLabel(p.month)||'P\u00e9riode'}${offNote}<\/td>\n      <td class=\"num\">${fmtEUR(c.caCat)}<\/td>\n      <td class=\"num\">${fmtEUR(c.caFac)}<\/td>\n      <td class=\"num\">${fmtEUR(c.total40)}<\/td>\n      <td class=\"num\">${g8on?fmtEUR(c.g8):'<span class=\"muted\">\u2014<\/span>'}<\/td>\n      <td class=\"num\" style=\"font-weight:600\">${fmtEUR(c.totalDu)}<\/td>\n      <td><button class=\"iconbtn\" title=\"Supprimer\" onclick=\"delPeriod(event,'${p.id}')\">\u2715<\/button><\/td>\n    <\/tr>\n    <tr class=\"detail\" data-detail=\"${p.id}\" style=\"display:none\"><td colspan=\"7\"><div class=\"detail-inner\">\n      <table class=\"subt\">\n        <thead><tr><th>Palier<\/th><th class=\"num\">Catalogue<\/th><th class=\"num\">Factur\u00e9<\/th><th class=\"num\">Cible 40 %<\/th><th class=\"num\">Rattrapage<\/th><\/tr><\/thead>\n        <tbody>${sub}\n          <tr><td colspan=\"4\" style=\"text-align:right;font-weight:600\">Total rattrapage 40 %<\/td><td class=\"num\" style=\"font-weight:600\">${fmtEUR2(c.total40)}<\/td><\/tr>\n          <tr><td>Prestation g8 \u2014 16,5 % sur ${fmtEUR2(c.remunerable)} (hors 0 % et 2,5 %)<\/td><td colspan=\"3\"><\/td><td class=\"num\">${g8on?fmtEUR2(c.g8):'d\u00e9sactiv\u00e9e'}<\/td><\/tr>\n          <tr><td colspan=\"4\" style=\"text-align:right;font-weight:700\">Total d\u00fb<\/td><td class=\"num\" style=\"font-weight:700\">${fmtEUR2(c.totalDu)}<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div><\/td><\/tr>`;\n  }).join('');\n  host.innerHTML=`<table>\n    <thead><tr><th>P\u00e9riode<\/th><th class=\"num\">CA catalogue<\/th><th class=\"num\">CA factur\u00e9<\/th><th class=\"num\">Rattrapage 40 %<\/th><th class=\"num\">Remise g8<\/th><th class=\"num\">Total d\u00fb<\/th><th><\/th><\/tr><\/thead>\n    <tbody>${rows}<\/tbody><\/table>`;\n  host.querySelectorAll('.period-row').forEach(tr=>{\n    tr.addEventListener('click',e=>{\n      if(e.target.closest('.iconbtn'))return;\n      const id=tr.dataset.id;\n      const det=host.querySelector(`[data-detail=\"${id}\"]`);\n      const ch=tr.querySelector('.chev');\n      const open=det.style.display==='none';\n      det.style.display=open?'table-row':'none';\n      ch.classList.toggle('open',open);\n    });\n  });\n}\n\nfunction officineNameByKey(key){\n  if(key===GRP_KEY) return 'Groupement \u2014 commission 3 %';\n  if(!key)return 'Toutes';\n  const a=officineAggregates().find(o=>o.key===key);\n  return a?a.name:key;\n}\nfunction fillPayOfficines(){\n  const sel=document.getElementById('payOfficine'); if(!sel)return;\n  const btn=document.getElementById('btnAddPay');\n  const offs=officineAggregates();\n  const cur=sel.value;\n  if(offs.length===0){\n    sel.innerHTML='<option value=\"\">\u2014 importez une p\u00e9riode \u2014<\/option>';\n    sel.disabled=true; if(btn)btn.disabled=true; return;\n  }\n  sel.disabled=false; if(btn)btn.disabled=false;\n  const opts=['<option value=\"\" disabled'+(cur?'':' selected')+'>Choisir l\u2019officine\u2026<\/option>']\n    .concat(offs.map(o=>`<option value=\"${escapeHtml(o.key)}\">${escapeHtml(o.name)}<\/option>`));\n  if(IS_GRP) opts.push(`<option value=\"${GRP_KEY}\">Groupement \u2014 commission 3 %<\/option>`);\n  sel.innerHTML=opts.join('');\n  if(cur && (cur===GRP_KEY || offs.some(o=>o.key===cur))) sel.value=cur;\n  else if(offs.length===1) sel.value=offs[0].key;   \/\/ une seule officine : pr\u00e9-s\u00e9lectionn\u00e9e\n}\nfunction renderPayments(){\n  fillPayOfficines();\n  const host=document.getElementById('paymentsTable');\n  if(state.payments.length===0){\n    host.innerHTML='<p class=\"muted\" style=\"margin:4px 0 0\">Aucun versement saisi.<\/p>';return;\n  }\n  const rows=[...state.payments].sort((a,b)=>(a.date||'').localeCompare(b.date||'')).map(p=>`\n    <tr><td>${monthLabel(p.date)||'\u2014'}<\/td><td>${escapeHtml(p.label||'')}<\/td>\n    <td>${escapeHtml(officineNameByKey(p.officine))}<\/td>\n    <td class=\"num\">${fmtEUR2(p.amount)}<\/td>\n    <td><button class=\"iconbtn\" onclick=\"delPayment('${p.id}')\">\u2715<\/button><\/td><\/tr>`).join('');\n  host.innerHTML=`<table>\n    <thead><tr><th>Mois<\/th><th>Libell\u00e9<\/th><th>Officine<\/th><th class=\"num\">Montant HT<\/th><th><\/th><\/tr><\/thead>\n    <tbody>${rows}<tr><td colspan=\"3\" style=\"text-align:right;font-weight:600\">Total vers\u00e9<\/td><td class=\"num\" style=\"font-weight:700\">${fmtEUR2(totalVerse())}<\/td><td><\/td><\/tr><\/tbody><\/table>`;\n}\n\nfunction escapeHtml(s){return String(s).replace(\/[&<>\"]\/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));}\n\n\/* ---------- charts ---------- *\/\nfunction timeline(){\n  const months=new Set();\n  state.periods.forEach(p=>p.month&&months.add(p.month));\n  state.payments.forEach(p=>p.date&&months.add(p.date));\n  const list=[...months].sort();\n  const g8on=state.settings.g8;\n  let cumDu=0,cumV=0;\n  return list.map(m=>{\n    const du=state.periods.filter(p=>p.month===m).reduce((a,p)=>a+compute(periodVals(p),g8on).totalDu,0);\n    const v=state.payments.filter(p=>p.date===m).reduce((a,p)=>a+(+p.amount||0),0);\n    cumDu+=du;cumV+=v;\n    return {m,du,v,balance:cumV-cumDu};\n  });\n}\nfunction renderCharts(g8on){\n  if(typeof Chart==='undefined')return;\n  const css=getComputedStyle(document.documentElement);\n  const brand=css.getPropertyValue('--brand').trim(), ink=css.getPropertyValue('--ink').trim(),\n        soft=css.getPropertyValue('--ink-soft').trim(), amber=css.getPropertyValue('--amber').trim(),\n        alert=css.getPropertyValue('--alert').trim(), line=css.getPropertyValue('--line').trim();\n  Chart.defaults.font.family=\"'IBM Plex Mono','Inter',monospace\";\n  Chart.defaults.color=soft;\n\n  const tl=timeline();\n  const mData={labels:tl.map(x=>monthLabel(x.m)),\n    datasets:[\n      {type:'bar',label:'D\u00fb',data:tl.map(x=>x.du),backgroundColor:brand,borderRadius:4,maxBarThickness:26,order:2},\n      {type:'bar',label:'Vers\u00e9',data:tl.map(x=>x.v),backgroundColor:css.getPropertyValue('--verse').trim(),borderRadius:4,maxBarThickness:26,order:2},\n      {type:'line',label:'Solde cumul\u00e9',data:tl.map(x=>x.balance),borderColor:alert,backgroundColor:alert,\n        borderWidth:2,tension:.25,yAxisID:'y1',pointRadius:3,order:1}\n    ]};\n  if(chartMonthly)chartMonthly.destroy();\n  chartMonthly=new Chart(document.getElementById('chartMonthly'),{data:mData,options:{\n    responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},\n    plugins:{legend:{position:'bottom',labels:{boxWidth:10,boxHeight:10,usePointStyle:true,font:{size:11}}},\n      tooltip:{callbacks:{label:c=>c.dataset.label+': '+fmtEUR2(c.parsed.y)}}},\n    scales:{\n      y:{grid:{color:line},ticks:{callback:v=>fmtEUR(v),font:{size:10}}},\n      y1:{position:'right',grid:{drawOnChartArea:false},ticks:{callback:v=>fmtEUR(v),font:{size:10},color:alert}},\n      x:{grid:{display:false},ticks:{font:{size:10}}}\n    }}});\n\n  \/\/ tier breakdown cumulative\n  const tierTot=[0,0,0,0];\n  state.periods.forEach(p=>{const c=compute(periodVals(p),g8on);c.tiers.forEach((t,i)=>tierTot[i]+=t.comp);});\n  let g8tot=0; state.periods.forEach(p=>g8tot+=compute(periodVals(p),g8on).g8);\n  const tLabels=['5 % \u2192 40','10 % \u2192 40','20 % \u2192 40','30 % \u2192 40'].concat(g8on?['g8 16,5 %']:[]);\n  const tVals=tierTot.concat(g8on?[g8tot]:[]);\n  const tColors=['#8fc4b1','#5aa98c','#2f8f6f',amber].concat(g8on?['#6b7d72']:[]);\n  if(chartTier)chartTier.destroy();\n  chartTier=new Chart(document.getElementById('chartTier'),{type:'bar',data:{labels:tLabels,\n    datasets:[{label:'Cumul\u00e9',data:tVals,backgroundColor:tColors,borderRadius:5,maxBarThickness:34}]},\n    options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,\n      plugins:{legend:{display:false},tooltip:{callbacks:{label:c=>fmtEUR2(c.parsed.x)}}},\n      scales:{x:{grid:{color:line},ticks:{callback:v=>fmtEUR(v),font:{size:10}}},y:{grid:{display:false},ticks:{font:{size:11}}}}}});\n}\n\n\/* ---------- actions ---------- *\/\nfunction toast(msg){const t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');clearTimeout(t._t);t._t=setTimeout(()=>t.classList.remove('show'),2200);}\nconst uid=()=>Math.random().toString(36).slice(2,9);\n\nwindow.openPeriod=function(){\n  document.getElementById('pasteArea').value='';\n  document.getElementById('periodLabel').value='';\n  document.getElementById('periodMonth').value='';\n  document.getElementById('monthGuess').textContent='';\n  document.getElementById('step2').hidden=true;\n  document.getElementById('pErr').style.display='none';\n  document.getElementById('confirmPeriod').disabled=true;\n  document.getElementById('pMeta').textContent='';\n  pendingOfficines=null;\n  const pf=document.querySelector('.pastefold'); if(pf)pf.open=false;\n  const dz=document.getElementById('dropzone');\n  dz.classList.remove('loaded','drag');\n  dz.querySelector('.dz-main').textContent='D\u00e9posez le fichier Excel ospharm ici';\n  dz.querySelector('.dz-sub').innerHTML='ou <button type=\"button\" class=\"linklike\" id=\"dzBrowse\">parcourir<\/button> \u00b7 formats .xlsx \/ .xls \/ .csv';\n  dz.querySelector('#dzBrowse').addEventListener('click',e=>{e.stopPropagation();document.getElementById('excelInput').click();});\n  document.getElementById('excelInput').value='';\n  document.getElementById('modalPeriod').classList.add('show');\n};\nfunction closePeriod(){document.getElementById('modalPeriod').classList.remove('show');}\nfunction defaultMonth(){const d=new Date();return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0');}\nfunction prevMonth(){const d=new Date();d.setDate(1);d.setMonth(d.getMonth()-1);return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0');}\nfunction inferMonthFromName(name){\n  if(!name)return null;\n  const s=name.toLowerCase();\n  let m=s.match(\/(20\\d{2})[ _\\-.]?(0[1-9]|1[0-2])(?![0-9])\/);      \/\/ YYYY-MM \/ YYYYMM\n  if(m)return m[1]+'-'+m[2];\n  m=s.match(\/(0[1-9]|1[0-2])[ _\\-.](20\\d{2})\/);                    \/\/ MM-YYYY\n  if(m)return m[2]+'-'+m[1];\n  const idx={janvier:1,'f\u00e9vrier':2,fevrier:2,mars:3,avril:4,mai:5,juin:6,juillet:7,'ao\u00fbt':8,aout:8,septembre:9,octobre:10,novembre:11,'d\u00e9cembre':12,decembre:12};\n  for(const nm in idx){ if(s.includes(nm)){ const ym=s.match(\/(20\\d{2})\/); if(ym)return ym[1]+'-'+String(idx[nm]).padStart(2,'0'); } }\n  return null;\n}\n\nlet pendingOfficines=null;\nfunction showPreview(res,extraMeta){\n  const err=document.getElementById('pErr'),pv=document.getElementById('pPreview');\n  if(res.error){pendingOfficines=null;document.getElementById('step2').hidden=true;err.textContent=res.error;err.style.display='block';\n    document.getElementById('confirmPeriod').disabled=true;document.getElementById('pMeta').textContent='';return false;}\n  err.style.display='none';pendingOfficines=res.officines;\n  const agg=sumVals(res.officines.map(o=>o.vals));\n  const c=compute(agg,state.settings.g8);\n  document.getElementById('pvCat').textContent=fmtEUR2(c.caCat);\n  document.getElementById('pvFac').textContent=fmtEUR2(c.caFac);\n  document.getElementById('pv40').textContent=fmtEUR2(c.total40);\n  document.getElementById('pvG8').textContent=state.settings.g8?fmtEUR2(c.g8):'d\u00e9sactiv\u00e9e';\n  document.getElementById('pvDu').textContent=fmtEUR2(c.totalDu);\n  pv.classList.add('show');\n  document.getElementById('step2').hidden=false;            \/\/ \u00e9tape 2 r\u00e9v\u00e9l\u00e9e apr\u00e8s lecture\n  const mp=document.getElementById('periodMonth');\n  if(!mp.value){ mp.value=prevMonth(); }                    \/\/ d\u00e9faut : mois pr\u00e9c\u00e9dent\n  const meta=[res.rows>1?(res.rows+' officines additionn\u00e9es'):'1 officine',extraMeta].filter(Boolean).join(' \u00b7 ');\n  document.getElementById('pMeta').textContent=meta;\n  document.getElementById('confirmPeriod').disabled=false;\n  return true;\n}\nfunction updatePreview(){ showPreview(parsePaste(document.getElementById('pasteArea').value)); }\n\nfunction handleExcelFile(file){\n  if(!file)return;\n  const dz=document.getElementById('dropzone');\n  const name=file.name||'fichier';\n  file.arrayBuffer().then(b=>{\n    const res=parseWorkbook(new Uint8Array(b)); \/\/ SheetJS reads .xlsx\/.xls\/.csv alike\n    const ok=showPreview(res, res.sheet?('feuille \u00ab '+res.sheet+' \u00bb'):'');\n    dz.classList.toggle('loaded',ok);\n    if(ok){\n      dz.querySelector('.dz-main').textContent='\u2713 '+name;\n      dz.querySelector('.dz-sub').innerHTML='Donn\u00e9es lues \u00b7 <button type=\"button\" class=\"linklike\" id=\"dzReplace\">changer de fichier<\/button>';\n      dz.querySelector('#dzReplace').addEventListener('click',e=>{e.stopPropagation();document.getElementById('excelInput').click();});\n      const guess=inferMonthFromName(name);\n      const gnote=document.getElementById('monthGuess');\n      if(guess){ document.getElementById('periodMonth').value=guess; gnote.textContent='\u2014 d\u00e9duit du nom du fichier, v\u00e9rifiez.'; }\n      else { gnote.textContent=''; }\n      const lbl=document.getElementById('periodLabel');\n      if(!lbl.value.trim()) lbl.value=guess?('Extraction '+monthLabel(guess)):name.replace(\/\\.(xlsx|xls|csv)$\/i,'').slice(0,40);\n    }else{\n      dz.querySelector('.dz-main').textContent='D\u00e9posez le fichier Excel ospharm ici';\n    }\n  }).catch(()=>{ showPreview({error:'Lecture du fichier impossible.'}); });\n}\nasync function confirmPeriod(){\n  if(!pendingOfficines)return;\n  const month=document.getElementById('periodMonth').value||defaultMonth();\n  const label=document.getElementById('periodLabel').value.trim()||monthLabel(month);\n  state.periods.push({id:uid(),month,label,officines:pendingOfficines});\n  await saveState();closePeriod();render();toast('P\u00e9riode ajout\u00e9e');\n}\nwindow.delPeriod=async function(ev,id){ev.stopPropagation();\n  if(!confirm('Supprimer cette p\u00e9riode ?'))return;\n  state.periods=state.periods.filter(p=>p.id!==id);await saveState();render();toast('P\u00e9riode supprim\u00e9e');};\n\nasync function addPayment(){\n  const amount=parseNum(document.getElementById('payAmount').value);\n  if(amount===null||amount===0){toast('Saisissez un montant');return;}\n  const date=document.getElementById('payMonth').value||defaultMonth();\n  const label=document.getElementById('payLabel').value.trim()||'Versement';\n  let officine=document.getElementById('payOfficine').value||'';\n  if(!IS_GRP){ const offs=officineAggregates(); officine=offs.length?offs[0].key:''; }\n  if(!officine){toast(IS_GRP?'Choisissez l\u2019officine concern\u00e9e':'Importez d\u2019abord votre extraction');return;}\n  state.payments.push({id:uid(),date,label,officine,amount});\n  document.getElementById('payLabel').value='';document.getElementById('payAmount').value='';\n  await saveState();render();toast('Versement ajout\u00e9');\n}\nwindow.delPayment=async function(id){state.payments=state.payments.filter(p=>p.id!==id);await saveState();render();toast('Versement supprim\u00e9');};\n\nwindow.loadDemo=async function(){\n  const demoVals=[319164.90,239335.884,102937.29,75739.053,216227.61,163596.831,3558.52,3558.52,28353.90,27645.0525,6516.71,6190.8745,4038.91,3635.019,46649.75,37319.80,1293.20,969.90,225313.55,157719.485,2145.82,1287.492];\n  state.periods=[{id:uid(),month:'2025-12',label:'2025 \u2014 exemple annuel',officines:[\n    {cip:'2148505',name:'PHARMACIE DES FORGES',vals:demoVals}\n  ]}];\n  state.payments=[\n    {id:uid(),date:'2025-06',officine:'2148505',label:'Facture prestation \/ coop. commerciale',amount:5775},\n    {id:uid(),date:'2025-09',officine:'2148505',label:'Avoir \u2014 ristourne compl\u00e9mentaire',amount:20499.92},\n    {id:uid(),date:'2025-12',officine:'2148505',label:'Avoir \u2014 ristourne compl\u00e9mentaire',amount:3686.10},\n    {id:uid(),date:'2025-05',officine:'2148505',label:'Pharminfo HT',amount:20338},\n    {id:uid(),date:'2025-08',officine:'2148505',label:'Pharminfo HT',amount:20085},\n    {id:uid(),date:'2025-03',officine:'2148505',label:'Pharmedisound',amount:1600},\n    {id:uid(),date:'2025-04',officine:'2148505',label:'Autre prestation',amount:1600},\n    {id:uid(),date:'2025-07',officine:'2148505',label:'Autre prestation',amount:2313}\n  ];\n  await saveState();render();toast('Exemple Arrow 2025 charg\u00e9');\n};\n\nasync function resetAll(){\n  if(!confirm('Effacer toutes les p\u00e9riodes et tous les versements ? Cette action est d\u00e9finitive.'))return;\n  state={periods:[],payments:[],settings:{g8:state.settings.g8}};\n  await saveState();render();toast('Donn\u00e9es effac\u00e9es');\n}\n\nfunction exportCSV(){\n  const g8on=state.settings.g8;\n  let csv='Type;Periode\/Mois;Libelle;CA catalogue;CA facture;Rattrapage 40;Remise g8;Total du;Montant verse\\n';\n  periodsSorted().forEach(p=>{const c=compute(periodVals(p),g8on);\n    csv+=`Periode;${p.month};${(p.label||'').replace(\/;\/g,',')};${c.caCat.toFixed(2)};${c.caFac.toFixed(2)};${c.total40.toFixed(2)};${(g8on?c.g8:0).toFixed(2)};${c.totalDu.toFixed(2)};\\n`;});\n  [...state.payments].sort((a,b)=>(a.date||'').localeCompare(b.date||'')).forEach(p=>{\n    csv+=`Versement;${p.date};${(p.label||'').replace(\/;\/g,',')};;;;;;${(+p.amount).toFixed(2)}\\n`;});\n  const cum=cumulative();\n  csv+=`\\nCUMUL;;;;;${cum.r40.toFixed(2)};${cum.g8.toFixed(2)};${cum.duOff.toFixed(2)};${cum.verse.toFixed(2)}\\n`;\n  if(commissionOn()){\n    csv+=`COMMISSION GROUPEMENT 3%;base ${cum.remun.toFixed(2)};;;;;;${cum.comm.toFixed(2)};vers\u00e9 ${cum.verseComm.toFixed(2)}\\n`;\n    csv+=`TOTAL DU (avec commission);;;;;;;${cum.du.toFixed(2)};\\n`;\n  }\n  csv+=`SOLDE (verse - du);;;;;;;;${cum.balance.toFixed(2)}\\n`;\n  const aggs=officineAggregates();\n  if(aggs.length){\n    const withC=aggs.map(a=>({...a,c:compute(a.totalVals,g8on)}));\n    const {map:vmap}=officineVerseMap(withC);\n    csv+='\\nPAR OFFICINE (cumule)\\n';\n    csv+='Officine;CIP;;CA catalogue;CA facture;Rattrapage 40;Remise g8;Total du;Verse;A recuperer\\n';\n    withC.sort((x,y)=>y.c.totalDu-x.c.totalDu).forEach(a=>{\n      const verse=vmap[a.key]||0, reste=a.c.totalDu-verse;\n      csv+=`${(a.name||'').replace(\/;\/g,',')};${(a.cip||'').replace(\/;\/g,',')};;${a.c.caCat.toFixed(2)};${a.c.caFac.toFixed(2)};${a.c.total40.toFixed(2)};${(g8on?a.c.g8:0).toFixed(2)};${a.c.totalDu.toFixed(2)};${verse.toFixed(2)};${(reste>0?reste:0).toFixed(2)}\\n`;\n    });\n  }\n  const blob=new Blob(['\\ufeff'+csv],{type:'text\/csv;charset=utf-8'});\n  const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='controle_remises_arrow.csv';a.click();\n  toast('Export CSV g\u00e9n\u00e9r\u00e9');\n}\n\n\/* ---------- wire up ---------- *\/\ndocument.getElementById('btnAddPeriod').addEventListener('click',openPeriod);\ndocument.getElementById('closePeriod').addEventListener('click',closePeriod);\ndocument.getElementById('modalPeriod').addEventListener('click',e=>{if(e.target.id==='modalPeriod')closePeriod();});\ndocument.getElementById('pasteArea').addEventListener('input',updatePreview);\ndocument.getElementById('confirmPeriod').addEventListener('click',confirmPeriod);\n\/\/ Excel import: click-to-browse + drag & drop\n(function(){\n  const dz=document.getElementById('dropzone'), inp=document.getElementById('excelInput');\n  dz.addEventListener('click',e=>{ if(!e.target.closest('button')) inp.click(); });\n  inp.addEventListener('change',e=>{ if(e.target.files[0]) handleExcelFile(e.target.files[0]); });\n  ['dragenter','dragover'].forEach(ev=>dz.addEventListener(ev,e=>{e.preventDefault();e.stopPropagation();dz.classList.add('drag');}));\n  ['dragleave','dragend'].forEach(ev=>dz.addEventListener(ev,e=>{e.preventDefault();e.stopPropagation();dz.classList.remove('drag');}));\n  dz.addEventListener('drop',e=>{e.preventDefault();e.stopPropagation();dz.classList.remove('drag');\n    const f=e.dataTransfer&&e.dataTransfer.files&&e.dataTransfer.files[0]; if(f)handleExcelFile(f);});\n})();\ndocument.getElementById('btnAddPay').addEventListener('click',addPayment);\ndocument.getElementById('btnReset').addEventListener('click',resetAll);\ndocument.getElementById('btnExport').addEventListener('click',exportCSV);\ndocument.getElementById('btnBackup').addEventListener('click',exportBackup);\ndocument.getElementById('btnRestore').addEventListener('click',()=>document.getElementById('restoreInput').click());\ndocument.getElementById('restoreInput').addEventListener('change',e=>{ if(e.target.files[0])importBackup(e.target.files[0]); e.target.value=''; });\ndocument.getElementById('g8toggle').addEventListener('change',async e=>{state.settings.g8=e.target.checked;await saveState();render();});\ndocument.getElementById('commtoggle').addEventListener('change',async e=>{state.settings.comm=e.target.checked;await saveState();render();});\ndocument.getElementById('tabDashboard').addEventListener('click',()=>switchTab('dashboard'));\ndocument.getElementById('tabOfficines').addEventListener('click',()=>switchTab('officines'));\ndocument.getElementById('btnExportGrp').addEventListener('click',exportForGroupement);\ndocument.getElementById('btnImportPharma').addEventListener('click',()=>document.getElementById('pharmaInput').click());\ndocument.getElementById('pharmaInput').addEventListener('change',e=>{ if(e.target.files[0])importPharmacy(e.target.files[0]); e.target.value=''; });\n\nconst GRP_THEME = {\n  '--ink':'#283848', '--ink-soft':'#5d6b77', '--line':'#dbe6e8',\n  '--bg':'#eef3f4', '--bg-tint':'#dcecef', '--surface-2':'#eef5f6',\n  '--brand':'#0098a8', '--brand-deep':'#03727f',\n  '--ok':'#4f8f15', '--ok-bg':'#eef6dd', '--amber':'#b06f0a', '--verse':'#f8a000',\n  '--hero1':'#07424c', '--hero2':'#0d2330',\n  '--sh':'0 1px 2px rgba(20,40,55,.05),0 8px 24px -16px rgba(20,40,55,.20)'\n};\nfunction applyMode(){\n  document.querySelector('.brandmark .t2').textContent = IS_GRP\n    ? 'Pharma G8 \u00b7 groupement \u2014 toutes les officines'\n    : 'Ma pharmacie \u00b7 suivi des remises Arrow';\n  document.getElementById('btnImportPharma').style.display = IS_GRP ? '' : 'none';\n  document.getElementById('btnExportGrp').style.display = IS_GRP ? 'none' : '';\n  document.querySelector('.tabs').style.display = IS_GRP ? '' : 'none';\n  const cs=document.getElementById('commSwitch'); if(cs) cs.style.display = IS_GRP ? '' : 'none';\n  const payOffLabel = document.getElementById('payOfficine') ? document.getElementById('payOfficine').closest('.fld') : null;\n  if(payOffLabel) payOffLabel.style.display = IS_GRP ? '' : 'none';\n  if(!IS_GRP) activeTab='dashboard';\n  if(IS_GRP){\n    const root=document.documentElement;\n    for(const k in GRP_THEME) root.style.setProperty(k, GRP_THEME[k]);\n    document.title='Contr\u00f4le des remises \u2014 Pharma G8 (groupement)';\n    const ic=document.getElementById('brandIcon');\n    if(ic){ ic.textContent=''; ic.style.background='transparent'; ic.style.boxShadow='none';\n      ic.style.padding='0'; ic.style.width='46px'; ic.style.height='46px';\n      const img=document.createElement('img'); img.src=PHARMA_G8_LOGO; img.alt='Pharma G8';\n      img.style.width='100%'; img.style.height='100%'; img.style.objectFit='contain';\n      ic.appendChild(img); }\n  }\n}\n\n(async function init(){\n  await loadState();\n  applyMode();\n  document.getElementById('g8toggle').checked=state.settings.g8!==false;\n  document.getElementById('commtoggle').checked=state.settings.comm!==false;\n  document.getElementById('payMonth').value=defaultMonth();\n  \/\/ probe writability so the badge is accurate even before the first change\n  if(storageMode==='m\u00e9moire'){\n    let writable=false;\n    try{ window.localStorage.setItem('arrow:probe','1'); window.localStorage.removeItem('arrow:probe'); writable=true; }catch(e){}\n    storageMode = hasClaudeStorage() ? 'aper\u00e7u Claude' : (writable ? 'navigateur' : 'm\u00e9moire (session)');\n  }\n  updateStorageBadge();\n  render();\n})();\n<\/script>\n<\/body>\n<\/html>\n\n","protected":false},"excerpt":{"rendered":"<p>Contr\u00f4le des remises \u2014 Pharma G8 (groupement) Rx Contr\u00f4le des remises compl\u00e9mentaires Arrow G\u00e9n\u00e9riques \u00b7 v\u00e9rification des reversements Prestation g8 (16,5 %) Commission groupement (3 %) Importer une pharmacie Exporter pour le groupement Sauvegarder Restaurer Exporter CSV Tout effacer Sauvegarde auto : \u2014 Tableau de bord Par officine Solde de contr\u00f4le \u00b7 cumul\u00e9 \u2014 Ajoutez [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-338","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/pages\/338","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/comments?post=338"}],"version-history":[{"count":2,"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/pages\/338\/revisions"}],"predecessor-version":[{"id":442,"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/pages\/338\/revisions\/442"}],"wp:attachment":[{"href":"https:\/\/lab.pharmavitale.net\/wp-cepi\/wp-json\/wp\/v2\/media?parent=338"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}