天猫详情商品信息提取辅助,用于批量开票的信息辅助
// ==UserScript== // @name 天猫详情商品信息提取 // @namespace http://tampermonkey.net/ // @homepage https://einvoice.taobao.com/?source=qn#/invoice/compensate // @version 1.1.0 // @description 天猫详情商品信息提取辅助,用于批量开票的信息辅助 // @author You // @match https://trade.tmall.com/detail/orderDetail.htm* // @match https://myseller.taobao.com/home.htm* // @match https://einvoice.taobao.com/* // @match https://qn.taobao.com/home.htm/* // @match https://dppt.jiangsu.chinatax.gov.cn:8443/* // @icon https://icons.duckduckgo.com/ip2/tmall.com.ico // @grant GM_setClipboard // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_listValues // @grant GM_deleteValue // @license MIT // ==/UserScript== (async function () { 'use strict'; /** * 新版天猫详情 * @returns obj */ async function get_order_obj() { const result = { hasRepeat: false } const products = [] let finded = await el_find("div[class^=order-info_order-info] tr.order-item"); if (!finded) { let msg = "页面加载超时/结构更新,未找到商品列表信息"; alert(msg); throw msg; } const order_item = document.querySelectorAll("div[class^=order-info_order-info] tr.order-item"); const titles = [] for (let item of order_item) { const temp = get_detail(item) console.log("temp=", temp) if (temp !== null && temp.price !== '0.00') { if (titles.includes(temp.title)) { result.hasRepeat = true continue; } products.push(temp) } } function get_detail(item) { const tds = item.querySelectorAll('td'); if (tds.length < 5) { return null } const details = {} // 标题 details.title = tds[0].querySelector('a')?.innerText; // 商品编码 const code = /[A-Z]{2,3}\d{6}-?[0-9A-Z]{0,5}/.exec(tds[0].innerText) details.code = code !== null ? code[0] : '' // 状态 details.status = tds[2].innerText // 单价 details.price = tds[3].innerText.replace('\n', '') // 数量 details.count = tds[4].innerText return details; } const order_id_re = /订单编号:[1-9][0-9]*/.exec(document.querySelector("div[class^=misc-info_misc-info__]")?.innerText) if (order_id_re === null) { return null } result.id = order_id_re[0].replace('订单编号:', '') result.products = products return result } function getExportInvoiceInfo() { const values = prompt("请粘贴天猫发票平台导出数据") const list = []; for (let row of values.replace('\r\n', '\n').split('\n')) { const cells = row.split('\t'); list.push(cells); } const T = {}; const title = list[0]; for (let i = 0; i < title.length; i++) { T[title[i]] = i; } const trades = {}; for (let item of list) { const id = item[T['*订单编号']]; if (id === '*订单编号') { continue; } let temp = trades[id] === void 0 ? { 'id': id, 'products': {}, 'company': {} } : trades[id]; // 如果企业信息未空,则填充 if (JSON.stringify(temp.company) === '{}') { temp.company.name = item[T['*发票抬头']] !== void 0 ? item[T['*发票抬头']] : ''; temp.company.id = item[T['购方税号']] !== void 0 ? item[T['购方税号']] : ''; temp.company.address = item[T['企业地址']] !== void 0 ? item[T['企业地址']] : ''; temp.company.phone = item[T['企业电话']] !== void 0 ? item[T['企业电话']] : ''; temp.company.bank = item[T['开户行']] !== void 0 ? item[T['开户行']] : ''; temp.company.account = item[T['开户账号']] !== void 0 ? item[T['开户账号']] : ''; } // 添加商品 let name = item[T['货物名称']]; temp.products[name] = { 'price_invoice': item[T['商品金额']], 'count': item[T['数量']] } // 重新写入trades trades[id] = temp; } return trades; } GM_registerMenuCommand("提取订单信息", () => { const trades = GM_listValues(); for (let item of trades) { GM_deleteValue(item) } let ids = prompt("输入订单号"); for (let k of ['\n', '\r', '\t', ' ', ',', ',,', ',,']) { ids = ids.replaceAll(k, ',') } const arr = ids.split(','); for (let id of arr) { GM_setValue(id, false); } if (arr.length > 0 && arr[0] !== '') { GM_setValue('running', true); GM_setValue('current', arr[0]); window.open(`https://trade.tmall.com/detail/orderDetail.htm?biz_order_id=${arr[0]}`) } }) GM_registerMenuCommand("导入发票信息", () => { const trades = GM_listValues(); const invoices = getExportInvoiceInfo(); for (let item of trades) { // 用订单号查询存储结果,没有结果的设为激活的,下一次处理 const trade = GM_getValue(item, false); console.log(typeof (trade), trade) const invoice = invoices[item]; // 从导入数据提取订单信息 if (trade && invoice !== void 0) { const ps = invoice.products; // 天猫导出的数据 const co = invoice.company; // 天猫导出的数据 console.log("ps=", ps) console.log("co=", co) console.log("trade=", trade) const nps = []; for (let p of trade.products) { if (!p.status.includes("退款成功")) { const p1 = ps[p.title]; nps.push({ 'title': p.title, 'code': p.code, 'price_invoice': p1.price_invoice, 'price_page': p.price_page, 'count': p1.count, 'status': p.status, }) } } trade.products = nps; trade.company = co; GM_setValue(item, trade); } } alert("发票信息导入处理完成"); }) GM_registerMenuCommand("结果=>发票基本信息", () => { const trades = GM_listValues(); let results = ''; for (let item of trades) { // 用订单号查询存储结果,没有结果的设为激活的,下一次处理 const trade = GM_getValue(item, false); if (trade.company !== void 0) { const id = trade.id; const co = trade.company; results += `${id}\t普通发票\t\t是\t\t${co.name}\t\t${co.id}\t${co.address}\t${co.phone}\t${co.bank}\t${co.account}\t${id}\n`; } } console.log(results); GM_setClipboard(results); alert("发票基本信息已复制") }) GM_registerMenuCommand("结果=>发票明细信息", () => { const trades = GM_listValues(); let results = ''; for (let item of trades) { // 用订单号查询存储结果,没有结果的设为激活的,下一次处理 const trade = GM_getValue(item, false); if (trade.company !== void 0) { const id = trade.id; const ps = trade.products; for (let p of ps) { results += `${id}\t${p.code}\t\t\t件\t${p.count}\t\t${p.price_invoice}\t0.13\n`; } } } console.log(results); GM_setClipboard(results); alert("发票明细信息已复制") }) GM_registerMenuCommand("屏蔽发票申请界面元素", () => { if (!document.URL.includes('merchant-invoice')) { return } const els = document.querySelectorAll("div[class*=SearchContent_search]"); const keys = ["是否免赔", "开票倒计时"]; for (let el of els) { for (let key of keys) { if (el.innerText.includes(key)) { el.style.display = "none"; } } } }) /** * * @param {string} selector * @param {number} count 查找次数,默认10 * @param {number} ms 查找间隔,默认500ms * @returns */ async function el_find(selector, count = 10, ms = 500) { let found = false for (let i = 0; i < count; i++) { console.log(`尝试${i + 1}/${count}次查找【${selector}】`) found = await new Promise((resolve) => { setTimeout(() => { resolve(document.querySelector(selector) !== null); }, ms); }) if (found) { console.log('got it ' + selector) break } } return found; } // 订单详情读取 if (!document.URL.includes('https://qn.taobao.com/home.htm/trade-platform/tp/detail')) { return; } setTimeout(async () => { const current = GM_getValue('current', false); if (!current) { return; } if (document.URL.includes(current)) { const p = await get_order_obj() console.log(current, ":", p) if (p.hasRepeat) { console.log(current, '存在重复商品,跳过未统计') } else { GM_setValue(current, p); console.log(GM_getValue(current, `GM获取失败,${current}`)); } } const running = GM_getValue('running', false); if (!running) { return; } const trades = GM_listValues(); for (let item of trades) { // 用订单号查询存储结果,没有结果的设为激活的,下一次处理 if (!GM_getValue(item, false)) { GM_setValue('current', item); window.location = `https://qn.taobao.com/home.htm/trade-platform/tp/detail?bizOrderId=${item}` return; } } GM_deleteValue('running'); GM_deleteValue('current'); alert("订单遍历完成"); }, 3000); })();