Files

180 lines
7.4 KiB
Markdown

---
name: tongtool-order-remark
description: 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
- `DrissionPage`
- `ddddocr`
- `requests`
## The Script
This script performs the following steps:
1. Opens a headless Chromium browser using DrissionPage (Crucial: 1920x1080 window size).
2. Navigates to the Tongtool login page (`passport.tongtool.com`).
3. Uses `ddddocr` to solve the captcha and logs in.
4. Navigates to the ERP dashboard to initialize the session.
5. Uses the global search URL to jump directly to the target order's detail page.
6. Clicks the "Add Remark" button (`#addOrderRemark`).
7. robustly injects the desired remark into the text area using JS events.
8. Clicks the "Save" button to apply the remark using `by_js=True`.
```python
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 use `driver.run_js` to set `value` and dispatch `input` and `change` events 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 `layui` popups (announcements) that cover the screen. Ensure the aggressive `nuke_popups` function is called before interacting.