Merged develop into feature/VBV-641-vorschau-lernbegleitung
This commit is contained in:
commit
0c170979c0
|
|
@ -13,7 +13,7 @@ const hideVersion = (import.meta.env.VITE_APP_ENVIRONMENT || "local").startsWith
|
|||
);
|
||||
|
||||
async function changeLocale(language: AvailableLanguages) {
|
||||
userStore.setUserLanguages(language);
|
||||
await userStore.updateUserProfile({ language });
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
:user="userStore"
|
||||
@logout="logout"
|
||||
@select-course-session="selectCourseSession"
|
||||
@close="emit('close')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
@ -14,6 +15,8 @@ import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|||
import { useUserStore } from "@/stores/user";
|
||||
import type { CourseSession } from "@/types";
|
||||
|
||||
const emit = defineEmits(["close"]);
|
||||
|
||||
const logout = () => {
|
||||
userStore.handleLogout();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import CourseSessionsMenu from "@/components/header/CourseSessionsMenu.vue";
|
||||
import type { UserState } from "@/stores/user";
|
||||
import type { User } from "@/stores/user";
|
||||
import type { CourseSession } from "@/types";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSessions: CourseSession[];
|
||||
user: UserState;
|
||||
user: User;
|
||||
selectedCourseSession?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(["selectCourseSession", "logout"]);
|
||||
const emit = defineEmits(["selectCourseSession", "logout", "close"]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
async function navigate(routeName: string) {
|
||||
await router.push({ name: routeName });
|
||||
emit("close");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -18,14 +26,17 @@ const emit = defineEmits(["selectCourseSession", "logout"]);
|
|||
<div class="flex justify-start">
|
||||
<div v-if="user.avatar_url">
|
||||
<img
|
||||
class="inline-block h-20 w-20 rounded-full"
|
||||
class="inline-block h-24 w-24 rounded-full"
|
||||
:src="user.avatar_url"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-6">
|
||||
<h3>{{ user.first_name }} {{ user.last_name }}</h3>
|
||||
<div class="text-sm text-gray-800">{{ user.email }}</div>
|
||||
<div class="mb-3 text-sm text-gray-800">{{ user.email }}</div>
|
||||
<button class="underline" @click="navigate('personalProfile')">
|
||||
{{ $t("a.Profil anzeigen") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ const hasMentorManagementMenu = computed(() => {
|
|||
:show="state.showMobileProfileMenu"
|
||||
@closemodal="state.showMobileProfileMenu = false"
|
||||
>
|
||||
<AccountMenu />
|
||||
<AccountMenu @close="state.showMobileProfileMenu = false" />
|
||||
</ItFullScreenModal>
|
||||
</Teleport>
|
||||
<Transition name="nav">
|
||||
|
|
@ -327,10 +327,11 @@ const hasMentorManagementMenu = computed(() => {
|
|||
</PopoverButton>
|
||||
|
||||
<PopoverPanel
|
||||
v-slot="{ close }"
|
||||
class="absolute -right-2 top-8 z-50 w-[500px] bg-white shadow-lg"
|
||||
>
|
||||
<div class="p-4">
|
||||
<AccountMenu />
|
||||
<AccountMenu @close="close" />
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type { UserState } from "@/stores/user";
|
||||
import type { User } from "@/stores/user";
|
||||
import type { CourseSession } from "@/types";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
|
|
@ -20,7 +20,7 @@ defineProps<{
|
|||
hasCockpitMenu: boolean;
|
||||
courseSession: CourseSession | undefined;
|
||||
mediaUrl?: string;
|
||||
user: UserState | undefined;
|
||||
user: User | undefined;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(["closemodal", "logout"]);
|
||||
|
|
@ -50,13 +50,6 @@ const courseSessionsStore = useCourseSessionsStore();
|
|||
</div>
|
||||
<div class="ml-6">
|
||||
<h3>{{ user?.first_name }} {{ user?.last_name }}</h3>
|
||||
<button
|
||||
class="mt-2 inline-block flex items-center"
|
||||
@click="clickLink('/settings')"
|
||||
>
|
||||
<it-icon-settings class="inline-block" />
|
||||
<span class="ml-3">{{ $t("general.settings") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { useEntities } from "@/services/onboarding";
|
||||
import { useEntities } from "@/services/entities";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { useEntities } from "@/services/onboarding";
|
||||
import { useEntities } from "@/services/entities";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const props = defineProps<{
|
|||
const userStore = useUserStore();
|
||||
|
||||
async function changeLocale(language: AvailableLanguages) {
|
||||
await userStore.setUserLanguages(language);
|
||||
await userStore.updateUserProfile({ language });
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,386 @@
|
|||
<script setup lang="ts">
|
||||
import { useEntities } from "@/services/entities";
|
||||
import AvatarImage from "@/components/ui/AvatarImage.vue";
|
||||
import { ref } from "vue";
|
||||
import { type User, useUserStore } from "@/stores/user";
|
||||
|
||||
const emit = defineEmits(["cancel", "save"]);
|
||||
|
||||
const user = useUserStore();
|
||||
const { countries, organisations } = useEntities();
|
||||
|
||||
const avatarLoading = ref(false);
|
||||
const error = ref(false);
|
||||
|
||||
const formData = ref({
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
email: user.email,
|
||||
street: user.street,
|
||||
street_number: user.street_number,
|
||||
postal_code: user.postal_code,
|
||||
city: user.city,
|
||||
country_id: user.country?.id,
|
||||
organisation: user.organisation,
|
||||
organisation_street: user.organisation_street,
|
||||
organisation_street_number: user.organisation_street_number,
|
||||
organisation_postal_code: user.organisation_postal_code,
|
||||
organisation_city: user.organisation_city,
|
||||
organisation_country_id: user.organisation_country?.id,
|
||||
invoice_address: user.invoice_address,
|
||||
});
|
||||
|
||||
async function save() {
|
||||
const { country_id, organisation_country_id, ...profileData } = formData.value;
|
||||
const typedProfileData: Partial<User> = { ...profileData };
|
||||
|
||||
typedProfileData.country = countries.value.find((c) => c.id === country_id);
|
||||
typedProfileData.organisation_country = countries.value.find(
|
||||
(c) => c.id === organisation_country_id
|
||||
);
|
||||
|
||||
await user.updateUserProfile(typedProfileData);
|
||||
emit("save");
|
||||
}
|
||||
|
||||
async function avatarUpload(e: Event) {
|
||||
const { files } = e.target as HTMLInputElement;
|
||||
if (!files?.length) return;
|
||||
avatarLoading.value = true;
|
||||
error.value = false;
|
||||
|
||||
try {
|
||||
await user.setUserAvatar(files[0]);
|
||||
emit("save");
|
||||
} catch (e) {
|
||||
error.value = true;
|
||||
} finally {
|
||||
avatarLoading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-end space-x-4">
|
||||
<button class="btn btn-secondary" @click="emit('cancel')">
|
||||
{{ $t("general.cancel") }}
|
||||
</button>
|
||||
<button class="btn btn-primary" @click="save">
|
||||
{{ $t("general.save") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="flex items-center space-x-3 bg-red-200 px-6 py-3">
|
||||
<it-icon-close class="h-8 w-8 text-red-800" />
|
||||
<span>
|
||||
{{
|
||||
$t(
|
||||
"a.Bitte überprüfe deine Eingaben. Es sind Fehler in deinem Formular aufgetreten."
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mb-4 bg-white p-3 md:p-6">
|
||||
<h3 class="mb-8">{{ $t("a.Persönliche Informationen") }}</h3>
|
||||
<section>
|
||||
<label for="first-name" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Vorname") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="first-name"
|
||||
v-model="formData.first_name"
|
||||
type="text"
|
||||
name="first-name"
|
||||
autocomplete="given-name"
|
||||
disabled
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
|
||||
/>
|
||||
|
||||
<label for="last-name" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Name") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="last-name"
|
||||
v-model="formData.last_name"
|
||||
type="text"
|
||||
name="last-name"
|
||||
autocomplete="family-name"
|
||||
disabled
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
|
||||
/>
|
||||
|
||||
<label for="email" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.E-Mail Adresse") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="email"
|
||||
:value="formData.email"
|
||||
type="email"
|
||||
name="email"
|
||||
autocomplete="email"
|
||||
disabled
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
|
||||
/>
|
||||
<label class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Profilbild") }}
|
||||
</label>
|
||||
<div class="flex items-center space-x-4">
|
||||
<AvatarImage
|
||||
:image-size="64"
|
||||
:loading="avatarLoading"
|
||||
:image-url="user.avatar_url"
|
||||
/>
|
||||
<div class="btn-secondary relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="upload"
|
||||
type="file"
|
||||
class="absolute bottom-0 left-0 right-0 top-0 opacity-0"
|
||||
accept="image/*"
|
||||
:disabled="avatarLoading"
|
||||
@change="avatarUpload"
|
||||
/>
|
||||
{{ $t("a.Bild hochladen") }}
|
||||
<it-icon-upload class="it-icon ml-2 h-6 w-6" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="mt-8 border-t pt-8">
|
||||
<h4 class="mb-4 text-lg font-bold">
|
||||
{{ $t("a.Privatadresse") }}
|
||||
</h4>
|
||||
|
||||
<div class="flex flex-col justify-start md:flex-row md:space-x-4">
|
||||
<div class="w-full md:max-w-sm">
|
||||
<label for="street-address" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Strasse") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="street-address"
|
||||
v-model="formData.street"
|
||||
type="text"
|
||||
name="street-address"
|
||||
autocomplete="street-address"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:max-w-[160px]">
|
||||
<label for="street-number" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Hausnummmer") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="street-number"
|
||||
v-model="formData.street_number"
|
||||
name="street-number"
|
||||
type="text"
|
||||
autocomplete="street-number"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-start md:flex-row md:space-x-4">
|
||||
<div class="w-full md:max-w-[160px]">
|
||||
<label for="postal-code" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.PLZ") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="postal-code"
|
||||
v-model="formData.postal_code"
|
||||
name="postal-code"
|
||||
type="text"
|
||||
autocomplete="postal-code"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full md:max-w-sm">
|
||||
<label for="city" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Ort") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="city"
|
||||
v-model="formData.city"
|
||||
type="text"
|
||||
name="city"
|
||||
autocomplete="address-level2"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="country" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Land") }}
|
||||
</label>
|
||||
|
||||
<select
|
||||
id="country"
|
||||
v-model="formData.country_id"
|
||||
name="country"
|
||||
autocomplete="country-name"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
|
||||
>
|
||||
<option v-for="country in countries" :key="country.id" :value="country.id">
|
||||
{{ country.name }}
|
||||
</option>
|
||||
</select>
|
||||
</section>
|
||||
</div>
|
||||
<div class="mb-4 bg-white p-3 md:p-6">
|
||||
<h3 class="mb-8">{{ $t("a.Geschäftsdaten") }}</h3>
|
||||
<section>
|
||||
<label for="organisation" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Unternehmen") }}
|
||||
</label>
|
||||
|
||||
<select
|
||||
id="organisation"
|
||||
v-model="formData.organisation"
|
||||
required
|
||||
name="organisation"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
|
||||
>
|
||||
<option
|
||||
v-for="organisation in organisations"
|
||||
:key="organisation.id"
|
||||
:value="organisation.id"
|
||||
>
|
||||
{{ organisation.name }}
|
||||
</option>
|
||||
</select>
|
||||
</section>
|
||||
<section class="mt-8 border-t pt-8">
|
||||
<h4 class="mb-4 text-lg font-bold">
|
||||
{{ $t("a.Firmenanschrift") }}
|
||||
</h4>
|
||||
|
||||
<div class="flex flex-col justify-start md:flex-row md:space-x-4">
|
||||
<div class="w-full md:max-w-sm">
|
||||
<label for="org-street-address" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Strasse") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="org-street-address"
|
||||
v-model="formData.organisation_street"
|
||||
type="text"
|
||||
name="org-street-address"
|
||||
autocomplete="street-address"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:max-w-[160px]">
|
||||
<label for="org-street-number" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Hausnummmer") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="org-street-number"
|
||||
v-model="formData.organisation_street_number"
|
||||
name="org-street-number"
|
||||
type="text"
|
||||
autocomplete="street-number"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-start md:flex-row md:space-x-4">
|
||||
<div class="w-full md:max-w-[160px]">
|
||||
<label for="org-postal-code" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.PLZ") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="org-postal-code"
|
||||
v-model="formData.organisation_postal_code"
|
||||
name="org-postal-code"
|
||||
type="text"
|
||||
autocomplete="postal-code"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full md:max-w-sm">
|
||||
<label for="org-city" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Ort") }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="org-city"
|
||||
v-model="formData.organisation_city"
|
||||
type="text"
|
||||
name="org-city"
|
||||
autocomplete="address-level2"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:text-sm sm:leading-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="org-country" class="block pb-1.5 leading-6">
|
||||
{{ $t("a.Land") }}
|
||||
</label>
|
||||
|
||||
<select
|
||||
id="org-country"
|
||||
v-model="formData.organisation_country_id"
|
||||
required
|
||||
name="org-country"
|
||||
autocomplete="country-name"
|
||||
class="disabled:bg-gray-50 mb-4 block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
|
||||
>
|
||||
<option v-for="country in countries" :key="country.id" :value="country.id">
|
||||
{{ country.name }}
|
||||
</option>
|
||||
</select>
|
||||
</section>
|
||||
</div>
|
||||
<div class="mb-4 bg-white p-3 md:p-6">
|
||||
<h3 class="mb-8">{{ $t("a.Rechnungsadresse") }}</h3>
|
||||
<fieldset class="mt-4">
|
||||
<legend class="sr-only">Notification method</legend>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
id="invoice-address-private"
|
||||
v-model="formData.invoice_address"
|
||||
type="radio"
|
||||
value="prv"
|
||||
class="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-600"
|
||||
/>
|
||||
<label
|
||||
for="invoice-address-private"
|
||||
class="ml-3 block text-sm font-medium leading-6 text-gray-900"
|
||||
>
|
||||
{{ $t("a.Gleich wie die Privatadresse") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
id="invoice-address-organisation"
|
||||
v-model="formData.invoice_address"
|
||||
type="radio"
|
||||
value="org"
|
||||
class="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-600"
|
||||
/>
|
||||
<label
|
||||
for="invoice-address-organisation"
|
||||
class="ml-3 block text-sm font-medium leading-6 text-gray-900"
|
||||
>
|
||||
{{ $t("a.Gleich wie die Firmenanschrift") }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-4">
|
||||
<button class="btn btn-secondary" @click="emit('cancel')">
|
||||
{{ $t("general.cancel") }}
|
||||
</button>
|
||||
<button class="btn btn-primary" @click="save">
|
||||
{{ $t("general.save") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
<script setup lang="ts">
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { computed } from "vue";
|
||||
import { useEntities } from "@/services/entities";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const user = useUserStore();
|
||||
const { organisations } = useEntities();
|
||||
|
||||
const privateAddress = computed(() => {
|
||||
let addressText = `${user.street} ${user.street_number}`.trim();
|
||||
if (user.postal_code || user.city) {
|
||||
if (addressText.length) {
|
||||
addressText += ", ";
|
||||
}
|
||||
addressText += `${user.postal_code} ${user.city}`;
|
||||
}
|
||||
if (user.country) {
|
||||
if (addressText.length) {
|
||||
addressText += ", ";
|
||||
}
|
||||
addressText += user.country.name;
|
||||
}
|
||||
|
||||
return addressText.trim();
|
||||
});
|
||||
|
||||
const organisationName = computed(() => {
|
||||
const org = organisations.value.find((o) => o.id === user.organisation);
|
||||
if (org) {
|
||||
return org.name;
|
||||
}
|
||||
return t("a.Keine Angabe");
|
||||
});
|
||||
|
||||
const orgAddress = computed(() => {
|
||||
let addressText =
|
||||
`${user.organisation_street} ${user.organisation_street_number}`.trim();
|
||||
if (user.organisation_postal_code || user.organisation_city) {
|
||||
if (addressText.length) {
|
||||
addressText += ", ";
|
||||
}
|
||||
addressText += `${user.organisation_postal_code} ${user.organisation_city}`;
|
||||
}
|
||||
if (user.organisation_country) {
|
||||
if (addressText.length) {
|
||||
addressText += ", ";
|
||||
}
|
||||
addressText += user.organisation_country.name;
|
||||
}
|
||||
|
||||
return addressText.trim();
|
||||
});
|
||||
|
||||
const invoiceAddress = computed(() => {
|
||||
if (user.invoice_address === "org") {
|
||||
return t("a.Gleich wie die Firmenanschrift");
|
||||
}
|
||||
return t("a.Gleich wie die Privatadresse");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mb-4 bg-white p-3 md:p-6">
|
||||
<h3 class="mb-2">{{ $t("a.Persönliche Informationen") }}</h3>
|
||||
<div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-8 sm:py-6">
|
||||
<label class="block font-semibold leading-6">{{ $t("a.Vorname") }}</label>
|
||||
<div class="mb-3 sm:col-span-2 sm:mb-0">{{ user.first_name }}</div>
|
||||
<label class="block font-semibold leading-6">{{ $t("a.Name") }}</label>
|
||||
<div class="mb-3 sm:col-span-2 sm:mb-0">{{ user.last_name }}</div>
|
||||
<label class="block font-semibold leading-6">
|
||||
{{ $t("a.E-Mail Adresse") }}
|
||||
</label>
|
||||
<div class="mb-3 sm:col-span-2 sm:mb-0">{{ user.email }}</div>
|
||||
<label class="block font-semibold leading-6">
|
||||
{{ $t("a.Privatadresse") }}
|
||||
</label>
|
||||
<div class="mb-3 sm:col-span-2 sm:mb-0">
|
||||
<template v-if="privateAddress">
|
||||
{{ privateAddress }}
|
||||
</template>
|
||||
<span v-else class="text-gray-800">{{ $t("a.Keine Angabe") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white p-3 md:p-6">
|
||||
<h3 class="my-2">{{ $t("a.Geschäftsdaten") }}</h3>
|
||||
<div class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-8 sm:py-6">
|
||||
<label class="block font-semibold leading-6">{{ $t("a.Unternehmen") }}</label>
|
||||
<div class="mb-3 sm:col-span-2 sm:mb-0">{{ organisationName }}</div>
|
||||
<label class="block font-semibold leading-6">
|
||||
{{ $t("a.Firmenanschrift") }}
|
||||
</label>
|
||||
<div class="sm:col-span-2">
|
||||
<template v-if="orgAddress">
|
||||
{{ orgAddress }}
|
||||
</template>
|
||||
<span v-else class="text-gray-800">{{ $t("a.Keine Angabe") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white p-3 md:p-6">
|
||||
<h3 class="my-2">{{ $t("a.Rechnungsadresse") }}</h3>
|
||||
{{ invoiceAddress }}
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,23 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
imageUrl?: string;
|
||||
loading?: boolean;
|
||||
imageSize?: number;
|
||||
}>(),
|
||||
{
|
||||
imageUrl: "",
|
||||
loading: false,
|
||||
imageSize: 172,
|
||||
}
|
||||
);
|
||||
|
||||
const imageStyle = computed(() => {
|
||||
return {
|
||||
width: `${props.imageSize}px`,
|
||||
height: `${props.imageSize}px`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative">
|
||||
<img
|
||||
v-if="props?.imageUrl"
|
||||
class="aspect-square w-[172px] rounded-full object-cover"
|
||||
class="aspect-square rounded-full object-cover"
|
||||
:style="imageStyle"
|
||||
:class="{ 'opacity-30': props.loading }"
|
||||
:src="props.imageUrl"
|
||||
alt="avatar"
|
||||
|
|
@ -26,8 +37,8 @@ const props = withDefaults(
|
|||
v-else
|
||||
:class="{ 'opacity-30': props.loading }"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="172"
|
||||
height="172"
|
||||
:width="imageSize"
|
||||
:height="imageSize"
|
||||
viewBox="0 0 172 172"
|
||||
fill="none"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}}:
|
||||
<a href="mailto:vermittler@vbv-afa.ch" class="underline">
|
||||
vermittler@vbv-afa.ch@vbv.ch
|
||||
vermittler@vbv-afa.ch
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
<script setup lang="ts">
|
||||
import { useUserStore } from "@/stores/user";
|
||||
|
||||
import { ref } from "vue";
|
||||
|
||||
import ProfileView from "@/components/personalProfile/ProfileView.vue";
|
||||
import ProfileEdit from "@/components/personalProfile/ProfileEdit.vue";
|
||||
|
||||
const user = useUserStore();
|
||||
const editMode = ref(false);
|
||||
const saved = ref(false);
|
||||
|
||||
let saveBannerTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function saveComplete() {
|
||||
editMode.value = false;
|
||||
saved.value = true;
|
||||
if (saveBannerTimeout) {
|
||||
clearTimeout(saveBannerTimeout);
|
||||
}
|
||||
saveBannerTimeout = setTimeout(() => {
|
||||
saved.value = false;
|
||||
}, 10 * 1000);
|
||||
}
|
||||
|
||||
function startEditMode() {
|
||||
editMode.value = true;
|
||||
saved.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-grow flex-col bg-gray-200 md:flex-row">
|
||||
<div class="bg-white p-8">
|
||||
<section class="flex max-w-lg flex-col items-center space-y-2">
|
||||
<img
|
||||
:alt="user.first_name"
|
||||
class="h-auto w-32 rounded-full md:w-48"
|
||||
:src="user.avatar_url"
|
||||
/>
|
||||
<h3>{{ user.first_name }} {{ user.last_name }}</h3>
|
||||
<span>{{ user.email }}</span>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-grow flex-col space-y-4 px-8 py-8 md:px-16">
|
||||
<div v-if="!editMode" class="flex justify-end space-x-4">
|
||||
<button class="btn btn-secondary" @click="startEditMode">
|
||||
{{ $t("a.Profil bearbeiten") }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="saved" class="flex items-center space-x-3 bg-green-200 px-6 py-3">
|
||||
<it-icon-check class="h-10 w-10 text-green-800" />
|
||||
<span>{{ $t("a.Deine Änderungen wurden gespeichert") }}.</span>
|
||||
</div>
|
||||
<ProfileEdit v-if="editMode" @cancel="editMode = false" @save="saveComplete" />
|
||||
<ProfileView v-else />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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<Country[]> = ref([]);
|
||||
const organisations: Ref<Organisation[]> = ref([]);
|
||||
|
||||
itGetCached("/api/core/entities/").then((res) => {
|
||||
countries.value = res.countries;
|
||||
organisations.value = res.organisations;
|
||||
});
|
||||
|
||||
return { organisations, countries };
|
||||
}
|
||||
|
|
@ -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<Entities | undefined> = 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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<User>) {
|
||||
if (profileData.language) {
|
||||
await setLocale(profileData.language);
|
||||
}
|
||||
|
||||
await itPost("/api/core/me/", profileData, { method: "PUT" });
|
||||
Object.assign(this.$state, profileData);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
]
|
||||
|
|
@ -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),
|
||||
),
|
||||
]
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ class NotificationService:
|
|||
):
|
||||
texts = {
|
||||
"de": "%(sender)s hat den Praxisauftrag «%(assignment_title)s» abgegeben.",
|
||||
"fr": "%(sender)s a soumis la mission pratique «%(assignment_title)s».",
|
||||
"it": "%(sender)s ha consegnato l'incarico pratico «%(assignment_title)s».",
|
||||
"fr": "%(sender)s a soumis l'exercice pratique «%(assignment_title)s».",
|
||||
"it": "%(sender)s ha consegnato il lavoro pratico «%(assignment_title)s».",
|
||||
}
|
||||
# this was the default case before the praxis assignment was introduced
|
||||
else:
|
||||
|
|
@ -87,8 +87,8 @@ class NotificationService:
|
|||
):
|
||||
texts = {
|
||||
"de": "%(sender)s hat den Praxisauftrag «%(assignment_title)s» bewertet.",
|
||||
"fr": "%(sender)s a évalué la mission pratique «%(assignment_title)s».",
|
||||
"it": "%(sender)s ha valutato l'incarico pratico «%(assignment_title)s».",
|
||||
"fr": "%(sender)s a évalué l'exercice pratique «%(assignment_title)s».",
|
||||
"it": "%(sender)s ha valutato il lavoro pratico «%(assignment_title)s».",
|
||||
}
|
||||
# this was the default case before the praxis assignment was introduced
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
),
|
||||
]
|
||||
|
|
@ -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()
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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__"
|
||||
|
|
|
|||
|
|
@ -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,38 @@ class CheckoutAPITestCase(APITestCase):
|
|||
)
|
||||
|
||||
self.client.login(username=USER_USERNAME, password=USER_PASSWORD)
|
||||
add_countries()
|
||||
|
||||
@patch("vbv_lernwelt.shop.views.init_transaction")
|
||||
def test_checkout_no_company_address_updates_user(self, mock_init_transaction):
|
||||
# GIVEN
|
||||
mock_init_transaction.return_value = "1234567890"
|
||||
|
||||
# WHEN
|
||||
response = self.client.post(
|
||||
path=reverse("checkout-vv"),
|
||||
format="json",
|
||||
data={
|
||||
"redirect_url": REDIRECT_URL,
|
||||
"product": VV_DE_PRODUCT_SKU,
|
||||
"address": {
|
||||
"first_name": "Test",
|
||||
"last_name": "User",
|
||||
"street": "Test Street",
|
||||
"street_number": "1",
|
||||
"postal_code": "1234",
|
||||
"city": "Test City",
|
||||
"country": "209",
|
||||
# NO company data
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
user = User.objects.get(username=USER_USERNAME)
|
||||
self.assertEqual(user.invoice_address, User.INVOICE_ADDRESS_PRIVATE)
|
||||
|
||||
@patch("vbv_lernwelt.shop.views.init_transaction")
|
||||
def test_checkout_happy_case(self, mock_init_transaction):
|
||||
|
|
@ -90,6 +123,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
|
||||
|
|
|
|||
|
|
@ -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,35 @@ 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
|
||||
|
||||
if checkout_info.country:
|
||||
user.country = Country.objects.filter(country_id=checkout_info.country).first()
|
||||
|
||||
if (
|
||||
checkout_info.company_name
|
||||
and checkout_info.company_street
|
||||
and checkout_info.company_street_number
|
||||
and checkout_info.company_postal_code
|
||||
and checkout_info.company_city
|
||||
and checkout_info.company_country
|
||||
):
|
||||
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
|
||||
|
||||
user.organisation_country = Country.objects.filter(
|
||||
country_id=checkout_info.company_country
|
||||
).first()
|
||||
|
||||
user.invoice_address = User.INVOICE_ADDRESS_ORGANISATION
|
||||
|
||||
user.save()
|
||||
|
|
|
|||
Loading…
Reference in New Issue