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

276 lines
12 KiB
JavaScript

// ==UserScript==
// @name Idea Invoice Famica
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Rozszerza funkcjonalność faktur IdeaERP o dodawanie numeru EORI i ulepszone drukowanie/generowanie PDF dla faktur Famica Ltd.
// @author You
// @match https://emma.ideaerp.pl/my/invoices*
// @icon https://emma.ideaerp.pl/web/image/res.company/1/favicon/
// @downloadURL https://n8n.emma.net.pl/webhook/invoice-famica
// @updateURL https://n8n.emma.net.pl/webhook/invoice-famica
// @grant none
// ==/UserScript==
/**
* IDEA INVOICE FAMICA - Skrypt Rozszerzający Przetwarzanie Faktur
*
* Ten skrypt użytkownika Tampermonkey rozszerza interfejs faktur IdeaERP poprzez
* dodanie trzech niestandardowych przycisków usprawniających workflow przetwarzania faktur.
*
* FUNKCJE:
* - Dodaje numer EORI (GB174521806000) do informacji o nabywcy na fakturach
* - Ulepszone drukowanie z prawidłową widocznością nagłówków tabel i cieniowania
* - Funkcjonalność generowania PDF z zachowaniem formatowania
*
* SZCZEGÓŁY TECHNICZNE:
* - Działa w systemie wyświetlania faktur opartym na iframe
* - Używa wstrzykiwania CSS do wymuszenia dostosowania kolorów przy drukowaniu
* - Implementuje obsługę MediaQueryList kompatybilną z różnymi przeglądarkami
* - Zapobiega duplikowaniu przycisków poprzez znaczniki dataset
*
* SPOSÓB UŻYCIA:
* 1. "Dodaj EORI" - Dodaje numer EORI do sekcji nabywcy na fakturze
* 2. "Wydrukuj z EORI" - Otwiera okno drukowania z ulepszonym formatowaniem
* 3. "Pobierz z EORI" - Prowadzi użytkownika przez zapisanie faktury jako PDF z formatowaniem
*
* HISTORIA ZMIAN:
* v0.2 (2025-09-24)
* - Dodano ulepszoną funkcjonalność drukowania z poprawkami widoczności nagłówków tabel
* - Zaimplementowano zachowanie cieniowania wierszy w wydruku
* - Dodano funkcjonalność pobierania PDF z instrukcją dla użytkownika
* - Naprawiono problemy kompatybilności MediaQueryList między przeglądarkami
* - Ulepszona obsługa błędów i mechanizmy czyszczenia
*
* v0.1 (2025-09-24)
* - Pierwsze wydanie z podstawową funkcjonalnością dodawania EORI
* - Podstawowe tworzenie przycisków i manipulacja zawartością iframe
* - Implementacja MutationObserver do wykrywania dynamicznej zawartości
*/
(function() {
'use strict';
function addButtons() {
const targetDivs = document.querySelectorAll('.o_download_pdf.btn-toolbar');
targetDivs.forEach(targetDiv => {
// Check if buttons are already added
if (targetDiv.dataset.customButtonsAdded) {
return;
}
// Create "Dodaj EORI" button
const eoriButtonGroup = document.createElement('div');
eoriButtonGroup.className = 'btn-group flex-grow-1 mr-1 mb-1';
const eoriButton = document.createElement('a');
eoriButton.className = 'btn btn-secondary btn-block';
eoriButton.href = '#';
eoriButton.innerHTML = '<i class="fa fa-plus"></i> Dodaj EORI';
eoriButton.onclick = function(e) {
e.preventDefault();
const iframe = document.getElementById('invoice_html');
if (!iframe || !iframe.contentWindow) {
alert('Ramka z fakturą nie została znaleziona.');
return false;
}
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const nabywcaDiv = iframeDoc.querySelector('.i_nabywca.col-4');
if (!nabywcaDiv) {
alert('Sekcja nabywcy nie została znaleziona w fakturze.');
return false;
}
// Check if EORI is already added
if (iframeDoc.querySelector('.eori-number')) {
alert('Numer EORI został już dodany.');
return false;
}
const br = iframeDoc.createElement('br');
const eoriTextNode = iframeDoc.createTextNode('EORI Number: ');
const eoriSpan = iframeDoc.createElement('span');
eoriSpan.className = 'eori-number';
eoriSpan.innerText = 'GB174521806000';
nabywcaDiv.appendChild(br);
nabywcaDiv.appendChild(eoriTextNode);
nabywcaDiv.appendChild(eoriSpan);
return false;
};
eoriButtonGroup.appendChild(eoriButton);
// Create "Wydrukuj z EORI" button
const printEoriButtonGroup = document.createElement('div');
printEoriButtonGroup.className = 'btn-group flex-grow-1 mb-1';
const printEoriButton = document.createElement('a');
printEoriButton.className = 'btn btn-secondary btn-block';
printEoriButton.href = '#';
printEoriButton.innerHTML = '<i class="fa fa-print"></i> Wydrukuj z EORI';
printEoriButton.onclick = function(e) {
e.preventDefault();
const iframe = document.getElementById('invoice_html');
if (iframe && iframe.contentWindow) {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const iframeWin = iframe.contentWindow;
// Inject style to force printing background colors and shadows
const style = iframeDoc.createElement('style');
style.id = 'print-fix-styles';
style.innerHTML = `
@media print {
body, body * {
-webkit-print-color-adjust: exact !important;
color-adjust: exact !important;
print-color-adjust: exact !important;
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: #f9f9f9 !important;
}
thead, thead *, thead th, thead th * {
color: black !important;
-webkit-text-fill-color: black !important;
visibility: visible !important;
}
}
`;
iframeDoc.head.appendChild(style);
const removePrintStyles = () => {
const styleToRemove = iframeDoc.getElementById('print-fix-styles');
if (styleToRemove) {
iframeDoc.head.removeChild(styleToRemove);
}
};
const mediaQueryList = iframeWin.matchMedia('print');
const handler = (mql) => {
if (!mql.matches) {
removePrintStyles();
// Use the correct method based on browser support
try {
if (mql.removeEventListener) {
mql.removeEventListener('change', handler);
} else if (mql.removeListener) {
mql.removeListener(handler);
}
} catch (e) {
// Ignore errors when removing listeners
console.log('Error removing print listener:', e);
}
}
};
try {
if (mediaQueryList.addEventListener) {
mediaQueryList.addEventListener('change', handler);
} else if (mediaQueryList.addListener) {
mediaQueryList.addListener(handler);
}
} catch (e) {
console.log('Error adding print listener:', e);
}
iframeWin.focus();
iframeWin.print();
} else {
window.print();
}
return false;
};
printEoriButtonGroup.appendChild(printEoriButton);
// Create "Pobierz z EORI" button
const downloadEoriButtonGroup = document.createElement('div');
downloadEoriButtonGroup.className = 'btn-group flex-grow-1 mr-1 mb-1';
const downloadEoriButton = document.createElement('a');
downloadEoriButton.className = 'btn btn-secondary btn-block';
downloadEoriButton.href = '#';
downloadEoriButton.innerHTML = '<i class="fa fa-download"></i> Pobierz z EORI';
downloadEoriButton.onclick = function(e) {
e.preventDefault();
const iframe = document.getElementById('invoice_html');
if (iframe && iframe.contentWindow) {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const iframeWin = iframe.contentWindow;
// Inject style to force printing background colors and shadows
const style = iframeDoc.createElement('style');
style.id = 'download-fix-styles';
style.innerHTML = `
@media print {
body, body * {
-webkit-print-color-adjust: exact !important;
color-adjust: exact !important;
print-color-adjust: exact !important;
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: #f9f9f9 !important;
}
thead, thead *, thead th, thead th * {
color: black !important;
-webkit-text-fill-color: black !important;
visibility: visible !important;
}
}
`;
iframeDoc.head.appendChild(style);
// Show instruction to user
alert('W oknie drukowania wybierz "Zapisz jako PDF" jako drukarkę docelową.');
// Create a function to handle PDF saving
const saveAsPdf = () => {
iframeWin.print();
// Clean up styles after a delay
setTimeout(() => {
const styleToRemove = iframeDoc.getElementById('download-fix-styles');
if (styleToRemove) {
iframeDoc.head.removeChild(styleToRemove);
}
}, 1000);
};
iframeWin.focus();
setTimeout(saveAsPdf, 500);
} else {
window.print();
}
return false;
};
downloadEoriButtonGroup.appendChild(downloadEoriButton);
// Append new buttons to the target div
targetDiv.appendChild(eoriButtonGroup);
targetDiv.appendChild(printEoriButtonGroup);
targetDiv.appendChild(downloadEoriButtonGroup);
// Mark as buttons added
targetDiv.dataset.customButtonsAdded = 'true';
});
}
// Use MutationObserver to detect when new content is added to the page
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
addButtons();
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
})();