7.4 KiB
7.4 KiB
name, description
| name | description |
|---|---|
| tongtool-order-remark | Log into Tongtool ERP, search for a specific order by ID, and add a remark. |
Tongtool Order Remark Automation
This skill automates the process of logging into the Tongtool ERP system, navigating to a specific order using global search, and adding a custom remark to the order details.
Dependencies
- Python 3
DrissionPageddddocrrequests
The Script
This script performs the following steps:
- Opens a headless Chromium browser using DrissionPage (Crucial: 1920x1080 window size).
- Navigates to the Tongtool login page (
passport.tongtool.com). - Uses
ddddocrto solve the captcha and logs in. - Navigates to the ERP dashboard to initialize the session.
- Uses the global search URL to jump directly to the target order's detail page.
- Clicks the "Add Remark" button (
#addOrderRemark). - robustly injects the desired remark into the text area using JS events.
- Clicks the "Save" button to apply the remark using
by_js=True.
from DrissionPage import ChromiumPage, ChromiumOptions
import ddddocr, requests, json, time, os
def add_order_remark(order_id: str, note_text: str, user: str = 'ozonezsjgw@163.com', pwd: str = '111111'):
co = ChromiumOptions()
# Path to playwright Chromium
co.set_browser_path('/home/ubuntu/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome')
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
# CRITICAL: Use a large window size to ensure the responsive UI displays the detail pane and action buttons
co.set_argument('--window-size=1920,1080')
co.headless()
co.auto_port()
driver = ChromiumPage(co)
# Helper: Fix ddddocr misrecognitions
def correct_ocr(raw_code):
cleaned = ''.join(c for c in raw_code if c.isascii() and c.isalnum())
return cleaned.translate(str.maketrans('oliOSsZz', '01105522'))
# Helper: Close layui popups that block interaction
def nuke_popups(page):
try:
page.run_js("""
document.querySelectorAll('.layui-layer-shade, .layui-layer, .modal, .overlay, .jclose-blue, .close, [title="关闭"], .layui-layer-close').forEach(el => { try { el.remove(); } catch(e){} });
""")
except: pass
# Login function with retry mechanism
def login(driver, user, pwd, max_retry=5):
for attempt in range(max_retry):
driver.get('https://passport.tongtool.com/')
time.sleep(3)
driver.ele('css:input[name="username"]').input(user)
driver.ele('css:input[name="password"]').input(pwd)
img = driver.ele('css:img.pic-yzm')
if not img:
time.sleep(2)
continue
src = img.attr('src')
cookies = {c['name']: c['value'] for c in driver.cookies()}
try:
resp = requests.get(src, cookies=cookies, timeout=10)
raw = ddddocr.DdddOcr(show_ad=False).classification(resp.content)
code = correct_ocr(raw)
if len(code) < 4:
continue
driver.ele('css:input[name="captcha"]').input(code)
driver.ele('css:button.btn-primary').click()
time.sleep(5)
except Exception as e:
print(f"Login request failed: {e}")
continue
for _ in range(5):
if 'member' in driver.url or 'dashboard' in driver.url or 'yijiety' in driver.url:
return True
time.sleep(2)
if 'passport' in driver.url:
body = driver.ele('tag:body').text
if '验证码输入错误' in body:
continue # Retry loop
elif '密码' in body and '错误' in body:
print("Wrong password")
return False
return False
print("Attempting login...")
success = login(driver, user, pwd)
if not success:
print("Login failed after max retries.")
driver.quit()
return False
print("Login successful.")
# Enter ERP to set session completely
driver.get("https://yijiety.tongtool.com/dashboard/homepage/index.htm")
time.sleep(8)
nuke_popups(driver)
# Navigate to the specific order using global search
print(f"Navigating to order: {order_id}")
search_url = (
f"https://yijiety.tongtool.com/search/order.htm"
f"?search_text_value={order_id}"
f"&search_mark=salesRecordNumber"
f"&source=global"
)
driver.get(search_url)
time.sleep(12)
nuke_popups(driver)
# Attempt to add the remark
remark_added = False
btn = driver.ele('css:#addOrderRemark')
if btn:
a_tag = btn.ele('tag:a')
if a_tag:
a_tag.click(by_js=True)
time.sleep(3)
textarea = driver.ele('tag:textarea')
if textarea:
print("Writing remark robustly...")
# MUST use JS dispatch for layui to detect the model change
js = f"""
var ta = document.querySelector('textarea');
if(ta) {{
ta.value = '{note_text}';
ta.dispatchEvent(new Event('input', {{bubbles: true}}));
ta.dispatchEvent(new Event('change', {{bubbles: true}}));
}}
"""
driver.run_js(js)
time.sleep(1)
for a in driver.eles('tag:a'):
if a.text.strip() == '保存':
a.click(by_js=True)
time.sleep(3)
print("Remark saved successfully!")
remark_added = True
break
else:
print("Could not find the text area for the remark.")
else:
print("Could not find clickable element inside the remark button.")
else:
print("Could not find the remark button (#addOrderRemark) on the page.")
driver.quit()
return remark_added
if __name__ == '__main__':
# Usage Example:
order_id = "16Ezyhbg-PO-211-05103794125430842"
custom_note = "Testing Order Remark Automation"
add_order_remark(order_id, custom_note)
Pitfalls
- Window Size Requirement (CRITICAL): It is critical to set a large window size (e.g.,
--window-size=1920,1080). The global search page uses a responsive grid. Without a wide window, the details pane (containing the remark button and other actions) may not be exposed or rendered in the DOM natively. - JS Event Dispatching for Textareas (CRITICAL): Using
textarea.input('text')often fails to update the underlying layui/Vue model, resulting in a blank note being saved. Always usedriver.run_jsto setvalueand dispatchinputandchangeevents natively. - JS Click for Save: The save dialog anchor should be clicked with
by_js=True(a.click(by_js=True)) because overlapping layui shades often block standard WebDriver clicks. - CSS Selector for Remark Button: The button has a specific ID. You must use
driver.ele('css:#addOrderRemark'), and then click the<a>tag nested inside it. - Popups Blocking Interaction: Tongtool frequently shows
layuipopups (announcements) that cover the screen. Ensure the aggressivenuke_popupsfunction is called before interacting.