Created
August 24, 2025 14:41
-
-
Save hsayed21/df48bcf0dcf508f74f714f5d5132d1da to your computer and use it in GitHub Desktop.
Extract Odoo tickets and format for Excel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Odoo Ticket Extractor | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.0 | |
| // @description Extract Odoo tickets and format for Excel | |
| // @author hsayed21 | |
| // @match https://www.posbank.me/web* | |
| // @match https://posbank.me/web* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=posbank.me | |
| // @grant none | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| function addExtractionButton() { | |
| if (!window.location.hash.includes('model=helpdesk.ticket') || !window.location.hash.includes('view_type=list')) { | |
| return; | |
| } | |
| const button = document.createElement('button'); | |
| button.innerHTML = '📋 Extract Tickets for Excel'; | |
| button.style.cssText = ` | |
| position: fixed; | |
| top: 0; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 9999; | |
| background: #5a3c52; | |
| color: white; | |
| border: none; | |
| padding: 10px 15px; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-weight: bold; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.2); | |
| `; | |
| button.addEventListener('click', extractTickets); | |
| document.body.appendChild(button); | |
| } | |
| function extractTickets() { | |
| const ticketRows = document.querySelectorAll('tr.o_data_row[data-id*="helpdesk.ticket"]'); | |
| if (ticketRows.length === 0) { | |
| alert('No tickets found on this page. Make sure you are on the ticket list view.'); | |
| return; | |
| } | |
| const excelData = []; | |
| excelData.push(['CaseId', 'Description', 'Status', 'OdooLink', 'Notes']); | |
| ticketRows.forEach(row => { | |
| try { | |
| // Extract Case ID | |
| const idCell = row.querySelector('td[name="id"]'); | |
| const caseId = idCell ? idCell.textContent.trim() : ''; | |
| // Extract Description | |
| const nameCell = row.querySelector('td[name="display_name"]'); | |
| const descriptionLink = nameCell ? nameCell.querySelector('a') : null; | |
| const description = descriptionLink ? descriptionLink.textContent.trim() : ''; | |
| // Extract Odoo Link | |
| const odooLink = descriptionLink ? descriptionLink.href : ''; | |
| // Status is always "New Feature" as requested | |
| const status = 'New Feature'; | |
| // Notes field is empty by default | |
| const notes = ''; | |
| if (caseId) { | |
| excelData.push([caseId, description, status, odooLink, notes]); | |
| } | |
| } catch (error) { | |
| console.error('Error extracting ticket data from row:', error); | |
| } | |
| }); | |
| // Convert to tab-separated format for easy Excel import | |
| const tsvContent = excelData.map(row => | |
| row.map(cell => `"${cell.toString().replace(/"/g, '""')}"`).join('\t') | |
| ).join('\n'); | |
| copyToClipboard(tsvContent); | |
| showNotification(`Successfully extracted ${excelData.length - 1} tickets! Data copied to clipboard. You can now paste it directly into Excel.`); | |
| } | |
| function copyToClipboard(text) { | |
| const textarea = document.createElement('textarea'); | |
| textarea.value = text; | |
| textarea.style.position = 'fixed'; | |
| textarea.style.opacity = '0'; | |
| document.body.appendChild(textarea); | |
| textarea.select(); | |
| document.execCommand('copy'); | |
| // Remove the temporary element | |
| document.body.removeChild(textarea); | |
| } | |
| function showNotification(message) { | |
| const notification = document.createElement('div'); | |
| notification.innerHTML = message; | |
| notification.style.cssText = ` | |
| position: fixed; | |
| top: 60px; | |
| right: 10px; | |
| z-index: 10000; | |
| background: #28a745; | |
| color: white; | |
| padding: 15px 20px; | |
| border-radius: 5px; | |
| max-width: 300px; | |
| font-weight: bold; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.2); | |
| line-height: 1.4; | |
| `; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| if (notification.parentNode) { | |
| notification.parentNode.removeChild(notification); | |
| } | |
| }, 5000); | |
| } | |
| // Wait for page to load and add button | |
| function init() { | |
| // Wait a bit for the page to fully load | |
| setTimeout(() => { | |
| addExtractionButton(); | |
| }, 2000); | |
| } | |
| // Initialize when DOM is ready | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', init); | |
| } else { | |
| init(); | |
| } | |
| // Also listen for navigation changes (SPA) | |
| let lastUrl = location.href; | |
| new MutationObserver(() => { | |
| const url = location.href; | |
| if (url !== lastUrl) { | |
| lastUrl = url; | |
| setTimeout(addExtractionButton, 2000); | |
| } | |
| }).observe(document, { subtree: true, childList: true }); | |
| })(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment