diff --git a/client/src/pages/onboarding/vv/CheckoutAddress.vue b/client/src/pages/onboarding/vv/CheckoutAddress.vue
index f8ff17ef..76e5a6ed 100644
--- a/client/src/pages/onboarding/vv/CheckoutAddress.vue
+++ b/client/src/pages/onboarding/vv/CheckoutAddress.vue
@@ -13,6 +13,12 @@ import DatatransCembraDeviceFingerprint from "@/components/onboarding/DatatransC
import { getLocalSessionKey } from "@/statistics";
import log from "loglevel";
import { normalizeSwissPhoneNumber, validatePhoneNumber } from "@/utils/phone";
+import {
+ ORGANISATION_NO_COMPANY_ID,
+ ORGANISATION_OTHER_BROKER_ID,
+ ORGANISATION_OTHER_HEALTH_INSURANCE_ID,
+ ORGANISATION_OTHER_PRIVATE_INSURANCE_ID,
+} from "@/consts";
const props = defineProps({
courseType: {
@@ -31,11 +37,14 @@ const userOrganisationName = computed(() => {
}
// Those IDs do not represent a company
- // 1: Other broker
- // 2: Other insurance
- // 3: Other private insurance
- // 31: No company relation
- if ([1, 2, 3, 31].includes(user.organisation)) {
+ if (
+ [
+ ORGANISATION_OTHER_BROKER_ID,
+ ORGANISATION_OTHER_HEALTH_INSURANCE_ID,
+ ORGANISATION_OTHER_PRIVATE_INSURANCE_ID,
+ ORGANISATION_NO_COMPANY_ID,
+ ].includes(user.organisation)
+ ) {
return null;
}
diff --git a/client/src/pages/personalProfile/PersonalProfilePage.vue b/client/src/pages/personalProfile/PersonalProfilePage.vue
index 01903add..99312b07 100644
--- a/client/src/pages/personalProfile/PersonalProfilePage.vue
+++ b/client/src/pages/personalProfile/PersonalProfilePage.vue
@@ -45,7 +45,11 @@ function startEditMode() {
-
diff --git a/client/src/services/onboarding.ts b/client/src/services/onboarding.ts
index 24b59a48..9c59ef48 100644
--- a/client/src/services/onboarding.ts
+++ b/client/src/services/onboarding.ts
@@ -1,4 +1,9 @@
import { isString, startsWith } from "lodash";
+import {
+ ORGANISATION_OTHER_BROKER_ID,
+ ORGANISATION_OTHER_HEALTH_INSURANCE_ID,
+ ORGANISATION_OTHER_PRIVATE_INSURANCE_ID,
+} from "@/consts";
export function profileNextRoute(courseType: string | string[]) {
if (courseType === "uk") {
@@ -10,3 +15,11 @@ export function profileNextRoute(courseType: string | string[]) {
}
return "";
}
+
+export function isOtherOrganisation(orgId: number) {
+ return [
+ ORGANISATION_OTHER_BROKER_ID,
+ ORGANISATION_OTHER_HEALTH_INSURANCE_ID,
+ ORGANISATION_OTHER_PRIVATE_INSURANCE_ID,
+ ].includes(orgId);
+}
diff --git a/cypress/e2e/checkout-vv/checkout.cy.js b/cypress/e2e/checkout-vv/checkout.cy.js
index 00f39ac7..e6007b44 100644
--- a/cypress/e2e/checkout-vv/checkout.cy.js
+++ b/cypress/e2e/checkout-vv/checkout.cy.js
@@ -17,22 +17,31 @@ describe("checkout.cy.js", () => {
cy.get('[data-cy="account-confirm-title"]').should(
"contain",
- "Konto erstellen"
+ "Konto erstellen",
);
cy.get('[data-cy="continue-button"]').click();
cy.get('[data-cy="account-profile-title"]').should(
"contain",
- "Profil ergänzen"
+ "Profil ergänzen",
);
cy.get('[data-cy="dropdown-select"]').click();
- cy.get('[data-cy="dropdown-select-option-Baloise"]').click();
+ cy.get(
+ '[data-cy="dropdown-select-option-andere Krankenversicherer"]',
+ ).click();
+ cy.get("#organisationDetailName").type("FdH GmbH");
cy.get('[data-cy="continue-button"]').click();
+ cy.loadUser("id", TEST_USER_EMPTY_ID).then((u) => {
+ expect(u.organisation_detail_name).to.equal("FdH GmbH");
+ // 2 -> andere Krankenversicherer
+ expect(u.organisation).to.equal(2);
+ });
+
// Adressdaten ausfüllen
cy.get('[data-cy="account-checkout-title"]').should(
"contain",
- "Lehrgang kaufen"
+ "Lehrgang kaufen",
);
cy.get("#street-address").type("Eggersmatt");
cy.get("#street-number").type("32");
@@ -40,7 +49,7 @@ describe("checkout.cy.js", () => {
cy.get("#city").type("Zumholz");
cy.get('[data-cy="add-company-address"]').click();
- cy.get("#company-name").type("Iterativ GmbH");
+ // cy.get("#company-name").clear().type("Iterativ GmbH");
cy.get("#company-street-address").type("Brückfeldstrasse");
cy.get("#company-street-number").type("16");
cy.get("#company-postal-code").type("3012");
@@ -60,7 +69,7 @@ describe("checkout.cy.js", () => {
expect(ci.country).to.equal("CH");
expect(ci.invoice_address).to.equal("org");
- expect(ci.organisation_detail_name).to.equal("Iterativ GmbH");
+ expect(ci.organisation_detail_name).to.equal("FdH GmbH");
expect(ci.organisation_street).to.equal("Brückfeldstrasse");
expect(ci.organisation_street_number).to.equal("16");
expect(ci.organisation_postal_code).to.equal("3012");
@@ -72,12 +81,32 @@ describe("checkout.cy.js", () => {
expect(ci.state).to.equal("ongoing");
});
+ cy.loadUser("id", TEST_USER_EMPTY_ID).then((u) => {
+ expect(u.first_name).to.equal("Flasche");
+ expect(u.last_name).to.equal("Leer");
+
+ expect(u.street).to.equal("Eggersmatt");
+ expect(u.street_number).to.equal("32");
+ expect(u.postal_code).to.equal("1719");
+ expect(u.city).to.equal("Zumholz");
+ expect(u.country).to.equal("CH");
+
+ expect(u.invoice_address).to.equal("org");
+ expect(u.organisation_detail_name).to.equal("FdH GmbH");
+ expect(u.organisation_street).to.equal("Brückfeldstrasse");
+ expect(u.organisation_street_number).to.equal("16");
+ expect(u.organisation_postal_code).to.equal("3012");
+ expect(u.organisation_city).to.equal("Bern");
+ // 2 -> andere Krankenversicherer
+ expect(u.organisation).to.equal(2);
+ });
+
// pay
cy.get('[data-cy="pay-button"]').click();
cy.get('[data-cy="checkout-success-title"]').should(
"contain",
- "Gratuliere"
+ "Gratuliere",
);
// wait for payment callback
cy.wait(3000);
@@ -86,7 +115,7 @@ describe("checkout.cy.js", () => {
// back on dashboard page
cy.get('[data-cy="db-course-title"]').should(
"contain",
- "Versicherungsvermittler"
+ "Versicherungsvermittler",
);
cy.loadCheckoutInformation("user_id", TEST_USER_EMPTY_ID).then((ci) => {
@@ -102,13 +131,13 @@ describe("checkout.cy.js", () => {
cy.get('[data-cy="account-confirm-title"]').should(
"contain",
- "Konto erstellen"
+ "Konto erstellen",
);
cy.get('[data-cy="continue-button"]').click();
cy.get('[data-cy="account-profile-title"]').should(
"contain",
- "Profil ergänzen"
+ "Profil ergänzen",
);
cy.get('[data-cy="dropdown-select"]').click();
cy.get('[data-cy="dropdown-select-option-Baloise"]').click();
@@ -117,10 +146,10 @@ describe("checkout.cy.js", () => {
// Adressdaten ausfüllen
cy.get('[data-cy="account-checkout-title"]').should(
"contain",
- "Lehrgang kaufen"
+ "Lehrgang kaufen",
);
- cy.get('#paymentMethod').select('cembra_byjuno');
+ cy.get("#paymentMethod").select("cembra_byjuno");
cy.get("#street-address").type("Eggersmatt");
cy.get("#street-number").type("32");
@@ -132,32 +161,38 @@ describe("checkout.cy.js", () => {
cy.get('[data-cy="continue-pay"]').click();
- cy.loadExternalApiRequestLog("request_username", "empty@example.com").then((entry) => {
- // ends with "/v1/transactions""
- expect(entry.api_url).to.contain("/v1/transactions");
- expect(entry.request_username).to.contain("empty@example.com");
+ cy.loadExternalApiRequestLog("request_username", "empty@example.com").then(
+ (entry) => {
+ // ends with "/v1/transactions""
+ expect(entry.api_url).to.contain("/v1/transactions");
+ expect(entry.request_username).to.contain("empty@example.com");
- expect(entry.api_request_data.amount).to.equal(32400);
- expect(entry.api_request_data.currency).to.equal("CHF");
- expect(entry.api_request_data.autoSettle).to.equal(true);
+ expect(entry.api_request_data.amount).to.equal(32400);
+ expect(entry.api_request_data.currency).to.equal("CHF");
+ expect(entry.api_request_data.autoSettle).to.equal(true);
- expect(entry.api_request_data.customer.firstName).to.equal("Flasche");
- expect(entry.api_request_data.customer.lastName).to.equal("Leer");
- expect(entry.api_request_data.customer.street).to.equal("Eggersmatt 32");
- expect(entry.api_request_data.customer.zipCode).to.equal("1719");
- expect(entry.api_request_data.customer.city).to.equal("Zumholz");
- expect(entry.api_request_data.customer.country).to.equal("CH");
- expect(entry.api_request_data.customer.type).to.equal("P");
- expect(entry.api_request_data.customer.phone).to.equal("+41792018586");
- expect(entry.api_request_data.customer.birthDate).to.equal("1982-06-09");
+ expect(entry.api_request_data.customer.firstName).to.equal("Flasche");
+ expect(entry.api_request_data.customer.lastName).to.equal("Leer");
+ expect(entry.api_request_data.customer.street).to.equal(
+ "Eggersmatt 32",
+ );
+ expect(entry.api_request_data.customer.zipCode).to.equal("1719");
+ expect(entry.api_request_data.customer.city).to.equal("Zumholz");
+ expect(entry.api_request_data.customer.country).to.equal("CH");
+ expect(entry.api_request_data.customer.type).to.equal("P");
+ expect(entry.api_request_data.customer.phone).to.equal("+41792018586");
+ expect(entry.api_request_data.customer.birthDate).to.equal(
+ "1982-06-09",
+ );
- expect(entry.api_request_data.INT.repaymentType).to.equal(3);
- expect(entry.api_request_data.INT.riskOwner).to.equal("IJ");
- expect(entry.api_request_data.INT.subtype).to.equal("INVOICE");
- expect(entry.api_request_data.INT.deviceFingerprintId).to.not.be.empty;
+ expect(entry.api_request_data.INT.repaymentType).to.equal(3);
+ expect(entry.api_request_data.INT.riskOwner).to.equal("IJ");
+ expect(entry.api_request_data.INT.subtype).to.equal("INVOICE");
+ expect(entry.api_request_data.INT.deviceFingerprintId).to.not.be.empty;
- expect(true).to.be.true;
- });
+ expect(true).to.be.true;
+ },
+ );
// check that results are stored on server
cy.loadCheckoutInformation("user_id", TEST_USER_EMPTY_ID).then((ci) => {
@@ -183,5 +218,23 @@ describe("checkout.cy.js", () => {
expect(ci.ip_address).to.not.be.empty;
expect(ci.device_fingerprint_session_key).to.not.be.empty;
});
+
+ cy.loadUser("id", TEST_USER_EMPTY_ID).then((u) => {
+ expect(u.first_name).to.equal("Flasche");
+ expect(u.last_name).to.equal("Leer");
+
+ expect(u.street).to.equal("Eggersmatt");
+ expect(u.street_number).to.equal("32");
+ expect(u.postal_code).to.equal("1719");
+ expect(u.city).to.equal("Zumholz");
+ expect(u.country).to.equal("CH");
+ expect(u.phone_number).to.equal("+41792018586");
+ expect(u.birth_date).to.equal("1982-06-09");
+
+ expect(u.invoice_address).to.equal("prv");
+
+ // 7 -> Baloise
+ expect(u.organisation).to.equal(7);
+ });
});
});
diff --git a/cypress/e2e/personalProfile/personalProfile.cy.js b/cypress/e2e/personalProfile/personalProfile.cy.js
new file mode 100644
index 00000000..2608f53b
--- /dev/null
+++ b/cypress/e2e/personalProfile/personalProfile.cy.js
@@ -0,0 +1,85 @@
+import { TEST_USER_EMPTY_ID } from "../../consts";
+import { login } from "../helpers";
+
+describe("personalProfile.cy.js", () => {
+ beforeEach(() => {
+ cy.manageCommand("cypress_reset");
+
+ login("empty@example.com", "test");
+ cy.visit("/profile");
+ });
+
+ it("can edit all profile fields", () => {
+ cy.get('[data-cy="editProfileButton"]').click();
+
+ cy.get("#phone").type("079 201 85 86");
+ cy.get('[data-test="dp-input"]').type("09.06.1982{enter}");
+
+ cy.get("#street-address").type("Hafen");
+ cy.get("#street-number").type("123");
+ cy.get("#postal-code").type("DE-20095");
+ cy.get("#city").type("Hamburg");
+ cy.get("#country").select("DE");
+
+ // andere broker
+ cy.get("#organisation").select("1");
+
+ cy.get("#org-detail-name").type("Judihui GmbH");
+ cy.get("#org-street-address").type("Auf der Alm");
+ cy.get("#org-street-number").type("17");
+ cy.get("#org-postal-code").type("AT-6020");
+ cy.get("#org-city").type("Innsbruck");
+ cy.get("#org-country").select("AT");
+
+ cy.get("#invoice-address-organisation").click();
+
+ cy.get('[data-cy="saveButton"]').click();
+
+ // check displayed data
+ cy.get('[data-cy="firstName"]').should("contain", "Flasche");
+ cy.get('[data-cy="lastName"]').should("contain", "Leer");
+ cy.get('[data-cy="email"]').should("contain", "empty@example.com");
+ cy.get('[data-cy="phone"]').should("contain", "079 201 85 86");
+ cy.get('[data-cy="birthDate"]').should("contain", "09.06.1982");
+
+ cy.get('[data-cy="privateAddress"]').should("contain", "Hafen 123");
+ cy.get('[data-cy="privateAddress"]').should("contain", "DE-20095 Hamburg");
+ cy.get('[data-cy="privateAddress"]').should("contain", "Deutschland");
+
+ cy.get('[data-cy="organisationDetailName"]').should(
+ "contain",
+ "andere Broker",
+ );
+ cy.get('[data-cy="organisationAddress"]').should("contain", "Judihui GmbH");
+ cy.get('[data-cy="organisationAddress"]').should(
+ "contain",
+ "Auf der Alm 17",
+ );
+ cy.get('[data-cy="organisationAddress"]').should(
+ "contain",
+ "AT-6020 Innsbruck",
+ );
+ cy.get('[data-cy="organisationAddress"]').should("contain", "Österreich");
+
+ // check stored data
+ cy.loadUser("id", TEST_USER_EMPTY_ID).then((u) => {
+ expect(u.first_name).to.equal("Flasche");
+ expect(u.last_name).to.equal("Leer");
+
+ expect(u.street).to.equal("Hafen");
+ expect(u.street_number).to.equal("123");
+ expect(u.postal_code).to.equal("DE-20095");
+ expect(u.city).to.equal("Hamburg");
+ expect(u.country).to.equal("DE");
+
+ expect(u.invoice_address).to.equal("org");
+ expect(u.organisation_detail_name).to.equal("Judihui GmbH");
+ expect(u.organisation_street).to.equal("Auf der Alm");
+ expect(u.organisation_street_number).to.equal("17");
+ expect(u.organisation_postal_code).to.equal("AT-6020");
+ expect(u.organisation_city).to.equal("Innsbruck");
+ // 1 -> andere Broker
+ expect(u.organisation).to.equal(1);
+ });
+ });
+});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 50dfdf30..3564a74c 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -178,6 +178,17 @@ Cypress.Commands.add("loadCheckoutInformation", (key, value) => {
);
});
+Cypress.Commands.add("loadUser", (key, value) => {
+ return loadObjectJson(
+ key,
+ value,
+ "vbv_lernwelt.core.models.User",
+ "vbv_lernwelt.core.serializers.CypressUserSerializer",
+ true
+ );
+});
+
+
Cypress.Commands.add("makeSelfEvaluation", (answers) => {
for (let i = 0; i < answers.length; i++) {
const answer = answers[i];
diff --git a/env_secrets/caprover_vbv-develop.env b/env_secrets/caprover_vbv-develop.env
index c633f417..5432d3c8 100644
Binary files a/env_secrets/caprover_vbv-develop.env and b/env_secrets/caprover_vbv-develop.env differ
diff --git a/env_secrets/local_daniel.env b/env_secrets/local_daniel.env
index 7ae4f656..703bd5e9 100644
Binary files a/env_secrets/local_daniel.env and b/env_secrets/local_daniel.env differ
diff --git a/server/config/settings/base.py b/server/config/settings/base.py
index 8251e138..de7744aa 100644
--- a/server/config/settings/base.py
+++ b/server/config/settings/base.py
@@ -333,7 +333,6 @@ X_FRAME_OPTIONS = "DENY"
# EMAIL
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
-# FIXME how to send emails?
EMAIL_BACKEND = env(
"DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
)
@@ -683,10 +682,12 @@ if APP_ENVIRONMENT.startswith("prod"):
DATATRANS_PAY_URL = "https://pay.datatrans.com"
else:
DATATRANS_API_ENDPOINT = env(
- "DATATRANS_API_ENDPOINT", default="https://api.sandbox.datatrans.com"
+ "DATATRANS_API_ENDPOINT",
+ default="http://localhost:8000/server/fakeapi/datatrans/api",
)
DATATRANS_PAY_URL = env(
- "DATATRANS_PAY_URL", default="https://pay.sandbox.datatrans.com"
+ "DATATRANS_PAY_URL",
+ default="http://localhost:8000/server/fakeapi/datatrans/pay",
)
# default settings for python sftpserver test-server
diff --git a/server/vbv_lernwelt/core/constants.py b/server/vbv_lernwelt/core/constants.py
index 75f24848..e36702b8 100644
--- a/server/vbv_lernwelt/core/constants.py
+++ b/server/vbv_lernwelt/core/constants.py
@@ -28,6 +28,7 @@ TEST_MENTOR1_USER_ID = "d1f5f5a9-5b0a-4e1a-9e1a-9e9b5b5e1b1b"
TEST_STUDENT1_VV_USER_ID = "5ff59857-8de5-415e-a387-4449f9a0337a"
TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID = "7e8ebf0b-e6e2-4022-88f4-6e663ba0a9db"
TEST_USER_EMPTY_ID = "daecbabe-4ab9-4edf-a71f-4119042ccb02"
+TEST_USER_DATATRANS_HANNA_ID = "6bec1a0d-f852-47aa-a4de-072df6e07ad1"
TEST_COURSE_SESSION_BERN_ID = -1
TEST_COURSE_SESSION_ZURICH_ID = -2
diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py
index 17acd036..beeaca53 100644
--- a/server/vbv_lernwelt/core/create_default_users.py
+++ b/server/vbv_lernwelt/core/create_default_users.py
@@ -4,6 +4,7 @@ from django.contrib.auth.models import Group, Permission
from django.core.files import File
from environs import Env
+from vbv_lernwelt.core.model_utils import add_countries
from vbv_lernwelt.media_files.models import UserImage
env = Env()
@@ -20,6 +21,7 @@ from vbv_lernwelt.core.constants import (
TEST_SUPERVISOR1_USER_ID,
TEST_TRAINER1_USER_ID,
TEST_TRAINER2_USER_ID,
+ TEST_USER_DATATRANS_HANNA_ID,
TEST_USER_EMPTY_ID,
)
from vbv_lernwelt.core.models import User
@@ -78,7 +80,30 @@ default_users = [
AVATAR_DIR = settings.APPS_DIR / "static" / "avatars"
+def create_datatrans_hanna_user():
+ hanna, _ = User.objects.get_or_create(
+ id=TEST_USER_DATATRANS_HANNA_ID,
+ )
+ hanna.username = "datatrans.hanna.vbv@example.com"
+ hanna.email = "datatrans.hanna.vbv@example.com"
+ hanna.language = "de"
+ hanna.first_name = "Hanna"
+ hanna.last_name = "Vbv"
+ hanna.street = "Bahnstrasse"
+ hanna.street_number = "2"
+ hanna.postal_code = "8603"
+ hanna.city = "Schwerzenbach"
+ hanna.country_id = "CH"
+ hanna.birth_date = "1970-01-01"
+ hanna.phone_number = "+41792018586"
+ hanna.password = make_password("test")
+ hanna.save()
+ return hanna
+
+
def create_default_users(default_password="test", set_avatar=False):
+ add_countries(small_set=True)
+
admin_group, created = Group.objects.get_or_create(name="admin_group")
_content_creator_group, _created = Group.objects.get_or_create(
name="content_creator_grop"
@@ -202,6 +227,8 @@ def create_default_users(default_password="test", set_avatar=False):
language="de",
)
+ hanna = create_datatrans_hanna_user()
+
for user_data in default_users:
_create_student_user(**user_data)
diff --git a/server/vbv_lernwelt/core/management/commands/cypress_reset.py b/server/vbv_lernwelt/core/management/commands/cypress_reset.py
index abfae404..1a02a343 100644
--- a/server/vbv_lernwelt/core/management/commands/cypress_reset.py
+++ b/server/vbv_lernwelt/core/management/commands/cypress_reset.py
@@ -17,8 +17,10 @@ from vbv_lernwelt.core.constants import (
TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID,
TEST_STUDENT3_USER_ID,
TEST_TRAINER1_USER_ID,
+ TEST_USER_DATATRANS_HANNA_ID,
TEST_USER_EMPTY_ID,
)
+from vbv_lernwelt.core.create_default_users import create_datatrans_hanna_user
from vbv_lernwelt.core.models import Organisation, User
from vbv_lernwelt.course.consts import (
COURSE_TEST_ID,
@@ -159,6 +161,9 @@ def command(
password=make_password("test"),
)
+ User.objects.filter(id=TEST_USER_DATATRANS_HANNA_ID).delete()
+ create_datatrans_hanna_user()
+
cursor = connection.cursor()
cursor.execute("truncate core_securityrequestresponselog;")
cursor.execute("truncate core_externalapirequestlog;")
diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py
index a1b09008..9d0ca936 100644
--- a/server/vbv_lernwelt/core/models.py
+++ b/server/vbv_lernwelt/core/models.py
@@ -114,8 +114,9 @@ class User(AbstractUser):
blank=True,
)
- # fields gathered from cembra pay form
birth_date = models.DateField(null=True, blank=True)
+
+ # phone number should be stored in the format +41792018586 (not validated)
phone_number = models.CharField(max_length=255, blank=True, default="")
# is only set by abacus invoice export code
diff --git a/server/vbv_lernwelt/core/serializers.py b/server/vbv_lernwelt/core/serializers.py
index 3a656c71..31965017 100644
--- a/server/vbv_lernwelt/core/serializers.py
+++ b/server/vbv_lernwelt/core/serializers.py
@@ -131,6 +131,12 @@ class UserSerializer(serializers.ModelSerializer):
return instance
+class CypressUserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = User
+ fields = "__all__"
+
+
class OrganisationSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source="organisation_id", read_only=True)
name = serializers.SerializerMethodField()
diff --git a/server/vbv_lernwelt/course/consts.py b/server/vbv_lernwelt/course/consts.py
index a0dd7b27..a9949458 100644
--- a/server/vbv_lernwelt/course/consts.py
+++ b/server/vbv_lernwelt/course/consts.py
@@ -28,3 +28,10 @@ UK_COURSE_IDS = [
COURSE_UK_TRAINING_FR,
COURSE_UK_TRAINING_IT,
]
+
+
+# Organization IDs
+ORGANISATION_OTHER_BROKER_ID = 1
+ORGANISATION_OTHER_HEALTH_INSURANCE_ID = 2
+ORGANISATION_OTHER_PRIVATE_INSURANCE_ID = 3
+ORGANISATION_NO_COMPANY_ID = 31
diff --git a/server/vbv_lernwelt/importer/services.py b/server/vbv_lernwelt/importer/services.py
index f4594a24..20de544f 100644
--- a/server/vbv_lernwelt/importer/services.py
+++ b/server/vbv_lernwelt/importer/services.py
@@ -765,7 +765,7 @@ def create_or_update_course_session_assignment(
csa.submission_deadline.save()
csa.evaluation_deadline.start = timezone.make_aware(
start
- ) + timezone.timedelta(days=45)
+ ) + timezone.timedelta(days=60)
csa.evaluation_deadline.end = None
csa.evaluation_deadline.save()
else:
diff --git a/server/vbv_lernwelt/shop/services.py b/server/vbv_lernwelt/shop/services.py
index f5998d38..0e7bd7b3 100644
--- a/server/vbv_lernwelt/shop/services.py
+++ b/server/vbv_lernwelt/shop/services.py
@@ -1,13 +1,11 @@
import hashlib
import hmac
-import uuid
import requests
import structlog
from django.conf import settings
from vbv_lernwelt.core.admin import User
-from vbv_lernwelt.shop.const import VV_PRODUCT_NUMBER
from vbv_lernwelt.shop.datatrans.datatrans_api_client import DatatransApiClient
from vbv_lernwelt.shop.models import CheckoutState
@@ -73,6 +71,7 @@ def is_signature_valid(
def init_datatrans_transaction(
user: User,
+ refno: str,
amount_chf_centimes: int,
redirect_url_success: str,
redirect_url_error: str,
@@ -91,8 +90,8 @@ def init_datatrans_transaction(
"amount": amount_chf_centimes,
"currency": "CHF",
"language": user.language,
- "refno": str(uuid.uuid4()),
- "refno2": refno2,
+ "refno": str(refno),
+ "refno2": str(refno2),
"webhook": {"url": webhook_url},
"redirect": {
"successUrl": redirect_url_success,
@@ -101,9 +100,8 @@ def init_datatrans_transaction(
},
}
- # FIXME: test with working cembra byjuno invoice customer?
- # if with_cembra_byjuno_invoice:
- # payload["paymentMethods"] = ["INT"]
+ if with_cembra_byjuno_invoice:
+ payload["paymentMethods"] = ["INT"]
if datatrans_customer_data:
payload["customer"] = datatrans_customer_data
if datatrans_int_data:
diff --git a/server/vbv_lernwelt/shop/tests/test_checkout_api.py b/server/vbv_lernwelt/shop/tests/test_checkout_api.py
index efbee5da..f5f46b37 100644
--- a/server/vbv_lernwelt/shop/tests/test_checkout_api.py
+++ b/server/vbv_lernwelt/shop/tests/test_checkout_api.py
@@ -71,9 +71,8 @@ class CheckoutAPITestCase(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(
- f"https://pay.sandbox.datatrans.com/v1/start/1234567890",
- response.json()["next_step_url"],
+ self.assertTrue(
+ response.json()["next_step_url"].endswith("v1/start/1234567890")
)
ci = CheckoutInformation.objects.first()
@@ -154,9 +153,11 @@ class CheckoutAPITestCase(APITestCase):
)
self.assertEqual(
- 0,
+ 1,
CheckoutInformation.objects.count(),
)
+ ci = CheckoutInformation.objects.first()
+ self.assertEqual(ci.state, CheckoutState.FAILED)
def test_checkout_already_paid(self):
# GIVEN
@@ -217,9 +218,8 @@ class CheckoutAPITestCase(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(
- f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id_next}",
- response.json()["next_step_url"],
+ self.assertTrue(
+ response.json()["next_step_url"].endswith(f"v1/start/{transaction_id_next}")
)
# check that we have two checkouts
@@ -277,9 +277,8 @@ class CheckoutAPITestCase(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(
- f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}",
- response.json()["next_step_url"],
+ self.assertTrue(
+ response.json()["next_step_url"].endswith(f"v1/start/{transaction_id}")
)
@patch("vbv_lernwelt.shop.views.init_datatrans_transaction")
@@ -310,7 +309,6 @@ class CheckoutAPITestCase(APITestCase):
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(
- f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}",
- response.json()["next_step_url"],
+ self.assertTrue(
+ response.json()["next_step_url"].endswith(f"v1/start/{transaction_id}")
)
diff --git a/server/vbv_lernwelt/shop/tests/test_datatrans_service.py b/server/vbv_lernwelt/shop/tests/test_datatrans_service.py
index 6dc907fe..7f22c8ee 100644
--- a/server/vbv_lernwelt/shop/tests/test_datatrans_service.py
+++ b/server/vbv_lernwelt/shop/tests/test_datatrans_service.py
@@ -24,10 +24,8 @@ class DatatransServiceTest(TestCase):
@override_settings(DATATRANS_BASIC_AUTH_KEY="BASIC_AUTH_KEY")
@patch("vbv_lernwelt.shop.services.requests.post")
- @patch("vbv_lernwelt.shop.services.uuid.uuid4")
- def test_init_transaction_201(self, mock_uuid, mock_post):
+ def test_init_transaction_201(self, mock_post):
# GIVEN
- mock_uuid.return_value = uuid.uuid4()
mock_post.return_value.status_code = 201
mock_post.return_value.json.return_value = {
"transactionId": 1234567890,
@@ -38,6 +36,7 @@ class DatatransServiceTest(TestCase):
# WHEN
transaction_id = init_datatrans_transaction(
user=self.user,
+ refno="123321",
amount_chf_centimes=324_30,
redirect_url_success=f"{REDIRECT_URL}/success",
redirect_url_error=f"{REDIRECT_URL}/error",
@@ -64,11 +63,12 @@ class DatatransServiceTest(TestCase):
with self.assertRaises(InitTransactionException):
init_datatrans_transaction(
user=self.user,
+ refno="123321",
amount_chf_centimes=324_30,
- redirect_url_success=f"/success",
- redirect_url_error=f"/error",
- redirect_url_cancel=f"/cancel",
- webhook_url=f"/webhook",
+ redirect_url_success="/success",
+ redirect_url_error="/error",
+ redirect_url_cancel="/cancel",
+ webhook_url="/webhook",
refno2="",
)
@@ -80,7 +80,4 @@ class DatatransServiceTest(TestCase):
url = get_payment_url(transaction_id)
# THEN
- self.assertEqual(
- url,
- f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}",
- )
+ self.assertTrue(url.endswith(f"v1/start/{transaction_id}"))
diff --git a/server/vbv_lernwelt/shop/views.py b/server/vbv_lernwelt/shop/views.py
index 1ce904a3..a245520a 100644
--- a/server/vbv_lernwelt/shop/views.py
+++ b/server/vbv_lernwelt/shop/views.py
@@ -93,7 +93,7 @@ def checkout_vv(request):
sku = request.data["product"]
base_redirect_url = request.data["redirect_url"]
- log.info(f"Checkout requested: sku", user_id=request.user.id, sku=sku)
+ log.info("Checkout requested: sku", user_id=request.user.id, sku=sku)
try:
product = Product.objects.get(sku=sku)
@@ -124,6 +124,38 @@ def checkout_vv(request):
disable_save="fakeapi" in settings.DATATRANS_API_ENDPOINT
)
+ address_data = request.data["address"]
+ country_code = address_data.pop("country_code")
+ address_data["country_id"] = country_code
+
+ organisation_country_code = "CH"
+ if "organisation_country_code" in address_data:
+ organisation_country_code = address_data.pop("organisation_country_code")
+ address_data["organisation_country_id"] = organisation_country_code
+
+ if "birth_date" in address_data and address_data["birth_date"]:
+ address_data["birth_date"] = date.fromisoformat(address_data["birth_date"])
+
+ checkout_info = CheckoutInformation.objects.create(
+ user=request.user,
+ state=CheckoutState.ONGOING,
+ # product
+ product_sku=sku,
+ product_price=product.price,
+ product_name=product.name,
+ product_description=product.description,
+ email=email,
+ ip_address=ip_address,
+ cembra_byjuno_invoice=with_cembra_byjuno_invoice,
+ device_fingerprint_session_key=request.data.get(
+ "device_fingerprint_session_key", ""
+ ),
+ # address
+ **request.data["address"],
+ )
+
+ checkout_info.set_increment_abacus_order_id()
+
refno2 = f"{request.user.abacus_debitor_number}_{VV_PRODUCT_NUMBER}"
try:
@@ -138,10 +170,10 @@ def checkout_vv(request):
"street": f'{request.data["address"]["street"]} {request.data["address"]["street_number"]}',
"city": request.data["address"]["city"],
"zipCode": request.data["address"]["postal_code"],
- "country": request.data["address"]["country_code"],
+ "country": request.data["address"]["country_id"],
"phone": request.data["address"]["phone_number"],
"email": email,
- "birthDate": request.data["address"]["birth_date"],
+ "birthDate": str(request.data["address"]["birth_date"]),
"language": request.user.language,
"ipAddress": ip_address,
"type": "P",
@@ -154,6 +186,7 @@ def checkout_vv(request):
}
transaction_id = init_datatrans_transaction(
user=request.user,
+ refno=str(checkout_info.abacus_order_id),
amount_chf_centimes=product.price,
redirect_url_success=checkout_success_url(
base_url=base_redirect_url, product_sku=sku
@@ -170,6 +203,8 @@ def checkout_vv(request):
with_cembra_byjuno_invoice=with_cembra_byjuno_invoice,
)
except InitTransactionException as e:
+ checkout_info.state = CheckoutState.FAILED.value
+ checkout_info.save()
if not settings.DEBUG:
log.error("Transaction initiation failed", exc_info=True, error=str(e))
capture_exception(e)
@@ -180,36 +215,8 @@ def checkout_vv(request):
),
)
- address_data = request.data["address"]
- country_code = address_data.pop("country_code")
- address_data["country_id"] = country_code
-
- organisation_country_code = "CH"
- if "organisation_country_code" in address_data:
- organisation_country_code = address_data.pop("organisation_country_code")
- address_data["organisation_country_id"] = organisation_country_code
-
- if "birth_date" in address_data and address_data["birth_date"]:
- address_data["birth_date"] = date.fromisoformat(address_data["birth_date"])
-
- checkout_info = CheckoutInformation.objects.create(
- user=request.user,
- state=CheckoutState.ONGOING,
- transaction_id=transaction_id,
- # product
- product_sku=sku,
- product_price=product.price,
- product_name=product.name,
- product_description=product.description,
- email=email,
- ip_address=ip_address,
- cembra_byjuno_invoice=with_cembra_byjuno_invoice,
- device_fingerprint_session_key=request.data.get(
- "device_fingerprint_session_key", ""
- ),
- # address
- **request.data["address"],
- )
+ checkout_info.transaction_id = transaction_id
+ checkout_info.save()
return next_step_response(url=get_payment_url(transaction_id))