diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e7b957..5b5555b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + +- Added handling of CVR recipients. + ## [1.1.2] - Updated logging. diff --git a/modules/os2forms_digital_post_examples/config/install/webform.webform.os2forms_digital_post_cvr.yml b/modules/os2forms_digital_post_examples/config/install/webform.webform.os2forms_digital_post_cvr.yml new file mode 100644 index 0000000..a6aadbb --- /dev/null +++ b/modules/os2forms_digital_post_examples/config/install/webform.webform.os2forms_digital_post_cvr.yml @@ -0,0 +1,240 @@ +langcode: da +status: open +dependencies: + enforced: + module: + - os2forms_digital_post_examples + module: + - os2forms_digital_post_examples +third_party_settings: + webform_revisions: + contentEntity_id: null +weight: 0 +open: null +close: null +uid: 1 +template: false +archive: false +id: os2forms_digital_post_cvr +title: 'OS2Forms Digital post example (CVR)' +description: 'Simple example form with a digital post handler' +category: Example +elements: |- + message: + '#type': textarea + '#title': Message + '#required': true + '#default_value': |- + [current-date:long] + + [random:hash:sha512] + recipient_cvr: + '#type': textfield + '#title': 'Recipient CVR' + '#required': true + '#default_value': '43486829' + digital_post_content_pdf: + '#type': 'webform_entity_print_attachment:pdf' + '#title': 'Digital post (PDF)' + '#display_on': view + '#filename': hat-og-briller.pdf +css: '' +javascript: '' +settings: + ajax: false + ajax_scroll_top: form + ajax_progress_type: '' + ajax_effect: '' + ajax_speed: null + page: true + page_submit_path: '' + page_confirm_path: '' + page_theme_name: '' + form_title: both + form_submit_once: false + form_open_message: '' + form_close_message: '' + form_exception_message: '' + form_previous_submissions: true + form_confidential: false + form_confidential_message: '' + form_disable_remote_addr: false + form_convert_anonymous: false + form_prepopulate: false + form_prepopulate_source_entity: false + form_prepopulate_source_entity_required: false + form_prepopulate_source_entity_type: '' + form_unsaved: false + form_disable_back: false + form_submit_back: false + form_disable_autocomplete: false + form_novalidate: false + form_disable_inline_errors: false + form_required: false + form_autofocus: false + form_details_toggle: false + form_reset: false + form_access_denied: default + form_access_denied_title: '' + form_access_denied_message: '' + form_access_denied_attributes: { } + form_file_limit: '' + form_attributes: { } + form_method: '' + form_action: '' + share: false + share_node: false + share_theme_name: '' + share_title: true + share_page_body_attributes: { } + submission_label: '' + submission_exception_message: '' + submission_locked_message: '' + submission_log: false + submission_excluded_elements: { } + submission_exclude_empty: false + submission_exclude_empty_checkbox: false + submission_views: { } + submission_views_replace: { } + submission_user_columns: { } + submission_user_duplicate: false + submission_access_denied: default + submission_access_denied_title: '' + submission_access_denied_message: '' + submission_access_denied_attributes: { } + previous_submission_message: '' + previous_submissions_message: '' + autofill: false + autofill_message: '' + autofill_excluded_elements: { } + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_progress_link: false + wizard_progress_states: false + wizard_start_label: '' + wizard_preview_link: false + wizard_confirmation: true + wizard_confirmation_label: '' + wizard_auto_forward: true + wizard_auto_forward_hide_next_button: false + wizard_keyboard: true + wizard_track: '' + wizard_prev_button_label: '' + wizard_next_button_label: '' + wizard_toggle: false + wizard_toggle_show_label: '' + wizard_toggle_hide_label: '' + preview: 0 + preview_label: '' + preview_title: '' + preview_message: '' + preview_attributes: { } + preview_excluded_elements: { } + preview_exclude_empty: true + preview_exclude_empty_checkbox: false + draft: none + draft_multiple: false + draft_auto_save: false + draft_saved_message: '' + draft_loaded_message: '' + draft_pending_single_message: '' + draft_pending_multiple_message: '' + confirmation_type: message + confirmation_url: '' + confirmation_title: '' + confirmation_message: '' + confirmation_attributes: { } + confirmation_back: true + confirmation_back_label: '' + confirmation_back_attributes: { } + confirmation_exclude_query: false + confirmation_exclude_token: false + confirmation_update: false + limit_total: null + limit_total_interval: null + limit_total_message: '' + limit_total_unique: false + limit_user: null + limit_user_interval: null + limit_user_message: '' + limit_user_unique: false + entity_limit_total: null + entity_limit_total_interval: null + entity_limit_user: null + entity_limit_user_interval: null + purge: all + purge_days: 30 + results_disabled: false + results_disabled_ignore: false + results_customize: false + token_view: false + token_update: false + token_delete: false + serial_disabled: false +access: + create: + roles: + - anonymous + - authenticated + users: { } + permissions: { } + view_any: + roles: { } + users: { } + permissions: { } + update_any: + roles: { } + users: { } + permissions: { } + delete_any: + roles: { } + users: { } + permissions: { } + purge_any: + roles: { } + users: { } + permissions: { } + view_own: + roles: { } + users: { } + permissions: { } + update_own: + roles: { } + users: { } + permissions: { } + delete_own: + roles: { } + users: { } + permissions: { } + administer: + roles: { } + users: { } + permissions: { } + test: + roles: { } + users: { } + permissions: { } + configuration: + roles: { } + users: { } + permissions: { } +handlers: + digital_post_sf1601: + id: digital_post_sf1601 + handler_id: digital_post_sf1601 + label: 'Digital post (sf1601)' + notes: '' + status: true + conditions: { } + weight: 0 + settings: + debug: false + memo_message: + type: 'Automatisk Valg' + recipient_element: recipient_cvr + attachment_element: digital_post_content_pdf + sender_label: 'Hilsen fra [site:url-brief]' + message_header_label: SF1601 + memo_actions: { } +variants: { } diff --git a/os2forms_digital_post.info.yml b/os2forms_digital_post.info.yml index 50181f8..6e2019f 100644 --- a/os2forms_digital_post.info.yml +++ b/os2forms_digital_post.info.yml @@ -7,5 +7,7 @@ core_version_requirement: ^8 || ^9 dependencies: - 'webform:webform' - 'webform:webform_submission_log' + - 'os2forms_cpr_lookup:os2forms_cpr_lookup' + - 'os2forms_cvr_lookup:os2forms_cvr_lookup' configure: os2forms_digital_post.admin.settings diff --git a/os2forms_digital_post.services.yml b/os2forms_digital_post.services.yml index d89bbe8..7788b0d 100644 --- a/os2forms_digital_post.services.yml +++ b/os2forms_digital_post.services.yml @@ -36,6 +36,7 @@ services: - "@Drupal\\os2forms_digital_post\\Helper\\CertificateLocatorHelper" - "@entity_type.manager" - "@os2forms_cpr_lookup.service" + - "@os2forms_cvr_lookup.service" - "@Drupal\\os2forms_digital_post\\Helper\\MeMoHelper" - "@logger.factory" diff --git a/src/Exception/InvalidRecipientDataException.php b/src/Exception/InvalidRecipientDataException.php new file mode 100644 index 0000000..8260b2c --- /dev/null +++ b/src/Exception/InvalidRecipientDataException.php @@ -0,0 +1,10 @@ +setIdType($options[WebformHelperSF1601::RECIPIENT_IDENTIFIER_TYPE]) ->setRecipientID($options[WebformHelperSF1601::RECIPIENT_IDENTIFIER]); - if (NULL !== $cprServiceResult) { - $name = implode(' ', array_filter([ - $cprServiceResult->getFirstName(), - $cprServiceResult->getMiddleName(), - $cprServiceResult->getLastName(), - ])); - - $recipient->setLabel($name); - $address = (new Address()) - ->setCo('') - ->setAddressLabel($cprServiceResult->getStreetName() ?: '') - ->setHouseNumber($cprServiceResult->getHouseNumber() ?: '') - ->setFloor($cprServiceResult->getFloor() ?: '') - ->setDoor($cprServiceResult->getSide() ?: '') - ->setZipCode($cprServiceResult->getPostalCode() ?: '') - ->setCity($cprServiceResult->getCity() ?: '') - ->setCountry('DA'); - $attentionData = (new AttentionData()) - ->setAttentionPerson((new AttentionPerson()) - ->setLabel($recipient->getLabel()) - ->setPersonID($recipient->getRecipientID()) - ) - ->setAddress($address); - - $recipient->setAttentionData($attentionData); - } + $this->enrichRecipient($recipient, $recipientData); $label = $this->replaceTokens($options[WebformHandlerSF1601::MESSAGE_HEADER_LABEL], $submission); $messageHeader = (new MessageHeader()) @@ -159,6 +136,65 @@ public function buildMessage(WebformSubmissionInterface $submission, array $opti return $message; } + /** + * Enrich recipient with additional data from a lookup. + */ + private function enrichRecipient(Recipient $recipient, $recipientData = NULL): Recipient { + if ($recipientData instanceof CprServiceResult) { + $name = implode(' ', array_filter([ + $recipientData->getFirstName(), + $recipientData->getMiddleName(), + $recipientData->getLastName(), + ])); + + $recipient->setLabel($name); + $address = (new Address()) + ->setCo('') + ->setAddressLabel($recipientData->getStreetName() ?: '') + ->setHouseNumber($recipientData->getHouseNumber() ?: '') + ->setFloor($recipientData->getFloor() ?: '') + ->setDoor($recipientData->getSide() ?: '') + ->setZipCode($recipientData->getPostalCode() ?: '') + ->setCity($recipientData->getCity() ?: '') + ->setCountry('DA'); + $attentionData = (new AttentionData()) + ->setAttentionPerson((new AttentionPerson()) + ->setLabel($recipient->getLabel()) + ->setPersonID($recipient->getRecipientID()) + ) + ->setAddress($address); + + $recipient->setAttentionData($attentionData); + } + elseif ($recipientData instanceof CvrServiceResult) { + $name = $recipientData->getName(); + + $recipient->setLabel($name); + $address = (new Address()) + ->setCo('') + ->setAddressLabel($recipientData->getStreetName() ?: '') + ->setHouseNumber($recipientData->getHouseNumber() ?: '') + ->setFloor($recipientData->getFloor() ?: '') + ->setDoor($recipientData->getSide() ?: '') + ->setZipCode($recipientData->getPostalCode() ?: '') + ->setCity($recipientData->getCity() ?: '') + ->setCountry('DA'); + $attentionData = (new AttentionData()) + ->setAttentionPerson((new AttentionPerson()) + ->setLabel($recipient->getLabel()) + ->setPersonID($recipient->getRecipientID()) + ) + ->setAddress($address); + + $recipient->setAttentionData($attentionData); + } + elseif (NULL !== $recipientData) { + throw new InvalidRecipientDataException(sprintf('Cannot handle recipient data of type %s', is_scalar($recipientData) ? gettype($recipientData) : get_class($recipientData))); + } + + return $recipient; + } + /** * Convert MeMo message to DOM document. */ diff --git a/src/Helper/WebformHelperSF1601.php b/src/Helper/WebformHelperSF1601.php index 0ce4077..17f6d94 100644 --- a/src/Helper/WebformHelperSF1601.php +++ b/src/Helper/WebformHelperSF1601.php @@ -10,6 +10,7 @@ use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Core\Logger\LoggerChannelInterface; use Drupal\os2forms_cpr_lookup\Service\CprServiceInterface; +use Drupal\os2forms_cvr_lookup\Service\CvrServiceInterface; use Drupal\os2forms_digital_post\Exception\InvalidRecipientIdentifierElementException; use Drupal\os2forms_digital_post\Exception\RuntimeException; use Drupal\os2forms_digital_post\Exception\SubmissionNotFoundException; @@ -68,6 +69,13 @@ final class WebformHelperSF1601 implements LoggerInterface { */ protected CprServiceInterface $cprService; + /** + * The CVR service. + * + * @var \Drupal\os2forms_cvr_lookup\Service\CvrServiceInterface + */ + protected CvrServiceInterface $cvrService; + /** * The MeMo helper. * @@ -97,6 +105,7 @@ public function __construct( CertificateLocatorHelper $certificateLocatorHelper, EntityTypeManagerInterface $entityTypeManager, CprServiceInterface $cprService, + CvrServiceInterface $cvrService, MeMoHelper $meMoHelper, LoggerChannelFactoryInterface $loggerChannelFactory ) { @@ -105,6 +114,7 @@ public function __construct( $this->webformSubmissionStorage = $entityTypeManager->getStorage('webform_submission'); $this->queueStorage = $entityTypeManager->getStorage('advancedqueue_queue'); $this->cprService = $cprService; + $this->cvrService = $cvrService; $this->meMoHelper = $meMoHelper; $this->logger = $loggerChannelFactory->get('os2forms_digital_post'); $this->submissionLogger = $loggerChannelFactory->get('webform_submission'); @@ -143,8 +153,6 @@ public function sendDigitalPost(WebformSubmissionInterface $submission, array $h $message)); } - // @todo handle CVR recipient - $recipientIdentifierType = 'CPR'; $recipientIdentifier = $submissionData[$recipientIdentifierKey] ?? NULL; if (NULL === $recipientIdentifier) { $message = 'Recipient identifier element (key: @element_key) not found in submission'; @@ -157,12 +165,29 @@ public function sendDigitalPost(WebformSubmissionInterface $submission, array $h $message)); } - try { - // Validate recipient identifier. - $cprServiceResult = $this->cprService->search($recipientIdentifier); + // Remove all non-digits from recipient identifier. + $recipientIdentifier = preg_replace('/[^\d]+/', '', $recipientIdentifier); + + $cprServiceResult = NULL; + $cvrServiceResult = NULL; + + if (preg_match('/^\d{8}$/', $recipientIdentifier)) { + try { + $cvrServiceResult = $this->cvrService->search($recipientIdentifier); + $recipientIdentifierType = 'CVR'; + } + catch (ServiceException $serviceException) { + throw new RuntimeException('Cannot validate recipient CVR'); + } } - catch (ServiceException $serviceException) { - throw new RuntimeException('Cannot validate recepient identifier'); + else { + try { + $cprServiceResult = $this->cprService->search($recipientIdentifier); + $recipientIdentifierType = 'CPR'; + } + catch (ServiceException $serviceException) { + throw new RuntimeException('Cannot validate recipient CPR'); + } } $senderSettings = $this->settings->getSender(); @@ -176,7 +201,7 @@ public function sendDigitalPost(WebformSubmissionInterface $submission, array $h WebformHandlerSF1601::SENDER_LABEL => $handlerMessageSettings[WebformHandlerSF1601::SENDER_LABEL], WebformHandlerSF1601::MESSAGE_HEADER_LABEL => $handlerMessageSettings[WebformHandlerSF1601::MESSAGE_HEADER_LABEL], ]; - $message = $this->meMoHelper->buildMessage($submission, $messageOptions, $handlerSettings, $submissionData, $cprServiceResult); + $message = $this->meMoHelper->buildMessage($submission, $messageOptions, $handlerSettings, $submissionData, $cprServiceResult ?? $cvrServiceResult); $options = [ 'test_mode' => (bool) $this->settings->getTestMode(),