Merge pull request 'PWA: Dezimal-Mengen, STZ-Freigabe, Notiz-Trennung' (#1) from claude/hopeful-liskov into main
This commit is contained in:
commit
d859cadbf4
4 changed files with 321 additions and 44 deletions
|
|
@ -30,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
||||||
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||||
dol_include_once('/stundenzettel/class/stundenzettel.class.php');
|
dol_include_once('/stundenzettel/class/stundenzettel.class.php');
|
||||||
|
dol_include_once('/stundenzettel/lib/stundenzettel.lib.php');
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=UTF-8');
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
|
||||||
|
|
@ -237,6 +238,20 @@ switch ($action) {
|
||||||
$customer = new Societe($db);
|
$customer = new Societe($db);
|
||||||
$customer->fetch($order->socid);
|
$customer->fetch($order->socid);
|
||||||
|
|
||||||
|
// Standard-Dienstleistung des Kunden ermitteln
|
||||||
|
$defaultServiceId = 0;
|
||||||
|
$defaultServiceLabel = '';
|
||||||
|
if (isset($customer->array_options['options_stundenzettel_default_service'])) {
|
||||||
|
$defaultServiceId = (int)$customer->array_options['options_stundenzettel_default_service'];
|
||||||
|
if ($defaultServiceId > 0) {
|
||||||
|
$sqlDS = "SELECT ref, label FROM ".MAIN_DB_PREFIX."product WHERE rowid = ".((int)$defaultServiceId);
|
||||||
|
$resDS = $db->query($sqlDS);
|
||||||
|
if ($resDS && ($objDS = $db->fetch_object($resDS))) {
|
||||||
|
$defaultServiceLabel = $objDS->ref.' - '.$objDS->label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Auftragsdaten
|
// Auftragsdaten
|
||||||
$response['order'] = array(
|
$response['order'] = array(
|
||||||
'id' => (int)$order->id,
|
'id' => (int)$order->id,
|
||||||
|
|
@ -244,7 +259,9 @@ switch ($action) {
|
||||||
'date' => dol_print_date($order->date_commande, 'day'),
|
'date' => dol_print_date($order->date_commande, 'day'),
|
||||||
'customer_name' => $customer->name,
|
'customer_name' => $customer->name,
|
||||||
'customer_id' => (int)$customer->id,
|
'customer_id' => (int)$customer->id,
|
||||||
'status' => (int)$order->statut
|
'status' => (int)$order->statut,
|
||||||
|
'default_service_id' => $defaultServiceId,
|
||||||
|
'default_service_label' => $defaultServiceLabel
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stundenzettel finden (per ID oder letzten Draft)
|
// Stundenzettel finden (per ID oder letzten Draft)
|
||||||
|
|
@ -1497,7 +1514,7 @@ switch ($action) {
|
||||||
$objLine->rowid, // fk_commandedet
|
$objLine->rowid, // fk_commandedet
|
||||||
null,
|
null,
|
||||||
$objLine->qty, // qty_original
|
$objLine->qty, // qty_original
|
||||||
0, // qty_done
|
1, // qty_done (Standard: 1)
|
||||||
'order',
|
'order',
|
||||||
$objLine->description
|
$objLine->description
|
||||||
);
|
);
|
||||||
|
|
@ -1543,7 +1560,7 @@ switch ($action) {
|
||||||
null, // fk_commandedet (kein Auftragsbezug)
|
null, // fk_commandedet (kein Auftragsbezug)
|
||||||
null,
|
null,
|
||||||
$qty, // qty_original = Zielmenge
|
$qty, // qty_original = Zielmenge
|
||||||
0, // qty_done
|
1, // qty_done (Standard: 1)
|
||||||
'added',
|
'added',
|
||||||
$description ?: $productLabel
|
$description ?: $productLabel
|
||||||
);
|
);
|
||||||
|
|
@ -1556,6 +1573,65 @@ switch ($action) {
|
||||||
$response['added'] = $added;
|
$response['added'] = $added;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// ---- Stundenzettel freigeben (validieren) ----
|
||||||
|
case 'validate_stz':
|
||||||
|
if (!$canWrite) {
|
||||||
|
$response['error'] = 'Keine Schreibberechtigung';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stzId = GETPOST('stz_id', 'int');
|
||||||
|
$stz = new Stundenzettel($db);
|
||||||
|
if ($stz->fetch($stzId) <= 0) {
|
||||||
|
$response['error'] = 'Stundenzettel nicht gefunden';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($stz->status != Stundenzettel::STATUS_DRAFT) {
|
||||||
|
$response['error'] = 'Stundenzettel ist nicht im Entwurf';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!canEditStz($stz, $user, $canWriteAll)) {
|
||||||
|
$response['error'] = 'Keine Berechtigung';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $stz->validate($user);
|
||||||
|
if ($result > 0) {
|
||||||
|
// Netto-Wert aller Stundenzettel des Auftrags neu berechnen
|
||||||
|
updateOrderNettoSTZ($db, $stz->fk_commande);
|
||||||
|
$response['success'] = true;
|
||||||
|
} else {
|
||||||
|
$response['error'] = $stz->error ?: 'Fehler beim Freigeben';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ---- Stundenzettel zurueck auf Entwurf setzen ----
|
||||||
|
case 'setdraft_stz':
|
||||||
|
if (!$canWrite) {
|
||||||
|
$response['error'] = 'Keine Schreibberechtigung';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stzId = GETPOST('stz_id', 'int');
|
||||||
|
$stz = new Stundenzettel($db);
|
||||||
|
if ($stz->fetch($stzId) <= 0) {
|
||||||
|
$response['error'] = 'Stundenzettel nicht gefunden';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($stz->status == Stundenzettel::STATUS_DRAFT) {
|
||||||
|
$response['error'] = 'Stundenzettel ist bereits im Entwurf';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $stz->setDraft($user);
|
||||||
|
if ($result > 0) {
|
||||||
|
updateOrderNettoSTZ($db, $stz->fk_commande);
|
||||||
|
$response['success'] = true;
|
||||||
|
} else {
|
||||||
|
$response['error'] = $stz->error ?: 'Fehler beim Zuruecksetzen';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$response['error'] = 'Unbekannte Aktion: '.$action;
|
$response['error'] = 'Unbekannte Aktion: '.$action;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
42
css/pwa.css
42
css/pwa.css
|
|
@ -530,6 +530,29 @@ body {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
.qty-editable {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border: 1px dashed transparent;
|
||||||
|
transition: border-color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
.qty-editable:active {
|
||||||
|
background: var(--colorbackline);
|
||||||
|
border-color: var(--colorborder);
|
||||||
|
}
|
||||||
|
.qty-inline-input {
|
||||||
|
width: 56px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--colorbackinput);
|
||||||
|
color: var(--colortext);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* === Accordion-Sections === */
|
/* === Accordion-Sections === */
|
||||||
.accordion-section {
|
.accordion-section {
|
||||||
|
|
@ -1269,6 +1292,25 @@ body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === STZ Aktions-Buttons === */
|
||||||
|
.stz-actions {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
.warning-box {
|
||||||
|
background: rgba(255, 193, 7, 0.15);
|
||||||
|
border: 1px solid rgba(255, 193, 7, 0.4);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.warning-box p {
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
.warning-box p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Safe-Area fuer Geraete mit Notch */
|
/* Safe-Area fuer Geraete mit Notch */
|
||||||
@supports (padding-bottom: env(safe-area-inset-bottom)) {
|
@supports (padding-bottom: env(safe-area-inset-bottom)) {
|
||||||
.fab {
|
.fab {
|
||||||
|
|
|
||||||
237
js/pwa.js
237
js/pwa.js
|
|
@ -464,6 +464,8 @@
|
||||||
self.state.customerName = res.order.customer_name;
|
self.state.customerName = res.order.customer_name;
|
||||||
self.state.canWrite = res.can_write;
|
self.state.canWrite = res.can_write;
|
||||||
self.state.canEditStz = res.can_edit_stz;
|
self.state.canEditStz = res.can_edit_stz;
|
||||||
|
self.state.defaultServiceId = res.order.default_service_id || 0;
|
||||||
|
self.state.defaultServiceLabel = res.order.default_service_label || '';
|
||||||
|
|
||||||
if (res.stz) {
|
if (res.stz) {
|
||||||
self.state.stzId = res.stz.id;
|
self.state.stzId = res.stz.id;
|
||||||
|
|
@ -652,6 +654,9 @@
|
||||||
this.renderPanelStundenzettel();
|
this.renderPanelStundenzettel();
|
||||||
this.renderPanelProducts();
|
this.renderPanelProducts();
|
||||||
this.renderPanelTracking();
|
this.renderPanelTracking();
|
||||||
|
|
||||||
|
// Scroll-Position aller Panels nach oben zuruecksetzen
|
||||||
|
$('.swipe-panel').scrollTop(0);
|
||||||
},
|
},
|
||||||
|
|
||||||
// ---- Panel 0: Alle Stundenzettel ----
|
// ---- Panel 0: Alle Stundenzettel ----
|
||||||
|
|
@ -892,9 +897,30 @@
|
||||||
html += '</div>'; // accordion-section
|
html += '</div>'; // accordion-section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- AKTIONS-BUTTONS (Freigeben / Wiedereroeffnen) ----
|
||||||
|
if (self.state.canWrite) {
|
||||||
|
html += '<div class="stz-actions mt-12">';
|
||||||
|
if (isDraft) {
|
||||||
|
html += '<button class="btn btn-primary w-full" id="btn-validate-stz">✅ Stundenzettel freigeben</button>';
|
||||||
|
} else {
|
||||||
|
html += '<button class="btn btn-ghost w-full" id="btn-setdraft-stz">🔓 Zur\u00fcck auf Entwurf</button>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
$panel.html(html);
|
$panel.html(html);
|
||||||
|
|
||||||
// ---- Event-Listener ----
|
// ---- Event-Listener ----
|
||||||
|
// Freigeben / Wiedereroeffnen
|
||||||
|
$('#btn-validate-stz').on('click', function() {
|
||||||
|
self.validateStz();
|
||||||
|
});
|
||||||
|
$('#btn-setdraft-stz').on('click', function() {
|
||||||
|
self.showConfirm('Zur\u00fcck auf Entwurf?', 'Stundenzettel wird wieder bearbeitbar.', 'Zur\u00fcck auf Entwurf').then(function(ok) {
|
||||||
|
if (ok) self.setDraftStz();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Leistungen
|
// Leistungen
|
||||||
$panel.find('.btn-edit-leistung').on('click', function(e) {
|
$panel.find('.btn-edit-leistung').on('click', function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -929,6 +955,52 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Menge direkt bearbeiten (Klick auf Zahl)
|
||||||
|
$panel.find('.qty-editable').on('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var $display = $(this);
|
||||||
|
if ($display.find('input').length) return; // Bereits im Edit-Modus
|
||||||
|
var id = $display.data('id');
|
||||||
|
var qty = parseFloat($display.data('qty'));
|
||||||
|
var max = parseFloat($display.data('max'));
|
||||||
|
var qtyStr = qty.toLocaleString('de-DE', {minimumFractionDigits: 0, maximumFractionDigits: 2});
|
||||||
|
$display.html('<input type="text" inputmode="decimal" class="qty-inline-input" value="' + qtyStr + '">');
|
||||||
|
var $input = $display.find('input');
|
||||||
|
$input.focus().select();
|
||||||
|
|
||||||
|
var submitQty = function() {
|
||||||
|
var raw = $input.val().replace(',', '.').trim();
|
||||||
|
var newQty = parseFloat(raw);
|
||||||
|
if (isNaN(newQty) || newQty < 0) {
|
||||||
|
$display.html(self.formatQty(qty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Auf 2 Dezimalstellen runden
|
||||||
|
newQty = Math.round(newQty * 100) / 100;
|
||||||
|
if (newQty === qty) {
|
||||||
|
$display.html(self.formatQty(qty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (max > 0 && newQty > max) {
|
||||||
|
self.showConfirm('Auftragsmenge \u00fcberschritten', 'Auftragsmenge: ' + self.formatQty(max) + '\nNeue Menge: ' + self.formatQty(newQty), 'Trotzdem', 'btn-warning').then(function(ok) {
|
||||||
|
if (ok) {
|
||||||
|
self.updateQty(id, newQty);
|
||||||
|
} else {
|
||||||
|
$display.html(self.formatQty(qty));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.updateQty(id, newQty);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$input.on('blur', submitQty);
|
||||||
|
$input.on('keydown', function(ev) {
|
||||||
|
if (ev.key === 'Enter') { ev.preventDefault(); $input.blur(); }
|
||||||
|
if (ev.key === 'Escape') { $input.off('blur'); $display.html(self.formatQty(qty)); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Produkt loeschen
|
// Produkt loeschen
|
||||||
$panel.find('.btn-delete-product').on('click', function(e) {
|
$panel.find('.btn-delete-product').on('click', function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -1060,7 +1132,7 @@
|
||||||
var activeFilter = self.state.productFilter || 'open';
|
var activeFilter = self.state.productFilter || 'open';
|
||||||
var isDraft = stz && stz.status == 0;
|
var isDraft = stz && stz.status == 0;
|
||||||
|
|
||||||
// Merkzettel-Notizen oben anzeigen (wie stundenzettel_commande.php)
|
// Merkzettel-Notizen oben anzeigen (Abhaken moeglich, neue Notizen nur auf Panel 1)
|
||||||
if (self.data.notes && self.data.notes.length) {
|
if (self.data.notes && self.data.notes.length) {
|
||||||
html += '<div class="merkzettel-box">';
|
html += '<div class="merkzettel-box">';
|
||||||
html += '<div class="merkzettel-box-header">';
|
html += '<div class="merkzettel-box-header">';
|
||||||
|
|
@ -1083,25 +1155,6 @@
|
||||||
html += '</li>';
|
html += '</li>';
|
||||||
});
|
});
|
||||||
html += '</ul>';
|
html += '</ul>';
|
||||||
// Notiz-Input (nur im Entwurf)
|
|
||||||
if (isDraft && canWrite) {
|
|
||||||
html += '<div class="merkzettel-box-add">';
|
|
||||||
html += '<input type="text" id="note-input-p2" placeholder="Neue Notiz...">';
|
|
||||||
html += '<button class="btn btn-primary btn-icon" id="btn-add-note-p2">+</button>';
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
} else if (isDraft && canWrite) {
|
|
||||||
// Leere Box mit nur Input zum Hinzufuegen
|
|
||||||
html += '<div class="merkzettel-box">';
|
|
||||||
html += '<div class="merkzettel-box-header">';
|
|
||||||
html += '<span class="merkzettel-box-icon">📋</span>';
|
|
||||||
html += '<strong>Merkzettel</strong>';
|
|
||||||
html += '</div>';
|
|
||||||
html += '<div class="merkzettel-box-add">';
|
|
||||||
html += '<input type="text" id="note-input-p2" placeholder="Neue Notiz...">';
|
|
||||||
html += '<button class="btn btn-primary btn-icon" id="btn-add-note-p2">+</button>';
|
|
||||||
html += '</div>';
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1295,12 +1348,10 @@
|
||||||
self.showCreateStzDialog();
|
self.showCreateStzDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Merkzettel auf Panel 2: Abhaken + Hinzufuegen
|
// Merkzettel auf Panel 2: Abhaken moeglich, neue Notizen nur auf Panel 1
|
||||||
$panel.find('.merkzettel-box .note-checkbox').on('click', function() {
|
$panel.find('.merkzettel-box .note-checkbox').on('click', function() {
|
||||||
self.toggleNote($(this).data('id'), $(this).data('checked'));
|
self.toggleNote($(this).data('id'), $(this).data('checked'));
|
||||||
});
|
});
|
||||||
$('#btn-add-note-p2').on('click', function() { self.addNoteFromPanel2(); });
|
|
||||||
$('#note-input-p2').on('keypress', function(e) { if (e.which === 13) self.addNoteFromPanel2(); });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderProductCard: function(p, isDraft, canWrite) {
|
renderProductCard: function(p, isDraft, canWrite) {
|
||||||
|
|
@ -1336,7 +1387,7 @@
|
||||||
if (isDraft && canWrite) {
|
if (isDraft && canWrite) {
|
||||||
html += '<div class="qty-controls">';
|
html += '<div class="qty-controls">';
|
||||||
html += '<button class="qty-btn btn-qty-minus" data-id="' + p.id + '" data-qty="' + p.qty_done + '">−</button>';
|
html += '<button class="qty-btn btn-qty-minus" data-id="' + p.id + '" data-qty="' + p.qty_done + '">−</button>';
|
||||||
html += '<span class="qty-display">' + self.formatQty(p.qty_done) + '</span>';
|
html += '<span class="qty-display qty-editable" data-id="' + p.id + '" data-qty="' + p.qty_done + '" data-max="' + (p.qty_original || 9999) + '">' + self.formatQty(p.qty_done) + '</span>';
|
||||||
html += '<button class="qty-btn btn-qty-plus" data-id="' + p.id + '" data-qty="' + p.qty_done + '" data-max="' + (p.qty_original || 9999) + '">+</button>';
|
html += '<button class="qty-btn btn-qty-plus" data-id="' + p.id + '" data-qty="' + p.qty_done + '" data-max="' + (p.qty_original || 9999) + '">+</button>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1698,6 +1749,129 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// AKTIONEN: Freigeben / Wiedereroeffnen
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
validateStz: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Pruefen ob Leistungen vorhanden
|
||||||
|
if (!self.data.leistungen || !self.data.leistungen.length) {
|
||||||
|
// Keine Leistungen - Warnung mit Option eine hinzuzufuegen
|
||||||
|
self.showValidateWarningDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leistungen vorhanden - direkt freigeben
|
||||||
|
self._doValidateStz();
|
||||||
|
},
|
||||||
|
|
||||||
|
showValidateWarningDialog: function() {
|
||||||
|
var self = this;
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
html += '<div class="warning-box mb-12">';
|
||||||
|
html += '<p><strong>⚠ Keine Leistungen erfasst!</strong></p>';
|
||||||
|
html += '<p>F\u00fcr die Rechnungsstellung wird mindestens eine Leistungsposition ben\u00f6tigt.</p>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="form-group"><label>Leistung ausw\u00e4hlen</label>';
|
||||||
|
html += '<select id="dlg-validate-service" style="width:100%;padding:12px;background:var(--colorbackinput);border:1px solid var(--colorborder);border-radius:8px;color:var(--colortext);font-size:16px;min-height:48px;">';
|
||||||
|
html += '<option value="">-- Ohne Leistung freigeben --</option>';
|
||||||
|
|
||||||
|
// Standard-Service des Kunden vorselektieren
|
||||||
|
if (self.state.defaultServiceId > 0) {
|
||||||
|
html += '<option value="' + self.state.defaultServiceId + '" selected>' + self.escHtml(self.state.defaultServiceLabel) + ' (Standard)</option>';
|
||||||
|
}
|
||||||
|
html += '</select></div>';
|
||||||
|
|
||||||
|
// Dienste laden
|
||||||
|
self.api('get_services', {}).then(function(res) {
|
||||||
|
if (res.success && res.services) {
|
||||||
|
var $select = $('#dlg-validate-service');
|
||||||
|
res.services.forEach(function(s) {
|
||||||
|
// Standard-Service nicht doppelt anzeigen
|
||||||
|
if (s.id == self.state.defaultServiceId) return;
|
||||||
|
$select.append('<option value="' + s.id + '">' + self.escHtml(s.ref + ' - ' + s.label) + '</option>');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var footer = '';
|
||||||
|
footer += '<button class="btn btn-primary w-full mb-8" id="dlg-validate-with-leistung">Mit Leistung freigeben</button>';
|
||||||
|
footer += '<button class="btn btn-ghost w-full" id="dlg-validate-without">Ohne Leistung freigeben</button>';
|
||||||
|
|
||||||
|
self.openBottomSheet('Stundenzettel freigeben', html, footer);
|
||||||
|
|
||||||
|
$('#dlg-validate-with-leistung').on('click', function() {
|
||||||
|
var serviceId = $('#dlg-validate-service').val();
|
||||||
|
if (!serviceId) {
|
||||||
|
self.showToast('Bitte eine Leistung ausw\u00e4hlen', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.closeBottomSheet();
|
||||||
|
// Leistung mit heutigem Datum und Standard-Zeiten anlegen, dann freigeben
|
||||||
|
var stz = self.data.stz;
|
||||||
|
var data = {
|
||||||
|
stz_id: self.state.stzId,
|
||||||
|
date: stz.date_iso || new Date().toISOString().substr(0, 10),
|
||||||
|
time_start: '08:00',
|
||||||
|
time_end: '16:00',
|
||||||
|
description: '',
|
||||||
|
fk_product: serviceId
|
||||||
|
};
|
||||||
|
self.showLoading();
|
||||||
|
self.api('add_leistung', data).then(function(res) {
|
||||||
|
if (res.success) {
|
||||||
|
self._doValidateStz();
|
||||||
|
} else {
|
||||||
|
self.hideLoading();
|
||||||
|
self.showToast(res.error || 'Fehler', 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#dlg-validate-without').on('click', function() {
|
||||||
|
self.closeBottomSheet();
|
||||||
|
self._doValidateStz();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_doValidateStz: function() {
|
||||||
|
var self = this;
|
||||||
|
self.showLoading();
|
||||||
|
self.api('validate_stz', {stz_id: self.state.stzId}).then(function(res) {
|
||||||
|
self.hideLoading();
|
||||||
|
if (res.success) {
|
||||||
|
self.showToast('Stundenzettel freigegeben', 'success');
|
||||||
|
self.reloadData();
|
||||||
|
} else {
|
||||||
|
self.showToast(res.error || 'Fehler beim Freigeben', 'error');
|
||||||
|
}
|
||||||
|
}).catch(function() {
|
||||||
|
self.hideLoading();
|
||||||
|
self.showToast('Verbindungsfehler', 'error');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setDraftStz: function() {
|
||||||
|
var self = this;
|
||||||
|
self.showLoading();
|
||||||
|
self.api('setdraft_stz', {stz_id: self.state.stzId}).then(function(res) {
|
||||||
|
self.hideLoading();
|
||||||
|
if (res.success) {
|
||||||
|
self.showToast('Stundenzettel zur\u00fcck auf Entwurf', 'success');
|
||||||
|
self.reloadData();
|
||||||
|
} else {
|
||||||
|
self.showToast(res.error || 'Fehler', 'error');
|
||||||
|
}
|
||||||
|
}).catch(function() {
|
||||||
|
self.hideLoading();
|
||||||
|
self.showToast('Verbindungsfehler', 'error');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// AKTIONEN: Notizen
|
// AKTIONEN: Notizen
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -1717,21 +1891,6 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addNoteFromPanel2: function() {
|
|
||||||
var self = this;
|
|
||||||
var text = $('#note-input-p2').val().trim();
|
|
||||||
if (!text) return;
|
|
||||||
|
|
||||||
self.api('add_note', {stz_id: self.state.stzId, note: text}).then(function(res) {
|
|
||||||
if (res.success) {
|
|
||||||
$('#note-input-p2').val('');
|
|
||||||
self.reloadData();
|
|
||||||
} else {
|
|
||||||
self.showToast(res.error || 'Fehler', 'error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleNote: function(id, checked) {
|
toggleNote: function(id, checked) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.api('toggle_note', {note_id: id, checked: checked, stz_id: self.state.stzId}).then(function(res) {
|
self.api('toggle_note', {note_id: id, checked: checked, stz_id: self.state.stzId}).then(function(res) {
|
||||||
|
|
|
||||||
4
pwa.php
4
pwa.php
|
|
@ -38,7 +38,7 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#4390dc');
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="img/icon-192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="img/icon-192.png">
|
||||||
<link rel="apple-touch-icon" href="img/icon-192.png">
|
<link rel="apple-touch-icon" href="img/icon-192.png">
|
||||||
<link rel="stylesheet" href="css/pwa.css?v=2.8">
|
<link rel="stylesheet" href="css/pwa.css?v=2.9">
|
||||||
<style>:root { --primary: <?php echo htmlspecialchars($themeColor); ?>; }</style>
|
<style>:root { --primary: <?php echo htmlspecialchars($themeColor); ?>; }</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -160,7 +160,7 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#4390dc');
|
||||||
authUrl: '<?php echo dol_buildpath('/stundenzettel/ajax/pwa_auth.php', 1); ?>'
|
authUrl: '<?php echo dol_buildpath('/stundenzettel/ajax/pwa_auth.php', 1); ?>'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="js/pwa.js?v=2.8"></script>
|
<script src="js/pwa.js?v=2.9"></script>
|
||||||
|
|
||||||
<!-- Service Worker Registration -->
|
<!-- Service Worker Registration -->
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue