Add base for wholesale orders
This commit is contained in:
parent
e514d72c8d
commit
c598f805e2
@ -1,28 +1,4 @@
|
|||||||
[{
|
[{
|
||||||
"model": "accounts.address",
|
|
||||||
"pk": 1,
|
|
||||||
"fields": {
|
|
||||||
"first_name": "Nathan",
|
|
||||||
"last_name": "Chapman",
|
|
||||||
"street_address_1": "1504 N 230 E",
|
|
||||||
"street_address_2": "",
|
|
||||||
"city": "North Logan",
|
|
||||||
"state": "UT",
|
|
||||||
"postal_code": "84341"
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
"model": "accounts.address",
|
|
||||||
"pk": 2,
|
|
||||||
"fields": {
|
|
||||||
"first_name": "John",
|
|
||||||
"last_name": "Doe",
|
|
||||||
"street_address_1": "90415 Pollich Skyway",
|
|
||||||
"street_address_2": "",
|
|
||||||
"city": "Jaskolskiburgh",
|
|
||||||
"state": "MS",
|
|
||||||
"postal_code": "32715"
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
"model": "accounts.user",
|
"model": "accounts.user",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"fields": {
|
"fields": {
|
||||||
@ -36,11 +12,13 @@
|
|||||||
"is_staff": true,
|
"is_staff": true,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"date_joined": "2022-04-28T01:24:47.591Z",
|
"date_joined": "2022-04-28T01:24:47.591Z",
|
||||||
"default_shipping_address": 1,
|
"shipping_street_address_1": "1504 N 230 E",
|
||||||
"default_billing_address": null,
|
"shipping_street_address_2": "",
|
||||||
|
"shipping_city": "North Logan",
|
||||||
|
"shipping_state": "UT",
|
||||||
|
"shipping_postal_code": "84341",
|
||||||
"groups": [],
|
"groups": [],
|
||||||
"user_permissions": [],
|
"user_permissions": []
|
||||||
"addresses": []
|
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
"model": "accounts.user",
|
"model": "accounts.user",
|
||||||
@ -56,10 +34,12 @@
|
|||||||
"is_staff": false,
|
"is_staff": false,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"date_joined": "2022-05-04T00:00:11Z",
|
"date_joined": "2022-05-04T00:00:11Z",
|
||||||
"default_shipping_address": 2,
|
"shipping_street_address_1": "90415 Pollich Skyway",
|
||||||
"default_billing_address": null,
|
"shipping_street_address_2": "",
|
||||||
|
"shipping_city": "Jaskolskiburgh",
|
||||||
|
"shipping_state": "MS",
|
||||||
|
"shipping_postal_code": "32715",
|
||||||
"groups": [],
|
"groups": [],
|
||||||
"user_permissions": [],
|
"user_permissions": []
|
||||||
"addresses": []
|
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|||||||
@ -5,8 +5,13 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"customer": 1,
|
"customer": 1,
|
||||||
"status": "unfulfilled",
|
"status": "unfulfilled",
|
||||||
"billing_address": null,
|
"shipping_first_name": "Nathan",
|
||||||
"shipping_address": 1,
|
"shipping_last_name": "Chapman",
|
||||||
|
"shipping_street_address_1": "1504 N 230 E",
|
||||||
|
"shipping_street_address_2": "",
|
||||||
|
"shipping_city": "North Logan",
|
||||||
|
"shipping_state": "UT",
|
||||||
|
"shipping_postal_code": "84341",
|
||||||
"coupon": null,
|
"coupon": null,
|
||||||
"subtotal_amount": "26.80",
|
"subtotal_amount": "26.80",
|
||||||
"coupon_amount": "0.00",
|
"coupon_amount": "0.00",
|
||||||
@ -22,8 +27,13 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"customer": 1,
|
"customer": 1,
|
||||||
"status": "unfulfilled",
|
"status": "unfulfilled",
|
||||||
"billing_address": null,
|
"shipping_first_name": "Nathan",
|
||||||
"shipping_address": 1,
|
"shipping_last_name": "Chapman",
|
||||||
|
"shipping_street_address_1": "1504 N 230 E",
|
||||||
|
"shipping_street_address_2": "",
|
||||||
|
"shipping_city": "North Logan",
|
||||||
|
"shipping_state": "UT",
|
||||||
|
"shipping_postal_code": "84341",
|
||||||
"coupon": null,
|
"coupon": null,
|
||||||
"subtotal_amount": "26.80",
|
"subtotal_amount": "26.80",
|
||||||
"coupon_amount": "0.00",
|
"coupon_amount": "0.00",
|
||||||
@ -39,8 +49,13 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"customer": 2,
|
"customer": 2,
|
||||||
"status": "unfulfilled",
|
"status": "unfulfilled",
|
||||||
"billing_address": null,
|
"shipping_first_name": "John",
|
||||||
"shipping_address": 1,
|
"shipping_last_name": "Doe",
|
||||||
|
"shipping_street_address_1": "90415 Pollich Skyway",
|
||||||
|
"shipping_street_address_2": "",
|
||||||
|
"shipping_city": "Jaskolskiburgh",
|
||||||
|
"shipping_state": "MS",
|
||||||
|
"shipping_postal_code": "32715",
|
||||||
"coupon": null,
|
"coupon": null,
|
||||||
"subtotal_amount": "26.80",
|
"subtotal_amount": "26.80",
|
||||||
"coupon_amount": "0.00",
|
"coupon_amount": "0.00",
|
||||||
|
|||||||
41
core/migrations/0009_wholesaleorder.py
Normal file
41
core/migrations/0009_wholesaleorder.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Generated by Django 4.1.6 on 2023-07-29 02:28
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
import django.contrib.postgres.fields
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('core', '0008_remove_order_billing_address_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WholesaleOrder',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('brazil', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('dantes_tornado', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('decaf', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('ethiopia', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('loop_d_loop', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('moka_java', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('nicaragua', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('pantomime', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('sumatra', django.contrib.postgres.fields.ArrayField(base_field=models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(50)]), size=2)),
|
||||||
|
('fulfilled', models.BooleanField(default=False)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('customer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wholesale_orders', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'wholesale order',
|
||||||
|
'verbose_name_plural': 'wholesale orders',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -14,6 +14,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
|||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.contrib.postgres.fields import ArrayField, HStoreField
|
from django.contrib.postgres.fields import ArrayField, HStoreField
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
|
from django.contrib.postgres.fields import ArrayField
|
||||||
|
|
||||||
from django_measurement.models import MeasurementField
|
from django_measurement.models import MeasurementField
|
||||||
from localflavor.us.us_states import USPS_CHOICES
|
from localflavor.us.us_states import USPS_CHOICES
|
||||||
@ -688,3 +689,77 @@ class SiteSettings(SingletonBase):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Site Settings'
|
verbose_name = 'Site Settings'
|
||||||
verbose_name_plural = 'Site Settings'
|
verbose_name_plural = 'Site Settings'
|
||||||
|
|
||||||
|
|
||||||
|
class WholesaleOrder(models.Model):
|
||||||
|
customer = models.ForeignKey(
|
||||||
|
User,
|
||||||
|
related_name='wholesale_orders',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
brazil = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
dantes_tornado = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
decaf = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
ethiopia = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
loop_d_loop = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
moka_java = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
nicaragua = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
pantomime = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
sumatra = ArrayField(
|
||||||
|
models.PositiveSmallIntegerField(default=0, validators=[
|
||||||
|
MinValueValidator(0), MaxValueValidator(50)
|
||||||
|
]),
|
||||||
|
size=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
fulfilled = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "wholesale order"
|
||||||
|
verbose_name_plural = "wholesale orders"
|
||||||
|
|
||||||
|
|||||||
@ -122,3 +122,4 @@ class ProductPhotoForm(forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ProductPhoto
|
model = ProductPhoto
|
||||||
fields = ('image',)
|
fields = ('image',)
|
||||||
|
|
||||||
|
|||||||
@ -1134,3 +1134,9 @@ footer > section {
|
|||||||
.show-modal {
|
.show-modal {
|
||||||
white-space: unset;
|
white-space: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#wholesale-faq {
|
||||||
|
display: flex;
|
||||||
|
gap: 3rem;
|
||||||
|
}
|
||||||
|
|||||||
@ -11,12 +11,27 @@ from localflavor.us.us_states import USPS_CHOICES
|
|||||||
from usps import USPSApi, Address
|
from usps import USPSApi, Address
|
||||||
from django_measurement.forms import MeasurementField
|
from django_measurement.forms import MeasurementField
|
||||||
|
|
||||||
from core.models import Order, ProductVariant, Subscription, SiteSettings
|
from core.models import (
|
||||||
|
Order, ProductVariant, Subscription, SiteSettings, WholesaleOrder
|
||||||
|
)
|
||||||
from core import CoffeeGrind, ShippingContainer
|
from core import CoffeeGrind, ShippingContainer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SplitWidget(forms.MultiWidget):
|
||||||
|
def decompress(self, value):
|
||||||
|
if value:
|
||||||
|
return [value[0], value[1]]
|
||||||
|
return [0, 0]
|
||||||
|
|
||||||
|
def value_from_datadict(self, data, files, name):
|
||||||
|
sixteen, five = super().value_from_datadict(data, files, name)
|
||||||
|
return [sixteen, five]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class VariantChoiceField(forms.ModelChoiceField):
|
class VariantChoiceField(forms.ModelChoiceField):
|
||||||
def label_from_instance(self, obj):
|
def label_from_instance(self, obj):
|
||||||
return f'{obj.name} | ${obj.price}'
|
return f'{obj.name} | ${obj.price}'
|
||||||
@ -142,3 +157,94 @@ class SubscriptionForm(forms.Form):
|
|||||||
stripe_price_id = forms.CharField(widget=forms.HiddenInput())
|
stripe_price_id = forms.CharField(widget=forms.HiddenInput())
|
||||||
total_quantity = forms.IntegerField(widget=forms.HiddenInput())
|
total_quantity = forms.IntegerField(widget=forms.HiddenInput())
|
||||||
total_weight = forms.CharField(widget=forms.HiddenInput())
|
total_weight = forms.CharField(widget=forms.HiddenInput())
|
||||||
|
|
||||||
|
|
||||||
|
class WholesaleOrderCreateForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = WholesaleOrder
|
||||||
|
fields = [
|
||||||
|
"brazil",
|
||||||
|
"dantes_tornado",
|
||||||
|
"decaf",
|
||||||
|
"ethiopia",
|
||||||
|
"loop_d_loop",
|
||||||
|
"moka_java",
|
||||||
|
"nicaragua",
|
||||||
|
"pantomime",
|
||||||
|
"sumatra",
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
"brazil": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"dantes_tornado": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"decaf": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"ethiopia": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"loop_d_loop": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"moka_java": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"nicaragua": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"pantomime": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
"sumatra": SplitWidget(widgets={
|
||||||
|
"16": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
"5": forms.NumberInput(attrs={
|
||||||
|
"size": 5
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
35
storefront/templates/storefront/wholesale_order.html
Normal file
35
storefront/templates/storefront/wholesale_order.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>Wholesale</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<section>
|
||||||
|
<p><a href="{% url 'storefront:wholesale-order-create' %}" class="btn">New wholesale order</a></p>
|
||||||
|
<table>
|
||||||
|
{% for order in user.wholesale_orders.all %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ order.created_at }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'storefront:wholesale-order-detail' user.pk order.pk %}"No. {{ order.pk }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<section id="wholesale-faq">
|
||||||
|
<div>
|
||||||
|
<h4>I'm already a wholesale customer</h4>
|
||||||
|
<p>If you are already a wholesale customer, please <a href="{% url 'account_login' %}?next=/wholesale-orders/new/">login</a> to place an order.<p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>I would like to become a wholesale customer</h4>
|
||||||
|
<p>You must become a wholesale customer to place an order. <a href="mailto:{{ site_settings.default_contact_email }}">Contact us</a> to become a wholesale customer.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>Place a wholesale order</h1>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<table class="form-table">
|
||||||
|
<thead>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>16 oz Qty / 5 lb Qty</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<button type="submit">Place order</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
73
storefront/templates/storefront/wholesale_order_detail.html
Normal file
73
storefront/templates/storefront/wholesale_order_detail.html
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<p><a href="{% url 'storefront:customer-detail' customer.pk %}">← Back</a></p>
|
||||||
|
<header>
|
||||||
|
<h1>Wholesale Order No. {{order.pk}}</h1>
|
||||||
|
<h3>Placed on {{order.created_at|date:"M j, Y"}}</h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>16 oz</th>
|
||||||
|
<th>5 lb</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Brazil</th>
|
||||||
|
{% for qty in order.brazil %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Dante's Tornado</th>
|
||||||
|
{% for qty in order.dantes_tornado %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Decaf</th>
|
||||||
|
{% for qty in order.decaf %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Ethiopia</th>
|
||||||
|
{% for qty in order.ethiopia %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Loop d' Loop</th>
|
||||||
|
{% for qty in order.loop_d_loop %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Moka Java</th>
|
||||||
|
{% for qty in order.moka_java %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Nicaragua</th>
|
||||||
|
{% for qty in order.nicaragua %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Pantomime</th>
|
||||||
|
{% for qty in order.pantomime %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr><tr>
|
||||||
|
<th>Sumatra</th>
|
||||||
|
{% for qty in order.sumatra %}
|
||||||
|
<td>{% if qty %}Qty: {{ qty }}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
@ -99,6 +99,13 @@ urlpatterns = [
|
|||||||
views.OrderDetailView.as_view(),
|
views.OrderDetailView.as_view(),
|
||||||
name='order-detail',
|
name='order-detail',
|
||||||
),
|
),
|
||||||
|
path('wholesale-orders/<int:order_pk>/', include([
|
||||||
|
path(
|
||||||
|
'',
|
||||||
|
views.WholesaleOrderDetailView.as_view(),
|
||||||
|
name='wholesale-order-detail'
|
||||||
|
),
|
||||||
|
])),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
path(
|
path(
|
||||||
@ -130,4 +137,16 @@ urlpatterns = [
|
|||||||
name='subscription-done'
|
name='subscription-done'
|
||||||
),
|
),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
|
# Wholesale Orders
|
||||||
|
path(
|
||||||
|
'wholesale-orders/',
|
||||||
|
views.WholesaleOrderView.as_view(),
|
||||||
|
name='wholesale-order'
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
'wholesale-orders/new/',
|
||||||
|
views.WholesaleOrderCreateView.as_view(),
|
||||||
|
name='wholesale-order-create'
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -19,7 +19,9 @@ from django.views.generic.edit import (
|
|||||||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||||
from django.views.generic.list import ListView
|
from django.views.generic.list import ListView
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.contrib.auth.mixins import (
|
||||||
|
PermissionRequiredMixin, LoginRequiredMixin, UserPassesTestMixin
|
||||||
|
)
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@ -40,7 +42,7 @@ from accounts.forms import CustomerUpdateForm, CustomerShippingAddressUpdateForm
|
|||||||
from core.models import (
|
from core.models import (
|
||||||
ProductCategory, Product, ProductVariant, ProductOption,
|
ProductCategory, Product, ProductVariant, ProductOption,
|
||||||
Order, Transaction, OrderLine, Coupon, ShippingRate,
|
Order, Transaction, OrderLine, Coupon, ShippingRate,
|
||||||
Subscription, SiteSettings
|
Subscription, SiteSettings, WholesaleOrder
|
||||||
)
|
)
|
||||||
from core.forms import ShippingRateForm
|
from core.forms import ShippingRateForm
|
||||||
from core.shipping import get_shipping_cost
|
from core.shipping import get_shipping_cost
|
||||||
@ -49,7 +51,7 @@ from core import OrderStatus, ShippingContainer, TransactionStatus
|
|||||||
from .forms import (
|
from .forms import (
|
||||||
AddToCartForm, CartItemUpdateForm, OrderCreateForm,
|
AddToCartForm, CartItemUpdateForm, OrderCreateForm,
|
||||||
AddressForm, CouponApplyForm, CheckoutShippingForm,
|
AddressForm, CouponApplyForm, CheckoutShippingForm,
|
||||||
SubscriptionForm
|
SubscriptionForm, WholesaleOrderCreateForm
|
||||||
)
|
)
|
||||||
from .cart import CartItem, Cart
|
from .cart import CartItem, Cart
|
||||||
from .payments import CaptureOrder
|
from .payments import CaptureOrder
|
||||||
@ -842,3 +844,45 @@ def stripe_webhook(request):
|
|||||||
|
|
||||||
return JsonResponse({'status': 'success'})
|
return JsonResponse({'status': 'success'})
|
||||||
|
|
||||||
|
|
||||||
|
class WholesaleOrderView(TemplateView):
|
||||||
|
template_name = "storefront/wholesale_order.html"
|
||||||
|
|
||||||
|
|
||||||
|
class WholesaleOrderCreateView(
|
||||||
|
PermissionRequiredMixin, LoginRequiredMixin, CreateView
|
||||||
|
):
|
||||||
|
permission_required = "core.create_wholesaleorder"
|
||||||
|
model = WholesaleOrder
|
||||||
|
template_name = 'storefront/wholesale_order_create_form.html'
|
||||||
|
form_class = WholesaleOrderCreateForm
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(
|
||||||
|
'storefront:wholesale-order-detail', kwargs={
|
||||||
|
'pk': self.object.customer.pk, 'order_pk': self.object.pk
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.customer = self.request.user
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class WholesaleOrderDetailView(
|
||||||
|
PermissionRequiredMixin, LoginRequiredMixin, DetailView
|
||||||
|
):
|
||||||
|
permission_required = "core.view_wholesaleorder"
|
||||||
|
model = WholesaleOrder
|
||||||
|
context_object_name = 'order'
|
||||||
|
pk_url_kwarg = 'order_pk'
|
||||||
|
template_name = 'storefront/wholesale_order_detail.html'
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
return self.request.user.pk == self.get_object().customer.pk
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['customer'] = User.objects.get(pk=self.kwargs['pk'])
|
||||||
|
return context
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,7 @@
|
|||||||
<li><a class="nav__link" href="{% url 'storefront:category-detail' category.pk %}">{{ category }}</a></li>
|
<li><a class="nav__link" href="{% url 'storefront:category-detail' category.pk %}">{{ category }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li><a class="nav__link" href="{% url 'storefront:subscription-form' %}">Subscribe</a></li>
|
<li><a class="nav__link" href="{% url 'storefront:subscription-form' %}">Subscribe</a></li>
|
||||||
|
<li><a class="nav__link" href="{% url 'storefront:wholesale-order' %}">Wholesale</a></li>
|
||||||
<li><a class="nav__link" href="{% url 'storefront:fair-trade' %}">Fair trade</a></li>
|
<li><a class="nav__link" href="{% url 'storefront:fair-trade' %}">Fair trade</a></li>
|
||||||
<li><a class="nav__link" href="{% url 'storefront:reviews' %}">Reviews</a></li>
|
<li><a class="nav__link" href="{% url 'storefront:reviews' %}">Reviews</a></li>
|
||||||
<li><a class="nav__link" href="{% url 'storefront:about' %}">About</a></li>
|
<li><a class="nav__link" href="{% url 'storefront:about' %}">About</a></li>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user