Source code for shuup.front.apps.customer_information.forms
import six
from django import forms
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from enumfields import EnumField
from registration.signals import user_registered
from shuup.core.fields import LanguageFormField
from shuup.core.models import (
CompanyContact,
MutableAddress,
PersonContact,
SavedAddress,
SavedAddressRole,
SavedAddressStatus,
get_company_contact,
get_person_contact,
)
from shuup.front.forms.widget import PictureDnDUploaderWidget
from shuup.front.utils.companies import company_registration_requires_approval
from shuup.utils.form_group import FormGroup
from shuup.utils.importing import cached_load
from .notify_events import CompanyAccountCreated
[docs]
class PersonContactForm(forms.ModelForm):
language = LanguageFormField(label=_("Language"), required=False)
[docs]
class Meta:
model = PersonContact
fields = (
"first_name",
"last_name",
"phone",
"email",
"gender",
"language",
"marketing_permission",
"timezone",
"picture",
)
[docs]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in ("first_name", "last_name", "email"):
self.fields[field].required = True
self.initial["language"] = self.instance.language
if settings.SHUUP_CUSTOMER_INFORMATION_ALLOW_PICTURE_UPLOAD:
self.fields["picture"].widget = PictureDnDUploaderWidget(clearable=True)
else:
self.fields.pop("picture")
field_properties = settings.SHUUP_PERSON_CONTACT_FIELD_PROPERTIES
for field, properties in field_properties.items():
for prop in properties:
setattr(self.fields[field], prop, properties[prop])
[docs]
def save(self, commit=True):
self.instance.language = self.cleaned_data["language"]
return super().save(commit)
[docs]
class CompanyContactForm(forms.ModelForm):
[docs]
class Meta:
model = CompanyContact
fields = (
"name",
"tax_number",
"phone",
"email",
"marketing_permission",
"picture",
)
[docs]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if settings.SHUUP_CUSTOMER_INFORMATION_ALLOW_PICTURE_UPLOAD:
self.fields["picture"].widget = PictureDnDUploaderWidget(clearable=True)
else:
self.fields.pop("picture")
for field in ("name", "tax_number", "email"):
self.fields[field].required = True
if not kwargs.get("instance"):
self.fields["email"].help_text = _("Will become default user email when linked.")
[docs]
def clean_tax_number(self):
"""
Clean Tax Number.
This is done because we want to prevent duplicates in the front-end.
"""
tax_number = self.cleaned_data["tax_number"]
company = CompanyContact.objects.filter(tax_number=tax_number).first()
if company:
error_message = _("Given Tax Number already exists. Please contact support.")
if not self.instance.pk:
raise ValidationError(error_message, code="existing_tax_number")
elif company.pk != self.instance.pk:
raise ValidationError(error_message, code="existing_tax_number")
return tax_number
[docs]
class SavedAddressForm(forms.Form):
title = forms.CharField(max_length=255, label=_("Address Title"))
role = EnumField(SavedAddressRole, default=SavedAddressRole.SHIPPING).formfield(label=_("Address Type"))
status = EnumField(SavedAddressStatus, default=SavedAddressStatus.ENABLED).formfield(label=_("Address Status"))
[docs]
class CustomerInformationFormGroup(FormGroup):
address_forms = ["billing", "shipping"]
[docs]
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super().__init__(*args, **kwargs)
contact = get_person_contact(self.request.user)
address_form_class = cached_load("SHUUP_ADDRESS_MODEL_FORM")
for form_name in self.address_forms:
self.add_form_def(
form_name,
address_form_class,
kwargs={"instance": getattr(contact, f"default_{form_name}_address")},
)
self.add_form_def("contact", PersonContactForm, kwargs={"instance": contact})
[docs]
def save(self):
contact = self.forms["contact"].save()
user = contact.user
if "billing" in self.forms:
billing_address = self.forms["billing"].save()
if billing_address.pk != contact.default_billing_address_id: # Identity changed due to immutability
contact.default_billing_address = billing_address
if "shipping" in self.forms:
shipping_address = self.forms["shipping"].save()
if shipping_address.pk != contact.default_shipping_address_id: # Identity changed due to immutability
contact.default_shipping_address = shipping_address
if not bool(get_company_contact(self.request.user)): # Only update user details for non-company members
user.email = contact.email
user.first_name = contact.first_name
user.last_name = contact.last_name
user.save()
contact.save()
return contact
[docs]
class CompanyInformationFormGroup(FormGroup):
address_forms = ["billing", "shipping"]
[docs]
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super().__init__(*args, **kwargs)
user = self.request.user
company = get_company_contact(user)
person = get_person_contact(user)
address_form_class = cached_load("SHUUP_ADDRESS_MODEL_FORM")
for form_name in self.address_forms:
self.add_form_def(
form_name,
address_form_class,
kwargs={"instance": _get_default_address_for_contact(company, f"default_{form_name}_address", person)},
)
self.add_form_def("contact", CompanyContactForm, kwargs={"instance": company})
[docs]
def save(self):
company = self.forms["contact"].save(commit=False)
is_new = not bool(company.pk)
company.save()
user = self.request.user
# TODO: Should this check if contact will be created? Or should we expect create always?
person = get_person_contact(user)
person.add_to_shop(self.request.shop)
company.members.add(person)
company.add_to_shop(self.request.shop)
if "billing" in self.forms:
billing_address = self.forms["billing"].save()
if billing_address.pk != company.default_billing_address_id: # Identity changed due to immutability
company.default_billing_address = billing_address
if "shipping" in self.forms:
shipping_address = self.forms["shipping"].save()
if shipping_address.pk != company.default_shipping_address_id: # Identity changed due to immutability
company.default_shipping_address = shipping_address
message = _("Company information was saved.")
# If company registration requires activation,
# company will be created as inactive.
if is_new and company_registration_requires_approval(self.request.shop):
company.is_active = False
message = _("Company information was saved. Please follow the instructions sent to your email address.")
messages.success(self.request, message)
company.save()
if is_new:
user_registered.send(sender=self.__class__, user=self.request.user, request=self.request)
CompanyAccountCreated(contact=company, customer_email=company.email).run(shop=self.request.shop)
return company
[docs]
class AddressBookFormGroup(FormGroup):
saved_address_form = SavedAddressForm
[docs]
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
self.instance = kwargs.pop("instance")
super().__init__(*args, **kwargs)
address_kwargs = {}
saved_address_kwargs = {}
if self.instance:
address_kwargs["instance"] = self.instance.address
saved_address_kwargs["initial"] = {
"role": self.instance.role,
"status": self.instance.status,
"title": self.instance.title,
}
self.add_form_def("address", cached_load("SHUUP_ADDRESS_MODEL_FORM"), kwargs=address_kwargs)
self.add_form_def("saved_address", self.saved_address_form, kwargs=saved_address_kwargs)
[docs]
def save(self):
address_form = self.forms["address"]
if self.instance:
# expect old
address = MutableAddress.objects.get(pk=self.instance.address.pk)
for k, v in six.iteritems(address_form.cleaned_data):
setattr(address, k, v)
address.save()
else:
address = address_form.save()
owner = self.request.customer
saf = self.forms["saved_address"]
saved_address, updated = SavedAddress.objects.update_or_create(
owner=owner,
address=address,
defaults={
"title": saf.cleaned_data.get("title"),
"role": saf.cleaned_data.get("role"),
"status": saf.cleaned_data.get("status"),
},
)
return saved_address
def _get_default_address_for_contact(contact, address_attr, fallback_contact):
if contact and getattr(contact, address_attr, None):
return getattr(contact, address_attr)
if fallback_contact and getattr(fallback_contact, address_attr, None):
return getattr(fallback_contact, address_attr)
return None