# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is a Python automation project using Playwright to interact with a Chinese ERP system (YonBIP/用友). The scripts automate login, navigation, form filling, and data submission workflows in a nested iframe environment. ## Environment Setup ```bash # Activate the virtual environment (Windows) .venv\Scripts\activate # Install all dependencies pip install -r requirements.txt # Install Playwright browsers playwright install chromium ``` ## Running Scripts ```bash # Run the main test script python tests/test_playwright.py # Run the record script python record.py # Run database query test python tests/test_db_query.py ``` ## Key Dependencies - **playwright==1.57.0**: Browser automation framework - **pyodbc**: SQL Server database connectivity - **pandas**: Data processing and Excel file handling - **openpyxl**: Excel file operations - Uses synchronous API (`playwright.sync_api`) ## Project Structure ``` playwrite/ ├── config/ # Configuration files (database, etc.) ├── db/ # Database connection components ├── data/ # Data files (excluded from git) ├── docs/ # Documentation (e.g., DEPENDENCIES.md) ├── tests/ # Test scripts (excluded from git) ├── requirements.txt # Python dependencies ├── .gitignore # Git ignore rules └── CLAUDE.md # This file ``` ## File Organization Rules **IMPORTANT**: When creating test scripts, always place them in the `tests/` folder: - Test scripts should be prefixed with `test_` - All files in `tests/` are excluded from git tracking - Examples: `tests/test_playwright.py`, `tests/test_db_query.py` **Configuration Management**: - Place configuration files in `config/` folder - Database credentials and settings go in `config/database_config.py` **Data Storage**: - Use `data/` folder for temporary data files - This folder is excluded from git tracking ## Architecture ### Nested Iframe Structure The target application uses deeply nested iframes that require careful handling: ``` page (browser context) └── #forwardFrame (main iframe) └── #mainiframe (inner iframe, contains actual application UI) ``` **Critical Pattern**: When accessing nested iframes, always wait for the inner iframe to be visible before extracting its content frame: ```python outer_frame = page.locator("#forwardFrame").content_frame inner_frame_locator = outer_frame.locator("#mainiframe") inner_frame_locator.wait_for(state="visible", timeout=15000) inner_frame = inner_frame_locator.content_frame ``` ### Loading State Management The application uses loading overlays ("加载中") that must be detected and waited for: ```python # Detect and wait for loading to complete loading_locator = frame.locator("div").filter(has_text="加载中").nth(1) try: loading_locator.wait_for(state="visible", timeout=3000) loading_locator.wait_for(state="hidden", timeout=0) # Infinite wait except TimeoutError: # Loading completed quickly or never appeared pass ``` ### Page Navigation Pattern New windows/popups are handled using `expect_popup()`: ```python with page.expect_popup() as popup_info: some_button.click() new_page = popup_info.value ``` ### Database Connection The project uses `pyodbc` to connect to SQL Server for data queries: ```python from db.connection import get_connection # Using context manager with get_connection() as db: results = db.execute_query("SELECT * FROM table") # Connection automatically closed # Using the query helper from db.connection import query_production_orders results = query_production_orders(['ID1', 'ID2', 'ID3']) ``` Database configuration is stored in `config/database_config.py`: ```python SQL_SERVER_CONFIG = { 'driver': 'ODBC Driver 18 for SQL Server', 'server': '192.168.110.114', 'database': 'CompanyDB', 'username': 'peng', 'password': 'Cqbld123456.', 'TrustServerCertificate': 'yes' } ``` ## Common Utilities ### `get_input_by_label(frame, label_text)` Locates input fields by their associated label text. Searches upward through parent containers to find the input: ```python input_box = get_input_by_label(frame, "生产部门") if input_box: current_value = input_box.input_value() input_box.fill("new value") ``` ### `click_button_until_disappear(frame, button_name, max_attempts, max_duration)` Continuously clicks a button until it disappears from the DOM. Includes logic to auto-fill missing data before each click. ## Development Notes - **Browser launch**: `chromium.launch(headless=False)` for debugging - **HTTPS errors**: Ignored with `ignore_https_errors=True` due to self-signed certificates on target system - **Locators**: Uses role-based locators (`get_by_role()`) where possible, falling back to CSS selectors and text filtering - **Text matching**: Uses regular expressions for matching text with patterns like `re.compile(r"^生产部门$")` - **Date format**: "YYYY-MM-DD" (e.g., "2025-12-28") - **Language**: Application is in Chinese; comments and print statements use Chinese for clarity - **Debugging**: Uses `pdb.set_trace()` for interactive debugging sessions ## Target Application Details - **URL**: `https://68.11.34.30:8082/` (internal network) - **System**: YonBIP (用友) ERP system - **Login**: Requires username and password; may show "force login" confirmation dialog - **Key workflows**: - "补货安排" (Replenishment Arrangement) - "生产订单" (Production Orders) - Batch data submission with "保存提交" (Save & Submit) button