""" Authentication module - Responsible for Yonyou BIP system login and logout operations """ from playwright.sync_api import Playwright, Browser, BrowserContext, Page, Frame def login( playwright: Playwright, username: str, password: str, url: str, headless: bool, ignore_https_errors: bool, verbose: bool = True, ) -> tuple[Browser, BrowserContext, Page, Frame]: """ Login to Yonyou BIP system Args: playwright: Playwright instance username: Username (required) password: Password (required) url: Complete login page URL (required) headless: Whether to use headless mode (required) ignore_https_errors: Whether to ignore HTTPS errors (required) verbose: Whether to print detailed logs (default: True) Returns: tuple: (browser, context, page, main_frame) - browser: Browser instance - context: Browser context - page: Page object - main_frame: Main iframe after login (forwardFrame) """ import time # Validate required parameters if not username or not username.strip(): raise ValueError("username is required and cannot be empty or whitespace") if not password or not password.strip(): raise ValueError("password is required and cannot be empty or whitespace") if not url or not url.strip(): raise ValueError("url is required and cannot be empty or whitespace") # Launch browser browser = playwright.chromium.launch(headless=headless) # Create context context = browser.new_context(ignore_https_errors=ignore_https_errors) # Create page page = context.new_page() # Navigate to login page page.goto(url) # Extract main iframe (forwardFrame) main_frame = page.locator("#forwardFrame").content_frame # Fill username main_frame.get_by_role("textbox", name="用户名").fill(username) # Fill password main_frame.get_by_role("textbox", name="密码").fill(password) # Click login button main_frame.get_by_role("button", name="登录").click() # Handle force login popup (check if exists) confirm_btn = main_frame.get_by_role("button", name="确定") if confirm_btn.count() > 0: confirm_btn.click() if verbose: print("Force login detected") else: if verbose: print("Normal login") return browser, context, page, main_frame def logout(main_frame: Frame, verbose: bool = True) -> None: """ Execute account logout Args: main_frame: Main iframe object (forwardFrame) verbose: Whether to print detailed logs """ import time if verbose: print("Clicking account menu button...") # Element 1: Account menu button (logo icon) main_frame.get_by_role("img", name="logo").click() # Wait for menu to appear time.sleep(1) if verbose: print("Clicking logout button...") # Element 2: "Logout" button main_frame.get_by_text("退出登录").click() # Wait for confirmation dialog to appear time.sleep(1) if verbose: print("Waiting for logout confirmation dialog...") # Element 3: Logout confirmation dialog (check if appears) try: confirm_text = main_frame.get_by_text("退出确定要退出当前账号吗?") if verbose: print("Found confirmation dialog, clicking confirm button") # Element 4: Confirm button main_frame.get_by_role("button", name="确定(Y)").click() except: if verbose: print("Confirmation dialog not found, may have auto-logged out") time.sleep(2) # Wait for logout to complete def close_session(browser: Browser, context: BrowserContext) -> None: """ Close browser session Args: browser: Browser instance context: Browser context """ # Check if auto-close browser is enabled auto_close = os.getenv("ERP_AUTO_CLOSE_BROWSER", "true").lower() in ("true", "1", "yes") if auto_close: context.close() browser.close() else: print("Note: Browser not auto-closed (ERP_AUTO_CLOSE_BROWSER=false)")