summaryrefslogtreecommitdiffstats
path: root/restaurant_orders/core
diff options
context:
space:
mode:
authorjdlugosz963 <jdlugosz963@gmail.com>2023-07-17 01:47:57 +0200
committerjdlugosz963 <jdlugosz963@gmail.com>2023-07-17 01:52:26 +0200
commitc21f4b3dacd597a15a5ec39d525df1dfe1b70376 (patch)
tree0dfc51d2ffeb7b5022d9ad852f2fd3620c72196a /restaurant_orders/core
parent2c6f98aeef4fa1aba5678fe17c8e762a11db7b40 (diff)
downloadrestaurant-orders-main.tar.gz
restaurant-orders-main.zip
Add project.main
Diffstat (limited to 'restaurant_orders/core')
-rw-r--r--restaurant_orders/core/__init__.py0
-rw-r--r--restaurant_orders/core/admin.py5
-rw-r--r--restaurant_orders/core/apps.py6
-rw-r--r--restaurant_orders/core/decorators.py36
-rw-r--r--restaurant_orders/core/models.py90
-rw-r--r--restaurant_orders/core/tasks.py51
-rw-r--r--restaurant_orders/core/utils.py100
7 files changed, 288 insertions, 0 deletions
diff --git a/restaurant_orders/core/__init__.py b/restaurant_orders/core/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/restaurant_orders/core/__init__.py
diff --git a/restaurant_orders/core/admin.py b/restaurant_orders/core/admin.py
new file mode 100644
index 0000000..5094530
--- /dev/null
+++ b/restaurant_orders/core/admin.py
@@ -0,0 +1,5 @@
1from django.contrib import admin
2from core.models import Restaurant, Order
3
4admin.site.register(Restaurant)
5admin.site.register(Order)
diff --git a/restaurant_orders/core/apps.py b/restaurant_orders/core/apps.py
new file mode 100644
index 0000000..8115ae6
--- /dev/null
+++ b/restaurant_orders/core/apps.py
@@ -0,0 +1,6 @@
1from django.apps import AppConfig
2
3
4class CoreConfig(AppConfig):
5 default_auto_field = 'django.db.models.BigAutoField'
6 name = 'core'
diff --git a/restaurant_orders/core/decorators.py b/restaurant_orders/core/decorators.py
new file mode 100644
index 0000000..d8583f8
--- /dev/null
+++ b/restaurant_orders/core/decorators.py
@@ -0,0 +1,36 @@
1from django.http import HttpResponse
2from core.models import Restaurant
3
4import base64
5import hashlib
6import hmac
7
8def get_webhook_secret_from_restaurant(pk):
9 try:
10 token = Restaurant.objects.get(pk=pk).woocommerce_webhook_secret
11 if token != '':
12 return token
13 except Restaurant.DoesNotExist:
14 pass
15 return None
16
17def compare_signatures(body, webhook_secret, request_sig):
18 signature = hmac.new(webhook_secret.encode(), body, hashlib.sha256).digest()
19 return hmac.compare_digest(request_sig.encode(), base64.b64encode(signature))
20
21
22def woocommerce_authentication_required(view):
23 def inner(request, restaurant_pk, *args, **kwargs):
24 webhook_secret = get_webhook_secret_from_restaurant(restaurant_pk)
25 request_sig = request.headers.get('x-wc-webhook-signature')
26
27 if not webhook_secret or not request_sig:
28 return HttpResponse('Unauthorized')
29
30 if compare_signatures(request.body, webhook_secret, request_sig):
31 return view(request, restaurant_pk, *args, **kwargs)
32
33 response = HttpResponse('Unauthorized')
34 response.status_code = 403
35 return response
36 return inner
diff --git a/restaurant_orders/core/models.py b/restaurant_orders/core/models.py
new file mode 100644
index 0000000..91147d2
--- /dev/null
+++ b/restaurant_orders/core/models.py
@@ -0,0 +1,90 @@
1from django.db import models
2from django.contrib.auth.models import User
3from django.shortcuts import Http404, get_object_or_404
4
5from datetime import datetime
6
7
8class Restaurant(models.Model):
9 name = models.CharField(max_length=50)
10 users = models.ManyToManyField(User)
11
12 wordpress_url = models.URLField(max_length=50, unique=True)
13 woocommerce_consumer_key = models.CharField(max_length=50)
14 woocommerce_consumer_secret = models.CharField(max_length=50)
15 woocommerce_webhook_secret = models.CharField(max_length=50)
16
17 @classmethod
18 def get_user_restaurants(cls, user: User):
19 return cls.objects.filter(users=user)
20
21 @classmethod
22 def get_user_restaurant_or_404(cls, pk, user: User):
23 return get_object_or_404(cls, pk=pk, users=user)
24
25 def __str__(self):
26 return self.name
27
28
29class Order(models.Model):
30 WP_STATES = (
31 ('pending', 'Oczekujace'),
32 ('processing', 'Przetwarzane'),
33 ('on-hold', 'Wstrzymane'),
34 ('completed', 'Zakonczone'),
35 ('cancelled', 'Anulowane'),
36 ('refunded', 'Zwrocone'),
37 ('failed', 'Nie powiodlo sie'),
38 ('trash', 'Usuniete')
39 )
40
41 wp_id = models.IntegerField(editable=False)
42 wp_status = models.CharField(max_length=30, choices=WP_STATES)
43 wp_order_key = models.CharField(max_length=50, editable=False)
44 date_created = models.DateField(editable=False)
45 date_modified = models.DateField()
46 line_items = models.JSONField()
47 billing = models.JSONField()
48 restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
49 can_display = models.BooleanField(default=True)
50
51 @classmethod
52 def update_or_create_from_response(cls, response, restaurant_model):
53 try:
54 obj = cls.objects.update_or_create(
55 wp_id=response['id'],
56 wp_order_key=response['order_key'],
57 restaurant=restaurant_model,
58 defaults={
59 'wp_status': response['status'],
60 'date_created': datetime.strptime(response['date_created'], '%Y-%m-%dT%H:%M:%S'),
61 'date_modified': datetime.strptime(response['date_modified'], '%Y-%m-%dT%H:%M:%S'),
62 'line_items': response['line_items'],
63 'billing': response['billing'],
64 }
65 )
66 return obj
67 except KeyError:
68 return None
69
70
71 @classmethod
72 def create_from_response_disable_view(cls, response, restaurant_model):
73 obj, _ = cls.update_or_create_from_response(response, restaurant_model)
74 obj.can_display = False
75 obj.save()
76 return obj
77
78 @classmethod
79 def get_order(cls, pk, user: User):
80 try:
81 return cls.objects.get(
82 pk=pk,
83 can_display=True,
84 restaurant__users=user
85 )
86 except cls.DoesNotExist:
87 raise Http404
88
89 def __str__(self):
90 return f'{self.wp_order_key} - {self.date_created}'
diff --git a/restaurant_orders/core/tasks.py b/restaurant_orders/core/tasks.py
new file mode 100644
index 0000000..f1c0192
--- /dev/null
+++ b/restaurant_orders/core/tasks.py
@@ -0,0 +1,51 @@
1from celery import shared_task
2
3from django.core import serializers
4
5from restaurant_orders.consumers import NotificationsConsumer
6
7from core.utils import Orders, SendMail, SendSms
8import re
9
10
11def send_notification(is_success, message, user_pk):
12 status = NotificationsConsumer.OK if is_success else NotificationsConsumer.ERROR
13 NotificationsConsumer.send_notifications(
14 user_pk,
15 status,
16 message
17 )
18
19
20
21@shared_task
22def create_order_and_send_notification(order, items, is_email=None, is_sms=None, user_pk=None):
23 order = [obj for obj in serializers.deserialize('json', order)]
24 if len(order) != 1:
25 return
26 order = order[0].object
27
28 phone = order.billing.get('phone')
29 email = order.billing.get('email')
30 new_order = Orders(order.restaurant, order.billing).create_custom_order(items)
31
32
33# if new_order is None:
34# send_notification(False,
35# "Niestety nie udalo sie skontaktowac z restauracja, prosze sprowbowac ponownie pozniej.",
36# user_pk)
37# return
38
39 EMAIL_REGEX = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
40
41 if is_sms: # TODO: Make regex for sms
42 sms = SendSms(new_order).send()
43 send_notification(*sms, user_pk)
44
45 if is_email and re.fullmatch(EMAIL_REGEX, str(email)):
46 mail = SendMail(new_order).send()
47 send_notification(*mail, user_pk)
48
49
50
51
diff --git a/restaurant_orders/core/utils.py b/restaurant_orders/core/utils.py
new file mode 100644
index 0000000..d716a9c
--- /dev/null
+++ b/restaurant_orders/core/utils.py
@@ -0,0 +1,100 @@
1import json
2from woocommerce import API
3from twilio.rest import Client
4from twilio.base.exceptions import TwilioRestException
5
6from django.core.mail import send_mail
7from django.conf import settings
8
9from core.models import Order
10
11class Sender():
12 def __init__(self, order):
13 self.order = order
14
15 def get_order_url(self):
16 order_id = self.order.wp_id
17 order_key = self.order.wp_order_key
18 restaurant_url = self.order.restaurant.wordpress_url
19 return f'{restaurant_url}/zamowienie/order-pay/{order_id}/?pay_for_order=true&key={order_key}'
20
21 def get_message_body(self):
22 return f'Prosze dokonac platnosci: {self.get_order_url()}'
23
24 def send(self) -> (bool, str):
25 pass
26
27
28class SendSms(Sender):
29 def __init__(self, order):
30 account_sid = settings.TWILIO_ACCOUNT_SID
31 auth_token = settings.TWILIO_TOKEN
32
33 self.client = Client(account_sid, auth_token)
34 self.from_ = "+17432007359"
35
36 super().__init__(order)
37
38 def send(self) -> (bool, str):
39 phone = self.order.billing.get('phone', None)
40 phone = "+48609155122"
41 if phone:
42 try:
43 message = self.client.messages.create(to=phone,
44 from_=self.from_,
45 body=self.get_message_body())
46 except TwilioRestException as err:
47 return (False, err.msg)
48 else:
49 return (True, 'Wyslano sms')
50 return (False, 'Nie znaleziono numeru telefonu.')
51
52
53
54class SendMail(Sender):
55 def send(self) -> (bool, str):
56 email = self.order.billing.get('email', None)
57 email = 'jdlugosz963@gmail.com'
58 if email: # Jesli sie spierdoli to wypluje
59 try:
60 send_mail('Strona do zaplaty', self.get_message_body(), 'no-reply@reami.pl', (email, ), fail_silently=False)
61 except smtplib.SMTPException:
62 return (False, "Niestety nie udalo sie wyslac maila.")
63 else:
64 return (True, "Wyslano maila.")
65 return (False, "Nie znaleziono maila.")
66
67
68class Orders:
69 def __init__(self, restaurant, billing):
70 self.restaurant = restaurant
71 self.billing = billing
72
73 self.wcapi = API(
74 url=restaurant.wordpress_url,
75 consumer_key=restaurant.woocommerce_consumer_key,
76 consumer_secret=restaurant.woocommerce_consumer_secret,
77 timeout=7
78 )
79
80 def get_custom_order_data(self, items):
81 return {
82 "payment_method": "bacs",
83 "payment_method_title": "Direct Bank Transfer",
84 "set_paid": False,
85 "billing": self.billing,
86 "shipping": self.billing,
87 "line_items": [
88 {
89 "product_id": pk,
90 "total": total,
91 "quantity": 1,
92 } for pk, total in items
93 ]
94 }
95
96 def create_custom_order(self, items):
97 data = self.get_custom_order_data(items)
98 response = self.wcapi.post("orders", data=data).json()
99 return Order.create_from_response_disable_view(response, self.restaurant)
100