kopia lustrzana https://github.com/longclawshop/longclaw
commit
c9d9e1e5eb
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-07 20:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('basket', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='basketitem',
|
||||
old_name='product',
|
||||
new_name='variant',
|
||||
),
|
||||
]
|
||||
|
|
@ -7,9 +7,8 @@ from rest_framework.decorators import api_view, permission_classes
|
|||
from rest_framework import permissions, status
|
||||
from rest_framework.response import Response
|
||||
from longclaw.basket.utils import get_basket_items, destroy_basket
|
||||
from longclaw.orders.models import Order, OrderItem, Address
|
||||
from longclaw.checkout.models import ShippingCountry
|
||||
from longclaw.checkout import serializers
|
||||
from longclaw.orders.models import Order, OrderItem
|
||||
from longclaw.shipping.models import Address
|
||||
from longclaw.checkout.utils import PaymentError
|
||||
from longclaw import settings
|
||||
|
||||
|
|
@ -95,7 +94,6 @@ def capture_payment(request):
|
|||
)
|
||||
order_item.save()
|
||||
|
||||
|
||||
try:
|
||||
gateway.create_payment(request, float(total)+postage)
|
||||
# Once the order has been successfully taken, we can empty the basket
|
||||
|
|
@ -108,70 +106,3 @@ def capture_payment(request):
|
|||
response = Response(data={"message": err.message, "order_id": order.id},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
return response
|
||||
|
||||
class InvalidShippingRate(Exception):
|
||||
pass
|
||||
|
||||
class InvalidShippingCountry(Exception):
|
||||
pass
|
||||
|
||||
def get_shipping_cost(country_code, option):
|
||||
try:
|
||||
obj = ShippingCountry.objects.get(country_code=country_code)
|
||||
if option == 'standard':
|
||||
return {"rate": obj.standard_rate,
|
||||
"description": obj.standard_rate_description,
|
||||
"carrier": obj.standard_rate_carrier}
|
||||
elif option == 'premium':
|
||||
return {"rate": obj.premium_rate,
|
||||
"description": obj.premium_rate_description,
|
||||
"carrier": obj.premium_rate_carrier}
|
||||
else:
|
||||
raise InvalidShippingRate
|
||||
|
||||
except ShippingCountry.DoesNotExist:
|
||||
if settings.DEFAULT_SHIPPING_ENABLED:
|
||||
return {"rate": settings.DEFAULT_SHIPPING_RATE,
|
||||
"description": "Standard shipping to rest of world",
|
||||
"carrier": settings.DEFAULT_SHIPPING_CARRIER}
|
||||
else:
|
||||
raise InvalidShippingCountry
|
||||
|
||||
|
||||
|
||||
@api_view(['GET'])
|
||||
@permission_classes({permissions.AllowAny})
|
||||
def shipping_cost(request):
|
||||
''' Returns the shipping cost for a given country
|
||||
If the shipping cost for the given country has not been set, it will
|
||||
fallback to the default shipping cost if it has been enabled in the app
|
||||
settings
|
||||
'''
|
||||
try:
|
||||
code = request.query_params.get('country_code')
|
||||
except AttributeError:
|
||||
return Response(data={"message": "No country code supplied"},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
option = request.query_params.get('shipping_option', 'standard')
|
||||
try:
|
||||
data = get_shipping_cost(code, option)
|
||||
response = Response(data=data, status=status.HTTP_200_OK)
|
||||
except InvalidShippingRate:
|
||||
response = Response(data={"message": "Shipping option {} is invalid".format(option)},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
except InvalidShippingCountry:
|
||||
response = Response(data={"message": "Shipping to {} is not available".format(code)},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
@permission_classes([permissions.AllowAny])
|
||||
def get_shipping_countries(request):
|
||||
''' Get all shipping countries
|
||||
'''
|
||||
queryset = ShippingCountry.objects.all()
|
||||
serializer = serializers.ShippingCountrySerializer(queryset, many=True)
|
||||
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import braintree
|
||||
from longclaw import settings
|
||||
from longclaw.longclawsettings.models import LongclawSettings
|
||||
from longclaw.checkout.utils import PaymentError
|
||||
from longclaw.checkout.gateways import BasePayment
|
||||
|
||||
|
|
@ -38,11 +39,12 @@ class PaypalVZeroPayment():
|
|||
self.gateway = braintree.BraintreeGateway(access_token=settings.VZERO_ACCESS_TOKEN)
|
||||
|
||||
def create_payment(self, request, amount, description=''):
|
||||
longclaw_settings = LongclawSettings.for_site(request.site)
|
||||
nonce = request.data['payment_method_nonce']
|
||||
result = self.gateway.transaction.sale({
|
||||
"amount": str(amount),
|
||||
"payment_method_nonce": nonce,
|
||||
"merchant_account_id": settings.CURRENCY,
|
||||
"merchant_account_id": longclaw_settings.currency,
|
||||
"options": {
|
||||
"paypal": {
|
||||
"description": description
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import math
|
||||
import stripe
|
||||
from longclaw.settings import CURRENCY, STRIPE_SECRET
|
||||
from longclaw.settings import STRIPE_SECRET
|
||||
from longclaw.longclawsettings.models import LongclawSettings
|
||||
from longclaw.checkout.utils import PaymentError
|
||||
from longclaw.checkout.gateways import BasePayment
|
||||
|
||||
|
|
@ -14,9 +15,10 @@ class StripePayment(BasePayment):
|
|||
|
||||
def create_payment(self, request, amount):
|
||||
try:
|
||||
currency = LongclawSettings.for_site(request.site).currency
|
||||
charge = stripe.Charge.create(
|
||||
amount=int(math.ceil(amount * 100)), # Amount in pence
|
||||
currency=CURRENCY.lower(),
|
||||
currency=currency.lower(),
|
||||
source=request.data['token'],
|
||||
description="Payment from"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 16:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('checkout', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='ShippingCountry',
|
||||
),
|
||||
]
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
class ShippingCountry(models.Model):
|
||||
''' Standard and premimum rate shipping for
|
||||
individual countries.
|
||||
'''
|
||||
country_code = models.CharField(max_length=3, primary_key=True)
|
||||
country_name = models.CharField(max_length=32)
|
||||
standard_rate = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
standard_rate_carrier = models.CharField(max_length=64, default="Royal Mail")
|
||||
standard_rate_description = models.CharField(max_length=128,
|
||||
default="Royal Mail standard shipping")
|
||||
premium_rate = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
premium_rate_carrier = models.CharField(max_length=64, default="Royal Mail")
|
||||
premium_rate_description = models.CharField(max_length=128,
|
||||
default="Royal Mail tracked and signed for")
|
||||
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from longclaw.products.serializers import ProductVariantSerializer
|
||||
from longclaw.checkout.models import ShippingCountry
|
||||
|
||||
class ShippingCountrySerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = ShippingCountry
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
|
|
@ -2,9 +2,6 @@ from django.conf.urls import url
|
|||
from longclaw.checkout import api
|
||||
|
||||
urlpatterns = [
|
||||
url(r'shipping/$',
|
||||
api.shipping_cost,
|
||||
name='shipping'),
|
||||
url(r'payment/$',
|
||||
api.capture_payment,
|
||||
name='payment'),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LongclawSettingsConfig(AppConfig):
|
||||
name = 'longclawsettings'
|
||||
verbose_name = 'Longclaw Settings'
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 14:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0030_index_on_pagerevision_created_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LongclawSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('default_shipping_rate', models.DecimalField(decimal_places=2, help_text='The default shipping rate for countries which have not been configured', max_digits=12)),
|
||||
('default_shipping_carrier', models.CharField(help_text='The default shipping carrier', max_length=32)),
|
||||
('default_shipping_enabled', models.BooleanField(default=False, help_text='Whether to enable default shipping. This essentially means you ship to all countries, not only those with configured shipping rates')),
|
||||
('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 14:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('longclawsettings', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='longclawsettings',
|
||||
name='default_shipping_carrier',
|
||||
field=models.CharField(default='Royal Mail', help_text='The default shipping carrier', max_length=32),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='longclawsettings',
|
||||
name='default_shipping_rate',
|
||||
field=models.DecimalField(decimal_places=2, default=3.95, help_text='The default shipping rate for countries which have not been configured', max_digits=12),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 15:03
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('longclawsettings', '0002_auto_20170212_1423'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='longclawsettings',
|
||||
name='currency_html_code',
|
||||
field=models.CharField(default='£', max_length=12),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 16:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('longclawsettings', '0003_longclawsettings_currency_html_code'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='longclawsettings',
|
||||
name='currency',
|
||||
field=models.CharField(default='GBP', max_length=6),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 16:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('longclawsettings', '0004_longclawsettings_currency'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='longclawsettings',
|
||||
name='currency',
|
||||
field=models.CharField(default='GBP', help_text='The iso currency code to use for payments', max_length=6),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='longclawsettings',
|
||||
name='currency_html_code',
|
||||
field=models.CharField(default='£', help_text='The HTML code for the currency symbol. Used for display purposes only', max_length=12),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
'''
|
||||
Admin confiurable settings for longclaw apps
|
||||
'''
|
||||
from wagtail.contrib.settings.models import BaseSetting, register_setting
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
||||
from django.db import models
|
||||
|
||||
|
||||
@register_setting
|
||||
class LongclawSettings(BaseSetting):
|
||||
default_shipping_rate = models.DecimalField(
|
||||
default=3.95,
|
||||
max_digits=12,
|
||||
decimal_places=2,
|
||||
help_text='The default shipping rate for countries which have not been configured'
|
||||
)
|
||||
default_shipping_carrier = models.CharField(
|
||||
default="Royal Mail",
|
||||
max_length=32,
|
||||
help_text='The default shipping carrier'
|
||||
)
|
||||
default_shipping_enabled = models.BooleanField(
|
||||
default=False,
|
||||
help_text=('Whether to enable default shipping.'
|
||||
' This essentially means you ship to all countries,'
|
||||
' not only those with configured shipping rates'))
|
||||
|
||||
currency_html_code = models.CharField(
|
||||
max_length=12,
|
||||
default="£",
|
||||
help_text="The HTML code for the currency symbol. Used for display purposes only"
|
||||
)
|
||||
currency = models.CharField(
|
||||
max_length=6,
|
||||
default="GBP",
|
||||
help_text="The iso currency code to use for payments"
|
||||
)
|
||||
|
||||
panels = (
|
||||
FieldPanel('default_shipping_rate'),
|
||||
FieldPanel('default_shipping_carrier'),
|
||||
FieldPanel('default_shipping_enabled'),
|
||||
FieldPanel('currency_html_code'),
|
||||
FieldPanel('currency')
|
||||
)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 16:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shipping', '__first__'),
|
||||
('orders', '0002_order_shipping_rate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='billing_address',
|
||||
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='orders_billing_address', to='shipping.Address'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='shipping_address',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders_shipping_address', to='shipping.Address'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Address',
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 16:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0003_auto_20170211_1632'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='order',
|
||||
name='shipping_rate',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,26 +1,6 @@
|
|||
from django.db import models
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
|
||||
from longclaw.settings import PRODUCT_VARIANT_MODEL, DEFAULT_SHIPPING_RATE
|
||||
|
||||
class Address(models.Model):
|
||||
name = models.CharField(max_length=64)
|
||||
line_1 = models.CharField(max_length=128)
|
||||
line_2 = models.CharField(max_length=128, blank=True)
|
||||
city = models.CharField(max_length=64)
|
||||
postcode = models.CharField(max_length=10)
|
||||
country = models.CharField(max_length=32)
|
||||
|
||||
panels = [
|
||||
FieldPanel('name'),
|
||||
FieldPanel('line_1'),
|
||||
FieldPanel('line_2'),
|
||||
FieldPanel('city'),
|
||||
FieldPanel('postcode'),
|
||||
FieldPanel('country')
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return "{}, {}, {}".format(self.name, self.city, self.country)
|
||||
from longclaw.settings import PRODUCT_VARIANT_MODEL
|
||||
from longclaw.shipping.models import Address
|
||||
|
||||
class Order(models.Model):
|
||||
SUBMITTED = 1
|
||||
|
|
@ -45,7 +25,8 @@ class Order(models.Model):
|
|||
|
||||
shipping_rate = models.DecimalField(max_digits=12,
|
||||
decimal_places=2,
|
||||
default=DEFAULT_SHIPPING_RATE)
|
||||
blank=True,
|
||||
null=True)
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
from django.apps import apps
|
||||
from rest_framework import serializers
|
||||
from longclaw.orders.models import Order, OrderItem, Address
|
||||
from longclaw.orders.models import Order, OrderItem
|
||||
from longclaw.products.serializers import ProductVariantSerializer
|
||||
|
||||
class AddressSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Address
|
||||
fields = "__all__"
|
||||
from longclaw.shipping.serializers import AddressSerializer
|
||||
|
||||
class OrderItemSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-07 20:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import django_extensions.db.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='productimage',
|
||||
old_name='page',
|
||||
new_name='product',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='productvariant',
|
||||
old_name='page',
|
||||
new_name='product',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productvariant',
|
||||
name='slug',
|
||||
field=django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from=('product', 'ref'), separator=''),
|
||||
),
|
||||
]
|
||||
|
|
@ -12,12 +12,13 @@ from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
|||
from wagtail.wagtailsearch import index
|
||||
|
||||
class ProductIndex(Page):
|
||||
pass
|
||||
subpage_types = ('products.Product',)
|
||||
|
||||
class ProductTag(TaggedItemBase):
|
||||
content_object = ParentalKey('Product', related_name='tagged_items')
|
||||
|
||||
class Product(Page):
|
||||
parent_page_types = ['products.ProductIndex']
|
||||
description = RichTextField()
|
||||
tags = ClusterTaggableManager(through=ProductTag, blank=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,6 @@ Default settings for longclaw apps
|
|||
'''
|
||||
from django.conf import settings
|
||||
|
||||
# The currency to use for payments
|
||||
CURRENCY = getattr(settings, "CURRENCY", "GBP")
|
||||
|
||||
# Default shipping rate to use when no configured ShippingCountry is found
|
||||
# for a given address
|
||||
DEFAULT_SHIPPING_RATE = getattr(settings, 'DEFAULT_SHIPPING_RATE', 3.95)
|
||||
|
||||
# Default carrier to use for the default rate
|
||||
DEFAULT_SHIPPING_CARRIER = getattr(
|
||||
settings, 'DEFAULT_SHIPPING_CARRIER', 'Royal Mail')
|
||||
|
||||
# Whether to fall back to using the default shipping rate if no ShippingCountry is found
|
||||
# This means shipping worldwide
|
||||
DEFAULT_SHIPPING_ENABLED = getattr(settings, 'DEFAULT_SHIPPING_ENABLED', True)
|
||||
|
||||
# The payment gateway backend to use
|
||||
# Can be 'longclaw.checkout.gateways.BraintreePayment',
|
||||
# 'longclaw.checkout.gateways.PaypalVZeroPayment',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework.response import Response
|
||||
from longclaw.shipping import serializers, models
|
||||
from longclaw.longclawsettings.models import LongclawSettings
|
||||
|
||||
class InvalidShippingRate(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidShippingCountry(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_shipping_cost(country_code, option, settings):
|
||||
try:
|
||||
obj = models.ShippingCountry.objects.get(country_code=country_code)
|
||||
try:
|
||||
shipping_rate = obj.shipping_rates.get(name=option)
|
||||
return {
|
||||
"rate": shipping_rate.rate,
|
||||
"description": shipping_rate.description,
|
||||
"carrier": shipping_rate.carrier
|
||||
}
|
||||
except models.ShippingRate.DoesNotExist:
|
||||
raise InvalidShippingRate
|
||||
|
||||
except models.ShippingCountry.DoesNotExist:
|
||||
if settings.default_shipping_enabled:
|
||||
return {"rate": settings.default_shipping_rate,
|
||||
"description": "Standard shipping to rest of world",
|
||||
"carrier": settings.default_shipping_rate}
|
||||
else:
|
||||
raise InvalidShippingCountry
|
||||
|
||||
|
||||
@api_view(['GET'])
|
||||
@permission_classes({permissions.AllowAny})
|
||||
def shipping_cost(request):
|
||||
''' Returns the shipping cost for a given country
|
||||
If the shipping cost for the given country has not been set, it will
|
||||
fallback to the default shipping cost if it has been enabled in the app
|
||||
settings
|
||||
'''
|
||||
try:
|
||||
code = request.query_params.get('country_code')
|
||||
except AttributeError:
|
||||
return Response(data={"message": "No country code supplied"},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
option = request.query_params.get('shipping_rate_name', 'standard')
|
||||
try:
|
||||
settings = LongclawSettings.for_site(request.site)
|
||||
data = get_shipping_cost(code, option, settings)
|
||||
response = Response(data=data, status=status.HTTP_200_OK)
|
||||
except InvalidShippingRate:
|
||||
response = Response(data={"message": "Shipping option {} is invalid".format(option)},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
except InvalidShippingCountry:
|
||||
response = Response(data={"message": "Shipping to {} is not available".format(code)},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
@permission_classes([permissions.AllowAny])
|
||||
def shipping_countries(request):
|
||||
''' Get all shipping countries
|
||||
'''
|
||||
queryset = models.ShippingCountry.objects.all()
|
||||
serializer = serializers.ShippingCountrySerializer(queryset, many=True)
|
||||
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ShippingConfig(AppConfig):
|
||||
name = 'shipping'
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 16:39
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Address',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('line_1', models.CharField(max_length=128)),
|
||||
('line_2', models.CharField(blank=True, max_length=128)),
|
||||
('city', models.CharField(max_length=64)),
|
||||
('postcode', models.CharField(max_length=10)),
|
||||
('country', models.CharField(max_length=32)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ShippingCountry',
|
||||
fields=[
|
||||
('country_code', models.CharField(max_length=3, primary_key=True, serialize=False)),
|
||||
('country_name', models.CharField(max_length=32)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ShippingRate',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=32)),
|
||||
('rate', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||
('carrier', models.CharField(max_length=64)),
|
||||
('description', models.CharField(max_length=128)),
|
||||
('shipping_country', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shipping_rates', to='shipping.ShippingCountry')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 17:06
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shipping', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='shippingcountry',
|
||||
options={'verbose_name_plural': 'shipping countries'},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 21:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0030_index_on_pagerevision_created_at'),
|
||||
('shipping', '0002_auto_20170211_1706'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='shippingcountry',
|
||||
name='page_ptr',
|
||||
field=models.OneToOneField(auto_created=True, default=1, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='wagtailcore.Page'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='shippingrate',
|
||||
name='shipping_country',
|
||||
field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='shipping_rates', to='shipping.ShippingCountry'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 21:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shipping', '0003_auto_20170211_2129'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='shippingcountry',
|
||||
name='page_ptr',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='shippingrate',
|
||||
name='shipping_country',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shipping_rates', to='shipping.ShippingCountry'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 21:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0030_index_on_pagerevision_created_at'),
|
||||
('shipping', '0004_auto_20170211_2132'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='shippingcountry',
|
||||
name='page_ptr',
|
||||
field=models.OneToOneField(auto_created=True, default=1, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='wagtailcore.Page'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='shippingrate',
|
||||
name='shipping_country',
|
||||
field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='shipping_rates', to='shipping.ShippingCountry'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-11 21:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_countries.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shipping', '0005_auto_20170211_2138'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='shippingcountry',
|
||||
name='country_code',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='shippingcountry',
|
||||
name='country_name',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='shippingcountry',
|
||||
name='country',
|
||||
field=django_countries.fields.CountryField(default='UK', max_length=2),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='shippingcountry',
|
||||
name='page_ptr',
|
||||
field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 11:57
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0030_index_on_pagerevision_created_at'),
|
||||
('shipping', '0006_auto_20170211_2149'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ShippingSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('default_shipping_rate', models.DecimalField(decimal_places=2, help_text='The default shipping rate for countries which have not been configured', max_digits=12)),
|
||||
('default_shipping_carrier', models.CharField(help_text='The default shipping carrier', max_length=32)),
|
||||
('default_shipping_enabled', models.BooleanField(default=False, help_text='Whether to enable default shipping. This essentially means you ship to all countries, not only those with configured shipping rates')),
|
||||
('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-02-12 14:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shipping', '0007_shippingsettings'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='shippingsettings',
|
||||
name='site',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ShippingSettings',
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
from django.db import models
|
||||
from modelcluster.fields import ParentalKey
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
|
||||
from django_countries.fields import CountryField
|
||||
|
||||
|
||||
class Address(models.Model):
|
||||
name = models.CharField(max_length=64)
|
||||
line_1 = models.CharField(max_length=128)
|
||||
line_2 = models.CharField(max_length=128, blank=True)
|
||||
city = models.CharField(max_length=64)
|
||||
postcode = models.CharField(max_length=10)
|
||||
country = models.CharField(max_length=32)
|
||||
|
||||
panels = [
|
||||
FieldPanel('name'),
|
||||
FieldPanel('line_1'),
|
||||
FieldPanel('line_2'),
|
||||
FieldPanel('city'),
|
||||
FieldPanel('postcode'),
|
||||
FieldPanel('country')
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return "{}, {}, {}".format(self.name, self.city, self.country)
|
||||
|
||||
|
||||
class ShippingCountry(Page):
|
||||
''' Standard and premimum rate shipping for
|
||||
individual countries.
|
||||
'''
|
||||
parent_page_types = ['wagtailcore.Page']
|
||||
country = CountryField()
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
FieldPanel('country'),
|
||||
InlinePanel('shipping_rates', label='Shipping rates')
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "shipping countries"
|
||||
|
||||
|
||||
class ShippingRate(models.Model):
|
||||
|
||||
name = models.CharField(max_length=32)
|
||||
rate = models.DecimalField(max_digits=12, decimal_places=2)
|
||||
carrier = models.CharField(max_length=64)
|
||||
description = models.CharField(max_length=128)
|
||||
shipping_country = ParentalKey(
|
||||
ShippingCountry, related_name="shipping_rates")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from rest_framework import serializers
|
||||
from longclaw.shipping.models import Address, ShippingCountry, ShippingRate
|
||||
|
||||
class AddressSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Address
|
||||
fields = "__all__"
|
||||
|
||||
class ShippingRateSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = ShippingRate
|
||||
fields = "__all__"
|
||||
|
||||
class ShippingCountrySerializer(serializers.ModelSerializer):
|
||||
|
||||
shipping_rates = ShippingRateSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = ShippingCountry
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from django.conf.urls import url
|
||||
from longclaw.shipping import api
|
||||
|
||||
urlpatterns = [
|
||||
url(r'shipping/cost/$',
|
||||
api.shipping_cost,
|
||||
name='shipping_cost'),
|
||||
url(r'shipping/countries/$',
|
||||
api.shipping_countries,
|
||||
name="shipping_countries")
|
||||
]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from wagtail.contrib.modeladmin.options import (
|
||||
ModelAdmin, modeladmin_register
|
||||
)
|
||||
from longclaw.shipping.models import ShippingCountry
|
||||
|
||||
|
||||
class ShippingCountryModelAdmin(ModelAdmin):
|
||||
model = ShippingCountry
|
||||
menu_order = 200
|
||||
menu_icon = 'site'
|
||||
add_to_settings_menu = False
|
||||
exclude_from_explorer = True
|
||||
list_display = ('country', 'country_code', 'shipping_rates')
|
||||
|
||||
def flag(self, obj):
|
||||
return obj.country.flag
|
||||
|
||||
def country_code(self, obj):
|
||||
return obj.country.alpha3
|
||||
|
||||
def shipping_rates(self, obj):
|
||||
return ", ".join(str(rate) for rate in obj.shipping_rates.all())
|
||||
|
||||
modeladmin_register(ShippingCountryModelAdmin)
|
||||
|
|
@ -4,3 +4,4 @@ django-extensions
|
|||
djangorestframework>=3.5.3
|
||||
wagtail==1.7
|
||||
simplejson==3.10.0
|
||||
django-countries==4.0
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue