Repository: pretix/pretix

Issue:
Enforce currencies to be a valid ISO code
**Hey!** If you ever wanted to contribute to an Open Source project, but didn't know how, you just found a perfect place to start. We try to write this issue as instructive as possible, but as we're humans we almost certainly forgot to write something important in here. If you want to try to tackle this issue and get stuck at _any_ point, please do not hesitate and ask us, either here on GitHub or via email at support@pretix.eu. We'll be very happy to help! 

We think this issue is very suitable to people who are just starting with Python or Django development or who never contributed to Open Source before. We'd like to ask people with wide experience in these areas to leave this issue to beginners. Thanks! (If you want something more advanced, just drop us a line, we'll find something :wink:)

## How to get started

First of all, you need pretix running locally on your machine. We have a guide on the developer installation [over here](https://docs.pretix.eu/en/latest/development/setup.html). Play around with it and get a feeling for what pretix is and how it works :) Try to find the places in the interface mentioned in this issue where something needs to change. Maybe try to find the same places in the code? Just searching for characteristic words can be a powerful tool to find your way around.


## What this is about

Currently, when creating or editing an event, there is a free text field to enter a currency that accepts any inputs, for example one could enter both ``EUR`` and ``€``. However, when we communicate with APIs like PayPal, ``€`` will fail and only ``EUR`` will work. Therefore, the user also should only be able to input [ISO-4217](https://www.iso.org/iso-4217-currency-codes.html) codes.

## How to do this

Go to ``pretix/base/models/event.py`` and add a [choice attribute](https://docs.djangoproject.com/en/1.10/ref/models/fields/#choices) to the ``currency`` field of the ``Event`` model. Make sure the forms behave correctly when creating and modifying events. You could base the choices on [pycountry](https://pypi.python.org/pypi/pycountry), so we don't need to keep a list of our own.

## I want to give it a try!

You're still reading, nice! If you want to try working on this, please comment below, so everyone else knows this is being looked at and can search for a different place to contribute. 

First, [fork](https://github.com/pretix/pretix/fork) the repository to your own GitHub account. Then clone it locally (or [set it as your remote](https://help.github.com/articles/configuring-a-remote-for-a-fork/) if you already cloned it earlier) and prepare your pull requests. There are a number of tutorials on the Pull Request workflow online, for example this [short version](https://guides.github.com/introduction/flow/) by GitHub itself and this [more in-depth tutorial](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github). We're excited to see your pull request! 

And don't forget: If you get stuck, don't hestitate to ask. We bound ourselves to a [Code of Conduct](https://docs.pretix.eu/en/latest/development/contribution/codeofconduct.html) and we're committed to providing a welcoming experience and giving constructive, friendly and useful feedback on your contributions.



Discussion:
I would love to give it a shot. I've been looking for a way to get into open source but a lot of projects have a high barrier to entry. 
Hi @emersonmde, sure, go ahead and tell me if there are any problems!

Resolving patch:
diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py
--- a/src/pretix/base/models/event.py
+++ b/src/pretix/base/models/event.py
@@ -59,6 +59,7 @@ class Event(LoggedModel):
     """
 
     settings_namespace = 'event'
+    CURRENCY_CHOICES = [(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES]
     organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT)
     name = I18nCharField(
         max_length=200,
@@ -83,6 +84,7 @@ class Event(LoggedModel):
                                        related_name="events", )
     currency = models.CharField(max_length=10,
                                 verbose_name=_("Default currency"),
+                                choices=CURRENCY_CHOICES,
                                 default=settings.DEFAULT_CURRENCY)
     date_from = models.DateTimeField(verbose_name=_("Event start time"))
     date_to = models.DateTimeField(null=True, blank=True,
diff --git a/src/pretix/settings.py b/src/pretix/settings.py
--- a/src/pretix/settings.py
+++ b/src/pretix/settings.py
@@ -1,6 +1,7 @@
 import configparser
 import os
 import sys
+from pycountry import currencies
 
 import django.conf.locale
 from django.contrib.messages import constants as messages  # NOQA
@@ -85,6 +86,7 @@
                                     fallback='pretix.plugins.sendmail,pretix.plugins.statistics')
 
 DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR')
+CURRENCIES = list(currencies)
 
 ALLOWED_HOSTS = ['*']
 
diff --git a/src/setup.py b/src/setup.py
--- a/src/setup.py
+++ b/src/setup.py
@@ -98,7 +98,8 @@ def run(self):
         'chardet>=2.3,<3',
         'mt-940==3.2',
         'django-i18nfield',
-        'vobject==0.9.*'
+        'vobject==0.9.*',
+        'pycountry'
     ],
     extras_require={
         'dev': [


Test patch:
diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py
--- a/src/tests/control/test_events.py
+++ b/src/tests/control/test_events.py
@@ -386,3 +386,47 @@ def test_create_event_missing_date_from(self):
             'basics-presale_end': '2016-11-24 18:00:00',
         })
         assert doc.select(".alert-danger")
+
+    def test_create_event_currency_symbol(self):
+        doc = self.post_doc('/control/events/add', {
+            'event_wizard-current_step': 'foundation',
+            'foundation-organizer': self.orga1.pk,
+            'foundation-locales': 'en'
+        })
+
+        doc = self.post_doc('/control/events/add', {
+            'event_wizard-current_step': 'basics',
+            'basics-name_0': '33C3',
+            'basics-slug': '31c4',
+            'basics-date_from': '2016-12-27 10:00:00',
+            'basics-date_to': '2016-12-30 19:00:00',
+            'basics-location_0': 'Hamburg',
+            'basics-currency': '$',
+            'basics-locale': 'en',
+            'basics-timezone': 'Europe/Berlin',
+            'basics-presale_start': '2016-11-01 10:00:00',
+            'basics-presale_end': '2016-11-30 18:00:00',
+        })
+        assert doc.select(".alert-danger")
+
+    def test_create_event_non_iso_currency(self):
+        doc = self.post_doc('/control/events/add', {
+            'event_wizard-current_step': 'foundation',
+            'foundation-organizer': self.orga1.pk,
+            'foundation-locales': 'en'
+        })
+
+        doc = self.post_doc('/control/events/add', {
+            'event_wizard-current_step': 'basics',
+            'basics-name_0': '33C3',
+            'basics-slug': '31c5',
+            'basics-date_from': '2016-12-27 10:00:00',
+            'basics-date_to': '2016-12-30 19:00:00',
+            'basics-location_0': 'Hamburg',
+            'basics-currency': 'ASD',
+            'basics-locale': 'en',
+            'basics-timezone': 'Europe/Berlin',
+            'basics-presale_start': '2016-11-01 10:00:00',
+            'basics-presale_end': '2016-11-30 18:00:00',
+        })
+        assert doc.select(".alert-danger")