Refactor country handling code

This commit is contained in:
Daniel Egger 2024-05-30 14:20:34 +02:00
parent 8ce7f9935e
commit 2646b072ee
18 changed files with 885 additions and 311 deletions

View File

@ -141,7 +141,11 @@ const orgAddress = computed({
autocomplete="country-name" autocomplete="country-name"
class="block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6" class="block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
> >
<option v-for="country in countries" :key="country.id" :value="country.id"> <option
v-for="country in countries"
:key="country.country_code"
:value="country.country_code"
>
{{ country.name }} {{ country.name }}
</option> </option>
</select> </select>

View File

@ -153,7 +153,11 @@ const address = computed({
autocomplete="country-name" autocomplete="country-name"
class="block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6" class="block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
> >
<option v-for="country in countries" :key="country.id" :value="country.id"> <option
v-for="country in countries"
:key="country.country_code"
:value="country.country_code"
>
{{ country.name }} {{ country.name }}
</option> </option>
</select> </select>

View File

@ -20,23 +20,25 @@ const formData = ref({
street_number: user.street_number, street_number: user.street_number,
postal_code: user.postal_code, postal_code: user.postal_code,
city: user.city, city: user.city,
country_id: user.country?.id, country_code: user.country?.country_code,
organisation: user.organisation, organisation: user.organisation,
organisation_street: user.organisation_street, organisation_street: user.organisation_street,
organisation_street_number: user.organisation_street_number, organisation_street_number: user.organisation_street_number,
organisation_postal_code: user.organisation_postal_code, organisation_postal_code: user.organisation_postal_code,
organisation_city: user.organisation_city, organisation_city: user.organisation_city,
organisation_country_id: user.organisation_country?.id, organisation_country_code: user.organisation_country?.country_code,
invoice_address: user.invoice_address, invoice_address: user.invoice_address,
}); });
async function save() { async function save() {
const { country_id, organisation_country_id, ...profileData } = formData.value; const { country_code, organisation_country_code, ...profileData } = formData.value;
const typedProfileData: Partial<User> = { ...profileData }; const typedProfileData: Partial<User> = { ...profileData };
typedProfileData.country = countries.value.find((c) => c.id === country_id); typedProfileData.country = countries.value.find(
(c) => c.country_code === country_code
);
typedProfileData.organisation_country = countries.value.find( typedProfileData.organisation_country = countries.value.find(
(c) => c.id === organisation_country_id (c) => c.country_code === organisation_country_code
); );
await user.updateUserProfile(typedProfileData); await user.updateUserProfile(typedProfileData);
@ -219,12 +221,16 @@ async function avatarUpload(e: Event) {
<select <select
id="country" id="country"
v-model="formData.country_id" v-model="formData.country_code"
name="country" name="country"
autocomplete="country-name" autocomplete="country-name"
class="disabled:bg-gray-50 mb-4 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 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6" class="disabled:bg-gray-50 mb-4 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 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
> >
<option v-for="country in countries" :key="country.id" :value="country.id"> <option
v-for="country in countries"
:key="country.country_code"
:value="country.country_code"
>
{{ country.name }} {{ country.name }}
</option> </option>
</select> </select>
@ -325,13 +331,17 @@ async function avatarUpload(e: Event) {
<select <select
id="org-country" id="org-country"
v-model="formData.organisation_country_id" v-model="formData.organisation_country_code"
required required
name="org-country" name="org-country"
autocomplete="country-name" autocomplete="country-name"
class="disabled:bg-gray-50 mb-4 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 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6" class="disabled:bg-gray-50 mb-4 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 disabled:cursor-not-allowed disabled:text-gray-500 disabled:ring-gray-200 sm:max-w-sm sm:text-sm sm:leading-6"
> >
<option v-for="country in countries" :key="country.id" :value="country.id"> <option
v-for="country in countries"
:key="country.country_code"
:value="country.country_code"
>
{{ country.name }} {{ country.name }}
</option> </option>
</select> </select>

View File

@ -8,7 +8,8 @@ export type Organisation = {
}; };
export type Country = { export type Country = {
id: number; country_code: string;
vbv_country_id: number;
name: string; name: string;
}; };

View File

@ -6,9 +6,7 @@ from dotenv import dotenv_values
script_path = os.path.abspath(__file__) script_path = os.path.abspath(__file__)
script_dir = os.path.dirname(script_path) script_dir = os.path.dirname(script_path)
dev_env = dotenv_values( dev_env = dotenv_values(f"{script_dir}/../../../env_secrets/caprover_vbv-develop.env")
f"{script_dir}/../../../env_secrets/caprover_vbv-develop.env"
)
os.environ["IT_APP_ENVIRONMENT"] = "local" os.environ["IT_APP_ENVIRONMENT"] = "local"

View File

@ -42,58 +42,32 @@ class EntitiesViewTest(APITestCase):
}, },
) )
countries = response.data["countries"]
self.assertEqual(
countries[0],
{
"id": 1,
"name": "Afghanistan",
},
)
def test_list_country_entities_ordered_by_country_id(self) -> None:
# GIVEN
url = reverse("list_entities")
first_country = Country.objects.get(country_id=1)
# WHEN
response = self.client.get(url)
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
countries = response.data["countries"]
self.assertEqual(
countries[0],
{
"id": first_country.country_id,
"name": first_country.name_de,
},
)
def test_list_country_entities_ordered_by_order_id(self) -> None: def test_list_country_entities_ordered_by_order_id(self) -> None:
# GIVEN
url = reverse("list_entities") url = reverse("list_entities")
switzerland = Country.objects.get(name_de="Schweiz")
switzerland.order_id = 1
switzerland.save()
# WHEN
response = self.client.get(url) response = self.client.get(url)
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
countries = response.data["countries"] countries = response.data["countries"]
self.assertEqual( self.assertEqual(
countries[0], countries[0],
{ {
"id": switzerland.country_id, "country_code": "CH",
"name": switzerland.name_de, "vbv_country_id": 209,
"name": "Schweiz",
},
)
usa = Country.objects.get(country_code="US")
usa.order_id = 0.5
usa.save()
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
countries = response.data["countries"]
self.assertEqual(
countries[0],
{
"country_code": "US",
"vbv_country_id": usa.vbv_country_id,
"name": usa.name_de,
}, },
) )

View File

@ -123,7 +123,8 @@ class OrganisationAdmin(admin.ModelAdmin):
class CountryAdmin(admin.ModelAdmin): class CountryAdmin(admin.ModelAdmin):
list_display = ( list_display = (
"order_id", "order_id",
"country_id", "country_code",
"vbv_country_id",
"name_de", "name_de",
"name_fr", "name_fr",
"name_it", "name_it",

View File

@ -2,6 +2,15 @@
from django.db import migrations, models from django.db import migrations, models
from vbv_lernwelt.core.model_utils import countries
def populate_country_order_id(apps, schema_editor):
Country = apps.get_model("core", "Country")
for country in Country.objects.all():
country.order_id = countries[country.country_id].get("order_id", 20.0)
country.save(update_fields=["order_id"])
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
@ -22,4 +31,5 @@ class Migration(migrations.Migration):
name="order_id", name="order_id",
field=models.FloatField(default=20), field=models.FloatField(default=20),
), ),
migrations.RunPython(populate_country_order_id),
] ]

View File

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('core', '0007_auto_20240220_1058'), ("core", "0007_auto_20240220_1058"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='user', model_name="user",
name='abacus_debitor_number', name="abacus_debitor_number",
field=models.BigIntegerField(blank=True, null=True, unique=True), field=models.BigIntegerField(blank=True, null=True, unique=True),
), ),
] ]

View File

@ -0,0 +1,112 @@
# Generated by Django 3.2.20 on 2024-05-30 10:34
from django.db import migrations, models
from vbv_lernwelt.core.model_utils import countries
def populate_country_code(apps, schema_editor):
Country = apps.get_model("core", "Country")
for country in Country.objects.all():
country.country_code = countries[country.country_id]["country_code"]
country.save(update_fields=["country_code"])
def migrate_user_country(apps, schema_editor):
User = apps.get_model("core", "User")
Country = apps.get_model("core", "Country")
for user in User.objects.all():
if user.old_country:
country = Country.objects.get(vbv_country_id=user.old_country)
user.country = country
if user.old_organisation_country:
country = Country.objects.get(vbv_country_id=user.old_organisation_country)
user.organisation_country = country
user.save(update_fields=["country", "organisation_country"])
class Migration(migrations.Migration):
dependencies = [
("core", "0008_user_abacus_debitor_number"),
]
operations = [
migrations.AlterField(
model_name="user",
name="country",
field=models.IntegerField(null=True, blank=True),
),
migrations.AlterField(
model_name="user",
name="organisation_country",
field=models.IntegerField(null=True, blank=True),
),
migrations.AddField(
model_name="country",
name="country_code",
field=models.CharField(max_length=2, null=True),
),
migrations.RunPython(populate_country_code),
migrations.AlterField(
model_name="country",
name="country_id",
field=models.IntegerField(),
),
migrations.RenameField(
model_name="country", old_name="country_id", new_name="vbv_country_id"
),
migrations.AlterField(
model_name="country",
name="country_code",
field=models.CharField(max_length=2, primary_key=True, serialize=False),
),
migrations.RenameField(
model_name="user",
old_name="country",
new_name="old_country",
),
migrations.RenameField(
model_name="user",
old_name="organisation_country",
new_name="old_organisation_country",
),
migrations.AddField(
model_name="user",
name="country",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=models.deletion.SET_NULL,
related_name="user_country",
to="core.country",
),
),
migrations.AddField(
model_name="user",
name="organisation_country",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=models.deletion.SET_NULL,
related_name="organisation_country",
to="core.country",
),
),
migrations.RunPython(migrate_user_country),
migrations.RemoveField(
model_name="user",
name="old_country",
),
migrations.RemoveField(
model_name="user",
name="old_organisation_country",
),
migrations.AlterModelOptions(
name="country",
options={
"ordering": ["order_id", "vbv_country_id"],
"verbose_name": "Country",
"verbose_name_plural": "Countries",
},
),
]

File diff suppressed because it is too large Load Diff

View File

@ -25,19 +25,20 @@ class Organisation(models.Model):
class Country(models.Model): class Country(models.Model):
country_id = models.IntegerField(primary_key=True) country_code = models.CharField(max_length=2, primary_key=True)
vbv_country_id = models.IntegerField(primary_key=False)
name_de = models.CharField(max_length=255) name_de = models.CharField(max_length=255)
name_fr = models.CharField(max_length=255) name_fr = models.CharField(max_length=255)
name_it = models.CharField(max_length=255) name_it = models.CharField(max_length=255)
order_id = models.FloatField(default=20) order_id = models.FloatField(default=20)
def __str__(self): def __str__(self):
return f"{self.name_de} ({self.country_id})" return f"{self.name_de} ({self.country_code}) ({self.vbv_country_id})"
class Meta: class Meta:
verbose_name = "Country" verbose_name = "Country"
verbose_name_plural = "Countries" verbose_name_plural = "Countries"
ordering = ["order_id", "country_id"] ordering = ["order_id", "vbv_country_id"]
class User(AbstractUser): class User(AbstractUser):

View File

@ -14,12 +14,11 @@ def create_json_from_objects(objects, serializer_class, many=True) -> str:
class CountrySerializer(serializers.ModelSerializer): class CountrySerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source="country_id", read_only=True)
name = serializers.SerializerMethodField() name = serializers.SerializerMethodField()
class Meta: class Meta:
model = Country model = Country
fields = ["id", "name"] fields = ["country_code", "vbv_country_id", "name"]
def get_name(self, obj): def get_name(self, obj):
language = self.context.get("langauge") language = self.context.get("langauge")
@ -32,11 +31,15 @@ class CountrySerializer(serializers.ModelSerializer):
return obj.name_de return obj.name_de
def to_internal_value(self, data): def to_internal_value(self, data):
country_id = data.get("id") country_code = data.get("country_code")
if country_id is not None: if country_code is not None:
try: try:
country = Country.objects.get(country_id=country_id) country = Country.objects.get(country_code=country_code)
return {"id": country.country_id, "name": self.get_name(country)} return {
"country_code": country.country_code,
"vbv_country_id": country.vbv_country_id,
"name": self.get_name(country),
}
except Country.DoesNotExist: except Country.DoesNotExist:
raise serializers.ValidationError({"id": "Invalid country ID"}) raise serializers.ValidationError({"id": "Invalid country ID"})
return super().to_internal_value(data) return super().to_internal_value(data)
@ -105,14 +108,14 @@ class UserSerializer(serializers.ModelSerializer):
setattr(instance, attr, value) setattr(instance, attr, value)
if country_data is not None: if country_data is not None:
country_id = country_data.get("id") country_code = country_data.get("country_code")
country_instance = Country.objects.filter(country_id=country_id).first() country_instance = Country.objects.filter(country_code=country_code).first()
instance.country = country_instance instance.country = country_instance
if organisation_country_data is not None: if organisation_country_data is not None:
organisation_country_id = organisation_country_data.get("id") organisation_country_code = organisation_country_data.get("country_code")
organisation_country_instance = Country.objects.filter( organisation_country_instance = Country.objects.filter(
country_id=organisation_country_id country_code=organisation_country_code
).first() ).first()
instance.organisation_country = organisation_country_instance instance.organisation_country = organisation_country_instance

View File

@ -74,3 +74,6 @@ After everything runs fine, we should be able to remove the following deprecated
8. `IT_OAUTH_SCOPE` 8. `IT_OAUTH_SCOPE`
### Datatrans Test Credit Card
5100 0010 0000 0014 06/25 123

View File

@ -3,6 +3,16 @@
from django.db import migrations, models from django.db import migrations, models
def add_default_shop_product(apps, schema_editor):
Product = apps.get_model("shop", "Product")
Product.objects.create(
sku="vv-de",
name="Versicherungsvermittler/-in VBV",
description="Versicherungsvermittler/-in VBV DE",
price=324_00,
)
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("shop", "0008_auto_20231117_0905"), ("shop", "0008_auto_20231117_0905"),
@ -16,4 +26,5 @@ class Migration(migrations.Migration):
help_text="The total price of the product in centimes -> 1000 = 10.00 CHF" help_text="The total price of the product in centimes -> 1000 = 10.00 CHF"
), ),
), ),
migrations.RunPython(add_default_shop_product),
] ]

View File

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('shop', '0012_delete_country'), ("shop", "0012_delete_country"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='checkoutinformation', model_name="checkoutinformation",
name='abacus_order_id', name="abacus_order_id",
field=models.BigIntegerField(blank=True, null=True, unique=True), field=models.BigIntegerField(blank=True, null=True, unique=True),
), ),
] ]

View File

@ -7,10 +7,10 @@ from vbv_lernwelt.core.admin import User
from vbv_lernwelt.core.create_default_users import create_default_users from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.shop.invoice.abacus import ( from vbv_lernwelt.shop.invoice.abacus import (
AbacusInvoiceCreator, AbacusInvoiceCreator,
render_invoice_xml,
render_customer_xml,
create_invoice_xml,
create_customer_xml, create_customer_xml,
create_invoice_xml,
render_customer_xml,
render_invoice_xml,
) )
from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository
from vbv_lernwelt.shop.models import CheckoutInformation from vbv_lernwelt.shop.models import CheckoutInformation

View File

@ -275,7 +275,9 @@ def update_user_address(user: User, checkout_info: CheckoutInformation):
user.city = checkout_info.city user.city = checkout_info.city
if checkout_info.country: if checkout_info.country:
user.country = Country.objects.filter(country_id=checkout_info.country).first() user.country = Country.objects.filter(
country_code=checkout_info.country
).first()
if ( if (
checkout_info.company_name checkout_info.company_name
@ -292,7 +294,7 @@ def update_user_address(user: User, checkout_info: CheckoutInformation):
user.organisation_city = checkout_info.company_city user.organisation_city = checkout_info.company_city
user.organisation_country = Country.objects.filter( user.organisation_country = Country.objects.filter(
country_id=checkout_info.company_country country_code=checkout_info.company_country
).first() ).first()
user.invoice_address = User.INVOICE_ADDRESS_ORGANISATION user.invoice_address = User.INVOICE_ADDRESS_ORGANISATION