1212
1313namespace LocalPickup \EventListeners ;
1414
15+ use libphonenumber \NumberParseException ;
16+ use libphonenumber \PhoneNumber ;
17+ use libphonenumber \PhoneNumberFormat ;
18+ use libphonenumber \PhoneNumberUtil ;
1519use LocalPickup \LocalPickup ;
1620use OpenApi \Events \DeliveryModuleOptionEvent ;
1721use OpenApi \Events \OpenApiEvents ;
1822use OpenApi \Model \Api \DeliveryModuleOption ;
1923use OpenApi \Model \Api \ModelFactory ;
2024use Propel \Runtime \Exception \PropelException ;
21- use Symfony \ Component \ DependencyInjection \ ContainerInterface ;
25+ use SmartyException ;
2226use Symfony \Component \EventDispatcher \EventSubscriberInterface ;
2327use Symfony \Component \HttpFoundation \RequestStack ;
24- use Thelia \Core \Event \TheliaEvents ;
28+ use Symfony \Component \Notifier \Exception \TransportExceptionInterface ;
29+ use Symfony \Component \Notifier \Message \SmsMessage ;
30+ use Symfony \Component \Notifier \TexterInterface ;
2531use Thelia \Core \Event \Order \OrderEvent ;
32+ use Thelia \Core \Event \TheliaEvents ;
33+ use Thelia \Core \Template \ParserInterface ;
34+ use Thelia \Core \Template \TemplateHelperInterface ;
35+ use Thelia \Exception \TheliaProcessException ;
2636use Thelia \Mailer \MailerFactory ;
37+ use Thelia \Model \CountryQuery ;
38+ use Thelia \Model \MessageQuery ;
2739use Thelia \Model \ModuleQuery ;
40+ use Thelia \Model \Order ;
2841use Thelia \Model \OrderStatus ;
2942
3043class APIListener implements EventSubscriberInterface
@@ -40,20 +53,24 @@ class APIListener implements EventSubscriberInterface
4053 */
4154 protected $ mailer ;
4255
56+
4357 /**
4458 * APIListener constructor.
45- *
46- * @param ContainerInterface $container We need the container because we use a service from another module
47- * which is not mandatory, and using its service without it being installed will crash
4859 */
49- public function __construct (ModelFactory $ modelFactory , RequestStack $ requestStack , MailerFactory $ mailer )
60+ public function __construct (
61+ ModelFactory $ modelFactory ,
62+ RequestStack $ requestStack ,
63+ MailerFactory $ mailer ,
64+ private ?TexterInterface $ texter ,
65+ private ParserInterface $ parser ,
66+ private TemplateHelperInterface $ templateHelper
67+ )
5068 {
5169 $ this ->modelFactory = $ modelFactory ;
5270 $ this ->requestStack = $ requestStack ;
5371 $ this ->mailer = $ mailer ;
5472 }
5573
56-
5774 public function getDeliveryModuleOptions (DeliveryModuleOptionEvent $ deliveryModuleOptionEvent ): void
5875 {
5976 $ module = ModuleQuery::create ()->findOneByCode (LocalPickup::getModuleCode ());
@@ -96,8 +113,7 @@ public function getDeliveryModuleOptions(DeliveryModuleOptionEvent $deliveryModu
96113 ->setMaximumDeliveryDate ($ maximumDeliveryDate )
97114 ->setPostage ($ postage )
98115 ->setPostageTax ($ postageTax )
99- ->setPostageUntaxed ($ postage - $ postageTax )
100- ;
116+ ->setPostageUntaxed ($ postage - $ postageTax );
101117
102118 // Pre-5.3.x compatibility
103119 if (method_exists ($ deliveryModuleOption , 'setDescription ' )) {
@@ -109,26 +125,117 @@ public function getDeliveryModuleOptions(DeliveryModuleOptionEvent $deliveryModu
109125
110126 /**
111127 * @throws PropelException
128+ * @throws TransportExceptionInterface
129+ * @throws SmartyException
112130 */
113- public function getOrderStatus (OrderEvent $ orderEvent )
131+ public function getOrderStatus (OrderEvent $ orderEvent ): void
114132 {
115133 $ order = $ orderEvent ->getOrder ();
116-
117- if ($ order ->getDeliveryModuleId () !== LocalPickup::getModuleId () || $ order ->getOrderStatus ()->getCode () !== OrderStatus::CODE_SENT ) {
134+ if (!$ this ->isEligibleForLocalPickupNotification ($ order )) {
118135 return ;
119136 }
137+ $ this ->sendLocalPickupEmail ($ order );
138+ if ($ this ->texter && LocalPickup::getConfigValue (LocalPickup::SMS_VAR_NAME )) {
139+ $ this ->sendSmsIfNeeded ($ order );
140+ }
141+ }
120142
143+ private function isEligibleForLocalPickupNotification (Order $ order ): bool
144+ {
145+ return $ order ->getDeliveryModuleId () === LocalPickup::getModuleId ()
146+ && $ order ->getOrderStatus ()->getCode () === OrderStatus::CODE_SENT ;
147+ }
148+
149+ private function sendLocalPickupEmail (Order $ order ): void
150+ {
121151 $ this ->mailer ->sendEmailToCustomer (
122152 LocalPickup::EMAIL_CUSTOM_LOCAL_PICKUP ,
123153 $ order ->getCustomer (),
124154 [
125155 'order_id ' => $ order ->getId (),
126156 'order_ref ' => $ order ->getRef (),
127- 'comment ' => LocalPickup::getConfigValue (LocalPickup::EMAIL_VAR_NAME , '' , $ order ->getLang ()->getLocale ()),
157+ 'comment ' => LocalPickup::getConfigValue (
158+ LocalPickup::EMAIL_VAR_NAME ,
159+ '' ,
160+ $ order ->getLang ()->getLocale ()
161+ ),
128162 ]
129163 );
130164 }
131165
166+ /**
167+ * @throws PropelException|TransportExceptionInterface
168+ * @throws SmartyException
169+ */
170+ private function sendSmsIfNeeded (Order $ order ): void
171+ {
172+ $ phoneNumber = $ order ->getOrderAddressRelatedByDeliveryOrderAddressId ()->getPhone ();
173+ $ cellPhoneNumber = $ order ->getOrderAddressRelatedByDeliveryOrderAddressId ()->getCellphone ();
174+
175+ $ numberToUse = $ cellPhoneNumber ?? $ phoneNumber ;
176+ if ($ numberToUse === null || $ this ->isSurtaxedNumber ($ numberToUse )) {
177+ return ;
178+ }
179+
180+ $ langCode = $ this ->getOrderLangCode ($ order );
181+ $ internationalNumber = $ this ->internationalizePhoneNumber ($ numberToUse , $ langCode );
182+ $ message = MessageQuery::create ()
183+ ->filterByName (LocalPickup::SMS_CUSTOM_LOCAL_PICKUP )
184+ ->findOne ();
185+ if (!$ message ) {
186+ throw new TheliaProcessException ('Template ' . LocalPickup::SMS_CUSTOM_LOCAL_PICKUP . ' not found. ' );
187+ }
188+ $ this ->parser ->setTemplateDefinition (
189+ $ this ->templateHelper ->getActiveAdminTemplate (),
190+ true
191+ );
192+ $ sms = new SmsMessage (
193+ $ internationalNumber ,
194+ $ this ->parser ->render ($ message ->getHtmlTemplateFileName (), ['order_id ' => $ order ->getId ()])
195+ );
196+ $ this ->texter ->send ($ sms );
197+ }
198+
199+ private function isSurtaxedNumber (string $ phoneNumber ): bool
200+ {
201+ $ surtaxedPatterns = [
202+ '/^089[0-9]{1}/ ' , // Start by 089
203+ '/^08[0-9]{2}/ ' , // Start by 08 (can be surtaxed)
204+ '/^36[0-9]{2}/ ' , // Short numbers (for instance 36xx, often surtaxed)
205+ ];
206+
207+ foreach ($ surtaxedPatterns as $ pattern ) {
208+ if (preg_match ($ pattern , $ phoneNumber )) {
209+ return true ;
210+ }
211+ }
212+
213+ return false ;
214+ }
215+
216+ /**
217+ * @throws NumberParseException
218+ */
219+ private function internationalizePhoneNumber (string $ phoneNumber , string $ region = 'FR ' ): string
220+ {
221+ $ phoneUtil = PhoneNumberUtil::getInstance ();
222+ /** @var PhoneNumber $phoneNumberObject */
223+ $ phoneNumberObject = $ phoneUtil ->parse ($ phoneNumber , $ region );
224+
225+ return $ phoneUtil ->format ($ phoneNumberObject , PhoneNumberFormat::E164 );
226+ }
227+
228+ /**
229+ * @throws PropelException
230+ */
231+ private function getOrderLangCode (Order $ order ): string
232+ {
233+ $ country = CountryQuery::create ()
234+ ->findOneById ($ order ->getOrderAddressRelatedByDeliveryOrderAddressId ()->getCountryId ());
235+
236+ return $ country ? $ country ->getIsoalpha2 () : 'FR ' ;
237+ }
238+
132239 public static function getSubscribedEvents ()
133240 {
134241 $ listenedEvents = [];
0 commit comments