tampermonkey/idea/idea-purchase.user.js
2025-12-15 19:42:21 +01:00

302 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==UserScript==
// @name IDEAERP - Zamówienia Zakupu
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Kopiowanie elementów na stronach zamówień zakupu - kliknij aby skopiować
// @match https://emma.ideaerp.pl/web*
// @icon https://emma.ideaerp.pl/web/image/res.company/1/favicon/
// @downloadURL https://n8n.emma.net.pl/webhook/purchase
// @updateURL https://n8n.emma.net.pl/webhook/purchase
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// ==/UserScript==
/* ========================OPIS==============================
WERSJA 1.0 - Kopiowanie elementów zamówień zakupu
DZIAŁANIE SKRYPTU:
- Kopiowanie nazwy zamówienia - kliknij na nazwę zamówienia (h1) aby skopiować
- Kopiowanie produktów z kolumny "Produkt" - kliknij na produkt aby skopiować SKU lub pełną nazwę
- Tryb kopiowania SKU/pełna nazwa - przełączalny przycisk
- Obsługa błędów w funkcjach kopiowania zgodnie z zasadami .rules
- Spójny styl wizualny ikon kopiowania (pomarańczowy kolor #ff9800)
AUTOMATYCZNE AKTUALIZACJE:
- Tampermonkey automatycznie sprawdza dostępność nowych wersji
- Jednoklikowa aktualizacja bez konieczności ręcznego pobierania
- Zachowanie ustawień użytkownika podczas aktualizacji
- Kompatybilność z systemem n8n do zarządzania wersjami
CHANGELOG:
v1.0 (2024) - PIERWSZA WERSJA SKRYPTU ZAKUPOWEGO
- Utworzono dedykowany skrypt dla zamówień zakupu (purchase.order)
- Dodano kopiowanie nazwy zamówienia (h1) z ikoną ⧉
- Dodano kopiowanie produktów z kolumny "Produkt" z ikoną ⧉
- Dodano tryb kopiowania SKU/pełna nazwa z przyciskiem przełączania
- Dodano funkcję extractSKU() do wyodrębniania SKU z nawiasów kwadratowych
- Dodano style CSS dla elementów h1 i td z ikonami kopiowania
- Dodano funkcje makeOrderNameCopyable() i makeProductCellCopyable()
- Obsługa błędów w funkcjach kopiowania zgodnie z zasadami .rules
- Spójny styl wizualny ikon kopiowania (pomarańczowy kolor #ff9800)
- Podwyższenie numeru wersji do 1.0
AUTOR: Adam Grodecki
DATA: 2024
LICENCJA: Własność EMMA SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ
================================================================ */
(function() {
'use strict';
// ——— Stałe ———
const COPY_MODE_KEY = 'tm_purchase_copy_mode';
const INIT_FLAG = 'tm_purchase_init_flag';
// ——— Style CSS ———
GM_addStyle(`
/* Kopiowanie nazwy zamówienia ikona dla h1 */
h1.tm-copyable { cursor: copy; }
h1.tm-copyable::after {
content: '⧉'; /* symbol kopiowania */
display: inline-block; /* w tej samej linii co tekst */
margin-left: 8px; /* odstęp od tekstu */
font-size: 14px; /* większy rozmiar dla h1 */
line-height: 1;
color: #ff9800; /* pomarańczowy */
opacity: .75; /* widoczna także bez hovera */
vertical-align: baseline;
pointer-events: none;
transition: opacity .15s ease;
}
h1.tm-copyable:hover::after { opacity: 1; }
h1.tm-copyable.tm-copied {
outline: 2px solid #ff9800;
background-image: linear-gradient(90deg, rgba(255,152,0,.12), rgba(255,152,0,0));
}
/* Kopiowanie produktów ikona tuż za tekstem (inline), mniejsza, pomarańczowa */
td.tm-copyable { cursor: copy; }
td.tm-copyable::after {
content: '⧉'; /* symbol kopiowania w kolorze CSS */
display: inline-block; /* w tej samej linii co tekst */
margin-left: 6px; /* mały odstęp od tekstu */
font-size: 12px; /* mniejszy rozmiar */
line-height: 1;
color: #ff9800; /* pomarańczowy */
opacity: .75; /* widoczna także bez hovera */
vertical-align: baseline;
pointer-events: none;
transition: opacity .15s ease;
}
td.tm-copyable:hover::after { opacity: 1; }
td.tm-copyable.tm-copied {
outline: 2px solid #ff9800;
background-image: linear-gradient(90deg, rgba(255,152,0,.12), rgba(255,152,0,0));
}
/* Przycisk przełączania trybu kopiowania w zakładkach */
.tm-copy-mode-btn {
background: #ff9800 !important;
color: white !important;
border: none !important;
padding: 8px 12px !important;
border-radius: 4px !important;
font-size: 12px !important;
cursor: pointer !important;
margin-left: 10px !important;
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
}
.tm-copy-mode-btn:hover {
background: #f57c00 !important;
}
`);
// ——— Pomocnicze ———
function isPurchaseOrderForm() {
const params = new URLSearchParams((location.hash || '').replace(/^#/, ''));
const model = params.get('model');
const viewType = params.get('view_type');
return model === 'purchase.order' && viewType === 'form';
}
async function tmCopyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
}
catch {
const ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
}
// ——— Funkcja wyodrębniania SKU z nawiasów kwadratowych ———
function extractSKU(productText) {
const match = productText.match(/\[([^\]]+)\]/);
return match ? match[1] : productText;
}
// ——— Kopiowanie nazwy zamówienia ———
function makeOrderNameCopyable(h1Element) {
if (!h1Element || h1Element.dataset.tmOrderNameCopyable) return;
h1Element.dataset.tmOrderNameCopyable = '1';
h1Element.classList.add('tm-copyable');
h1Element.style.cursor = 'pointer';
h1Element.title = 'Kliknij aby skopiować nazwę zamówienia';
h1Element.addEventListener('click', async (e) => {
e.stopPropagation();
const orderName = (h1Element.textContent || '').trim();
if (!orderName) return;
try {
await tmCopyToClipboard(orderName);
const old = h1Element.title;
h1Element.title = `Skopiowano nazwę zamówienia: ${orderName}`;
h1Element.classList.add('tm-copied');
setTimeout(() => {
h1Element.title = old || 'Kliknij aby skopiować nazwę zamówienia';
h1Element.classList.remove('tm-copied');
}, 1200);
} catch (error) {
console.error('[Tampermonkey] Błąd podczas kopiowania nazwy zamówienia:', error);
h1Element.title = 'Błąd podczas kopiowania';
}
}, true); // capture=true
}
// ——— Kopiowanie produktów z trybem SKU/pełna nazwa ———
function makeProductCellCopyable(td) {
if (!td || td.dataset.tmProductCopyable) return;
td.dataset.tmProductCopyable = '1';
td.classList.add('tm-copyable');
td.addEventListener('click', async (e) => {
e.stopPropagation(); // nie wyzwalaj akcji Odoo na wierszu
const fullText = (td.textContent || '').trim();
if (!fullText) return;
try {
const copyOnlySKU = GM_getValue(COPY_MODE_KEY, true); // domyślnie SKU
const textToCopy = copyOnlySKU ? extractSKU(fullText) : fullText;
await tmCopyToClipboard(textToCopy);
const old = td.title;
const mode = copyOnlySKU ? 'SKU' : 'pełną nazwę';
td.title = `Skopiowano ${mode}: ${textToCopy}`;
td.classList.add('tm-copied');
setTimeout(() => { td.title = old || 'Skopiowano'; td.classList.remove('tm-copied'); }, 1200);
} catch (error) {
console.error('[Tampermonkey] Błąd podczas kopiowania produktu:', error);
td.title = 'Błąd podczas kopiowania';
}
}, true); // capture=true
}
// ——— Lokalizowanie docelowego miejsca dla przycisku ———
function findActionsContainer() {
// Znajdź kontener przycisków poniżej zakładek
const massBtn = document.querySelector('button[name="523"]');
if (massBtn) return massBtn.closest('.tab-pane') || massBtn.parentElement;
// Fallback - znajdź kontener o_form_buttons
const buttonsContainer = document.querySelector('.o_form_buttons');
if (buttonsContainer) return buttonsContainer;
return null;
}
// ——— Dodaj przycisk przełączania trybu kopiowania ———
function addCopyModeToggleButton() {
const host = findActionsContainer();
if (!host) {
console.log('[Tampermonkey] Nie znaleziono kontenera akcji, pomijam dodawanie przycisku');
return;
}
const idBtn = 'tm-purchase-copy-mode-toggle-button';
if (host.querySelector('#' + idBtn)) {
console.log('[Tampermonkey] Przycisk już istnieje, pomijam');
return;
}
console.log('[Tampermonkey] Znaleziono kontener akcji:', host);
console.log('[Tampermonkey] Tworzę przycisk przełączania trybu kopiowania');
const btn = document.createElement('button');
btn.type = 'button';
btn.id = idBtn;
btn.className = 'btn btn-secondary';
const label = document.createElement('span');
btn.appendChild(label);
const refresh = () => {
const copyOnlySKU = GM_getValue(COPY_MODE_KEY, true);
label.textContent = copyOnlySKU ? 'Kopiuję: SKU' : 'Kopiuję: Pełna nazwa';
btn.title = copyOnlySKU ? 'Kliknij aby przełączyć na kopiowanie pełnej nazwy produktu' : 'Kliknij aby przełączyć na kopiowanie tylko SKU produktu';
console.log('[Tampermonkey] Tryb kopiowania:', copyOnlySKU ? 'SKU' : 'Pełna nazwa');
};
btn.addEventListener('click', () => {
const currentMode = GM_getValue(COPY_MODE_KEY, true);
GM_setValue(COPY_MODE_KEY, !currentMode);
refresh();
console.log('[Tampermonkey] Tryb kopiowania zmieniony na:', !currentMode ? 'SKU' : 'Pełna nazwa');
});
// Dodaj przycisk do kontenera przycisków (zawsze poniżej zakładek)
const massBtn = host.querySelector('button[name="523"]');
if (massBtn && massBtn.parentElement) {
massBtn.insertAdjacentElement('afterend', btn);
console.log('[Tampermonkey] Przycisk dodany obok przycisku Masowa zmiana');
} else {
host.appendChild(btn);
console.log('[Tampermonkey] Przycisk dodany do kontenera akcji');
}
refresh();
}
// ——— Główna procedura ———
function processOnce() {
if (!isPurchaseOrderForm()) return;
if (!GM_getValue(INIT_FLAG)) { GM_setValue(INIT_FLAG, true); }
// Dodaj kopiowanie nazwy zamówienia
const orderNameSpan = document.querySelector('h1 span[name="name"]');
if (orderNameSpan) {
const orderNameH1 = orderNameSpan.closest('h1');
if (orderNameH1) {
makeOrderNameCopyable(orderNameH1);
}
}
// Dodaj kopiowanie produktów z kolumny "Produkt"
// Komórki produktów mają klasę o_list_many2one i są w 3. kolumnie tabeli
const productCells = document.querySelectorAll('td.o_data_cell.o_field_cell.o_list_many2one.o_readonly_modifier.o_required_modifier');
productCells.forEach(cell => {
// Sprawdź czy to rzeczywiście komórka produktu (zawiera nawiasy kwadratowe z SKU)
const text = cell.textContent || '';
if (text.includes('[') && text.includes(']')) {
makeProductCellCopyable(cell);
}
});
// Dodaj przycisk przełączania trybu kopiowania
addCopyModeToggleButton();
}
// ——— Start + reagowanie na zmiany ———
let deb = null; const debounce = (fn, ms = 150) => { clearTimeout(deb); deb = setTimeout(fn, ms); };
window.addEventListener('load', () => debounce(processOnce, 150));
window.addEventListener('hashchange', () => debounce(processOnce, 250));
new MutationObserver(() => debounce(processOnce, 150)).observe(document.documentElement, { childList: true, subtree: true });
})();