NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name bushop-dettaglio-ordini // @namespace bushop-dettaglio-ordini // @version 1.5.0 // @description Aggiunge dettagli alla pagina ordini di Bushop // @match https://bushop.org/cronologia-ordini // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; function init() { // -------------- Preparazione della struttura di base -------------- // Rimozione colonna "Metodo di pagamento" removeColumn(4); // Rimozione colonna "Fattura" removeColumn(6); // L'ultima colonna viene allargate per recuperare spazio in cui inserire i contenuti resizeColumn(7, '50%'); // -------------- Creazione foglio di stile per tabella -------------- createCssStyle("table.order-detail-list { margin-bottom:10px; }"); createCssStyle("table.order-detail-list td.product-detail { padding: 3px 10px !important; border-top:none !important}"); createCssStyle("table.order-detail-list td.product-detail-name { font-weight:bold }"); createCssStyle("table.order-detail-list td.product-detail-duration { font-style:italic }"); // ----------- Gestione ordini -------------- enhanceOrders(); } /* * ################################################# CSS RESTYLE ################################################# */ /** * Elabora tutte le righe degli ordini */ function enhanceOrders() { var ordersTable = document.querySelector("table#order-list"); var rows = ordersTable.querySelectorAll("tbody tr:not(.ordini-enhanced)"); rows.forEach(function(row) { // Imposto la riga come già elaborata row.classList.add("ordini-enhanced"); // Elaboro al riga enhanceOrder(row); }); } /** * Elabora la riga di un ordine */ function enhanceOrder(row) { // [CSS] Il pulsante "Riordina" viene nascosto dalla tabella (disponibile nel dettaglio ordine) var historyDetails = row.querySelector("td.history_detail"); historyDetails.querySelector("a[title='Riordina']").style.display = "none"; // [CSS] Il pulsante "Dettaglio" diventa float:right historyDetails.querySelector("a.button").style.float = "right"; // Estrazione ID univoco dell'ordine che servirà per recuperare i dettagli via XHR var detailsA = row.querySelector("td:first-child a.color-myaccount"); var href = detailsA.getAttribute("href"); var pattern = /javascript:showOrder\((\d+), (\d+)/g; var match = pattern.exec(href); var orderId = match[2]; // Avvio XHR per recupero dettagli degli ordini, indicando la riga di appartenenza in cui inserire le informazioni una volta scaricate fetchOrderDetails(row, orderId); } /* * ################################################# ORDER ENGINE ################################################# */ /** * Scarica i dettagli di un ordine e inserisce nella riga i prodotti trovati */ function fetchOrderDetails(orderListRow, orderId) { var xhrOrders = new XMLHttpRequest(); // Scaricato il dettaglio degli ordini si inizia ad aggiungere le informazioni xhrOrders.onreadystatechange = function() { if (this.readyState == XMLHttpRequest.DONE && this.status == 200) { try { onFetchOrderDetailsCompleted(orderListRow, this.responseText); } catch(e) { // alert("Errore elaborazione ordine " + orderId + "\n" + e); console.log(e); } } }; xhrOrders.open("GET", "/index.php?controller=order-detail&id_order=" + orderId + "&ajax=true", true); xhrOrders.send(); } /** * Funziona invocata quando l'HTML XHR di un ordine è stato scaricato */ function onFetchOrderDetailsCompleted(orderListRow, responseText) { // Estrazione metadati dall'html dell'ordine var parsed = parseOrderDetailsHtml(responseText); // Elaborazione riga enhanceOrderDetails(orderListRow, parsed); } /** * Riceve un contenuto HTML relativo al dettaglio dell'ordine, parse il DOM ed estrae le informazioni inserendole in un oggetto */ function parseOrderDetailsHtml(input) { var html = new DOMParser().parseFromString(input, "text/html"); // Oggetto in cui verranno inseriti tutti i metadati estratti var object = {}; // ############### Riferimento Ordine ############### var pattern = /Riferimento Ordine ([A-Z]+) - effettuato il (\d{2,2}\/\d{2,2}\/\d{4,4})/g; var match = pattern.exec(html.querySelector("p.dark strong").innerText); if(match == null) { throw "Riferimento ordine non trovato"; } object.codiceOrdine = match[1]; object.dataOrdine = match[2]; // ############### Stato ############### var statusTable = html.querySelectorAll("div.table_block table.detail_step_by_step")[0]; var statusRows = statusTable.querySelectorAll("tbody tr"); var statuses = []; statusRows.forEach(function(tr) { statuses.push({ data : tr.querySelectorAll("td")[0].innerText, nome : tr.querySelectorAll("td")[1].innerText }); }); object.statuses = statuses; // ############### Prodotti ############### var productsRows = html.querySelectorAll("div#order-detail-content tbody tr"); var products = []; productsRows.forEach(function(tr) { var tds = tr.querySelectorAll("td"); var name = tds[1].innerText.trim(); var pattern = /(.+)(?: - |, )Modalità Acquisto : Consegna a (\d+) giorni/g; var match = pattern.exec(name); if(match == null) { throw "Nome prodotto/Giorni attesa non trovati nel valore \"" + name + "\""; } products.push({ nome : match[1], attesa : parseInt(match[2]), quantita : parseInt(tds[2].innerText.trim()), prezzoUnitario : parseFloat(tds[3].innerText.trim().replace(" €", "")), prezzoTotale : parseFloat(tds[4].innerText.trim().replace(" €", "")) }); }); object.products = products; // End of parse return object; } /** * Elabora la riga con i dettagli dell'ordine */ function enhanceOrderDetails(orderListRow, json) { // Generazione dell'HTML da inserire var content = renderProducts(json); // Aggiunta HTML alla riga dell'ordine var detailsColumn = orderListRow.querySelector("td.history_detail"); detailsColumn.innerHTML = content + detailsColumn.innerHTML; } /** * Genera l'HTML da inserire con l'elenco dei prodotti */ function renderProducts(json) { var currentStatusName = getOrderCurrentStatus(json); // "In attesa di pagamento con bonifico bancario": il countdown non può ancora partire // "Pagamento accettato": il countdown è partito // "Preparazione in corso": il countdown ha superato il valore, sarà negativo // "Consegnato": il countdown non è più necessario var output = []; // ################ Dettagli dell'ordine ################ output.push("<table class='order-detail-list'>"); json.products.forEach(function(product) { // Potrebbero essere NULL se l'ordine non ha ancora raggiunto lo stato appropriato var pagamentoAccettatoDate = getOrderStatusDate(json, "Pagamento accettato"); var consegnatoDate = getOrderStatusDate(json, "Consegnato"); output.push("<tr>"); output.push("<td class='product-detail product-detail-name'>" + product.quantita + "x " + product.nome + "</td>"); output.push("<td class='product-detail product-detail-days'>" + product.attesa + " giorni</td>"); // Data prevista di fine ordine output.push("<td class='product-detail product-detail-enddate'>"); if(currentStatusName == "Pagamento accettato" || currentStatusName == "Preparazione in corso" || currentStatusName == "Consegnato") { var endDate = pagamentoAccettatoDate.add(product.attesa, 'days'); output.push(endDate.format("DD-MM-YYYY")); } output.push("</td>"); // Stato di avanzamento dell'ordine output.push("<td class='product-detail product-detail-datedetails'>"); var duration; if(currentStatusName == "Pagamento accettato" || currentStatusName == "Preparazione in corso") { duration = moment.duration(moment().diff(pagamentoAccettatoDate)); duration = (duration.get("months")*31 + duration.get("days")); output.push((duration > 0 ? "+":"") + duration + " giorni"); } else if(currentStatusName == "Consegnato") { duration = moment.duration(consegnatoDate.diff(endDate)); duration = (duration.get("months")*31 + duration.get("days")); output.push((duration > 0 ? "+":"") + duration + " giorni" + "<br />" + consegnatoDate.format("DD-MM-YYYY")); } output.push("</td>"); output.push("</tr>"); }); // HTML viene montato output.push("</table>"); return output.join(""); } /* * ################################################# UTILS ################################################# */ /** * Fornisce il nome dello stato più recente dell'ordine */ function getOrderCurrentStatus(json) { return json.statuses[0].nome; } /** * Fornisce la data relativo allo stato dell'ordine fornito */ function getOrderStatusDate(json, statusName) { var returnValue = null; json.statuses.forEach(function(item){ if(item.nome == statusName) { returnValue = moment(item.data, 'DD/MM/YYYY'); } }); return returnValue; } /* * ################################################# SETUP AMBIENTE ################################################# */ /** * Rimuove una colonna dalla tabella degli ordini con l'indice fornito */ function removeColumn(index) { var ordersTable = document.querySelector("table#order-list"); var columns = ordersTable.querySelectorAll("tr td:nth-child(" + index + "), tr th:nth-child(" + index + ")"); columns.forEach(function(element) { element.style.display = "none"; }); } /** * Ridimensiona la colonna della tabella degli ordini */ function resizeColumn(index, size) { var ordersTable = document.querySelector("table#order-list"); var element = ordersTable.querySelector("tr th:nth-child(" + index + ")"); element.style.width = size; } /** * Aggiunge uno stile di CSS al documento */ function createCssStyle(content) { var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = content; document.querySelector("head").appendChild(style); } // AVVIO DELLO SCRIPT init(); })();