Merge branch 'release/1.3.6'
This commit is contained in:
commit
a6fb7feb1b
@ -3,15 +3,16 @@ from allauth.account.models import EmailAddress
|
|||||||
from .models import Address, User
|
from .models import Address, User
|
||||||
from .tasks import send_account_created_email
|
from .tasks import send_account_created_email
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_customer(request, form, shipping_address):
|
def get_or_create_customer(request, form, shipping_address):
|
||||||
address, a_created = Address.objects.get_or_create(
|
address, a_created = Address.objects.get_or_create(
|
||||||
first_name = shipping_address['first_name'],
|
first_name=shipping_address['first_name'],
|
||||||
last_name = shipping_address['last_name'],
|
last_name=shipping_address['last_name'],
|
||||||
street_address_1 = shipping_address['street_address_1'],
|
street_address_1=shipping_address['street_address_1'],
|
||||||
street_address_2 = shipping_address['street_address_2'],
|
street_address_2=shipping_address['street_address_2'],
|
||||||
city = shipping_address['city'],
|
city=shipping_address['city'],
|
||||||
state = shipping_address['state'],
|
state=shipping_address['state'],
|
||||||
postal_code = shipping_address['postal_code']
|
postal_code=shipping_address['postal_code']
|
||||||
)
|
)
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
@ -23,8 +24,8 @@ def get_or_create_customer(request, form, shipping_address):
|
|||||||
else:
|
else:
|
||||||
user, u_created = User.objects.get_or_create(
|
user, u_created = User.objects.get_or_create(
|
||||||
email=form.cleaned_data['email'],
|
email=form.cleaned_data['email'],
|
||||||
defaults = {
|
defaults={
|
||||||
'username': form.cleaned_data['email'],
|
'username': form.cleaned_data['email'].lower(),
|
||||||
'is_staff': False,
|
'is_staff': False,
|
||||||
'is_active': True,
|
'is_active': True,
|
||||||
'is_superuser': False,
|
'is_superuser': False,
|
||||||
@ -39,7 +40,9 @@ def get_or_create_customer(request, form, shipping_address):
|
|||||||
user.addresses.add(address)
|
user.addresses.add(address)
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
EmailAddress.objects.create(user=user, email=user.email, primary=True, verified=False)
|
EmailAddress.objects.create(
|
||||||
|
user=user, email=user.email, primary=True, verified=False
|
||||||
|
)
|
||||||
|
|
||||||
u = {
|
u = {
|
||||||
'full_name': user.get_full_name(),
|
'full_name': user.get_full_name(),
|
||||||
|
|||||||
@ -1 +1,190 @@
|
|||||||
[{"model": "core.product", "pk": 1, "fields": {"name": "Ethiopia", "description": "Spicy espresso reminiscent of Northern Italy, on the mild side. Perfect for espresso, and steamed milk drinks. Also, a full-bodied, earthy sweet drip or Americano. Contains organic beans from Indonesia, Africa and America.", "sku": "23468", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 4, "created_at": "2022-02-19T20:15:36.292Z", "updated_at": "2022-03-28T17:29:26.300Z"}}, {"model": "core.product", "pk": 2, "fields": {"name": "Sumatra", "description": "Dark heavy-bodied roast with a lingering chocolatey taste. Organic Single origin.", "sku": "89765", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 3, "created_at": "2022-02-19T20:15:59.741Z", "updated_at": "2022-03-28T17:29:08.706Z"}}, {"model": "core.product", "pk": 3, "fields": {"name": "Pantomime", "description": "Very Dark French Roast\r\nOur darkest drip. A blend of five different beans roasted two ways. Organic Africa, Indonesia, and South and Central America.", "sku": "565656", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 1, "created_at": "2022-02-23T17:59:00.711Z", "updated_at": "2022-03-28T17:28:58.670Z"}}, {"model": "core.product", "pk": 4, "fields": {"name": "Decaf", "description": "French Roast (Water Processed)\r\n\r\n“I can’t believe it’s decaf!”. The best-tasting Swiss water process decaf we have developed over the past 30 years, for an unbelievable espresso or drip coffee. Organic Africa, Indonesia and South and Central America.", "sku": "566565", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 2, "created_at": "2022-02-23T17:59:32.099Z", "updated_at": "2022-03-28T17:28:45.512Z"}}, {"model": "core.product", "pk": 5, "fields": {"name": "Moka Java Blend", "description": "Dark Roast\r\n\r\nA classic Moka Java style blend dark roasted with organic beans for a perfect body and sweetness with a hint of citrus.", "sku": "56466", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": false, "sorting": 5, "created_at": "2022-02-23T18:05:41.742Z", "updated_at": "2022-03-28T17:29:39.761Z"}}, {"model": "core.product", "pk": 6, "fields": {"name": "Loop d’ Loop", "description": "Mild Dark Roast\r\n\r\nOur most popular blend reminiscent of Central Italy. A dark, chocolaty flavor perfect for espresso or drip. It’s dark Vienna roast properties make it ideal for steamed milk drinks. Organic Indonesia, Africa and America.", "sku": "53264", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 6, "created_at": "2022-02-23T18:06:09.881Z", "updated_at": "2022-03-28T17:29:53.104Z"}}, {"model": "core.product", "pk": 7, "fields": {"name": "Dante’s Tornado", "description": "Medium Roast\r\n\r\nFull City spicy espresso roast reminiscent of Northern Italy, on the mild side. A full- bodied, earthy sweet drip or Americano. Organic Indonesia, Africa and America.", "sku": "78945", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 7, "created_at": "2022-02-23T18:06:35.593Z", "updated_at": "2022-03-28T17:30:11.445Z"}}, {"model": "core.product", "pk": 8, "fields": {"name": "Nicaragua", "description": "Mild Roast\r\n\r\nOur mildest roast with sweet and fruity notes, containing organic beans from Nicaragua. Single origin.", "sku": "12365", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 8, "created_at": "2022-02-23T18:06:57.624Z", "updated_at": "2022-03-28T17:30:20.941Z"}}, {"model": "core.productphoto", "pk": 1, "fields": {"product": 1, "image": "products/images/slice2.png"}}, {"model": "core.productphoto", "pk": 2, "fields": {"product": 2, "image": "products/images/slice1.png"}}, {"model": "core.productphoto", "pk": 5, "fields": {"product": 5, "image": "products/images/moka_java.png"}}, {"model": "core.productphoto", "pk": 6, "fields": {"product": 6, "image": "products/images/loop_d_loop.png"}}, {"model": "core.productphoto", "pk": 7, "fields": {"product": 7, "image": "products/images/dantes_tornado.png"}}, {"model": "core.productphoto", "pk": 8, "fields": {"product": 8, "image": "products/images/nicaragua.png"}}, {"model": "core.productphoto", "pk": 15, "fields": {"product": 3, "image": "products/images/pantomime_800.png"}}, {"model": "core.productphoto", "pk": 16, "fields": {"product": 3, "image": "products/images/pantomime_beans.png"}}, {"model": "core.productphoto", "pk": 18, "fields": {"product": 2, "image": "products/images/pantomime_beans_J2bFBiH.png"}}, {"model": "core.productphoto", "pk": 19, "fields": {"product": 4, "image": "products/images/decaf_800.png"}}, {"model": "core.productphoto", "pk": 20, "fields": {"product": 4, "image": "products/images/pantomime_beans_Lo0hJRx.png"}}]
|
[{
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"name": "Ethiopia",
|
||||||
|
"description": "Spicy espresso reminiscent of Northern Italy, on the mild side. Perfect for espresso, and steamed milk drinks. Also, a full-bodied, earthy sweet drip or Americano. Contains organic beans from Indonesia, Africa and America.",
|
||||||
|
"sku": "23468",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 4,
|
||||||
|
"created_at": "2022-02-19T20:15:36.292Z",
|
||||||
|
"updated_at": "2022-03-28T17:29:26.300Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"name": "Sumatra",
|
||||||
|
"description": "Dark heavy-bodied roast with a lingering chocolatey taste. Organic Single origin.",
|
||||||
|
"sku": "89765",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 3,
|
||||||
|
"created_at": "2022-02-19T20:15:59.741Z",
|
||||||
|
"updated_at": "2022-03-28T17:29:08.706Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 3,
|
||||||
|
"fields": {
|
||||||
|
"name": "Pantomime",
|
||||||
|
"description": "Very Dark French Roast\r\nOur darkest drip. A blend of five different beans roasted two ways. Organic Africa, Indonesia, and South and Central America.",
|
||||||
|
"sku": "565656",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 1,
|
||||||
|
"created_at": "2022-02-23T17:59:00.711Z",
|
||||||
|
"updated_at": "2022-03-28T17:28:58.670Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 4,
|
||||||
|
"fields": {
|
||||||
|
"name": "Decaf",
|
||||||
|
"description": "French Roast (Water Processed)\r\n\r\n“I can’t believe it’s decaf!”. The best-tasting Swiss water process decaf we have developed over the past 30 years, for an unbelievable espresso or drip coffee. Organic Africa, Indonesia and South and Central America.",
|
||||||
|
"sku": "566565",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 2,
|
||||||
|
"created_at": "2022-02-23T17:59:32.099Z",
|
||||||
|
"updated_at": "2022-03-28T17:28:45.512Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"name": "Moka Java Blend",
|
||||||
|
"description": "Dark Roast\r\n\r\nA classic Moka Java style blend dark roasted with organic beans for a perfect body and sweetness with a hint of citrus.",
|
||||||
|
"sku": "56466",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": false,
|
||||||
|
"sorting": 5,
|
||||||
|
"created_at": "2022-02-23T18:05:41.742Z",
|
||||||
|
"updated_at": "2022-03-28T17:29:39.761Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 6,
|
||||||
|
"fields": {
|
||||||
|
"name": "Loop d’ Loop",
|
||||||
|
"description": "Mild Dark Roast\r\n\r\nOur most popular blend reminiscent of Central Italy. A dark, chocolaty flavor perfect for espresso or drip. It’s dark Vienna roast properties make it ideal for steamed milk drinks. Organic Indonesia, Africa and America.",
|
||||||
|
"sku": "53264",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 6,
|
||||||
|
"created_at": "2022-02-23T18:06:09.881Z",
|
||||||
|
"updated_at": "2022-03-28T17:29:53.104Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 7,
|
||||||
|
"fields": {
|
||||||
|
"name": "Dante’s Tornado",
|
||||||
|
"description": "Medium Roast\r\n\r\nFull City spicy espresso roast reminiscent of Northern Italy, on the mild side. A full- bodied, earthy sweet drip or Americano. Organic Indonesia, Africa and America.",
|
||||||
|
"sku": "78945",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 7,
|
||||||
|
"created_at": "2022-02-23T18:06:35.593Z",
|
||||||
|
"updated_at": "2022-03-28T17:30:11.445Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.product",
|
||||||
|
"pk": 8,
|
||||||
|
"fields": {
|
||||||
|
"name": "Nicaragua",
|
||||||
|
"description": "Mild Roast\r\n\r\nOur mildest roast with sweet and fruity notes, containing organic beans from Nicaragua. Single origin.",
|
||||||
|
"sku": "12365",
|
||||||
|
"price": "13.40",
|
||||||
|
"weight": "16.0:oz",
|
||||||
|
"visible_in_listings": true,
|
||||||
|
"sorting": 8,
|
||||||
|
"created_at": "2022-02-23T18:06:57.624Z",
|
||||||
|
"updated_at": "2022-03-28T17:30:20.941Z"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"product": 1,
|
||||||
|
"image": "products/images/slice2.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"product": 2,
|
||||||
|
"image": "products/images/slice1.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 5,
|
||||||
|
"fields": {
|
||||||
|
"product": 5,
|
||||||
|
"image": "products/images/moka_java.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 6,
|
||||||
|
"fields": {
|
||||||
|
"product": 6,
|
||||||
|
"image": "products/images/loop_d_loop.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 7,
|
||||||
|
"fields": {
|
||||||
|
"product": 7,
|
||||||
|
"image": "products/images/dantes_tornado.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 8,
|
||||||
|
"fields": {
|
||||||
|
"product": 8,
|
||||||
|
"image": "products/images/nicaragua.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 15,
|
||||||
|
"fields": {
|
||||||
|
"product": 3,
|
||||||
|
"image": "products/images/pantomime_800.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 16,
|
||||||
|
"fields": {
|
||||||
|
"product": 3,
|
||||||
|
"image": "products/images/pantomime_beans.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 18,
|
||||||
|
"fields": {
|
||||||
|
"product": 2,
|
||||||
|
"image": "products/images/pantomime_beans_J2bFBiH.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 19,
|
||||||
|
"fields": {
|
||||||
|
"product": 4,
|
||||||
|
"image": "products/images/decaf_800.png"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.productphoto",
|
||||||
|
"pk": 20,
|
||||||
|
"fields": {
|
||||||
|
"product": 4,
|
||||||
|
"image": "products/images/pantomime_beans_Lo0hJRx.png"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|||||||
@ -28,5 +28,24 @@
|
|||||||
<span class="object__item">No orders</span>
|
<span class="object__item">No orders</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<div class="pagination">
|
||||||
|
<p class="step-links">
|
||||||
|
{% if page_obj.has_previous %}
|
||||||
|
<a href="?page=1">« first</a>
|
||||||
|
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="current">
|
||||||
|
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{% if page_obj.has_next %}
|
||||||
|
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||||
|
<a href="?page={{ page_obj.paginator.num_pages }}">last »</a>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</article>
|
</article>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -134,6 +134,7 @@ class CouponDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
|||||||
class OrderListView(LoginRequiredMixin, ListView):
|
class OrderListView(LoginRequiredMixin, ListView):
|
||||||
model = Order
|
model = Order
|
||||||
template_name = 'dashboard/order_list.html'
|
template_name = 'dashboard/order_list.html'
|
||||||
|
paginate_by = 50
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
query = self.request.GET.get('status')
|
query = self.request.GET.get('status')
|
||||||
|
|||||||
@ -6,7 +6,7 @@ load_dotenv()
|
|||||||
DEBUG = os.environ.get('DEBUG', 'True') == 'True'
|
DEBUG = os.environ.get('DEBUG', 'True') == 'True'
|
||||||
|
|
||||||
DATABASE_CONFIG = {
|
DATABASE_CONFIG = {
|
||||||
'ENGINE' : 'django.db.backends.postgresql',
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'service': 'pg_service',
|
'service': 'pg_service',
|
||||||
'passfile': '.pgpass'
|
'passfile': '.pgpass'
|
||||||
@ -14,8 +14,8 @@ DATABASE_CONFIG = {
|
|||||||
}
|
}
|
||||||
SECRET_KEY = os.environ.get('SECRET_KEY', '')
|
SECRET_KEY = os.environ.get('SECRET_KEY', '')
|
||||||
CACHE_CONFIG = {
|
CACHE_CONFIG = {
|
||||||
'LOCATION' : 'redis://127.0.0.1:6379',
|
'LOCATION': 'redis://127.0.0.1:6379',
|
||||||
'BACKEND' : 'django.core.cache.backends.redis.RedisCache',
|
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
||||||
}
|
}
|
||||||
|
|
||||||
PAYPAL_CLIENT_ID = os.environ.get('PAYPAL_CLIENT_ID', '')
|
PAYPAL_CLIENT_ID = os.environ.get('PAYPAL_CLIENT_ID', '')
|
||||||
|
|||||||
@ -240,6 +240,16 @@ input[type=submit]:hover,
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.contact-form {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.contact-form p:nth-child(6),
|
||||||
|
.contact-form p:last-child {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ from core import CoffeeGrind
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PayPalClient:
|
class PayPalClient:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.client_id = settings.PAYPAL_CLIENT_ID
|
self.client_id = settings.PAYPAL_CLIENT_ID
|
||||||
@ -25,10 +26,14 @@ class PayPalClient:
|
|||||||
"""Setting up and Returns PayPal SDK environment with PayPal Access credentials.
|
"""Setting up and Returns PayPal SDK environment with PayPal Access credentials.
|
||||||
For demo purpose, we are using SandboxEnvironment. In production this will be
|
For demo purpose, we are using SandboxEnvironment. In production this will be
|
||||||
LiveEnvironment."""
|
LiveEnvironment."""
|
||||||
if settings.PAYPAL_ENVIRONMENT == 'LIVE':
|
if settings.PAYPAL_ENVIRONMENT == "LIVE":
|
||||||
self.environment = LiveEnvironment(client_id=self.client_id, client_secret=self.client_secret)
|
self.environment = LiveEnvironment(
|
||||||
|
client_id=self.client_id, client_secret=self.client_secret
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.environment = SandboxEnvironment(client_id=self.client_id, client_secret=self.client_secret)
|
self.environment = SandboxEnvironment(
|
||||||
|
client_id=self.client_id, client_secret=self.client_secret
|
||||||
|
)
|
||||||
|
|
||||||
""" Returns PayPal HTTP client instance with environment which has access
|
""" Returns PayPal HTTP client instance with environment which has access
|
||||||
credentials context. This can be used invoke PayPal API's provided the
|
credentials context. This can be used invoke PayPal API's provided the
|
||||||
@ -44,25 +49,36 @@ class PayPalClient:
|
|||||||
itr = json_data.__dict__.iteritems()
|
itr = json_data.__dict__.iteritems()
|
||||||
else:
|
else:
|
||||||
itr = json_data.__dict__.items()
|
itr = json_data.__dict__.items()
|
||||||
for key,value in itr:
|
for key, value in itr:
|
||||||
# Skip internal attributes.
|
# Skip internal attributes.
|
||||||
if key.startswith("__") or key.startswith("_"):
|
if key.startswith("__") or key.startswith("_"):
|
||||||
continue
|
continue
|
||||||
result[key] = self.array_to_json_array(value) if isinstance(value, list) else\
|
result[key] = (
|
||||||
self.object_to_json(value) if not self.is_primittive(value) else\
|
self.array_to_json_array(value)
|
||||||
value
|
if isinstance(value, list)
|
||||||
|
else self.object_to_json(value)
|
||||||
|
if not self.is_primittive(value)
|
||||||
|
else value
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def array_to_json_array(self, json_array):
|
def array_to_json_array(self, json_array):
|
||||||
result =[]
|
result = []
|
||||||
if isinstance(json_array, list):
|
if isinstance(json_array, list):
|
||||||
for item in json_array:
|
for item in json_array:
|
||||||
result.append(self.object_to_json(item) if not self.is_primittive(item) \
|
result.append(
|
||||||
else self.array_to_json_array(item) if isinstance(item, list) else item)
|
self.object_to_json(item)
|
||||||
|
if not self.is_primittive(item)
|
||||||
|
else self.array_to_json_array(item)
|
||||||
|
if isinstance(item, list)
|
||||||
|
else item
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def is_primittive(self, data):
|
def is_primittive(self, data):
|
||||||
return isinstance(data, str) or isinstance(data, unicode) or isinstance(data, int)
|
return (
|
||||||
|
isinstance(data, str) or isinstance(data, unicode) or isinstance(data, int)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateOrder(PayPalClient):
|
class CreateOrder(PayPalClient):
|
||||||
@ -72,19 +88,34 @@ class CreateOrder(PayPalClient):
|
|||||||
|
|
||||||
def build_request_body(self, params):
|
def build_request_body(self, params):
|
||||||
"""Method to create body with CAPTURE intent"""
|
"""Method to create body with CAPTURE intent"""
|
||||||
processed_items = [{
|
processed_items = [
|
||||||
|
{
|
||||||
# Shows within upper-right dropdown during payment approval
|
# Shows within upper-right dropdown during payment approval
|
||||||
'name': f'{item["product"]}: ' + ', '.join([next((f"{value['quantity']} x {v[1]}" for i, v in enumerate(CoffeeGrind.GRIND_CHOICES) if v[0] == key), None)
|
"name": f'{item["product"]}: '
|
||||||
for key, value in item['variations'].items()])[:100],
|
", ".join(
|
||||||
# Item details will also be in the completed paypal.com transaction view
|
[
|
||||||
|
next(
|
||||||
'description': item['product'].subtitle,
|
(
|
||||||
'unit_amount': {
|
f"{value['quantity']} x {v[1]}"
|
||||||
'currency_code': settings.DEFAULT_CURRENCY,
|
for i, v in enumerate(CoffeeGrind.GRIND_CHOICES)
|
||||||
'value': f'{item["price"]}'
|
if v[0] == key
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
for key, value in item["variations"].items()
|
||||||
|
]
|
||||||
|
)[:100],
|
||||||
|
# Item details will also be in the completed paypal.com
|
||||||
|
# transaction view
|
||||||
|
"description": item["product"].subtitle,
|
||||||
|
"unit_amount": {
|
||||||
|
"currency_code": settings.DEFAULT_CURRENCY,
|
||||||
|
"value": f'{item["price"]}',
|
||||||
},
|
},
|
||||||
'quantity': f'{item["quantity"]}'
|
"quantity": f'{item["quantity"]}',
|
||||||
} for item in params['items']]
|
}
|
||||||
|
for item in params["items"]
|
||||||
|
]
|
||||||
|
|
||||||
request_body = {
|
request_body = {
|
||||||
"intent": "CAPTURE",
|
"intent": "CAPTURE",
|
||||||
@ -100,41 +131,40 @@ class CreateOrder(PayPalClient):
|
|||||||
{
|
{
|
||||||
# "reference_id": "PUHF",
|
# "reference_id": "PUHF",
|
||||||
"description": "Coffee",
|
"description": "Coffee",
|
||||||
|
|
||||||
# "custom_id": "CUST-HighFashions",
|
# "custom_id": "CUST-HighFashions",
|
||||||
# "soft_descriptor": "HighFashions",
|
# "soft_descriptor": "HighFashions",
|
||||||
"amount": {
|
"amount": {
|
||||||
"currency_code": "USD",
|
"currency_code": "USD",
|
||||||
"value": params['total_price'],
|
"value": params["total_price"],
|
||||||
"breakdown": {
|
"breakdown": {
|
||||||
"item_total": {
|
"item_total": {
|
||||||
"currency_code": "USD",
|
"currency_code": "USD",
|
||||||
"value": params['item_total']
|
"value": params["item_total"],
|
||||||
},
|
},
|
||||||
"shipping": {
|
"shipping": {
|
||||||
"currency_code": "USD",
|
"currency_code": "USD",
|
||||||
"value": params['shipping_price']
|
"value": params["shipping_price"],
|
||||||
},
|
},
|
||||||
"tax_total": {
|
"tax_total": {
|
||||||
"currency_code": "USD",
|
"currency_code": "USD",
|
||||||
"value": params['tax_total']
|
"value": params["tax_total"],
|
||||||
},
|
},
|
||||||
"discount": {
|
"discount": {
|
||||||
"currency_code": "USD",
|
"currency_code": "USD",
|
||||||
"value": params['discount']
|
"value": params["discount"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"items": processed_items,
|
"items": processed_items,
|
||||||
"shipping": {
|
"shipping": {
|
||||||
"method": params['shipping_method'],
|
"method": params["shipping_method"],
|
||||||
"address": params['shipping_address']
|
"address": params["shipping_address"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(f'\nRequest body: {request_body}\n')
|
logger.info(f"\nRequest body: {request_body}\n")
|
||||||
|
|
||||||
return request_body
|
return request_body
|
||||||
|
|
||||||
@ -143,29 +173,37 @@ class CreateOrder(PayPalClient):
|
|||||||
|
|
||||||
def create_order(self, params, debug=False):
|
def create_order(self, params, debug=False):
|
||||||
request = OrdersCreateRequest()
|
request = OrdersCreateRequest()
|
||||||
request.headers['prefer'] = 'return=representation'
|
request.headers["prefer"] = "return=representation"
|
||||||
request.request_body(self.build_request_body(params))
|
request.request_body(self.build_request_body(params))
|
||||||
response = self.client.execute(request)
|
response = self.client.execute(request)
|
||||||
if debug:
|
if debug:
|
||||||
logger.debug(f'\nStatus Code: {response.status_code}', )
|
logger.debug(
|
||||||
logger.debug(f'\nStatus: {response.result.status}', )
|
f"\nStatus Code: {response.status_code}",
|
||||||
logger.debug(f'\nOrder ID: {response.result.id}', )
|
)
|
||||||
logger.debug(f'\nIntent: {response.result.intent}', )
|
logger.debug(
|
||||||
|
f"\nStatus: {response.result.status}",
|
||||||
|
)
|
||||||
|
logger.debug(
|
||||||
|
f"\nOrder ID: {response.result.id}",
|
||||||
|
)
|
||||||
|
logger.debug(
|
||||||
|
f"\nIntent: {response.result.intent}",
|
||||||
|
)
|
||||||
logger.debug(f"\njson_data: {response.result}")
|
logger.debug(f"\njson_data: {response.result}")
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
class CaptureOrder(PayPalClient):
|
|
||||||
|
|
||||||
|
class CaptureOrder(PayPalClient):
|
||||||
def capture_order(self, order_id, debug=False):
|
def capture_order(self, order_id, debug=False):
|
||||||
"""Method to capture order using order_id"""
|
"""Method to capture order using order_id"""
|
||||||
request = OrdersCaptureRequest(order_id)
|
request = OrdersCaptureRequest(order_id)
|
||||||
response = self.client.execute(request)
|
response = self.client.execute(request)
|
||||||
|
|
||||||
data = response.result.__dict__['_dict']
|
data = response.result.__dict__["_dict"]
|
||||||
data['redirect_urls'] = {
|
data["redirect_urls"] = {
|
||||||
'return_url': f"https://{self.site_domain}{reverse_lazy('storefront:payment-done')}",
|
"return_url": f"https://{self.site_domain}{reverse_lazy('storefront:payment-done')}",
|
||||||
'cancel_url': f"https://{self.site_domain}{reverse_lazy('storefront:payment-canceled')}"
|
"cancel_url": f"https://{self.site_domain}{reverse_lazy('storefront:payment-canceled')}",
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|||||||
25
src/templates/account/password_reset.html
Executable file
25
src/templates/account/password_reset.html
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load account %}
|
||||||
|
|
||||||
|
{% block head_title %}{% trans "Password Reset" %} | {% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<h1>{% trans "Password Reset" %}</h1>
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{% include "account/snippets/already_logged_in.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}</p>
|
||||||
|
|
||||||
|
<form method="POST" action="{% url 'account_reset_password' %}" class="password_reset">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<input type="submit" value="{% trans 'Reset My Password' %}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p>{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}</p>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
@ -1,7 +1,18 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load account %}
|
||||||
|
|
||||||
|
{% block head_title %}{% trans "Password Reset" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<article class="panel">
|
<article>
|
||||||
<p>An email with password reset instructions has been sent.</p>
|
<h1>{% trans "Password Reset" %}</h1>
|
||||||
|
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{% include "account/snippets/already_logged_in.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p>{% blocktrans %}We have sent you an e-mail. If you have not received it please check your spam folder. Otherwise contact us if you do not receive it in a few minutes.{% endblocktrans %}</p>
|
||||||
</article>
|
</article>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
{% extends 'base.html' %} {% block content %}
|
|
||||||
<article class="panel">
|
|
||||||
<h1>Reset your password</h1>
|
|
||||||
<p>Enter your email address below and we'll send you instructions on how to reset your password.</p>
|
|
||||||
<form method="post" action="{% url 'password_reset' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.as_p }}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<input type="submit" value="Send me instructions" class="action-button">
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
{% endblock %}
|
|
||||||
21
src/templates/account/password_reset_from_key.html
Normal file
21
src/templates/account/password_reset_from_key.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% block head_title %}{% trans "Change Password" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<h1>{% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}</h1>
|
||||||
|
|
||||||
|
{% if token_fail %}
|
||||||
|
{% url 'account_reset_password' as passwd_reset_url %}
|
||||||
|
<p>{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a <a href="{{ passwd_reset_url }}">new password reset</a>.{% endblocktrans %}</p>
|
||||||
|
{% else %}
|
||||||
|
<form method="POST" action="{{ action_url }}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<input type="submit" name="action" value="{% trans 'change password' %}"/>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
11
src/templates/account/password_reset_from_key_done.html
Normal file
11
src/templates/account/password_reset_from_key_done.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% block head_title %}{% trans "Change Password" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<h1>{% trans "Change Password" %}</h1>
|
||||||
|
<p>{% trans 'Your password is now changed.' %}</p>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
Loading…
x
Reference in New Issue
Block a user