forked from CGM_Public/pretix_original
Add note field and external identifier to customers (#2605)
This commit is contained in:
committed by
GitHub
parent
657cdd07ab
commit
edff7b8717
@@ -14,6 +14,7 @@ The customer resource contains the following public fields:
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
identifier string Internal ID of the customer
|
||||
external_identifier string External ID of the customer (or ``null``)
|
||||
email string Customer email address
|
||||
name string Name of this customer (or ``null``)
|
||||
name_parts object of strings Decomposition of name (i.e. given name, family name)
|
||||
@@ -24,6 +25,7 @@ last_login datetime Date and time o
|
||||
date_joined datetime Date and time of registration
|
||||
locale string Preferred language of the customer
|
||||
last_modified datetime Date and time of modification of the record
|
||||
notes string Internal notes and comments (or ``null``)
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
.. versionadded:: 4.0
|
||||
@@ -58,6 +60,7 @@ Endpoints
|
||||
"results": [
|
||||
{
|
||||
"identifier": "8WSAJCJ",
|
||||
"external_identifier": null,
|
||||
"email": "customer@example.org",
|
||||
"name": "John Doe",
|
||||
"name_parts": {
|
||||
@@ -69,7 +72,8 @@ Endpoints
|
||||
"last_login": null,
|
||||
"date_joined": "2021-04-06T13:44:22.809216Z",
|
||||
"locale": "de",
|
||||
"last_modified": "2021-04-06T13:44:22.809377Z"
|
||||
"last_modified": "2021-04-06T13:44:22.809377Z",
|
||||
"notes": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -103,6 +107,7 @@ Endpoints
|
||||
|
||||
{
|
||||
"identifier": "8WSAJCJ",
|
||||
"external_identifier": null,
|
||||
"email": "customer@example.org",
|
||||
"name": "John Doe",
|
||||
"name_parts": {
|
||||
@@ -114,7 +119,8 @@ Endpoints
|
||||
"last_login": null,
|
||||
"date_joined": "2021-04-06T13:44:22.809216Z",
|
||||
"locale": "de",
|
||||
"last_modified": "2021-04-06T13:44:22.809377Z"
|
||||
"last_modified": "2021-04-06T13:44:22.809377Z",
|
||||
"notes": null
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
@@ -150,6 +156,7 @@ Endpoints
|
||||
|
||||
{
|
||||
"identifier": "8WSAJCJ",
|
||||
"external_identifier": null,
|
||||
"email": "test@example.org",
|
||||
...
|
||||
}
|
||||
@@ -193,6 +200,7 @@ Endpoints
|
||||
|
||||
{
|
||||
"identifier": "8WSAJCJ",
|
||||
"external_identifier": null,
|
||||
"email": "test@example.org",
|
||||
…
|
||||
}
|
||||
@@ -226,6 +234,7 @@ Endpoints
|
||||
|
||||
{
|
||||
"identifier": "8WSAJCJ",
|
||||
"external_identifier": null,
|
||||
"email": null,
|
||||
…
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ class CustomerSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Customer
|
||||
fields = ('identifier', 'email', 'name', 'name_parts', 'is_active', 'is_verified', 'last_login', 'date_joined',
|
||||
'locale', 'last_modified')
|
||||
fields = ('identifier', 'external_identifier', 'email', 'name', 'name_parts', 'is_active', 'is_verified', 'last_login', 'date_joined',
|
||||
'locale', 'last_modified', 'notes')
|
||||
|
||||
|
||||
class MembershipTypeSerializer(I18nAwareModelSerializer):
|
||||
|
||||
@@ -259,7 +259,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
payment_providers=Subquery(p_providers, output_field=CharField()),
|
||||
invoice_numbers=Subquery(i_numbers, output_field=CharField()),
|
||||
pcnt=Subquery(s, output_field=IntegerField())
|
||||
).select_related('invoice_address')
|
||||
).select_related('invoice_address', 'customer')
|
||||
|
||||
qs = self._date_filter(qs, form_data, rel='')
|
||||
|
||||
@@ -268,8 +268,8 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
tax_rates = self._get_all_tax_rates(qs)
|
||||
|
||||
headers = [
|
||||
_('Event slug'), _('Order code'), _('Order total'), _('Status'), _('Email'), _('Phone number'), _('Order date'),
|
||||
_('Order time'), _('Company'), _('Name'),
|
||||
_('Event slug'), _('Order code'), _('Order total'), _('Status'), _('Email'), _('Phone number'),
|
||||
_('Order date'), _('Order time'), _('Company'), _('Name'),
|
||||
]
|
||||
name_scheme = PERSON_NAME_SCHEMES[self.event.settings.name_scheme] if not self.is_multievent else None
|
||||
if name_scheme and len(name_scheme['fields']) > 1:
|
||||
@@ -294,6 +294,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
headers.append(_('Follow-up date'))
|
||||
headers.append(_('Positions'))
|
||||
headers.append(_('E-mail address verified'))
|
||||
headers.append(_('External customer ID'))
|
||||
headers.append(_('Payment providers'))
|
||||
if form_data.get('include_payment_amounts'):
|
||||
payment_methods = self._get_all_payment_methods(qs)
|
||||
@@ -400,6 +401,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
|
||||
row.append(order.pcnt)
|
||||
row.append(_('Yes') if order.email_known_to_work else _('No'))
|
||||
row.append(str(order.customer.external_identifier) if order.customer and order.customer.external_identifier else '')
|
||||
row.append(', '.join([
|
||||
str(self.providers.get(p, p)) for p in sorted(set((order.payment_providers or '').split(',')))
|
||||
if p and p != 'free'
|
||||
@@ -428,7 +430,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
order__event__in=self.events,
|
||||
).annotate(
|
||||
payment_providers=Subquery(p_providers, output_field=CharField()),
|
||||
).select_related('order', 'order__invoice_address', 'tax_rule')
|
||||
).select_related('order', 'order__invoice_address', 'order__customer', 'tax_rule')
|
||||
if form_data['paid_only']:
|
||||
qs = qs.filter(order__status=Order.STATUS_PAID, canceled=False)
|
||||
|
||||
@@ -459,6 +461,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
_('Address'), _('ZIP code'), _('City'), _('Country'), pgettext('address', 'State'), _('VAT ID'),
|
||||
]
|
||||
|
||||
headers.append(_('External customer ID'))
|
||||
headers.append(_('Payment providers'))
|
||||
yield headers
|
||||
|
||||
@@ -502,6 +505,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
]
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
row += [''] * (8 + (len(name_scheme['fields']) if name_scheme and len(name_scheme['fields']) > 1 else 0))
|
||||
row.append(str(order.customer.external_identifier) if order.customer and order.customer.external_identifier else '')
|
||||
row.append(', '.join([
|
||||
str(self.providers.get(p, p)) for p in sorted(set((op.payment_providers or '').split(',')))
|
||||
if p and p != 'free'
|
||||
@@ -524,7 +528,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
qs = base_qs.annotate(
|
||||
payment_providers=Subquery(p_providers, output_field=CharField()),
|
||||
).select_related(
|
||||
'order', 'order__invoice_address', 'item', 'variation',
|
||||
'order', 'order__invoice_address', 'order__customer', 'item', 'variation',
|
||||
'voucher', 'tax_rule'
|
||||
).prefetch_related(
|
||||
'answers', 'answers__question', 'answers__options'
|
||||
@@ -611,6 +615,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
headers += [
|
||||
_('Sales channel'), _('Order locale'),
|
||||
_('E-mail address verified'),
|
||||
_('External customer ID'),
|
||||
_('Payment providers'),
|
||||
]
|
||||
|
||||
@@ -730,7 +735,8 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
row += [
|
||||
order.sales_channel,
|
||||
order.locale,
|
||||
_('Yes') if order.email_known_to_work else _('No')
|
||||
_('Yes') if order.email_known_to_work else _('No'),
|
||||
str(order.customer.external_identifier) if order.customer and order.customer.external_identifier else '',
|
||||
]
|
||||
row.append(', '.join([
|
||||
str(self.providers.get(p, p)) for p in sorted(set((op.payment_providers or '').split(',')))
|
||||
|
||||
23
src/pretix/base/migrations/0214_customer_notes_ext_id.py
Normal file
23
src/pretix/base/migrations/0214_customer_notes_ext_id.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.2.12 on 2022-04-28 08:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0213_discount_condition_ignore_voucher_discounted'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customer',
|
||||
name='external_identifier',
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customer',
|
||||
name='notes',
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
]
|
||||
@@ -59,6 +59,8 @@ class Customer(LoggedModel):
|
||||
default=settings.LANGUAGE_CODE,
|
||||
verbose_name=_('Language'))
|
||||
last_modified = models.DateTimeField(auto_now=True)
|
||||
external_identifier = models.CharField(max_length=255, verbose_name=_('External identifier'), null=True, blank=True)
|
||||
notes = models.TextField(verbose_name=_('Notes'), null=True, blank=True)
|
||||
|
||||
objects = ScopedManager(organizer='organizer')
|
||||
|
||||
@@ -90,6 +92,8 @@ class Customer(LoggedModel):
|
||||
self.name_cached = ''
|
||||
self.email = None
|
||||
self.phone = None
|
||||
self.external_identifier = None
|
||||
self.notes = None
|
||||
self.save()
|
||||
self.all_logentries().update(data={}, shredded=True)
|
||||
self.orders.all().update(customer=None)
|
||||
|
||||
@@ -1265,7 +1265,8 @@ class CustomerFilterForm(FilterForm):
|
||||
orders = {
|
||||
'email': 'email',
|
||||
'identifier': 'identifier',
|
||||
'name_cached': 'name_cached',
|
||||
'name': 'name_cached',
|
||||
'external_identifier': 'external_identifier',
|
||||
}
|
||||
query = forms.CharField(
|
||||
label=_('Search query'),
|
||||
|
||||
@@ -607,7 +607,7 @@ class CustomerUpdateForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Customer
|
||||
fields = ['is_active', 'name_parts', 'email', 'is_verified', 'phone', 'locale']
|
||||
fields = ['is_active', 'external_identifier', 'name_parts', 'email', 'is_verified', 'phone', 'locale', 'notes']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -651,7 +651,7 @@ class CustomerCreateForm(CustomerUpdateForm):
|
||||
|
||||
class Meta:
|
||||
model = Customer
|
||||
fields = ['identifier', 'is_active', 'name_parts', 'email', 'is_verified', 'locale']
|
||||
fields = ['is_active', 'identifier', 'external_identifier', 'name_parts', 'email', 'is_verified', 'phone', 'locale', 'notes']
|
||||
|
||||
|
||||
class MembershipUpdateForm(forms.ModelForm):
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>#{{ customer.identifier }}</dd>
|
||||
{% if customer.external_identifier %}
|
||||
<dt>{% trans "External identifier" %}</dt>
|
||||
<dd>{{ customer.external_identifier }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>
|
||||
{% if not customer.is_active %}
|
||||
@@ -59,6 +63,10 @@
|
||||
<dt>{% trans "Last login" %}</dt>
|
||||
<dd>{% if customer.last_login %}{{ customer.last_login|date:"SHORT_DATETIME_FORMAT" }}{% else %}
|
||||
–{% endif %}</dd>
|
||||
{% if customer.notes %}
|
||||
<dt>{% trans "Notes" %}</dt>
|
||||
<dd>{{ customer.notes }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</form>
|
||||
<div class="text-right">
|
||||
|
||||
@@ -62,6 +62,9 @@
|
||||
<th>{% trans "Name" %}
|
||||
<a href="?{% url_replace request 'ordering' '-name' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'name' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
<th>{% trans "External ID" %}
|
||||
<a href="?{% url_replace request 'ordering' '-external_identifier' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'external_identifier' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -81,6 +84,7 @@
|
||||
{% if not c.is_verified %}</strike>{% endif %}
|
||||
</td>
|
||||
<td>{{ c.name }}</td>
|
||||
<td>{% if c.external_identifier %}{{ c.external_identifier }}{% endif %}</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url "control:organizer.customer" organizer=request.organizer.slug customer=c.identifier %}"
|
||||
class="btn btn-default btn-sm" data-toggle="tooltip" title="{% trans "Details" %}">
|
||||
|
||||
@@ -36,6 +36,7 @@ def customer(organizer, event):
|
||||
|
||||
TEST_CUSTOMER_RES = {
|
||||
"identifier": "8WSAJCJ",
|
||||
"external_identifier": None,
|
||||
"email": "foo@example.org",
|
||||
"name": "Foo",
|
||||
"name_parts": {
|
||||
@@ -46,7 +47,8 @@ TEST_CUSTOMER_RES = {
|
||||
"last_login": None,
|
||||
"date_joined": "2021-04-06T13:44:22.809216Z",
|
||||
"locale": "en",
|
||||
"last_modified": "2021-04-06T13:44:22.809377Z"
|
||||
"last_modified": "2021-04-06T13:44:22.809377Z",
|
||||
"notes": None,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user