180 lines
7.4 KiB
Markdown
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. |