feat: address handling
This commit is contained in:
parent
e9f8b7dadc
commit
bbf4208228
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue