From f5c8c092be42faff5dcb4091159549b24902a5f6 Mon Sep 17 00:00:00 2001 From: Mathias G Date: Thu, 21 Aug 2025 12:58:03 +0200 Subject: [PATCH 1/7] Changed opret_kundekontakter when 0 or 1 aftale is given --- .../sap/opret_kundekontakt.py | 76 ++++++++++++++----- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/itk_dev_shared_components/sap/opret_kundekontakt.py b/itk_dev_shared_components/sap/opret_kundekontakt.py index 57c91b7..67ff8ce 100644 --- a/itk_dev_shared_components/sap/opret_kundekontakt.py +++ b/itk_dev_shared_components/sap/opret_kundekontakt.py @@ -26,7 +26,63 @@ def opret_kundekontakter(session, fp: str, aftaler: list[str] | None, """ fmcacov.open_forretningspartner(session, fp) - # Click 'Opret kundekontakt-flere + if not aftaler: + _select_no_aftaler(session) + elif len(aftaler) == 1: + _select_single_aftale(session, aftaler[0]) + else: + _select_multiple_aftaler(session, aftaler) + + # Set art + session.findById("wnd[0]/usr/tabsTSTRIP/tabpT_CA/ssubSUBSR_TSTRIP:SAPLBPT1:0510/cmbBCONTD-CTYPE").Value = art + + # Go to editor and paste (lock if multithreaded) + session.findById("wnd[0]/usr/subNOTICE:SAPLEENO:1002/btnEENO_TEXTE-EDITOR").press() + if lock: + lock.acquire() + _set_clipboard(notat) + session.findById("wnd[0]/tbar[1]/btn[9]").press() + if lock: + lock.release() + + # Back and save + session.findById("wnd[0]/tbar[0]/btn[3]").press() + session.findById("wnd[0]/tbar[0]/btn[11]").press() + + _confirm_kundekontakt(session, notat, art) + + +def _select_no_aftaler(session): + """Right click the fp and select 'Opret kundekontakt'. + + Args: + session: The sap session object. + """ + session.findById("wnd[0]/shellcont/shell").nodeContextMenu("GP0000000001") + session.findById("wnd[0]/shellcont/shell").selectContextMenuItem("POPUP") + + +def _select_single_aftale(session, aftale: str): + """Right click a single aftale and select 'Opret kundekontakt'. + + Args: + session: The sap session object. + aftale: The aftale number. + """ + tree = session.findById("wnd[0]/shellcont/shell") + node_key = tree_util.get_node_key_by_text(tree, aftale) + tree.nodeContextMenu(node_key) + tree.selectContextMenuItem("POPUP") + + +def _select_multiple_aftaler(session, aftaler: list[str]): + """Use 'Opret kundekontakt-flere' to select multiple aftaler. + + Args: + session: The sap session object. + aftaler: The list of aftaler. + """ + # Click 'Opret kundekontakt-flere session.findById("wnd[0]/shellcont/shell").nodeContextMenu("GP0000000001") session.findById("wnd[0]/shellcont/shell").selectContextMenuItem("FLERE") @@ -49,24 +105,6 @@ def opret_kundekontakter(session, fp: str, aftaler: list[str] | None, aftale_tree.ChangeCheckBox(key, name, True) session.findById("wnd[1]/usr/cntlCONTAINER_PSOBKEY/shellcont/shell/shellcont[1]/shell[0]").pressButton("OK") - # Set art - session.findById("wnd[0]/usr/tabsTSTRIP/tabpT_CA/ssubSUBSR_TSTRIP:SAPLBPT1:0510/cmbBCONTD-CTYPE").Value = art - - # Go to editor and paste (lock if multithreaded) - session.findById("wnd[0]/usr/subNOTICE:SAPLEENO:1002/btnEENO_TEXTE-EDITOR").press() - if lock: - lock.acquire() - _set_clipboard(notat) - session.findById("wnd[0]/tbar[1]/btn[9]").press() - if lock: - lock.release() - - # Back and save - session.findById("wnd[0]/tbar[0]/btn[3]").press() - session.findById("wnd[0]/tbar[0]/btn[11]").press() - - _confirm_kundekontakt(session, notat, art) - def _set_clipboard(text: str) -> None: """Private function to set text to the clipboard. From bfa3c7c362412e375c81aaca0b55dab51643cfaa Mon Sep 17 00:00:00 2001 From: Mathias G Date: Thu, 21 Aug 2025 14:19:11 +0200 Subject: [PATCH 2/7] Updated changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 5a42e79..d2d221c 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- SAP: Changed how kundekontakter are created with 0 or 1 aftaler. + ## [2.13.0] - 2025-08-13 ### Added From be1fe284a8caacc48a30b3e86d650a2f3d650cfd Mon Sep 17 00:00:00 2001 From: Mathias G Date: Thu, 21 Aug 2025 14:24:07 +0200 Subject: [PATCH 3/7] Lint --- itk_dev_shared_components/sap/opret_kundekontakt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itk_dev_shared_components/sap/opret_kundekontakt.py b/itk_dev_shared_components/sap/opret_kundekontakt.py index 67ff8ce..d08539b 100644 --- a/itk_dev_shared_components/sap/opret_kundekontakt.py +++ b/itk_dev_shared_components/sap/opret_kundekontakt.py @@ -82,7 +82,7 @@ def _select_multiple_aftaler(session, aftaler: list[str]): session: The sap session object. aftaler: The list of aftaler. """ - # Click 'Opret kundekontakt-flere + # Click 'Opret kundekontakt-flere session.findById("wnd[0]/shellcont/shell").nodeContextMenu("GP0000000001") session.findById("wnd[0]/shellcont/shell").selectContextMenuItem("FLERE") From 205b55b6379d1d0baa6b62172fb65ece46c493bc Mon Sep 17 00:00:00 2001 From: kri-bak Date: Thu, 21 Aug 2025 14:55:00 +0200 Subject: [PATCH 4/7] Added script for handling eflyt letters --- changelog.md | 4 + .../eflyt/eflyt_letter.py | 132 ++++++++++++++++++ .../eflyt/eflyt_login.py | 2 +- 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 itk_dev_shared_components/eflyt/eflyt_letter.py diff --git a/changelog.md b/changelog.md index 5a42e79..55b8c05 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Eflyt: Added functions for sending letters. + ## [2.13.0] - 2025-08-13 ### Added diff --git a/itk_dev_shared_components/eflyt/eflyt_letter.py b/itk_dev_shared_components/eflyt/eflyt_letter.py new file mode 100644 index 0000000..8147c61 --- /dev/null +++ b/itk_dev_shared_components/eflyt/eflyt_letter.py @@ -0,0 +1,132 @@ +"""This module contains functions for sending letters from a case in eFlyt.""" + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.select import Select +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import TimeoutException + +from itk_dev_shared_components.eflyt import eflyt_case + + +def send_letter_to_anmelder(browser: webdriver.Chrome, letter_text: str) -> bool: + """Open the 'Breve' tab and send a letter to the anmelder. + + Args: + browser: The webdriver browser object. + original_letter: The title of the original logiværtserklæring. + + Returns: + bool: True if the letter was sent. + """ + eflyt_case.change_tab(browser, tab_index=3) + + click_letter_template(browser, "- Individuelt brev") + + # Select the anmelder as the receiver + select_letter_receiver(browser, "anmelder") + + # Click 'Send brev' + browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnSendBrev").click() + + # Insert the correct letter text + text_area = browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_txtStandardText") + text_area.clear() + text_area.send_keys(letter_text) + # Click 'Ok' + browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnOK").click() + + # Check if a warning appears + if check_digital_post_warning(browser): + # Click 'Nej' + browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnDeleteLetter").click() + return False + + # Click 'Ja' + browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnSaveLetter").click() + return True + + +def click_letter_template(browser: webdriver.Chrome, letter_name: str): + """Click the letter template with the given name under the "Breve" tab. + + Args: + browser: The webdriver browser object. + letter_name: The literal name of the letter template to click. + + Raises: + ValueError: If the letter wasn't found in the list. + """ + letter_table = browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_GridViewBreveNew") + rows = letter_table.find_elements(By.TAG_NAME, "tr") + + for row in rows: + text = row.find_element(By.XPATH, "td[2]").text + if text == letter_name: + row.find_element(By.XPATH, "td[1]/input").click() + return + + raise ValueError(f"Template with the name '{letter_name}' was not found.") + + +def select_letter_receiver(browser: webdriver.Chrome, receiver_name: str) -> None: + """Select the receiver for the letter. The search is fuzzy so it's only checked + if the options contains the receiver name. + + I some cases there's only one option for the receiver in which + case there's a text label instead of a select element. In this + case the predefined name is still checked against the desired receiver. + + Args: + browser: The webdriver browser object. + receiver_name: The name of the receiver to select. + + Raises: + ValueError: If the given name isn't found in the select options. + ValueError: If the given name doesn't match the static label. + """ + # Check if there is a select for the receiver name + try: + # Wait for the dropdown to be present + name_select_element = WebDriverWait(browser, 2).until( + EC.presence_of_element_located((By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_ddlModtager")) + ) + name_select = Select(name_select_element) + + # Wait until the dropdown has more than one option + WebDriverWait(browser, 2).until(lambda browser: len(name_select.options) > 1) + + for i, option in enumerate(name_select.options): + if receiver_name in option.text: + name_select.select_by_index(i) + return + + raise ValueError(f"'{receiver_name}' wasn't found on the list of possible receivers.") + + except TimeoutException: + pass # Continue to the next check if the dropdown is not found + + # If there's simply a label for the receiver, check if the name matches + try: + name_label = WebDriverWait(browser, 2).until( + EC.presence_of_element_located((By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_lblModtagerName")) + ) + if receiver_name not in name_label.text: + raise ValueError(f"'{receiver_name}' didn't match the predefined receiver.") + except TimeoutException as exc: + raise ValueError("Receiver name label did not load in time.") from exc + + +def check_digital_post_warning(browser: webdriver.Chrome) -> bool: + """Check if a red warning text has appeared warning that + a letter must be sent manually. + + Args: + browser: The webdriver browser object. + + Returns: + bool: True if the warning has appeared. + """ + warning_text = browser.find_elements(By.XPATH, "//font[@color='red']") + return len(warning_text) != 0 and "Dokumentet skal sendes manuelt" in warning_text[0].text diff --git a/itk_dev_shared_components/eflyt/eflyt_login.py b/itk_dev_shared_components/eflyt/eflyt_login.py index 9285efa..07cae5e 100644 --- a/itk_dev_shared_components/eflyt/eflyt_login.py +++ b/itk_dev_shared_components/eflyt/eflyt_login.py @@ -1,4 +1,4 @@ -"""Module for logging into Eflyt/Daedalus/Whatchamacallit using Selenium""" +"""Module for logging into Eflyt using Selenium""" import time from selenium import webdriver From 084deb8b708407486ca48a857996dc5080489498 Mon Sep 17 00:00:00 2001 From: Kristian Bakspace Date: Fri, 22 Aug 2025 10:20:44 +0200 Subject: [PATCH 5/7] Update itk_dev_shared_components/eflyt/eflyt_letter.py Co-authored-by: ghbm-itk <123645708+ghbm-itk@users.noreply.github.com> --- itk_dev_shared_components/eflyt/eflyt_letter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itk_dev_shared_components/eflyt/eflyt_letter.py b/itk_dev_shared_components/eflyt/eflyt_letter.py index 8147c61..f6b1167 100644 --- a/itk_dev_shared_components/eflyt/eflyt_letter.py +++ b/itk_dev_shared_components/eflyt/eflyt_letter.py @@ -11,7 +11,7 @@ def send_letter_to_anmelder(browser: webdriver.Chrome, letter_text: str) -> bool: - """Open the 'Breve' tab and send a letter to the anmelder. + """Open the 'Breve' tab and send a letter to the anmelder using the 'Individuelt brev'-template. Args: browser: The webdriver browser object. From 923f99a352586fce5eb00db829e2146281bf844d Mon Sep 17 00:00:00 2001 From: Kristian Bakspace Date: Fri, 22 Aug 2025 10:27:12 +0200 Subject: [PATCH 6/7] Update changelog.md --- changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 7429e8b..d79739d 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.14.0] - 2025-08-22 + ### Changed - SAP: Changed how kundekontakter are created with 0 or 1 aftaler. @@ -246,7 +248,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial release -[Unreleased]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/compare/2.13.0...HEAD +[Unreleased]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/compare/2.14.0...HEAD +[2.14.0]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.14.0 [2.13.0]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.13.0 [2.12.1]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.12.1 [2.12.0]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.12.0 From b918b72ef299bcde4a72b586a66795291c50906a Mon Sep 17 00:00:00 2001 From: Kristian Bakspace Date: Fri, 22 Aug 2025 10:27:22 +0200 Subject: [PATCH 7/7] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2231fbd..eb528fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "itk_dev_shared_components" -version = "2.13.0" +version = "2.14.0" authors = [ { name="ITK Development", email="itk-rpa@mkb.aarhus.dk" }, ]