diff --git a/rero_ils/modules/notifications/api.py b/rero_ils/modules/notifications/api.py index f76b4716ad..b21d0311c5 100644 --- a/rero_ils/modules/notifications/api.py +++ b/rero_ils/modules/notifications/api.py @@ -231,7 +231,22 @@ def patron_transactions(self): for result in results: yield PatronTransaction.get_record(result.meta.id) - # CLASS METHODS =========================================================== + def update_effective_recipients(self, recipients): + """Update the notification to set effective recipients. + + :param recipients: a list of tuple ; first element is the recipient + type, second element is the recipient address. + :return the updated notification. + """ + recipients = recipients or [] + for type_, address in recipients: + self.setdefault('effective_recipients', []).append({ + 'type': type_, + 'address': address + }) + return self.update( + data=self.dumps(), commit=True, dbcommit=True, reindex=True) + def update_process_date(self, sent=False, status=NotificationStatus.DONE): """Update the notification to set process date. diff --git a/rero_ils/modules/notifications/dispatcher.py b/rero_ils/modules/notifications/dispatcher.py index fb57fafbaf..a6898f2d35 100644 --- a/rero_ils/modules/notifications/dispatcher.py +++ b/rero_ils/modules/notifications/dispatcher.py @@ -77,7 +77,7 @@ def get_dispatcher_function(channel): # The aggregation key we build ensure than aggregated notifications # are always send to same recipient (patron, lib, vendor, ...) with # the same communication channel. So we can check any notification - # of the set to get the theses informations. + # of the set to get the these informations. for aggr_key, aggr_notifications in aggregated.items(): notification = aggr_notifications[0] comm_channel = notification.get_communication_channel() @@ -90,11 +90,13 @@ def get_dispatcher_function(channel): f'patron: {notification.patron.pid} ' f'documents: {counter}' ) - result = dispatcher_function(aggr_notifications) + result, recipients = dispatcher_function(aggr_notifications) for notification in aggr_notifications: notification.update_process_date(sent=result) if result: sent += counter + for notification in aggr_notifications: + notification.update_effective_recipients(recipients) else: not_sent += counter return { @@ -181,7 +183,10 @@ def send_mail_for_printing(notifications=None): generator process working with CUPS (using RabbitMQ queue) :param notifications: the notification set to perform. - :return True if email is send, False if errors are found. + :return a tuple where first element is a boolean value to determine if + email was sent (False if errors are found) ; second element is a + list of used recipients. + :rtype tuple """ notifications = notifications or [] if not notifications: @@ -219,14 +224,14 @@ def send_mail_for_printing(notifications=None): current_app.logger.warning( f'Notification#{notification.pid} for printing is lost :: ' f'({")(".join(error_reasons)})') - return False + return False, None # 2. Build the context to render the template notif_class = notification.__class__ context = notif_class.get_notification_context(notifications) # 3. Force patron communication channel to 'mail' - # In some cases we force the notification to be send by mail despite - # the patron asked to received them by email (cipo reminders + # In some cases we force the notification to be sent by mail despite + # the patron asked to receive them by email (cipo reminders # notifications with a communication channel to 'mail' value). # Ensure than the ``include_patron_address`` are set to True. context['include_patron_address'] = True @@ -239,14 +244,17 @@ def send_mail_for_printing(notifications=None): template=notification.get_template_path() ) task_send_email.apply_async((msg.__dict__,)) - return True + return True, [(RecipientType.TO, recipient)] @staticmethod def send_notification_by_email(notifications): """Send the notification by email to the patron. :param notifications: the notification set to perform. - :return True if email is send, False if errors are found. + :return a tuple where first element is a boolean value to determine if + email was sent (False if errors are found) ; second element is a + list of used recipients. + :rtype tuple """ notifications = notifications or [] if not notifications: @@ -265,7 +273,7 @@ def send_notification_by_email(notifications): current_app.logger.warning( f'Notification#{notification.pid} is lost :: ' f'({")(".join(error_reasons)})') - return False + return False, None # build the context for this notification set notif_class = notification.__class__ @@ -279,4 +287,4 @@ def send_notification_by_email(notifications): ) delay = context.get('delay', 0) task_send_email.apply_async((msg.__dict__,), countdown=delay) - return True + return True, [(RecipientType.TO, addr) for addr in recipients] diff --git a/rero_ils/modules/notifications/jsonschemas/notifications/notification-v0.0.1.json b/rero_ils/modules/notifications/jsonschemas/notifications/notification-v0.0.1.json index 49bb6a61d2..7f26f6ca0c 100644 --- a/rero_ils/modules/notifications/jsonschemas/notifications/notification-v0.0.1.json +++ b/rero_ils/modules/notifications/jsonschemas/notifications/notification-v0.0.1.json @@ -148,6 +148,38 @@ "acquisition_order" ] }, + "effective_recipients": { + "title": "Effective notification recipients", + "description": "Where the notification were really dispatched", + "type": "array", + "minItems": 1, + "items": { + "title": "Recipient", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "address" + ], + "properties": { + "type": { + "title": "Recipient type", + "type": "string", + "enum": [ + "to", + "cc", + "bcc", + "reply_to" + ] + }, + "address": { + "type": "string", + "format": "email", + "pattern": "^.*@.*\\..+$" + } + } + } + }, "context": { "oneOf": [ { diff --git a/rero_ils/modules/notifications/mappings/v7/notifications/notification-v0.0.1.json b/rero_ils/modules/notifications/mappings/v7/notifications/notification-v0.0.1.json index 440b7b9803..a7b0274f47 100644 --- a/rero_ils/modules/notifications/mappings/v7/notifications/notification-v0.0.1.json +++ b/rero_ils/modules/notifications/mappings/v7/notifications/notification-v0.0.1.json @@ -24,6 +24,16 @@ "notification_type": { "type": "keyword" }, + "effective_recipients": { + "properties": { + "type": { + "type": "keyword" + }, + "address": { + "type": "keyword" + } + } + }, "context": { "properties": { "loan": { diff --git a/tests/api/notifications/test_notifications_rest.py b/tests/api/notifications/test_notifications_rest.py index d4beb8aa31..17426e079d 100644 --- a/tests/api/notifications/test_notifications_rest.py +++ b/tests/api/notifications/test_notifications_rest.py @@ -72,6 +72,16 @@ def test_delayed_notifications( assert loan.is_notified(notification_type=NotificationType.AVAILABILITY) assert loan.is_notified(notification_type=NotificationType.AT_DESK) + # Ensure than `effective_recipients` is filled + # The notification should be sent to library AT_DESK email setting. + notification = Notification.get_record(notification.id) + effective_recipients = [ + recipient['address'] + for recipient in notification.get('effective_recipients') + ] + assert effective_recipients == \ + [lib_martigny.get_email(NotificationType.AT_DESK)] + # One notification will be sent : AVAILABILITY (sent to patron). # Get the last message from mailbox and check it. availability_msg = mailbox[-1]