feat: address handling

This commit is contained in:
Reto Aebersold 2023-11-17 11:22:02 +01:00 committed by Christian Cueni
parent e9f8b7dadc
commit bbf4208228
6 changed files with 131 additions and 93 deletions

View File

@ -3,11 +3,12 @@ import { computed } from "vue";
const props = defineProps<{
modelValue: {
street: string;
streetNumber: string;
postalCode: 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;
};
}>();
@ -25,6 +26,24 @@ const orgAddress = computed({
<template>
<div class="my-10 grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
<div class="col-span-full">
<label
for="company-name"
class="block text-sm font-medium leading-6 text-gray-900"
>
Name
</label>
<div class="mt-2">
<input
id="company-name"
v-model="orgAddress.company_name"
type="text"
required
name="company-name"
class="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 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div class="sm:col-span-5">
<label
for="company-street-address"
@ -35,7 +54,7 @@ const orgAddress = computed({
<div class="mt-2">
<input
id="company-street-address"
v-model="orgAddress.street"
v-model="orgAddress.company_street"
type="text"
required
name="street-address"
@ -55,7 +74,7 @@ const orgAddress = computed({
<div class="mt-2">
<input
id="company-street-number"
v-model="orgAddress.streetNumber"
v-model="orgAddress.company_street_number"
name="street-number"
type="text"
autocomplete="street-number"
@ -73,7 +92,7 @@ const orgAddress = computed({
<div class="mt-2">
<input
id="company-postal-code"
v-model="orgAddress.postalCode"
v-model="orgAddress.company_postal_code"
type="text"
required
name="postal-code"
@ -93,7 +112,7 @@ const orgAddress = computed({
<div class="mt-2">
<input
id="company-city"
v-model="orgAddress.city"
v-model="orgAddress.company_city"
type="text"
name="city"
required
@ -113,7 +132,7 @@ const orgAddress = computed({
<div class="mt-2">
<select
id="company-country"
v-model="orgAddress.country"
v-model="orgAddress.company_country"
required
name="country"
autocomplete="country-name"

View File

@ -4,11 +4,11 @@ import { useEntities } from "@/services/onboarding";
const props = defineProps<{
modelValue: {
firstName: string;
lastName: string;
first_name: string;
last_name: string;
street: string;
streetNumber: string;
postalCode: string;
street_number: string;
postal_code: string;
city: string;
country: string;
};
@ -37,7 +37,7 @@ const address = computed({
<div class="mt-2">
<input
id="first-name"
v-model="address.firstName"
v-model="address.first_name"
type="text"
name="first-name"
required
@ -54,7 +54,7 @@ const address = computed({
<div class="mt-2">
<input
id="last-name"
v-model="address.lastName"
v-model="address.last_name"
type="text"
name="last-name"
required
@ -94,7 +94,7 @@ const address = computed({
<div class="mt-2">
<input
id="street-number"
v-model="address.streetNumber"
v-model="address.street_number"
name="street-number"
type="text"
autocomplete="street-number"
@ -113,7 +113,7 @@ const address = computed({
<div class="mt-2">
<input
id="postal-code"
v-model="address.postalCode"
v-model="address.postal_code"
type="text"
required
name="postal-code"

View File

@ -7,6 +7,7 @@ import PersonalAddress from "@/components/onboarding/PersonalAddress.vue";
import OrganisationAddress from "@/components/onboarding/OrganisationAddress.vue";
import { itPost, itPut, useFetch } from "@/fetchHelpers";
import { useEntities } from "@/services/onboarding";
import { useDebounceFn } from "@vueuse/core";
type BillingAddressType = {
first_name: string;
@ -16,6 +17,7 @@ type BillingAddressType = {
postal_code: string;
city: string;
country: string;
company_name: string;
company_street: string;
company_street_number: string;
company_postal_code: string;
@ -26,74 +28,64 @@ type BillingAddressType = {
const user = useUserStore();
const { organisations } = useEntities();
const userOrganisationName = computed(
() => organisations.value?.find((c) => c.id === user.organisation)?.name
);
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)) {
return null;
}
return organisations.value?.find((c) => c.id === user.organisation)?.name;
});
const address = ref({
firstName: user.first_name,
lastName: user.last_name,
first_name: "",
last_name: "",
street: "",
streetNumber: "",
postalCode: "",
street_number: "",
postal_code: "",
city: "",
country: "",
company_name: "",
company_street: "",
company_street_number: "",
company_postal_code: "",
company_city: "",
company_country: "",
});
const useCompanyAddress = ref(false);
const orgAddress = ref({
name: "",
street: "",
streetNumber: "",
postalCode: "",
city: "",
country: "",
});
const fetchBillingAddress = useFetch("/api/shop/billing-address/");
const billingAddressData: Ref<BillingAddressType | null> = fetchBillingAddress.data;
watch(billingAddressData, (newVal) => {
if (newVal) {
address.value = {
firstName: newVal.first_name || "",
lastName: newVal.last_name || "",
street: newVal.street || "",
streetNumber: newVal.street_number || "",
postalCode: newVal.postal_code || "",
city: newVal.city || "",
country: newVal.country || "",
};
orgAddress.value = {
name: userOrganisationName.value || "",
street: newVal.company_street || "",
streetNumber: newVal.company_street_number || "",
postalCode: newVal.company_postal_code || "",
city: newVal.company_city || "",
country: newVal.company_country || "",
};
address.value = newVal;
useCompanyAddress.value = !!newVal.company_name;
}
});
const executePayment = () => {
// FIXME do this as user types, not on submit -> the is the whole point of this ;)
// FIXME same address and orgaddress too
itPut("/api/shop/billing-address/update/", {
first_name: address.value.firstName,
last_name: address.value.lastName,
street: address.value.street,
street_number: address.value.streetNumber,
postal_code: address.value.postalCode,
city: address.value.city,
country: address.value.country,
company_street: orgAddress.value.street,
company_street_number: orgAddress.value.streetNumber,
company_postal_code: orgAddress.value.postalCode,
company_city: orgAddress.value.city,
company_country: orgAddress.value.country,
});
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 executePayment = () => {
// 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).
@ -103,7 +95,6 @@ const executePayment = () => {
itPost("/api/shop/vv/checkout/", {
redirect_url: fullHost,
organisation_address: orgAddress.value,
address: address.value,
}).then((res) => {
console.log("Going to next page", res.next_step_url);
@ -136,7 +127,10 @@ const executePayment = () => {
class="underline"
@click="useCompanyAddress = true"
>
{{ `Rechnungsadresse von ${userOrganisationName} hinzufügen` }}
<template v-if="userOrganisationName">
{{ `Rechnungsadresse von ${userOrganisationName} hinzufügen` }}
</template>
<template v-else>Rechnungsadresse hinzufügen</template>
</button>
<transition
@ -149,12 +143,15 @@ const executePayment = () => {
>
<div v-if="useCompanyAddress">
<div class="flex items-center justify-between">
<h3>{{ `Rechnungsaddresse von ${userOrganisationName}` }}</h3>
<h3 v-if="userOrganisationName">
{{ `Rechnungsadresse von ${userOrganisationName}` }}
</h3>
<h3 v-else>Rechnungsadresse</h3>
<button class="underline" @click="useCompanyAddress = false">
Entfernen
</button>
</div>
<OrganisationAddress v-model="orgAddress" />
<OrganisationAddress v-model="address" />
</div>
</transition>
</template>

View File

@ -0,0 +1,29 @@
# Generated by Django 3.2.20 on 2023-11-17 08:05
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("shop", "0007_checkoutinformation_webhook_history"),
]
operations = [
migrations.RemoveField(
model_name="billingaddress",
name="id",
),
migrations.AlterField(
model_name="billingaddress",
name="user",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to=settings.AUTH_USER_MODEL,
),
),
]

View File

@ -23,7 +23,11 @@ class BillingAddress(models.Model):
Draft of a billing address for a purchase from the shop.
"""
user = models.ForeignKey("core.User", on_delete=models.CASCADE)
user = models.OneToOneField(
"core.User",
on_delete=models.CASCADE,
primary_key=True,
)
# user
first_name = models.CharField(max_length=255, blank=True)

View File

@ -1,6 +1,5 @@
import structlog
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
@ -41,10 +40,15 @@ def webhook_url(base_url):
@api_view(["GET"])
@permission_classes([IsAuthenticated])
def get_billing_address(request):
billing_address = get_object_or_404(BillingAddress, user=request.user)
serializer = BillingAddressSerializer(billing_address)
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
data["last_name"] = request.user.last_name
return Response(serializer.data)
return Response(data)
@api_view(["PUT"])
@ -143,7 +147,6 @@ def checkout_vv(request):
# )
address = request.data["address"]
organization = request.data["organisation_address"]
base_redirect_url = request.data["redirect_url"]
# not yet initialized at all or canceled/failed
@ -166,21 +169,7 @@ def checkout_vv(request):
product_price=product.price,
product_name=product.name,
product_description=product.description,
# address
first_name=address["firstName"],
last_name=address["lastName"],
street=address["street"],
street_number=address["streetNumber"],
postal_code=address["postalCode"],
city=address["city"],
country=address["country"],
# organization
company_name=organization.get("name"),
company_street=organization.get("street"),
company_street_number=organization.get("streetNumber"),
company_postal_code=organization.get("postalCode"),
company_city=organization.get("city"),
company_country=organization.get("country"),
**address,
)
return JsonResponse(