Finish first iteration of subscriptions

This commit is contained in:
Nathan Chapman 2022-12-30 11:07:07 -07:00
parent 467e736147
commit 14246afd19
8 changed files with 202 additions and 100 deletions

View File

@ -0,0 +1,19 @@
# Generated by Django 4.0.2 on 2022-12-30 16:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0026_orderline_product'),
]
operations = [
migrations.AddField(
model_name='order',
name='subscription',
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='core.subscription'),
),
]

View File

@ -382,6 +382,13 @@ class Order(models.Model):
blank=True,
null=True
)
subscription = models.ForeignKey(
'Subscription',
related_name='orders',
editable=False,
null=True,
on_delete=models.SET_NULL
)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True)
@ -548,13 +555,59 @@ class Subscription(models.Model):
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True)
def convert_int_to_decimal(self, price):
return Decimal(str(price)[:-2] + '.' + str(price)[-2:])
def format_product(self, data):
return {
'product': Product.objects.get(pk=data['pk']),
'quantity': data['quantity']
}
def deserialize_subscription(self, data):
subscription = {}
for x in data:
if 'products_and_quantities' in x['metadata']:
subscription['unit_price'] = self.convert_int_to_decimal(x['price']['unit_amount'])
if 'Shipping' in x['description']:
subscription['shipping_cost'] = self.convert_int_to_decimal(x['amount'])
return subscription
def create_order(self, data_object):
pass
subscription = self.deserialize_subscription(data_object['lines']['data'])
subscription['items'] = map(self.format_product, self.metadata['products_and_quantities'])
subscription['customer_note'] = f"Grind: {self.metadata['grind']}"
order = Order.objects.create(
customer=self.customer,
status=OrderStatus.UNFULFILLED,
shipping_address=self.shipping_address,
subtotal_amount=self.convert_int_to_decimal(data_object['subtotal']),
shipping_total=subscription['shipping_cost'],
total_amount=self.convert_int_to_decimal(data_object['total']),
weight=self.total_weight,
subscription=self
)
bulk_lines = [OrderLine(
order=order,
product=item['product'],
quantity=item['quantity'],
customer_note=subscription['customer_note'],
unit_price=subscription['unit_price']
) for item in subscription['items']]
OrderLine.objects.bulk_create(bulk_lines)
def format_metadata(self):
metadata = {}
for key, value in self.metadata.items():
metadata[key] = json.dumps(value)
if 'products_and_quantities' in key:
metadata[key] = json.dumps(value)
else:
metadata[key] = value
metadata['subscription_pk'] = self.pk
return metadata

View File

@ -28,38 +28,28 @@ def format_product(data, unit_price):
}
def find_products(data):
for x in data:
if 'products_and_quantities' in x['metadata']:
return map(format_product, x['metadata']['products_and_quantities'])
break
else:
continue
shipping_cost = None
unit_price = None
items = None
customer_note = ''
def deserialize_subscription(data):
sub_data = {}
for x in data:
if 'products_and_quantities' in x['metadata']:
customer_note = f"Grind: {x['metadata']['grind']}"
unit_price = convert_int_to_decimal(x['price']['unit_amount'])
items = map(format_product, x['metadata']['products_and_quantities'])
sub_data['customer_note'] = f"Grind: {x['metadata']['grind']}"
sub_data['unit_price'] = convert_int_to_decimal(x['price']['unit_amount'])
sub_data['items'] = map(format_product, x['metadata']['products_and_quantities'])
sub_data['total_weight'] = x['metadata']['total_weight']
if x['description'] == 'Shipping':
shipping_cost = convert_int_to_decimal(x['amount'])
sub_data['shipping_cost'] = convert_int_to_decimal(x['amount'])
continue
return sub_data
# shipping_cost = find_shipping_cost(data_object['lines']['data'])
# items = find_products(data_object['lines']['data'])
# unit_price = find_unit_price(data_object['lines']['data'])
deserialize_subscription(data_object['lines']['data'])
sub_data = deserialize_subscription(data_object['lines']['data'])
order = Order.objects.create(
customer=,
@ -76,9 +66,9 @@ order.lines.add(
[OrderLine(
product=item['product'],
quantity=item['quantity'],
customer_note='Grind: ',
unit_price=unit_price
) for item in items]
customer_note=sub_data['customer_note'],
unit_price=sub_data['unit_price']
) for item in sub_data['items']]
)
order.save()

View File

@ -23,20 +23,37 @@
</div>
{% for item in order.lines.all %}
<div class="object__item object__item--col5">
{% with product=item.variant.product %}
<figure class="item__figure">
{% if item.variant.image %}
<img class="item__image" src="{{item.variant.image.image.url}}" alt="{{item.variant.image.image}}">
{% else %}
<img class="item__image" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
{% endif %}
<figcaption><strong>{{item.variant}}</strong><br>{{item.customer_note}}</figcaption>
</figure>
<span>{{product.sku}}</span>
<span>{{item.quantity}}</span>
<span>${{item.unit_price}}</span>
<span>${{item.get_total}}</span>
{% endwith %}
{% if item.variant %}
{% with product=item.variant.product %}
<figure class="item__figure">
{% if item.variant.image %}
<img class="item__image" src="{{item.variant.image.image.url}}" alt="{{item.variant.image.image}}">
{% else %}
<img class="item__image" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
{% endif %}
<figcaption><strong>{{item.variant}}</strong><br>{{item.customer_note}}</figcaption>
</figure>
<span>{{product.sku}}</span>
<span>{{item.quantity}}</span>
<span>${{item.unit_price}}</span>
<span>${{item.get_total}}</span>
{% endwith %}
{% elif item.product %}
{% with product=item.product %}
<figure class="item__figure">
{% if item.variant.image %}
<img class="item__image" src="{{item.variant.image.image.url}}" alt="{{item.variant.image.image}}">
{% else %}
<img class="item__image" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
{% endif %}
<figcaption><strong>{{item.product}}</strong><br>{{item.customer_note}}</figcaption>
</figure>
<span>{{product.sku}}</span>
<span>{{item.quantity}}</span>
<span>${{item.unit_price}}</span>
<span>${{item.get_total}}</span>
{% endwith %}
{% endif %}
</div>
{% empty %}
<p>No items in order yet.</p>
@ -114,15 +131,26 @@
</div>
</section>
<section class="object__panel">
<div class="object__item panel__header">
<h4>Transaction</h4>
</div>
<div class="panel__item">
<p>PayPal transaction ID: <strong>{{order.transaction.paypal_id}}</strong><br>
Status: <strong>{{order.transaction.get_status_display}}</strong>
</p>
</div>
</section>
{% if order.subscription %}
<section class="object__panel">
<div class="object__item panel__header">
<h4>Subscription</h4>
</div>
<div class="panel__item">
<p>{{ order.subscription.stripe_id }}</p>
</div>
</section>
{% else %}
<section class="object__panel">
<div class="object__item panel__header">
<h4>Transaction</h4>
</div>
<div class="panel__item">
<p>PayPal transaction ID: <strong>{{order.transaction.paypal_id}}</strong><br>
Status: <strong>{{order.transaction.get_status_display}}</strong>
</p>
</div>
</section>
{% endif %}
</article>
{% endblock content %}

View File

@ -57,6 +57,29 @@
{% endfor %}
</div>
</section>
{% if customer.subscriptions.count > 0 %}
<section>
<h3>Your subscriptions</h3>
<table>
<thead>
<tr>
<th>Subscription #</th>
<th colspan="2">Status</th>
</tr>
</thead>
<tbody>
{% for subscription in subscriptions %}
<tr>
<td>{{ subscription.id }}</td>
<td>{{ subscription.status }}</td>
<td><a href="https://dashboard.stripe.com/test/subscriptions/{{ subscription.id }}">manage subscription &nearr;</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endif %}
<section>
<h3>Your orders</h3>
<table>

View File

@ -21,22 +21,41 @@
<tbody>
{% for item in order.lines.all %}
<tr>
{% with product=item.variant.product %}
<td>
{% if item.variant.image %}
<img class="line__image" src="{{item.variant.image.image.url}}" alt="{{item.variant.image.image}}">
{% else %}
<img class="line__image" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
{% endif %}
</td>
<td>
<strong>{{ item.variant }}</strong><br>
{{item.customer_note}}
</td>
<td>{{item.quantity}}</td>
<td>${{item.unit_price}}</td>
<td>${{item.get_total}}</td>
{% endwith %}
{% if item.variant %}
{% with product=item.variant.product %}
<td>
{% if item.variant.image %}
<img class="line__image" src="{{item.variant.image.image.url}}" alt="{{item.variant.image.image}}">
{% else %}
<img class="line__image" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
{% endif %}
</td>
<td>
<strong>{{ item.variant }}</strong><br>
{{item.customer_note}}
</td>
<td>{{item.quantity}}</td>
<td>${{item.unit_price}}</td>
<td>${{item.get_total}}</td>
{% endwith %}
{% elif item.product %}
{% with product=item.product %}
<td>
{% if item.variant.image %}
<img class="line__image" src="{{item.variant.image.image.url}}" alt="{{item.variant.image.image}}">
{% else %}
<img class="line__image" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
{% endif %}
</td>
<td>
<strong>{{ item.product }}</strong><br>
{{item.customer_note}}
</td>
<td>{{item.quantity}}</td>
<td>${{item.unit_price}}</td>
<td>${{item.get_total}}</td>
{% endwith %}
{% endif %}
</tr>
{% empty %}
<tr>

View File

@ -127,22 +127,5 @@ urlpatterns = [
views.SubscriptionDoneView.as_view(),
name='subscription-done'
),
path('<int:pk>/', include([
path(
'',
views.SubscriptionDetailView.as_view(),
name='subscription-detail'
),
path(
'update/',
views.SubscriptionUpdateView.as_view(),
name='subscription-update'
),
path(
'delete/',
views.SubscriptionDeleteView.as_view(),
name='subscription-delete'
),
])),
])),
]

View File

@ -437,6 +437,10 @@ class CustomerDetailView(UserPassesTestMixin, LoginRequiredMixin, DetailView):
context['order_list'] = Order.objects.without_drafts().filter(
customer=self.object
).prefetch_related('lines')
subscriptions = []
if self.object.stripe_id is not None:
subscriptions = stripe.Subscription.list(customer=self.object.stripe_id)['data']
context['subscriptions'] = subscriptions
return context
def test_func(self):
@ -720,7 +724,7 @@ class SubscriptionCreateView(SuccessMessageMixin, CreateView):
session = stripe.checkout.Session.create(
customer=self.object.customer.get_or_create_stripe_id(),
success_url='http://' + Site.objects.get_current().domain + reverse(
'storefront:subscription-detail', kwargs={'pk': self.object.pk}
'storefront:subscription-done'
) + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url='http://' + Site.objects.get_current().domain + reverse(
'storefront:subscription-create'),
@ -743,27 +747,10 @@ class SubscriptionCreateView(SuccessMessageMixin, CreateView):
return super().form_valid(form)
class SubscriptionDetailView(DetailView):
model = Subscription
template_name = 'storefront/subscription/detail.html'
class SubscriptionDoneView(TemplateView):
template_name = 'storefront/subscription/done.html'
class SubscriptionUpdateView(SuccessMessageMixin, UpdateView):
model = Subscription
success_message = 'Subscription saved.'
fields = '__all__'
class SubscriptionDeleteView(SuccessMessageMixin, DeleteView):
model = Subscription
success_message = 'Subscription deleted.'
success_url = reverse_lazy('subscription-list')
# stripe listen --forward-to localhost:8000/stripe-webhook
@csrf_exempt
@require_POST