Add more variants
This commit is contained in:
parent
34b6eb6bfd
commit
504bc3a146
@ -162,7 +162,7 @@ class ProductVariant(models.Model):
|
|||||||
return f'{self.product}: {self.name}'
|
return f'{self.product}: {self.name}'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
pass
|
ordering = ['weight']
|
||||||
|
|
||||||
|
|
||||||
class ProductOption(models.Model):
|
class ProductOption(models.Model):
|
||||||
|
|||||||
@ -6,13 +6,7 @@
|
|||||||
<header class="object__header">
|
<header class="object__header">
|
||||||
<h1><img src="{% static 'images/box.png' %}" alt=""> Order #{{order.pk}}</h1>
|
<h1><img src="{% static 'images/box.png' %}" alt=""> Order #{{order.pk}}</h1>
|
||||||
<div class="object__menu">
|
<div class="object__menu">
|
||||||
<div class="dropdown">
|
<a class="action-button action-button--warning" href="{% url 'dashboard:order-cancel' order.pk %}">Cancel order</a>
|
||||||
<span class="dropdown__menu">Options ↓</span>
|
|
||||||
<div class="dropdown__child">
|
|
||||||
<a href="{% url 'dashboard:order-cancel' order.pk %}">Cancel order</a>
|
|
||||||
<a href="">Return order</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span class="order__status order__status--{{order.status}}">{{order.get_status_display}} ({{order.total_quantity_fulfilled}} / {{order.total_quantity_ordered}})</span>
|
<span class="order__status order__status--{{order.status}}">{{order.get_status_display}} ({{order.total_quantity_fulfilled}} / {{order.total_quantity_ordered}})</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -33,7 +27,7 @@
|
|||||||
</figure>
|
</figure>
|
||||||
<span>{{product.sku}}</span>
|
<span>{{product.sku}}</span>
|
||||||
<span>{{item.quantity}}</span>
|
<span>{{item.quantity}}</span>
|
||||||
<span>${{product.price}}</span>
|
<span>${{item.variant.price}}</span>
|
||||||
<span>${{item.get_total}}</span>
|
<span>${{item.get_total}}</span>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
<form method="POST" action="">
|
<form method="POST" action="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.management_form }}
|
{{ form.management_form }}
|
||||||
|
|
||||||
<section class="object__list">
|
<section class="object__list">
|
||||||
{% for dict in form.errors %}
|
{% for dict in form.errors %}
|
||||||
{% for error in dict.values %}
|
{% for error in dict.values %}
|
||||||
@ -20,15 +19,15 @@
|
|||||||
<span>Product</span>
|
<span>Product</span>
|
||||||
<span>SKU</span>
|
<span>SKU</span>
|
||||||
<span>Quantity to fulfill</span>
|
<span>Quantity to fulfill</span>
|
||||||
<span>Grind</span>
|
<span>Options</span>
|
||||||
</div>
|
</div>
|
||||||
{% for form in form %}
|
{% for form in form %}
|
||||||
<div class="object__item object__item--col4">
|
<div class="object__item object__item--col4">
|
||||||
{% with product=form.instance.product %}
|
{% with product=form.instance.variant.product %}
|
||||||
{{form.id}}
|
{{form.id}}
|
||||||
<figure class="item__figure">
|
<figure class="item__figure">
|
||||||
<img class="product__image product__image--small" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
|
<img class="product__image product__image--small" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
|
||||||
<figcaption><strong>{{product.name}}</strong></figcaption>
|
<figcaption><strong>{{form.instance.variant}}</strong></figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<span>{{product.sku}}</span>
|
<span>{{product.sku}}</span>
|
||||||
<span>{{form.quantity_fulfilled}} / {{form.instance.quantity}}</span>
|
<span>{{form.quantity_fulfilled}} / {{form.instance.quantity}}</span>
|
||||||
|
|||||||
@ -32,13 +32,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{% for variant in product.variants.all %}
|
{% for variant in product.variants.all %}
|
||||||
<div class="panel__item">
|
<div class="panel__item">
|
||||||
<p>name: {{ variant.name }}</p>
|
<h3>{{ variant.name }}</h3>
|
||||||
<p>sku: {{ variant.sku }}</p>
|
<p>SKU: {{ variant.sku }}</p>
|
||||||
<p>stripe_id: {{ variant.stripe_id }}</p>
|
<p>Price: ${{ variant.price }}</p>
|
||||||
<p>price: ${{ variant.price }}</p>
|
<p>Weight: {{ variant.weight }}</p>
|
||||||
<p>weight: {{ variant.weight }}</p>
|
{% if variant.track_inventory %}
|
||||||
<p>track_inventory: {{ variant.track_inventory }}</p>
|
<p>Stock: {{ variant.stock }}</p>
|
||||||
<p>stock: {{ variant.stock }}</p>
|
{% endif %}
|
||||||
|
<p><a href="{% url 'dashboard:variant-update' product.pk variant.pk %}">Edit</a></p>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
{% extends "dashboard.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<header class="object__header">
|
||||||
|
<h1>Delete Variant</h1>
|
||||||
|
</header>
|
||||||
|
<section class="variant__detail object__panel">
|
||||||
|
<form method="post" class="panel__item">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>Are you sure you want to delete "{{ object }}"?</p>
|
||||||
|
{{ form.as_p }}
|
||||||
|
<p>
|
||||||
|
<input class="action-button action-button--warning" type="submit" value="Confirm"> or <a href="{% url 'dashboard:product-detail' product.pk %}">cancel</a>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
18
src/dashboard/templates/dashboard/variant_form.html
Normal file
18
src/dashboard/templates/dashboard/variant_form.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{% extends "dashboard.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article class="product">
|
||||||
|
<header class="object__header">
|
||||||
|
<h1>Update variant</h1>
|
||||||
|
</header>
|
||||||
|
<section class="object__panel">
|
||||||
|
<form class="panel__item" method="POST" action="{% url 'dashboard:variant-update' product.pk variant.pk %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{form.as_p}}
|
||||||
|
<p class="form__submit">
|
||||||
|
<input class="action-button" type="submit" value="Create variant"> or <a href="{% url 'dashboard:product-detail' product.pk %}">cancel</a>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
@ -130,11 +130,6 @@ urlpatterns = [
|
|||||||
name='variant-create'
|
name='variant-create'
|
||||||
),
|
),
|
||||||
path('<int:variant_pk>/', include([
|
path('<int:variant_pk>/', include([
|
||||||
path(
|
|
||||||
'',
|
|
||||||
views.ProductVariantDetailView.as_view(),
|
|
||||||
name='variant-detail'
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
'update/',
|
'update/',
|
||||||
views.ProductVariantUpdateView.as_view(),
|
views.ProductVariantUpdateView.as_view(),
|
||||||
|
|||||||
@ -239,6 +239,20 @@ class ProductDetailView(LoginRequiredMixin, DetailView):
|
|||||||
model = Product
|
model = Product
|
||||||
template_name = 'dashboard/product_detail.html'
|
template_name = 'dashboard/product_detail.html'
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
pk = self.kwargs.get(self.pk_url_kwarg)
|
||||||
|
queryset = Product.objects.filter(
|
||||||
|
pk=pk
|
||||||
|
).select_related(
|
||||||
|
'category',
|
||||||
|
).prefetch_related(
|
||||||
|
'variants',
|
||||||
|
'options',
|
||||||
|
'productphoto_set'
|
||||||
|
)
|
||||||
|
obj = queryset.get()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class ProductUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
class ProductUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||||
model = Product
|
model = Product
|
||||||
@ -293,8 +307,8 @@ class ProductPhotoDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView
|
|||||||
|
|
||||||
class ProductVariantCreateView(SuccessMessageMixin, CreateView):
|
class ProductVariantCreateView(SuccessMessageMixin, CreateView):
|
||||||
model = ProductVariant
|
model = ProductVariant
|
||||||
success_message = 'ProductVariant created.'
|
success_message = 'Variant created.'
|
||||||
template_name = 'dashboard/productvariant_create_form.html'
|
template_name = 'dashboard/variant_create_form.html'
|
||||||
fields = [
|
fields = [
|
||||||
'name',
|
'name',
|
||||||
'sku',
|
'sku',
|
||||||
@ -303,7 +317,6 @@ class ProductVariantCreateView(SuccessMessageMixin, CreateView):
|
|||||||
'track_inventory',
|
'track_inventory',
|
||||||
'stock',
|
'stock',
|
||||||
]
|
]
|
||||||
success_message = 'Variant created.'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
@ -318,22 +331,44 @@ class ProductVariantCreateView(SuccessMessageMixin, CreateView):
|
|||||||
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
||||||
|
|
||||||
|
|
||||||
class ProductVariantDetailView(DetailView):
|
|
||||||
model = ProductVariant
|
|
||||||
pk_url_kwarg = 'variant_pk'
|
|
||||||
|
|
||||||
|
|
||||||
class ProductVariantUpdateView(SuccessMessageMixin, UpdateView):
|
class ProductVariantUpdateView(SuccessMessageMixin, UpdateView):
|
||||||
model = ProductVariant
|
model = ProductVariant
|
||||||
pk_url_kwarg = 'variant_pk'
|
pk_url_kwarg = 'variant_pk'
|
||||||
success_message = 'ProductVariant saved.'
|
success_message = 'ProductVariant saved.'
|
||||||
fields = '__all__'
|
template_name = 'dashboard/variant_form.html'
|
||||||
|
fields = [
|
||||||
|
'name',
|
||||||
|
'sku',
|
||||||
|
'price',
|
||||||
|
'weight',
|
||||||
|
'track_inventory',
|
||||||
|
'stock',
|
||||||
|
]
|
||||||
|
context_object_name = 'variant'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['product'] = Product.objects.get(pk=self.kwargs['pk'])
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.product = Product.objects.get(pk=self.kwargs['pk'])
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
||||||
|
|
||||||
|
|
||||||
class ProductVariantDeleteView(SuccessMessageMixin, DeleteView):
|
class ProductVariantDeleteView(SuccessMessageMixin, DeleteView):
|
||||||
model = ProductVariant
|
model = ProductVariant
|
||||||
pk_url_kwarg = 'variant_pk'
|
pk_url_kwarg = 'variant_pk'
|
||||||
success_message = 'ProductVariant deleted.'
|
success_message = 'ProductVariant deleted.'
|
||||||
|
template_name = 'dashboard/variant_confirm_delete.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['product'] = Product.objects.get(pk=self.kwargs['pk'])
|
||||||
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
||||||
|
|||||||
@ -257,7 +257,9 @@ CELERY_TASK_TRACK_STARTED = True
|
|||||||
CELERY_TIMEZONE = 'US/Mountain'
|
CELERY_TIMEZONE = 'US/Mountain'
|
||||||
|
|
||||||
# Sentry
|
# Sentry
|
||||||
sentry_sdk.init(
|
|
||||||
|
if not DEBUG:
|
||||||
|
sentry_sdk.init(
|
||||||
dsn=SENTRY_DSN,
|
dsn=SENTRY_DSN,
|
||||||
environment=SENTRY_ENV,
|
environment=SENTRY_ENV,
|
||||||
integrations=[DjangoIntegration()],
|
integrations=[DjangoIntegration()],
|
||||||
@ -270,4 +272,4 @@ sentry_sdk.init(
|
|||||||
# If you wish to associate users to errors (assuming you are using
|
# If you wish to associate users to errors (assuming you are using
|
||||||
# django.contrib.auth) you may enable sending PII data.
|
# django.contrib.auth) you may enable sending PII data.
|
||||||
send_default_pii=True
|
send_default_pii=True
|
||||||
)
|
)
|
||||||
|
|||||||
@ -23,12 +23,47 @@ from core import (
|
|||||||
build_usps_rate_request
|
build_usps_rate_request
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .forms import UpdateCartItemForm
|
||||||
from .payments import CreateOrder
|
from .payments import CreateOrder
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CartItem:
|
||||||
|
update_form = UpdateCartItemForm
|
||||||
|
|
||||||
|
def __init__(self, item):
|
||||||
|
self.variant = item['variant']
|
||||||
|
self.quantity = item['quantity']
|
||||||
|
self.options = item['options']
|
||||||
|
|
||||||
|
def get_update_form(self, index):
|
||||||
|
return self.update_form(initial={
|
||||||
|
'item_pk': index,
|
||||||
|
'quantity': self.quantity
|
||||||
|
})
|
||||||
|
|
||||||
|
def build_paypal_dict(self):
|
||||||
|
return {
|
||||||
|
# Shows within upper-right dropdown during payment approval
|
||||||
|
"name": str(self.variant),
|
||||||
|
# Item details will also be in the completed paypal.com
|
||||||
|
# transaction view
|
||||||
|
"description": self.variant.product.subtitle,
|
||||||
|
"unit_amount": {
|
||||||
|
"currency_code": settings.DEFAULT_CURRENCY,
|
||||||
|
"value": f'{self.variant.price}',
|
||||||
|
},
|
||||||
|
"quantity": f'{item["quantity"]}',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.variant)
|
||||||
|
|
||||||
|
|
||||||
class Cart:
|
class Cart:
|
||||||
|
item_class = CartItem
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
self.session = request.session
|
self.session = request.session
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user