Skip to content

Commit 7d8a747

Browse files
authored
Merge pull request #18 from YannLe/main
Add optional texter for local pickup
2 parents 9401882 + ee35fc5 commit 7d8a747

13 files changed

Lines changed: 267 additions & 56 deletions

File tree

Config/module.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<descriptive locale="fr_FR">
88
<title>Retrait sur place</title>
99
</descriptive>
10-
<version>2.0.5</version>
10+
<version>2.1.0</version>
1111
<author>
1212
<name>Thelia</name>
1313
<email>info@thelia.net</email>

Controller/ConfigurationController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ public function configure()
5050
$price = $vform->get('price')->getData();
5151
$description = $vform->get('description')->getData();
5252
$email = $vform->get('email')->getData();
53+
$sms = $vform->get('sms')->getData();
5354

5455
LocalPickup::setConfigValue(LocalPickup::PRICE_VAR_NAME, (float) $price);
5556
LocalPickup::setConfigValue(LocalPickup::DESCRIPTION_VAR_NAME, $description, $this->getCurrentEditionLocale());
5657
LocalPickup::setConfigValue(LocalPickup::EMAIL_VAR_NAME, $email, $this->getCurrentEditionLocale());
58+
LocalPickup::setConfigValue(LocalPickup::SMS_VAR_NAME, (bool) $sms);
5759
} catch (FormValidationException $ex) {
5860
$errmes = $this->createStandardFormValidationErrorMessage($ex);
5961
} catch (\Exception $ex) {

EventListeners/APIListener.php

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,32 @@
1212

1313
namespace LocalPickup\EventListeners;
1414

15+
use libphonenumber\NumberParseException;
16+
use libphonenumber\PhoneNumber;
17+
use libphonenumber\PhoneNumberFormat;
18+
use libphonenumber\PhoneNumberUtil;
1519
use LocalPickup\LocalPickup;
1620
use OpenApi\Events\DeliveryModuleOptionEvent;
1721
use OpenApi\Events\OpenApiEvents;
1822
use OpenApi\Model\Api\DeliveryModuleOption;
1923
use OpenApi\Model\Api\ModelFactory;
2024
use Propel\Runtime\Exception\PropelException;
21-
use Symfony\Component\DependencyInjection\ContainerInterface;
25+
use SmartyException;
2226
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
2327
use 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;
2531
use 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;
2636
use Thelia\Mailer\MailerFactory;
37+
use Thelia\Model\CountryQuery;
38+
use Thelia\Model\MessageQuery;
2739
use Thelia\Model\ModuleQuery;
40+
use Thelia\Model\Order;
2841
use Thelia\Model\OrderStatus;
2942

3043
class 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 = [];

Form/ConfigurationForm.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace LocalPickup\Form;
1414

1515
use LocalPickup\LocalPickup;
16+
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
1617
use Symfony\Component\Form\Extension\Core\Type\NumberType;
1718
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
1819
use Symfony\Component\Form\Extension\Core\Type\TextType;
@@ -70,6 +71,25 @@ protected function buildForm()
7071
],
7172
]
7273
)
74+
->add(
75+
"sms",
76+
CheckboxType::class,
77+
[
78+
"required" => false,
79+
"label"=> Translator::getInstance()->trans("Send SMS", [], LocalPickup::DOMAIN_NAME),
80+
'attr' => [
81+
'rows' => 5,
82+
],
83+
"label_attr"=> [
84+
"for"=>"sms",
85+
'help' => Translator::getInstance()->trans(
86+
'Help SMS',
87+
[],
88+
LocalPickup::DOMAIN_NAME
89+
),
90+
],
91+
]
92+
)
7393
;
7494
}
7595

Hook/HookManager.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public function onModuleConfiguration(HookRenderEvent $event): void
4141
'price' => (float) LocalPickup::getConfigValue(LocalPickup::PRICE_VAR_NAME, 0),
4242
'description' => LocalPickup::getConfigValue(LocalPickup::DESCRIPTION_VAR_NAME, '', $locale),
4343
'email' => LocalPickup::getConfigValue(LocalPickup::EMAIL_VAR_NAME, '', $locale),
44+
'sms' => LocalPickup::getConfigValue(LocalPickup::SMS_VAR_NAME, false),
4445
]
4546
)
4647
);

I18n/en_US.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
'Commentary email' => 'Write a comment visible for the user in the email',
66
'Price' => 'Price without taxes',
77
'price must be a number !' => 'price must be a number !',
8+
'Send SMS' => 'Send an sms to the customer when the command status is sent',
9+
'Help SMS' => 'Need configuration of a Texter with a valid DSN',
810
);

I18n/fr_FR.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
'Commentary email' => 'Vous pouvez indiquer un commentaire qui sera affiché à vos client dans l\'email',
66
'Price' => 'Prix HT',
77
'price must be a number !' => 'Le prix doit être un nombre !',
8+
'Send SMS' => 'Envoyer un sms au client lorsque le statut de la commande passe à Envoyé',
9+
'Help SMS' => 'Nécessite la configuration d\'un Texter avec un DSN valide',
810
);

0 commit comments

Comments
 (0)