![avatar]()
diff --git a/client/src/pages/onboarding/AccountProfile.vue b/client/src/pages/onboarding/AccountProfile.vue
index 31b982b8..be7e1ed4 100644
--- a/client/src/pages/onboarding/AccountProfile.vue
+++ b/client/src/pages/onboarding/AccountProfile.vue
@@ -5,7 +5,8 @@ import { computed, ref, watch } from "vue";
import { useUserStore } from "@/stores/user";
import { useRoute } from "vue-router";
import { useTranslation } from "i18next-vue";
-import { profileNextRoute, useEntities } from "@/services/onboarding";
+import { profileNextRoute } from "@/services/onboarding";
+import { useEntities } from "@/services/entities";
import AvatarImage from "@/components/ui/AvatarImage.vue";
const { t } = useTranslation();
@@ -56,7 +57,9 @@ async function avatarUpload(e: Event) {
}
watch(selectedOrganisation, async (organisation) => {
- await user.setUserOrganisation(organisation.id);
+ await user.updateUserProfile({
+ organisation: organisation.id,
+ });
});
const nextRoute = computed(() => {
diff --git a/client/src/pages/onboarding/vv/CheckoutAddress.vue b/client/src/pages/onboarding/vv/CheckoutAddress.vue
index 3fcd46f9..40875d10 100644
--- a/client/src/pages/onboarding/vv/CheckoutAddress.vue
+++ b/client/src/pages/onboarding/vv/CheckoutAddress.vue
@@ -6,7 +6,7 @@ import { useUserStore } from "@/stores/user";
import PersonalAddress from "@/components/onboarding/PersonalAddress.vue";
import OrganisationAddress from "@/components/onboarding/OrganisationAddress.vue";
import { itPost, itPut } from "@/fetchHelpers";
-import { useEntities } from "@/services/onboarding";
+import { useEntities } from "@/services/entities";
import { useDebounceFn, useFetch } from "@vueuse/core";
import { useRoute } from "vue-router";
import { useTranslation } from "i18next-vue";
@@ -237,7 +237,7 @@ const executePayment = () => {
$t("a.Fehler bei der Zahlung. Bitte versuche es erneut oder kontaktiere uns")
}}:
- vermittler@vbv-afa.ch@vbv.ch
+ vermittler@vbv-afa.ch
diff --git a/client/src/pages/personalProfile/PersonalProfilePage.vue b/client/src/pages/personalProfile/PersonalProfilePage.vue
new file mode 100644
index 00000000..01903add
--- /dev/null
+++ b/client/src/pages/personalProfile/PersonalProfilePage.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+ {{ user.first_name }} {{ user.last_name }}
+ {{ user.email }}
+
+
+
+
+
+
+
+
+
+ {{ $t("a.Deine Änderungen wurden gespeichert") }}.
+
+
+
+
+
+
diff --git a/client/src/router/index.ts b/client/src/router/index.ts
index 44eeb299..e157b92d 100644
--- a/client/src/router/index.ts
+++ b/client/src/router/index.ts
@@ -312,6 +312,11 @@ const router = createRouter({
path: "/messages",
component: () => import("@/pages/MessagesPage.vue"),
},
+ {
+ path: "/profile",
+ component: () => import("@/pages/personalProfile/PersonalProfilePage.vue"),
+ name: "personalProfile",
+ },
{
path: "/settings",
component: () => import("@/pages/SettingsPage.vue"),
diff --git a/client/src/services/entities.ts b/client/src/services/entities.ts
new file mode 100644
index 00000000..c87548c4
--- /dev/null
+++ b/client/src/services/entities.ts
@@ -0,0 +1,25 @@
+import { itGetCached } from "@/fetchHelpers";
+import type { Ref } from "vue";
+import { ref } from "vue";
+
+export type Organisation = {
+ id: number;
+ name: string;
+};
+
+export type Country = {
+ id: number;
+ name: string;
+};
+
+export function useEntities() {
+ const countries: Ref
= ref([]);
+ const organisations: Ref = ref([]);
+
+ itGetCached("/api/core/entities/").then((res) => {
+ countries.value = res.countries;
+ organisations.value = res.organisations;
+ });
+
+ return { organisations, countries };
+}
diff --git a/client/src/services/onboarding.ts b/client/src/services/onboarding.ts
index da1bdb9a..24b59a48 100644
--- a/client/src/services/onboarding.ts
+++ b/client/src/services/onboarding.ts
@@ -1,8 +1,4 @@
-import { itGetCached } from "@/fetchHelpers";
-import { useUserStore } from "@/stores/user";
import { isString, startsWith } from "lodash";
-import type { Ref } from "vue";
-import { computed, ref } from "vue";
export function profileNextRoute(courseType: string | string[]) {
if (courseType === "uk") {
@@ -14,53 +10,3 @@ export function profileNextRoute(courseType: string | string[]) {
}
return "";
}
-
-export type Organisation = {
- organisation_id: number;
- name_de: string;
- name_fr: string;
- name_it: string;
-};
-
-export type Country = {
- country_id: number;
- name_de: string;
- name_fr: string;
- name_it: string;
-};
-
-export type Entities = {
- organisations: Organisation[];
- countries: Country[];
-};
-
-export function useEntities() {
- const user = useUserStore();
- const entities: Ref = ref();
-
- itGetCached("/api/core/entities/").then((res) => {
- entities.value = res;
- });
-
- const organisations = computed(() => {
- if (entities.value) {
- return entities.value.organisations.map((c) => ({
- id: c.organisation_id,
- name: c[`name_${user.language}`],
- }));
- }
- return [];
- });
-
- const countries = computed(() => {
- if (entities.value) {
- return entities.value.countries.map((c) => ({
- id: c.country_id,
- name: c[`name_${user.language}`],
- }));
- }
- return [];
- });
-
- return { organisations, countries };
-}
diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts
index 1243cb19..548c1f51 100644
--- a/client/src/stores/user.ts
+++ b/client/src/stores/user.ts
@@ -1,40 +1,50 @@
-import log from "loglevel";
-
import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers";
import { setI18nLanguage } from "@/i18nextWrapper";
+import type { Country } from "@/services/entities";
import { directUpload } from "@/services/files";
import dayjs from "dayjs";
import { defineStore } from "pinia";
let logoutRedirectUrl = import.meta.env.VITE_LOGOUT_REDIRECT || "/";
-// TODO: check if user logged in with SSO or login-local
if (import.meta.env.VITE_OAUTH_API_BASE_URL) {
logoutRedirectUrl = `${
import.meta.env.VITE_OAUTH_API_BASE_URL
}logout/?post_logout_redirect_uri=${window.location.origin}`;
}
-// typed state https://stackoverflow.com/questions/71012513/when-using-pinia-and-typescript-how-do-you-use-an-action-to-set-the-state
+const AVAILABLE_LANGUAGES = ["de", "fr", "it"];
export type AvailableLanguages = "de" | "fr" | "it";
+export type InvoiceAddress = "prv" | "org";
-export type UserState = {
+export interface User {
id: string;
+ username: string;
first_name: string;
last_name: string;
email: string;
- username: string;
avatar_url: string;
organisation: number | null;
is_superuser: boolean;
- course_session_experts: string[];
loggedIn: boolean;
language: AvailableLanguages;
-};
+ course_session_experts: string[];
+ invoice_address: InvoiceAddress | null;
+ street: string | null;
+ street_number: string | null;
+ postal_code: string | null;
+ city: string | null;
+ country: Country | null;
+ organisation_detail_name: string | null;
+ organisation_street: string | null;
+ organisation_street_number: string | null;
+ organisation_postal_code: string | null;
+ organisation_city: string | null;
+ organisation_country: Country | null;
+}
let defaultLanguage: AvailableLanguages = "de";
-const AVAILABLE_LANGUAGES = ["de", "fr", "it"];
const isAvailableLanguage = (language: string): language is AvailableLanguages => {
return AVAILABLE_LANGUAGES.includes(language);
@@ -51,7 +61,7 @@ for (const language of languagesWithoutCountryCode) {
}
}
-const initialUserState: UserState = {
+const initialUserState: User = {
id: "",
email: "",
first_name: "",
@@ -63,6 +73,18 @@ const initialUserState: UserState = {
course_session_experts: [],
loggedIn: false,
language: defaultLanguage,
+ invoice_address: "prv",
+ street: null,
+ street_number: null,
+ postal_code: null,
+ city: null,
+ country: null,
+ organisation_detail_name: null,
+ organisation_street: null,
+ organisation_street_number: null,
+ organisation_postal_code: null,
+ organisation_city: null,
+ organisation_country: null,
};
async function setLocale(language: AvailableLanguages) {
@@ -82,7 +104,7 @@ async function setLocale(language: AvailableLanguages) {
export const useUserStore = defineStore({
id: "user",
- state: () => initialUserState as UserState,
+ state: () => initialUserState as User,
getters: {
getFullName(): string {
return `${this.first_name} ${this.last_name}`.trim();
@@ -102,24 +124,20 @@ export const useUserStore = defineStore({
},
},
actions: {
- handleLogin(username: string, password: string, next = "/") {
+ async handleLogin(username: string, password: string, next = "/") {
if (username && password) {
- itPost("/api/core/login/", {
- username,
- password,
- })
- .then((data) => {
- this.$state = data;
- this.loggedIn = true;
- log.debug("bust cache");
- bustItGetCache();
- log.debug(`redirect to ${next}`);
- window.location.href = next;
- })
- .catch(() => {
- this.loggedIn = false;
- alert("Login failed");
+ try {
+ this.$state = await itPost("/api/core/login/", {
+ username,
+ password,
});
+ this.loggedIn = true;
+ bustItGetCache();
+ window.location.href = next;
+ } catch (e) {
+ this.loggedIn = false;
+ alert("Login failed");
+ }
}
},
handleLogout() {
@@ -142,18 +160,17 @@ export const useUserStore = defineStore({
this.loggedIn = true;
await setLocale(data.language);
},
- async setUserLanguages(language: AvailableLanguages) {
- await setLocale(language);
- this.$state.language = language;
- await itPost("/api/core/me/", { language }, { method: "PUT" });
- },
- async setUserOrganisation(organisation: number) {
- this.$state.organisation = organisation;
- await itPost("/api/core/me/", { organisation }, { method: "PUT" });
- },
async setUserAvatar(file: File) {
const r = await directUpload("/api/core/avatar/", file);
this.$state.avatar_url = r.url;
},
+ async updateUserProfile(profileData: Partial) {
+ if (profileData.language) {
+ await setLocale(profileData.language);
+ }
+
+ await itPost("/api/core/me/", profileData, { method: "PUT" });
+ Object.assign(this.$state, profileData);
+ },
},
});
diff --git a/server/vbv_lernwelt/api/directory.py b/server/vbv_lernwelt/api/directory.py
index 502dae55..bc5d9673 100644
--- a/server/vbv_lernwelt/api/directory.py
+++ b/server/vbv_lernwelt/api/directory.py
@@ -2,10 +2,8 @@ from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
-from vbv_lernwelt.core.models import Organisation
-from vbv_lernwelt.core.serializers import OrganisationSerializer
-from vbv_lernwelt.shop.models import Country
-from vbv_lernwelt.shop.serializers import CountrySerializer
+from vbv_lernwelt.core.models import Country, Organisation
+from vbv_lernwelt.core.serializers import CountrySerializer, OrganisationSerializer
@api_view(["GET"])
@@ -20,8 +18,12 @@ def list_entities(request):
field_name = field_mapping.get(language_code, field_mapping["de"])
+ context = {"langauge": request.user.language}
+
organisations = OrganisationSerializer(
- Organisation.objects.all().order_by(field_name), many=True
+ Organisation.objects.all().order_by(field_name), many=True, context=context
+ ).data
+ countries = CountrySerializer(
+ Country.objects.all(), many=True, context=context
).data
- countries = CountrySerializer(Country.objects.all(), many=True).data
return Response({"organisations": organisations, "countries": countries})
diff --git a/server/vbv_lernwelt/api/tests/test_entities_api.py b/server/vbv_lernwelt/api/tests/test_entities_api.py
index df6f90a3..3ca843fc 100644
--- a/server/vbv_lernwelt/api/tests/test_entities_api.py
+++ b/server/vbv_lernwelt/api/tests/test_entities_api.py
@@ -2,9 +2,8 @@ from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
-from vbv_lernwelt.core.model_utils import add_organisations
+from vbv_lernwelt.core.model_utils import add_countries, add_organisations
from vbv_lernwelt.core.models import Organisation, User
-from vbv_lernwelt.shop.model_utils import add_countries
class EntitiesViewTest(APITestCase):
@@ -24,6 +23,9 @@ class EntitiesViewTest(APITestCase):
# GIVEN
url = reverse("list_entities")
+ self.user.language = "it"
+ self.user.save()
+
# WHEN
response = self.client.get(url)
@@ -35,10 +37,8 @@ class EntitiesViewTest(APITestCase):
self.assertEqual(
organisations[-1],
{
- "organisation_id": 28,
- "name_de": "Zürich",
- "name_fr": "Zurich",
- "name_it": "Zurigo",
+ "id": 28,
+ "name": "Zurigo",
},
)
@@ -47,9 +47,7 @@ class EntitiesViewTest(APITestCase):
self.assertEqual(
countries[0],
{
- "country_id": 1,
- "name_de": "Afghanistan",
- "name_fr": "Afghanistan",
- "name_it": "Afghanistan",
+ "id": 1,
+ "name": "Afghanistan",
},
)
diff --git a/server/vbv_lernwelt/api/tests/test_me_api.py b/server/vbv_lernwelt/api/tests/test_me_api.py
index a19fdfec..2e6442f9 100644
--- a/server/vbv_lernwelt/api/tests/test_me_api.py
+++ b/server/vbv_lernwelt/api/tests/test_me_api.py
@@ -2,9 +2,8 @@ from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
-from vbv_lernwelt.core.model_utils import add_organisations
+from vbv_lernwelt.core.model_utils import add_countries, add_organisations
from vbv_lernwelt.core.models import User
-from vbv_lernwelt.shop.model_utils import add_countries
class MeUserViewTest(APITestCase):
diff --git a/server/vbv_lernwelt/api/tests/test_profile.py b/server/vbv_lernwelt/api/tests/test_profile.py
index 4c73b50e..bc31e8ec 100644
--- a/server/vbv_lernwelt/api/tests/test_profile.py
+++ b/server/vbv_lernwelt/api/tests/test_profile.py
@@ -56,5 +56,17 @@ class ProfileViewTest(APITestCase):
"is_superuser": False,
"course_session_experts": [],
"language": "de",
+ "invoice_address": "prv",
+ "street": "",
+ "street_number": "",
+ "postal_code": "",
+ "city": "",
+ "country": None,
+ "organisation_detail_name": "",
+ "organisation_street": "",
+ "organisation_street_number": "",
+ "organisation_postal_code": "",
+ "organisation_city": "",
+ "organisation_country": None,
},
)
diff --git a/server/vbv_lernwelt/api/user.py b/server/vbv_lernwelt/api/user.py
index 4f865a8f..a1e7b44a 100644
--- a/server/vbv_lernwelt/api/user.py
+++ b/server/vbv_lernwelt/api/user.py
@@ -17,18 +17,20 @@ def me_user_view(request):
if not request.user.is_authenticated:
return Response(status=403)
+ context = {"langauge": request.user.language}
+
if request.method == "GET":
- return Response(UserSerializer(request.user).data)
+ return Response(UserSerializer(request.user, context=context).data)
if request.method == "PUT":
serializer = UserSerializer(
- request.user,
- data=request.data,
- partial=True,
+ request.user, data=request.data, partial=True, context=context
)
if serializer.is_valid():
serializer.save()
return Response(UserSerializer(request.user).data)
+ else:
+ return Response(serializer.errors, status=400)
return Response(status=400)
diff --git a/server/vbv_lernwelt/core/admin.py b/server/vbv_lernwelt/core/admin.py
index 2fb286cc..d5a98ca1 100644
--- a/server/vbv_lernwelt/core/admin.py
+++ b/server/vbv_lernwelt/core/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
from django.contrib.auth import admin as auth_admin, get_user_model
from django.utils.translation import gettext_lazy as _
-from vbv_lernwelt.core.models import JobLog, Organisation
+from vbv_lernwelt.core.models import Country, JobLog, Organisation
from vbv_lernwelt.core.utils import pretty_print_json
User = get_user_model()
@@ -43,7 +43,35 @@ class UserAdmin(auth_admin.UserAdmin):
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
- (_("Profile"), {"fields": ("organisation", "language", "avatar")}),
+ (
+ _("Profile"),
+ {
+ "fields": (
+ "language",
+ "avatar",
+ "street",
+ "street_number",
+ "postal_code",
+ "city",
+ "country",
+ "invoice_address",
+ )
+ },
+ ),
+ (
+ _("Organisation"),
+ {
+ "fields": (
+ "organisation",
+ "organisation_detail_name",
+ "organisation_street",
+ "organisation_street_number",
+ "organisation_postal_code",
+ "organisation_city",
+ "organisation_country",
+ )
+ },
+ ),
(_("Additional data"), {"fields": ("additional_json_data",)}),
)
list_display = [
@@ -89,3 +117,13 @@ class OrganisationAdmin(admin.ModelAdmin):
"name_fr",
"name_it",
)
+
+
+@admin.register(Country)
+class CountryAdmin(admin.ModelAdmin):
+ list_display = (
+ "country_id",
+ "name_de",
+ "name_fr",
+ "name_it",
+ )
diff --git a/server/vbv_lernwelt/core/migrations/0005_country.py b/server/vbv_lernwelt/core/migrations/0005_country.py
new file mode 100644
index 00000000..a3e4ac5a
--- /dev/null
+++ b/server/vbv_lernwelt/core/migrations/0005_country.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.2.20 on 2024-01-24 19:40
+
+from django.db import migrations, models
+
+from vbv_lernwelt.core.model_utils import add_countries, remove_countries
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("core", "0004_auto_20240108_0943"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="Country",
+ fields=[
+ ("country_id", models.IntegerField(primary_key=True, serialize=False)),
+ ("name_de", models.CharField(max_length=255)),
+ ("name_fr", models.CharField(max_length=255)),
+ ("name_it", models.CharField(max_length=255)),
+ ],
+ options={
+ "verbose_name": "Country",
+ "verbose_name_plural": "Countries",
+ "ordering": ["country_id"],
+ },
+ ),
+ migrations.RunPython(add_countries, remove_countries),
+ ]
diff --git a/server/vbv_lernwelt/core/migrations/0006_auto_20240125_0915.py b/server/vbv_lernwelt/core/migrations/0006_auto_20240125_0915.py
new file mode 100644
index 00000000..d78db882
--- /dev/null
+++ b/server/vbv_lernwelt/core/migrations/0006_auto_20240125_0915.py
@@ -0,0 +1,89 @@
+# Generated by Django 3.2.20 on 2024-01-25 08:15
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("core", "0005_country"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="user",
+ name="city",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="country",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="user_country",
+ to="core.country",
+ ),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="invoice_address",
+ field=models.CharField(
+ choices=[("prv", "Private"), ("org", "Organisation")],
+ default="prv",
+ max_length=3,
+ ),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="organisation_city",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="organisation_country",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="organisation_country",
+ to="core.country",
+ ),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="organisation_detail_name",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="organisation_postal_code",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="organisation_street",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="organisation_street_number",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="postal_code",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="street",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ migrations.AddField(
+ model_name="user",
+ name="street_number",
+ field=models.CharField(blank=True, max_length=255),
+ ),
+ ]
diff --git a/server/vbv_lernwelt/core/model_utils.py b/server/vbv_lernwelt/core/model_utils.py
index f363f917..4098996a 100644
--- a/server/vbv_lernwelt/core/model_utils.py
+++ b/server/vbv_lernwelt/core/model_utils.py
@@ -182,3 +182,432 @@ def migrate_avatars(apps=None, schema_editor=None):
user.save()
except FileNotFoundError:
pass
+
+
+countries = {
+ 1: {"de": "Afghanistan", "fr": "Afghanistan", "it": "Afghanistan"},
+ 2: {"de": "Albanien", "fr": "Albanie", "it": "Albania"},
+ 3: {"de": "Algerien", "fr": "Algérie", "it": "Algeria"},
+ 5: {"de": "Andorra", "fr": "Andorra", "it": "Andorra"},
+ 6: {"de": "Angola", "fr": "Angola", "it": "Angola"},
+ 9: {
+ "de": "Antigua und Barbuda",
+ "fr": "Antigua et Barbuda",
+ "it": "Antigua e Barbuda",
+ },
+ 10: {"de": "Argentinien", "fr": "Argentine", "it": "Argentina"},
+ 11: {"de": "Armenien", "fr": "Armenia", "it": "Armenia"},
+ 13: {"de": "Australien", "fr": "Australie", "it": "Australia"},
+ 14: {"de": "Österreich", "fr": "Autriche", "it": "Austria"},
+ 15: {"de": "Aserbaidschan", "fr": "Azerbaïdjan", "it": "Azerbaijan"},
+ 16: {"de": "Bahamas", "fr": "Bahamas", "it": "Bahamas"},
+ 17: {"de": "Bahrain", "fr": "Bahrain", "it": "Bahrain"},
+ 18: {"de": "Bangladesh", "fr": "Bangladesh", "it": "Bangladesh"},
+ 19: {"de": "Barbados", "fr": "Barbados", "it": "Barbados"},
+ 20: {"de": "Belarus", "fr": "Belarus", "it": "Belarus"},
+ 21: {"de": "Belgien", "fr": "Belgique", "it": "Belgio"},
+ 22: {"de": "Belize", "fr": "Belize", "it": "Belize"},
+ 23: {"de": "Benin", "fr": "Benin", "it": "Benin"},
+ 25: {"de": "Bhutan", "fr": "Bhutan", "it": "Bhutan"},
+ 26: {"de": "Bolivien", "fr": "Bolivia", "it": "Bolivia"},
+ 27: {
+ "de": "Bosnien und Herzegowina",
+ "fr": "Bosnia et Herzegowina",
+ "it": "Bosnia e Herzegovina",
+ },
+ 28: {"de": "Botswana", "fr": "Botswana", "it": "Botswana"},
+ 30: {"de": "Brasilien", "fr": "Brésil", "it": "Brasile"},
+ 32: {"de": "Brunei", "fr": "Brunei", "it": "Brunei"},
+ 33: {"de": "Bulgarien", "fr": "Bulgarie", "it": "Bulgaria"},
+ 34: {"de": "Burkina Faso", "fr": "Burkina Faso", "it": "Burkina Faso"},
+ 35: {"de": "Burundi", "fr": "Burundi", "it": "Burundi"},
+ 36: {"de": "Kambodscha", "fr": "Cambodia", "it": "Cambogia"},
+ 37: {"de": "Kamerun", "fr": "Cameroon", "it": "Camerun"},
+ 38: {"de": "Kanada", "fr": "Canada", "it": "Canada"},
+ 39: {"de": "Kap Verde", "fr": "Cap Vert", "it": "Capo Verde"},
+ 41: {
+ "de": "Zentralafrikanische Republik",
+ "fr": "Centrafricaine (République)",
+ "it": "Repubblica Centrafricana",
+ },
+ 42: {"de": "Tschad", "fr": "Tchad", "it": "Ciad"},
+ 43: {"de": "Chile", "fr": "Chile", "it": "Cile"},
+ 44: {"de": "Volksrepublik China", "fr": "Chine (Rép. pop.)", "it": "Cina"},
+ 47: {"de": "Kolumbien", "fr": "Colombia", "it": "Colombia"},
+ 48: {"de": "Komoren", "fr": "Comoros", "it": "Comoros"},
+ 49: {
+ "de": "Kongo, Republik",
+ "fr": "Congo, Republic of the",
+ "it": "Congo, Repubblica del",
+ },
+ 50: {
+ "de": "Kongo, Demokratische Republik",
+ "fr": "Congo, The Democratic Republic of the",
+ "it": "Congo, Repubblica Democratica del",
+ },
+ 51: {"de": "Grenada", "fr": "Grenade", "it": "Grenada"},
+ 52: {"de": "Costa Rica", "fr": "Costa Rica", "it": "Costa Rica"},
+ 53: {"de": "Elfenbeinküste", "fr": "Côte d´Ivoire", "it": "Costa d´Avorio"},
+ 54: {"de": "Kroatien", "fr": "Croatia", "it": "Croazia"},
+ 55: {"de": "Kuba", "fr": "Cuba", "it": "Cuba"},
+ 56: {"de": "Zypern", "fr": "Cyprus", "it": "Cipro"},
+ 57: {
+ "de": "Tschechische Republik",
+ "fr": "Czech Rebublic",
+ "it": "Repubblica Ceca",
+ },
+ 58: {"de": "Dänemark", "fr": "Danemark", "it": "Danimarca"},
+ 59: {"de": "Dschibuti", "fr": "Djibouti", "it": "Gibuti"},
+ 60: {"de": "Dominica", "fr": "Dominique", "it": "Dominica"},
+ 61: {
+ "de": "Dominikanische Republik",
+ "fr": "République Dominicaine",
+ "it": "Repubblica Dominicana",
+ },
+ 62: {"de": "Ost Timor", "fr": "Timor Oriental", "it": "Timor Est"},
+ 63: {"de": "Ecuador", "fr": "Équateur", "it": "Ecuador"},
+ 64: {"de": "Ägypten", "fr": "Égyptien", "it": "Egitto"},
+ 65: {"de": "El Salvador", "fr": "Salvador", "it": "El Salvador"},
+ 66: {
+ "de": "Äquatorialguniea",
+ "fr": "Guinée équatoriale",
+ "it": "Guinea Equatoriale",
+ },
+ 67: {"de": "Eritrea", "fr": "Érythrée", "it": "Eritrea"},
+ 68: {"de": "Estland", "fr": "Estonia", "it": "Estonia"},
+ 69: {"de": "Äthiopien", "fr": "Éthiopie", "it": "Etiopia"},
+ 72: {"de": "Fidschi-Inseln", "fr": "Iles Fidji", "it": "Isole Figi"},
+ 73: {"de": "Finnland", "fr": "Finlande", "it": "Finlandia"},
+ 74: {"de": "Frankreich", "fr": "France", "it": "Francia"},
+ 79: {"de": "Gabun", "fr": "Gabon", "it": "Gabon"},
+ 80: {"de": "Gambia", "fr": "Gambie", "it": "Gambia"},
+ 81: {"de": "Georgien", "fr": "Géorgie", "it": "Georgia"},
+ 82: {"de": "Deutschland", "fr": "Allemagne", "it": "Germania"},
+ 83: {"de": "Ghana", "fr": "Ghana", "it": "Ghana"},
+ 85: {"de": "Griechenland", "fr": "Grèce", "it": "Grecia"},
+ 90: {"de": "Guatemala", "fr": "Guatemala", "it": "Guatemala"},
+ 92: {"de": "Guinea", "fr": "Guinée", "it": "Guinea"},
+ 93: {"de": "Guinea-Bissau", "fr": "Guinée-Bissau", "it": "Guinea-Bissau"},
+ 94: {"de": "Guyana", "fr": "Guyana", "it": "Guyana"},
+ 95: {"de": "Haiti", "fr": "Haïti", "it": "Haiti"},
+ 97: {"de": "Honduras", "fr": "Honduras", "it": "Honduras"},
+ 99: {"de": "Ungarn", "fr": "Hongrie", "it": "Ungheria"},
+ 100: {"de": "Island", "fr": "Icelande", "it": "Islanda"},
+ 101: {"de": "Indien", "fr": "India", "it": "India"},
+ 102: {"de": "Indonesien", "fr": "Indonésie", "it": "Indonesia"},
+ 103: {"de": "Iran", "fr": "Iran", "it": "Iran"},
+ 104: {"de": "Irak", "fr": "Irak", "it": "Iraq"},
+ 105: {"de": "Irland", "fr": "Irlande", "it": "Irlanda"},
+ 107: {"de": "Israel", "fr": "Israël", "it": "Israele"},
+ 108: {"de": "Italien", "fr": "Italie", "it": "Italia"},
+ 109: {"de": "Jamaika", "fr": "Jamaïque", "it": "Giamaica"},
+ 110: {"de": "Japan", "fr": "Japon", "it": "Giappone"},
+ 112: {"de": "Jordanien", "fr": "Jordanie", "it": "Giordania"},
+ 113: {"de": "Kasachstan", "fr": "Kazakstan", "it": "Kazakistan"},
+ 114: {"de": "Kenia", "fr": "Kénia", "it": "Kenia"},
+ 115: {"de": "Kiribati", "fr": "Kiribati", "it": "Kiribati"},
+ 116: {
+ "de": "Korea, Demokratische Volksrepublik",
+ "fr": "Corée du Nord",
+ "it": "Corea, Repubblica Popolare Democratica",
+ },
+ 117: {
+ "de": "Korea, Republik (auch: Südkorea)",
+ "fr": "Corée du Sud",
+ "it": "Corea, Repubblica (anche: Corea del Sud)",
+ },
+ 118: {"de": "Kuwait", "fr": "Koweït", "it": "Kuwait"},
+ 119: {"de": "Kirgisistan", "fr": "Kirgistan", "it": "Kirghizistan"},
+ 120: {"de": "Laos", "fr": "Laos", "it": "Laos"},
+ 121: {"de": "Lettland", "fr": "Lettonie", "it": "Lettonia"},
+ 122: {"de": "Libanon", "fr": "Lebanon", "it": "Libano"},
+ 123: {"de": "Lesotho", "fr": "Lesotho", "it": "Lesotho"},
+ 124: {"de": "Liberia", "fr": "Liberia", "it": "Liberia"},
+ 125: {"de": "Libyen", "fr": "Libye", "it": "Libia"},
+ 126: {"de": "Liechtenstein", "fr": "Liechtenstein", "it": "Liechtenstein"},
+ 127: {"de": "Litauen", "fr": "Lituanie", "it": "Lituania"},
+ 128: {"de": "Luxembourg", "fr": "Luxembourg", "it": "Lussemburgo"},
+ 130: {
+ "de": "Nordmazedonien",
+ "fr": "Macédoine du Nord",
+ "it": "Macedonia del Nord",
+ },
+ 131: {"de": "Madagaskar", "fr": "Madagascar", "it": "Madagascar"},
+ 132: {"de": "Malawi", "fr": "Malawi", "it": "Malawi"},
+ 133: {"de": "Malaysia", "fr": "Malaisie", "it": "Malesia"},
+ 134: {"de": "Malediven", "fr": "Maldives", "it": "Maldive"},
+ 135: {"de": "Mali", "fr": "Mali", "it": "Mali"},
+ 136: {"de": "Malta", "fr": "Malte", "it": "Malta"},
+ 137: {"de": "Marshall Inseln", "fr": "Iles Marshall", "it": "Isole Marshall"},
+ 139: {"de": "Mauretanien", "fr": "Mauritanie", "it": "Mauritania"},
+ 140: {"de": "Mauritius", "fr": "Ile Maurice", "it": "Mauritius"},
+ 142: {"de": "Mexico", "fr": "Mexique", "it": "Messico"},
+ 143: {"de": "Mikronesien", "fr": "Micronésie", "it": "Micronesia"},
+ 144: {"de": "Moldavien", "fr": "Moldavie", "it": "Moldova"},
+ 145: {"de": "Monaco", "fr": "Monaco", "it": "Monaco"},
+ 146: {"de": "Mongolei", "fr": "Mongolie", "it": "Mongolia"},
+ 148: {"de": "Marokko", "fr": "Morocco", "it": "Marocco"},
+ 149: {"de": "Mosambik", "fr": "Mozambique", "it": "Mozambico"},
+ 150: {"de": "Myanmar", "fr": "Myanmar", "it": "Myanmar"},
+ 151: {"de": "Namibia", "fr": "Namibie", "it": "Namibia"},
+ 152: {"de": "Nauru", "fr": "Nauru", "it": "Nauru"},
+ 153: {"de": "Nepal", "fr": "Népal", "it": "Nepal"},
+ 154: {"de": "Niederlande", "fr": "Pays-Bas", "it": "Paesi Bassi"},
+ 157: {"de": "Neuseeland", "fr": "Nouvelle-Zélande", "it": "Nuova Zelanda"},
+ 158: {"de": "Nicaragua", "fr": "Nicaragua", "it": "Nicaragua"},
+ 159: {"de": "Niger", "fr": "Niger", "it": "Niger"},
+ 160: {"de": "Nigeria", "fr": "Nigeria", "it": "Nigeria"},
+ 164: {"de": "Norwegen", "fr": "Norvège", "it": "Norvegia"},
+ 165: {"de": "Oman", "fr": "Oman", "it": "Oman"},
+ 166: {"de": "Pakistan", "fr": "Pakistan", "it": "Pakistan"},
+ 167: {"de": "Palau", "fr": "Palau", "it": "Palau"},
+ 168: {"de": "Panama", "fr": "Panama", "it": "Panama"},
+ 170: {
+ "de": "Papua-Neuguinea",
+ "fr": "Papouasie Nouvelle-Guinée",
+ "it": "Papua Nuova Guinea",
+ },
+ 171: {"de": "Paraguay", "fr": "Paraguay", "it": "Paraguay"},
+ 172: {"de": "Peru", "fr": "Pérou", "it": "Perù"},
+ 173: {"de": "Philippinen", "fr": "Philippines", "it": "Filippine"},
+ 175: {"de": "Polen", "fr": "Pologne", "it": "Polonia"},
+ 176: {"de": "Portugal", "fr": "Portugal", "it": "Portogallo"},
+ 178: {"de": "Katar", "fr": "Qatar", "it": "Qatar"},
+ 180: {"de": "Rumänien", "fr": "Roumanie", "it": "Romania"},
+ 181: {"de": "Russische Föderation", "fr": "Russie", "it": "Russia"},
+ 182: {"de": "Ruanda", "fr": "Ruanda", "it": "Ruanda"},
+ 183: {
+ "de": "Saint Kitts und Nevis",
+ "fr": "Saint-Kitts-et-Nevis",
+ "it": "Saint Kitts e Nevis",
+ },
+ 184: {"de": "St. Lucia", "fr": "Sainte-Lucie", "it": "Santa Lucia"},
+ 185: {
+ "de": "St. Vincent und die Grenadinen",
+ "fr": "Saint-Vincent-et-Les Grenadines",
+ "it": "Saint Vincent e Grenadine",
+ },
+ 186: {"de": "Samoa", "fr": "Samoa", "it": "Samoa"},
+ 187: {"de": "San Marino", "fr": "San Marino", "it": "San Marino"},
+ 188: {
+ "de": "Sao Tome und Principe",
+ "fr": "Sao Tomé-et-Principe",
+ "it": "São Tomé e Principe",
+ },
+ 189: {"de": "Saudi-Arabien", "fr": "Arabie Saoudite", "it": "Arabia Saudita"},
+ 190: {"de": "Senegal", "fr": "Sénégal", "it": "Senegal"},
+ 191: {"de": "Seychellen", "fr": "Seychelles", "it": "Seychelles"},
+ 192: {"de": "Sierra Leone", "fr": "Sierra Leone", "it": "Sierra Leone"},
+ 193: {"de": "Singapur", "fr": "Singapour", "it": "Singapore"},
+ 194: {"de": "Slowakei", "fr": "Slovaquie", "it": "Slovacchia"},
+ 195: {"de": "Slowenien", "fr": "Slovénie", "it": "Slovenia"},
+ 196: {"de": "Salomonen", "fr": "Iles Salomon", "it": "Salomone"},
+ 197: {"de": "Somalia", "fr": "Somalie", "it": "Somalia"},
+ 198: {"de": "Südafrika", "fr": "Afrique du Sud", "it": "Africa del Sud"},
+ 200: {"de": "Spanien", "fr": "Espagne", "it": "Spagna"},
+ 201: {"de": "Sri Lanka", "fr": "Sri Lanka", "it": "Sri Lanka"},
+ 204: {"de": "Sudan", "fr": "Soudan", "it": "Sudan"},
+ 205: {"de": "Suriname", "fr": "Suriname", "it": "Suriname"},
+ 207: {"de": "Swasiland", "fr": "Swaziland", "it": "Swaziland"},
+ 208: {"de": "Schweden", "fr": "Suède", "it": "Svezia"},
+ 209: {"de": "Schweiz", "fr": "Suisse", "it": "Svizzera"},
+ 210: {"de": "Syrien", "fr": "Syrie", "it": "Siria"},
+ 211: {"de": "Taiwan", "fr": "Taïwan", "it": "Taiwan"},
+ 212: {"de": "Tadschikistan", "fr": "Tadjikistan", "it": "Tagikistan"},
+ 213: {"de": "Tansania", "fr": "Tanzanie", "it": "Tanzania"},
+ 214: {"de": "Thailand", "fr": "Thaïlande", "it": "Tailandia"},
+ 215: {"de": "Togo", "fr": "Togo", "it": "Togo"},
+ 217: {"de": "Tonga", "fr": "Tonga", "it": "Tonga"},
+ 218: {
+ "de": "Trinidad und Tobago",
+ "fr": "Trinité-et-Tobago",
+ "it": "Trinidad e Tobago",
+ },
+ 219: {"de": "Tunesien", "fr": "Tunisie", "it": "Tunisia"},
+ 220: {"de": "Türkei", "fr": "Turchia", "it": "Turchia"},
+ 221: {"de": "Turkmenistan", "fr": "Turkménistan", "it": "Turkmenistan"},
+ 223: {"de": "Tuvalu", "fr": "Tuvalu", "it": "Tuvalu"},
+ 224: {"de": "Uganda", "fr": "Ouganda", "it": "Uganda"},
+ 225: {"de": "Ukraine", "fr": "Ukraine", "it": "Ucraina"},
+ 226: {
+ "de": "Vereinigte Arabische Emirate",
+ "fr": "Émirats Arabes Unis",
+ "it": "Emirati Arabi Uniti",
+ },
+ 227: {"de": "Großbritannien", "fr": "Royaume-Uni", "it": "Regno Unito"},
+ 228: {"de": "USA", "fr": "États-Unis", "it": "Stati Uniti d´ America"},
+ 230: {"de": "Uruguay", "fr": "Uruguay", "it": "Uruguay"},
+ 231: {"de": "Usbekistan", "fr": "Ouzbékistan", "it": "Uzbekistan"},
+ 232: {"de": "Vanuatu", "fr": "Vanuatu", "it": "Vanuatu"},
+ 233: {"de": "Vatikanstadt", "fr": "Vatican", "it": "Città del Vaticano"},
+ 234: {"de": "Venezuela", "fr": "Venezuela", "it": "Venezuela"},
+ 235: {"de": "Vietnam", "fr": "Viêtnam", "it": "Vietnam"},
+ 239: {"de": "Sahara", "fr": "Sahara", "it": "Sahara"},
+ 240: {"de": "Jemen", "fr": "Yémen", "it": "Yemen"},
+ 241: {"de": "Serbien", "fr": "Serbie", "it": "Serbia"},
+ 242: {"de": "Montenegro", "fr": "Monténégro", "it": "Montenegro"},
+ 243: {"de": "Sambia", "fr": "Zambie", "it": "Zambia"},
+ 244: {"de": "Simbabwe", "fr": "Zimbabwe", "it": "Zimbabwe"},
+ 245: {"de": "Hong Kong", "fr": "Hong Kong", "it": "Hong Kong"},
+ 246: {"de": "Falkland Inseln", "fr": "Îles Malouines", "it": "Isole Falkland"},
+ 247: {"de": "Aruba", "fr": "Aruba", "it": "Aruba"},
+ 248: {"de": "Bermuda", "fr": "Bermudes", "it": "Bermuda"},
+ 249: {
+ "de": "Britische Jungferninseln",
+ "fr": "Îles Vierges britanniques",
+ "it": "Isole Vergini britanniche",
+ },
+ 250: {"de": "Curaçao", "fr": "Curaçao", "it": "Curaçao"},
+ 251: {"de": "Anguilla", "fr": "Anguilla", "it": "Anguilla"},
+ 252: {"de": "Montserrat", "fr": "Montserrat", "it": "Montserrat"},
+ 253: {
+ "de": "Bonaire, Sint Eustatius und Saba",
+ "fr": "Bonaire, Saint-Eustache et Saba",
+ "it": "Bonaire, Sint Eustatius e Saba",
+ },
+ 254: {"de": "Cayman Inseln", "fr": "Îles Caïmans", "it": "Isole Cayman"},
+ 255: {"de": "Sint Maarten", "fr": "Saint-Martin", "it": "Sint Maarten"},
+ 256: {
+ "de": "Turks- und Caicos-Inseln",
+ "fr": "Îles Turks et Caïques",
+ "it": "Turks e Caicos",
+ },
+ 257: {"de": "Saint-Barth", "fr": "Saint-Barthélemy", "it": "Saint-Barth"},
+ 258: {
+ "de": "Palästinensisches Gebiet",
+ "fr": "Territoires palestiniens occupés",
+ "it": "Territori palestinesi",
+ },
+ 259: {"de": "Kosovo", "fr": "Kosovo", "it": "Kosovo"},
+ 260: {"de": "Gibraltar", "fr": "Gibraltar", "it": "Gibilterra"},
+ 261: {"de": "Neukaledonien", "fr": "Nouvelle-Calédonie", "it": "Nuova Caledonia"},
+ 262: {
+ "de": "Französisch-Polynesien",
+ "fr": "Polynésie française",
+ "it": "Polinesia francese",
+ },
+ 310: {
+ "de": "Niederländische Antillen",
+ "fr": "Antilles néerlandaises",
+ "it": "Antille olandesi",
+ },
+ 311: {"de": "Antarktika", "fr": "Antarctique", "it": "Antartide"},
+ 312: {
+ "de": "Amerikanisch-Samoa",
+ "fr": "Samoa américaines",
+ "it": "Samoa americane",
+ },
+ 313: {"de": "Åland", "fr": "Åland", "it": "Åland"},
+ 314: {"de": "Bouvetinsel", "fr": "Île Bouvet", "it": "Isola Bouvet"},
+ 315: {"de": "Kokosinseln", "fr": "Îles Cocos", "it": "Isole Cocos (Keeling)"},
+ 316: {"de": "Cookinseln", "fr": "Îles Cook", "it": "Isole Cook"},
+ 317: {
+ "de": "Clipperton-Insel",
+ "fr": "Île de Clipperton",
+ "it": "Isola di Clipperton",
+ },
+ 318: {"de": "Weihnachtsinsel", "fr": "Île Christmas", "it": "Isola di Natale"},
+ 319: {"de": "Färöer-Inseln", "fr": "Îles Féroé", "it": "Isole Färöer"},
+ 320: {
+ "de": "Französisch-Guayana",
+ "fr": "Guyane française",
+ "it": "Guyana francese",
+ },
+ 321: {"de": "Guernsey", "fr": "Guernsey", "it": "Guernsey"},
+ 322: {"de": "Grönland", "fr": "Groenland", "it": "Groenlandia"},
+ 323: {"de": "Guadeloupe", "fr": "Guadeloupe", "it": "Guadalupa"},
+ 324: {
+ "de": "Südgeorgien und die Südlichen Sandwichinseln",
+ "fr": "Géorgie du Sud et Îles Sandwich du Sud",
+ "it": "Georgia del Sud e Sandwich Australi",
+ },
+ 325: {"de": "Guam", "fr": "Guam", "it": "Guam"},
+ 326: {
+ "de": "Heard und McDonaldinseln",
+ "fr": "Îles Heard et McDonald",
+ "it": "Isola Heard e Isole McDonald",
+ },
+ 327: {"de": "Insel Man", "fr": "Île de Man", "it": "Isola di Man"},
+ 328: {
+ "de": "Britisches Territorium im Indischen Ozean",
+ "fr": "Territoire britannique de l´océan Indien",
+ "it": "Territori Britannici dell´Oceano Indiano",
+ },
+ 329: {"de": "Jersey", "fr": "Jersey", "it": "Jersey"},
+ 330: {"de": "Saint-Martin", "fr": "Saint-Martin", "it": "Saint Martin"},
+ 331: {"de": "Macau", "fr": "Macao", "it": "Macao"},
+ 332: {
+ "de": "Nördliche Marianen",
+ "fr": "Îles Mariannes du Nord",
+ "it": "Isole Marianne Settentrionali",
+ },
+ 333: {"de": "Martinique", "fr": "Martinique", "it": "Martinica"},
+ 334: {"de": "Norfolkinsel", "fr": "Île Norfolk", "it": "Isola Norfolk"},
+ 335: {"de": "Niue", "fr": "Niue", "it": "Niue"},
+ 336: {
+ "de": "Saint-Pierre und Miquelon",
+ "fr": "Saint-Pierre-et-Miquelon",
+ "it": "Saint-Pierre e Miquelon",
+ },
+ 337: {"de": "Pitcairninseln", "fr": "Îles Pitcairn", "it": "Isole Pitcairn"},
+ 338: {"de": "Puerto Rico", "fr": "Porto Rico", "it": "Porto Rico"},
+ 339: {"de": "La Réunion", "fr": "La Réunion", "it": "Isola della Riunione"},
+ 340: {
+ "de": "St. Helena, Ascension und Tristan da Cunha",
+ "fr": "Sainte-Hélène, Ascension et Tristan da Cunha",
+ "it": "Sant´Elena, Ascensione e Tristan da Cunha",
+ },
+ 341: {
+ "de": "Spitzbergen, Jan Mayen",
+ "fr": "Spitzberg, Jan Mayen",
+ "it": "Svalbard e Jan Mayen",
+ },
+ 342: {"de": "Südsudan", "fr": "Sud-Soudan", "it": "Sudan del Sud"},
+ 343: {
+ "de": "Französische Süd- und Antarktisgebiete",
+ "fr": "Terres australes et antarctiques françaises",
+ "it": "Territori australi e antartico francese",
+ },
+ 344: {"de": "Tokelau", "fr": "Tokelau", "it": "Tokelau"},
+ 345: {
+ "de": "United States Minor Outlying Islands",
+ "fr": "Îles mineures éloignées des États-Unis",
+ "it": "Isole Minori Esterne degli Stati Uniti",
+ },
+ 346: {
+ "de": "Amerikanische Jungferninseln",
+ "fr": "Îles Vierges américaines",
+ "it": "Isole Vergini Americane",
+ },
+ 347: {"de": "Wallis und Futuna", "fr": "Wallis et Futuna", "it": "Wallis e Futuna"},
+ 348: {"de": "Mayotte", "fr": "Mayotte", "it": "Mayotte"},
+}
+
+
+def add_countries(apps=None, schema_editor=None):
+ if apps is None:
+ # pylint: disable=import-outside-toplevel
+ from vbv_lernwelt.core.models import Country
+ else:
+ Country = apps.get_model("core", "Country")
+
+ for country_id, country_name in countries.items():
+ Country.objects.get_or_create(
+ country_id=country_id,
+ name_de=country_name["de"],
+ name_fr=country_name["fr"],
+ name_it=country_name["it"],
+ )
+
+
+def remove_countries(apps=None, schema_editor=None):
+ if apps is None:
+ # pylint: disable=import-outside-toplevel
+ from vbv_lernwelt.core.models import Country
+ else:
+ Country = apps.get_model("core", "Country")
+
+ for country_id in countries.keys():
+ Country.objects.filter(
+ country_id=country_id,
+ ).delete()
diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py
index d8a9bb2c..1221dee3 100644
--- a/server/vbv_lernwelt/core/models.py
+++ b/server/vbv_lernwelt/core/models.py
@@ -21,6 +21,21 @@ class Organisation(models.Model):
ordering = ["organisation_id"]
+class Country(models.Model):
+ country_id = models.IntegerField(primary_key=True)
+ name_de = models.CharField(max_length=255)
+ name_fr = models.CharField(max_length=255)
+ name_it = models.CharField(max_length=255)
+
+ def __str__(self):
+ return f"{self.name_de} ({self.country_id})"
+
+ class Meta:
+ verbose_name = "Country"
+ verbose_name_plural = "Countries"
+ ordering = ["country_id"]
+
+
class User(AbstractUser):
"""
Default custom user model for VBV Lernwelt.
@@ -33,6 +48,14 @@ class User(AbstractUser):
("it", "Italiano"),
)
+ INVOICE_ADDRESS_PRIVATE = "prv"
+ INVOICE_ADDRESS_ORGANISATION = "org"
+
+ INVOICE_ADDRESS_CHOICES = (
+ (INVOICE_ADDRESS_PRIVATE, "Private"),
+ (INVOICE_ADDRESS_ORGANISATION, "Organisation"),
+ )
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
avatar = models.ForeignKey(
@@ -54,6 +77,35 @@ class User(AbstractUser):
Organisation, on_delete=models.SET_NULL, null=True, blank=True
)
+ invoice_address = models.CharField(
+ max_length=3, choices=INVOICE_ADDRESS_CHOICES, default="prv"
+ )
+
+ street = models.CharField(max_length=255, blank=True)
+ street_number = models.CharField(max_length=255, blank=True)
+ postal_code = models.CharField(max_length=255, blank=True)
+ city = models.CharField(max_length=255, blank=True)
+ country = models.ForeignKey(
+ Country,
+ related_name="user_country",
+ on_delete=models.SET_NULL,
+ null=True,
+ blank=True,
+ )
+
+ organisation_detail_name = models.CharField(max_length=255, blank=True)
+ organisation_street = models.CharField(max_length=255, blank=True)
+ organisation_street_number = models.CharField(max_length=255, blank=True)
+ organisation_postal_code = models.CharField(max_length=255, blank=True)
+ organisation_city = models.CharField(max_length=255, blank=True)
+ organisation_country = models.ForeignKey(
+ Country,
+ related_name="organisation_country",
+ on_delete=models.SET_NULL,
+ null=True,
+ blank=True,
+ )
+
@property
def avatar_url(self):
if self.avatar:
diff --git a/server/vbv_lernwelt/core/serializers.py b/server/vbv_lernwelt/core/serializers.py
index 31483733..e836dd52 100644
--- a/server/vbv_lernwelt/core/serializers.py
+++ b/server/vbv_lernwelt/core/serializers.py
@@ -3,7 +3,7 @@ from typing import List
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
-from vbv_lernwelt.core.models import Organisation, User
+from vbv_lernwelt.core.models import Country, Organisation, User
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
@@ -13,8 +13,39 @@ def create_json_from_objects(objects, serializer_class, many=True) -> str:
return JSONRenderer().render(serializer.data).decode("utf-8")
+class CountrySerializer(serializers.ModelSerializer):
+ id = serializers.IntegerField(source="country_id", read_only=True)
+ name = serializers.SerializerMethodField()
+
+ class Meta:
+ model = Country
+ fields = ["id", "name"]
+
+ def get_name(self, obj):
+ language = self.context.get("langauge")
+
+ if language == "fr":
+ return obj.name_fr
+ elif language == "it":
+ return obj.name_it
+
+ return obj.name_de
+
+ def to_internal_value(self, data):
+ country_id = data.get("id")
+ if country_id is not None:
+ try:
+ country = Country.objects.get(country_id=country_id)
+ return {"id": country.country_id, "name": self.get_name(country)}
+ except Country.DoesNotExist:
+ raise serializers.ValidationError({"id": "Invalid country ID"})
+ return super().to_internal_value(data)
+
+
class UserSerializer(serializers.ModelSerializer):
course_session_experts = serializers.SerializerMethodField()
+ country = CountrySerializer()
+ organisation_country = CountrySerializer()
class Meta:
model = User
@@ -29,6 +60,18 @@ class UserSerializer(serializers.ModelSerializer):
"is_superuser",
"course_session_experts",
"language",
+ "invoice_address",
+ "street",
+ "street_number",
+ "postal_code",
+ "city",
+ "country",
+ "organisation_detail_name",
+ "organisation_street",
+ "organisation_street_number",
+ "organisation_postal_code",
+ "organisation_city",
+ "organisation_country",
]
read_only_fields = [
"id",
@@ -54,8 +97,43 @@ class UserSerializer(serializers.ModelSerializer):
return [str(_id) for _id in (supervisor_in_session_ids | expert_in_session_ids)]
+ def update(self, instance, validated_data):
+ country_data = validated_data.pop("country", None)
+ organisation_country_data = validated_data.pop("organisation_country", None)
+
+ for attr, value in validated_data.items():
+ setattr(instance, attr, value)
+
+ if country_data is not None:
+ country_id = country_data.get("id")
+ country_instance = Country.objects.filter(country_id=country_id).first()
+ instance.country = country_instance
+
+ if organisation_country_data is not None:
+ organisation_country_id = organisation_country_data.get("id")
+ organisation_country_instance = Country.objects.filter(
+ country_id=organisation_country_id
+ ).first()
+ instance.organisation_country = organisation_country_instance
+
+ instance.save()
+ return instance
+
class OrganisationSerializer(serializers.ModelSerializer):
+ id = serializers.IntegerField(source="organisation_id", read_only=True)
+ name = serializers.SerializerMethodField()
+
class Meta:
model = Organisation
- fields = "__all__"
+ fields = ["id", "name"]
+
+ def get_name(self, obj):
+ language = self.context.get("langauge")
+
+ if language == "fr":
+ return obj.name_fr
+ elif language == "it":
+ return obj.name_it
+
+ return obj.name_de
diff --git a/server/vbv_lernwelt/shop/admin.py b/server/vbv_lernwelt/shop/admin.py
index 4bbf4168..e3330232 100644
--- a/server/vbv_lernwelt/shop/admin.py
+++ b/server/vbv_lernwelt/shop/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
-from vbv_lernwelt.shop.models import CheckoutInformation, Country, Product
+from vbv_lernwelt.shop.models import CheckoutInformation, Product
from vbv_lernwelt.shop.services import get_transaction_state
@@ -37,16 +37,6 @@ class CheckoutInformationAdmin(admin.ModelAdmin):
actions = [generate_invoice, sync_transaction_state]
-@admin.register(Country)
-class CountryAdmin(admin.ModelAdmin):
- list_display = (
- "country_id",
- "name_de",
- "name_fr",
- "name_it",
- )
-
-
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = (
diff --git a/server/vbv_lernwelt/shop/migrations/0003_auto_20231114_2036.py b/server/vbv_lernwelt/shop/migrations/0003_auto_20231114_2036.py
index 2757d89b..fdc29b1a 100644
--- a/server/vbv_lernwelt/shop/migrations/0003_auto_20231114_2036.py
+++ b/server/vbv_lernwelt/shop/migrations/0003_auto_20231114_2036.py
@@ -2,8 +2,6 @@
from django.db import migrations, models
-from vbv_lernwelt.shop.model_utils import add_countries, remove_countries
-
class Migration(migrations.Migration):
dependencies = [
@@ -39,5 +37,4 @@ class Migration(migrations.Migration):
name="sku",
field=models.CharField(max_length=255, primary_key=True, serialize=False),
),
- migrations.RunPython(add_countries, remove_countries),
]
diff --git a/server/vbv_lernwelt/shop/migrations/0012_delete_country.py b/server/vbv_lernwelt/shop/migrations/0012_delete_country.py
new file mode 100644
index 00000000..d36492ed
--- /dev/null
+++ b/server/vbv_lernwelt/shop/migrations/0012_delete_country.py
@@ -0,0 +1,15 @@
+# Generated by Django 3.2.20 on 2024-01-24 19:40
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("shop", "0011_alter_checkoutinformation_state"),
+ ]
+
+ operations = [
+ migrations.DeleteModel(
+ name="Country",
+ ),
+ ]
diff --git a/server/vbv_lernwelt/shop/model_utils.py b/server/vbv_lernwelt/shop/model_utils.py
deleted file mode 100644
index ec959b86..00000000
--- a/server/vbv_lernwelt/shop/model_utils.py
+++ /dev/null
@@ -1,427 +0,0 @@
-countries = {
- 1: {"de": "Afghanistan", "fr": "Afghanistan", "it": "Afghanistan"},
- 2: {"de": "Albanien", "fr": "Albanie", "it": "Albania"},
- 3: {"de": "Algerien", "fr": "Algérie", "it": "Algeria"},
- 5: {"de": "Andorra", "fr": "Andorra", "it": "Andorra"},
- 6: {"de": "Angola", "fr": "Angola", "it": "Angola"},
- 9: {
- "de": "Antigua und Barbuda",
- "fr": "Antigua et Barbuda",
- "it": "Antigua e Barbuda",
- },
- 10: {"de": "Argentinien", "fr": "Argentine", "it": "Argentina"},
- 11: {"de": "Armenien", "fr": "Armenia", "it": "Armenia"},
- 13: {"de": "Australien", "fr": "Australie", "it": "Australia"},
- 14: {"de": "Österreich", "fr": "Autriche", "it": "Austria"},
- 15: {"de": "Aserbaidschan", "fr": "Azerbaïdjan", "it": "Azerbaijan"},
- 16: {"de": "Bahamas", "fr": "Bahamas", "it": "Bahamas"},
- 17: {"de": "Bahrain", "fr": "Bahrain", "it": "Bahrain"},
- 18: {"de": "Bangladesh", "fr": "Bangladesh", "it": "Bangladesh"},
- 19: {"de": "Barbados", "fr": "Barbados", "it": "Barbados"},
- 20: {"de": "Belarus", "fr": "Belarus", "it": "Belarus"},
- 21: {"de": "Belgien", "fr": "Belgique", "it": "Belgio"},
- 22: {"de": "Belize", "fr": "Belize", "it": "Belize"},
- 23: {"de": "Benin", "fr": "Benin", "it": "Benin"},
- 25: {"de": "Bhutan", "fr": "Bhutan", "it": "Bhutan"},
- 26: {"de": "Bolivien", "fr": "Bolivia", "it": "Bolivia"},
- 27: {
- "de": "Bosnien und Herzegowina",
- "fr": "Bosnia et Herzegowina",
- "it": "Bosnia e Herzegovina",
- },
- 28: {"de": "Botswana", "fr": "Botswana", "it": "Botswana"},
- 30: {"de": "Brasilien", "fr": "Brésil", "it": "Brasile"},
- 32: {"de": "Brunei", "fr": "Brunei", "it": "Brunei"},
- 33: {"de": "Bulgarien", "fr": "Bulgarie", "it": "Bulgaria"},
- 34: {"de": "Burkina Faso", "fr": "Burkina Faso", "it": "Burkina Faso"},
- 35: {"de": "Burundi", "fr": "Burundi", "it": "Burundi"},
- 36: {"de": "Kambodscha", "fr": "Cambodia", "it": "Cambogia"},
- 37: {"de": "Kamerun", "fr": "Cameroon", "it": "Camerun"},
- 38: {"de": "Kanada", "fr": "Canada", "it": "Canada"},
- 39: {"de": "Kap Verde", "fr": "Cap Vert", "it": "Capo Verde"},
- 41: {
- "de": "Zentralafrikanische Republik",
- "fr": "Centrafricaine (République)",
- "it": "Repubblica Centrafricana",
- },
- 42: {"de": "Tschad", "fr": "Tchad", "it": "Ciad"},
- 43: {"de": "Chile", "fr": "Chile", "it": "Cile"},
- 44: {"de": "Volksrepublik China", "fr": "Chine (Rép. pop.)", "it": "Cina"},
- 47: {"de": "Kolumbien", "fr": "Colombia", "it": "Colombia"},
- 48: {"de": "Komoren", "fr": "Comoros", "it": "Comoros"},
- 49: {
- "de": "Kongo, Republik",
- "fr": "Congo, Republic of the",
- "it": "Congo, Repubblica del",
- },
- 50: {
- "de": "Kongo, Demokratische Republik",
- "fr": "Congo, The Democratic Republic of the",
- "it": "Congo, Repubblica Democratica del",
- },
- 51: {"de": "Grenada", "fr": "Grenade", "it": "Grenada"},
- 52: {"de": "Costa Rica", "fr": "Costa Rica", "it": "Costa Rica"},
- 53: {"de": "Elfenbeinküste", "fr": "Côte d´Ivoire", "it": "Costa d´Avorio"},
- 54: {"de": "Kroatien", "fr": "Croatia", "it": "Croazia"},
- 55: {"de": "Kuba", "fr": "Cuba", "it": "Cuba"},
- 56: {"de": "Zypern", "fr": "Cyprus", "it": "Cipro"},
- 57: {
- "de": "Tschechische Republik",
- "fr": "Czech Rebublic",
- "it": "Repubblica Ceca",
- },
- 58: {"de": "Dänemark", "fr": "Danemark", "it": "Danimarca"},
- 59: {"de": "Dschibuti", "fr": "Djibouti", "it": "Gibuti"},
- 60: {"de": "Dominica", "fr": "Dominique", "it": "Dominica"},
- 61: {
- "de": "Dominikanische Republik",
- "fr": "République Dominicaine",
- "it": "Repubblica Dominicana",
- },
- 62: {"de": "Ost Timor", "fr": "Timor Oriental", "it": "Timor Est"},
- 63: {"de": "Ecuador", "fr": "Équateur", "it": "Ecuador"},
- 64: {"de": "Ägypten", "fr": "Égyptien", "it": "Egitto"},
- 65: {"de": "El Salvador", "fr": "Salvador", "it": "El Salvador"},
- 66: {
- "de": "Äquatorialguniea",
- "fr": "Guinée équatoriale",
- "it": "Guinea Equatoriale",
- },
- 67: {"de": "Eritrea", "fr": "Érythrée", "it": "Eritrea"},
- 68: {"de": "Estland", "fr": "Estonia", "it": "Estonia"},
- 69: {"de": "Äthiopien", "fr": "Éthiopie", "it": "Etiopia"},
- 72: {"de": "Fidschi-Inseln", "fr": "Iles Fidji", "it": "Isole Figi"},
- 73: {"de": "Finnland", "fr": "Finlande", "it": "Finlandia"},
- 74: {"de": "Frankreich", "fr": "France", "it": "Francia"},
- 79: {"de": "Gabun", "fr": "Gabon", "it": "Gabon"},
- 80: {"de": "Gambia", "fr": "Gambie", "it": "Gambia"},
- 81: {"de": "Georgien", "fr": "Géorgie", "it": "Georgia"},
- 82: {"de": "Deutschland", "fr": "Allemagne", "it": "Germania"},
- 83: {"de": "Ghana", "fr": "Ghana", "it": "Ghana"},
- 85: {"de": "Griechenland", "fr": "Grèce", "it": "Grecia"},
- 90: {"de": "Guatemala", "fr": "Guatemala", "it": "Guatemala"},
- 92: {"de": "Guinea", "fr": "Guinée", "it": "Guinea"},
- 93: {"de": "Guinea-Bissau", "fr": "Guinée-Bissau", "it": "Guinea-Bissau"},
- 94: {"de": "Guyana", "fr": "Guyana", "it": "Guyana"},
- 95: {"de": "Haiti", "fr": "Haïti", "it": "Haiti"},
- 97: {"de": "Honduras", "fr": "Honduras", "it": "Honduras"},
- 99: {"de": "Ungarn", "fr": "Hongrie", "it": "Ungheria"},
- 100: {"de": "Island", "fr": "Icelande", "it": "Islanda"},
- 101: {"de": "Indien", "fr": "India", "it": "India"},
- 102: {"de": "Indonesien", "fr": "Indonésie", "it": "Indonesia"},
- 103: {"de": "Iran", "fr": "Iran", "it": "Iran"},
- 104: {"de": "Irak", "fr": "Irak", "it": "Iraq"},
- 105: {"de": "Irland", "fr": "Irlande", "it": "Irlanda"},
- 107: {"de": "Israel", "fr": "Israël", "it": "Israele"},
- 108: {"de": "Italien", "fr": "Italie", "it": "Italia"},
- 109: {"de": "Jamaika", "fr": "Jamaïque", "it": "Giamaica"},
- 110: {"de": "Japan", "fr": "Japon", "it": "Giappone"},
- 112: {"de": "Jordanien", "fr": "Jordanie", "it": "Giordania"},
- 113: {"de": "Kasachstan", "fr": "Kazakstan", "it": "Kazakistan"},
- 114: {"de": "Kenia", "fr": "Kénia", "it": "Kenia"},
- 115: {"de": "Kiribati", "fr": "Kiribati", "it": "Kiribati"},
- 116: {
- "de": "Korea, Demokratische Volksrepublik",
- "fr": "Corée du Nord",
- "it": "Corea, Repubblica Popolare Democratica",
- },
- 117: {
- "de": "Korea, Republik (auch: Südkorea)",
- "fr": "Corée du Sud",
- "it": "Corea, Repubblica (anche: Corea del Sud)",
- },
- 118: {"de": "Kuwait", "fr": "Koweït", "it": "Kuwait"},
- 119: {"de": "Kirgisistan", "fr": "Kirgistan", "it": "Kirghizistan"},
- 120: {"de": "Laos", "fr": "Laos", "it": "Laos"},
- 121: {"de": "Lettland", "fr": "Lettonie", "it": "Lettonia"},
- 122: {"de": "Libanon", "fr": "Lebanon", "it": "Libano"},
- 123: {"de": "Lesotho", "fr": "Lesotho", "it": "Lesotho"},
- 124: {"de": "Liberia", "fr": "Liberia", "it": "Liberia"},
- 125: {"de": "Libyen", "fr": "Libye", "it": "Libia"},
- 126: {"de": "Liechtenstein", "fr": "Liechtenstein", "it": "Liechtenstein"},
- 127: {"de": "Litauen", "fr": "Lituanie", "it": "Lituania"},
- 128: {"de": "Luxembourg", "fr": "Luxembourg", "it": "Lussemburgo"},
- 130: {
- "de": "Nordmazedonien",
- "fr": "Macédoine du Nord",
- "it": "Macedonia del Nord",
- },
- 131: {"de": "Madagaskar", "fr": "Madagascar", "it": "Madagascar"},
- 132: {"de": "Malawi", "fr": "Malawi", "it": "Malawi"},
- 133: {"de": "Malaysia", "fr": "Malaisie", "it": "Malesia"},
- 134: {"de": "Malediven", "fr": "Maldives", "it": "Maldive"},
- 135: {"de": "Mali", "fr": "Mali", "it": "Mali"},
- 136: {"de": "Malta", "fr": "Malte", "it": "Malta"},
- 137: {"de": "Marshall Inseln", "fr": "Iles Marshall", "it": "Isole Marshall"},
- 139: {"de": "Mauretanien", "fr": "Mauritanie", "it": "Mauritania"},
- 140: {"de": "Mauritius", "fr": "Ile Maurice", "it": "Mauritius"},
- 142: {"de": "Mexico", "fr": "Mexique", "it": "Messico"},
- 143: {"de": "Mikronesien", "fr": "Micronésie", "it": "Micronesia"},
- 144: {"de": "Moldavien", "fr": "Moldavie", "it": "Moldova"},
- 145: {"de": "Monaco", "fr": "Monaco", "it": "Monaco"},
- 146: {"de": "Mongolei", "fr": "Mongolie", "it": "Mongolia"},
- 148: {"de": "Marokko", "fr": "Morocco", "it": "Marocco"},
- 149: {"de": "Mosambik", "fr": "Mozambique", "it": "Mozambico"},
- 150: {"de": "Myanmar", "fr": "Myanmar", "it": "Myanmar"},
- 151: {"de": "Namibia", "fr": "Namibie", "it": "Namibia"},
- 152: {"de": "Nauru", "fr": "Nauru", "it": "Nauru"},
- 153: {"de": "Nepal", "fr": "Népal", "it": "Nepal"},
- 154: {"de": "Niederlande", "fr": "Pays-Bas", "it": "Paesi Bassi"},
- 157: {"de": "Neuseeland", "fr": "Nouvelle-Zélande", "it": "Nuova Zelanda"},
- 158: {"de": "Nicaragua", "fr": "Nicaragua", "it": "Nicaragua"},
- 159: {"de": "Niger", "fr": "Niger", "it": "Niger"},
- 160: {"de": "Nigeria", "fr": "Nigeria", "it": "Nigeria"},
- 164: {"de": "Norwegen", "fr": "Norvège", "it": "Norvegia"},
- 165: {"de": "Oman", "fr": "Oman", "it": "Oman"},
- 166: {"de": "Pakistan", "fr": "Pakistan", "it": "Pakistan"},
- 167: {"de": "Palau", "fr": "Palau", "it": "Palau"},
- 168: {"de": "Panama", "fr": "Panama", "it": "Panama"},
- 170: {
- "de": "Papua-Neuguinea",
- "fr": "Papouasie Nouvelle-Guinée",
- "it": "Papua Nuova Guinea",
- },
- 171: {"de": "Paraguay", "fr": "Paraguay", "it": "Paraguay"},
- 172: {"de": "Peru", "fr": "Pérou", "it": "Perù"},
- 173: {"de": "Philippinen", "fr": "Philippines", "it": "Filippine"},
- 175: {"de": "Polen", "fr": "Pologne", "it": "Polonia"},
- 176: {"de": "Portugal", "fr": "Portugal", "it": "Portogallo"},
- 178: {"de": "Katar", "fr": "Qatar", "it": "Qatar"},
- 180: {"de": "Rumänien", "fr": "Roumanie", "it": "Romania"},
- 181: {"de": "Russische Föderation", "fr": "Russie", "it": "Russia"},
- 182: {"de": "Ruanda", "fr": "Ruanda", "it": "Ruanda"},
- 183: {
- "de": "Saint Kitts und Nevis",
- "fr": "Saint-Kitts-et-Nevis",
- "it": "Saint Kitts e Nevis",
- },
- 184: {"de": "St. Lucia", "fr": "Sainte-Lucie", "it": "Santa Lucia"},
- 185: {
- "de": "St. Vincent und die Grenadinen",
- "fr": "Saint-Vincent-et-Les Grenadines",
- "it": "Saint Vincent e Grenadine",
- },
- 186: {"de": "Samoa", "fr": "Samoa", "it": "Samoa"},
- 187: {"de": "San Marino", "fr": "San Marino", "it": "San Marino"},
- 188: {
- "de": "Sao Tome und Principe",
- "fr": "Sao Tomé-et-Principe",
- "it": "São Tomé e Principe",
- },
- 189: {"de": "Saudi-Arabien", "fr": "Arabie Saoudite", "it": "Arabia Saudita"},
- 190: {"de": "Senegal", "fr": "Sénégal", "it": "Senegal"},
- 191: {"de": "Seychellen", "fr": "Seychelles", "it": "Seychelles"},
- 192: {"de": "Sierra Leone", "fr": "Sierra Leone", "it": "Sierra Leone"},
- 193: {"de": "Singapur", "fr": "Singapour", "it": "Singapore"},
- 194: {"de": "Slowakei", "fr": "Slovaquie", "it": "Slovacchia"},
- 195: {"de": "Slowenien", "fr": "Slovénie", "it": "Slovenia"},
- 196: {"de": "Salomonen", "fr": "Iles Salomon", "it": "Salomone"},
- 197: {"de": "Somalia", "fr": "Somalie", "it": "Somalia"},
- 198: {"de": "Südafrika", "fr": "Afrique du Sud", "it": "Africa del Sud"},
- 200: {"de": "Spanien", "fr": "Espagne", "it": "Spagna"},
- 201: {"de": "Sri Lanka", "fr": "Sri Lanka", "it": "Sri Lanka"},
- 204: {"de": "Sudan", "fr": "Soudan", "it": "Sudan"},
- 205: {"de": "Suriname", "fr": "Suriname", "it": "Suriname"},
- 207: {"de": "Swasiland", "fr": "Swaziland", "it": "Swaziland"},
- 208: {"de": "Schweden", "fr": "Suède", "it": "Svezia"},
- 209: {"de": "Schweiz", "fr": "Suisse", "it": "Svizzera"},
- 210: {"de": "Syrien", "fr": "Syrie", "it": "Siria"},
- 211: {"de": "Taiwan", "fr": "Taïwan", "it": "Taiwan"},
- 212: {"de": "Tadschikistan", "fr": "Tadjikistan", "it": "Tagikistan"},
- 213: {"de": "Tansania", "fr": "Tanzanie", "it": "Tanzania"},
- 214: {"de": "Thailand", "fr": "Thaïlande", "it": "Tailandia"},
- 215: {"de": "Togo", "fr": "Togo", "it": "Togo"},
- 217: {"de": "Tonga", "fr": "Tonga", "it": "Tonga"},
- 218: {
- "de": "Trinidad und Tobago",
- "fr": "Trinité-et-Tobago",
- "it": "Trinidad e Tobago",
- },
- 219: {"de": "Tunesien", "fr": "Tunisie", "it": "Tunisia"},
- 220: {"de": "Türkei", "fr": "Turchia", "it": "Turchia"},
- 221: {"de": "Turkmenistan", "fr": "Turkménistan", "it": "Turkmenistan"},
- 223: {"de": "Tuvalu", "fr": "Tuvalu", "it": "Tuvalu"},
- 224: {"de": "Uganda", "fr": "Ouganda", "it": "Uganda"},
- 225: {"de": "Ukraine", "fr": "Ukraine", "it": "Ucraina"},
- 226: {
- "de": "Vereinigte Arabische Emirate",
- "fr": "Émirats Arabes Unis",
- "it": "Emirati Arabi Uniti",
- },
- 227: {"de": "Großbritannien", "fr": "Royaume-Uni", "it": "Regno Unito"},
- 228: {"de": "USA", "fr": "États-Unis", "it": "Stati Uniti d´ America"},
- 230: {"de": "Uruguay", "fr": "Uruguay", "it": "Uruguay"},
- 231: {"de": "Usbekistan", "fr": "Ouzbékistan", "it": "Uzbekistan"},
- 232: {"de": "Vanuatu", "fr": "Vanuatu", "it": "Vanuatu"},
- 233: {"de": "Vatikanstadt", "fr": "Vatican", "it": "Città del Vaticano"},
- 234: {"de": "Venezuela", "fr": "Venezuela", "it": "Venezuela"},
- 235: {"de": "Vietnam", "fr": "Viêtnam", "it": "Vietnam"},
- 239: {"de": "Sahara", "fr": "Sahara", "it": "Sahara"},
- 240: {"de": "Jemen", "fr": "Yémen", "it": "Yemen"},
- 241: {"de": "Serbien", "fr": "Serbie", "it": "Serbia"},
- 242: {"de": "Montenegro", "fr": "Monténégro", "it": "Montenegro"},
- 243: {"de": "Sambia", "fr": "Zambie", "it": "Zambia"},
- 244: {"de": "Simbabwe", "fr": "Zimbabwe", "it": "Zimbabwe"},
- 245: {"de": "Hong Kong", "fr": "Hong Kong", "it": "Hong Kong"},
- 246: {"de": "Falkland Inseln", "fr": "Îles Malouines", "it": "Isole Falkland"},
- 247: {"de": "Aruba", "fr": "Aruba", "it": "Aruba"},
- 248: {"de": "Bermuda", "fr": "Bermudes", "it": "Bermuda"},
- 249: {
- "de": "Britische Jungferninseln",
- "fr": "Îles Vierges britanniques",
- "it": "Isole Vergini britanniche",
- },
- 250: {"de": "Curaçao", "fr": "Curaçao", "it": "Curaçao"},
- 251: {"de": "Anguilla", "fr": "Anguilla", "it": "Anguilla"},
- 252: {"de": "Montserrat", "fr": "Montserrat", "it": "Montserrat"},
- 253: {
- "de": "Bonaire, Sint Eustatius und Saba",
- "fr": "Bonaire, Saint-Eustache et Saba",
- "it": "Bonaire, Sint Eustatius e Saba",
- },
- 254: {"de": "Cayman Inseln", "fr": "Îles Caïmans", "it": "Isole Cayman"},
- 255: {"de": "Sint Maarten", "fr": "Saint-Martin", "it": "Sint Maarten"},
- 256: {
- "de": "Turks- und Caicos-Inseln",
- "fr": "Îles Turks et Caïques",
- "it": "Turks e Caicos",
- },
- 257: {"de": "Saint-Barth", "fr": "Saint-Barthélemy", "it": "Saint-Barth"},
- 258: {
- "de": "Palästinensisches Gebiet",
- "fr": "Territoires palestiniens occupés",
- "it": "Territori palestinesi",
- },
- 259: {"de": "Kosovo", "fr": "Kosovo", "it": "Kosovo"},
- 260: {"de": "Gibraltar", "fr": "Gibraltar", "it": "Gibilterra"},
- 261: {"de": "Neukaledonien", "fr": "Nouvelle-Calédonie", "it": "Nuova Caledonia"},
- 262: {
- "de": "Französisch-Polynesien",
- "fr": "Polynésie française",
- "it": "Polinesia francese",
- },
- 310: {
- "de": "Niederländische Antillen",
- "fr": "Antilles néerlandaises",
- "it": "Antille olandesi",
- },
- 311: {"de": "Antarktika", "fr": "Antarctique", "it": "Antartide"},
- 312: {
- "de": "Amerikanisch-Samoa",
- "fr": "Samoa américaines",
- "it": "Samoa americane",
- },
- 313: {"de": "Åland", "fr": "Åland", "it": "Åland"},
- 314: {"de": "Bouvetinsel", "fr": "Île Bouvet", "it": "Isola Bouvet"},
- 315: {"de": "Kokosinseln", "fr": "Îles Cocos", "it": "Isole Cocos (Keeling)"},
- 316: {"de": "Cookinseln", "fr": "Îles Cook", "it": "Isole Cook"},
- 317: {
- "de": "Clipperton-Insel",
- "fr": "Île de Clipperton",
- "it": "Isola di Clipperton",
- },
- 318: {"de": "Weihnachtsinsel", "fr": "Île Christmas", "it": "Isola di Natale"},
- 319: {"de": "Färöer-Inseln", "fr": "Îles Féroé", "it": "Isole Färöer"},
- 320: {
- "de": "Französisch-Guayana",
- "fr": "Guyane française",
- "it": "Guyana francese",
- },
- 321: {"de": "Guernsey", "fr": "Guernsey", "it": "Guernsey"},
- 322: {"de": "Grönland", "fr": "Groenland", "it": "Groenlandia"},
- 323: {"de": "Guadeloupe", "fr": "Guadeloupe", "it": "Guadalupa"},
- 324: {
- "de": "Südgeorgien und die Südlichen Sandwichinseln",
- "fr": "Géorgie du Sud et Îles Sandwich du Sud",
- "it": "Georgia del Sud e Sandwich Australi",
- },
- 325: {"de": "Guam", "fr": "Guam", "it": "Guam"},
- 326: {
- "de": "Heard und McDonaldinseln",
- "fr": "Îles Heard et McDonald",
- "it": "Isola Heard e Isole McDonald",
- },
- 327: {"de": "Insel Man", "fr": "Île de Man", "it": "Isola di Man"},
- 328: {
- "de": "Britisches Territorium im Indischen Ozean",
- "fr": "Territoire britannique de l´océan Indien",
- "it": "Territori Britannici dell´Oceano Indiano",
- },
- 329: {"de": "Jersey", "fr": "Jersey", "it": "Jersey"},
- 330: {"de": "Saint-Martin", "fr": "Saint-Martin", "it": "Saint Martin"},
- 331: {"de": "Macau", "fr": "Macao", "it": "Macao"},
- 332: {
- "de": "Nördliche Marianen",
- "fr": "Îles Mariannes du Nord",
- "it": "Isole Marianne Settentrionali",
- },
- 333: {"de": "Martinique", "fr": "Martinique", "it": "Martinica"},
- 334: {"de": "Norfolkinsel", "fr": "Île Norfolk", "it": "Isola Norfolk"},
- 335: {"de": "Niue", "fr": "Niue", "it": "Niue"},
- 336: {
- "de": "Saint-Pierre und Miquelon",
- "fr": "Saint-Pierre-et-Miquelon",
- "it": "Saint-Pierre e Miquelon",
- },
- 337: {"de": "Pitcairninseln", "fr": "Îles Pitcairn", "it": "Isole Pitcairn"},
- 338: {"de": "Puerto Rico", "fr": "Porto Rico", "it": "Porto Rico"},
- 339: {"de": "La Réunion", "fr": "La Réunion", "it": "Isola della Riunione"},
- 340: {
- "de": "St. Helena, Ascension und Tristan da Cunha",
- "fr": "Sainte-Hélène, Ascension et Tristan da Cunha",
- "it": "Sant´Elena, Ascensione e Tristan da Cunha",
- },
- 341: {
- "de": "Spitzbergen, Jan Mayen",
- "fr": "Spitzberg, Jan Mayen",
- "it": "Svalbard e Jan Mayen",
- },
- 342: {"de": "Südsudan", "fr": "Sud-Soudan", "it": "Sudan del Sud"},
- 343: {
- "de": "Französische Süd- und Antarktisgebiete",
- "fr": "Terres australes et antarctiques françaises",
- "it": "Territori australi e antartico francese",
- },
- 344: {"de": "Tokelau", "fr": "Tokelau", "it": "Tokelau"},
- 345: {
- "de": "United States Minor Outlying Islands",
- "fr": "Îles mineures éloignées des États-Unis",
- "it": "Isole Minori Esterne degli Stati Uniti",
- },
- 346: {
- "de": "Amerikanische Jungferninseln",
- "fr": "Îles Vierges américaines",
- "it": "Isole Vergini Americane",
- },
- 347: {"de": "Wallis und Futuna", "fr": "Wallis et Futuna", "it": "Wallis e Futuna"},
- 348: {"de": "Mayotte", "fr": "Mayotte", "it": "Mayotte"},
-}
-
-
-def add_countries(apps=None, schema_editor=None):
- if apps is None:
- # pylint: disable=import-outside-toplevel
- from vbv_lernwelt.shop.models import Country
- else:
- Country = apps.get_model("shop", "Country")
-
- for country_id, country_name in countries.items():
- Country.objects.get_or_create(
- country_id=country_id,
- name_de=country_name["de"],
- name_fr=country_name["fr"],
- name_it=country_name["it"],
- )
-
-
-def remove_countries(apps=None, schema_editor=None):
- if apps is None:
- # pylint: disable=import-outside-toplevel
- from vbv_lernwelt.shop.models import Country
- else:
- Country = apps.get_model("shop", "Country")
-
- for country_id in countries.keys():
- Country.objects.filter(
- country_id=country_id,
- ).delete()
diff --git a/server/vbv_lernwelt/shop/models.py b/server/vbv_lernwelt/shop/models.py
index e2f728b0..0c3e75f5 100644
--- a/server/vbv_lernwelt/shop/models.py
+++ b/server/vbv_lernwelt/shop/models.py
@@ -1,21 +1,6 @@
from django.db import models
-class Country(models.Model):
- country_id = models.IntegerField(primary_key=True)
- name_de = models.CharField(max_length=255)
- name_fr = models.CharField(max_length=255)
- name_it = models.CharField(max_length=255)
-
- def __str__(self):
- return f"{self.name_de} ({self.country_id})"
-
- class Meta:
- verbose_name = "Country"
- verbose_name_plural = "Countries"
- ordering = ["country_id"]
-
-
class BillingAddress(models.Model):
"""
Draft of a billing address for a purchase from the shop.
diff --git a/server/vbv_lernwelt/shop/serializers.py b/server/vbv_lernwelt/shop/serializers.py
index 89a1787c..c29b8b3e 100644
--- a/server/vbv_lernwelt/shop/serializers.py
+++ b/server/vbv_lernwelt/shop/serializers.py
@@ -1,6 +1,6 @@
from rest_framework import serializers
-from .models import BillingAddress, Country
+from .models import BillingAddress
class BillingAddressSerializer(serializers.ModelSerializer):
@@ -21,9 +21,3 @@ class BillingAddressSerializer(serializers.ModelSerializer):
"company_city",
"company_country",
]
-
-
-class CountrySerializer(serializers.ModelSerializer):
- class Meta:
- model = Country
- fields = "__all__"
diff --git a/server/vbv_lernwelt/shop/tests/test_checkout_api.py b/server/vbv_lernwelt/shop/tests/test_checkout_api.py
index bd7e942b..3c0b3ec8 100644
--- a/server/vbv_lernwelt/shop/tests/test_checkout_api.py
+++ b/server/vbv_lernwelt/shop/tests/test_checkout_api.py
@@ -5,6 +5,7 @@ from rest_framework import status
from rest_framework.test import APITestCase
from vbv_lernwelt.core.admin import User
+from vbv_lernwelt.core.model_utils import add_countries
from vbv_lernwelt.shop.const import VV_DE_PRODUCT_SKU
from vbv_lernwelt.shop.models import CheckoutInformation, CheckoutState, Product
from vbv_lernwelt.shop.services import InitTransactionException
@@ -20,13 +21,13 @@ TEST_ADDRESS_DATA = {
"street_number": "1",
"postal_code": "1234",
"city": "Test City",
- "country": "Test Country",
+ "country": "209",
"company_name": "Test Company",
"company_street": "Test Company Street",
"company_street_number": "1",
"company_postal_code": "1234",
"company_city": "Test Company City",
- "company_country": "Test Company Country",
+ "company_country": "209",
}
REDIRECT_URL = "http://testserver/redirect-url"
@@ -49,6 +50,7 @@ class CheckoutAPITestCase(APITestCase):
)
self.client.login(username=USER_USERNAME, password=USER_PASSWORD)
+ add_countries()
@patch("vbv_lernwelt.shop.views.init_transaction")
def test_checkout_happy_case(self, mock_init_transaction):
@@ -90,6 +92,12 @@ class CheckoutAPITestCase(APITestCase):
webhook_url=f"{REDIRECT_URL}/api/shop/transaction/webhook/",
)
+ user = User.objects.get(username=USER_USERNAME)
+
+ self.assertEqual(user.street, TEST_ADDRESS_DATA["street"])
+ self.assertEqual(str(user.country.country_id), TEST_ADDRESS_DATA["country"])
+ self.assertEqual(user.invoice_address, User.INVOICE_ADDRESS_ORGANISATION)
+
@patch("vbv_lernwelt.shop.views.init_transaction")
def test_incomplete_setup(self, mock_init_transaction):
# GIVEN
diff --git a/server/vbv_lernwelt/shop/views.py b/server/vbv_lernwelt/shop/views.py
index 31edf824..84fb4907 100644
--- a/server/vbv_lernwelt/shop/views.py
+++ b/server/vbv_lernwelt/shop/views.py
@@ -7,6 +7,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from sentry_sdk import capture_exception
+from vbv_lernwelt.core.models import Country, User
from vbv_lernwelt.course.consts import (
COURSE_VERSICHERUNGSVERMITTLERIN_FR_ID,
COURSE_VERSICHERUNGSVERMITTLERIN_ID,
@@ -174,7 +175,7 @@ def checkout_vv(request):
),
)
- CheckoutInformation.objects.create(
+ checkout_info = CheckoutInformation.objects.create(
user=request.user,
state=CheckoutState.ONGOING,
transaction_id=transaction_id,
@@ -187,6 +188,8 @@ def checkout_vv(request):
**request.data["address"],
)
+ update_user_address(user=request.user, checkout_info=checkout_info)
+
return next_step_response(url=get_payment_url(transaction_id))
@@ -267,3 +270,38 @@ def checkout_cancel_url(base_url: str) -> str:
def checkout_success_url(product_sku: str, base_url: str = "") -> str:
return f"{base_url}/onboarding/{product_sku}/checkout/complete"
+
+
+def update_user_address(user: User, checkout_info: CheckoutInformation):
+ user.street = checkout_info.street
+ user.street_number = checkout_info.street_number
+ user.postal_code = checkout_info.postal_code
+ user.city = checkout_info.city
+ try:
+ user.country = Country.objects.get(country_id=checkout_info.country)
+ except Country.DoesNotExist:
+ pass
+
+ user.organisation_detail_name = checkout_info.company_name
+ user.organisation_street = checkout_info.company_street
+ user.organisation_street_number = checkout_info.company_street_number
+ user.organisation_postal_code = checkout_info.company_postal_code
+ user.organisation_city = checkout_info.company_city
+ try:
+ user.organisation_country = Country.objects.get(
+ country_id=checkout_info.company_country
+ )
+ except Country.DoesNotExist:
+ pass
+
+ if (
+ user.organisation_detail_name
+ and user.organisation_street
+ and user.organisation_street_number
+ and user.organisation_postal_code
+ and user.organisation_city
+ and user.organisation_country
+ ):
+ user.invoice_address = User.INVOICE_ADDRESS_ORGANISATION
+
+ user.save()