Merge branch 'release/3.4.0'

This commit is contained in:
Nathan Chapman 2023-07-16 12:43:38 -06:00
commit adf275331c
34 changed files with 1517 additions and 1470 deletions

View File

@ -2,7 +2,7 @@ from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from .models import Address, User
from .models import User
from .forms import AccountCreateForm, AccountUpdateForm
class UserAdmin(UserAdmin):
@ -12,5 +12,4 @@ class UserAdmin(UserAdmin):
list_display = ['email', 'username',]
admin.site.register(Address)
admin.site.register(User, UserAdmin)

View File

@ -9,3 +9,4 @@ class AccountsConfig(AppConfig):
from .signals import (
user_saved
)

View File

@ -2,21 +2,7 @@ from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from allauth.account.forms import SignupForm
from captcha.fields import CaptchaField
from .models import Address, User
class AddressForm(forms.ModelForm):
class Meta:
model = Address
fields = (
'first_name',
'last_name',
'street_address_1',
'street_address_2',
'city',
'state',
'postal_code',
)
from .models import User
class AccountCreateForm(UserCreationForm):
@ -32,24 +18,41 @@ class AccountUpdateForm(UserChangeForm):
'first_name',
'last_name',
'email',
'default_shipping_address',
'addresses',
'shipping_street_address_1',
'shipping_street_address_2',
'shipping_city',
'shipping_state',
'shipping_postal_code',
)
class CustomerUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['default_shipping_address'].queryset = kwargs['instance'].addresses
class Meta:
model = User
fields = (
'email',
)
class CustomerShippingAddressUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = (
'first_name',
'last_name',
'email',
'default_shipping_address',
'shipping_street_address_1',
'shipping_street_address_2',
'shipping_city',
'shipping_state',
'shipping_postal_code',
)
labels = {
'shipping_street_address_1': 'Street line 1',
'shipping_street_address_2': 'Street line 2',
'shipping_city': 'City',
'shipping_state': 'State',
'shipping_postal_code': 'ZIP code',
}
class UserSignupForm(SignupForm):
@ -62,3 +65,4 @@ class UserSignupForm(SignupForm):
widget=forms.TextInput(attrs={'placeholder': 'Last name'})
)
captcha = CaptchaField()

View File

@ -0,0 +1,38 @@
# Generated by Django 4.1.6 on 2023-07-15 02:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0002_address_accounts_address_all_key'),
]
operations = [
migrations.AddField(
model_name='user',
name='shipping_city',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='user',
name='shipping_postal_code',
field=models.CharField(blank=True, max_length=20),
),
migrations.AddField(
model_name='user',
name='shipping_state',
field=models.CharField(blank=True, choices=[('AL', 'Alabama'), ('AK', 'Alaska'), ('AS', 'American Samoa'), ('AZ', 'Arizona'), ('AR', 'Arkansas'), ('AA', 'Armed Forces Americas'), ('AE', 'Armed Forces Europe'), ('AP', 'Armed Forces Pacific'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DE', 'Delaware'), ('DC', 'District of Columbia'), ('FM', 'Federated States of Micronesia'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('IA', 'Iowa'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('ME', 'Maine'), ('MH', 'Marshall Islands'), ('MD', 'Maryland'), ('MA', 'Massachusetts'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MS', 'Mississippi'), ('MO', 'Missouri'), ('MT', 'Montana'), ('NE', 'Nebraska'), ('NV', 'Nevada'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NY', 'New York'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('MP', 'Northern Mariana Islands'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PW', 'Palau'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VT', 'Vermont'), ('VI', 'Virgin Islands'), ('VA', 'Virginia'), ('WA', 'Washington'), ('WV', 'West Virginia'), ('WI', 'Wisconsin'), ('WY', 'Wyoming')], max_length=2),
),
migrations.AddField(
model_name='user',
name='shipping_street_address_1',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='user',
name='shipping_street_address_2',
field=models.CharField(blank=True, max_length=256),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 4.1.6 on 2023-07-15 02:56
from django.db import migrations
def copy_default_address_to_user(apps, schema_editor):
User = apps.get_model("accounts", "User")
for user in User.objects.all():
if user.default_shipping_address:
user.shipping_street_address_1 = user.default_shipping_address.street_address_1
user.shipping_street_address_2 = user.default_shipping_address.street_address_2
user.shipping_city = user.default_shipping_address.city
user.shipping_state = user.default_shipping_address.state
user.shipping_postal_code = user.default_shipping_address.postal_code
user.save()
class Migration(migrations.Migration):
dependencies = [
('accounts', '0003_user_shipping_city_user_shipping_postal_code_and_more'),
]
operations = [
migrations.RunPython(copy_default_address_to_user)
]

View File

@ -0,0 +1,29 @@
# Generated by Django 4.1.6 on 2023-07-15 03:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0008_remove_order_billing_address_and_more'),
('accounts', '0004_transfer_address_data_to_user_model'),
]
operations = [
migrations.RemoveField(
model_name='user',
name='addresses',
),
migrations.RemoveField(
model_name='user',
name='default_billing_address',
),
migrations.RemoveField(
model_name='user',
name='default_shipping_address',
),
migrations.DeleteModel(
name='Address',
),
]

View File

@ -5,76 +5,29 @@ from django.contrib.auth.models import AbstractUser
from localflavor.us.us_states import USPS_CHOICES
class Address(models.Model):
first_name = models.CharField(max_length=256, blank=True)
last_name = models.CharField(max_length=256, blank=True)
street_address_1 = models.CharField(max_length=256, blank=True)
street_address_2 = models.CharField(max_length=256, blank=True)
city = models.CharField(max_length=256, blank=True)
state = models.CharField(
class User(AbstractUser):
stripe_id = models.CharField(max_length=255, blank=True)
# Shipping address
shipping_street_address_1 = models.CharField(max_length=256, blank=True)
shipping_street_address_2 = models.CharField(max_length=256, blank=True)
shipping_city = models.CharField(max_length=256, blank=True)
shipping_state = models.CharField(
max_length=2,
choices=USPS_CHOICES,
blank=True
)
postal_code = models.CharField(max_length=20, blank=True)
shipping_postal_code = models.CharField(max_length=20, blank=True)
def as_stripe_dict(self):
return {
'name': f'{self.first_name} {self.last_name}',
'address': {
'line1': self.street_address_1,
'line2': self.street_address_2,
'city': self.city,
'state': self.state,
'postal_code': self.postal_code
}
}
def __str__(self):
return f"""
{self.first_name} {self.last_name}
{self.street_address_1}
{self.street_address_2}
{self.city}, {self.state}, {self.postal_code}
"""
def __iter__(self):
yield ('address_line_1', self.street_address_1),
yield ('address_line_2', self.street_address_2),
yield ('admin_area_2', self.city),
yield ('admin_area_1', self.state),
yield ('postal_code', self.postal_code),
yield ('country_code', 'US')
class Meta:
constraints = [
models.UniqueConstraint(
name='accounts_address_all_key',
fields=[
'first_name',
'last_name',
'street_address_1',
'street_address_2',
'city',
'state',
'postal_code'
],
violation_error_message='Duplicate: Address already exists.'
)
]
class User(AbstractUser):
addresses = models.ManyToManyField(
Address, blank=True, related_name="user_addresses"
)
default_shipping_address = models.ForeignKey(
Address, related_name="+", null=True, blank=True, on_delete=models.SET_NULL
)
default_billing_address = models.ForeignKey(
Address, related_name="+", null=True, blank=True, on_delete=models.SET_NULL
)
stripe_id = models.CharField(max_length=255, blank=True)
@property
def has_shipping_address(self):
if (self.shipping_street_address_1 != ''
and self.shipping_street_address_2 != ''
and self.shipping_city != ''
and self.shipping_state != ''
and self.shipping_postal_code != ''):
return True
return False
def get_or_create_stripe_id(self):
if not self.stripe_id:

View File

@ -5,7 +5,7 @@ from django.dispatch import receiver
from django.db import models
from django.conf import settings
from .models import Address, User
from .models import User
logger = logging.getLogger(__name__)
stripe.api_key = settings.STRIPE_API_KEY
@ -16,3 +16,4 @@ def user_saved(sender, instance, created, **kwargs):
logger.info('User was saved')
if created or not instance.stripe_id:
instance.get_or_create_stripe_id()

View File

@ -1,26 +1,9 @@
from allauth.account.models import EmailAddress
from .models import Address, User
from .tasks import send_account_created_email
from .models import User
def get_or_create_customer(request, shipping_address):
address, a_created = Address.objects.get_or_create(
first_name=shipping_address['first_name'],
last_name=shipping_address['last_name'],
street_address_1=shipping_address['street_address_1'],
street_address_2=shipping_address['street_address_2'],
city=shipping_address['city'],
state=shipping_address['state'],
postal_code=shipping_address['postal_code']
)
if request.user.is_authenticated:
user = request.user
user.addresses.add(address)
if not user.default_shipping_address:
user.default_shipping_address = address
user.save()
else:
user, u_created = User.objects.get_or_create(
email=shipping_address['email'].lower(),
@ -29,27 +12,17 @@ def get_or_create_customer(request, shipping_address):
'is_staff': False,
'is_active': True,
'is_superuser': False,
'first_name': address.first_name,
'last_name': address.last_name,
'default_shipping_address': address,
'first_name': shipping_address['first_name'],
'last_name': shipping_address['last_name'],
'shipping_street_address_1': shipping_address['street_address_1'],
'shipping_street_address_2': shipping_address['street_address_2'],
'shipping_city': shipping_address['city'],
'shipping_state': shipping_address['state'],
'shipping_postal_code': shipping_address['postal_code']
}
)
if u_created:
password = User.objects.make_random_password()
user.set_password(password)
user.addresses.add(address)
user.make_random_password()
user.save()
EmailAddress.objects.create(
user=user, email=user.email, primary=True, verified=False
)
u = {
'full_name': user.get_full_name(),
'email': user.email,
'password': password
}
send_account_created_email.delay(u)
return user, address
return user

View File

@ -0,0 +1,63 @@
# Generated by Django 4.1.6 on 2023-07-15 02:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_rename_max_order_per_customer_productvariant_order_limit'),
]
operations = [
migrations.AddField(
model_name='order',
name='shipping_city',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='order',
name='shipping_postal_code',
field=models.CharField(blank=True, max_length=20),
),
migrations.AddField(
model_name='order',
name='shipping_state',
field=models.CharField(blank=True, choices=[('AL', 'Alabama'), ('AK', 'Alaska'), ('AS', 'American Samoa'), ('AZ', 'Arizona'), ('AR', 'Arkansas'), ('AA', 'Armed Forces Americas'), ('AE', 'Armed Forces Europe'), ('AP', 'Armed Forces Pacific'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DE', 'Delaware'), ('DC', 'District of Columbia'), ('FM', 'Federated States of Micronesia'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('IA', 'Iowa'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('ME', 'Maine'), ('MH', 'Marshall Islands'), ('MD', 'Maryland'), ('MA', 'Massachusetts'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MS', 'Mississippi'), ('MO', 'Missouri'), ('MT', 'Montana'), ('NE', 'Nebraska'), ('NV', 'Nevada'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NY', 'New York'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('MP', 'Northern Mariana Islands'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PW', 'Palau'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VT', 'Vermont'), ('VI', 'Virgin Islands'), ('VA', 'Virginia'), ('WA', 'Washington'), ('WV', 'West Virginia'), ('WI', 'Wisconsin'), ('WY', 'Wyoming')], max_length=2),
),
migrations.AddField(
model_name='order',
name='shipping_street_address_1',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='order',
name='shipping_street_address_2',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='subscription',
name='shipping_city',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='subscription',
name='shipping_postal_code',
field=models.CharField(blank=True, max_length=20),
),
migrations.AddField(
model_name='subscription',
name='shipping_state',
field=models.CharField(blank=True, choices=[('AL', 'Alabama'), ('AK', 'Alaska'), ('AS', 'American Samoa'), ('AZ', 'Arizona'), ('AR', 'Arkansas'), ('AA', 'Armed Forces Americas'), ('AE', 'Armed Forces Europe'), ('AP', 'Armed Forces Pacific'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DE', 'Delaware'), ('DC', 'District of Columbia'), ('FM', 'Federated States of Micronesia'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('IA', 'Iowa'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('ME', 'Maine'), ('MH', 'Marshall Islands'), ('MD', 'Maryland'), ('MA', 'Massachusetts'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MS', 'Mississippi'), ('MO', 'Missouri'), ('MT', 'Montana'), ('NE', 'Nebraska'), ('NV', 'Nevada'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NY', 'New York'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('MP', 'Northern Mariana Islands'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PW', 'Palau'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VT', 'Vermont'), ('VI', 'Virgin Islands'), ('VA', 'Virginia'), ('WA', 'Washington'), ('WV', 'West Virginia'), ('WI', 'Wisconsin'), ('WY', 'Wyoming')], max_length=2),
),
migrations.AddField(
model_name='subscription',
name='shipping_street_address_1',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='subscription',
name='shipping_street_address_2',
field=models.CharField(blank=True, max_length=256),
),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 4.1.6 on 2023-07-15 02:59
from django.db import migrations
def copy_address_to_order(apps, schema_editor):
Order = apps.get_model("core", "Order")
for order in Order.objects.all():
if order.shipping_address:
order.shipping_street_address_1 = order.shipping_address.street_address_1
order.shipping_street_address_2 = order.shipping_address.street_address_2
order.shipping_city = order.shipping_address.city
order.shipping_state = order.shipping_address.state
order.shipping_postal_code = order.shipping_address.postal_code
order.save()
def copy_address_to_subscription(apps, schema_editor):
Subscription = apps.get_model("core", "Subscription")
for subscription in Subscription.objects.all():
if subscription.shipping_address:
subscription.shipping_street_address_1 = subscription.shipping_address.street_address_1
subscription.shipping_street_address_2 = subscription.shipping_address.street_address_2
subscription.shipping_city = subscription.shipping_address.city
subscription.shipping_state = subscription.shipping_address.state
subscription.shipping_postal_code = subscription.shipping_address.postal_code
subscription.save()
class Migration(migrations.Migration):
dependencies = [
('core', '0004_order_shipping_city_order_shipping_postal_code_and_more'),
]
operations = [
migrations.RunPython(copy_address_to_order),
migrations.RunPython(copy_address_to_subscription)
]

View File

@ -0,0 +1,33 @@
# Generated by Django 4.1.6 on 2023-07-15 03:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_transfer_address_data_to_order_and_subscription_models'),
]
operations = [
migrations.AddField(
model_name='order',
name='shipping_first_name',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='order',
name='shipping_last_name',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='subscription',
name='shipping_first_name',
field=models.CharField(blank=True, max_length=256),
),
migrations.AddField(
model_name='subscription',
name='shipping_last_name',
field=models.CharField(blank=True, max_length=256),
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 4.1.6 on 2023-07-15 03:20
from django.db import migrations
def copy_address_to_order(apps, schema_editor):
Order = apps.get_model("core", "Order")
for order in Order.objects.all():
if order.shipping_address:
order.shipping_first_name = order.shipping_address.first_name
order.shipping_last_name = order.shipping_address.last_name
order.save()
def copy_address_to_subscription(apps, schema_editor):
Subscription = apps.get_model("core", "Subscription")
for subscription in Subscription.objects.all():
if subscription.shipping_address:
subscription.shipping_first_name = subscription.shipping_address.first_name
subscription.shipping_last_name = subscription.shipping_address.last_name
subscription.save()
class Migration(migrations.Migration):
dependencies = [
('core', '0006_order_shipping_first_name_order_shipping_last_name_and_more'),
]
operations = [
migrations.RunPython(copy_address_to_order),
migrations.RunPython(copy_address_to_subscription)
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.1.6 on 2023-07-15 03:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0007_transfer_address_first_and_last_names_to_order_and_subscriptions'),
]
operations = [
migrations.RemoveField(
model_name='order',
name='billing_address',
),
migrations.RemoveField(
model_name='order',
name='shipping_address',
),
migrations.RemoveField(
model_name='subscription',
name='shipping_address',
),
]

View File

@ -16,8 +16,9 @@ from django.contrib.postgres.fields import ArrayField, HStoreField
from django.forms.models import model_to_dict
from django_measurement.models import MeasurementField
from localflavor.us.us_states import USPS_CHOICES
from accounts.models import User, Address
from accounts.models import User
from . import (
DiscountValueType,
@ -337,20 +338,18 @@ class Order(models.Model):
default=OrderStatus.UNFULFILLED,
choices=OrderStatus.CHOICES
)
billing_address = models.ForeignKey(
Address,
related_name="+",
editable=False,
null=True,
on_delete=models.SET_NULL
)
shipping_address = models.ForeignKey(
Address,
related_name="+",
editable=False,
null=True,
on_delete=models.SET_NULL
# Shipping address
shipping_first_name = models.CharField(max_length=256, blank=True)
shipping_last_name = models.CharField(max_length=256, blank=True)
shipping_street_address_1 = models.CharField(max_length=256, blank=True)
shipping_street_address_2 = models.CharField(max_length=256, blank=True)
shipping_city = models.CharField(max_length=256, blank=True)
shipping_state = models.CharField(
max_length=2,
choices=USPS_CHOICES,
blank=True
)
shipping_postal_code = models.CharField(max_length=20, blank=True)
coupon = models.ForeignKey(
Coupon,
related_name='orders',
@ -544,13 +543,18 @@ class Subscription(models.Model):
on_delete=models.SET_NULL,
null=True
)
shipping_address = models.ForeignKey(
Address,
related_name='+',
editable=False,
null=True,
on_delete=models.SET_NULL
# Shipping address
shipping_first_name = models.CharField(max_length=256, blank=True)
shipping_last_name = models.CharField(max_length=256, blank=True)
shipping_street_address_1 = models.CharField(max_length=256, blank=True)
shipping_street_address_2 = models.CharField(max_length=256, blank=True)
shipping_city = models.CharField(max_length=256, blank=True)
shipping_state = models.CharField(
max_length=2,
choices=USPS_CHOICES,
blank=True
)
shipping_postal_code = models.CharField(max_length=20, blank=True)
items = ArrayField(
models.JSONField(blank=True, null=True),
default=list

View File

@ -53,13 +53,13 @@ def transaction_created(sender, instance, created, **kwargs):
'shipping_total': str(instance.order.shipping_total),
'total_amount': str(instance.order.total_amount),
'shipping_address': {
'first_name': instance.order.shipping_address.first_name,
'last_name': instance.order.shipping_address.last_name,
'street_address_1': instance.order.shipping_address.street_address_1,
'street_address_2': instance.order.shipping_address.street_address_2,
'city': instance.order.shipping_address.city,
'state': instance.order.shipping_address.state,
'postal_code': instance.order.shipping_address.postal_code
'first_name': instance.order.shipping_first_name,
'last_name': instance.order.shipping_last_name,
'street_address_1': instance.order.shipping_street_address_1,
'street_address_2': instance.order.shipping_street_address_2,
'city': instance.order.shipping_city,
'state': instance.order.shipping_state,
'postal_code': instance.order.shipping_postal_code
},
'line_items': list(
format_order_lines(instance.order.lines.all())

View File

@ -19,21 +19,14 @@
<h4>Details</h4>
</header>
<dl class="panel-datalist">
<dt>Primary email address</dt>
<dt>Email address</dt>
<dd>
<a href="mailto:{{ customer.email }}">{{ customer.email }} &nearr;</a>
</dd>
<dt>Default shipping address</dt>
<dt>Shipping address</dt>
<dd>
{% include 'dashboard/partials/_address.html' with address=customer.default_shipping_address %}
</dd>
<dt>All addresses</dt>
<dd>
{% for address in customer.addresses.all %}
{% include 'dashboard/partials/_address.html' with address=address %}
{% endfor %}
{% include 'dashboard/partials/_address.html' with full_name=customer.get_full_name address=customer %}
</dd>
</dl>
</section>

View File

@ -133,7 +133,7 @@
<div class="panel-section panel-shipping">
<div>
<strong>Shipping address</strong>
{% include 'dashboard/partials/_address.html' with address=order.shipping_address %}
{% include 'dashboard/partials/_address.html' with full_name=order.customer.get_full_name address=order %}
</div>
<table>
<thead>

View File

@ -17,7 +17,7 @@
</header>
<div class="panel-section">
<strong>Shipping address</strong>
{% include 'dashboard/partials/_address.html' with address=order.shipping_address %}
{% include 'dashboard/partials/_address.html' with full_name=order.customer.get_full_name address=order %}
</div>
<form method="POST">
{% csrf_token %}

View File

@ -1,9 +1,8 @@
<address>
{{address.first_name}}
{{address.last_name}}<br>
{{address.street_address_1}}<br>
{% if address.street_address_2 %}
{{address.street_address_2}}<br>
{{ full_name }}<br>
{{ address.shipping_street_address_1 }}<br>
{% if address.shipping_street_address_2 %}
{{ address.shipping_street_address_2 }}<br>
{% endif %}
{{address.city}}, {{address.state}}, {{address.postal_code}}
{{ address.shipping_city }}, {{ address.shipping_state }}, {{ address.shipping_postal_code }}
</address>

View File

@ -31,7 +31,6 @@ from django.db.models.functions import Coalesce
from accounts.models import User
from accounts.utils import get_or_create_customer
from accounts.forms import AddressForm
from core.models import (
ProductCategory,
Product,
@ -259,8 +258,6 @@ class OrderDetailView(LoginRequiredMixin, DetailView):
self.kwargs.get(self.pk_url_kwarg)
).select_related(
'customer',
'billing_address',
'shipping_address'
).prefetch_related(
'lines__variant__product__productphoto_set'
)
@ -717,9 +714,11 @@ class CustomerUpdateView(
'first_name',
'last_name',
'email',
'is_staff',
'addresses',
'default_shipping_address'
'shipping_street_address_1',
'shipping_street_address_2',
'shipping_city',
'shipping_state',
'shipping_postal_code',
)
def get_success_url(self):

View File

@ -25,7 +25,7 @@ services:
- redis
web:
build: .
command: sh -c "python manage.py migrate && python manage.py collectstatic --no-input && gunicorn --bind :8000 --reload ptcoffee.wsgi:application"
command: gunicorn --bind :8000 --reload ptcoffee.wsgi:application
volumes:
- .:/app
- ./static/:/var/www/static

View File

@ -19,6 +19,10 @@ server {
listen [::]:443 ssl http2;
server_name ptcoffee.com www.ptcoffee.com;
if ($http_host !~* ^(ptcoffee.com|www.ptcoffee.com)$ ) {
return 444;
}
# SSL
ssl_certificate /etc/letsencrypt/live/ptcoffee.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ptcoffee.com/privkey.pem;

2109
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -72,10 +72,7 @@ INSTALLED_APPS = [
# 3rd Party
'django_filters',
'storages',
'localflavor',
'django_celery_beat',
'django_celery_results',
'anymail',
'compressor',
'allauth',

View File

@ -11,12 +11,9 @@ django = "^4.1.5"
celery = {extras = ["redis"], version = "^5.2.7"}
django-allauth = "^0.52.0"
django-anymail = {extras = ["mailgun"], version = "^9.0"}
django-celery-beat = "^2.4.0"
django-celery-results = "^2.4.0"
django-compressor = "^4.1"
django-filter = "^22.1"
django-measurement = "^3.2.4"
django-storages = "^1.13.2"
django-templated-email = "^3.0.1"
paypal-checkout-serversdk = "^1.0.1"
pillow = "^9.4.0"

View File

@ -126,6 +126,21 @@ table a {
white-space: normal;
}
.form-table {
width: unset;
border: none;
}
.form-table th,
.form-table td {
border: none;
}
.form-table th {
padding: 0 1rem 1rem 0;
}
.form-table td {
padding: 0 0 1rem 0;
}
/* ==========================================================================
Forms

View File

@ -255,7 +255,7 @@ class Cart:
if container is None:
container = self.get_shipping_container()
if not self.total_weight > Weight(lb=0):
if self.total_weight <= Weight(lb=0):
return Decimal('0.00')
if len(self) > 0 and self.session.get('shipping_address'):
@ -286,11 +286,11 @@ class Cart:
)
if usps_rate_request['service'] == ShippingContainer.PRIORITY:
shipping_cost = Decimal(postage['Rate'])
shipping_price = Decimal(postage['Rate'])
elif usps_rate_request['service'] == ShippingContainer.PRIORITY_COMMERCIAL:
shipping_cost = Decimal(postage['CommercialRate'])
shipping_price = Decimal(postage['CommercialRate'])
return shipping_cost
return shipping_price
else:
raise ShippingAddressError(
'Could not retrieve shipping address.'

View File

@ -5,61 +5,32 @@
{% block content %}
<article>
<header class="article__header--with-action">
<h1>{{customer.get_full_name}}</h1>
<a href="{% url 'storefront:customer-update' customer.pk %}" class="btn">Edit profile</a>
</header>
<section>
<h4>Info</h4>
<h2>My Account</h2>
<p>
<a href="{% url 'account_change_password' %}">Change password</a>
</p>
<div class="customer__detail-section">
<div>
<strong>Email address</strong><br>
{{customer.email}}<br>
<a href="{% url 'account_email' %}">Manage</a>
<strong>Email address</strong> <a href="{% url 'storefront:customer-update' customer.pk %}">Change</a><br>
{{customer.email}}
</div>
<div>
<strong>Default shipping address</strong>
{% with shipping_address=customer.default_shipping_address %}
<strong>Shipping address</strong> <a href="{% url 'storefront:customer-shipping-address-update' customer.pk %}">Change</a>
<address>
{{shipping_address.first_name}}
{{shipping_address.last_name}}<br>
{{shipping_address.street_address_1}}<br>
{% if shipping_address.street_address_2 %}
{{shipping_address.street_address_2}}<br>
{{ customer.get_full_name }}<br>
{{customer.shipping_street_address_1}}<br>
{% if shipping_street_address_2 %}
{{customer.shipping_street_address_2}}<br>
{% endif %}
{{shipping_address.city}}, {{shipping_address.state}}, {{shipping_address.postal_code}}
{{customer.shipping_city}}, {{customer.shipping_state}}, {{customer.shipping_postal_code}}
</address>
{% endwith %}
</div>
</div>
</section>
<section>
<h4>Your addresses</h4>
<p>
<a href="{% url 'storefront:customer-address-create' user.pk %}" class="btn">+ New address</a>
</p>
<div>
{% for address in customer.addresses.all %}
<p>
<address>
{{address.first_name}}
{{address.last_name}}<br>
{{address.street_address_1}}<br>
{% if address.street_address_2 %}
{{address.street_address_2}}<br>
{% endif %}
{{address.city}}, {{address.state}}, {{address.postal_code}}
</address>
<a href="{% url 'storefront:address-update' customer.pk address.pk %}">Edit</a>
</p>
{% empty %}
<p>No other addresses.</p>
{% endfor %}
</div>
</section>
{% if customer.subscriptions.count > 0 %}
<section>
<h3>Your subscriptions</h3>
<h3>My subscriptions</h3>
<table>
<thead>
<tr>
@ -81,7 +52,7 @@
</section>
{% endif %}
<section>
<h3>Your orders</h3>
<h3>My orders</h3>
<table>
<thead>
<tr>

View File

@ -5,17 +5,22 @@
<header>
<p><a href="{% url 'storefront:customer-detail' customer.pk %}">&larr; Back</a></p>
<h1>Update your profile</h1>
<p>
<a href="{% url 'account_change_password' %}">Change password</a>
</p>
</header>
<section>
<form method="POST" action="{% url 'storefront:customer-update' customer.pk %}">
{% csrf_token %}
{{form.as_p}}
<p>
<input type="submit" value="Save changes">
</p>
<table class="form-table">
{{form.as_table}}
<tfoot>
<tr>
<td>
</td>
<td>
<button type="submit">Save changes</button>
</td>
</tr>
</tfoot>
</table>
</form>
</section>
</article>

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block content %}
<article>
<header>
<p><a href="{% url 'storefront:customer-detail' customer.pk %}">&larr; Back</a></p>
<h1>Update your shipping address</h1>
</header>
<section>
<form method="POST">
{% csrf_token %}
<table class="form-table">
{{form.as_table}}
<tfoot>
<tr>
<td>
</td>
<td>
<button type="submit">Save changes</button>
</td>
</tr>
</tfoot>
</table>
</form>
</section>
</article>
{% endblock %}

View File

@ -89,26 +89,16 @@ urlpatterns = [
views.CustomerUpdateView.as_view(),
name='customer-update',
),
# path(
# 'delete/',
# views.CustomerDeleteView.as_view(),
# name='customer-delete'
# ),
path(
'shipping-address/update/',
views.CustomerShippingAddressUpdateView.as_view(),
name='customer-shipping-address-update',
),
path(
'orders/<int:order_pk>/',
views.OrderDetailView.as_view(),
name='order-detail',
),
path(
'addresses/new/',
views.CustomerAddressCreateView.as_view(),
name='customer-address-create',
),
path(
'addresses/<int:address_pk>/update/',
views.CustomerAddressUpdateView.as_view(),
name='address-update',
)
])),
path(

View File

@ -34,11 +34,9 @@ from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
from moneyed import Money, USD
from accounts.models import User, Address
from accounts.models import User
from accounts.utils import get_or_create_customer
from accounts.forms import (
AddressForm as AccountAddressForm, CustomerUpdateForm
)
from accounts.forms import CustomerUpdateForm, CustomerShippingAddressUpdateForm
from core.models import (
ProductCategory, Product, ProductVariant, ProductOption,
Order, Transaction, OrderLine, Coupon, ShippingRate,
@ -252,16 +250,15 @@ class CheckoutAddressView(FormView):
def get_initial(self):
user = self.request.user
initial = None
if user.is_authenticated and user.default_shipping_address:
address = user.default_shipping_address
if user.is_authenticated:
initial = {
'full_name': address.first_name + ' ' + address.last_name,
'full_name': user.first_name + ' ' + user.last_name,
'email': user.email,
'street_address_1': address.street_address_1,
'street_address_2': address.street_address_2,
'city': address.city,
'state': address.state,
'postal_code': address.postal_code
'street_address_1': user.shipping_street_address_1,
'street_address_2': user.shipping_street_address_2,
'city': user.shipping_city,
'state': user.shipping_state,
'postal_code': user.shipping_postal_code
}
elif self.request.session.get('shipping_address'):
address = self.request.session.get('shipping_address')
@ -413,7 +410,14 @@ class OrderCreateView(CreateView):
shipping_container = cart.get_shipping_container()
form.instance.shipping_total = cart.get_shipping_price(shipping_container)
shipping_address = self.request.session.get('shipping_address')
form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address)
form.instance.shipping_first_name = shipping_address['first_name']
form.instance.shipping_last_name = shipping_address['last_name']
form.instance.shipping_street_address_1 = shipping_address['street_address_1']
form.instance.shipping_street_address_2 = shipping_address['street_address_2']
form.instance.shipping_city = shipping_address['city']
form.instance.shipping_state = shipping_address['state']
form.instance.shipping_postal_code = shipping_address['postal_code']
form.instance.customer = get_or_create_customer(self.request, shipping_address)
form.instance.status = OrderStatus.DRAFT
self.object = form.save()
bulk_list = cart.build_bulk_list(self.object)
@ -443,7 +447,14 @@ class FreeOrderCreateView(CreateView):
shipping_container = cart.get_shipping_container()
form.instance.shipping_total = cart.get_shipping_price(shipping_container)
shipping_address = self.request.session.get('shipping_address')
form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address)
form.instance.shipping_first_name = shipping_address['first_name']
form.instance.shipping_last_name = shipping_address['last_name']
form.instance.shipping_street_address_1 = shipping_address['street_address_1']
form.instance.shipping_street_address_2 = shipping_address['street_address_2']
form.instance.shipping_city = shipping_address['city']
form.instance.shipping_state = shipping_address['state']
form.instance.shipping_postal_code = shipping_address['postal_code']
form.instance.customer = get_or_create_customer(self.request, shipping_address)
form.instance.status = OrderStatus.UNFULFILLED
self.object = form.save()
bulk_list = cart.build_bulk_list(self.object)
@ -546,6 +557,24 @@ class CustomerUpdateView(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
)
class CustomerShippingAddressUpdateView(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = User
template_name = 'storefront/customer_shipping_address_form.html'
context_object_name = 'customer'
form_class = CustomerShippingAddressUpdateForm
permission_denied_message = 'Not authorized.'
raise_exception = True
def test_func(self):
return self.request.user.pk == self.get_object().pk
def get_success_url(self):
return reverse(
'storefront:customer-detail', kwargs={'pk': self.object.pk}
)
class OrderDetailView(UserPassesTestMixin, LoginRequiredMixin, DetailView):
model = Order
template_name = 'storefront/order_detail.html'
@ -562,58 +591,6 @@ class OrderDetailView(UserPassesTestMixin, LoginRequiredMixin, DetailView):
return context
class CustomerAddressCreateView(
UserPassesTestMixin, LoginRequiredMixin, CreateView
):
model = Address
template_name = 'storefront/address_create_form.html'
form_class = AccountAddressForm
permission_denied_message = 'Not authorized.'
raise_exception = True
def test_func(self):
return self.request.user.pk == self.kwargs['pk']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['customer'] = User.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
customer = User.objects.get(pk=self.kwargs['pk'])
self.object = form.save()
customer.addresses.add(self.object)
return super().form_valid(form)
def get_success_url(self):
return reverse(
'storefront:customer-detail', kwargs={'pk': self.kwargs['pk']}
)
class CustomerAddressUpdateView(
UserPassesTestMixin, LoginRequiredMixin, UpdateView
):
model = Address
pk_url_kwarg = 'address_pk'
template_name = 'storefront/address_form.html'
form_class = AccountAddressForm
permission_denied_message = 'Not authorized.'
raise_exception = True
def test_func(self):
return self.request.user.pk == self.kwargs['pk']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['customer'] = User.objects.get(pk=self.kwargs['pk'])
return context
def get_success_url(self):
return reverse('storefront:customer-detail', kwargs={'pk': self.kwargs['pk']})
class AboutView(TemplateView):
template_name = 'storefront/about.html'
@ -754,7 +731,7 @@ class SubscriptionCreateView(SuccessMessageMixin, CreateView):
).get('recurring')
shipping_cost = get_shipping_cost(
self.object.total_weight,
self.object.shipping_address.postal_code
self.object.shipping_postal_code
) * 100
line_items.append({
'price_data': {
@ -789,7 +766,16 @@ class SubscriptionCreateView(SuccessMessageMixin, CreateView):
def form_valid(self, form):
shipping_address = self.request.session.get('shipping_address')
subscription = self.request.session.get('subscription')
form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address)
form.instance.shipping_first_name = shipping_address['first_name']
form.instance.shipping_last_name = shipping_address['last_name']
form.instance.shipping_street_address_1 = shipping_address['street_address_1']
form.instance.shipping_street_address_2 = shipping_address['street_address_2']
form.instance.shipping_city = shipping_address['city']
form.instance.shipping_state = shipping_address['state']
form.instance.shipping_postal_code = shipping_address['postal_code']
form.instance.customer = get_or_create_customer(
self.request, shipping_address
)
weight, unit = subscription['metadata']['total_weight'].split(':')
form.instance.total_weight = guess(
float(weight), unit, measures=[Weight]
@ -855,3 +841,4 @@ def stripe_webhook(request):
subscription.create_order(event.data.object)
return JsonResponse({'status': 'success'})

View File

@ -14,8 +14,20 @@
<form method="POST" action="{% url 'account_change_password' %}" class="password_change">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="{% trans "Save changes" %}">
<table class="form-table">
<tbody>
{{ form.as_table }}
</tbody>
<tfoot>
<tr>
<td>
</td>
<td>
<button type="submit">Save changes</button>
</td>
</tr>
</tfoot>
</table>
</form>
</article>
{% endblock %}