diff --git a/client/src/pages/onboarding/vv/CheckoutAddress.vue b/client/src/pages/onboarding/vv/CheckoutAddress.vue index 3bcfa0d3..918dc3a3 100644 --- a/client/src/pages/onboarding/vv/CheckoutAddress.vue +++ b/client/src/pages/onboarding/vv/CheckoutAddress.vue @@ -229,6 +229,7 @@ const executePayment = async () => { redirect_url: fullHost, address: addressData, product: props.courseType, + chosen_profile: user.chosen_profile?.id || "", with_cembra_byjuno_invoice: address.value.payment_method === "cembra_byjuno", device_fingerprint_session_key: getLocalSessionKey(), }).then((res: any) => { diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts index 2c85dba7..6e9e9e53 100644 --- a/client/src/stores/user.ts +++ b/client/src/stores/user.ts @@ -1,6 +1,6 @@ import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers"; import { setI18nLanguage } from "@/i18nextWrapper"; -import type { Country } from "@/services/entities"; +import type { Country, CourseProfile } from "@/services/entities"; import { directUpload } from "@/services/files"; import dayjs from "dayjs"; import { defineStore } from "pinia"; @@ -44,6 +44,7 @@ export interface User { organisation_postal_code: string; organisation_city: string; organisation_country: Country | null; + chosen_profile?: CourseProfile; } let defaultLanguage: AvailableLanguages = "de"; @@ -89,6 +90,7 @@ const initialUserState: User = { organisation_postal_code: "", organisation_city: "", organisation_country: null, + chosen_profile: undefined, }; async function setLocale(language: AvailableLanguages) { @@ -176,5 +178,8 @@ export const useUserStore = defineStore({ await itPost("/api/core/me/", profileData, { method: "PUT" }); Object.assign(this.$state, profileData); }, + updateChosenCourseProfile(courseProfile: CourseProfile) { + Object.assign(this.$state, { chosen_profile: courseProfile }); + }, }, }); diff --git a/server/vbv_lernwelt/course/migrations/0010_alter_coursecompletion_completion_status.py b/server/vbv_lernwelt/course/migrations/0010_alter_coursecompletion_completion_status.py new file mode 100644 index 00000000..277c4058 --- /dev/null +++ b/server/vbv_lernwelt/course/migrations/0010_alter_coursecompletion_completion_status.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.13 on 2024-07-22 19:45 + +from django.db import migrations, models +import vbv_lernwelt.course.models + + +class Migration(migrations.Migration): + dependencies = [ + ("course", "0009_coursesessionuser_chosen_profile"), + ] + + operations = [ + migrations.AlterField( + model_name="coursecompletion", + name="completion_status", + field=models.CharField( + choices=[ + ("SUCCESS", "Success"), + ("FAIL", "Fail"), + ("UNKNOWN", "Unknown"), + ], + default=vbv_lernwelt.course.models.CourseCompletionStatus["UNKNOWN"], + max_length=255, + ), + ), + ] diff --git a/server/vbv_lernwelt/media_files/migrations/0002_alter_contentimagerendition_file_and_more.py b/server/vbv_lernwelt/media_files/migrations/0002_alter_contentimagerendition_file_and_more.py new file mode 100644 index 00000000..f2c445fd --- /dev/null +++ b/server/vbv_lernwelt/media_files/migrations/0002_alter_contentimagerendition_file_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.13 on 2024-07-22 19:45 + +from django.db import migrations +import wagtail.images.models + + +class Migration(migrations.Migration): + dependencies = [ + ("media_files", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="contentimagerendition", + name="file", + field=wagtail.images.models.WagtailImageField( + height_field="height", + storage=wagtail.images.models.get_rendition_storage, + upload_to=wagtail.images.models.get_rendition_upload_to, + width_field="width", + ), + ), + migrations.AlterField( + model_name="userimagerendition", + name="file", + field=wagtail.images.models.WagtailImageField( + height_field="height", + storage=wagtail.images.models.get_rendition_storage, + upload_to=wagtail.images.models.get_rendition_upload_to, + width_field="width", + ), + ), + ] diff --git a/server/vbv_lernwelt/shop/migrations/0017_checkoutinformation_chosen_profile.py b/server/vbv_lernwelt/shop/migrations/0017_checkoutinformation_chosen_profile.py new file mode 100644 index 00000000..28a4f136 --- /dev/null +++ b/server/vbv_lernwelt/shop/migrations/0017_checkoutinformation_chosen_profile.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.13 on 2024-07-22 19:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("learnpath", "0020_auto_20240711_1736"), + ("shop", "0016_alter_checkoutinformation_refno2"), + ] + + operations = [ + migrations.AddField( + model_name="checkoutinformation", + name="chosen_profile", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="learnpath.courseprofile", + ), + ), + ] diff --git a/server/vbv_lernwelt/shop/models.py b/server/vbv_lernwelt/shop/models.py index 04694fc6..5a6da1ac 100644 --- a/server/vbv_lernwelt/shop/models.py +++ b/server/vbv_lernwelt/shop/models.py @@ -106,7 +106,9 @@ class CheckoutInformation(models.Model): null=True, blank=True, ) - + chosen_profile = models.ForeignKey( + "learnpath.CourseProfile", on_delete=models.SET_NULL, null=True, blank=True + ) # webhook metadata webhook_history = models.JSONField(default=list) diff --git a/server/vbv_lernwelt/shop/views.py b/server/vbv_lernwelt/shop/views.py index a245520a..ea752b23 100644 --- a/server/vbv_lernwelt/shop/views.py +++ b/server/vbv_lernwelt/shop/views.py @@ -8,6 +8,8 @@ from rest_framework.permissions import IsAuthenticated from sentry_sdk import capture_exception from vbv_lernwelt.course.models import CourseSession, CourseSessionUser +from vbv_lernwelt.learnpath.consts import COURSE_PROFILE_ALL_ID +from vbv_lernwelt.learnpath.models import CourseProfile from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email from vbv_lernwelt.shop.const import ( VV_DE_PRODUCT_SKU, @@ -17,11 +19,11 @@ from vbv_lernwelt.shop.const import ( ) from vbv_lernwelt.shop.models import CheckoutInformation, CheckoutState, Product from vbv_lernwelt.shop.services import ( + InitTransactionException, create_context_data_log, datatrans_state_to_checkout_state, get_payment_url, init_datatrans_transaction, - InitTransactionException, is_signature_valid, ) @@ -92,6 +94,7 @@ def checkout_vv(request): sku = request.data["product"] base_redirect_url = request.data["redirect_url"] + chosen_profile_id = request.data["chosen_profile"] log.info("Checkout requested: sku", user_id=request.user.id, sku=sku) @@ -106,6 +109,11 @@ def checkout_vv(request): ), ) + try: + chosen_profile = CourseProfile.objects.get(id=chosen_profile_id) + except CourseProfile.DoesNotExist: + chosen_profile = CourseProfile.objects.get(id=COURSE_PROFILE_ALL_ID) + checkouts = CheckoutInformation.objects.filter( user=request.user, product_sku=sku, @@ -151,6 +159,7 @@ def checkout_vv(request): "device_fingerprint_session_key", "" ), # address + chosen_profile=chosen_profile, **request.data["address"], ) @@ -257,9 +266,11 @@ def create_vv_course_session_user(checkout_info: CheckoutInformation): _, created = CourseSessionUser.objects.get_or_create( user=checkout_info.user, role=CourseSessionUser.Role.MEMBER, + chosen_profile=checkout_info.chosen_profile, course_session=CourseSession.objects.get( id=PRODUCT_SKU_TO_COURSE_SESSION_ID[checkout_info.product_sku] ), + # chosen_profile=bla, ) if created: