Skip to content

Commit 62ddfb8

Browse files
authored
Add staff-side customer create view (#562)
Staff can now create customers directly in the workshop without requiring the customer to self-register via allauth. The form creates a User with an unusable password and marks the email as verified, with an optional checkbox to immediately send a password-reset email so the customer can set their own password. German translations included.
1 parent 94989a0 commit 62ddfb8

10 files changed

Lines changed: 295 additions & 161 deletions

File tree

bakeup/shop/models.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def get_production_state(self):
138138
)
139139
for state in states:
140140
state["label"] = (
141-
f'#{state["count"]} {ProductionPlan.state_display_value(state["state"])}'
141+
f"#{state['count']} {ProductionPlan.state_display_value(state['state'])}"
142142
)
143143
state["css_class"] = ProductionPlan.state_css_class(state["state"])
144144
return states
@@ -699,12 +699,23 @@ class Customer(CommonBaseClass):
699699
blank=True,
700700
null=True,
701701
related_name="customers",
702+
verbose_name=_("Point of Sale"),
703+
)
704+
street = models.CharField(
705+
max_length=100, blank=True, null=True, verbose_name=_("Street")
706+
)
707+
street_number = models.CharField(
708+
max_length=10, blank=True, null=True, verbose_name=_("Street Number")
709+
)
710+
postal_code = models.CharField(
711+
max_length=10, blank=True, null=True, verbose_name=_("Postal Code")
712+
)
713+
city = models.CharField(
714+
max_length=100, blank=True, null=True, verbose_name=_("City")
715+
)
716+
telephone_number = models.CharField(
717+
max_length=20, blank=True, null=True, verbose_name=_("Telephone Number")
702718
)
703-
street = models.CharField(max_length=100, blank=True, null=True)
704-
street_number = models.CharField(max_length=10, blank=True, null=True)
705-
postal_code = models.CharField(max_length=10, blank=True, null=True)
706-
city = models.CharField(max_length=100, blank=True, null=True)
707-
telephone_number = models.CharField(max_length=20, blank=True, null=True)
708719

709720
class Meta:
710721
ordering = ("user__email",)
@@ -1327,7 +1338,7 @@ def create_customer_order_template(
13271338
request,
13281339
messages.INFO,
13291340
"Es sind nicht mehr genügend Abo Plätze verfügbar. Es wurde"
1330-
f" eine kleinere Menge von {product.name } abonniert.",
1341+
f" eine kleinere Menge von {product.name} abonniert.",
13311342
)
13321343
(
13331344
order_template_position,

bakeup/templates/workshop/customer_detail.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ <h3>{% trans "Account" %}:</h3>
110110
{% trans "last login" %}:
111111
</div>
112112
<div class="col-8">
113-
{{ object.user.last_login }}
113+
{{ object.user.last_login|default:"-" }}
114114
</div>
115115
</div>
116116
</div>

bakeup/templates/workshop/customer_list.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
{% block page_heading_action %}
1111
<div class="btn-group float-end" role="group" aria-label="Basic example">
12+
<a href="{% url 'workshop:customer-add' %}" class="btn btn-primary btn-sm"><i class="fas fa-plus"></i> {% trans "Add Customer" %}</a>
1213
<a href="{% url 'workshop:group-list' %}" class="btn btn-primary btn-sm"><i class="fas fa-users"></i> {% trans "Groups" %}</a>
1314
<a href="{% url 'workshop:customer-order-template-overview' %}" class="btn btn-primary btn-sm">{% trans "Customer abo overview" %}</a>
1415
<a href="{% url 'workshop:customer-abo' %}" class="btn btn-primary btn-sm">{% trans "Customer abos" %}</a>

bakeup/workshop/forms.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from bakeup.core.models import UOM
2222
from bakeup.shop.models import Customer, CustomerOrder, PointOfSale, ProductionDay
23+
from bakeup.users.models import User
2324
from bakeup.workshop.models import Category, Product, ProductionPlan, ReminderMessage
2425

2526

@@ -225,8 +226,8 @@ class SelectProductionDayForm(Form):
225226

226227

227228
class CustomerForm(ModelForm):
228-
first_name = CharField()
229-
last_name = CharField()
229+
first_name = CharField(label=_("First name"))
230+
last_name = CharField(label=_("Last name"))
230231
is_active = BooleanField(
231232
required=False,
232233
label=_("active"),
@@ -259,6 +260,26 @@ class Meta:
259260
]
260261

261262

263+
class CustomerCreateForm(CustomerForm):
264+
email = forms.EmailField()
265+
send_invite = forms.BooleanField(
266+
required=False,
267+
label=_("Send password reset email"),
268+
help_text=_(
269+
"Send the customer an email with a link to set their own password."
270+
),
271+
)
272+
273+
class Meta(CustomerForm.Meta):
274+
fields = ["email"] + CustomerForm.Meta.fields
275+
276+
def clean_email(self):
277+
email = self.cleaned_data["email"]
278+
if User.objects.filter(email=email).exists():
279+
raise forms.ValidationError(_("A user with this email already exists."))
280+
return email
281+
282+
262283
class CustomerOrderForm(ModelForm):
263284
production_day = forms.IntegerField(required=False, widget=forms.HiddenInput)
264285
customer = forms.ModelChoiceField(

bakeup/workshop/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
CategoryUpdateView,
1818
CreateUpdateInstructionsView,
1919
CustomerAutocomplete,
20+
CustomerCreateView,
2021
CustomerDeleteView,
2122
CustomerDetailView,
2223
CustomerListView,
@@ -346,6 +347,7 @@
346347
"groups/<int:pk>/update/", view=GroupUpdateView.as_view(), name="group-update"
347348
),
348349
path("customers/", view=CustomerListView.as_view(), name="customer-list"),
350+
path("customers/add/", view=CustomerCreateView.as_view(), name="customer-add"),
349351
path(
350352
"customers/export/ready2order/",
351353
view=CustomerReady2OrderExportView.as_view(),

bakeup/workshop/views.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from bakeup.users.models import User
5959
from bakeup.workshop.forms import (
6060
AddProductFormSet,
61+
CustomerCreateForm,
6162
CustomerForm,
6263
CustomerOrderForm,
6364
ProductForm,
@@ -1593,6 +1594,44 @@ def export_name(self):
15931594
return "customers-{}".format(now().strftime("%Y%m%d-%H%M%S"))
15941595

15951596

1597+
class CustomerCreateView(StaffPermissionsMixin, CreateView):
1598+
model = Customer
1599+
form_class = CustomerCreateForm
1600+
template_name = "workshop/customer_form.html"
1601+
success_url = reverse_lazy("workshop:customer-list")
1602+
1603+
def form_valid(self, form):
1604+
from allauth.account.models import EmailAddress
1605+
1606+
email = form.cleaned_data["email"]
1607+
user = User.objects.create_user(
1608+
username=email,
1609+
email=email,
1610+
first_name=form.cleaned_data["first_name"],
1611+
last_name=form.cleaned_data["last_name"],
1612+
is_active=True,
1613+
password=None,
1614+
)
1615+
user.groups.set(form.cleaned_data["groups"])
1616+
EmailAddress.objects.create(user=user, email=email, verified=True, primary=True)
1617+
customer = user.customer
1618+
customer.point_of_sale = form.cleaned_data.get("point_of_sale")
1619+
customer.street = form.cleaned_data.get("street", "")
1620+
customer.street_number = form.cleaned_data.get("street_number", "")
1621+
customer.postal_code = form.cleaned_data.get("postal_code", "")
1622+
customer.city = form.cleaned_data.get("city", "")
1623+
customer.telephone_number = form.cleaned_data.get("telephone_number", "")
1624+
customer.save()
1625+
self.object = customer
1626+
if form.cleaned_data.get("send_invite"):
1627+
from bakeup.users.forms import CustomResetPasswordForm
1628+
1629+
reset_form = CustomResetPasswordForm(data={"email": email})
1630+
if reset_form.is_valid():
1631+
reset_form.save(self.request)
1632+
return HttpResponseRedirect(self.get_success_url())
1633+
1634+
15961635
class CustomerDeleteView(StaffPermissionsMixin, DeleteView):
15971636
model = User
15981637
template_name = "workshop/customer_confirm_delete.html"

locale/de_DE/LC_MESSAGES/django.mo

613 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)