Remove unused BillingAddress model
This commit is contained in:
parent
2646b072ee
commit
ec21238ece
|
|
@ -4,12 +4,12 @@ import { useEntities } from "@/services/entities";
|
|||
|
||||
const props = defineProps<{
|
||||
modelValue: {
|
||||
company_name: string;
|
||||
company_street: string;
|
||||
company_street_number: string;
|
||||
company_postal_code: string;
|
||||
company_city: string;
|
||||
company_country: string;
|
||||
organisation_detail_name: string;
|
||||
organisation_street: string;
|
||||
organisation_street_number: string;
|
||||
organisation_postal_code: string;
|
||||
organisation_city: string;
|
||||
organisation_country_code: string;
|
||||
};
|
||||
}>();
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ const orgAddress = computed({
|
|||
<div class="mt-2">
|
||||
<input
|
||||
id="company-name"
|
||||
v-model="orgAddress.company_name"
|
||||
v-model="orgAddress.organisation_detail_name"
|
||||
type="text"
|
||||
required
|
||||
name="company-name"
|
||||
|
|
@ -57,7 +57,7 @@ const orgAddress = computed({
|
|||
<div class="mt-2">
|
||||
<input
|
||||
id="company-street-address"
|
||||
v-model="orgAddress.company_street"
|
||||
v-model="orgAddress.organisation_street"
|
||||
type="text"
|
||||
required
|
||||
name="street-address"
|
||||
|
|
@ -77,7 +77,7 @@ const orgAddress = computed({
|
|||
<div class="mt-2">
|
||||
<input
|
||||
id="company-street-number"
|
||||
v-model="orgAddress.company_street_number"
|
||||
v-model="orgAddress.organisation_street_number"
|
||||
name="street-number"
|
||||
type="text"
|
||||
autocomplete="street-number"
|
||||
|
|
@ -95,7 +95,7 @@ const orgAddress = computed({
|
|||
<div class="mt-2">
|
||||
<input
|
||||
id="company-postal-code"
|
||||
v-model="orgAddress.company_postal_code"
|
||||
v-model="orgAddress.organisation_postal_code"
|
||||
type="text"
|
||||
required
|
||||
name="postal-code"
|
||||
|
|
@ -115,7 +115,7 @@ const orgAddress = computed({
|
|||
<div class="mt-2">
|
||||
<input
|
||||
id="company-city"
|
||||
v-model="orgAddress.company_city"
|
||||
v-model="orgAddress.organisation_city"
|
||||
type="text"
|
||||
name="city"
|
||||
required
|
||||
|
|
@ -135,7 +135,7 @@ const orgAddress = computed({
|
|||
<div class="mt-2">
|
||||
<select
|
||||
id="company-country"
|
||||
v-model="orgAddress.company_country"
|
||||
v-model="orgAddress.organisation_country_code"
|
||||
required
|
||||
name="country"
|
||||
autocomplete="country-name"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const props = defineProps<{
|
|||
street_number: string;
|
||||
postal_code: string;
|
||||
city: string;
|
||||
country: string;
|
||||
country_code: string;
|
||||
};
|
||||
}>();
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ const address = computed({
|
|||
<div class="mt-2">
|
||||
<select
|
||||
id="country"
|
||||
v-model="address.country"
|
||||
v-model="address.country_code"
|
||||
required
|
||||
name="country"
|
||||
autocomplete="country-name"
|
||||
|
|
|
|||
|
|
@ -2,32 +2,15 @@
|
|||
import WizardPage from "@/components/onboarding/WizardPage.vue";
|
||||
import type { Ref } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { type User, useUserStore } from "@/stores/user";
|
||||
import PersonalAddress from "@/components/onboarding/PersonalAddress.vue";
|
||||
import OrganisationAddress from "@/components/onboarding/OrganisationAddress.vue";
|
||||
import { itPost, itPut } from "@/fetchHelpers";
|
||||
import { itPost } from "@/fetchHelpers";
|
||||
import { useEntities } from "@/services/entities";
|
||||
import { useDebounceFn, useFetch } from "@vueuse/core";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
import { getVVCourseName } from "./composables";
|
||||
|
||||
type BillingAddressType = {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
street: string;
|
||||
street_number: string;
|
||||
postal_code: string;
|
||||
city: string;
|
||||
country: string;
|
||||
company_name: string;
|
||||
company_street: string;
|
||||
company_street_number: string;
|
||||
company_postal_code: string;
|
||||
company_city: string;
|
||||
company_country: string;
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
courseType: {
|
||||
type: String,
|
||||
|
|
@ -37,7 +20,7 @@ const props = defineProps({
|
|||
|
||||
const user = useUserStore();
|
||||
const route = useRoute();
|
||||
const { organisations } = useEntities();
|
||||
const { organisations, countries } = useEntities();
|
||||
|
||||
const userOrganisationName = computed(() => {
|
||||
if (!user.organisation) {
|
||||
|
|
@ -61,56 +44,27 @@ const paymentError = computed(() => {
|
|||
});
|
||||
|
||||
const address = ref({
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
street: "",
|
||||
street_number: "",
|
||||
postal_code: "",
|
||||
city: "",
|
||||
country: "",
|
||||
company_name: "",
|
||||
company_street: "",
|
||||
company_street_number: "",
|
||||
company_postal_code: "",
|
||||
company_city: "",
|
||||
company_country: "",
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
street: user.street,
|
||||
street_number: user.street_number,
|
||||
postal_code: user.postal_code,
|
||||
city: user.city,
|
||||
country_code: user.country?.country_code ?? "CH",
|
||||
organisation_detail_name: user.organisation_detail_name,
|
||||
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_code: user.organisation_country?.country_code ?? "CH",
|
||||
invoice_address: user.invoice_address ?? "prv",
|
||||
});
|
||||
|
||||
const useCompanyAddress = ref(false);
|
||||
const fetchBillingAddress = useFetch("/api/shop/billing-address/").json();
|
||||
const billingAddressData: Ref<BillingAddressType | null> = fetchBillingAddress.data;
|
||||
const useCompanyAddress = ref(user.invoice_address === "org");
|
||||
|
||||
watch(billingAddressData, (newVal) => {
|
||||
if (newVal) {
|
||||
address.value = newVal;
|
||||
useCompanyAddress.value = !!newVal.company_name;
|
||||
}
|
||||
});
|
||||
|
||||
const updateAddress = useDebounceFn(() => {
|
||||
itPut("/api/shop/billing-address/update/", address.value);
|
||||
}, 500);
|
||||
|
||||
watch(
|
||||
address,
|
||||
(newVal, oldVal) => {
|
||||
if (Object.values(oldVal).every((x) => x === "")) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateAddress();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const removeCompanyAddress = () => {
|
||||
useCompanyAddress.value = false;
|
||||
address.value.company_name = "";
|
||||
address.value.company_street = "";
|
||||
address.value.company_street_number = "";
|
||||
address.value.company_postal_code = "";
|
||||
address.value.company_city = "";
|
||||
address.value.company_country = "";
|
||||
const setUseCompanyAddress = (value: boolean) => {
|
||||
useCompanyAddress.value = value;
|
||||
address.value.invoice_address = value ? "org" : "prv";
|
||||
};
|
||||
|
||||
type FormErrors = {
|
||||
|
|
@ -153,44 +107,59 @@ function validateAddress() {
|
|||
formErrors.value.personal.push(t("a.Ort"));
|
||||
}
|
||||
|
||||
if (!address.value.country) {
|
||||
if (!address.value.country_code) {
|
||||
formErrors.value.personal.push(t("a.Land"));
|
||||
}
|
||||
|
||||
if (useCompanyAddress.value) {
|
||||
if (!address.value.company_name) {
|
||||
if (!address.value.organisation_detail_name) {
|
||||
formErrors.value.company.push(t("a.Name"));
|
||||
}
|
||||
|
||||
if (!address.value.company_street) {
|
||||
if (!address.value.organisation_street) {
|
||||
formErrors.value.company.push(t("a.Strasse"));
|
||||
}
|
||||
|
||||
if (!address.value.company_street_number) {
|
||||
if (!address.value.organisation_street_number) {
|
||||
formErrors.value.company.push(t("a.Hausnummmer"));
|
||||
}
|
||||
|
||||
if (!address.value.company_postal_code) {
|
||||
if (!address.value.organisation_postal_code) {
|
||||
formErrors.value.company.push(t("a.PLZ"));
|
||||
}
|
||||
|
||||
if (!address.value.company_city) {
|
||||
if (!address.value.organisation_city) {
|
||||
formErrors.value.company.push(t("a.Ort"));
|
||||
}
|
||||
|
||||
if (!address.value.company_country) {
|
||||
if (!address.value.organisation_country_code) {
|
||||
formErrors.value.company.push(t("a.Land"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const executePayment = () => {
|
||||
validateAddress();
|
||||
async function saveAddress() {
|
||||
const { country_code, organisation_country_code, ...profileData } = address.value;
|
||||
const typedProfileData: Partial<User> = { ...profileData };
|
||||
|
||||
typedProfileData.country = countries.value.find(
|
||||
(c) => c.country_code === country_code
|
||||
);
|
||||
typedProfileData.organisation_country = countries.value.find(
|
||||
(c) => c.country_code === organisation_country_code
|
||||
);
|
||||
|
||||
await user.updateUserProfile(typedProfileData);
|
||||
}
|
||||
|
||||
const executePayment = async () => {
|
||||
validateAddress();
|
||||
if (formErrors.value.personal.length > 0 || formErrors.value.company.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await saveAddress();
|
||||
|
||||
// Where the payment page will redirect to after the payment is done:
|
||||
// The reason why this is here is convenience: We could also do this in the backend
|
||||
// then we'd need to configure this for all environments (including Caprover).
|
||||
|
|
@ -242,6 +211,7 @@ const executePayment = () => {
|
|||
</p>
|
||||
|
||||
<h3 class="mb-4 mt-10">{{ $t("a.Adresse") }}</h3>
|
||||
<pre>{{ address }}</pre>
|
||||
<p class="mb-2">
|
||||
{{
|
||||
$t(
|
||||
|
|
@ -266,7 +236,7 @@ const executePayment = () => {
|
|||
<button
|
||||
v-if="!useCompanyAddress"
|
||||
class="underline"
|
||||
@click="useCompanyAddress = true"
|
||||
@click="setUseCompanyAddress(true)"
|
||||
>
|
||||
<template v-if="userOrganisationName">
|
||||
{{
|
||||
|
|
@ -296,7 +266,7 @@ const executePayment = () => {
|
|||
}}
|
||||
</h3>
|
||||
<h3 v-else>{{ $t("a.Rechnungsadresse") }}</h3>
|
||||
<button class="underline" @click="removeCompanyAddress">
|
||||
<button class="underline" @click="setUseCompanyAddress(false)">
|
||||
{{ $t("a.Entfernen") }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,16 +31,16 @@ export interface User {
|
|||
language: AvailableLanguages;
|
||||
course_session_experts: string[];
|
||||
invoice_address: InvoiceAddress | null;
|
||||
street: string | null;
|
||||
street_number: string | null;
|
||||
postal_code: string | null;
|
||||
city: string | null;
|
||||
street: string;
|
||||
street_number: string;
|
||||
postal_code: string;
|
||||
city: string;
|
||||
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_detail_name: string;
|
||||
organisation_street: string;
|
||||
organisation_street_number: string;
|
||||
organisation_postal_code: string;
|
||||
organisation_city: string;
|
||||
organisation_country: Country | null;
|
||||
}
|
||||
|
||||
|
|
@ -74,16 +74,16 @@ const initialUserState: User = {
|
|||
loggedIn: false,
|
||||
language: defaultLanguage,
|
||||
invoice_address: "prv",
|
||||
street: null,
|
||||
street_number: null,
|
||||
postal_code: null,
|
||||
city: null,
|
||||
street: "",
|
||||
street_number: "",
|
||||
postal_code: "",
|
||||
city: "",
|
||||
country: null,
|
||||
organisation_detail_name: null,
|
||||
organisation_street: null,
|
||||
organisation_street_number: null,
|
||||
organisation_postal_code: null,
|
||||
organisation_city: null,
|
||||
organisation_detail_name: "",
|
||||
organisation_street: "",
|
||||
organisation_street_number: "",
|
||||
organisation_postal_code: "",
|
||||
organisation_city: "",
|
||||
organisation_country: null,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class Migration(migrations.Migration):
|
|||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.deletion.SET_NULL,
|
||||
related_name="user_country",
|
||||
related_name="+",
|
||||
to="core.country",
|
||||
),
|
||||
),
|
||||
|
|
@ -88,7 +88,7 @@ class Migration(migrations.Migration):
|
|||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.deletion.SET_NULL,
|
||||
related_name="organisation_country",
|
||||
related_name="+",
|
||||
to="core.country",
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class User(AbstractUser):
|
|||
city = models.CharField(max_length=255, blank=True)
|
||||
country = models.ForeignKey(
|
||||
Country,
|
||||
related_name="user_country",
|
||||
related_name="+",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
|
|
@ -105,7 +105,7 @@ class User(AbstractUser):
|
|||
organisation_city = models.CharField(max_length=255, blank=True)
|
||||
organisation_country = models.ForeignKey(
|
||||
Country,
|
||||
related_name="organisation_country",
|
||||
related_name="+",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,23 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
|
||||
def migrate_checkout_information_country(apps, schema_editor):
|
||||
CheckoutInformation = apps.get_model("shop", "CheckoutInformation")
|
||||
Country = apps.get_model("core", "Country")
|
||||
for info in CheckoutInformation.objects.all():
|
||||
if info.old_country:
|
||||
country = Country.objects.get(vbv_country_id=info.old_country)
|
||||
info.country = country
|
||||
if info.old_company_country:
|
||||
country = Country.objects.get(vbv_country_id=info.old_company_country)
|
||||
info.organisation_country = country
|
||||
info.save(update_fields=["country", "organisation_country"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("shop", "0012_delete_country"),
|
||||
("core", "0009_country_refactor"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
@ -14,4 +28,79 @@ class Migration(migrations.Migration):
|
|||
name="abacus_order_id",
|
||||
field=models.BigIntegerField(blank=True, null=True, unique=True),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="company_name",
|
||||
new_name="organisation_detail_name",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="company_street",
|
||||
new_name="organisation_street",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="company_street_number",
|
||||
new_name="organisation_street_number",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="company_postal_code",
|
||||
new_name="organisation_postal_code",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="company_city",
|
||||
new_name="organisation_city",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="country",
|
||||
new_name="old_country",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="checkoutinformation",
|
||||
old_name="company_country",
|
||||
new_name="old_company_country",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="checkoutinformation",
|
||||
name="invoice_address",
|
||||
field=models.CharField(
|
||||
choices=[("prv", "Private"), ("org", "Organisation")],
|
||||
default="prv",
|
||||
max_length=3,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="checkoutinformation",
|
||||
name="country",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.deletion.SET_NULL,
|
||||
related_name="+",
|
||||
to="core.country",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="checkoutinformation",
|
||||
name="organisation_country",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.deletion.SET_NULL,
|
||||
related_name="+",
|
||||
to="core.country",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_checkout_information_country),
|
||||
migrations.RemoveField(
|
||||
model_name="checkoutinformation",
|
||||
name="old_country",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="checkoutinformation",
|
||||
name="old_company_country",
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,34 +1,7 @@
|
|||
from django.db import models
|
||||
from django.db.models import Max
|
||||
|
||||
|
||||
class BillingAddress(models.Model):
|
||||
"""
|
||||
Draft of a billing address for a purchase from the shop.
|
||||
"""
|
||||
|
||||
user = models.OneToOneField(
|
||||
"core.User",
|
||||
on_delete=models.CASCADE,
|
||||
primary_key=True,
|
||||
)
|
||||
|
||||
# user
|
||||
first_name = models.CharField(max_length=255, blank=True)
|
||||
last_name = models.CharField(max_length=255, blank=True)
|
||||
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.CharField(max_length=255, blank=True)
|
||||
|
||||
# company (optional)
|
||||
company_name = models.CharField(max_length=255, blank=True)
|
||||
company_street = models.CharField(max_length=255, blank=True)
|
||||
company_street_number = models.CharField(max_length=255, blank=True)
|
||||
company_postal_code = models.CharField(max_length=255, blank=True)
|
||||
company_city = models.CharField(max_length=255, blank=True)
|
||||
company_country = models.CharField(max_length=255, blank=True)
|
||||
from vbv_lernwelt.core.models import Country
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
|
|
@ -62,6 +35,14 @@ class CheckoutState(models.TextChoices):
|
|||
|
||||
|
||||
class CheckoutInformation(models.Model):
|
||||
INVOICE_ADDRESS_PRIVATE = "prv"
|
||||
INVOICE_ADDRESS_ORGANISATION = "org"
|
||||
|
||||
INVOICE_ADDRESS_CHOICES = (
|
||||
(INVOICE_ADDRESS_PRIVATE, "Private"),
|
||||
(INVOICE_ADDRESS_ORGANISATION, "Organisation"),
|
||||
)
|
||||
|
||||
user = models.ForeignKey("core.User", on_delete=models.PROTECT)
|
||||
|
||||
product_sku = models.CharField(max_length=255)
|
||||
|
|
@ -89,15 +70,31 @@ class CheckoutInformation(models.Model):
|
|||
street_number = models.CharField(max_length=255)
|
||||
postal_code = models.CharField(max_length=255)
|
||||
city = models.CharField(max_length=255)
|
||||
country = models.CharField(max_length=255)
|
||||
country = models.ForeignKey(
|
||||
Country,
|
||||
related_name="+",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# company (optional)
|
||||
company_name = models.CharField(max_length=255, blank=True)
|
||||
company_street = models.CharField(max_length=255, blank=True)
|
||||
company_street_number = models.CharField(max_length=255, blank=True)
|
||||
company_postal_code = models.CharField(max_length=255, blank=True)
|
||||
company_city = models.CharField(max_length=255, blank=True)
|
||||
company_country = models.CharField(max_length=255, blank=True)
|
||||
invoice_address = models.CharField(
|
||||
max_length=3, choices=INVOICE_ADDRESS_CHOICES, default="prv"
|
||||
)
|
||||
|
||||
# organisation data (optional)
|
||||
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="+",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# webhook metadata
|
||||
webhook_history = models.JSONField(default=list)
|
||||
|
|
|
|||
|
|
@ -1,23 +1 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from .models import BillingAddress
|
||||
|
||||
|
||||
class BillingAddressSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = BillingAddress
|
||||
fields = [
|
||||
"first_name",
|
||||
"last_name",
|
||||
"street",
|
||||
"street_number",
|
||||
"postal_code",
|
||||
"city",
|
||||
"country",
|
||||
"company_name",
|
||||
"company_street",
|
||||
"company_street_number",
|
||||
"company_postal_code",
|
||||
"company_city",
|
||||
"company_country",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from vbv_lernwelt.core.admin import User
|
||||
from vbv_lernwelt.shop.models import BillingAddress
|
||||
|
||||
|
||||
class BillingAddressViewTest(APITestCase):
|
||||
def setUp(self) -> None:
|
||||
self.user = User.objects.create_user(
|
||||
"testuser", "test@example.com", "testpassword"
|
||||
)
|
||||
self.client.login(username="testuser", password="testpassword")
|
||||
|
||||
self.billing_address = BillingAddress.objects.create(
|
||||
user=self.user,
|
||||
first_name="John",
|
||||
last_name="Doe",
|
||||
street="123 Main St",
|
||||
street_number="45A",
|
||||
postal_code="12345",
|
||||
city="Test City",
|
||||
country="Test Country",
|
||||
company_name="Test Company",
|
||||
company_street="456 Company St",
|
||||
company_street_number="67B",
|
||||
company_postal_code="67890",
|
||||
company_city="Company City",
|
||||
company_country="Company Country",
|
||||
)
|
||||
|
||||
def test_get_billing_address(self) -> None:
|
||||
# GIVEN
|
||||
# user is logged in and has a billing address
|
||||
|
||||
# WHEN
|
||||
url = reverse("get-billing-address")
|
||||
response = self.client.get(url)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["first_name"], "John")
|
||||
self.assertEqual(response.data["last_name"], "Doe")
|
||||
self.assertEqual(response.data["street"], "123 Main St")
|
||||
self.assertEqual(response.data["street_number"], "45A")
|
||||
self.assertEqual(response.data["postal_code"], "12345")
|
||||
self.assertEqual(response.data["city"], "Test City")
|
||||
self.assertEqual(response.data["country"], "Test Country")
|
||||
self.assertEqual(response.data["company_name"], "Test Company")
|
||||
self.assertEqual(response.data["company_street"], "456 Company St")
|
||||
self.assertEqual(response.data["company_street_number"], "67B")
|
||||
self.assertEqual(response.data["company_postal_code"], "67890")
|
||||
self.assertEqual(response.data["company_city"], "Company City")
|
||||
self.assertEqual(response.data["company_country"], "Company Country")
|
||||
|
||||
def test_update_billing_address(self) -> None:
|
||||
# GIVEN
|
||||
new_data = {
|
||||
"first_name": "Jane",
|
||||
"last_name": "Smith",
|
||||
"street": "789 New St",
|
||||
"street_number": "101C",
|
||||
"postal_code": "54321",
|
||||
"city": "New City",
|
||||
"country": "New Country",
|
||||
"company_name": "New Company",
|
||||
"company_street": "789 Company St",
|
||||
"company_street_number": "102D",
|
||||
"company_postal_code": "98765",
|
||||
"company_city": "New Company City",
|
||||
"company_country": "New Company Country",
|
||||
}
|
||||
|
||||
# WHEN
|
||||
url = reverse("update-billing-address")
|
||||
response = self.client.put(url, new_data)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
updated_address = BillingAddress.objects.get(user=self.user)
|
||||
self.assertEqual(updated_address.first_name, "Jane")
|
||||
self.assertEqual(updated_address.last_name, "Smith")
|
||||
self.assertEqual(updated_address.street, "789 New St")
|
||||
self.assertEqual(updated_address.street_number, "101C")
|
||||
self.assertEqual(updated_address.postal_code, "54321")
|
||||
self.assertEqual(updated_address.city, "New City")
|
||||
self.assertEqual(updated_address.country, "New Country")
|
||||
self.assertEqual(updated_address.company_name, "New Company")
|
||||
self.assertEqual(updated_address.company_street, "789 Company St")
|
||||
self.assertEqual(updated_address.company_street_number, "102D")
|
||||
self.assertEqual(updated_address.company_postal_code, "98765")
|
||||
self.assertEqual(updated_address.company_city, "New Company City")
|
||||
self.assertEqual(updated_address.company_country, "New Company Country")
|
||||
|
||||
def test_unauthenticated_access(self) -> None:
|
||||
# GIVEN
|
||||
self.client.logout()
|
||||
|
||||
# WHEN
|
||||
get_response = self.client.get(reverse("get-billing-address"))
|
||||
put_response = self.client.put(reverse("update-billing-address"), {})
|
||||
|
||||
# THEN
|
||||
self.assertTrue(get_response["Location"], "/login/")
|
||||
self.assertTrue(put_response["Location"], "/login/")
|
||||
|
|
@ -1,18 +1,9 @@
|
|||
from django.urls import path
|
||||
|
||||
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
|
||||
from vbv_lernwelt.shop.views import (
|
||||
checkout_vv,
|
||||
get_billing_address,
|
||||
transaction_webhook,
|
||||
update_billing_address,
|
||||
)
|
||||
from vbv_lernwelt.shop.views import checkout_vv, transaction_webhook
|
||||
|
||||
urlpatterns = [
|
||||
path("billing-address/", get_billing_address, name="get-billing-address"),
|
||||
path(
|
||||
"billing-address/update/", update_billing_address, name="update-billing-address"
|
||||
),
|
||||
path("vv/checkout/", checkout_vv, name="checkout-vv"),
|
||||
path(
|
||||
"transaction/webhook/",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ 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.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email
|
||||
from vbv_lernwelt.shop.const import (
|
||||
|
|
@ -15,13 +14,7 @@ from vbv_lernwelt.shop.const import (
|
|||
VV_FR_PRODUCT_SKU,
|
||||
VV_IT_PRODUCT_SKU,
|
||||
)
|
||||
from vbv_lernwelt.shop.models import (
|
||||
BillingAddress,
|
||||
CheckoutInformation,
|
||||
CheckoutState,
|
||||
Product,
|
||||
)
|
||||
from vbv_lernwelt.shop.serializers import BillingAddressSerializer
|
||||
from vbv_lernwelt.shop.models import CheckoutInformation, CheckoutState, Product
|
||||
from vbv_lernwelt.shop.services import (
|
||||
datatrans_state_to_checkout_state,
|
||||
get_payment_url,
|
||||
|
|
@ -40,39 +33,6 @@ PRODUCT_SKU_TO_COURSE_SESSION_ID = {
|
|||
}
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def get_billing_address(request):
|
||||
try:
|
||||
billing_address = BillingAddress.objects.get(user=request.user)
|
||||
data = BillingAddressSerializer(billing_address).data
|
||||
except BillingAddress.DoesNotExist:
|
||||
data = BillingAddressSerializer().data
|
||||
data["first_name"] = request.user.first_name # noqa
|
||||
data["last_name"] = request.user.last_name # noqa
|
||||
|
||||
return Response(data)
|
||||
|
||||
|
||||
@api_view(["PUT"])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def update_billing_address(request):
|
||||
try:
|
||||
billing_address = BillingAddress.objects.get(user=request.user)
|
||||
except BillingAddress.DoesNotExist:
|
||||
billing_address = None
|
||||
|
||||
serializer = BillingAddressSerializer(
|
||||
billing_address, data=request.data, partial=True
|
||||
)
|
||||
|
||||
if serializer.is_valid():
|
||||
serializer.save(user=request.user)
|
||||
return Response(serializer.data)
|
||||
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
def transaction_webhook(request):
|
||||
"""IMPORTANT: This is not called for timed out transactions!"""
|
||||
|
|
@ -125,7 +85,7 @@ def checkout_vv(request):
|
|||
sku = request.data["product"]
|
||||
base_redirect_url = request.data["redirect_url"]
|
||||
|
||||
logger.info(f"Checkout requested: sku={sku}", user_id=request.user.id)
|
||||
logger.info(f"Checkout requested: sku", user_id=request.user.id, sku=sku)
|
||||
|
||||
try:
|
||||
product = Product.objects.get(sku=sku)
|
||||
|
|
@ -171,6 +131,15 @@ 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
|
||||
|
||||
checkout_info = CheckoutInformation.objects.create(
|
||||
user=request.user,
|
||||
state=CheckoutState.ONGOING,
|
||||
|
|
@ -184,8 +153,6 @@ 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))
|
||||
|
||||
|
||||
|
|
@ -266,37 +233,3 @@ 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_code=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_code=checkout_info.company_country
|
||||
).first()
|
||||
|
||||
user.invoice_address = User.INVOICE_ADDRESS_ORGANISATION
|
||||
|
||||
user.save()
|
||||
|
|
|
|||
Loading…
Reference in New Issue