feat: already_done checkbox in schedule modal; fix: capacity check skips ended slots; feat: ha_poll_interval configurable
Deploy via Portainer / deploy (push) Successful in 0s
Deploy via Portainer / deploy (push) Successful in 0s
This commit is contained in:
@@ -1799,6 +1799,14 @@ def api_new_slot():
|
|||||||
if over:
|
if over:
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({'error': f"Capacité maximale atteinte : {peak} impression(s) déjà en cours sur ce créneau (limite : {max_printers} simultanées). Décalez l'horaire ou attendez qu'une imprimante se libère."}), 409
|
return jsonify({'error': f"Capacité maximale atteinte : {peak} impression(s) déjà en cours sur ce créneau (limite : {max_printers} simultanées). Décalez l'horaire ou attendez qu'une imprimante se libère."}), 409
|
||||||
|
already_done = bool(d.get('already_done', False))
|
||||||
|
if already_done:
|
||||||
|
# Retroactive — skip capacity/overlap checks, insert as done
|
||||||
|
conn.execute(
|
||||||
|
'INSERT INTO print_slots(job_id,printer_id,planned_start,planned_end,status) VALUES(?,?,?,?,?)',
|
||||||
|
(job_id, printer_id, start_dt.isoformat(), end_dt.isoformat(), 'done'))
|
||||||
|
conn.commit(); conn.close()
|
||||||
|
return jsonify({'ok': True})
|
||||||
conn.execute('INSERT INTO print_slots(job_id,printer_id,planned_start,planned_end,status) VALUES(?,?,?,?,?)',
|
conn.execute('INSERT INTO print_slots(job_id,printer_id,planned_start,planned_end,status) VALUES(?,?,?,?,?)',
|
||||||
(job_id, printer_id, start_dt.isoformat(), end_dt.isoformat(), 'planned'))
|
(job_id, printer_id, start_dt.isoformat(), end_dt.isoformat(), 'planned'))
|
||||||
conn.commit(); conn.close()
|
conn.commit(); conn.close()
|
||||||
|
|||||||
+38
-5
@@ -148,6 +148,15 @@
|
|||||||
<label class="form-label fw-semibold">Date et heure de lancement</label>
|
<label class="form-label fw-semibold">Date et heure de lancement</label>
|
||||||
<input type="datetime-local" id="modalStart" class="form-control">
|
<input type="datetime-local" id="modalStart" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="modalAlreadyDone" onchange="onAlreadyDoneChange()">
|
||||||
|
<label class="form-check-label fw-semibold text-success" for="modalAlreadyDone">
|
||||||
|
<i class="bi bi-check-circle-fill me-1"></i>Déjà imprimé
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-text ms-4">Enregistre le plateau comme terminé sans passer par le planning actif.</div>
|
||||||
|
</div>
|
||||||
<div id="modalSuggestion" class="alert alert-success py-2 small d-none">
|
<div id="modalSuggestion" class="alert alert-success py-2 small d-none">
|
||||||
<i class="bi bi-magic me-1"></i><span id="modalSuggestionText"></span>
|
<i class="bi bi-magic me-1"></i><span id="modalSuggestionText"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,7 +166,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuler</button>
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||||
<button type="button" class="btn fw-bold" style="background:#ff6b35;color:#fff" onclick="saveSlot()">
|
<button type="button" id="modalSaveBtn" class="btn fw-bold" style="background:#ff6b35;color:#fff" onclick="saveSlot()">
|
||||||
<i class="bi bi-save me-1"></i>Planifier
|
<i class="bi bi-save me-1"></i>Planifier
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -525,11 +534,33 @@ function formatDt(iso) {
|
|||||||
d.toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'});
|
d.toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onAlreadyDoneChange() {
|
||||||
|
const done = document.getElementById('modalAlreadyDone').checked;
|
||||||
|
const btn = document.getElementById('modalSaveBtn');
|
||||||
|
if (done) {
|
||||||
|
btn.innerHTML = '<i class="bi bi-check-circle me-1"></i>Enregistrer comme terminé';
|
||||||
|
btn.style.background = '#22c55e';
|
||||||
|
// Reset to "now" so user picks when it was done
|
||||||
|
const now = new Date();
|
||||||
|
now.setMinutes(0, 0, 0);
|
||||||
|
document.getElementById('modalStart').value = now.toISOString().slice(0,16);
|
||||||
|
document.getElementById('modalSuggestion').classList.add('d-none');
|
||||||
|
document.getElementById('modalError').classList.add('d-none');
|
||||||
|
} else {
|
||||||
|
btn.innerHTML = '<i class="bi bi-save me-1"></i>Planifier';
|
||||||
|
btn.style.background = '#ff6b35';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openScheduleModal(jobId, printTime, jobName) {
|
function openScheduleModal(jobId, printTime, jobName) {
|
||||||
document.getElementById('modalJobId').value = jobId;
|
document.getElementById('modalJobId').value = jobId;
|
||||||
document.getElementById('modalPrintTime').value = printTime;
|
document.getElementById('modalPrintTime').value = printTime;
|
||||||
document.getElementById('modalJobName').textContent = jobName;
|
document.getElementById('modalJobName').textContent = jobName;
|
||||||
document.getElementById('modalSuggestion').classList.add('d-none');
|
document.getElementById('modalSuggestion').classList.add('d-none');
|
||||||
|
document.getElementById('modalError').classList.add('d-none');
|
||||||
|
// Reset already-done checkbox
|
||||||
|
document.getElementById('modalAlreadyDone').checked = false;
|
||||||
|
onAlreadyDoneChange();
|
||||||
// Default: now rounded to next hour
|
// Default: now rounded to next hour
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setMinutes(0, 0, 0);
|
now.setMinutes(0, 0, 0);
|
||||||
@@ -559,15 +590,17 @@ async function suggestSlot(jobId, printTime, jobName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveSlot() {
|
async function saveSlot() {
|
||||||
const jobId = parseInt(document.getElementById('modalJobId').value);
|
const jobId = parseInt(document.getElementById('modalJobId').value);
|
||||||
const printer = parseInt(document.getElementById('modalPrinter').value);
|
const printer = parseInt(document.getElementById('modalPrinter').value);
|
||||||
const start = document.getElementById('modalStart').value;
|
const start = document.getElementById('modalStart').value;
|
||||||
|
const alreadyDone = document.getElementById('modalAlreadyDone').checked;
|
||||||
// Clear previous error
|
// Clear previous error
|
||||||
document.getElementById('modalError').classList.add('d-none');
|
document.getElementById('modalError').classList.add('d-none');
|
||||||
const r = await fetch('/api/slots/new', {
|
const r = await fetch('/api/slots/new', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type':'application/json'},
|
headers: {'Content-Type':'application/json'},
|
||||||
body: JSON.stringify({job_id: jobId, printer_id: printer, planned_start: start})
|
body: JSON.stringify({job_id: jobId, printer_id: printer, planned_start: start,
|
||||||
|
already_done: alreadyDone})
|
||||||
});
|
});
|
||||||
const res = await r.json();
|
const res = await r.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user