Nerviges Aufträge suchen Problem >Lösung

Moin,

anbei mal ein Code, der etwas abstellt was mich bei plenty nervt. Es geht um die Suche in den Aufträgen. Man ist so getriggert, das man etwas eingibt und enter drückt. Nur leider erwartet plenty eine Auswahl.

Bei uns ist das häufigste Auftrags ID oder Name. Ich habe die Reihenfolge so eingestellt, das erst die ID kommt. Wenn man nun aber einen Namen eingibt und enter drückt steht da ID und der Name > Ergebniss keine anzeige. Also löschen, Name eingeben, enter, ach mist schon wieder ID. Also nochmal und diesmal dann mit der Maus Kundendaten ausgewählt. Tausende nervige klicks Pro Jahr.

Wenn das auch stört. Hier der passende code.

Er bewirkt das wenn ich eine Zahl eingebe, die ID oben bleibt (also bewirkt er hier eigentlich gar nichts :wink:

Wenn ich nun aber den Namen eingebe entfernt der Code die ID und Aufrtragsdaten rutscht nach oben und ich kann einfach enter drücken.

Also wen das Nervt hier der Code.

Noch ein paar wichtige Infos. Ich nutze es in Chrome, keine ahnung ob es so auch einem anderen Browser geht. Ich übernehme keine Haftung für irgendwas. Ihr macht das auf euer Risiko. Ihr könnte Code gerne von Chatgpt checken lassen.

Ihr benötigt Tempermonkey und diesen Code:

(diese Zeile // @match ://p7615.my.plentysystems.com/ müsst Ihr auf euch anpassen)

// ==UserScript==
// @name Plenty: Auftrags-ID & Artikel-ID entfernen + Vorauswahl (prod)
// @namespace http://tampermonkey.net/
// @version 4.5
// @description Entfernt „Auftrags-ID“ & „Artikel-ID“ aus der Autocomplete-Liste, markiert erste gültige Option visuell und wählt sie erst bei Enter. Produktiv, keine Debug-Logs.
// @match ://p7615.my.plentysystems.com/
// @run-at document-idle
// @grant none
// ==/UserScript==

(function () {
‚use strict‘;

const REMOVE_KEYWORDS = ['Auftrags-ID', 'Artikel-ID'];
const DEBOUNCE_MS = 160;
let searchField = null;
let listenersAttached = false;
let processTimer = null;

function isVisible(el) {
    if (!el) return false;
    const r = el.getBoundingClientRect();
    return r.width > 0 && r.height > 0;
}

function findSearchField() {
    const active = document.activeElement;
    if (active && active.tagName && active.tagName.toLowerCase() === 'input' && isVisible(active)) {
        return active;
    }
    const sel = 'input[type="text"], input[type="search"], input[placeholder], input[aria-label]';
    const inputs = Array.from(document.querySelectorAll(sel)).filter(isVisible);
    if (inputs.length) {
        inputs.sort((a,b) => (b.value||'').length - (a.value||'').length);
        return inputs[0];
    }
    return null;
}

function collectOptionElements() {
    let opts = [];
    try { opts = opts.concat(Array.from(document.querySelectorAll('mat-option'))); } catch (e) {}
    try {
        const overlays = Array.from(document.querySelectorAll('.cdk-overlay-container, .cdk-overlay-pane'));
        overlays.forEach(container => {
            try { opts = opts.concat(Array.from(container.querySelectorAll('mat-option, [role="option"], li, .mat-mdc-option'))); } catch (e) {}
        });
    } catch (e) {}
    const uniq = Array.from(new Set(opts)).filter(el => el && isVisible(el));
    return uniq;
}

function hideOption(opt) {
    try {
        opt.setAttribute('aria-hidden', 'true');
        opt.hidden = true;
        opt.style.display = 'none';
    } catch (e) { /* still continue silently */ }
}

function showPreselect(el) {
    document.querySelectorAll('.tm-preselect-outline').forEach(x => {
        x.classList.remove('tm-preselect-outline');
        x.style.outline = '';
        x.style.backgroundColor = '';
    });
    if (!el) return;
    if (!el.id) el.id = `tm-opt-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;
    el.classList.add('tm-preselect-outline');
    el.style.outline = '3px solid rgba(0,200,120,0.95)';
    el.style.backgroundColor = 'rgba(0,200,120,0.06)';
}

function processDropdown() {
    if (processTimer) { clearTimeout(processTimer); processTimer = null; }
    if (!searchField) searchField = findSearchField();
    const searchVal = searchField ? (searchField.value || '').trim() : '';
    const containsLetters = /[a-zA-ZÄÖÜäöüß]/.test(searchVal);
    const opts = collectOptionElements();
    if (!opts.length) return;

    let firstValid = null;
    for (const o of opts) {
        const txt = (o.textContent || '').trim();
        if (containsLetters && REMOVE_KEYWORDS.some(k => txt.includes(k))) {
            hideOption(o);
        } else {
            if (!firstValid) firstValid = o;
        }
    }
    if (firstValid) {
        showPreselect(firstValid);
        try { if (searchField) searchField.setAttribute('aria-activedescendant', firstValid.id); } catch (e) {}
    } else {
        showPreselect(null);
    }
}

function scheduleProcess() {
    if (processTimer) clearTimeout(processTimer);
    processTimer = setTimeout(processDropdown, DEBOUNCE_MS);
}

function onEnterHandler(ev) {
    if (ev.key === 'Enter') {
        ev.stopPropagation();
        ev.stopImmediatePropagation();
        ev.preventDefault();
        const active = document.querySelector('.tm-preselect-outline') || document.querySelector('.mat-mdc-option-active');
        if (active) {
            active.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
            active.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
            active.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
        }
    }
}

function attachListenersOnce() {
    if (listenersAttached) return;
    listenersAttached = true;

    window.addEventListener('keydown', onEnterHandler, true);

    const inputObserver = new MutationObserver(() => {
        const f = findSearchField();
        if (f && f !== searchField) {
            searchField = f;
            if (!f.__tm_input_hooked) {
                f.__tm_input_hooked = true;
                f.addEventListener('input', scheduleProcess, { passive: true });
                f.addEventListener('keyup', scheduleProcess, { passive: true });
            }
        }
    });
    inputObserver.observe(document.body, { childList: true, subtree: true });

    const overlayObserver = new MutationObserver((mutations) => {
        for (const m of mutations) {
            if (m.addedNodes.length || m.removedNodes.length) {
                scheduleProcess();
                break;
            }
        }
    });
    overlayObserver.observe(document.body, { childList: true, subtree: true });
}

// Startup
setTimeout(() => {
    try {
        attachListenersOnce();
        searchField = findSearchField();
        if (searchField && !searchField.__tm_input_hooked) {
            searchField.__tm_input_hooked = true;
            searchField.addEventListener('input', scheduleProcess, { passive: true });
            searchField.addEventListener('keyup', scheduleProcess, { passive: true });
        }
        scheduleProcess();
    } catch (e) { /* silent fail in prod */ }
}, 900);

})();

So sieht es dann in Tempermonkey aus:

Bitte habt verständniss, dass ich keinen Support dafür bieten kann. Ich nutze den Code jetzt bestimmt ein Jahr. Vor einigen Wochen musste ich Ihn anpassen. Weil plenty was geändert hatte.

Der Code kann sicher noch indivualsiert werden (e-mail) aber für uns reicht er so.

Viele Grüße

Marco

Cool! Werde ich nächste Woche testen wenn ich wieder da bin, danke :heart_hands:

Kannst du deinen Code bitte noch korrekt formatieren fürs Copypaste? Einfach vor und nach dem Code drei Accents, jeweils in eigener Zeile :nerd_face:

„```“
Ohne die Anführungszeichen

Hm, theoretisch könnte man das sogar noch optimieren und statt ausblenden die Reihenfolge anpassen.

Wenn man sich dann noch rauszieht, welche ui man gerade offen hat, könnte man das dann auch noch dynamisch für alle Seiten umsetzen, die ähnliche suchen nutzen.

Na toll wieder eine Beschäftigung für den Feierabend.

Boah, das geht mir ja schon seit Einführung der neuen UI auf den Sack!

Es sollte auch als Bookmark funktionieren:

javascript:(function()%7Bconst%20REMOVE_KEYWORDS%20%3D%20%5B'Auftrags-ID'%2C%20'Artikel-ID'%5D%3B%0Aconst%20DEBOUNCE_MS%20%3D%20160%3B%0Alet%20searchField%20%3D%20null%3B%0Alet%20listenersAttached%20%3D%20false%3B%0Alet%20processTimer%20%3D%20null%3B%0A%0Afunction%20isVisible(el)%20%7B%0A%20%20%20%20if%20(!el)%20return%20false%3B%0A%20%20%20%20const%20r%20%3D%20el.getBoundingClientRect()%3B%0A%20%20%20%20return%20r.width%20%3E%200%20%26%26%20r.height%20%3E%200%3B%0A%7D%0A%0Afunction%20findSearchField()%20%7B%0A%20%20%20%20const%20active%20%3D%20document.activeElement%3B%0A%20%20%20%20if%20(active%20%26%26%20active.tagName%20%26%26%20active.tagName.toLowerCase()%20%3D%3D%3D%20'input'%20%26%26%20isVisible(active))%20%7B%0A%20%20%20%20%20%20%20%20return%20active%3B%0A%20%20%20%20%7D%0A%20%20%20%20const%20sel%20%3D%20'input%5Btype%3D%22text%22%5D%2C%20input%5Btype%3D%22search%22%5D%2C%20input%5Bplaceholder%5D%2C%20input%5Baria-label%5D'%3B%0A%20%20%20%20const%20inputs%20%3D%20Array.from(document.querySelectorAll(sel)).filter(isVisible)%3B%0A%20%20%20%20if%20(inputs.length)%20%7B%0A%20%20%20%20%20%20%20%20inputs.sort((a%2Cb)%20%3D%3E%20(b.value%7C%7C'').length%20-%20(a.value%7C%7C'').length)%3B%0A%20%20%20%20%20%20%20%20return%20inputs%5B0%5D%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20null%3B%0A%7D%0A%0Afunction%20collectOptionElements()%20%7B%0A%20%20%20%20let%20opts%20%3D%20%5B%5D%3B%0A%20%20%20%20try%20%7B%20opts%20%3D%20opts.concat(Array.from(document.querySelectorAll('mat-option')))%3B%20%7D%20catch%20(e)%20%7B%7D%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20const%20overlays%20%3D%20Array.from(document.querySelectorAll('.cdk-overlay-container%2C%20.cdk-overlay-pane'))%3B%0A%20%20%20%20%20%20%20%20overlays.forEach(container%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%20opts%20%3D%20opts.concat(Array.from(container.querySelectorAll('mat-option%2C%20%5Brole%3D%22option%22%5D%2C%20li%2C%20.mat-mdc-option')))%3B%20%7D%20catch%20(e)%20%7B%7D%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%7D%20catch%20(e)%20%7B%7D%0A%20%20%20%20const%20uniq%20%3D%20Array.from(new%20Set(opts)).filter(el%20%3D%3E%20el%20%26%26%20isVisible(el))%3B%0A%20%20%20%20return%20uniq%3B%0A%7D%0A%0Afunction%20hideOption(opt)%20%7B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20opt.setAttribute('aria-hidden'%2C%20'true')%3B%0A%20%20%20%20%20%20%20%20opt.hidden%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20opt.style.display%20%3D%20'none'%3B%0A%20%20%20%20%7D%20catch%20(e)%20%7B%20%2F*%20still%20continue%20silently%20*%2F%20%7D%0A%7D%0A%0Afunction%20showPreselect(el)%20%7B%0A%20%20%20%20document.querySelectorAll('.tm-preselect-outline').forEach(x%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20x.classList.remove('tm-preselect-outline')%3B%0A%20%20%20%20%20%20%20%20x.style.outline%20%3D%20''%3B%0A%20%20%20%20%20%20%20%20x.style.backgroundColor%20%3D%20''%3B%0A%20%20%20%20%7D)%3B%0A%20%20%20%20if%20(!el)%20return%3B%0A%20%20%20%20if%20(!el.id)%20el.id%20%3D%20%60tm-opt-%24%7BDate.now()%7D-%24%7BMath.random().toString(36).slice(2%2C8)%7D%60%3B%0A%20%20%20%20el.classList.add('tm-preselect-outline')%3B%0A%20%20%20%20el.style.outline%20%3D%20'3px%20solid%20rgba(0%2C200%2C120%2C0.95)'%3B%0A%20%20%20%20el.style.backgroundColor%20%3D%20'rgba(0%2C200%2C120%2C0.06)'%3B%0A%7D%0A%0Afunction%20processDropdown()%20%7B%0A%20%20%20%20if%20(processTimer)%20%7B%20clearTimeout(processTimer)%3B%20processTimer%20%3D%20null%3B%20%7D%0A%20%20%20%20if%20(!searchField)%20searchField%20%3D%20findSearchField()%3B%0A%20%20%20%20const%20searchVal%20%3D%20searchField%20%3F%20(searchField.value%20%7C%7C%20'').trim()%20%3A%20''%3B%0A%20%20%20%20const%20containsLetters%20%3D%20%2F%5Ba-zA-Z%C3%84%C3%96%C3%9C%C3%A4%C3%B6%C3%BC%C3%9F%5D%2F.test(searchVal)%3B%0A%20%20%20%20const%20opts%20%3D%20collectOptionElements()%3B%0A%20%20%20%20if%20(!opts.length)%20return%3B%0A%0A%20%20%20%20let%20firstValid%20%3D%20null%3B%0A%20%20%20%20for%20(const%20o%20of%20opts)%20%7B%0A%20%20%20%20%20%20%20%20const%20txt%20%3D%20(o.textContent%20%7C%7C%20'').trim()%3B%0A%20%20%20%20%20%20%20%20if%20(containsLetters%20%26%26%20REMOVE_KEYWORDS.some(k%20%3D%3E%20txt.includes(k)))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20hideOption(o)%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!firstValid)%20firstValid%20%3D%20o%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20if%20(firstValid)%20%7B%0A%20%20%20%20%20%20%20%20showPreselect(firstValid)%3B%0A%20%20%20%20%20%20%20%20try%20%7B%20if%20(searchField)%20searchField.setAttribute('aria-activedescendant'%2C%20firstValid.id)%3B%20%7D%20catch%20(e)%20%7B%7D%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20showPreselect(null)%3B%0A%20%20%20%20%7D%0A%7D%0A%0Afunction%20scheduleProcess()%20%7B%0A%20%20%20%20if%20(processTimer)%20clearTimeout(processTimer)%3B%0A%20%20%20%20processTimer%20%3D%20setTimeout(processDropdown%2C%20DEBOUNCE_MS)%3B%0A%7D%0A%0Afunction%20onEnterHandler(ev)%20%7B%0A%20%20%20%20if%20(ev.key%20%3D%3D%3D%20'Enter')%20%7B%0A%20%20%20%20%20%20%20%20ev.stopPropagation()%3B%0A%20%20%20%20%20%20%20%20ev.stopImmediatePropagation()%3B%0A%20%20%20%20%20%20%20%20ev.preventDefault()%3B%0A%20%20%20%20%20%20%20%20const%20active%20%3D%20document.querySelector('.tm-preselect-outline')%20%7C%7C%20document.querySelector('.mat-mdc-option-active')%3B%0A%20%20%20%20%20%20%20%20if%20(active)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20active.dispatchEvent(new%20MouseEvent('mousedown'%2C%20%7B%20bubbles%3A%20true%2C%20cancelable%3A%20true%20%7D))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20active.dispatchEvent(new%20MouseEvent('mouseup'%2C%20%7B%20bubbles%3A%20true%2C%20cancelable%3A%20true%20%7D))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20active.dispatchEvent(new%20MouseEvent('click'%2C%20%7B%20bubbles%3A%20true%2C%20cancelable%3A%20true%20%7D))%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%0Afunction%20attachListenersOnce()%20%7B%0A%20%20%20%20if%20(listenersAttached)%20return%3B%0A%20%20%20%20listenersAttached%20%3D%20true%3B%0A%0A%20%20%20%20window.addEventListener('keydown'%2C%20onEnterHandler%2C%20true)%3B%0A%0A%20%20%20%20const%20inputObserver%20%3D%20new%20MutationObserver(()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20f%20%3D%20findSearchField()%3B%0A%20%20%20%20%20%20%20%20if%20(f%20%26%26%20f%20!%3D%3D%20searchField)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20searchField%20%3D%20f%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!f.__tm_input_hooked)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f.__tm_input_hooked%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f.addEventListener('input'%2C%20scheduleProcess%2C%20%7B%20passive%3A%20true%20%7D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f.addEventListener('keyup'%2C%20scheduleProcess%2C%20%7B%20passive%3A%20true%20%7D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%20%20inputObserver.observe(document.body%2C%20%7B%20childList%3A%20true%2C%20subtree%3A%20true%20%7D)%3B%0A%0A%20%20%20%20const%20overlayObserver%20%3D%20new%20MutationObserver((mutations)%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20for%20(const%20m%20of%20mutations)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(m.addedNodes.length%20%7C%7C%20m.removedNodes.length)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scheduleProcess()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%20%20overlayObserver.observe(document.body%2C%20%7B%20childList%3A%20true%2C%20subtree%3A%20true%20%7D)%3B%0A%7D%0A%0A%2F%2F%20Startup%0AsetTimeout(()%20%3D%3E%20%7B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20attachListenersOnce()%3B%0A%20%20%20%20%20%20%20%20searchField%20%3D%20findSearchField()%3B%0A%20%20%20%20%20%20%20%20if%20(searchField%20%26%26%20!searchField.__tm_input_hooked)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20searchField.__tm_input_hooked%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20searchField.addEventListener('input'%2C%20scheduleProcess%2C%20%7B%20passive%3A%20true%20%7D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20searchField.addEventListener('keyup'%2C%20scheduleProcess%2C%20%7B%20passive%3A%20true%20%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20scheduleProcess()%3B%0A%20%20%20%20%7D%20catch%20(e)%20%7B%20%2F*%20silent%20fail%20in%20prod%20*%2F%20%7D%0A%7D%2C%20900)%3B%7D)()%3B

Einfach ein neues Lesezeichen erstellen und den Code dort als Link reinkopieren. Ich habe es aber nicht getestet, da ich ich im Backend nicht damit arbeite, aber prinzipiell funktioniert das so.

edit: hiermit z.B. kann man das Script umwandeln

Danke. Ist je toll das es solche Möglichkeiten gibt.
Damit das funktioniert, muss ich es wo eingeben?

Plenty Auftrags-ID & Artikel-ID entfernen Vorauswahl (prod).txt (6,3 KB)

So besser?

Schau mal Tampermonkey ist ein chromeplugin

Hab noch mehr plenty anpassungen. schaue mal das ich sie ins Forum kriege

@mr.blonde
Danke Dir

hat sich erledigt, Plenty hat es nach zwei Jahren dann doch selber geschafft :slight_smile: