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

785 lines
33 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 - KFZ Korekty
// @namespace http://tampermonkey.net/
// @version 1.8
// @description Kopiowanie danych z tabeli KFZ, pól formularza i podsumowania - 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/kfz
// @updateURL https://n8n.emma.net.pl/webhook/kfz
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// @run-at document-end
// ==/UserScript==
/* ========================OPIS==============================
WERSJA 1.1 - Funkcjonalność kopiowania danych z tabeli KFZ, pól formularza i podsumowania
WYMAGANE PARAMETRY URL:
- action=278 → ID akcji KFZ
- menu_id=141 → ID menu KFZ
- model=account.move → Model faktury/dokumentu
- view_type=form → Widok formularza
WYMAGANE KOLUMNY w tabeli KFZ:
- product_id → Produkt
- quantity_after → Ilość po korekcie
- price_unit_after → Cena jednostkowa po korekcie
- price_subtotal → Wartość
- invoice_line_net → Netto
- invoice_line_tax Podatek
- invoice_line_gross → Brutto
DZIAŁANIE SKRYPTU:
- Kopiowanie produktów z trybem SKU/pełna nazwa (jak w idea-orders)
- Kopiowanie wartości liczbowych z kolumn KFZ
- Ikona kopiowania ⧉ przy każdej kopiowalnej komórce
- Przycisk przełączania trybu kopiowania produktów
- Obsługa błędów zgodnie z zasadami .rules
- Automatyczne wykrywanie kolumn po atrybucie title
FUNKCJE KOPIOWANIA:
- Produkt: SKU (z nawiasów kwadratowych) lub pełna nazwa
- Ilość po korekcie: wartość liczbowa
- Cena jednostkowa po korekcie: wartość liczbowa
- Wartość: wartość liczbowa
- Netto: wartość liczbowa
- Podatek: wartość liczbowa
- Brutto: wartość liczbowa
ZAPAMIĘTYWANIE USTAWIEŃ:
- Tryb kopiowania produktów (SKU/pełna nazwa) zapisywany w pamięci
- Ustawienia przetrwają restart przeglądarki i odświeżenie strony
AUTOMATYCZNE AKTUALIZACJE:
- Serwer aktualizacji: https://n8n.emma.net.pl/webhook/kfz
- Tampermonkey automatycznie sprawdza dostępność nowych wersji
CHANGELOG:
v1.8 (2024) - PRZYWRÓCONO DWUKROPEK W TEKŚCIE PRZYCISKU
- Przywrócono dwukropek po słowie "Kopiuję"
- Tekst przycisku: "Kopiuję: SKU" i "Kopiuję: Pełna nazwa"
- Podwyższenie numeru wersji do 1.8
v1.7 (2024) - USUNIĘTO DWUKROPEK Z TEKSTU PRZYCISKU
- Zmieniono tekst przycisku z "Kopiuję: SKU" na "Kopiuję SKU"
- Zmieniono tekst z "Kopiuję: Pełna nazwa" na "Kopiuję Pełna nazwa"
- Usunięto dwukropek dla lepszej czytelności
- Podwyższenie numeru wersji do 1.7
v1.6 (2024) - POPRAWKA LOKALIZACJI PRZYCISKU W TAB-CONTENT
- Poprawiono findActionsContainer() - szuka w .tab-content > .tab-pane
- Przycisk umieszczany w zakładce obok "Załaduj z cennika" i "Masowa zmiana"
- Dodano wyszukiwanie zakładek z przyciskami (load_from_pricelist, button[name="523"])
- Przycisk w właściwym miejscu: <div class="tab-content"> zamiast o_control_panel
- Podwyższenie numeru wersji do 1.6
v1.5 (2024) - POPRAWKA UMIESZCZANIA PRZYCISKU
- Poprawiono funkcję findActionsContainer() - priorytet dla .o_form_buttons_view
- Przycisk "Kopiuję: SKU" teraz umieszczany w tym samym miejscu co w idea-orders
- Usunięto skomplikowaną logikę umieszczania w zakładkach
- Dokładnie ta sama strategia umieszczania co w idea-orders.user.js
- Podwyższenie numeru wersji do 1.5
v1.4 (2024) - DODANO BRAKUJĄCE POLA
- Dodano kopiowanie nazwy dokumentu (name) - np. "KFZ/302/10/2025"
- Dodano kopiowanie NIP (invoice_name) - np. "CSG SPÓŁKA AKCYJNA"
- Poprawiono mapowanie pól formularza na rzeczywiste selektory HTML
- Rozszerzone selektory dla pola name (również h1 span[name="name"])
- Podwyższenie numeru wersji do 1.4
v1.3 (2024) - NAPRAWKA BŁĘDU SELEKTORÓW CSS
- Usunięto nieprawidłowe selektory :contains() powodujące błąd SyntaxError
- Dodano bezpieczne try/catch dla wszystkich selektorów
- Alternatywne szukanie pól po etykietach bez :contains()
- Naprawiono blokowanie strony przez nieprawidłowe selektory
- Podwyższenie numeru wersji do 1.3
v1.2 (2024) - POPRAWKI KOPIOWANIA VAT I PÓL FORMULARZA
- Poprawiono kopiowanie VAT - znaczek kopiowania przy wartościach, nie nazwach
- Dodano commercial_partner_id jako "Nazwa na fakturę"
- Rozszerzone selektory dla NIP (partner_vat, vat)
- Dodano kopiowanie uwag (note, narration)
- Lepsze wykrywanie elementów po etykietach
- Podwyższenie numeru wersji do 1.2
v1.1 (2024) - KOPIOWANIE PÓL FORMULARZA I PODSUMOWANIA
- Dodano kopiowanie pól formularza: Nazwa na fakturę, NIP, Odnosnik, Data faktury
- Dodano kopiowanie pól podsumowania: Należność, Suma, Kwota bez podatku
- Dodano kopiowanie VAT (różne stawki)
- Automatyczna konwersja wartości ujemnych na dodatnie w podsumowaniu
- Funkcja makePositive() dla zapewnienia dodatnich wartości
- Rozszerzone selektory dla różnych typów elementów (span, a, input)
- Podwyższenie numeru wersji do 1.1
v1.0 (2024) - PIERWSZA WERSJA
- Dodano kopiowanie produktów z trybem SKU/pełna nazwa
- Dodano kopiowanie wartości liczbowych z 6 kolumn KFZ
- Dodano style CSS dla elementów kopiowalnych (pomarańczowy kolor #ff9800)
- Dodano przycisk przełączania trybu kopiowania
- Dodano menu Tampermonkey z opcjami konfiguracji
- Obsługa błędów w funkcjach kopiowania zgodnie z zasadami .rules
- Automatyczne wykrywanie kolumn po title zamiast indeksów
- Spójny styl wizualny z idea-orders.user.js
- Precyzyjne wykrywanie strony KFZ po parametrach action=278 i menu_id=141
============================================================ */
(function () {
'use strict';
// Debug flag zgodnie z zasadami .rules
const DEBUG = false;
const log = (...args) => DEBUG && console.log('[Tampermonkey KFZ]', ...args);
// ——— Klucze stanu ———
const COPY_MODE_KEY = 'tm_kfz_product_copy_mode_v10'; // true = SKU, false = pełna nazwa (domyślnie true)
const TABLE_SEL = 'table.o_list_table.o_section_and_note_list_view';
// Mapowanie title na data-name dla kolumn do kopiowania
const COPYABLE_COLUMNS = {
'Produkt': 'product_id',
'Ilość po korekcie': 'quantity_after',
'Cena jednostkowa po korekcie': 'price_unit_after',
'Wartość': 'price_subtotal',
'Netto': 'invoice_line_net',
'Podatek': 'invoice_line_tax',
'Brutto': 'invoice_line_gross'
};
// Pola formularza do kopiowania
const COPYABLE_FORM_FIELDS = {
'name': 'Nazwa dokumentu',
'commercial_partner_id': 'Nazwa na fakturę',
'partner_id': 'Kontrahent',
'invoice_name': 'NIP',
'partner_vat': 'NIP (alternatywny)',
'ref': 'Odnosnik',
'invoice_date': 'Data faktury',
'note': 'Uwagi',
'narration': 'Uwagi wewnętrzne'
};
// Pola podsumowania do kopiowania
const COPYABLE_SUMMARY_FIELDS = {
'amount_residual': 'Należność',
'amount_total': 'Suma',
'amount_untaxed': 'Kwota bez podatku'
};
GM_addStyle(`
/* Kopiowanie ikona tuż za liczbą (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 liczba */
margin-left: 6px; /* mały odstęp od liczby */
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));
}
`);
// ——— Pomocnicze funkcje ———
const textOf = el => el ? (el.getAttribute('title') || el.textContent || '').trim() : '';
// Funkcja do konwersji wartości na dodatnią
const makePositive = (value) => {
if (typeof value === 'string') {
// Usuń znaki waluty i spacje
const cleanValue = value.replace(/[^\d,.-]/g, '').replace(',', '.');
const numValue = parseFloat(cleanValue);
return isNaN(numValue) ? value : Math.abs(numValue).toString();
}
return Math.abs(parseFloat(value) || 0).toString();
};
function isKFZForm() {
const params = new URLSearchParams((location.hash || '').replace(/^#/, ''));
const action = params.get('action');
const menuId = params.get('menu_id');
const model = params.get('model');
const viewType = params.get('view_type');
log('Sprawdzanie parametrów URL:', {
action, menuId, model, viewType,
hash: location.hash,
url: location.href
});
// Sprawdź czy to właściwa strona KFZ z konkretnymi parametrami
const isKFZ = action === '278' &&
menuId === '141' &&
model === 'account.move' &&
viewType === 'form';
log('Czy to strona KFZ?', isKFZ);
return isKFZ;
}
// ——— Funkcja wyodrębniania SKU z nawiasów kwadratowych ———
function extractSKU(productText) {
const match = productText.match(/\[([^\]]+)\]/);
return match ? match[1] : productText;
}
// ——— Schowek: kopiowanie wartości z komórek ———
async function tmCopyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
} catch (error) {
log('Fallback do document.execCommand dla kopiowania');
const ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
}
// ——— Kopiowanie zwykłych komórek (wartości liczbowe) ———
function makeCellCopyable(td, makePositiveValue = false) {
if (!td || td.dataset.tmCopyable) return;
td.dataset.tmCopyable = '1';
td.classList.add('tm-copyable');
td.addEventListener('click', async (e) => {
e.stopPropagation(); // nie wyzwalaj akcji Odoo na wierszu
let val = (td.textContent || '').trim();
if (!val) return;
// Konwertuj na wartość dodatnią jeśli wymagane
if (makePositiveValue) {
val = makePositive(val);
}
try {
await tmCopyToClipboard(val);
const old = td.title;
td.title = `Skopiowano: ${val}`;
td.classList.add('tm-copied');
setTimeout(() => {
td.title = old || 'Skopiowano';
td.classList.remove('tm-copied');
}, 1200);
log('Skopiowano komórkę:', val);
} catch (error) {
console.error('[Tampermonkey KFZ] Błąd podczas kopiowania komórki:', error);
td.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);
log('Skopiowano produkt:', textToCopy, 'tryb:', mode);
} catch (error) {
console.error('[Tampermonkey KFZ] Błąd podczas kopiowania produktu:', error);
td.title = 'Błąd podczas kopiowania';
}
}, true); // capture=true
}
// ——— Kopiowanie pól formularza ———
function makeFormFieldCopyable(element, fieldName) {
if (!element || element.dataset.tmFormCopyable) return;
element.dataset.tmFormCopyable = '1';
element.classList.add('tm-copyable');
element.style.cursor = 'copy';
element.title = `Kliknij aby skopiować ${fieldName}`;
element.addEventListener('click', async (e) => {
e.stopPropagation();
let text = '';
// Różne sposoby wyciągania tekstu w zależności od typu elementu
if (element.tagName === 'SPAN') {
text = (element.textContent || '').trim();
} else if (element.tagName === 'A') {
text = (element.querySelector('span')?.textContent || element.textContent || '').trim();
} else {
text = (element.textContent || element.value || '').trim();
}
if (!text) return;
try {
await tmCopyToClipboard(text);
const old = element.title;
element.title = `Skopiowano ${fieldName}: ${text}`;
element.classList.add('tm-copied');
setTimeout(() => {
element.title = old || `Kliknij aby skopiować ${fieldName}`;
element.classList.remove('tm-copied');
}, 1200);
log(`Skopiowano ${fieldName}:`, text);
} catch (error) {
console.error(`[Tampermonkey KFZ] Błąd podczas kopiowania ${fieldName}:`, error);
element.title = 'Błąd podczas kopiowania';
}
}, true);
}
// ——— Kopiowanie pól podsumowania (z konwersją na wartości dodatnie) ———
function makeSummaryFieldCopyable(element, fieldName) {
if (!element || element.dataset.tmSummaryCopyable) return;
element.dataset.tmSummaryCopyable = '1';
element.classList.add('tm-copyable');
element.style.cursor = 'copy';
element.title = `Kliknij aby skopiować ${fieldName}`;
element.addEventListener('click', async (e) => {
e.stopPropagation();
let text = (element.textContent || '').trim();
if (!text) return;
// Konwertuj na wartość dodatnią
const positiveValue = makePositive(text);
try {
await tmCopyToClipboard(positiveValue);
const old = element.title;
element.title = `Skopiowano ${fieldName}: ${positiveValue}`;
element.classList.add('tm-copied');
setTimeout(() => {
element.title = old || `Kliknij aby skopiować ${fieldName}`;
element.classList.remove('tm-copied');
}, 1200);
log(`Skopiowano ${fieldName}:`, positiveValue);
} catch (error) {
console.error(`[Tampermonkey KFZ] Błąd podczas kopiowania ${fieldName}:`, error);
element.title = 'Błąd podczas kopiowania';
}
}, true);
}
// ——— Znajdowanie kolumn po title ———
function findColumnByTitle(table, title) {
const headers = table.querySelectorAll('thead th');
log(`Szukanie kolumny "${title}" w ${headers.length} nagłówkach`);
for (let i = 0; i < headers.length; i++) {
const headerTitle = headers[i].getAttribute('title');
const headerText = headers[i].textContent?.trim();
log(`Nagłówek ${i}: title="${headerTitle}", text="${headerText}"`);
if (headerTitle === title) {
log(`Znaleziono kolumnę "${title}" na pozycji ${i}`);
return i;
}
}
log(`Nie znaleziono kolumny "${title}"`);
return -1;
}
// ——— Główna funkcja dodawania kopiowania ———
function addCopyFunctionality(table) {
log('Dodawanie funkcjonalności kopiowania do tabeli');
log('Selektor tabeli:', table.className, table.tagName);
// Iteruj przez wszystkie wiersze danych
const rows = table.querySelectorAll('tbody tr.o_data_row');
log(`Znaleziono ${rows.length} wierszy danych`);
if (rows.length === 0) {
// Spróbuj alternatywnych selektorów
const altRows = table.querySelectorAll('tbody tr');
log(`Alternatywnie znaleziono ${altRows.length} wierszy (wszystkie)`);
altRows.forEach((row, rowIndex) => {
log(`Wiersz ${rowIndex}:`, row.className, row.children.length, 'komórek');
});
}
rows.forEach((row, rowIndex) => {
log(`Przetwarzanie wiersza ${rowIndex} z ${row.children.length} komórkami`);
// Dla każdej kolumny do kopiowania
Object.entries(COPYABLE_COLUMNS).forEach(([title, dataName]) => {
const colIndex = findColumnByTitle(table, title);
if (colIndex === -1) {
return;
}
const cell = row.children[colIndex];
if (!cell) {
log(`Brak komórki w wierszu ${rowIndex}, kolumna ${colIndex}`);
return;
}
log(`Dodawanie kopiowania do komórki [${rowIndex}, ${colIndex}] "${title}": "${cell.textContent?.trim()}"`);
// Specjalne traktowanie dla kolumny Produkt
if (title === 'Produkt') {
makeProductCellCopyable(cell);
} else {
makeCellCopyable(cell);
}
});
});
}
// ——— Dodawanie kopiowania dla pól formularza ———
function addFormFieldsCopyFunctionality() {
log('Dodawanie kopiowania dla pól formularza...');
Object.entries(COPYABLE_FORM_FIELDS).forEach(([fieldName, displayName]) => {
// Szukaj różnych selektorów dla pól
let selectors = [
`span[name="${fieldName}"]`,
`a[name="${fieldName}"]`,
`input[name="${fieldName}"]`,
`[name="${fieldName}"] span`,
`[name="${fieldName}"]`
];
// Specjalne selektory dla konkretnych pól
if (fieldName === 'invoice_name') {
selectors = [
'span[name="invoice_name"]',
'input[name="invoice_name"]',
'[name="invoice_name"]'
];
} else if (fieldName === 'name') {
selectors = [
'span[name="name"]',
'input[name="name"]',
'[name="name"]',
'h1 span[name="name"]'
];
} else if (fieldName === 'partner_vat') {
selectors = [
'span[name="partner_vat"]',
'input[name="partner_vat"]',
'[name="partner_vat"]',
'span[name="vat"]',
'input[name="vat"]',
'[name="vat"]'
];
} else if (fieldName === 'note' || fieldName === 'narration') {
selectors = [
`span[name="${fieldName}"]`,
`textarea[name="${fieldName}"]`,
`div[name="${fieldName}"]`,
`[name="${fieldName}"]`
];
}
let found = false;
for (const selector of selectors) {
try {
const element = document.querySelector(selector);
if (element && element.textContent?.trim()) {
log(`Znaleziono pole ${displayName} (${fieldName}):`, element.tagName, element.textContent?.trim());
makeFormFieldCopyable(element, displayName);
found = true;
break;
}
} catch (error) {
log(`Błąd selektora "${selector}":`, error.message);
}
}
// Jeśli nie znaleziono standardowymi selektorami, spróbuj szukać po etykietach
if (!found) {
if (fieldName === 'partner_vat') {
const labels = document.querySelectorAll('label');
for (const label of labels) {
if (label.textContent?.includes('NIP') || label.textContent?.includes('VAT')) {
const nextElement = label.nextElementSibling?.querySelector('span') ||
label.parentElement?.nextElementSibling?.querySelector('span');
if (nextElement && nextElement.textContent?.trim()) {
log(`Znaleziono ${displayName} przez etykietę:`, nextElement.textContent?.trim());
makeFormFieldCopyable(nextElement, displayName);
break;
}
}
}
} else if (fieldName === 'note' || fieldName === 'narration') {
const labels = document.querySelectorAll('label');
for (const label of labels) {
if (label.textContent?.includes('Uwagi') || label.textContent?.includes('Notatki') || label.textContent?.includes('Opis')) {
const nextElement = label.nextElementSibling?.querySelector('span, textarea, div') ||
label.parentElement?.nextElementSibling?.querySelector('span, textarea, div');
if (nextElement && nextElement.textContent?.trim()) {
log(`Znaleziono ${displayName} przez etykietę:`, nextElement.textContent?.trim());
makeFormFieldCopyable(nextElement, displayName);
break;
}
}
}
}
}
});
}
// ——— Dodawanie kopiowania dla pól podsumowania ———
function addSummaryFieldsCopyFunctionality() {
log('Dodawanie kopiowania dla pól podsumowania...');
Object.entries(COPYABLE_SUMMARY_FIELDS).forEach(([fieldName, displayName]) => {
// Szukaj różnych selektorów dla pól podsumowania
const selectors = [
`span[name="${fieldName}"]`,
`[name="${fieldName}"]`,
`.oe_subtotal_footer_separator[name="${fieldName}"]`
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element) {
log(`Znaleziono pole ${displayName} (${fieldName}):`, element.textContent?.trim());
makeSummaryFieldCopyable(element, displayName);
break;
}
}
});
// Dodatkowe szukanie VAT - znaczek kopiowania przy wartościach, nie nazwach
const vatRows = document.querySelectorAll('tr:has(.oe_tax_group_name)');
vatRows.forEach((row, index) => {
const nameElement = row.querySelector('.oe_tax_group_name');
const valueElement = row.querySelector('.oe_tax_group_amount_value, .oe_tax_group_editable span');
if (nameElement && valueElement && nameElement.textContent?.includes('VAT')) {
const vatName = nameElement.textContent?.trim() || `VAT ${index + 1}`;
log(`Znaleziono VAT "${vatName}":`, valueElement.textContent?.trim());
makeSummaryFieldCopyable(valueElement, vatName);
}
});
// Fallback - jeśli powyższe nie zadziała, szukaj bezpośrednio wartości VAT
const vatValues = document.querySelectorAll('.oe_tax_group_amount_value');
vatValues.forEach((element, index) => {
if (!element.dataset.tmSummaryCopyable) {
log(`Znaleziono wartość VAT ${index + 1}:`, element.textContent?.trim());
makeSummaryFieldCopyable(element, `VAT ${index + 1}`);
}
});
}
// ——— Lokalizowanie docelowego miejsca dla przycisku ———
function findActionsContainer() {
log('Szukanie kontenera dla przycisku...');
// PRIORYTET 1: Dokładnie jak w idea-orders.user.js - szukaj notebook_page_130
const byId = document.getElementById('notebook_page_130');
if (byId) {
log('Znaleziono notebook_page_130');
return byId;
}
// PRIORYTET 2: Szukaj przycisku load_from_pricelist i jego kontener .tab-pane
const pricelistBtn = document.querySelector('button[name="load_from_pricelist"]');
if (pricelistBtn) {
const container = pricelistBtn.closest('.tab-pane') || pricelistBtn.parentElement;
log('Znaleziono kontener przez load_from_pricelist:', container.id || container.className);
return container;
}
// PRIORYTET 3: Szukaj w .tab-content > .tab-pane (gdzie są zakładki)
const tabPanes = document.querySelectorAll('.tab-content .tab-pane');
for (const pane of tabPanes) {
// Szukaj zakładki z przyciskami (load_from_pricelist lub button[name="523"])
if (pane.querySelector('button[name="load_from_pricelist"], button[name="523"]')) {
log('Znaleziono zakładkę z przyciskami:', pane.id || pane.className);
return pane;
}
}
// PRIORYTET 4: Szukaj konkretnych zakładek KFZ - notebook_page_669 (Pozycje faktury)
const kfzTab = document.getElementById('notebook_page_669');
if (kfzTab) {
log('Znaleziono notebook_page_669 (Pozycje faktury)');
return kfzTab;
}
log('Nie znaleziono odpowiedniego kontenera');
return null;
}
// ——— Przycisk przełączania trybu kopiowania produktów ———
function addCopyModeToggleButton() {
const host = findActionsContainer();
if (!host) {
log('Nie znaleziono kontenera dla przycisku');
return;
}
const idBtn = 'tm-kfz-copy-mode-toggle-button';
if (host.querySelector('#' + idBtn)) {
log('Przycisk już istnieje');
return;
}
log('Tworzenie przycisku przełączania trybu kopiowania...');
const btn = document.createElement('button');
btn.type = 'button';
btn.id = idBtn;
btn.className = 'btn btn-secondary'; // Dokładnie jak w idea-orders
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';
log('Odświeżono przycisk, tryb:', copyOnlySKU ? 'SKU' : 'Pełna nazwa');
};
btn.addEventListener('click', () => {
const newMode = !GM_getValue(COPY_MODE_KEY, true);
GM_setValue(COPY_MODE_KEY, newMode);
refresh();
log('Zmieniono tryb kopiowania na:', newMode ? 'SKU' : 'Pełna nazwa');
});
// Dokładnie ta sama logika umieszczania jak w idea-orders.user.js
const massBtn = host.querySelector('button[name="523"]');
if (massBtn && massBtn.parentElement) {
massBtn.insertAdjacentElement('afterend', btn);
log('Dodano przycisk obok button[name="523"]');
} else {
host.appendChild(btn);
log('Dodano przycisk do kontenera:', host.className || host.tagName);
}
refresh();
}
// ——— Menu Tampermonkey ———
GM_registerMenuCommand('Ustawienia KFZ ▶', () => {
const choice = prompt(
'Ustawienia KFZ (wpisz numer):\n' +
'1) Przełącz tryb kopiowania (SKU ⇄ Pełna nazwa)\n' +
'2) Test kopiowania produktów\n' +
'3) Wyświetl aktualny tryb kopiowania'
);
if (!choice) return;
switch (choice.trim()) {
case '1':
const currentMode = GM_getValue(COPY_MODE_KEY, true);
GM_setValue(COPY_MODE_KEY, !currentMode);
const newModeText = !currentMode ? 'SKU' : 'Pełna nazwa';
alert(`Tryb kopiowania zmieniony na: ${newModeText}`);
break;
case '2':
const tables = document.querySelectorAll(TABLE_SEL);
if (tables.length > 0) {
addCopyFunctionality(tables[0]);
alert('Funkcja kopiowania została zastosowana do tabeli KFZ.');
} else {
alert('Nie znaleziono tabeli KFZ na tej stronie.');
}
break;
case '3':
const mode = GM_getValue(COPY_MODE_KEY, true);
alert(`Aktualny tryb kopiowania: ${mode ? 'SKU' : 'Pełna nazwa'}`);
break;
default:
// nic
}
});
// ——— Główna procedura ———
function processOnce() {
log('=== ROZPOCZĘCIE processOnce ===');
if (!isKFZForm()) {
log('Nie jest to formularz KFZ - kończenie');
return;
}
log('Wykryto formularz KFZ, inicjalizacja...');
const tables = document.querySelectorAll(TABLE_SEL);
log(`Znaleziono ${tables.length} tabel z selektorem: ${TABLE_SEL}`);
if (!tables.length) {
// Spróbuj alternatywnych selektorów
const altTables = document.querySelectorAll('table.o_list_table');
log(`Alternatywnie znaleziono ${altTables.length} tabel z selektorem: table.o_list_table`);
const allTables = document.querySelectorAll('table');
log(`Wszystkich tabel na stronie: ${allTables.length}`);
allTables.forEach((table, i) => {
log(`Tabela ${i}:`, table.className);
});
addCopyModeToggleButton();
return;
}
tables.forEach((table, i) => {
log(`Przetwarzanie tabeli ${i}:`, table.className);
addCopyFunctionality(table);
});
// Dodaj kopiowanie dla pól formularza i podsumowania
addFormFieldsCopyFunctionality();
addSummaryFieldsCopyFunctionality();
addCopyModeToggleButton();
log('=== ZAKOŃCZENIE processOnce ===');
}
// ——— Start + reagowanie na zmiany ———
let debounceTimer = null;
const debounce = (fn, ms = 150) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(fn, ms);
};
// Inicjalizacja po załadowaniu DOM
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => debounce(processOnce, 150));
} else {
debounce(processOnce, 150);
}
// Reagowanie na zmiany w DOM i hash
window.addEventListener('hashchange', () => debounce(processOnce, 250));
new MutationObserver(() => debounce(processOnce, 150))
.observe(document.documentElement, { childList: true, subtree: true });
log('Skrypt KFZ załadowany');
})();