diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b5555b..f7bf17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Added handling of CVR recipients. +- Added handling of Beskedfordeler messages. ## [1.1.2] diff --git a/composer.json b/composer.json index aaf1734..e3792d2 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "dompdf/dompdf": "~0.8.0", "drupal/webform": "^6.0", "http-interop/http-factory-guzzle": "^1.0.0", + "itk-dev/beskedfordeler-drupal": "^1.0", "itk-dev/os2forms_cpr_lookup": "^1.6", "itk-dev/serviceplatformen": "^1.2", "os2forms/os2forms": "^3.0", diff --git a/os2forms_digital_post.info.yml b/os2forms_digital_post.info.yml index 6e2019f..764bd20 100644 --- a/os2forms_digital_post.info.yml +++ b/os2forms_digital_post.info.yml @@ -5,9 +5,10 @@ package: 'OS2Forms' core: 8.x core_version_requirement: ^8 || ^9 dependencies: - - 'webform:webform' - - 'webform:webform_submission_log' + - 'beskedfordeler:beskedfordeler' - 'os2forms_cpr_lookup:os2forms_cpr_lookup' - 'os2forms_cvr_lookup:os2forms_cvr_lookup' + - 'webform:webform' + - 'webform:webform_submission_log' configure: os2forms_digital_post.admin.settings diff --git a/os2forms_digital_post.install b/os2forms_digital_post.install new file mode 100644 index 0000000..760743c --- /dev/null +++ b/os2forms_digital_post.install @@ -0,0 +1,19 @@ + + */ +function os2forms_digital_post_schema() { + return Drupal::service(BeskedfordelerHelper::class)->schema(); +} diff --git a/os2forms_digital_post.services.yml b/os2forms_digital_post.services.yml index 7788b0d..b67c233 100644 --- a/os2forms_digital_post.services.yml +++ b/os2forms_digital_post.services.yml @@ -1,4 +1,8 @@ services: + logger.channel.os2forms_digital_post: + parent: logger.channel_base + arguments: [ 'os2forms_digital_post' ] + os2forms_digital_post.template_manager: class: Drupal\os2forms_digital_post\Manager\TemplateManager arguments: ["@config.factory", "@twig", "@twig.loader.filesystem"] @@ -38,6 +42,22 @@ services: - "@os2forms_cpr_lookup.service" - "@os2forms_cvr_lookup.service" - "@Drupal\\os2forms_digital_post\\Helper\\MeMoHelper" + - "@Drupal\\os2forms_digital_post\\Helper\\BeskedfordelerHelper" - "@logger.factory" Drupal\os2forms_digital_post\Helper\SF1461Helper: + + Drupal\os2forms_digital_post\EventSubscriber\BeskedfordelerEventSubscriber: + arguments: + - '@Drupal\os2forms_digital_post\Helper\BeskedfordelerHelper' + - '@Drupal\beskedfordeler\Helper\MessageHelper' + - '@Drupal\os2forms_digital_post\Helper\WebformHelperSF1601' + - '@logger.channel.os2forms_digital_post' + tags: + - { name: 'event_subscriber' } + + Drupal\os2forms_digital_post\Helper\BeskedfordelerHelper: + arguments: + - '@database' + - '@Drupal\os2forms_digital_post\Helper\MeMoHelper' + - '@logger.channel.os2forms_digital_post' diff --git a/src/EventSubscriber/BeskedfordelerEventSubscriber.php b/src/EventSubscriber/BeskedfordelerEventSubscriber.php new file mode 100644 index 0000000..05e9be6 --- /dev/null +++ b/src/EventSubscriber/BeskedfordelerEventSubscriber.php @@ -0,0 +1,89 @@ +beskedfordelerHelper = $beskedfordelerHelper; + $this->messageHelper = $messageHelper; + $this->webformHelper = $webformHelper; + } + + /** + * {@inheritdoc} + */ + protected function processPostStatusBeskedModtag(PostStatusBeskedModtagEvent $event): void { + $message = $event->getDocument()->saveXML(); + try { + $data = $this->messageHelper->getBeskeddata($message); + + $channel = $data['KanalKode'] ?? NULL; + if (self::KANAL_KODE !== $channel) { + $this->logger->debug('Ignoring message data on channel @channel', [ + '@channel' => $channel ?? '(null)', + ]); + return; + } + + $messageUUID = $data[self::MESSAGE_UUID_KEY] ?? NULL; + if (NULL === $messageUUID) { + $this->logger->debug('Missing message UUID (@message_uuid_key) in data on channel @channel: @data', [ + '@message_uuid_key' => self::MESSAGE_UUID_KEY, + '@channel' => $channel, + '@data' => json_encode($data), + ]); + return; + } + + if ($this->beskedfordelerHelper->addBeskedfordelerMessage($messageUUID, $message)) { + $message = $this->beskedfordelerHelper->loadMessage($messageUUID); + $this->webformHelper->processBeskedfordelerData($message->submissionId, $data); + } + } + catch (\Exception $exception) { + $this->logger->error('Error processing message: @exception_message', [ + '@exception_message' => $exception->getMessage(), + 'message' => $message, + ]); + } + } + +} diff --git a/src/Exception/InvalidMessage.php b/src/Exception/InvalidMessage.php new file mode 100644 index 0000000..1a73130 --- /dev/null +++ b/src/Exception/InvalidMessage.php @@ -0,0 +1,10 @@ +database = $database; + $this->meMoHelper = $meMoHelper; + $this->setLogger($logger); + } + + /** + * Save MeMo message in database. + */ + public function createMessage(int $submissionId, MeMoMessage $message, string $receipt) { + $messageUUID = $message->getMessageHeader()->getMessageUUID(); + $message = $this->meMoHelper->message2dom($message)->saveXML(); + + return $this->database + ->insert(self::TABLE_NAME) + ->fields([ + 'submission_id' => $submissionId, + 'message_uuid' => $messageUUID, + 'message' => $message, + 'receipt' => $receipt, + ]) + ->execute(); + } + + /** + * Load message. + * + * @param string $messageUUID + * The message UUID. + * + * @return \Drupal\os2forms_digital_post\Model\Message|null + * The message. + */ + public function loadMessage(string $messageUUID): ?Message { + return $this->database + ->select(self::TABLE_NAME, 'm') + ->fields('m') + ->condition('message_uuid', $messageUUID) + ->execute() + ->fetchObject(Message::class, []) ?: NULL; + } + + /** + * Add Beskedfordeler message to message. + */ + public function addBeskedfordelerMessage(string $messageUUID, string $beskedfordelerMessage): bool { + $message = $this->loadMessage($messageUUID); + + if (NULL === $message) { + throw new InvalidMessage(sprintf('Invalid message UUID: %s', $messageUUID)); + } + + return $this->database + ->update(self::TABLE_NAME) + ->fields([ + 'beskedfordeler_message' => $beskedfordelerMessage, + ]) + ->condition('message_uuid', $messageUUID) + ->execute() > 0; + } + + /** + * Delete messages for submissions. + * + * @param array|WebformSubmissionInterface[] $submissions + * The submissions. + */ + public function deleteMessages(array $submissions) { + $submissionIds = array_map(static function (WebformSubmissionInterface $submission) { + return $submission->id(); + }, $submissions); + + $this->database + ->delete(self::TABLE_NAME) + ->condition('submission_id', $submissionIds, 'IN') + ->execute(); + } + + /** + * Implements hook_schema(). + * + * @phpstan-return array + */ + public function schema(): array { + return [ + self::TABLE_NAME => [ + 'description' => 'OSForms digital post beskedfordeler', + 'fields' => [ + 'submission_id' => [ + 'description' => 'The submission id.', + 'type' => 'int', + 'not null' => TRUE, + ], + 'message_uuid' => [ + 'description' => 'The message UUID (formatted with dashes).', + 'type' => 'varchar', + 'length' => 36, + 'not null' => TRUE, + ], + 'message' => [ + 'description' => 'The MeMo message (XML).', + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + ], + 'receipt' => [ + 'description' => 'The MeMo message receipt (XML).', + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + ], + 'beskedfordeler_message' => [ + 'description' => 'The Beskedfordeler message (XML).', + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + ], + ], + 'indexes' => [ + 'submission_id' => ['submission_id'], + ], + 'primary key' => ['message_uuid'], + ], + ]; + } + +} diff --git a/src/Helper/WebformHelperSF1601.php b/src/Helper/WebformHelperSF1601.php index 17f6d94..09a5e6b 100644 --- a/src/Helper/WebformHelperSF1601.php +++ b/src/Helper/WebformHelperSF1601.php @@ -83,6 +83,13 @@ final class WebformHelperSF1601 implements LoggerInterface { */ protected MeMoHelper $meMoHelper; + /** + * The Beskedfordeler helper. + * + * @var BeskedfordelerHelper + */ + private BeskedfordelerHelper $beskedfordelerHelper; + /** * The logger. * @@ -107,6 +114,7 @@ public function __construct( CprServiceInterface $cprService, CvrServiceInterface $cvrService, MeMoHelper $meMoHelper, + BeskedfordelerHelper $beskedfordelerHelper, LoggerChannelFactoryInterface $loggerChannelFactory ) { $this->settings = $settings; @@ -116,6 +124,7 @@ public function __construct( $this->cprService = $cprService; $this->cvrService = $cvrService; $this->meMoHelper = $meMoHelper; + $this->beskedfordelerHelper = $beskedfordelerHelper; $this->logger = $loggerChannelFactory->get('os2forms_digital_post'); $this->submissionLogger = $loggerChannelFactory->get('webform_submission'); } @@ -134,10 +143,6 @@ public function __construct( * [The response, The MeMo message]. */ public function sendDigitalPost(WebformSubmissionInterface $submission, array $handlerSettings, array $submissionData = []): array { - $logContext = [ - 'webform_submission' => $submission, - ]; - $submissionData = $submissionData + $submission->getData(); $handlerMessageSettings = $handlerSettings[WebformHandlerSF1601::MEMO_MESSAGE]; @@ -213,10 +218,7 @@ public function sendDigitalPost(WebformSubmissionInterface $submission, array $h $type = $handlerMessageSettings[WebformHandlerSF1601::TYPE] ?? SF1601::TYPE_DIGITAL_POST; $response = $service->kombiPostAfsend($transactionId, $type, $message); - $this->notice('Digital post sent', $logContext + [ - 'handler_id' => 'os2forms_digital_post', - 'operation' => 'digital post sent', - ]); + $this->beskedfordelerHelper->createMessage($submission->id(), $message, (string) $response->getContent()); return [$response, $service->getLastKombiMeMoMessage()]; } @@ -327,4 +329,43 @@ public function processJob(Job $job): JobResult { } } + /** + * Process Beskedfordeler data. + */ + public function processBeskedfordelerData(int $submissionId, array $data) { + $webformSubmission = $this->loadSubmission($submissionId); + if (NULL !== $webformSubmission) { + $context = [ + 'webform_submission' => $webformSubmission, + 'handler_id' => 'os2forms_digital_post', + ]; + $status = $data['TransaktionsStatusKode']; + + if (!empty($data['FejlDetaljer'])) { + $this->error('@status; @error_code: @error_text', $context + [ + 'operation' => 'digital post failed', + '@status' => $status, + '@error_code' => $data['FejlDetaljer']['FejlKode'], + '@error_text' => $data['FejlDetaljer']['FejlTekst'], + 'data' => $data, + ]); + } + else { + $this->info('@status', $context + [ + 'operation' => 'digital post success', + '@status' => $status, + ]); + } + } + } + + /** + * Proxy for BeskedfordelerHelper::deleteMessages(). + * + * @see BeskedfordelerHelper::deleteMessages() + */ + public function deleteMessages(array $webformSubmissions) { + $this->beskedfordelerHelper->deleteMessages($webformSubmissions); + } + } diff --git a/src/Model/Message.php b/src/Model/Message.php new file mode 100644 index 0000000..6b424fb --- /dev/null +++ b/src/Model/Message.php @@ -0,0 +1,63 @@ + 'submissionId', + 'message_uuid' => 'messageUUID', + 'beskedfordeler_message' => 'beskedfordelerMessage', + ][$name] ?? $name; + + if (!property_exists($this, $property)) { + throw new \RuntimeException(sprintf('Invalid property: %s', $property)); + } + + $this->$property = $value; + } + +} diff --git a/src/Plugin/WebformHandler/WebformHandlerSF1601.php b/src/Plugin/WebformHandler/WebformHandlerSF1601.php index 1c99e09..7688c67 100644 --- a/src/Plugin/WebformHandler/WebformHandlerSF1601.php +++ b/src/Plugin/WebformHandler/WebformHandlerSF1601.php @@ -336,6 +336,20 @@ public function postSave(WebformSubmissionInterface $webformSubmission, $update $this->helper->createJob($webformSubmission, $this->configuration); } + /** + * {@inheritdoc} + */ + public function postDelete(WebformSubmissionInterface $webformSubmission) { + $this->helper->deleteMessages([$webformSubmission]); + } + + /** + * {@inheritdoc} + */ + public function postPurge(array $webformSubmissions) { + $this->helper->deleteMessages($webformSubmissions); + } + /** * Display the invoked plugin method to end user. *