Refactor code structure and remove redundant sections for improved readability and maintainability

This commit is contained in:
Stefan
2026-07-10 21:03:55 +02:00
parent 42997c4f49
commit 83d1c28b29
18 changed files with 696 additions and 86 deletions
+32 -7
View File
@@ -130,7 +130,7 @@ $inv.d = {
let l = $inv.d.layout(); l.aC('freeze');
$ocms.postXT({
url: $ocms.url('inv/dopen'), data: { payload: JSON.stringify(payload) }, success: (r) => {
$inv.d.tbl().data('dtoken', r.token).data('dver', r.version).data('dhashes', $inv.d.hashes());
$inv.d.tbl().data('dtoken', r.token).data('dver', r.version).data('dhashes', $inv.d.hashes()).data('dorder', $inv.d.order());
$fis.draft.bind(r.token, {
onReady: () => $inv.d.refresh(),
onExpiring: (s) => $inv.d.warnExpiry(s),
@@ -155,6 +155,17 @@ $inv.d = {
tbl.data('dver', state.version).data('serverSums', state.sums);
$inv.d.footer(tbl, state.sums || {}, state.admin || {});
$inv.d.validation(state.validation || []);
$inv.d.applyPositions(tbl, state.req || []);
},
/* Push the server's authoritative position numbers back onto the rendered rows so the online
editor and the PDF preview always agree (the server numbers priced lines continuously; the
browser must not keep its own numbering). Only the position cell is touched — no re-render. */
applyPositions: function (tbl, req) {
(req || []).forEach((b) => (b && b.itm || []).forEach((co) => {
if (!co || (co.id || '') === '') { return; }
let cell = tbl.find('#itm' + co.id + ' td.keep').first();
if (cell.length) { cell.text(co.p != null ? co.p : ''); }
}));
},
/* Send one change to the server; the draftReady signal and this success both refresh. */
sync: function (delta) {
@@ -166,16 +177,23 @@ $inv.d = {
error: (xhr) => { $inv.d.layout().rC('freeze'); if (xhr && xhr.status === 410) { $inv.d.closed('expired'); } }
});
},
/* The current section id sequence (used to detect a reorder that changes no block content). */
order: function () { return (($inv.d.tbl().data('bai')) || []).map((b) => (b.Id || '').toString()); },
/* Diff the freshly-rebuilt blocks against the last-synced state and send only the
changed/removed blocks as granular block.replace / block.remove deltas. */
changed/removed blocks as granular block.replace / block.remove deltas. A pure section
reorder (same blocks, new sequence) changes no block hash, so it is sent separately as a
block.order delta; the server reorders the cache, renumbers positions and pushes them back. */
syncChanged: function (tbl) {
if (($inv.d.token()) === '') { return; }
let bai = tbl.data('bai') || [], prev = tbl.data('dhashes') || {}, next = {}, changed = [], removed = [];
$.each(bai, (i, b) => { let id = (b.Id || '').toString(), h = JSON.stringify(b); next[id] = h; if (prev[id] !== h) { changed.push(b); } });
$.each(prev, (id) => { if (next[id] === undefined) { removed.push(id); } });
tbl.data('dhashes', next);
let order = $inv.d.order(), prevOrder = tbl.data('dorder') || [];
tbl.data('dhashes', next).data('dorder', order);
changed.forEach((b) => $inv.d.sync({ Target: 'block.replace', Ref: (b.Id || '').toString(), Value: b }));
removed.forEach((id) => $inv.d.sync({ Target: 'block.remove', Ref: id }));
let sameSet = prevOrder.length === order.length && prevOrder.slice().sort().join(',') === order.slice().sort().join(',');
if (sameSet && prevOrder.join(',') !== order.join(',')) { $inv.d.sync({ Target: 'block.order', Value: order }); }
},
/* Map an inline recipient field to its delta target and send it. */
syncField: function (nme, val) {
@@ -672,9 +690,12 @@ $inv.cSt = function (data) {
};
$inv.eHtml = function (ev) {
let t = $(this), frmct = ev.data instanceof jQuery ? ev.data : ev.data.t;
/* invoiceemail must stay plain text — using the TinyMCE/html editor here used to wrap the
address in <p> tags, which then got posted and persisted verbatim into SendToEmail. */
let isPlainText = ev.data.nme === 'invoiceemail';
/* Single-line fields must stay plain text — the TinyMCE/html editor wraps the value in <p>
tags, which used to get posted and persisted verbatim (e.g. <p>18.06.2026</p> in the
Leistungsdatum). The backend sanitises HTML too (single source of truth, ADR 0006), but
keeping these plain here avoids the UI briefly holding the wrapped value. Multi-line fields
(invoiceaddress, loc) stay HTML-capable and are normalised to newlines server-side. */
let isPlainText = ['invoiceemail', 'provisionperiod', 'invoicetitle'].includes(ev.data.nme);
let flds = isPlainText
? [{ name: 'txt', label: 'Text', type: 'text', value: frmct.text() }]
: [{ name: 'txt', label: 'Text', type: 'html', value: frmct.html(), tinymce: true, attr: { style: 'height: 300px' } }];
@@ -777,7 +798,11 @@ $inv.eRw = function(row, dta, flds) {
}, typedvalues: true
});
};
$inv.bdysort = (i, e) => { $(e).Sortable({ dragItem: false, dragHandleClass: 'ico', parentident: 'tr', swapdone: (p1, p2, i1, i2) => { $inv.t_fds_inv(); } }) }
/* Reorder items via drag. The DOM swap happens inside the Sortable during the drag; we commit
the new order once, reliably, on drop (onend) — that recomputes positions/totals and pushes the
changed block(s) to the backend session (t_fds_inv -> syncChanged). Committing on drop (rather
than on every mid-drag hover-swap) avoids rebuilding the row that is currently being dragged. */
$inv.bdysort = (i, e) => { $(e).Sortable({ dragItem: false, dragHandleClass: 'ico', parentident: 'tr', onend: () => { $inv.t_fds_inv(); } }) }
$inv.rrw = function () {
let rw = $(this), dta = rw.data(), co = {}, ph = rw.is('.placeholder'), hn = rw.is('.hidenote');
let oHtml = (e) => $$.d().append(e).html();
+10 -2
View File
@@ -63,7 +63,8 @@ if (!Element.prototype.closest) {
this._dragging = false;
this._dragHandleClass = this._options.dragHandleClass || '';
this._parentident = this._options.parentident || '';
this._swapdone = typeof this._options.swapdone === "function" ? this._options._swapdone : null;
this._swapdone = typeof this._options.swapdone === "function" ? this._options.swapdone : null;
this._onend = typeof this._options.onend === "function" ? this._options.onend : null;
this._container.setAttribute("data-is-sortable", 1);
this._container.classList.add("sortable");
@@ -213,10 +214,17 @@ if (!Element.prototype.closest) {
}
},
// on item release/drop
// on item release/drop
_onRelease: function (e) {
// Was THIS list mid-drag? (mouseup fires on every instance's window listener.)
var wasDragging = this._dragging === true && this._clickItem !== null;
this._dragging = false;
this._trashDragItem();
// Fire a single "drag finished" callback so callers can commit the new order once,
// reliably, on drop — rather than relying on the per-hover _swapdone during the drag.
if (wasDragging && typeof this._onend === 'function') {
this._onend();
}
},
// on item drag/move