Merge branch 'release/1.3.0'
This commit is contained in:
commit
356c759155
@ -1,5 +1,6 @@
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class DiscountValueType:
|
||||
FIXED = "fixed"
|
||||
PERCENTAGE = "percentage"
|
||||
@ -46,6 +47,7 @@ class OrderStatus:
|
||||
(CANCELED, "Canceled"),
|
||||
]
|
||||
|
||||
|
||||
class TransactionStatus:
|
||||
CREATED = "CREATED" # The order was created with the specified context.
|
||||
SAVED = "SAVED" # The order was saved and persisted. The order status continues to be in progress until a capture is made with final_capture = true for all purchase units within the order.
|
||||
@ -63,6 +65,7 @@ class TransactionStatus:
|
||||
(PAYER_ACTION_REQUIRED, "Payer action required")
|
||||
]
|
||||
|
||||
|
||||
class ShippingMethodType:
|
||||
PRICE_BASED = "price"
|
||||
WEIGHT_BASED = "weight"
|
||||
@ -72,6 +75,7 @@ class ShippingMethodType:
|
||||
(WEIGHT_BASED, "Weight based shipping"),
|
||||
]
|
||||
|
||||
|
||||
class ShippingService:
|
||||
FIRST_CLASS = "FIRST CLASS"
|
||||
PRIORITY = "PRIORITY"
|
||||
@ -83,6 +87,7 @@ class ShippingService:
|
||||
(PRIORITY_COMMERCIAL, "Priority Commercial")
|
||||
]
|
||||
|
||||
|
||||
class ShippingContainer:
|
||||
LG_FLAT_RATE_BOX = "LG FLAT RATE BOX"
|
||||
REGIONAL_RATE_BOX_A = "REGIONALRATEBOXA"
|
||||
|
||||
129
src/core/fixtures/orders.json
Normal file
129
src/core/fixtures/orders.json
Normal file
@ -0,0 +1,129 @@
|
||||
[
|
||||
{
|
||||
"model": "core.order",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"customer": null,
|
||||
"status": "unfulfilled",
|
||||
"billing_address": null,
|
||||
"shipping_address": 1,
|
||||
"shipping_method": null,
|
||||
"coupon": null,
|
||||
"shipping_total": "9.55",
|
||||
"total_net_amount": "13.40",
|
||||
"weight": "0.0:oz",
|
||||
"created_at": "2022-03-15T17:18:59.584Z",
|
||||
"updated_at": "2022-03-15T17:18:59.584Z"
|
||||
}
|
||||
}, {
|
||||
"model": "core.order",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"customer": null,
|
||||
"status": "unfulfilled",
|
||||
"billing_address": null,
|
||||
"shipping_address": 1,
|
||||
"shipping_method": null,
|
||||
"coupon": null,
|
||||
"shipping_total": "9.55",
|
||||
"total_net_amount": "13.40",
|
||||
"weight": "0.0:oz",
|
||||
"created_at": "2022-03-15T17:22:18.440Z",
|
||||
"updated_at": "2022-03-15T17:22:18.440Z"
|
||||
}
|
||||
}, {
|
||||
"model": "core.order",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"customer": null,
|
||||
"status": "unfulfilled",
|
||||
"billing_address": null,
|
||||
"shipping_address": 1,
|
||||
"shipping_method": null,
|
||||
"coupon": null,
|
||||
"shipping_total": "9.55",
|
||||
"total_net_amount": "13.40",
|
||||
"weight": "0.0:oz",
|
||||
"created_at": "2022-03-15T17:26:27.869Z",
|
||||
"updated_at": "2022-03-15T17:26:27.869Z"
|
||||
}
|
||||
}, {
|
||||
"model": "core.orderline",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"order": 1,
|
||||
"product": 1,
|
||||
"quantity": 2,
|
||||
"quantity_fulfilled": 0,
|
||||
"customer_note": "Whole Beans",
|
||||
"currency": "USD",
|
||||
"unit_price": "13.40",
|
||||
"tax_rate": "2.00"
|
||||
}
|
||||
}, {
|
||||
"model": "core.orderline",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"order": 1,
|
||||
"product": 1,
|
||||
"quantity": 1,
|
||||
"quantity_fulfilled": 1,
|
||||
"customer_note": "Espresso",
|
||||
"currency": "USD",
|
||||
"unit_price": "13.40",
|
||||
"tax_rate": "2.00"
|
||||
}
|
||||
}, {
|
||||
"model": "core.orderline",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"order": 2,
|
||||
"product": 8,
|
||||
"quantity": 1,
|
||||
"quantity_fulfilled": 1,
|
||||
"customer_note": "Whole Beans",
|
||||
"currency": "USD",
|
||||
"unit_price": "13.40",
|
||||
"tax_rate": "2.00"
|
||||
}
|
||||
}, {
|
||||
"model": "core.orderline",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"order": 2,
|
||||
"product": 7,
|
||||
"quantity": 1,
|
||||
"quantity_fulfilled": 1,
|
||||
"customer_note": "Cone Drip",
|
||||
"currency": "USD",
|
||||
"unit_price": "13.40",
|
||||
"tax_rate": "2.00"
|
||||
}
|
||||
}, {
|
||||
"model": "core.orderline",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"order": 3,
|
||||
"product": 4,
|
||||
"quantity": 1,
|
||||
"quantity_fulfilled": 0,
|
||||
"customer_note": "Percolator",
|
||||
"currency": "USD",
|
||||
"unit_price": "13.40",
|
||||
"tax_rate": "2.00"
|
||||
}
|
||||
}, {
|
||||
"model": "core.orderline",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"order": 3,
|
||||
"product": 6,
|
||||
"quantity": 1,
|
||||
"quantity_fulfilled": 0,
|
||||
"customer_note": "Whole Beans",
|
||||
"currency": "USD",
|
||||
"unit_price": "13.40",
|
||||
"tax_rate": "2.00"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -1,10 +1,19 @@
|
||||
import logging
|
||||
from django import forms
|
||||
|
||||
from core.models import Order, OrderLine, ShippingMethod, TrackingNumber, Coupon, ProductPhoto
|
||||
from core import OrderStatus
|
||||
from core.models import (
|
||||
Order,
|
||||
OrderLine,
|
||||
ShippingMethod,
|
||||
TrackingNumber,
|
||||
Coupon,
|
||||
ProductPhoto
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CouponForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Coupon
|
||||
@ -19,10 +28,10 @@ class CouponForm(forms.ModelForm):
|
||||
'products',
|
||||
)
|
||||
widgets = {
|
||||
'valid_from': forms.DateInput(attrs = {
|
||||
'valid_from': forms.DateInput(attrs={
|
||||
'type': 'date'
|
||||
}),
|
||||
'valid_to': forms.DateInput(attrs = {
|
||||
'valid_to': forms.DateInput(attrs={
|
||||
'type': 'date'
|
||||
}),
|
||||
}
|
||||
@ -35,7 +44,7 @@ class OrderLineFulfillForm(forms.ModelForm):
|
||||
model = OrderLine
|
||||
fields = ('quantity_fulfilled',)
|
||||
widgets = {
|
||||
'quantity_fulfilled': forms.NumberInput(attrs = {
|
||||
'quantity_fulfilled': forms.NumberInput(attrs={
|
||||
'min': 0,
|
||||
})
|
||||
}
|
||||
@ -44,12 +53,22 @@ class OrderLineFulfillForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['quantity_fulfilled'].widget.attrs['max'] = self.instance.quantity
|
||||
|
||||
|
||||
OrderLineFormset = forms.inlineformset_factory(
|
||||
Order, OrderLine, form=OrderLineFulfillForm,
|
||||
extra=0, can_delete=False
|
||||
)
|
||||
|
||||
|
||||
class OrderCancelForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ('status',)
|
||||
widgets = {
|
||||
'status': forms.HiddenInput()
|
||||
}
|
||||
|
||||
|
||||
class OrderTrackingForm(forms.ModelForm):
|
||||
# send_shipment_details_to_customer = forms.BooleanField(initial=True)
|
||||
|
||||
@ -57,6 +76,7 @@ class OrderTrackingForm(forms.ModelForm):
|
||||
model = TrackingNumber
|
||||
fields = ('tracking_id',)
|
||||
|
||||
|
||||
OrderTrackingFormset = forms.inlineformset_factory(
|
||||
Order, TrackingNumber, form=OrderTrackingForm,
|
||||
extra=1, can_delete=False
|
||||
|
||||
17
src/dashboard/templates/dashboard/order_cancel_form.html
Normal file
17
src/dashboard/templates/dashboard/order_cancel_form.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends "dashboard.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<header class="object__header">
|
||||
<h1>Cancel Order #{{order.pk}}</h1>
|
||||
</header>
|
||||
<section class="object__panel">
|
||||
<form method="POST" action="{% url 'dashboard:order-cancel' order.pk %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input class="action-button action-button--warning order__fulfill" type="submit" value="Cancel order"> or <a href="{% url 'dashboard:order-detail' order.pk %}">cancel</a>
|
||||
</section>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock content %}
|
||||
@ -9,7 +9,7 @@
|
||||
<div class="dropdown">
|
||||
<span class="dropdown__menu">Options ↓</span>
|
||||
<div class="dropdown__child">
|
||||
<a href="">Cancel order</a>
|
||||
<a href="{% url 'dashboard:order-cancel' order.pk %}">Cancel order</a>
|
||||
<a href="">Return order</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
0
src/dashboard/tests/__init__.py
Normal file
0
src/dashboard/tests/__init__.py
Normal file
80
src/dashboard/tests/test_views.py
Normal file
80
src/dashboard/tests/test_views.py
Normal file
@ -0,0 +1,80 @@
|
||||
import logging
|
||||
from decimal import Decimal
|
||||
|
||||
from django.test import TestCase, Client, RequestFactory
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from measurement.measures import Weight
|
||||
from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
|
||||
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
|
||||
|
||||
from accounts.models import User, Address
|
||||
from core.models import Product, Order, Coupon
|
||||
from core import CoffeeGrind
|
||||
from dashboard.forms import (
|
||||
CouponForm,
|
||||
OrderLineFulfillForm,
|
||||
OrderTrackingForm,
|
||||
ProductPhotoForm,
|
||||
)
|
||||
from dashboard.views import (
|
||||
DashboardHomeView,
|
||||
DashboardConfigView,
|
||||
ShippingMethodCreateView,
|
||||
ShippingMethodDetailView,
|
||||
CouponListView,
|
||||
CouponCreateView,
|
||||
CouponDetailView,
|
||||
CouponUpdateView,
|
||||
CouponDeleteView,
|
||||
OrderListView,
|
||||
OrderDetailView,
|
||||
OrderFulfillView,
|
||||
OrderTrackingView,
|
||||
ProductListView,
|
||||
ProductDetailView,
|
||||
ProductUpdateView,
|
||||
ProductCreateView,
|
||||
ProductDeleteView,
|
||||
ProductPhotoCreateView,
|
||||
ProductPhotoDeleteView,
|
||||
CustomerListView,
|
||||
CustomerDetailView,
|
||||
CustomerUpdateView
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OrderCancelViewTests(TestCase):
|
||||
fixtures = [
|
||||
'accounts.json',
|
||||
'coupons.json',
|
||||
'products.json',
|
||||
'orders.json'
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.admin_user = User.objects.get(pk=1)
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.client.force_login(self.admin_user)
|
||||
|
||||
def test_view_url_exists_at_desired_location(self):
|
||||
response = self.client.get('/dashboard/orders/1/cancel/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_view_url_accesible_by_name(self):
|
||||
response = self.client.get(
|
||||
reverse('dashboard:order-cancel', kwargs={'pk': 1})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_view_uses_correct_template(self):
|
||||
response = self.client.get(
|
||||
reverse('dashboard:order-cancel', kwargs={'pk': 1})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'dashboard/order_cancel_form.html')
|
||||
@ -21,9 +21,8 @@ urlpatterns = [
|
||||
path('orders/', views.OrderListView.as_view(), name='order-list'),
|
||||
path('orders/<int:pk>/', include([
|
||||
path('', views.OrderDetailView.as_view(), name='order-detail'),
|
||||
# path('update/', views.OrderUpdateView.as_view(), name='product-update'),
|
||||
# path('delete/', views.OrderDeleteView.as_view(), name='product-delete'),
|
||||
path('fulfill/', views.OrderFulfillView.as_view(), name='order-fulfill'),
|
||||
path('cancel/', views.OrderCancelView.as_view(), name='order-cancel'),
|
||||
path('ship/', views.OrderTrackingView.as_view(), name='order-ship'),
|
||||
])),
|
||||
|
||||
|
||||
@ -6,7 +6,9 @@ from django.shortcuts import render, reverse, redirect, get_object_or_404
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.views.generic.base import RedirectView, TemplateView
|
||||
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView, FormMixin
|
||||
from django.views.generic.edit import (
|
||||
FormView, CreateView, UpdateView, DeleteView, FormMixin
|
||||
)
|
||||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||
from django.views.generic.list import ListView
|
||||
from django.contrib.auth.decorators import login_required
|
||||
@ -34,11 +36,24 @@ from core.models import (
|
||||
Coupon
|
||||
)
|
||||
|
||||
from core import DiscountValueType, VoucherType, OrderStatus, ShippingMethodType
|
||||
from .forms import OrderLineFulfillForm, OrderLineFormset, OrderTrackingFormset, CouponForm, ProductPhotoForm
|
||||
from core import (
|
||||
DiscountValueType,
|
||||
VoucherType,
|
||||
OrderStatus,
|
||||
ShippingMethodType
|
||||
)
|
||||
from .forms import (
|
||||
OrderLineFulfillForm,
|
||||
OrderLineFormset,
|
||||
OrderCancelForm,
|
||||
OrderTrackingFormset,
|
||||
CouponForm,
|
||||
ProductPhotoForm
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DashboardHomeView(LoginRequiredMixin, TemplateView):
|
||||
template_name = 'dashboard/dashboard_detail.html'
|
||||
|
||||
@ -56,6 +71,7 @@ class DashboardHomeView(LoginRequiredMixin, TemplateView):
|
||||
).aggregate(total=Sum('total_net_amount'))['total']
|
||||
return context
|
||||
|
||||
|
||||
class DashboardConfigView(TemplateView):
|
||||
template_name = 'dashboard/config.html'
|
||||
|
||||
@ -68,40 +84,42 @@ class DashboardConfigView(TemplateView):
|
||||
return context
|
||||
|
||||
|
||||
|
||||
|
||||
class ShippingMethodCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = ShippingMethod
|
||||
template_name = 'dashboard/shipmeth_create_form.html'
|
||||
fields = '__all__'
|
||||
success_message = '%(name)s created.'
|
||||
|
||||
|
||||
class ShippingMethodDetailView(LoginRequiredMixin, DetailView):
|
||||
model = ShippingMethod
|
||||
template_name = 'dashboard/shipmeth_detail.html'
|
||||
|
||||
|
||||
|
||||
class CouponListView(LoginRequiredMixin, ListView):
|
||||
model = Coupon
|
||||
template_name = 'dashboard/coupon_list.html'
|
||||
|
||||
|
||||
class CouponCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = Coupon
|
||||
template_name = 'dashboard/coupon_create_form.html'
|
||||
form_class = CouponForm
|
||||
success_message = '%(name)s created.'
|
||||
|
||||
|
||||
class CouponDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Coupon
|
||||
template_name = 'dashboard/coupon_detail.html'
|
||||
|
||||
|
||||
class CouponUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Coupon
|
||||
template_name = 'dashboard/coupon_form.html'
|
||||
success_message = '%(name)s saved.'
|
||||
form_class = CouponForm
|
||||
|
||||
|
||||
class CouponDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = Coupon
|
||||
template_name = 'dashboard/coupon_confirm_delete.html'
|
||||
@ -109,7 +127,6 @@ class CouponDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
success_message = 'Coupon deleted.'
|
||||
|
||||
|
||||
|
||||
class OrderListView(LoginRequiredMixin, ListView):
|
||||
model = Order
|
||||
template_name = 'dashboard/order_list.html'
|
||||
@ -135,6 +152,7 @@ class OrderListView(LoginRequiredMixin, ListView):
|
||||
|
||||
return object_list
|
||||
|
||||
|
||||
class OrderDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Order
|
||||
template_name = 'dashboard/order_detail.html'
|
||||
@ -171,6 +189,20 @@ class OrderFulfillView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk})
|
||||
|
||||
|
||||
class OrderCancelView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Order
|
||||
template_name = "dashboard/order_cancel_form.html"
|
||||
form_class = OrderCancelForm
|
||||
success_message = "Order canceled."
|
||||
initial = {
|
||||
'status': OrderStatus.CANCELED
|
||||
}
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk})
|
||||
|
||||
|
||||
class OrderTrackingView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Order
|
||||
template_name = "dashboard/order_tracking_form.html"
|
||||
@ -198,22 +230,26 @@ class ProductListView(LoginRequiredMixin, ListView):
|
||||
# )
|
||||
# return object_list
|
||||
|
||||
|
||||
class ProductDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Product
|
||||
template_name = 'dashboard/product_detail.html'
|
||||
|
||||
|
||||
class ProductUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = Product
|
||||
template_name = 'dashboard/product_update_form.html'
|
||||
fields = '__all__'
|
||||
success_message = '%(name)s saved.'
|
||||
|
||||
|
||||
class ProductCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = Product
|
||||
template_name = 'dashboard/product_create_form.html'
|
||||
fields = '__all__'
|
||||
success_message = '%(name)s created.'
|
||||
|
||||
|
||||
class ProductDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = Product
|
||||
template_name = 'dashboard/product_confirm_delete.html'
|
||||
@ -240,6 +276,7 @@ class ProductPhotoCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||
def get_success_url(self):
|
||||
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
|
||||
class ProductPhotoDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = ProductPhoto
|
||||
pk_url_kwarg = 'photo_pk'
|
||||
@ -250,10 +287,6 @@ class ProductPhotoDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView
|
||||
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CustomerListView(LoginRequiredMixin, ListView):
|
||||
model = User
|
||||
template_name = 'dashboard/customer_list.html'
|
||||
@ -271,11 +304,13 @@ class CustomerListView(LoginRequiredMixin, ListView):
|
||||
|
||||
return object_list
|
||||
|
||||
|
||||
class CustomerDetailView(LoginRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'dashboard/customer_detail.html'
|
||||
context_object_name = 'customer'
|
||||
|
||||
|
||||
class CustomerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = User
|
||||
template_name = 'dashboard/customer_form.html'
|
||||
@ -292,4 +327,3 @@ class CustomerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('dashboard:customer-detail', kwargs={'pk': self.object.pk})
|
||||
|
||||
|
||||
4413
src/fixtures/db.json
4413
src/fixtures/db.json
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user