quyenca/TCB_PRINTING
0
1import flet as ft2import threading3import time4from selenium import webdriver5from selenium.webdriver.firefox.service import Service6from selenium.webdriver.firefox.options import Options7from selenium.webdriver.support.ui import WebDriverWait8from selenium.webdriver.support import expected_conditions as EC9from selenium.webdriver.common.by import By10import pandas as pd11from datetime import datetime12import logging13import uuid14from Utils import PandasTable15 16 17# Set up logging18logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')19logger = logging.getLogger(__name__)20 21# Simulated user database22USER_CREDENTIALS = {"user":"password"}23 24# Selenium Functions25def mo_trinh_duyet():26 gecko_path =r"D:\Quyen\XuLyDuLieu\Du an\TCB_printing\geckodriver.exe"27 profile = webdriver.FirefoxProfile()28 profile.set_preference("print.always_print_silent", True)29 profile.set_preference("print.show_print_progress", False)30 options = Options()31 options.profile = profile32 service = Service(executable_path=gecko_path)33 driver = webdriver.Firefox(options=options,34 service=service)35 path = "https://ib.techcombank.com.vn/corp/servlet/BrowserServlet"36 driver.get(path)37 time.sleep(2)38 print('''Mo trinh duyet thanh cong''')39 return driver40 41def log_in(driver, username, password):42 try:43 user = WebDriverWait(driver, 10).until(44 EC.presence_of_element_located(45 (By.XPATH, '/html/body/div/form/div[3]/div[2]/div[2]/div[1]/div[1]/div[2]/input'))46 )47 user.send_keys(username)48 pass_word = driver.find_element(By.XPATH, '/html/body/div/form/div[3]/div[2]/div[2]/div[1]/div[1]/div[4]/input')49 pass_word.send_keys(password)50 dang_nhap = driver.find_element(By.XPATH,51 '/html/body/div/form/div[3]/div[2]/div[2]/div[1]/div[1]/div[9]/input[1]')52 dang_nhap.click()53 time.sleep(2)54 logger.info("Login successful")55 return True56 except Exception as e:57 logger.error(f"Login failed: {str(e)}")58 return False59 60def button_click(driver, xpath, description="button"):61 try:62 btn = WebDriverWait(driver, 10).until(63 EC.element_to_be_clickable((By.XPATH, xpath))64 )65 btn.click()66 logger.info(f"Clicked {description}")67 return True68 except Exception as e:69 logger.error(f"Failed to click {description}: {str(e)}")70 return False71 72def fill_in_blank(driver, xpath, value, field_name="field"):73 try:74 txt_box = WebDriverWait(driver, 10).until(75 EC.presence_of_element_located((By.XPATH, xpath))76 )77 txt_box.clear()78 txt_box.send_keys(value)79 logger.info(f"Filled {field_name} with value: {value}")80 return True81 except Exception as e:82 logger.error(f"Failed to fill {field_name} with value {value}: {str(e)}")83 return False84 85def fetch_transactions(driver, account_number, start_date, end_date):86 try:87 if not button_click(driver, '/html/body/div/table/tbody/tr[2]/td/table/tbody/tr/td[1]/div[3]/ul[1]/li/span',88 "Account Info menu"):89 return None90 if not button_click(driver,91 '/html/body/div/table/tbody/tr[2]/td/table/tbody/tr/td[1]/div[3]/ul[1]/li/ul/li[1]/a',92 "Transaction Query link"):93 return None94 95 if not fill_in_blank(driver, '//*[@id="fieldName:FIELD.1"]', account_number, "Account Number"):96 return None97 if not fill_in_blank(driver, '//*[@id="fieldName:DATE.FIELD.1"]', start_date, "Start Date"):98 return None99 if not fill_in_blank(driver, '//*[@id="fieldName:DATE.FIELD.2"]', end_date, "End Date"):100 return None101 102 if not button_click(driver,103 '/html/body/div/table/tbody/tr[2]/td/table/tbody/tr/td[2]/div[3]/div[2]/form[1]/div[4]/table[2]/tbody/tr/td[2]/a',104 "Execute Query button"):105 return None106 107 body_path = '/html/body/div[1]/table/tbody/tr[2]/td/table/tbody/tr/td[2]/div[3]/div/form/div/table/tbody/tr[2]/td[2]/div[2]/div/table/tbody'108 body = WebDriverWait(driver, 10).until(109 EC.presence_of_element_located((By.XPATH, body_path))110 )111 rows = body.find_elements(By.TAG_NAME, 'tr')112 lst = []113 for index, row in enumerate(rows, start=1):114 cells = row.find_elements(By.TAG_NAME, 'td')115 dic = {'Số TT': index}116 for i, cell in enumerate(cells, start=1):117 dic[f"Cột {i}"] = cell.text118 lst.append(dic)119 120 dt = pd.DataFrame(lst)121 dt.rename(columns={122 'Số TT': 'Số TT',123 'Cột 1': 'Ngày',124 'Cột 2': 'Số bút toán',125 'Cột 3': 'Tài khoản đích',126 'Cột 4': 'Diễn giải',127 'Cột 5': 'Số tiền ghi nợ',128 'Cột 6': 'Số tiền ghi có',129 'Cột 7': 'Số dư',130 'Cột 8': 'Print'131 }, inplace=True)132 # dt['Số tiền ghi nợ'] = dt['Số tiền ghi nợ'].str.replace(',', '')133 # dt['Số tiền ghi có'] = dt['Số tiền ghi có'].str.replace(',', '')134 # dt['Số tiền ghi nợ'] = pd.to_numeric(dt['Số tiền ghi nợ'])135 # dt['Số tiền ghi có'] = pd.to_numeric(dt['Số tiền ghi có'])136 logger.info(f"Fetched {len(dt)} transactions")137 return dt138 except Exception as e:139 logger.error(f"Failed to fetch transactions: {str(e)}")140 return None141 142def print_transaction(driver, index, main_window):143 try:144 print_buttons = driver.find_elements(By.XPATH,145 "/html/body/div/table/tbody/tr[2]/td/table/tbody/tr/td[2]/div[3]/div/form/div/table/tbody/tr[2]/td[2]/div[2]/div/table/tbody/tr/td[8]/a/img")146 print_buttons[index].click()147 WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1)148 for handle in driver.window_handles:149 if handle != main_window:150 driver.switch_to.window(handle)151 break152 time.sleep(3)153 button_click(driver, '/html/body/div/table/tbody/tr[1]/td/a/img', "Print command")154 driver.close()155 driver.switch_to.window(main_window)156 logger.info(f"Printed transaction at index {index}")157 return True158 except Exception as e:159 logger.error(f"Failed to print transaction at index {index}: {str(e)}")160 driver.switch_to.window(main_window)161 return False162 163# Flet App164def main(page: ft.Page):165 page.title = "Bank Transaction Printer"166 page.theme_mode = ft.ThemeMode.LIGHT167 page.padding = 20168 page.window_width = 1200169 page.window_height = 800170 driver = None171 main_window = None172 transactions = None173 printed_transactions = []174 175 def show_login_view():176 page.controls.clear()177 username = ft.TextField(label="Username", width=300)178 password = ft.TextField(label="Password", password=True, width=300,can_reveal_password=True)179 show_password = ft.IconButton(180 icon=ft.icons.VISIBILITY_OFF,181 on_click=lambda e: toggle_password_visibility(e, password)182 )183 login_error = ft.Text(color=ft.Colors.RED)184 login_btn = ft.ElevatedButton("Login", on_click=lambda e: start_login_thread())185 186 def start_login_thread():187 login_error.value = ""188 login_btn.disabled = True189 login_btn.text = "Starting browser..."190 page.update()191 threading.Thread(target=login_task, daemon=True).start()192 193 def login_task():194 nonlocal driver, main_window195 try:196 # Attempt to open browser197 driver = mo_trinh_duyet()198 main_window = driver.current_window_handle199 # Actually perform login on the portal200 if not log_in(driver, username.value, password.value):201 login_error.value = "Failed to log in to bank portal. Please check credentials."202 login_btn.disabled = False203 login_btn.text = "Login"204 page.update()205 return206 207 # If successful, proceed to main view208 show_main_view(username.value, password.value)209 except Exception as e:210 login_error.value = f"Could not start browser or login: {str(e)}"211 login_btn.disabled = False212 login_btn.text = "Login"213 page.update()214 215 page.add(216 ft.Column(217 [218 ft.Text("Login", size=30, weight=ft.FontWeight.BOLD),219 ft.Row([username], spacing=0,alignment=ft.MainAxisAlignment.CENTER),220 ft.Row([password], spacing=0, alignment=ft.MainAxisAlignment.CENTER),221 login_error,222 login_btn223 ],224 alignment=ft.MainAxisAlignment.CENTER,225 horizontal_alignment=ft.CrossAxisAlignment.CENTER,226 spacing=20227 )228 )229 page.update()230 231 def show_main_view(user_name,password):232 page.controls.clear()233 # account_number = ft.TextField(label="Account Number", value="19025071243072", width=300)234 account_number = ft.Dropdown(235 label="Account Number",236 width=300,237 options=[238 ft.dropdown.Option("19025071243072"),239 ft.dropdown.Option("10225071243011"),240 ft.dropdown.Option("19125071243018"),241 ],242 value="19025071243072" # giá trị mặc định243 )244 start_date = ft.TextField(label="Start Date (DD/MM/YYYY)", value="15/05/2025", width=300)245 end_date = ft.TextField(label="End Date (DD/MM/YYYY)", value="20/05/2025", width=300)246 status_text = ft.Text()247 error_log = ft.Text(value="", color=ft.Colors.RED, size=12)248 stats_text = ft.Text()249 data_table = ft.DataTable(250 columns=[251 ft.DataColumn(ft.Text("Select")),252 ft.DataColumn(ft.Text("Số TT")),253 ft.DataColumn(ft.Text("Ngày")),254 ft.DataColumn(ft.Text("Số bút toán")),255 ft.DataColumn(ft.Text("Tài khoản đích")),256 ft.DataColumn(ft.Text("Diễn giải")),257 ft.DataColumn(ft.Text("Số tiền ghi nợ")),258 ft.DataColumn(ft.Text("Số tiền ghi có")),259 ft.DataColumn(ft.Text("Số dư"))260 ],261 rows=[],262 column_spacing=10,263 horizontal_lines=ft.BorderSide(1, ft.Colors.GREY),264 vertical_lines=ft.BorderSide(1, ft.Colors.GREY)265 )266 print_status = ft.Text()267 printed_list = ft.ListView(expand=True, spacing=5, padding=10)268 269 def update_statistics():270 if transactions is not None and not transactions.empty:271 total_debit = pd.to_numeric(transactions['Số tiền ghi nợ'].str.replace(',',''), errors='coerce').sum()272 total_credit = pd.to_numeric(transactions['Số tiền ghi có'].str.replace(',',''), errors='coerce').sum()273 stats_text.value = f"Total Debit: {total_debit:,.2f} | Total Credit: {total_credit:,.2f}"274 else:275 stats_text.value = "No data available"276 page.update()277 278 def update_printed_list():279 printed_list.controls.clear()280 for idx in printed_transactions:281 if idx < len(transactions):282 row = transactions.iloc[idx]283 printed_list.controls.append(284 ft.Text(f"Printed: {row['Ngày']} - {row['Số bút toán']} - {row['Diễn giải']}")285 )286 page.update()287 288 def toggle_select(value: bool):289 for row in data_table.rows:290 row.cells[0].content.value = value291 page.update()292 293 def fetch_data(e):294 nonlocal driver, main_window, transactions295 status_text.value = "Fetching data..."296 error_log.value = ""297 page.update()298 299 def run_selenium():300 nonlocal driver, transactions301 try:302 if driver is None:303 status_text.value = "Browser not started. Please login again."304 page.update()305 return306 307 transactions = fetch_transactions(driver, account_number.value, start_date.value, end_date.value)308 if transactions is not None and not transactions.empty:309 data_table.rows.clear()310 for idx, row in transactions.iterrows():311 checkbox = ft.Checkbox(value=False, data=idx)312 data_table.rows.append(313 ft.DataRow(cells=[314 ft.DataCell(checkbox),315 ft.DataCell(ft.Text(str(row['Số TT']))),316 ft.DataCell(ft.Text(row['Ngày'])),317 ft.DataCell(ft.Text(row['Số bút toán'])),318 ft.DataCell(ft.Text(row['Tài khoản đích'])),319 ft.DataCell(ft.Text(row['Diễn giải'])),320 ft.DataCell(ft.Text(row['Số tiền ghi nợ'])),321 ft.DataCell(ft.Text(row['Số tiền ghi có'])),322 ft.DataCell(ft.Text(row['Số dư']))323 ])324 )325 status_text.value = "Data fetched successfully"326 update_statistics()327 else:328 status_text.value = "No transactions found or error occurred"329 error_log.value = "Check portal for results or verify account/dates"330 except Exception as ex:331 status_text.value = "Error during data fetch"332 error_log.value = f"Error: {str(ex)}"333 finally:334 page.update()335 336 threading.Thread(target=run_selenium, daemon=True).start()337 338 # threading.Thread(target=run_selenium, daemon=True).start()339 340 def print_selected(e):341 nonlocal driver, main_window342 if driver is None or transactions is None:343 print_status.value = "Please fetch data first"344 page.update()345 return346 347 selected_indices = [row.cells[0].content.data for row in data_table.rows if row.cells[0].content.value]348 if not selected_indices:349 print_status.value = "No transactions selected"350 page.update()351 return352 353 def run_print():354 nonlocal printed_transactions355 printed = []356 for idx in selected_indices:357 if print_transaction(driver, idx, main_window):358 printed.append(idx)359 printed_transactions.extend(printed)360 print_status.value = f"Printed {len(printed)} transaction(s) successfully"361 update_printed_list()362 page.update()363 364 print_status.value = "Printing..."365 page.update()366 threading.Thread(target=run_print, daemon=True).start()367 368 horizontal_scroll_container = ft.Container(369 content=data_table,370 width=1000, # hoặc giá trị lớn hơn màn hình nếu muốn scroll ngang371 scale=True, # kích hoạt scroll ngang khi tràn372 373 )374 375 # ListView để cuộn dọc376 list_view = ft.ListView(377 controls=[horizontal_scroll_container],378 expand=True379 )380 page.add(381 ft.Column(382 [383 ft.Text("Bank Transaction Printer", size=30, weight=ft.FontWeight.BOLD),384 ft.Row([account_number, start_date, end_date], spacing=20),385 ft.Row([386 ft.ElevatedButton("Fetch Transactions", on_click=fetch_data, icon=ft.Icons.DOWNLOAD),387 ft.ElevatedButton("Select All", on_click=lambda _: toggle_select(True), icon=ft.Icons.SELECT_ALL),388 ft.ElevatedButton("Deselect All", on_click=lambda _: toggle_select(False), icon=ft.Icons.DESELECT),389 ft.ElevatedButton("Print Selected", on_click=print_selected, icon=ft.Icons.PRINT)390 ], spacing=10),391 status_text,392 stats_text,393 error_log,394 # ft.Container(395 ft.Container(396 content = ft.Column([data_table], scroll=ft.ScrollMode.ALWAYS),397 border=ft.border.all(1, ft.Colors.BLACK),398 padding=10,399 width=1150,400 height=400,401 ),402 print_status,403 ft.Container(404 content=ft.Column([405 ft.Text("Printed Transactions:", weight=ft.FontWeight.BOLD),406 printed_list407 ]),408 border=ft.border.all(1, ft.Colors.BLACK),409 padding=10,410 width=1100,411 height=100,412 )413 ],414 spacing=20415 )416 )417 page.update()418 419 def cleanup():420 nonlocal driver421 if driver:422 try:423 driver.quit()424 except:425 pass426 427 page.on_close = cleanup428 show_login_view()429 430if __name__ == "__main__":431 ft.app(target=main)