View Javadoc
1   /*
2    * Copyright (c) 2002-2018, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.stock.modules.billetterie.web;
35  
36  import fr.paris.lutece.plugins.stock.business.attribute.AbstractAttributeNum;
37  import fr.paris.lutece.plugins.stock.business.attribute.product.ProductAttributeNum;
38  import fr.paris.lutece.plugins.stock.business.attribute.purchase.PurchaseAttribute;
39  import fr.paris.lutece.plugins.stock.business.offer.Offer;
40  import fr.paris.lutece.plugins.stock.business.product.Product;
41  import fr.paris.lutece.plugins.stock.business.product.ProductFilter;
42  import fr.paris.lutece.plugins.stock.business.provider.Provider;
43  import fr.paris.lutece.plugins.stock.business.provider.ProviderFilter;
44  import fr.paris.lutece.plugins.stock.business.purchase.Purchase;
45  import fr.paris.lutece.plugins.stock.business.purchase.PurchaseFilter;
46  import fr.paris.lutece.plugins.stock.commons.ResultList;
47  import fr.paris.lutece.plugins.stock.commons.exception.FunctionnalException;
48  import fr.paris.lutece.plugins.stock.modules.billetterie.utils.constants.BilletterieConstants;
49  import fr.paris.lutece.plugins.stock.modules.tickets.business.*;
50  import fr.paris.lutece.plugins.stock.modules.tickets.service.*;
51  import fr.paris.lutece.plugins.stock.modules.tickets.utils.constants.TicketsConstants;
52  
53  import fr.paris.lutece.plugins.stock.service.IProductService;
54  import fr.paris.lutece.plugins.stock.utils.constants.StockConstants;
55  import fr.paris.lutece.portal.service.i18n.I18nService;
56  import fr.paris.lutece.portal.service.spring.SpringContextService;
57  import fr.paris.lutece.portal.service.template.AppTemplateService;
58  import fr.paris.lutece.portal.service.util.AppPathService;
59  import fr.paris.lutece.portal.service.util.AppPropertiesService;
60  import fr.paris.lutece.util.html.HtmlTemplate;
61  
62  import org.apache.commons.lang.StringUtils;
63  
64  import org.apache.log4j.Logger;
65  
66  import java.util.*;
67  import java.util.stream.Collectors;
68  
69  import javax.inject.Inject;
70  import javax.inject.Named;
71  import javax.servlet.http.HttpServletRequest;
72  
73  import static java.util.Optional.ofNullable;
74  import static java.util.stream.Collectors.groupingBy;
75  import static java.util.stream.Collectors.mapping;
76  
77  /**
78   * This class provides the user interface to manage notifications
79   */
80  public class NotificationJspBean extends AbstractJspBean
81  {
82      public static final Logger LOGGER = Logger.getLogger( NotificationJspBean.class );
83      public static final String RESOURCE_TYPE = "STOCK";
84      public static final String RIGHT_MANAGE_NOTIFICATIONS = "OFFERS_MANAGEMENT";
85  
86      // MARKS
87      public static final String MARK_NOTIFICATION = "notification";
88      public static final String MARK_TITLE = "title";
89      public static final String MARK_OFFER_ID = "offer_id";
90      public static final String MARK_CANCEL = "cancel";
91      public static final String MARK_NOTIFY = "notify";
92      public static final String MARK_LOCK = "lock";
93      public static final String MARK_NOTIFICATION_ACTION = "notificationAction";
94      public static final String MARK_AVERTISSEMENT = "avertissement";
95      public static final String MARK_SEANCE = "seance";
96      public static final String MARK_LIST_RESERVATION = "liste_reservations";
97      public static final String MARK_TARIF_REDUIT_ID = "idTarifReduit";
98      public static final String MARK_BASE_URL = "base_url";
99      public static final String MARK_PARTNER_CONTACT_MAIL = "partner_contact_mail";
100 
101     // BEAN
102     private static final String BEAN_STOCK_TICKETS_SEANCE_SERVICE = "stock-tickets.seanceService";
103     private static final String BEAN_STOCK_TICKETS_RESERVATION_DAO = "stock-tickets.reservationDAO";
104 
105     // JSP
106     private static final String JSP_MANAGE_OFFERS = "jsp/admin/plugins/stock/modules/billetterie/ManageOffers.jsp";
107     private static final String JSP_SEND_NOTIFICATION = "SendNotification.jsp";
108 
109     // TEMPLATES
110     private static final String TEMPLATE_SEND_NOTIFICATION = "admin/plugins/stock/modules/billetterie/send_notification.html";
111     private static final String TEMPLATE_MAIL_LOCK = "admin/plugins/stock/modules/billetterie/mail_lock.html";
112     private static final String TEMPLATE_MAIL_CANCEL = "admin/plugins/stock/modules/billetterie/mail_cancel.html";
113 
114     // PAGE TITLES
115     private static final String PROPERTY_PAGE_TITLE_SEND_NOTIFICATION = "module.stock.billetterie.send_notification.title";
116     private static final String PROPERTY_MAIL_SEPARATOR = "mail.list.separator";
117 
118     // MESSAGES
119     private static final String MESSAGE_AVERTISSEMENT_CANCEL_OFFER = "module.stock.billetterie.message.cancelOffer.avertissement";
120     private static final String MESSAGE_AVERTISSEMENT_SEND_OFFER = "module.stock.billetterie.message.sendOffer.avertissement";
121     private static final String MESSAGE_NOTIFICATION_CANCEL_SUBJECT = "module.stock.billetterie.notification.cancel.subject";
122     private static final String MESSAGE_NOTIFICATION_BOOKING_LIST_SUBJECT = "module.stock.billetterie.notification.bookingList.subject";
123 
124     // ORDER FILTER
125     private static final String ORDER_FILTER_USER_NAME = "userName";
126     private ISeanceService _serviceOffer;
127     private IPurchaseService _servicePurchase;
128     private INotificationService _serviceNotification;
129     private IProviderService _serviceProvider;
130     private IShowService _serviceShow;
131 
132     /** The _dao purchase. */
133     private IReservationDAO _daoPurchase;
134 
135     /**
136      * Instantiates a new notification jsp bean.
137      */
138     public NotificationJspBean( )
139     {
140         super( );
141         _serviceOffer = (ISeanceService) SpringContextService.getBean( BEAN_STOCK_TICKETS_SEANCE_SERVICE );
142         _servicePurchase = SpringContextService.getContext( ).getBean( IPurchaseService.class );
143         _serviceNotification = SpringContextService.getContext( ).getBean( INotificationService.class );
144         _serviceProvider = SpringContextService.getContext( ).getBean( IProviderService.class );
145         _serviceShow = SpringContextService.getContext( ).getBean( IShowService.class );
146         _daoPurchase = (IReservationDAO) SpringContextService.getContext( ).getBean( BEAN_STOCK_TICKETS_RESERVATION_DAO );
147     }
148 
149     /**
150      * Returns the form for notification creation
151      * 
152      * @param request
153      *            The HTTP request
154      * @return HTML Form
155      */
156     public String getSendNotification( HttpServletRequest request )
157     {
158         NotificationDTO notification;
159         Map<String, Object> model = new HashMap<String, Object>( );
160 
161         // Manage validation errors
162         FunctionnalException ve = getErrorOnce( request );
163 
164         if ( ve != null )
165         {
166             notification = (NotificationDTO) ve.getBean( );
167             model.put( BilletterieConstants.ERROR, getHtmlError( ve ) );
168         }
169         else
170         {
171             setPageTitleProperty( PROPERTY_PAGE_TITLE_SEND_NOTIFICATION );
172             // Create new notification
173             notification = new NotificationDTO( );
174         }
175 
176         String strIdOffer = request.getParameter( MARK_OFFER_ID );
177         String strCancel = request.getParameter( MARK_CANCEL );
178         String strNotify = request.getParameter( MARK_NOTIFY );
179         String strLock = request.getParameter( MARK_LOCK );
180         Integer idOffer = Integer.parseInt( strIdOffer );
181 
182         SeanceDTO seanceDto = new SeanceDTO( );
183         seanceDto = ofNullable( idOffer ).map( e -> _serviceOffer.findSeanceById( e ) ).orElse( null );
184         ShowDTO showDto = ofNullable( seanceDto ).map( e -> e.getProduct( ) ).orElse( null );
185         Product productById = ofNullable(showDto).map( e ->_serviceShow.getProductById(e.getId()) ).orElse(null);
186         PurchaseFilter filter = new PurchaseFilter( );
187 
188         List<ReservationDTO> listReservations = new ArrayList<>( );
189         Set<ReservationDTO> listReservationsPurchaseByUsername = new HashSet<>();
190         Set<ReservationDTO> listReservationsPurchase = new HashSet<>();
191 
192         List<String> listContact = new ArrayList<>( );
193         // If the notification is about an offer
194         if ( StringUtils.isNotEmpty( strIdOffer ) )
195         {
196             SeanceDTO seance = this._serviceOffer.findSeanceById( idOffer );
197             notification.setIdOffer( idOffer );
198 
199             List<String> orderList = new ArrayList<String>( );
200             orderList.add( ORDER_FILTER_USER_NAME );
201             filter.setOrders( orderList );
202             filter.setOrderAsc( true );
203             filter.setIdOffer( idOffer );
204 
205             listReservations = this._servicePurchase.findByFilter( filter, null );
206 
207             Map<String, Integer> reservationDtoByUsername = listReservations.stream()
208                     .collect(groupingBy(ReservationDTO::getUserName, Collectors.summingInt(ReservationDTO::getQuantity)
209                     ));
210 
211             listReservationsPurchaseByUsername = listReservations.stream()
212                     .collect(Collectors.toCollection(() ->
213                             new TreeSet<>(Comparator.comparing(ReservationDTO::getUserName))));
214 
215             if (reservationDtoByUsername != null) {
216                 for (ReservationDTO reservationDTO:listReservationsPurchaseByUsername) {
217                     reservationDTO.setQuantity(reservationDtoByUsername.get(reservationDTO.getUserName()));
218                     listReservationsPurchase.add(reservationDTO);
219                 }
220             }
221 
222             // If the action is to cancel the offer
223             if ( StringUtils.isNotEmpty( strCancel )
224                     || ( ( notification.getNotificationAction( ) != null ) && notification.getNotificationAction( ).equals(
225                             TicketsConstants.OFFER_STATUT_CANCEL ) ) )
226             {
227 
228                 notification.setNotificationAction( TicketsConstants.OFFER_STATUT_CANCEL );
229 
230                 // Set the default recipientsTo
231                 StringBuilder recipientsTo = new StringBuilder( 100 );
232 
233                 for ( ReservationDTO reservationDTO : listReservationsPurchase )
234                 {
235                     recipientsTo.append( reservationDTO.getEmailAgent( ) ).append( AppPropertiesService.getProperty( PROPERTY_MAIL_SEPARATOR ) );
236 
237                 }
238 
239                 notification.setRecipientsTo( recipientsTo.toString( ) );
240                 // Set the default subject
241                 notification
242                         .setSubject( getMessage( MESSAGE_NOTIFICATION_CANCEL_SUBJECT, seance.getProduct( ).getName( ), seance.getDate( ), seance.getHour( ) ) );
243 
244                 // Set the default message
245                 Map<String, Object> modelMail = new HashMap<String, Object>( );
246                 modelMail.put( MARK_BASE_URL, AppPathService.getBaseUrl( request ) );
247                 modelMail.put( MARK_SEANCE, seance );
248 
249                 HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MAIL_CANCEL, request.getLocale( ), modelMail );
250                 notification.setMessage( template.getHtml( ) );
251             }
252 
253             // Action to send notification to partner and lock the offer
254             else
255                 if ( StringUtils.isNotEmpty( strLock )
256                         || ( ( notification.getNotificationAction( ) != null ) && notification.getNotificationAction( ).equals(
257                                 TicketsConstants.OFFER_STATUT_LOCK ) ) )
258                 {
259                     ShowDTO product = seance.getProduct( );
260 
261                     if ( product != null )
262                     {
263                         Integer idSalle = product.getIdProvider( );
264 
265                         if ( idSalle != null )
266                         {
267                             Integer [ ] arrayIdContact = seance.getIdContact( );
268 
269                             if ( ( arrayIdContact != null ) && ( arrayIdContact.length > 0 ) )
270                             {
271                                 PartnerDTO partner = _serviceProvider.findById( idSalle );
272                                 StringBuilder contactMail = null;
273 
274                                 for ( Integer idContact : arrayIdContact )
275                                 {
276                                     // Hide empty contact in mailing list
277                                     if ( idContact >= 0 )
278                                     {
279                                         if ( contactMail == null )
280                                         {
281                                             contactMail = new StringBuilder( );
282                                         }
283                                         else
284                                         {
285                                             contactMail.append( ";" );
286                                         }
287 
288                                         contactMail.append( partner.getContactMail( idContact ) );
289                                     }
290                                 }
291 
292                                 if ( contactMail != null )
293                                 {
294                                     if ( contactMail.toString( ) != null )
295                                     {
296                                         notification.setRecipientsTo( contactMail.toString( ) );
297                                     }
298                                     else
299                                     {
300                                         notification.setRecipientsTo( "" );
301                                     }
302                                 }
303                             }
304                         }
305                     }
306 
307                     notification.setNotificationAction( TicketsConstants.OFFER_STATUT_LOCK );
308                     // Set the default recipientsTo
309                     // Set the default subject
310                     notification.setSubject( getMessage( MESSAGE_NOTIFICATION_BOOKING_LIST_SUBJECT, seance.getProduct( ).getName( ) + " " + seance.getDate( )
311                             + " - " + seance.getHour( ) ) );
312 
313                     // Set the default message
314                     Map<String, Object> modelMail = new HashMap<String, Object>( );
315                     modelMail.put( MARK_BASE_URL, AppPathService.getBaseUrl( request ) );
316                     modelMail.put( MARK_SEANCE, seance );
317                     modelMail.put( MARK_TARIF_REDUIT_ID, TicketsConstants.OFFER_TYPE_REDUCT_ID );
318                     modelMail.put( MARK_LIST_RESERVATION, listReservationsPurchase );
319 
320                     HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_MAIL_LOCK, request.getLocale( ), modelMail );
321                     notification.setMessage( template.getHtml( ) );
322                 }
323 
324                 // Else action to send notification
325                 else
326                 {
327                     // Set the default recipientsTo
328                     StringBuilder recipientsTo = new StringBuilder( );
329 
330                     for ( ReservationDTO purchase : listReservationsPurchase )
331                     {
332                         recipientsTo.append( purchase.getEmailAgent( ) ).append( AppPropertiesService.getProperty( PROPERTY_MAIL_SEPARATOR ) );
333                     }
334 
335                     notification.setRecipientsTo( recipientsTo.toString( ) );
336                 }
337         }
338 
339         // If the action is to cancel the offer
340         if ( StringUtils.isNotEmpty( strCancel )
341                 || ( ( notification.getNotificationAction( ) != null ) && notification.getNotificationAction( ).equals( TicketsConstants.OFFER_STATUT_CANCEL ) ) )
342         {
343             model.put( MARK_AVERTISSEMENT, I18nService.getLocalizedString( MESSAGE_AVERTISSEMENT_CANCEL_OFFER, request.getLocale( ) ) );
344         }
345 
346         // Action to send notification to partner and lock the offer
347         else
348             if ( StringUtils.isNotEmpty( strLock )
349                     || ( ( notification.getNotificationAction( ) != null ) && notification.getNotificationAction( ).equals( TicketsConstants.OFFER_STATUT_LOCK ) ) )
350             {
351                 model.put( MARK_AVERTISSEMENT, I18nService.getLocalizedString( MESSAGE_AVERTISSEMENT_SEND_OFFER, request.getLocale( ) ) );
352             }
353 
354         // Add the JSP wich called this action
355         model.put( StockConstants.MARK_JSP_BACK, request.getParameter( StockConstants.MARK_JSP_BACK ) );
356         model.put( MARK_NOTIFICATION, notification );
357 
358         model.put( MARK_TITLE, I18nService.getLocalizedString( PROPERTY_PAGE_TITLE_SEND_NOTIFICATION, Locale.getDefault( ) ) );
359 
360         if ( StringUtils.isNotEmpty( strCancel ) || StringUtils.isNotEmpty( strNotify ) )
361         {
362             if (listReservationsPurchase !=null){
363                 for ( ReservationDTO reservationDTO : listReservationsPurchase )
364                 {
365                     listContact.add( reservationDTO.getEmailAgent( ) );
366                 }
367             }
368         }
369         else
370         {
371             List<Contact> lstContact = ofNullable( showDto ).map( e -> _serviceProvider.findById( e.getIdProvider( ) ).getContactList( ) ).orElse( null );
372 
373             if ( lstContact != null && productById != null )
374             {
375                 for ( Contact c : lstContact )
376                 {
377                     for ( AbstractAttributeNum sh : productById.getAttributeNumList( ) )
378                     {
379                         if ( sh.getValue( ).intValueExact( ) == c.getId( ) && sh.getKey( ).contains( "idContact" ) )
380                         {
381                             listContact.add( c.getMail( ) );
382                         }
383                     }
384                 }
385             }
386         }
387 
388         model.put( MARK_PARTNER_CONTACT_MAIL, listContact );
389 
390         HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_SEND_NOTIFICATION, getLocale( ), model );
391 
392         return getAdminPage( template.getHtml( ) );
393     }
394 
395     /**
396      * Send a notification
397      * 
398      * @param request
399      *            The HTTP request
400      * @return redirection url
401      */
402     public String doSendNotification( HttpServletRequest request )
403     {
404         if ( null != request.getParameter( StockConstants.PARAMETER_BUTTON_CANCEL ) )
405         {
406             return doGoBack( request );
407         }
408 
409         NotificationDTO notification = new NotificationDTO( );
410         populate( notification, request );
411 
412         try
413         {
414             // Controls mandatory fields
415             validateBilletterie( notification );
416 
417             if ( ( notification.getIdOffer( ) != null ) && ( notification.getIdOffer( ) > 0 ) )
418             {
419                 SeanceDTO seance = this._serviceOffer.findSeanceById( notification.getIdOffer( ) );
420 
421                 if ( notification.getNotificationAction( ).equals( TicketsConstants.OFFER_STATUT_LOCK ) )
422                 {
423                     seance.setStatut( TicketsConstants.OFFER_STATUT_LOCK );
424                     this._serviceOffer.update( seance );
425                 }
426                 else
427                     if ( notification.getNotificationAction( ).equals( TicketsConstants.OFFER_STATUT_CANCEL ) )
428                     {
429                         seance.setStatut( TicketsConstants.OFFER_STATUT_CANCEL );
430                         this._serviceOffer.update( seance );
431                     }
432             }
433 
434             _serviceNotification.send( notification );
435         }
436         catch( FunctionnalException e )
437         {
438             return manageFunctionnalException( request, e, JSP_SEND_NOTIFICATION );
439         }
440 
441         return doGoBack( request );
442     }
443 
444     /**
445      * Return the url of the JSP which called the last action
446      * 
447      * @param request
448      *            The Http request
449      * @return The url of the last JSP
450      */
451     private String doGoBack( HttpServletRequest request )
452     {
453         String strJspBack = request.getParameter( StockConstants.MARK_JSP_BACK );
454 
455         return StringUtils.isNotBlank( strJspBack ) ? ( AppPathService.getBaseUrl( request ) + strJspBack )
456                 : ( AppPathService.getBaseUrl( request ) + JSP_MANAGE_OFFERS );
457     }
458 }