Skip to content

Commit 0444bad

Browse files
authored
Merge pull request #118 from itk-dev-rpa/release/2.14.0
Release/2.14.0
2 parents 5fcb7db + b918b72 commit 0444bad

File tree

5 files changed

+202
-21
lines changed

5 files changed

+202
-21
lines changed

changelog.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.14.0] - 2025-08-22
11+
12+
### Changed
13+
14+
- SAP: Changed how kundekontakter are created with 0 or 1 aftaler.
15+
16+
### Added
17+
18+
- Eflyt: Added functions for sending letters.
19+
1020
## [2.13.0] - 2025-08-13
1121

1222
### Added
@@ -238,7 +248,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
238248

239249
- Initial release
240250

241-
[Unreleased]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/compare/2.13.0...HEAD
251+
[Unreleased]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/compare/2.14.0...HEAD
252+
[2.14.0]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.14.0
242253
[2.13.0]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.13.0
243254
[2.12.1]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.12.1
244255
[2.12.0]: https://github.com/itk-dev-rpa/ITK-dev-shared-components/releases/tag/2.12.0
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"""This module contains functions for sending letters from a case in eFlyt."""
2+
3+
from selenium import webdriver
4+
from selenium.webdriver.common.by import By
5+
from selenium.webdriver.support.select import Select
6+
from selenium.webdriver.support.ui import WebDriverWait
7+
from selenium.webdriver.support import expected_conditions as EC
8+
from selenium.common.exceptions import TimeoutException
9+
10+
from itk_dev_shared_components.eflyt import eflyt_case
11+
12+
13+
def send_letter_to_anmelder(browser: webdriver.Chrome, letter_text: str) -> bool:
14+
"""Open the 'Breve' tab and send a letter to the anmelder using the 'Individuelt brev'-template.
15+
16+
Args:
17+
browser: The webdriver browser object.
18+
original_letter: The title of the original logiværtserklæring.
19+
20+
Returns:
21+
bool: True if the letter was sent.
22+
"""
23+
eflyt_case.change_tab(browser, tab_index=3)
24+
25+
click_letter_template(browser, "- Individuelt brev")
26+
27+
# Select the anmelder as the receiver
28+
select_letter_receiver(browser, "anmelder")
29+
30+
# Click 'Send brev'
31+
browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnSendBrev").click()
32+
33+
# Insert the correct letter text
34+
text_area = browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_txtStandardText")
35+
text_area.clear()
36+
text_area.send_keys(letter_text)
37+
# Click 'Ok'
38+
browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnOK").click()
39+
40+
# Check if a warning appears
41+
if check_digital_post_warning(browser):
42+
# Click 'Nej'
43+
browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnDeleteLetter").click()
44+
return False
45+
46+
# Click 'Ja'
47+
browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_btnSaveLetter").click()
48+
return True
49+
50+
51+
def click_letter_template(browser: webdriver.Chrome, letter_name: str):
52+
"""Click the letter template with the given name under the "Breve" tab.
53+
54+
Args:
55+
browser: The webdriver browser object.
56+
letter_name: The literal name of the letter template to click.
57+
58+
Raises:
59+
ValueError: If the letter wasn't found in the list.
60+
"""
61+
letter_table = browser.find_element(By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_GridViewBreveNew")
62+
rows = letter_table.find_elements(By.TAG_NAME, "tr")
63+
64+
for row in rows:
65+
text = row.find_element(By.XPATH, "td[2]").text
66+
if text == letter_name:
67+
row.find_element(By.XPATH, "td[1]/input").click()
68+
return
69+
70+
raise ValueError(f"Template with the name '{letter_name}' was not found.")
71+
72+
73+
def select_letter_receiver(browser: webdriver.Chrome, receiver_name: str) -> None:
74+
"""Select the receiver for the letter. The search is fuzzy so it's only checked
75+
if the options contains the receiver name.
76+
77+
I some cases there's only one option for the receiver in which
78+
case there's a text label instead of a select element. In this
79+
case the predefined name is still checked against the desired receiver.
80+
81+
Args:
82+
browser: The webdriver browser object.
83+
receiver_name: The name of the receiver to select.
84+
85+
Raises:
86+
ValueError: If the given name isn't found in the select options.
87+
ValueError: If the given name doesn't match the static label.
88+
"""
89+
# Check if there is a select for the receiver name
90+
try:
91+
# Wait for the dropdown to be present
92+
name_select_element = WebDriverWait(browser, 2).until(
93+
EC.presence_of_element_located((By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_ddlModtager"))
94+
)
95+
name_select = Select(name_select_element)
96+
97+
# Wait until the dropdown has more than one option
98+
WebDriverWait(browser, 2).until(lambda browser: len(name_select.options) > 1)
99+
100+
for i, option in enumerate(name_select.options):
101+
if receiver_name in option.text:
102+
name_select.select_by_index(i)
103+
return
104+
105+
raise ValueError(f"'{receiver_name}' wasn't found on the list of possible receivers.")
106+
107+
except TimeoutException:
108+
pass # Continue to the next check if the dropdown is not found
109+
110+
# If there's simply a label for the receiver, check if the name matches
111+
try:
112+
name_label = WebDriverWait(browser, 2).until(
113+
EC.presence_of_element_located((By.ID, "ctl00_ContentPlaceHolder2_ptFanePerson_bcPersonTab_lblModtagerName"))
114+
)
115+
if receiver_name not in name_label.text:
116+
raise ValueError(f"'{receiver_name}' didn't match the predefined receiver.")
117+
except TimeoutException as exc:
118+
raise ValueError("Receiver name label did not load in time.") from exc
119+
120+
121+
def check_digital_post_warning(browser: webdriver.Chrome) -> bool:
122+
"""Check if a red warning text has appeared warning that
123+
a letter must be sent manually.
124+
125+
Args:
126+
browser: The webdriver browser object.
127+
128+
Returns:
129+
bool: True if the warning has appeared.
130+
"""
131+
warning_text = browser.find_elements(By.XPATH, "//font[@color='red']")
132+
return len(warning_text) != 0 and "Dokumentet skal sendes manuelt" in warning_text[0].text

itk_dev_shared_components/eflyt/eflyt_login.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Module for logging into Eflyt/Daedalus/Whatchamacallit using Selenium"""
1+
"""Module for logging into Eflyt using Selenium"""
22
import time
33

44
from selenium import webdriver

itk_dev_shared_components/sap/opret_kundekontakt.py

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,62 @@ def opret_kundekontakter(session, fp: str, aftaler: list[str] | None,
2626
"""
2727
fmcacov.open_forretningspartner(session, fp)
2828

29+
if not aftaler:
30+
_select_no_aftaler(session)
31+
elif len(aftaler) == 1:
32+
_select_single_aftale(session, aftaler[0])
33+
else:
34+
_select_multiple_aftaler(session, aftaler)
35+
36+
# Set art
37+
session.findById("wnd[0]/usr/tabsTSTRIP/tabpT_CA/ssubSUBSR_TSTRIP:SAPLBPT1:0510/cmbBCONTD-CTYPE").Value = art
38+
39+
# Go to editor and paste (lock if multithreaded)
40+
session.findById("wnd[0]/usr/subNOTICE:SAPLEENO:1002/btnEENO_TEXTE-EDITOR").press()
41+
if lock:
42+
lock.acquire()
43+
_set_clipboard(notat)
44+
session.findById("wnd[0]/tbar[1]/btn[9]").press()
45+
if lock:
46+
lock.release()
47+
48+
# Back and save
49+
session.findById("wnd[0]/tbar[0]/btn[3]").press()
50+
session.findById("wnd[0]/tbar[0]/btn[11]").press()
51+
52+
_confirm_kundekontakt(session, notat, art)
53+
54+
55+
def _select_no_aftaler(session):
56+
"""Right click the fp and select 'Opret kundekontakt'.
57+
58+
Args:
59+
session: The sap session object.
60+
"""
61+
session.findById("wnd[0]/shellcont/shell").nodeContextMenu("GP0000000001")
62+
session.findById("wnd[0]/shellcont/shell").selectContextMenuItem("POPUP")
63+
64+
65+
def _select_single_aftale(session, aftale: str):
66+
"""Right click a single aftale and select 'Opret kundekontakt'.
67+
68+
Args:
69+
session: The sap session object.
70+
aftale: The aftale number.
71+
"""
72+
tree = session.findById("wnd[0]/shellcont/shell")
73+
node_key = tree_util.get_node_key_by_text(tree, aftale)
74+
tree.nodeContextMenu(node_key)
75+
tree.selectContextMenuItem("POPUP")
76+
77+
78+
def _select_multiple_aftaler(session, aftaler: list[str]):
79+
"""Use 'Opret kundekontakt-flere' to select multiple aftaler.
80+
81+
Args:
82+
session: The sap session object.
83+
aftaler: The list of aftaler.
84+
"""
2985
# Click 'Opret kundekontakt-flere
3086
session.findById("wnd[0]/shellcont/shell").nodeContextMenu("GP0000000001")
3187
session.findById("wnd[0]/shellcont/shell").selectContextMenuItem("FLERE")
@@ -49,24 +105,6 @@ def opret_kundekontakter(session, fp: str, aftaler: list[str] | None,
49105
aftale_tree.ChangeCheckBox(key, name, True)
50106
session.findById("wnd[1]/usr/cntlCONTAINER_PSOBKEY/shellcont/shell/shellcont[1]/shell[0]").pressButton("OK")
51107

52-
# Set art
53-
session.findById("wnd[0]/usr/tabsTSTRIP/tabpT_CA/ssubSUBSR_TSTRIP:SAPLBPT1:0510/cmbBCONTD-CTYPE").Value = art
54-
55-
# Go to editor and paste (lock if multithreaded)
56-
session.findById("wnd[0]/usr/subNOTICE:SAPLEENO:1002/btnEENO_TEXTE-EDITOR").press()
57-
if lock:
58-
lock.acquire()
59-
_set_clipboard(notat)
60-
session.findById("wnd[0]/tbar[1]/btn[9]").press()
61-
if lock:
62-
lock.release()
63-
64-
# Back and save
65-
session.findById("wnd[0]/tbar[0]/btn[3]").press()
66-
session.findById("wnd[0]/tbar[0]/btn[11]").press()
67-
68-
_confirm_kundekontakt(session, notat, art)
69-
70108

71109
def _set_clipboard(text: str) -> None:
72110
"""Private function to set text to the clipboard.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "itk_dev_shared_components"
7-
version = "2.13.0"
7+
version = "2.14.0"
88
authors = [
99
{ name="ITK Development", email="itk-rpa@mkb.aarhus.dk" },
1010
]

0 commit comments

Comments
 (0)