diff --git a/.DS_Store b/.DS_Store index 7abf56cd..7f3454fd 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/api/migrations/__pycache__/0001_initial.cpython-311.pyc b/api/migrations/__pycache__/0001_initial.cpython-311.pyc index 3ab95273..c2ffaec9 100644 Binary files a/api/migrations/__pycache__/0001_initial.cpython-311.pyc and b/api/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/car_inventory/__pycache__/settings.cpython-311.pyc b/car_inventory/__pycache__/settings.cpython-311.pyc index bfca115e..95c85ce6 100644 Binary files a/car_inventory/__pycache__/settings.cpython-311.pyc and b/car_inventory/__pycache__/settings.cpython-311.pyc differ diff --git a/inventory/.DS_Store b/inventory/.DS_Store index fe3f9110..06c5b984 100644 Binary files a/inventory/.DS_Store and b/inventory/.DS_Store differ diff --git a/inventory/__pycache__/admin.cpython-311.pyc b/inventory/__pycache__/admin.cpython-311.pyc index 2eb5c0b1..ecd33b84 100644 Binary files a/inventory/__pycache__/admin.cpython-311.pyc and b/inventory/__pycache__/admin.cpython-311.pyc differ diff --git a/inventory/__pycache__/forms.cpython-311.pyc b/inventory/__pycache__/forms.cpython-311.pyc index cc02050d..7df52c3a 100644 Binary files a/inventory/__pycache__/forms.cpython-311.pyc and b/inventory/__pycache__/forms.cpython-311.pyc differ diff --git a/inventory/__pycache__/middleware.cpython-311.pyc b/inventory/__pycache__/middleware.cpython-311.pyc index d83d83be..197f3ccc 100644 Binary files a/inventory/__pycache__/middleware.cpython-311.pyc and b/inventory/__pycache__/middleware.cpython-311.pyc differ diff --git a/inventory/__pycache__/models.cpython-311.pyc b/inventory/__pycache__/models.cpython-311.pyc index b28dcc1f..b7905805 100644 Binary files a/inventory/__pycache__/models.cpython-311.pyc and b/inventory/__pycache__/models.cpython-311.pyc differ diff --git a/inventory/__pycache__/services.cpython-311.pyc b/inventory/__pycache__/services.cpython-311.pyc index f702d2b6..c764f373 100644 Binary files a/inventory/__pycache__/services.cpython-311.pyc and b/inventory/__pycache__/services.cpython-311.pyc differ diff --git a/inventory/__pycache__/urls.cpython-311.pyc b/inventory/__pycache__/urls.cpython-311.pyc index b1feacde..6d0fd69f 100644 Binary files a/inventory/__pycache__/urls.cpython-311.pyc and b/inventory/__pycache__/urls.cpython-311.pyc differ diff --git a/inventory/__pycache__/utils.cpython-311.pyc b/inventory/__pycache__/utils.cpython-311.pyc index a95b42fb..8cf446f8 100644 Binary files a/inventory/__pycache__/utils.cpython-311.pyc and b/inventory/__pycache__/utils.cpython-311.pyc differ diff --git a/inventory/__pycache__/views.cpython-311.pyc b/inventory/__pycache__/views.cpython-311.pyc index 238b4abb..36101ce1 100644 Binary files a/inventory/__pycache__/views.cpython-311.pyc and b/inventory/__pycache__/views.cpython-311.pyc differ diff --git a/inventory/admin.py b/inventory/admin.py index b3dcfaa2..8fc668a6 100644 --- a/inventory/admin.py +++ b/inventory/admin.py @@ -32,7 +32,7 @@ admin.site.register(models.VatRate) admin.site.register(models.Customer) admin.site.register(models.Opportunity) admin.site.register(models.Notification) -admin.site.register(models.OpportunityLog) +admin.site.register(models.Lead) @admin.register(models.CarMake) class CarMakeAdmin(admin.ModelAdmin): diff --git a/inventory/forms.py b/inventory/forms.py index 5d230a7d..af72a108 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1,3 +1,4 @@ +from django_countries.widgets import CountrySelectWidget from phonenumber_field.formfields import PhoneNumberField from django.core.validators import MinLengthValidator from django.core.validators import RegexValidator @@ -25,7 +26,7 @@ from .models import ( SaleQuotationCar, AdditionalServices, Staff, - Opportunity, DealStatus, Priority, Sources, + Opportunity, Priority, Sources, ) from django_ledger.models import ItemModel, InvoiceModel from django.forms import ModelMultipleChoiceField, ValidationError @@ -61,21 +62,21 @@ class StaffForm(forms.ModelForm): model = Staff fields = ["name", "arabic_name", "phone_number", "staff_type"] - def __init__(self, *args, **kwargs): - user_instance = kwargs.get("instance") - if user_instance and user_instance.user: - initial = kwargs.setdefault("initial", {}) - initial["email"] = user_instance.user.email - super().__init__(*args, **kwargs) - - def save(self, commit=True): - staff_instance = super().save(commit=False) - user = staff_instance.user - user.email = self.cleaned_data["email"] - if commit: - user.save() - staff_instance.save() - return staff_instance + # def __init__(self, *args, **kwargs): + # user_instance = kwargs.get("instance") + # if user_instance and user_instance.user: + # initial = kwargs.setdefault("initial", {}) + # initial["email"] = user_instance.user.email + # super().__init__(*args, **kwargs) + # + # def save(self, commit=True): + # user_instance = super().save(commit=False) + # user = user_instance.user + # user.email = self.cleaned_data["email"] + # if commit: + # user.save() + # user_instance.save() + # return user_instance # Dealer Form @@ -105,7 +106,9 @@ class CustomerForm(forms.ModelForm, AddClassMixin): "national_id", "phone_number", "address", + "country" ] + widgets = {"country": CountrySelectWidget()} class CarForm( @@ -559,6 +562,6 @@ class OpportunityForm(forms.ModelForm): class Meta: model = Opportunity fields = [ - 'car', 'deal_name', 'deal_value', + 'car', 'customer', 'stage', ] diff --git a/inventory/migrations/0006_remove_customer_is_lead_customer_dob_customer_gender_and_more.py b/inventory/migrations/0006_remove_customer_is_lead_customer_dob_customer_gender_and_more.py new file mode 100644 index 00000000..3e6592a1 --- /dev/null +++ b/inventory/migrations/0006_remove_customer_is_lead_customer_dob_customer_gender_and_more.py @@ -0,0 +1,59 @@ +# Generated by Django 5.1.4 on 2025-01-08 19:03 + +import django.utils.timezone +import django_countries.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0005_alter_additionalservices_item'), + ] + + operations = [ + migrations.RemoveField( + model_name='customer', + name='is_lead', + ), + migrations.AddField( + model_name='customer', + name='dob', + field=models.DateField(default=django.utils.timezone.now, verbose_name='Date of Birth'), + preserve_default=False, + ), + migrations.AddField( + model_name='customer', + name='gender', + field=models.CharField(choices=[('m', 'Male'), ('f', 'Female')], default='m', max_length=1, verbose_name='Gender'), + preserve_default=False, + ), + migrations.AddField( + model_name='customer', + name='nationality', + field=django_countries.fields.CountryField(blank=True, max_length=2, verbose_name='Nationality'), + ), + migrations.AddField( + model_name='customer', + name='obligations', + field=models.PositiveIntegerField(default=1000, verbose_name='Obligations'), + preserve_default=False, + ), + migrations.AddField( + model_name='customer', + name='salary', + field=models.PositiveIntegerField(default=10000, verbose_name='Salary'), + preserve_default=False, + ), + migrations.AddField( + model_name='customer', + name='title', + field=models.CharField(choices=[('mr', 'Mr'), ('mrs', 'Mrs'), ('ms', 'Ms'), ('miss', 'Miss'), ('dr', 'Dr'), ('prof', 'Prof'), ('prince', 'Prince'), ('princess', 'Princess'), ('company', 'Company')], default='mr', max_length=10, verbose_name='Title'), + preserve_default=False, + ), + migrations.AddField( + model_name='customer', + name='updated', + field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + ), + ] diff --git a/inventory/migrations/0007_remove_customer_nationality_customer_country_and_more.py b/inventory/migrations/0007_remove_customer_nationality_customer_country_and_more.py new file mode 100644 index 00000000..f9e6d93f --- /dev/null +++ b/inventory/migrations/0007_remove_customer_nationality_customer_country_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.1.4 on 2025-01-09 05:46 + +import django_countries.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0006_remove_customer_is_lead_customer_dob_customer_gender_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='customer', + name='nationality', + ), + migrations.AddField( + model_name='customer', + name='country', + field=django_countries.fields.CountryField(blank=True, max_length=2, verbose_name='Country'), + ), + migrations.AlterField( + model_name='customer', + name='title', + field=models.CharField(choices=[('mr', 'Mr'), ('mrs', 'Mrs'), ('ms', 'Ms'), ('miss', 'Miss'), ('dr', 'Dr'), ('prof', 'Prof'), ('prince', 'Prince'), ('princess', 'Princess'), ('company', 'Company'), ('na', 'N/A')], default='na', max_length=10, verbose_name='Title'), + ), + migrations.AlterField( + model_name='opportunity', + name='deal_status', + field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('canceled', 'Canceled'), ('lost', 'Lost'), ('won', 'Won')], default='new', max_length=20, verbose_name='Deal Status'), + ), + migrations.AlterField( + model_name='opportunitylog', + name='new_status', + field=models.CharField(blank=True, choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('canceled', 'Canceled'), ('lost', 'Lost'), ('won', 'Won')], max_length=50, null=True, verbose_name='New Status'), + ), + migrations.AlterField( + model_name='opportunitylog', + name='old_status', + field=models.CharField(blank=True, choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('canceled', 'Canceled'), ('lost', 'Lost'), ('won', 'Won')], max_length=50, null=True, verbose_name='Old Status'), + ), + ] diff --git a/inventory/migrations/0008_alter_notes_options_alter_notification_options_and_more.py b/inventory/migrations/0008_alter_notes_options_alter_notification_options_and_more.py new file mode 100644 index 00000000..47e0ef38 --- /dev/null +++ b/inventory/migrations/0008_alter_notes_options_alter_notification_options_and_more.py @@ -0,0 +1,224 @@ +# Generated by Django 5.1.4 on 2025-01-09 09:19 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('inventory', '0007_remove_customer_nationality_customer_country_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='notes', + options={'verbose_name': 'Note', 'verbose_name_plural': 'Notes'}, + ), + migrations.AlterModelOptions( + name='notification', + options={'ordering': ['-created'], 'verbose_name': 'Notification', 'verbose_name_plural': 'Notifications'}, + ), + migrations.RemoveField( + model_name='customer', + name='obligations', + ), + migrations.RemoveField( + model_name='customer', + name='salary', + ), + migrations.RemoveField( + model_name='notes', + name='created_at', + ), + migrations.RemoveField( + model_name='notes', + name='opportunity', + ), + migrations.RemoveField( + model_name='notes', + name='updated_at', + ), + migrations.RemoveField( + model_name='notification', + name='created_at', + ), + migrations.RemoveField( + model_name='opportunity', + name='created_at', + ), + migrations.RemoveField( + model_name='opportunity', + name='created_by', + ), + migrations.RemoveField( + model_name='opportunity', + name='deal_name', + ), + migrations.RemoveField( + model_name='opportunity', + name='deal_status', + ), + migrations.RemoveField( + model_name='opportunity', + name='deal_value', + ), + migrations.RemoveField( + model_name='opportunity', + name='priority', + ), + migrations.RemoveField( + model_name='opportunity', + name='updated_at', + ), + migrations.RemoveField( + model_name='staff', + name='created_at', + ), + migrations.RemoveField( + model_name='staff', + name='updated_at', + ), + migrations.AddField( + model_name='notes', + name='content_type', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'), + preserve_default=False, + ), + migrations.AddField( + model_name='notes', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created'), + preserve_default=False, + ), + migrations.AddField( + model_name='notes', + name='object_id', + field=models.PositiveIntegerField(default=1), + preserve_default=False, + ), + migrations.AddField( + model_name='notes', + name='updated', + field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + ), + migrations.AddField( + model_name='notification', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created'), + preserve_default=False, + ), + migrations.AddField( + model_name='opportunity', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created'), + preserve_default=False, + ), + migrations.AddField( + model_name='opportunity', + name='dealer', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.dealer'), + preserve_default=False, + ), + migrations.AddField( + model_name='opportunity', + name='staff', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owner', to='inventory.staff', verbose_name='Owner'), + ), + migrations.AddField( + model_name='opportunity', + name='stage', + field=models.CharField(choices=[('prospect', 'Prospect'), ('proposal', 'Proposal'), ('negotiation', 'Negotiation'), ('closed_won', 'Closed Won'), ('closed_lost', 'Closed Lost')], default='prospect', max_length=20, verbose_name='Stage'), + preserve_default=False, + ), + migrations.AddField( + model_name='opportunity', + name='updated', + field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + ), + migrations.AddField( + model_name='staff', + name='created', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created'), + preserve_default=False, + ), + migrations.AddField( + model_name='staff', + name='updated', + field=models.DateTimeField(auto_now=True, verbose_name='Updated'), + ), + migrations.AlterField( + model_name='notes', + name='created_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='notes_created', to='inventory.staff'), + ), + migrations.CreateModel( + name='Activity', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('activity_type', models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('add_car', 'Add Car'), ('reserve_car', 'Reserve Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type')), + ('notes', models.TextField(blank=True, null=True, verbose_name='Notes')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), + ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created', to='inventory.staff')), + ], + options={ + 'verbose_name': 'Activity', + 'verbose_name_plural': 'Activities', + }, + ), + migrations.CreateModel( + name='Lead', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(choices=[('mr', 'Mr'), ('mrs', 'Mrs'), ('ms', 'Ms'), ('miss', 'Miss'), ('dr', 'Dr'), ('prof', 'Prof'), ('prince', 'Prince'), ('princess', 'Princess'), ('company', 'Company'), ('na', 'N/A')], max_length=20, verbose_name='Title')), + ('first_name', models.CharField(max_length=50, verbose_name='First Name')), + ('last_name', models.CharField(max_length=50, verbose_name='Last Name')), + ('email', models.EmailField(db_index=True, max_length=254, unique=True, verbose_name='Email')), + ('salary', models.PositiveIntegerField(blank=True, null=True, verbose_name='Salary')), + ('obligations', models.PositiveIntegerField(blank=True, null=True, verbose_name='Obligations')), + ('year', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Year')), + ('source', models.CharField(choices=[('referrals', 'Referrals'), ('whatsapp', 'WhatsApp'), ('showroom', 'Showroom'), ('tiktok', 'TikTok'), ('instagram', 'Instagram'), ('x', 'X'), ('facebook', 'Facebook'), ('motory', 'Motory'), ('influencers', 'Influencers'), ('youtube', 'Youtube'), ('campaign', 'Campaign')], max_length=50, verbose_name='Source')), + ('channel', models.CharField(choices=[('walk_in', 'Walk In'), ('toll_free', 'Toll Free'), ('website', 'Website'), ('email', 'Email'), ('form', 'Form')], max_length=50, verbose_name='Channel')), + ('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='low', max_length=10, verbose_name='Priority')), + ('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('assigned', 'Assigned'), ('in_progress', 'In Progress'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], db_index=True, max_length=50, verbose_name='Status')), + ('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Created')), + ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), + ('assigned', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned', to='inventory.staff', verbose_name='Assigned')), + ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.dealer')), + ('id_car_make', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')), + ('id_car_model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model')), + ], + options={ + 'verbose_name': 'Lead', + 'verbose_name_plural': 'Leads', + }, + ), + migrations.AddField( + model_name='customer', + name='lead', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='converted', to='inventory.lead', verbose_name='Lead'), + ), + migrations.CreateModel( + name='LeadStatusHistory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('old_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('assigned', 'Assigned'), ('in_progress', 'In Progress'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status')), + ('new_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('assigned', 'Assigned'), ('in_progress', 'In Progress'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status')), + ('changed_at', models.DateTimeField(auto_now_add=True, verbose_name='Changed At')), + ('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='status_changes', to='inventory.staff')), + ('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='inventory.lead')), + ], + options={ + 'verbose_name': 'Lead Status History', + 'verbose_name_plural': 'Lead Status Histories', + }, + ), + migrations.DeleteModel( + name='OpportunityLog', + ), + ] diff --git a/inventory/migrations/0009_alter_staff_managers.py b/inventory/migrations/0009_alter_staff_managers.py new file mode 100644 index 00000000..1b94fe19 --- /dev/null +++ b/inventory/migrations/0009_alter_staff_managers.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.4 on 2025-01-09 09:57 + +import inventory.models +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0008_alter_notes_options_alter_notification_options_and_more'), + ] + + operations = [ + migrations.AlterModelManagers( + name='staff', + managers=[ + ('objects', inventory.models.StaffUserManager()), + ], + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 6606a917..6f0bf0c5 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -23,21 +23,32 @@ from django.db.models import Sum from decimal import Decimal, InvalidOperation from django.core.exceptions import ValidationError from phonenumber_field.modelfields import PhoneNumberField -from django.contrib.contenttypes.models import ContentType from django.utils.timezone import now +from sqlalchemy.orm.base import object_state + from .utilities.financials import get_financial_value, get_total, get_total_financials from django.db.models import FloatField from .mixins import LocalizedNameMixin from django_ledger.models import EntityModel,ItemModel +from django_countries.fields import CountryField +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType class DealerUserManager(UserManager): def create_user_with_dealer(self, email, password, dealer_name, arabic_name, crn, vrn, address, **extra_fields): - user = self.create_user(email=email, password=password, **extra_fields) + user = self.create_user(username=email, email=email, password=password, **extra_fields) Dealer.objects.create(user=user, name=dealer_name,arabic_name=arabic_name, crn=crn, vrn=vrn, address=address, **extra_fields) - return user + +class StaffUserManager(UserManager): + def create_user_with_staff(self, email, password, name, arabic_name, phone_number, staff_type, **extra_fields): + user = self.create_user(username=email, email=email, password=password, **extra_fields) + Staff.objects.create(user=user, name=name, arabic_name=arabic_name, phone_number=phone_number, staff_type=staff_type, **extra_fields) + return user + + class UnitOfMeasure(models.TextChoices): EACH = 'EA', 'Each' PAIR = 'PR', 'Pair' @@ -54,6 +65,8 @@ class UnitOfMeasure(models.TextChoices): SQUARE_METER = 'SQ_M', 'Square Meter' PIECE = 'PC', 'Piece' BUNDLE = 'BDL', 'Bundle' + + class VatRate(models.Model): rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal('0.15')) is_active = models.BooleanField(default=True) @@ -62,6 +75,7 @@ class VatRate(models.Model): def __str__(self): return f"Rate: {self.rate}%" + class CarMake(models.Model, LocalizedNameMixin): id_car_make = models.AutoField(primary_key=True) name = models.CharField(max_length=255, blank=True, null=True) @@ -382,6 +396,7 @@ class CarFinance(models.Model): verbose_name = _("Car Financial Details") verbose_name_plural = _("Car Financial Details") + class ExteriorColors(models.Model, LocalizedNameMixin): name = models.CharField(max_length=255, verbose_name=_("Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) @@ -545,7 +560,6 @@ class Subscription(models.Model): return self.users.count() - class SubscriptionUser(models.Model): subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE) @@ -679,8 +693,10 @@ class Staff(models.Model, LocalizedNameMixin): arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) staff_type = models.CharField(choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type")) - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) - updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) + created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) + updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated")) + + objects = StaffUserManager() class Meta: verbose_name = _("Staff") @@ -691,33 +707,10 @@ class Staff(models.Model, LocalizedNameMixin): return f"{self.name} - {self.get_staff_type_display()}" -class ActionChoices(models.TextChoices): - CREATE = "create", _("Create") - UPDATE = "update", _("Update") - DELETE = "delete", _("Delete") - STATUS_CHANGE = "status_change", _("Status Change") - - -class DealStatus(models.TextChoices): - NEW = "new", _("New") - PENDING = "pending", _("Pending") - CANCELED = "canceled", _("Canceled") - COMPLETED = "completed", _("Completed") - - -class Priority(models.TextChoices): - LOW = "low", _("Low") - MEDIUM = "medium", _("Medium") - HIGH = "high", _("High") - - class Sources(models.TextChoices): REFERRALS = "referrals", _("Referrals") - WALK_IN = "walk_in", _("Walk In") - TOLL_FREE = "toll_free", _("Toll Free") WHATSAPP = "whatsapp", _("WhatsApp") SHOWROOM = "showroom", _("Showroom") - WEBSITE = "website", _("Website") TIKTOK = "tiktok", _("TikTok") INSTAGRAM = "instagram", _("Instagram") X = "x", _("X") @@ -725,78 +718,125 @@ class Sources(models.TextChoices): MOTORY = "motory", _("Motory") INFLUENCERS = "influencers", _("Influencers") YOUTUBE = "youtube", _("Youtube") - EMAIL = "email", _("Email") + CAMPAIGN = "campaign", _("Campaign") -class ContactStatus(models.TextChoices): +class Channel(models.TextChoices): + WALK_IN = "walk_in", _("Walk In") + TOLL_FREE = "toll_free", _("Toll Free") + WEBSITE = "website", _("Website") + EMAIL = "email", _("Email") + FORM = "form", _("Form") + + +class Status(models.TextChoices): NEW = "new", _("New") PENDING = "pending", _("Pending") ASSIGNED = "assigned", _("Assigned") + IN_PROGRESS = "in_progress", _("In Progress") CONTACTED = "contacted", _("Contacted") - ACCEPTED = "accepted", _("Accepted") QUALIFIED = "qualified", _("Qualified") CANCELED = "canceled", _("Canceled") +class Title(models.TextChoices): + MR = "mr", _("Mr") + MRS = "mrs", _("Mrs") + MS = "ms", _("Ms") + MISS = "miss", _("Miss") + DR = "dr", _("Dr") + PROF = "prof", _("Prof") + PRINCE = "prince", _("Prince") + PRINCESS = "princess", _("Princess") + COMPANY = "company", _("Company") + NA = "na", _("N/A") -# class Contact(models.Model): -# AGE_RANGES = ( -# ('18-30', '18 - 30'), -# ('31-40', '31 - 40'), -# ('41-50', '41 - 50'), -# ('51-60', '51 - 60'), -# ('61-70', '61 - 70'), -# ('71-80', '71 - 80'), -# ('81-90', '81 - 90'), -# ) -# -# dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="contacts") -# first_name = models.CharField(max_length=50, verbose_name=_("First Name")) -# last_name = models.CharField(max_length=50, verbose_name=_("Last Name")) -# age = models.CharField(choices=AGE_RANGES, max_length=20, verbose_name=_("Age")) -# gender = models.CharField(choices=[('m', _('Male')), ('f', _('Female'))], max_length=1, verbose_name=_("Gender")) -# phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) -# email = models.EmailField(verbose_name=_("Email")) -# id_car_make = models.ForeignKey(CarMake, on_delete=models.DO_NOTHING, verbose_name=_("Make")) -# id_car_model = models.ForeignKey(CarModel, on_delete=models.DO_NOTHING, verbose_name=_("Model")) -# year = models.PositiveSmallIntegerField(verbose_name=_("Year")) -# status = models.CharField(choices=ContactStatus.choices, max_length=255, verbose_name=_("Status"), default=ContactStatus.NEW) -# created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) -# updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) -# enquiry_type = models.CharField(choices=[("quotation", _("Quote")),("testdrive", _("Test drive"))], max_length=50, verbose_name=_("Enquiry Type")) -# purchase_method = models.CharField(choices=[("c", _("Cash")),("f", _("Finance"))], max_length=1, verbose_name=_("Purchase Method")) -# source = models.CharField(max_length=100, choices=Sources.choices, verbose_name=_("Source")) -# salary = models.PositiveIntegerField(verbose_name=_("Salary")) -# obligations = models.PositiveIntegerField(verbose_name=_("Obligations")) -# -# class Meta: -# verbose_name = _("Contact") -# verbose_name_plural = _("Contacts") -# -# def __str__(self): -# return self.first_name + " " + self.last_name +class ActionChoices(models.TextChoices): + CALL = "call", _("Call") + SMS = "sms", _("SMS") + EMAIL = "email", _("Email") + WHATSAPP = "whatsapp", _("WhatsApp") + VISIT = "visit", _("Visit") + ADD_CAR = "add_car", _("Add Car") + RESERVE_CAR = "reserve_car", _("Reserve Car") + REMOVE_CAR = "remove_car", _("Remove Car") + CREATE_QUOTATION = "create_quotation", _("Create Quotation") + CANCEL_QUOTATION = "cancel_quotation", _("Cancel Quotation") + CREATE_ORDER = "create_order", _("Create Order") + CANCEL_ORDER = "cancel_order", _("Cancel Order") + CREATE_INVOICE = "create_invoice", _("Create Invoice") + CANCEL_INVOICE = "cancel_invoice", _("Cancel Invoice") +class Stage(models.TextChoices): + PROSPECT = "prospect", _("Prospect") + PROPOSAL = "proposal", _("Proposal") + NEGOTIATION = "negotiation", _("Negotiation") + CLOSED_WON = "closed_won", _("Closed Won") + CLOSED_LOST = "closed_lost", _("Closed Lost") + +class Priority(models.TextChoices): + LOW = "low", _("Low") + MEDIUM = "medium", _("Medium") + HIGH = "high", _("High") + +class Lead(models.Model): + dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="leads") + title = models.CharField(max_length=20, choices=Title.choices, verbose_name=_("Title")) + first_name = models.CharField(max_length=50, verbose_name=_("First Name")) + last_name = models.CharField(max_length=50, verbose_name=_("Last Name")) + email = models.EmailField(unique=True, verbose_name=_("Email"), db_index=True) + salary = models.PositiveIntegerField(blank=True, null=True, verbose_name=_("Salary")) + obligations = models.PositiveIntegerField(blank=True, null=True, verbose_name=_("Obligations")) + id_car_make = models.ForeignKey(CarMake, on_delete=models.DO_NOTHING, blank=True, null=True, verbose_name=_("Make")) + id_car_model = models.ForeignKey(CarModel, on_delete=models.DO_NOTHING, blank=True, null=True, verbose_name=_("Model")) + year = models.PositiveSmallIntegerField(verbose_name=_("Year"), blank=True, null=True) + source = models.CharField(max_length=50, choices=Sources.choices, verbose_name=_("Source")) + channel = models.CharField(max_length=50, choices=Channel.choices, verbose_name=_("Channel")) + assigned = models.ForeignKey(Staff, on_delete=models.SET_NULL, null=True, related_name="assigned", verbose_name=_("Assigned")) + priority = models.CharField(max_length=10, choices=Priority.choices, default=Priority.LOW, + verbose_name=_("Priority")) + status = models.CharField(max_length=50, choices=Status.choices, verbose_name=_("Status"), db_index=True) + created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"), db_index=True) + updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated")) + + class Meta: + verbose_name = _("Lead") + verbose_name_plural = _("Leads") + + def __str__(self): + return f"{self.first_name} {self.last_name}" + + +class LeadStatusHistory(models.Model): + lead = models.ForeignKey(Lead, on_delete=models.CASCADE, related_name="status_history") + old_status = models.CharField(max_length=50, choices=Status.choices, verbose_name=_("Old Status")) + new_status = models.CharField(max_length=50, choices=Status.choices, verbose_name=_("New Status")) + changed_by = models.ForeignKey(Staff, on_delete=models.DO_NOTHING, related_name="status_changes") + changed_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Changed At")) + + class Meta: + verbose_name = _("Lead Status History") + verbose_name_plural = _("Lead Status Histories") + + def __str__(self): + return f"{self.lead}: {self.old_status} → {self.new_status}" class Customer(models.Model): - dealer = models.ForeignKey(Dealer, - on_delete=models.CASCADE, - related_name="customers") + lead = models.OneToOneField(Lead, on_delete=models.SET_NULL, null=True, blank=True, + related_name="converted", verbose_name=_("Lead")) + dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE,related_name="customers") + title = models.CharField(choices=Title.choices, default=Title.NA, max_length=10, verbose_name=_("Title")) first_name = models.CharField(max_length=50, verbose_name=_("First Name")) - middle_name = models.CharField( - max_length=50, blank=True, null=True, verbose_name=_("Middle Name") - ) + middle_name = models.CharField(max_length=50, blank=True, null=True, verbose_name=_("Middle Name")) last_name = models.CharField(max_length=50, verbose_name=_("Last Name")) + gender = models.CharField(choices=[('m', _('Male')), ('f', _('Female'))], max_length=1, verbose_name=_("Gender")) + dob = models.DateField(verbose_name=_("Date of Birth")) email = models.EmailField(unique=True, verbose_name=_("Email")) - national_id = models.CharField( - max_length=10, unique=True, verbose_name=_("National ID") - ) - phone_number = PhoneNumberField( - region="SA", unique=True, verbose_name=_("Phone Number") - ) - address = models.CharField( - max_length=200, blank=True, null=True, verbose_name=_("Address") - ) + national_id = models.CharField(max_length=10, unique=True, verbose_name=_("National ID")) + country = CountryField(blank=True, verbose_name=_("Country")) + phone_number = PhoneNumberField(region="SA", unique=True, verbose_name=_("Phone Number")) + address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address")) created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) - is_lead = models.BooleanField(default=False, verbose_name=_("Is Lead")) + updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated")) class Meta: verbose_name = _("Customer") @@ -812,65 +852,67 @@ class Customer(models.Model): class Opportunity(models.Model): + dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="opportunities") customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="opportunities") car = models.ForeignKey(Car, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Car")) - deal_name = models.CharField(max_length=255, verbose_name=_("Deal Name")) - deal_value = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_("Deal Value")) - deal_status = models.CharField(max_length=20, choices=DealStatus.choices, default=DealStatus.NEW, verbose_name=_("Deal Status")) - priority = models.CharField(max_length=10, choices=Priority.choices, default=Priority.LOW, verbose_name=_("Priority")) - created_by = models.ForeignKey(Staff, on_delete=models.SET_NULL, null=True, related_name="deals_created") - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) - updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) - + stage = models.CharField(max_length=20, choices=Stage.choices, verbose_name=_("Stage")) + staff = models.ForeignKey(Staff, on_delete=models.SET_NULL, null=True, related_name="owner", verbose_name=_("Owner")) + created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) + updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated")) class Meta: verbose_name = _("Opportunity") verbose_name_plural = _("Opportunities") def __str__(self): - return f"{self.deal_name} - {self.customer.get_full_name}" + return f"{self.car.id_car_make.name} - {self.car.id_car_model.name} : {self.customer.get_full_name}" class Notes(models.Model): - opportunity = models.ForeignKey(Opportunity, on_delete=models.CASCADE, related_name="notes") + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') note = models.TextField(verbose_name=_("Note")) - created_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name="notes_created") - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) - updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) + created_by = models.ForeignKey(Staff, on_delete=models.DO_NOTHING, related_name="notes_created") + created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) + updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated")) class Meta: - verbose_name = _("Notes") + verbose_name = _("Note") verbose_name_plural = _("Notes") + def __str__(self): + return f"Note by {self.created_by.name} on {self.content_object}" -class OpportunityLog(models.Model): - opportunity = models.ForeignKey(Opportunity, on_delete=models.CASCADE, related_name="logs") - action = models.CharField(max_length=50, choices=ActionChoices.choices, verbose_name=_("Action")) - staff = models.ForeignKey(Staff, on_delete=models.SET_NULL, null=True, verbose_name=_("Staff")) - old_status = models.CharField(max_length=50, choices=DealStatus.choices, null=True, blank=True, verbose_name=_("Old Status")) - new_status = models.CharField(max_length=50, choices=DealStatus.choices, null=True, blank=True, verbose_name=_("New Status")) - details = models.TextField(blank=True, null=True, verbose_name=_("Details")) - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) + +class Activity(models.Model): + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + activity_type = models.CharField(max_length=50, choices=ActionChoices.choices, verbose_name=_("Activity Type")) + notes = models.TextField(blank=True, null=True, verbose_name=_("Notes")) + created_by = models.ForeignKey(Staff, on_delete=models.DO_NOTHING, related_name="activities_created") + created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) + updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated")) class Meta: - verbose_name = _("Log") - verbose_name_plural = _("Logs") - ordering = ['-created_at'] + verbose_name = _("Activity") + verbose_name_plural = _("Activities") def __str__(self): - return f"{self.get_action_display()} by {self.user} on {self.opportunity.deal_name}" + return f"{self.get_activity_type_display()} by {self.created_by.name} on {self.content_object}" class Notification(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="notifications") message = models.CharField(max_length=255, verbose_name=_("Message")) is_read = models.BooleanField(default=False, verbose_name=_("Is Read")) - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) + created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) class Meta: verbose_name = _("Notification") verbose_name_plural = _("Notifications") - ordering = ['-created_at'] + ordering = ['-created'] def __str__(self): return self.message diff --git a/inventory/signals.py b/inventory/signals.py index 8ada6434..eedc5957 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -12,7 +12,6 @@ from django_ledger.models import ( VendorModel, ) from . import models -from .models import OpportunityLog User = get_user_model() @@ -430,47 +429,47 @@ def update_item_model_cost(sender, instance, created, **kwargs): @receiver(post_save, sender=models.Opportunity) -def notify_staff_on_deal_status_change(sender, instance, **kwargs): +def notify_staff_on_deal_stage_change(sender, instance, **kwargs): if instance.pk: previous = models.Opportunity.objects.get(pk=instance.pk) - if previous.deal_status != instance.deal_status: - message = f"Deal '{instance.deal_name}' status changed from {previous.deal_status} to {instance.deal_status}." + if previous.stage != instance.deal_status: + message = f"Deal '{instance.deal_name}' status changed from {previous.stage} to {instance.stage}." models.Notification.objects.create( staff=instance.created_by, message=message ) -@receiver(post_save, sender=models.Opportunity) -def log_opportunity_creation(sender, instance, created, **kwargs): - if created: - models.OpportunityLog.objects.create( - opportunity=instance, - action="create", - user=instance.created_by, - details=f"Opportunity '{instance.deal_name}' was created.", - ) +# @receiver(post_save, sender=models.Opportunity) +# def log_opportunity_creation(sender, instance, created, **kwargs): +# if created: +# models.OpportunityLog.objects.create( +# opportunity=instance, +# action="create", +# user=instance.created_by, +# details=f"Opportunity '{instance.deal_name}' was created.", +# ) -@receiver(pre_save, sender=models.Opportunity) -def log_opportunity_update(sender, instance, **kwargs): - if instance.pk: - previous = models.Opportunity.objects.get(pk=instance.pk) - if previous.deal_status != instance.deal_status: - models.OpportunityLog.objects.create( - opportunity=instance, - action="status_change", - user=instance.created_by, - old_status=previous.deal_status, - new_status=instance.deal_status, - details=f"Status changed from {previous.deal_status} to {instance.deal_status}.", - ) - else: - models.OpportunityLog.objects.create( - opportunity=instance, - action="update", - user=instance.created_by, - details=f"Opportunity '{instance.deal_name}' was updated.", - ) +# @receiver(pre_save, sender=models.Opportunity) +# def log_opportunity_update(sender, instance, **kwargs): +# if instance.pk: +# previous = models.Opportunity.objects.get(pk=instance.pk) +# if previous.stage != instance.deal_status: +# models.OpportunityLog.objects.create( +# opportunity=instance, +# action="status_change", +# user=instance.created_by, +# old_status=previous.deal_status, +# new_status=instance.deal_status, +# details=f"Status changed from {previous.deal_status} to {instance.deal_status}.", +# ) +# else: +# models.OpportunityLog.objects.create( +# opportunity=instance, +# action="update", +# user=instance.created_by, +# details=f"Opportunity '{instance.deal_name}' was updated.", +# ) @receiver(post_save, sender=models.AdditionalServices) diff --git a/inventory/urls.py b/inventory/urls.py index 3d083a63..05d0cb03 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -49,7 +49,7 @@ urlpatterns = [ path('crm/opportunities//edit/', views.OpportunityUpdateView.as_view(), name='update_opportunity'), path('crm/opportunities/', views.OpportunityListView.as_view(), name='opportunity_list'), path('crm/opportunities//delete/', views.delete_opportunity, name='delete_opportunity'), - path('crm/opportunities//logs/', views.OpportunityLogsView.as_view(), name='opportunity_logs'), + # path('crm/opportunities//logs/', views.OpportunityLogsView.as_view(), name='opportunity_logs'), path('crm/notifications/', views.NotificationListView.as_view(), name='notifications_history'), path('crm/fetch_notifications/', views.fetch_notifications, name='fetch_notifications'), path('crm/notifications//mark_as_read/', views.mark_notification_as_read, name='mark_notification_as_read'), diff --git a/inventory/views.py b/inventory/views.py index 333c06ee..276b1428 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -758,7 +758,7 @@ class CustomerListView(LoginRequiredMixin, ListView): query = self.request.GET.get("q") dealer = get_user_type(self.request) - customers = models.Customer.objects.filter(dealer=dealer, is_lead=False) + customers = models.Customer.objects.filter(dealer=dealer) if query: customers = customers.filter( @@ -1275,7 +1275,8 @@ class UserCreateView( success_message = _("User created successfully.") def form_valid(self, form): - form.instance.dealer = self.request.user.dealer + dealer = get_user_type(self.request) + form.instance.dealer = dealer email = form.cleaned_data["email"] password = "Tenhal@123" user = User.objects.create_user(username=email, email=email, password=password) @@ -2390,21 +2391,21 @@ def delete_opportunity(request, pk): return redirect("opportunity_list") -class OpportunityLogsView(LoginRequiredMixin, ListView): - model = models.OpportunityLog - template_name = "crm/opportunity_logs.html" - context_object_name = "logs" - - def get_queryset(self): - opportunity_id = self.kwargs["pk"] - return models.OpportunityLog.objects.filter( - opportunity_id=opportunity_id - ).order_by("-created_at") - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["opportunity"] = models.Opportunity.objects.get(pk=self.kwargs["pk"]) - return context +# class OpportunityLogsView(LoginRequiredMixin, ListView): +# model = models.OpportunityLog +# template_name = "crm/opportunity_logs.html" +# context_object_name = "logs" +# +# def get_queryset(self): +# opportunity_id = self.kwargs["pk"] +# return models.OpportunityLog.objects.filter( +# opportunity_id=opportunity_id +# ).order_by("-created_at") +# +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) +# context["opportunity"] = models.Opportunity.objects.get(pk=self.kwargs["pk"]) +# return context class NotificationListView(LoginRequiredMixin, ListView): diff --git a/requirements.txt b/requirements.txt index 501dfa5f..bf4bb8cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,6 +36,7 @@ django-autoslug==1.9.9 django-bootstrap5==24.3 django-classy-tags==4.1.0 django-cors-headers==4.6.0 +django-countries==7.6.1 django-crispy-forms==2.3 django-debug-toolbar==4.4.6 django-extensions==3.2.3 diff --git a/static/.DS_Store b/static/.DS_Store index 8c123dc7..a442dcf5 100644 Binary files a/static/.DS_Store and b/static/.DS_Store differ diff --git a/static/flags/.DS_Store b/static/flags/.DS_Store new file mode 100644 index 00000000..0db71a55 Binary files /dev/null and b/static/flags/.DS_Store differ diff --git a/static/flags/AA.gif b/static/flags/AA.gif new file mode 100644 index 00000000..3f07a5bf Binary files /dev/null and b/static/flags/AA.gif differ diff --git a/static/flags/AC.gif b/static/flags/AC.gif new file mode 100644 index 00000000..6a5570f3 Binary files /dev/null and b/static/flags/AC.gif differ diff --git a/static/flags/AE.gif b/static/flags/AE.gif new file mode 100644 index 00000000..9c92c1a9 Binary files /dev/null and b/static/flags/AE.gif differ diff --git a/static/flags/AF.gif b/static/flags/AF.gif new file mode 100644 index 00000000..2239e847 Binary files /dev/null and b/static/flags/AF.gif differ diff --git a/static/flags/AG.gif b/static/flags/AG.gif new file mode 100644 index 00000000..b6976367 Binary files /dev/null and b/static/flags/AG.gif differ diff --git a/static/flags/AJ.gif b/static/flags/AJ.gif new file mode 100644 index 00000000..cdaa445a Binary files /dev/null and b/static/flags/AJ.gif differ diff --git a/static/flags/AL.gif b/static/flags/AL.gif new file mode 100644 index 00000000..3f849966 Binary files /dev/null and b/static/flags/AL.gif differ diff --git a/static/flags/AM.gif b/static/flags/AM.gif new file mode 100644 index 00000000..29b30657 Binary files /dev/null and b/static/flags/AM.gif differ diff --git a/static/flags/AN.gif b/static/flags/AN.gif new file mode 100644 index 00000000..a3ca692f Binary files /dev/null and b/static/flags/AN.gif differ diff --git a/static/flags/AO.gif b/static/flags/AO.gif new file mode 100644 index 00000000..c342c3a0 Binary files /dev/null and b/static/flags/AO.gif differ diff --git a/static/flags/AQ.gif b/static/flags/AQ.gif new file mode 100644 index 00000000..092edc8d Binary files /dev/null and b/static/flags/AQ.gif differ diff --git a/static/flags/AR.gif b/static/flags/AR.gif new file mode 100644 index 00000000..218180a6 Binary files /dev/null and b/static/flags/AR.gif differ diff --git a/static/flags/AS.gif b/static/flags/AS.gif new file mode 100644 index 00000000..1cc0e2ab Binary files /dev/null and b/static/flags/AS.gif differ diff --git a/static/flags/AT.gif b/static/flags/AT.gif new file mode 100644 index 00000000..80819a9b Binary files /dev/null and b/static/flags/AT.gif differ diff --git a/static/flags/AU.gif b/static/flags/AU.gif new file mode 100644 index 00000000..471a19be Binary files /dev/null and b/static/flags/AU.gif differ diff --git a/static/flags/AV.gif b/static/flags/AV.gif new file mode 100644 index 00000000..bd40a4d7 Binary files /dev/null and b/static/flags/AV.gif differ diff --git a/static/flags/AX.gif b/static/flags/AX.gif new file mode 100644 index 00000000..06dd323d Binary files /dev/null and b/static/flags/AX.gif differ diff --git a/static/flags/BA.gif b/static/flags/BA.gif new file mode 100644 index 00000000..07d24e0d Binary files /dev/null and b/static/flags/BA.gif differ diff --git a/static/flags/BB.gif b/static/flags/BB.gif new file mode 100644 index 00000000..4936710b Binary files /dev/null and b/static/flags/BB.gif differ diff --git a/static/flags/BC.gif b/static/flags/BC.gif new file mode 100644 index 00000000..8c8b13ee Binary files /dev/null and b/static/flags/BC.gif differ diff --git a/static/flags/BD.gif b/static/flags/BD.gif new file mode 100644 index 00000000..1d079308 Binary files /dev/null and b/static/flags/BD.gif differ diff --git a/static/flags/BE.gif b/static/flags/BE.gif new file mode 100644 index 00000000..8346a78a Binary files /dev/null and b/static/flags/BE.gif differ diff --git a/static/flags/BF.gif b/static/flags/BF.gif new file mode 100644 index 00000000..89b6ff45 Binary files /dev/null and b/static/flags/BF.gif differ diff --git a/static/flags/BG.gif b/static/flags/BG.gif new file mode 100644 index 00000000..27c3c8a4 Binary files /dev/null and b/static/flags/BG.gif differ diff --git a/static/flags/BH.gif b/static/flags/BH.gif new file mode 100644 index 00000000..90c7f756 Binary files /dev/null and b/static/flags/BH.gif differ diff --git a/static/flags/BK.gif b/static/flags/BK.gif new file mode 100644 index 00000000..c6d7f2ef Binary files /dev/null and b/static/flags/BK.gif differ diff --git a/static/flags/BL.gif b/static/flags/BL.gif new file mode 100644 index 00000000..5bf4f05d Binary files /dev/null and b/static/flags/BL.gif differ diff --git a/static/flags/BM.gif b/static/flags/BM.gif new file mode 100644 index 00000000..041bd477 Binary files /dev/null and b/static/flags/BM.gif differ diff --git a/static/flags/BN.gif b/static/flags/BN.gif new file mode 100644 index 00000000..6d590fbe Binary files /dev/null and b/static/flags/BN.gif differ diff --git a/static/flags/BO.gif b/static/flags/BO.gif new file mode 100644 index 00000000..92a3d5aa Binary files /dev/null and b/static/flags/BO.gif differ diff --git a/static/flags/BP.gif b/static/flags/BP.gif new file mode 100644 index 00000000..8e86d39a Binary files /dev/null and b/static/flags/BP.gif differ diff --git a/static/flags/BQ.gif b/static/flags/BQ.gif new file mode 100644 index 00000000..99a4d901 Binary files /dev/null and b/static/flags/BQ.gif differ diff --git a/static/flags/BR.gif b/static/flags/BR.gif new file mode 100644 index 00000000..940432b2 Binary files /dev/null and b/static/flags/BR.gif differ diff --git a/static/flags/BT.gif b/static/flags/BT.gif new file mode 100644 index 00000000..7a98c558 Binary files /dev/null and b/static/flags/BT.gif differ diff --git a/static/flags/BU.gif b/static/flags/BU.gif new file mode 100644 index 00000000..681c6c1a Binary files /dev/null and b/static/flags/BU.gif differ diff --git a/static/flags/BV.gif b/static/flags/BV.gif new file mode 100644 index 00000000..3a47dfb2 Binary files /dev/null and b/static/flags/BV.gif differ diff --git a/static/flags/BX.gif b/static/flags/BX.gif new file mode 100644 index 00000000..4a14cfb3 Binary files /dev/null and b/static/flags/BX.gif differ diff --git a/static/flags/BY.gif b/static/flags/BY.gif new file mode 100644 index 00000000..7c469ea6 Binary files /dev/null and b/static/flags/BY.gif differ diff --git a/static/flags/CA.gif b/static/flags/CA.gif new file mode 100644 index 00000000..f2391cd2 Binary files /dev/null and b/static/flags/CA.gif differ diff --git a/static/flags/CB.gif b/static/flags/CB.gif new file mode 100644 index 00000000..eb8a45ad Binary files /dev/null and b/static/flags/CB.gif differ diff --git a/static/flags/CD.gif b/static/flags/CD.gif new file mode 100644 index 00000000..245028e2 Binary files /dev/null and b/static/flags/CD.gif differ diff --git a/static/flags/CE.gif b/static/flags/CE.gif new file mode 100644 index 00000000..30ccb5b4 Binary files /dev/null and b/static/flags/CE.gif differ diff --git a/static/flags/CF.gif b/static/flags/CF.gif new file mode 100644 index 00000000..11174ace Binary files /dev/null and b/static/flags/CF.gif differ diff --git a/static/flags/CG.gif b/static/flags/CG.gif new file mode 100644 index 00000000..a2eda541 Binary files /dev/null and b/static/flags/CG.gif differ diff --git a/static/flags/CH.gif b/static/flags/CH.gif new file mode 100644 index 00000000..b7219be7 Binary files /dev/null and b/static/flags/CH.gif differ diff --git a/static/flags/CI.gif b/static/flags/CI.gif new file mode 100644 index 00000000..bd4af3a9 Binary files /dev/null and b/static/flags/CI.gif differ diff --git a/static/flags/CJ.gif b/static/flags/CJ.gif new file mode 100644 index 00000000..16f0fded Binary files /dev/null and b/static/flags/CJ.gif differ diff --git a/static/flags/CK.gif b/static/flags/CK.gif new file mode 100644 index 00000000..59ca2c01 Binary files /dev/null and b/static/flags/CK.gif differ diff --git a/static/flags/CM.gif b/static/flags/CM.gif new file mode 100644 index 00000000..31af499a Binary files /dev/null and b/static/flags/CM.gif differ diff --git a/static/flags/CN.gif b/static/flags/CN.gif new file mode 100644 index 00000000..c67cc06f Binary files /dev/null and b/static/flags/CN.gif differ diff --git a/static/flags/CO.gif b/static/flags/CO.gif new file mode 100644 index 00000000..cf91dbc4 Binary files /dev/null and b/static/flags/CO.gif differ diff --git a/static/flags/CQ.gif b/static/flags/CQ.gif new file mode 100644 index 00000000..a1347183 Binary files /dev/null and b/static/flags/CQ.gif differ diff --git a/static/flags/CR.gif b/static/flags/CR.gif new file mode 100644 index 00000000..eb13f19d Binary files /dev/null and b/static/flags/CR.gif differ diff --git a/static/flags/CS.gif b/static/flags/CS.gif new file mode 100644 index 00000000..5fec3bb1 Binary files /dev/null and b/static/flags/CS.gif differ diff --git a/static/flags/CT.gif b/static/flags/CT.gif new file mode 100644 index 00000000..8a148d0c Binary files /dev/null and b/static/flags/CT.gif differ diff --git a/static/flags/CU.gif b/static/flags/CU.gif new file mode 100644 index 00000000..73d88b07 Binary files /dev/null and b/static/flags/CU.gif differ diff --git a/static/flags/CV.gif b/static/flags/CV.gif new file mode 100644 index 00000000..6c6633c0 Binary files /dev/null and b/static/flags/CV.gif differ diff --git a/static/flags/CW.gif b/static/flags/CW.gif new file mode 100644 index 00000000..0f3b83ee Binary files /dev/null and b/static/flags/CW.gif differ diff --git a/static/flags/CY.gif b/static/flags/CY.gif new file mode 100644 index 00000000..eb0dbf85 Binary files /dev/null and b/static/flags/CY.gif differ diff --git a/static/flags/DA.gif b/static/flags/DA.gif new file mode 100644 index 00000000..646fa3f9 Binary files /dev/null and b/static/flags/DA.gif differ diff --git a/static/flags/DJ.gif b/static/flags/DJ.gif new file mode 100644 index 00000000..53a9d4c8 Binary files /dev/null and b/static/flags/DJ.gif differ diff --git a/static/flags/DO.gif b/static/flags/DO.gif new file mode 100644 index 00000000..ce0b9dbe Binary files /dev/null and b/static/flags/DO.gif differ diff --git a/static/flags/DR.gif b/static/flags/DR.gif new file mode 100644 index 00000000..c77e1652 Binary files /dev/null and b/static/flags/DR.gif differ diff --git a/static/flags/DX.gif b/static/flags/DX.gif new file mode 100644 index 00000000..ee0252c5 Binary files /dev/null and b/static/flags/DX.gif differ diff --git a/static/flags/EC.gif b/static/flags/EC.gif new file mode 100644 index 00000000..5337fc51 Binary files /dev/null and b/static/flags/EC.gif differ diff --git a/static/flags/EE.gif b/static/flags/EE.gif new file mode 100644 index 00000000..97c742b7 Binary files /dev/null and b/static/flags/EE.gif differ diff --git a/static/flags/EG.gif b/static/flags/EG.gif new file mode 100644 index 00000000..0b069404 Binary files /dev/null and b/static/flags/EG.gif differ diff --git a/static/flags/EI.gif b/static/flags/EI.gif new file mode 100644 index 00000000..53ce5afd Binary files /dev/null and b/static/flags/EI.gif differ diff --git a/static/flags/EK.gif b/static/flags/EK.gif new file mode 100644 index 00000000..03c8577b Binary files /dev/null and b/static/flags/EK.gif differ diff --git a/static/flags/EN.gif b/static/flags/EN.gif new file mode 100644 index 00000000..3fcad607 Binary files /dev/null and b/static/flags/EN.gif differ diff --git a/static/flags/ER.gif b/static/flags/ER.gif new file mode 100644 index 00000000..712bda75 Binary files /dev/null and b/static/flags/ER.gif differ diff --git a/static/flags/ES.gif b/static/flags/ES.gif new file mode 100644 index 00000000..2e5e0120 Binary files /dev/null and b/static/flags/ES.gif differ diff --git a/static/flags/ET.gif b/static/flags/ET.gif new file mode 100644 index 00000000..036f16fa Binary files /dev/null and b/static/flags/ET.gif differ diff --git a/static/flags/EZ.gif b/static/flags/EZ.gif new file mode 100644 index 00000000..b2e9763a Binary files /dev/null and b/static/flags/EZ.gif differ diff --git a/static/flags/FI.gif b/static/flags/FI.gif new file mode 100644 index 00000000..268207e8 Binary files /dev/null and b/static/flags/FI.gif differ diff --git a/static/flags/FJ.gif b/static/flags/FJ.gif new file mode 100644 index 00000000..175c6a15 Binary files /dev/null and b/static/flags/FJ.gif differ diff --git a/static/flags/FK.gif b/static/flags/FK.gif new file mode 100644 index 00000000..12e5de30 Binary files /dev/null and b/static/flags/FK.gif differ diff --git a/static/flags/FM.gif b/static/flags/FM.gif new file mode 100644 index 00000000..1fcaf2c6 Binary files /dev/null and b/static/flags/FM.gif differ diff --git a/static/flags/FO.gif b/static/flags/FO.gif new file mode 100644 index 00000000..f7dae99a Binary files /dev/null and b/static/flags/FO.gif differ diff --git a/static/flags/FP.gif b/static/flags/FP.gif new file mode 100644 index 00000000..49006b51 Binary files /dev/null and b/static/flags/FP.gif differ diff --git a/static/flags/FR.gif b/static/flags/FR.gif new file mode 100644 index 00000000..c9ac8506 Binary files /dev/null and b/static/flags/FR.gif differ diff --git a/static/flags/FS.gif b/static/flags/FS.gif new file mode 100644 index 00000000..af6d849f Binary files /dev/null and b/static/flags/FS.gif differ diff --git a/static/flags/GA.gif b/static/flags/GA.gif new file mode 100644 index 00000000..29a60442 Binary files /dev/null and b/static/flags/GA.gif differ diff --git a/static/flags/GB.gif b/static/flags/GB.gif new file mode 100644 index 00000000..9d171a6f Binary files /dev/null and b/static/flags/GB.gif differ diff --git a/static/flags/GG.gif b/static/flags/GG.gif new file mode 100644 index 00000000..b537bcd5 Binary files /dev/null and b/static/flags/GG.gif differ diff --git a/static/flags/GH.gif b/static/flags/GH.gif new file mode 100644 index 00000000..739ac028 Binary files /dev/null and b/static/flags/GH.gif differ diff --git a/static/flags/GI.gif b/static/flags/GI.gif new file mode 100644 index 00000000..c56a001b Binary files /dev/null and b/static/flags/GI.gif differ diff --git a/static/flags/GJ.gif b/static/flags/GJ.gif new file mode 100644 index 00000000..9bab7922 Binary files /dev/null and b/static/flags/GJ.gif differ diff --git a/static/flags/GK.gif b/static/flags/GK.gif new file mode 100644 index 00000000..aa762600 Binary files /dev/null and b/static/flags/GK.gif differ diff --git a/static/flags/GL.gif b/static/flags/GL.gif new file mode 100644 index 00000000..e5dc211c Binary files /dev/null and b/static/flags/GL.gif differ diff --git a/static/flags/GM.gif b/static/flags/GM.gif new file mode 100644 index 00000000..6d01d755 Binary files /dev/null and b/static/flags/GM.gif differ diff --git a/static/flags/GQ.gif b/static/flags/GQ.gif new file mode 100644 index 00000000..6c84c8f6 Binary files /dev/null and b/static/flags/GQ.gif differ diff --git a/static/flags/GR.gif b/static/flags/GR.gif new file mode 100644 index 00000000..fc070fdb Binary files /dev/null and b/static/flags/GR.gif differ diff --git a/static/flags/GT.gif b/static/flags/GT.gif new file mode 100644 index 00000000..85a89dd4 Binary files /dev/null and b/static/flags/GT.gif differ diff --git a/static/flags/GV.gif b/static/flags/GV.gif new file mode 100644 index 00000000..4426494c Binary files /dev/null and b/static/flags/GV.gif differ diff --git a/static/flags/GY.gif b/static/flags/GY.gif new file mode 100644 index 00000000..e94b5507 Binary files /dev/null and b/static/flags/GY.gif differ diff --git a/static/flags/HA.gif b/static/flags/HA.gif new file mode 100644 index 00000000..c219a97d Binary files /dev/null and b/static/flags/HA.gif differ diff --git a/static/flags/HK.gif b/static/flags/HK.gif new file mode 100644 index 00000000..edf29d40 Binary files /dev/null and b/static/flags/HK.gif differ diff --git a/static/flags/HM.gif b/static/flags/HM.gif new file mode 100644 index 00000000..9622e532 Binary files /dev/null and b/static/flags/HM.gif differ diff --git a/static/flags/HO.gif b/static/flags/HO.gif new file mode 100644 index 00000000..dae527c6 Binary files /dev/null and b/static/flags/HO.gif differ diff --git a/static/flags/HR.gif b/static/flags/HR.gif new file mode 100644 index 00000000..875b2f6b Binary files /dev/null and b/static/flags/HR.gif differ diff --git a/static/flags/HU.gif b/static/flags/HU.gif new file mode 100644 index 00000000..752ce464 Binary files /dev/null and b/static/flags/HU.gif differ diff --git a/static/flags/IC.gif b/static/flags/IC.gif new file mode 100644 index 00000000..7aaa70c6 Binary files /dev/null and b/static/flags/IC.gif differ diff --git a/static/flags/ID.gif b/static/flags/ID.gif new file mode 100644 index 00000000..b260456a Binary files /dev/null and b/static/flags/ID.gif differ diff --git a/static/flags/IM.gif b/static/flags/IM.gif new file mode 100644 index 00000000..998345de Binary files /dev/null and b/static/flags/IM.gif differ diff --git a/static/flags/IN.gif b/static/flags/IN.gif new file mode 100644 index 00000000..6480d010 Binary files /dev/null and b/static/flags/IN.gif differ diff --git a/static/flags/IO.gif b/static/flags/IO.gif new file mode 100644 index 00000000..944a97cb Binary files /dev/null and b/static/flags/IO.gif differ diff --git a/static/flags/IP.gif b/static/flags/IP.gif new file mode 100644 index 00000000..c271eb8a Binary files /dev/null and b/static/flags/IP.gif differ diff --git a/static/flags/IR.gif b/static/flags/IR.gif new file mode 100644 index 00000000..7c939554 Binary files /dev/null and b/static/flags/IR.gif differ diff --git a/static/flags/IT.gif b/static/flags/IT.gif new file mode 100644 index 00000000..10caf592 Binary files /dev/null and b/static/flags/IT.gif differ diff --git a/static/flags/IV.gif b/static/flags/IV.gif new file mode 100644 index 00000000..121a055b Binary files /dev/null and b/static/flags/IV.gif differ diff --git a/static/flags/IZ.gif b/static/flags/IZ.gif new file mode 100644 index 00000000..94d3f1e6 Binary files /dev/null and b/static/flags/IZ.gif differ diff --git a/static/flags/JA.gif b/static/flags/JA.gif new file mode 100644 index 00000000..2de4b487 Binary files /dev/null and b/static/flags/JA.gif differ diff --git a/static/flags/JE.gif b/static/flags/JE.gif new file mode 100644 index 00000000..de32c0dd Binary files /dev/null and b/static/flags/JE.gif differ diff --git a/static/flags/JM.gif b/static/flags/JM.gif new file mode 100644 index 00000000..d9df73cb Binary files /dev/null and b/static/flags/JM.gif differ diff --git a/static/flags/JN.gif b/static/flags/JN.gif new file mode 100644 index 00000000..587cafdd Binary files /dev/null and b/static/flags/JN.gif differ diff --git a/static/flags/JO.gif b/static/flags/JO.gif new file mode 100644 index 00000000..66903bda Binary files /dev/null and b/static/flags/JO.gif differ diff --git a/static/flags/KE.gif b/static/flags/KE.gif new file mode 100644 index 00000000..efb1b2ea Binary files /dev/null and b/static/flags/KE.gif differ diff --git a/static/flags/KG.gif b/static/flags/KG.gif new file mode 100644 index 00000000..6190ce21 Binary files /dev/null and b/static/flags/KG.gif differ diff --git a/static/flags/KN.gif b/static/flags/KN.gif new file mode 100644 index 00000000..92c104b1 Binary files /dev/null and b/static/flags/KN.gif differ diff --git a/static/flags/KR.gif b/static/flags/KR.gif new file mode 100644 index 00000000..0551c691 Binary files /dev/null and b/static/flags/KR.gif differ diff --git a/static/flags/KS.gif b/static/flags/KS.gif new file mode 100644 index 00000000..39fc52c0 Binary files /dev/null and b/static/flags/KS.gif differ diff --git a/static/flags/KT.gif b/static/flags/KT.gif new file mode 100644 index 00000000..cd540240 Binary files /dev/null and b/static/flags/KT.gif differ diff --git a/static/flags/KU.gif b/static/flags/KU.gif new file mode 100644 index 00000000..dd30d20e Binary files /dev/null and b/static/flags/KU.gif differ diff --git a/static/flags/KV.gif b/static/flags/KV.gif new file mode 100644 index 00000000..2bdc542e Binary files /dev/null and b/static/flags/KV.gif differ diff --git a/static/flags/KZ.gif b/static/flags/KZ.gif new file mode 100644 index 00000000..9546b3a7 Binary files /dev/null and b/static/flags/KZ.gif differ diff --git a/static/flags/LA.gif b/static/flags/LA.gif new file mode 100644 index 00000000..cdeda567 Binary files /dev/null and b/static/flags/LA.gif differ diff --git a/static/flags/LE.gif b/static/flags/LE.gif new file mode 100644 index 00000000..2c88298c Binary files /dev/null and b/static/flags/LE.gif differ diff --git a/static/flags/LG.gif b/static/flags/LG.gif new file mode 100644 index 00000000..39be2676 Binary files /dev/null and b/static/flags/LG.gif differ diff --git a/static/flags/LH.gif b/static/flags/LH.gif new file mode 100644 index 00000000..e602d43b Binary files /dev/null and b/static/flags/LH.gif differ diff --git a/static/flags/LI.gif b/static/flags/LI.gif new file mode 100644 index 00000000..beddd3e7 Binary files /dev/null and b/static/flags/LI.gif differ diff --git a/static/flags/LO.gif b/static/flags/LO.gif new file mode 100644 index 00000000..80e1455b Binary files /dev/null and b/static/flags/LO.gif differ diff --git a/static/flags/LS.gif b/static/flags/LS.gif new file mode 100644 index 00000000..7514d0e5 Binary files /dev/null and b/static/flags/LS.gif differ diff --git a/static/flags/LT.gif b/static/flags/LT.gif new file mode 100644 index 00000000..18b6eb78 Binary files /dev/null and b/static/flags/LT.gif differ diff --git a/static/flags/LU.gif b/static/flags/LU.gif new file mode 100644 index 00000000..4f2e7d56 Binary files /dev/null and b/static/flags/LU.gif differ diff --git a/static/flags/LY.gif b/static/flags/LY.gif new file mode 100644 index 00000000..4a4f6678 Binary files /dev/null and b/static/flags/LY.gif differ diff --git a/static/flags/MA.gif b/static/flags/MA.gif new file mode 100644 index 00000000..73d82598 Binary files /dev/null and b/static/flags/MA.gif differ diff --git a/static/flags/MC.gif b/static/flags/MC.gif new file mode 100644 index 00000000..9d072785 Binary files /dev/null and b/static/flags/MC.gif differ diff --git a/static/flags/MD.gif b/static/flags/MD.gif new file mode 100644 index 00000000..ec8be54f Binary files /dev/null and b/static/flags/MD.gif differ diff --git a/static/flags/MG.gif b/static/flags/MG.gif new file mode 100644 index 00000000..d24b4108 Binary files /dev/null and b/static/flags/MG.gif differ diff --git a/static/flags/MH.gif b/static/flags/MH.gif new file mode 100644 index 00000000..88a70c23 Binary files /dev/null and b/static/flags/MH.gif differ diff --git a/static/flags/MI.gif b/static/flags/MI.gif new file mode 100644 index 00000000..b21c0854 Binary files /dev/null and b/static/flags/MI.gif differ diff --git a/static/flags/MJ.gif b/static/flags/MJ.gif new file mode 100644 index 00000000..bdbdf2b9 Binary files /dev/null and b/static/flags/MJ.gif differ diff --git a/static/flags/MK.gif b/static/flags/MK.gif new file mode 100644 index 00000000..434dbe29 Binary files /dev/null and b/static/flags/MK.gif differ diff --git a/static/flags/ML.gif b/static/flags/ML.gif new file mode 100644 index 00000000..401fa025 Binary files /dev/null and b/static/flags/ML.gif differ diff --git a/static/flags/MN.gif b/static/flags/MN.gif new file mode 100644 index 00000000..8640cf74 Binary files /dev/null and b/static/flags/MN.gif differ diff --git a/static/flags/MO.gif b/static/flags/MO.gif new file mode 100644 index 00000000..5589cb4e Binary files /dev/null and b/static/flags/MO.gif differ diff --git a/static/flags/MP.gif b/static/flags/MP.gif new file mode 100644 index 00000000..69e1025f Binary files /dev/null and b/static/flags/MP.gif differ diff --git a/static/flags/MR.gif b/static/flags/MR.gif new file mode 100644 index 00000000..3b27a97c Binary files /dev/null and b/static/flags/MR.gif differ diff --git a/static/flags/MT.gif b/static/flags/MT.gif new file mode 100644 index 00000000..3e69fce6 Binary files /dev/null and b/static/flags/MT.gif differ diff --git a/static/flags/MU.gif b/static/flags/MU.gif new file mode 100644 index 00000000..b72eb231 Binary files /dev/null and b/static/flags/MU.gif differ diff --git a/static/flags/MV.gif b/static/flags/MV.gif new file mode 100644 index 00000000..e69a1f3f Binary files /dev/null and b/static/flags/MV.gif differ diff --git a/static/flags/MX.gif b/static/flags/MX.gif new file mode 100644 index 00000000..dec6bdb5 Binary files /dev/null and b/static/flags/MX.gif differ diff --git a/static/flags/MY.gif b/static/flags/MY.gif new file mode 100644 index 00000000..4c89f94d Binary files /dev/null and b/static/flags/MY.gif differ diff --git a/static/flags/MZ.gif b/static/flags/MZ.gif new file mode 100644 index 00000000..739577ac Binary files /dev/null and b/static/flags/MZ.gif differ diff --git a/static/flags/NC.gif b/static/flags/NC.gif new file mode 100644 index 00000000..28564767 Binary files /dev/null and b/static/flags/NC.gif differ diff --git a/static/flags/NE.gif b/static/flags/NE.gif new file mode 100644 index 00000000..d2b7eda9 Binary files /dev/null and b/static/flags/NE.gif differ diff --git a/static/flags/NF.gif b/static/flags/NF.gif new file mode 100644 index 00000000..11520be9 Binary files /dev/null and b/static/flags/NF.gif differ diff --git a/static/flags/NG.gif b/static/flags/NG.gif new file mode 100644 index 00000000..20c4c158 Binary files /dev/null and b/static/flags/NG.gif differ diff --git a/static/flags/NH.gif b/static/flags/NH.gif new file mode 100644 index 00000000..f5e76fc6 Binary files /dev/null and b/static/flags/NH.gif differ diff --git a/static/flags/NI.gif b/static/flags/NI.gif new file mode 100644 index 00000000..41a7ead6 Binary files /dev/null and b/static/flags/NI.gif differ diff --git a/static/flags/NL.gif b/static/flags/NL.gif new file mode 100644 index 00000000..49e1e3a3 Binary files /dev/null and b/static/flags/NL.gif differ diff --git a/static/flags/NN.gif b/static/flags/NN.gif new file mode 100644 index 00000000..d1cb60c2 Binary files /dev/null and b/static/flags/NN.gif differ diff --git a/static/flags/NO.gif b/static/flags/NO.gif new file mode 100644 index 00000000..a41e0cba Binary files /dev/null and b/static/flags/NO.gif differ diff --git a/static/flags/NP.gif b/static/flags/NP.gif new file mode 100644 index 00000000..9591956b Binary files /dev/null and b/static/flags/NP.gif differ diff --git a/static/flags/NR.gif b/static/flags/NR.gif new file mode 100644 index 00000000..1068a18d Binary files /dev/null and b/static/flags/NR.gif differ diff --git a/static/flags/NS.gif b/static/flags/NS.gif new file mode 100644 index 00000000..cfc6156b Binary files /dev/null and b/static/flags/NS.gif differ diff --git a/static/flags/NU.gif b/static/flags/NU.gif new file mode 100644 index 00000000..28d3c95a Binary files /dev/null and b/static/flags/NU.gif differ diff --git a/static/flags/NZ.gif b/static/flags/NZ.gif new file mode 100644 index 00000000..77c9afbf Binary files /dev/null and b/static/flags/NZ.gif differ diff --git a/static/flags/OD.gif b/static/flags/OD.gif new file mode 100644 index 00000000..78c803af Binary files /dev/null and b/static/flags/OD.gif differ diff --git a/static/flags/PA.gif b/static/flags/PA.gif new file mode 100644 index 00000000..1fac8aa7 Binary files /dev/null and b/static/flags/PA.gif differ diff --git a/static/flags/PC.gif b/static/flags/PC.gif new file mode 100644 index 00000000..2c5731c0 Binary files /dev/null and b/static/flags/PC.gif differ diff --git a/static/flags/PE.gif b/static/flags/PE.gif new file mode 100644 index 00000000..bc98826e Binary files /dev/null and b/static/flags/PE.gif differ diff --git a/static/flags/PK.gif b/static/flags/PK.gif new file mode 100644 index 00000000..76894682 Binary files /dev/null and b/static/flags/PK.gif differ diff --git a/static/flags/PL.gif b/static/flags/PL.gif new file mode 100644 index 00000000..397152a5 Binary files /dev/null and b/static/flags/PL.gif differ diff --git a/static/flags/PM.gif b/static/flags/PM.gif new file mode 100644 index 00000000..d3929cac Binary files /dev/null and b/static/flags/PM.gif differ diff --git a/static/flags/PO.gif b/static/flags/PO.gif new file mode 100644 index 00000000..63bc2d4b Binary files /dev/null and b/static/flags/PO.gif differ diff --git a/static/flags/PP.gif b/static/flags/PP.gif new file mode 100644 index 00000000..0cd6ee06 Binary files /dev/null and b/static/flags/PP.gif differ diff --git a/static/flags/PS.gif b/static/flags/PS.gif new file mode 100644 index 00000000..5b876910 Binary files /dev/null and b/static/flags/PS.gif differ diff --git a/static/flags/PU.gif b/static/flags/PU.gif new file mode 100644 index 00000000..366ffe2d Binary files /dev/null and b/static/flags/PU.gif differ diff --git a/static/flags/QA.gif b/static/flags/QA.gif new file mode 100644 index 00000000..9be229fc Binary files /dev/null and b/static/flags/QA.gif differ diff --git a/static/flags/RI.gif b/static/flags/RI.gif new file mode 100644 index 00000000..7549f69d Binary files /dev/null and b/static/flags/RI.gif differ diff --git a/static/flags/RM.gif b/static/flags/RM.gif new file mode 100644 index 00000000..7c6178fe Binary files /dev/null and b/static/flags/RM.gif differ diff --git a/static/flags/RN.gif b/static/flags/RN.gif new file mode 100644 index 00000000..078ce526 Binary files /dev/null and b/static/flags/RN.gif differ diff --git a/static/flags/RO.gif b/static/flags/RO.gif new file mode 100644 index 00000000..f119b35b Binary files /dev/null and b/static/flags/RO.gif differ diff --git a/static/flags/RP.gif b/static/flags/RP.gif new file mode 100644 index 00000000..64309d12 Binary files /dev/null and b/static/flags/RP.gif differ diff --git a/static/flags/RQ.gif b/static/flags/RQ.gif new file mode 100644 index 00000000..a5c95751 Binary files /dev/null and b/static/flags/RQ.gif differ diff --git a/static/flags/RS.gif b/static/flags/RS.gif new file mode 100644 index 00000000..b2380ec4 Binary files /dev/null and b/static/flags/RS.gif differ diff --git a/static/flags/RW.gif b/static/flags/RW.gif new file mode 100644 index 00000000..dd038bf4 Binary files /dev/null and b/static/flags/RW.gif differ diff --git a/static/flags/SA.gif b/static/flags/SA.gif new file mode 100644 index 00000000..c73987ef Binary files /dev/null and b/static/flags/SA.gif differ diff --git a/static/flags/SB.gif b/static/flags/SB.gif new file mode 100644 index 00000000..faaee253 Binary files /dev/null and b/static/flags/SB.gif differ diff --git a/static/flags/SC.gif b/static/flags/SC.gif new file mode 100644 index 00000000..fcce7df0 Binary files /dev/null and b/static/flags/SC.gif differ diff --git a/static/flags/SE.gif b/static/flags/SE.gif new file mode 100644 index 00000000..9656b236 Binary files /dev/null and b/static/flags/SE.gif differ diff --git a/static/flags/SF.gif b/static/flags/SF.gif new file mode 100644 index 00000000..f83f703a Binary files /dev/null and b/static/flags/SF.gif differ diff --git a/static/flags/SG.gif b/static/flags/SG.gif new file mode 100644 index 00000000..90b8b757 Binary files /dev/null and b/static/flags/SG.gif differ diff --git a/static/flags/SH.gif b/static/flags/SH.gif new file mode 100644 index 00000000..d228f3be Binary files /dev/null and b/static/flags/SH.gif differ diff --git a/static/flags/SI.gif b/static/flags/SI.gif new file mode 100644 index 00000000..98949b16 Binary files /dev/null and b/static/flags/SI.gif differ diff --git a/static/flags/SL.gif b/static/flags/SL.gif new file mode 100644 index 00000000..0fd12aa0 Binary files /dev/null and b/static/flags/SL.gif differ diff --git a/static/flags/SM.gif b/static/flags/SM.gif new file mode 100644 index 00000000..24a233f0 Binary files /dev/null and b/static/flags/SM.gif differ diff --git a/static/flags/SN.gif b/static/flags/SN.gif new file mode 100644 index 00000000..5e192745 Binary files /dev/null and b/static/flags/SN.gif differ diff --git a/static/flags/SO.gif b/static/flags/SO.gif new file mode 100644 index 00000000..012871b9 Binary files /dev/null and b/static/flags/SO.gif differ diff --git a/static/flags/SP.gif b/static/flags/SP.gif new file mode 100644 index 00000000..7cf2cb7c Binary files /dev/null and b/static/flags/SP.gif differ diff --git a/static/flags/ST.gif b/static/flags/ST.gif new file mode 100644 index 00000000..a2c4e593 Binary files /dev/null and b/static/flags/ST.gif differ diff --git a/static/flags/SU.gif b/static/flags/SU.gif new file mode 100644 index 00000000..dbbc7bf0 Binary files /dev/null and b/static/flags/SU.gif differ diff --git a/static/flags/SV.gif b/static/flags/SV.gif new file mode 100644 index 00000000..3788bc4e Binary files /dev/null and b/static/flags/SV.gif differ diff --git a/static/flags/SW.gif b/static/flags/SW.gif new file mode 100644 index 00000000..4fdb247a Binary files /dev/null and b/static/flags/SW.gif differ diff --git a/static/flags/SX.gif b/static/flags/SX.gif new file mode 100644 index 00000000..8db3abe2 Binary files /dev/null and b/static/flags/SX.gif differ diff --git a/static/flags/SY.gif b/static/flags/SY.gif new file mode 100644 index 00000000..ecba9bfb Binary files /dev/null and b/static/flags/SY.gif differ diff --git a/static/flags/SZ.gif b/static/flags/SZ.gif new file mode 100644 index 00000000..ea21a6b1 Binary files /dev/null and b/static/flags/SZ.gif differ diff --git a/static/flags/TB.gif b/static/flags/TB.gif new file mode 100644 index 00000000..a165d9e4 Binary files /dev/null and b/static/flags/TB.gif differ diff --git a/static/flags/TD.gif b/static/flags/TD.gif new file mode 100644 index 00000000..7d578a0b Binary files /dev/null and b/static/flags/TD.gif differ diff --git a/static/flags/TH.gif b/static/flags/TH.gif new file mode 100644 index 00000000..55813891 Binary files /dev/null and b/static/flags/TH.gif differ diff --git a/static/flags/TI.gif b/static/flags/TI.gif new file mode 100644 index 00000000..a2d1403c Binary files /dev/null and b/static/flags/TI.gif differ diff --git a/static/flags/TK.gif b/static/flags/TK.gif new file mode 100644 index 00000000..0bfb031b Binary files /dev/null and b/static/flags/TK.gif differ diff --git a/static/flags/TL.gif b/static/flags/TL.gif new file mode 100644 index 00000000..d59697ba Binary files /dev/null and b/static/flags/TL.gif differ diff --git a/static/flags/TN.gif b/static/flags/TN.gif new file mode 100644 index 00000000..4e1f69bd Binary files /dev/null and b/static/flags/TN.gif differ diff --git a/static/flags/TO.gif b/static/flags/TO.gif new file mode 100644 index 00000000..847a085d Binary files /dev/null and b/static/flags/TO.gif differ diff --git a/static/flags/TP.gif b/static/flags/TP.gif new file mode 100644 index 00000000..3d3f2936 Binary files /dev/null and b/static/flags/TP.gif differ diff --git a/static/flags/TS.gif b/static/flags/TS.gif new file mode 100644 index 00000000..b09a9589 Binary files /dev/null and b/static/flags/TS.gif differ diff --git a/static/flags/TT.gif b/static/flags/TT.gif new file mode 100644 index 00000000..8627e3b0 Binary files /dev/null and b/static/flags/TT.gif differ diff --git a/static/flags/TU.gif b/static/flags/TU.gif new file mode 100644 index 00000000..1033c1cf Binary files /dev/null and b/static/flags/TU.gif differ diff --git a/static/flags/TV.gif b/static/flags/TV.gif new file mode 100644 index 00000000..c5636e4f Binary files /dev/null and b/static/flags/TV.gif differ diff --git a/static/flags/TW.gif b/static/flags/TW.gif new file mode 100644 index 00000000..346a4fc4 Binary files /dev/null and b/static/flags/TW.gif differ diff --git a/static/flags/TX.gif b/static/flags/TX.gif new file mode 100644 index 00000000..f577c4fa Binary files /dev/null and b/static/flags/TX.gif differ diff --git a/static/flags/TZ.gif b/static/flags/TZ.gif new file mode 100644 index 00000000..5619e72f Binary files /dev/null and b/static/flags/TZ.gif differ diff --git a/static/flags/UC.gif b/static/flags/UC.gif new file mode 100644 index 00000000..b675e991 Binary files /dev/null and b/static/flags/UC.gif differ diff --git a/static/flags/UG.gif b/static/flags/UG.gif new file mode 100644 index 00000000..daa2888b Binary files /dev/null and b/static/flags/UG.gif differ diff --git a/static/flags/UK.gif b/static/flags/UK.gif new file mode 100644 index 00000000..17b15b75 Binary files /dev/null and b/static/flags/UK.gif differ diff --git a/static/flags/UM.gif b/static/flags/UM.gif new file mode 100644 index 00000000..7269199a Binary files /dev/null and b/static/flags/UM.gif differ diff --git a/static/flags/UP.gif b/static/flags/UP.gif new file mode 100644 index 00000000..0a3a3610 Binary files /dev/null and b/static/flags/UP.gif differ diff --git a/static/flags/US.gif b/static/flags/US.gif new file mode 100644 index 00000000..12a78a83 Binary files /dev/null and b/static/flags/US.gif differ diff --git a/static/flags/UV.gif b/static/flags/UV.gif new file mode 100644 index 00000000..e7b376be Binary files /dev/null and b/static/flags/UV.gif differ diff --git a/static/flags/UY.gif b/static/flags/UY.gif new file mode 100644 index 00000000..31c81fdb Binary files /dev/null and b/static/flags/UY.gif differ diff --git a/static/flags/UZ.gif b/static/flags/UZ.gif new file mode 100644 index 00000000..00426418 Binary files /dev/null and b/static/flags/UZ.gif differ diff --git a/static/flags/VC.gif b/static/flags/VC.gif new file mode 100644 index 00000000..3050029f Binary files /dev/null and b/static/flags/VC.gif differ diff --git a/static/flags/VE.gif b/static/flags/VE.gif new file mode 100644 index 00000000..f4f2bb0b Binary files /dev/null and b/static/flags/VE.gif differ diff --git a/static/flags/VI.gif b/static/flags/VI.gif new file mode 100644 index 00000000..fd91d6c4 Binary files /dev/null and b/static/flags/VI.gif differ diff --git a/static/flags/VM.gif b/static/flags/VM.gif new file mode 100644 index 00000000..d14403a5 Binary files /dev/null and b/static/flags/VM.gif differ diff --git a/static/flags/VQ.gif b/static/flags/VQ.gif new file mode 100644 index 00000000..34ce601d Binary files /dev/null and b/static/flags/VQ.gif differ diff --git a/static/flags/VT.gif b/static/flags/VT.gif new file mode 100644 index 00000000..e2be0237 Binary files /dev/null and b/static/flags/VT.gif differ diff --git a/static/flags/WA.gif b/static/flags/WA.gif new file mode 100644 index 00000000..fb0661e2 Binary files /dev/null and b/static/flags/WA.gif differ diff --git a/static/flags/WF.gif b/static/flags/WF.gif new file mode 100644 index 00000000..bdfe3563 Binary files /dev/null and b/static/flags/WF.gif differ diff --git a/static/flags/WQ.gif b/static/flags/WQ.gif new file mode 100644 index 00000000..80daab2b Binary files /dev/null and b/static/flags/WQ.gif differ diff --git a/static/flags/WS.gif b/static/flags/WS.gif new file mode 100644 index 00000000..9bd05dda Binary files /dev/null and b/static/flags/WS.gif differ diff --git a/static/flags/WZ.gif b/static/flags/WZ.gif new file mode 100644 index 00000000..6f486d74 Binary files /dev/null and b/static/flags/WZ.gif differ diff --git a/static/flags/YM.gif b/static/flags/YM.gif new file mode 100644 index 00000000..485c12ac Binary files /dev/null and b/static/flags/YM.gif differ diff --git a/static/flags/ZA.gif b/static/flags/ZA.gif new file mode 100644 index 00000000..54e7652c Binary files /dev/null and b/static/flags/ZA.gif differ diff --git a/static/flags/ZI.gif b/static/flags/ZI.gif new file mode 100644 index 00000000..71aae712 Binary files /dev/null and b/static/flags/ZI.gif differ diff --git a/static/flags/flags.rtf b/static/flags/flags.rtf new file mode 100644 index 00000000..07181fce --- /dev/null +++ b/static/flags/flags.rtf @@ -0,0 +1,256 @@ +{\rtf1\ansi\ansicpg1252\cocoartf2821 +\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +{\*\expandedcolortbl;;} +\paperw11900\paperh16840\margl1440\margr1440\vieww27940\viewh17020\viewkind0 +\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 + +\f0\fs96 \cf0 \ +AF, Afghanistan,\ +AL, Albania,\ +DZ, Algeria\ +AS, American Samoa\ +AD, Andorra\ +AO, Angola \ +AI, Anguilla\ +AQ, Antarctica\ +AG, Antigua and Barbuda\ +AR, Argentina\ +AM, Armenia\ +AW, Aruba\ +AU, Australia\ +AT, Austria\ +AZ, Azerbaijan\ +BS, Bahamas\ +BH, Bahrain\ +BD, Bangladesh\ +BB, Barbados\ +BY, Belarus\ +BE, Belgium\ +BZ, Belize\ +BJ, Benin\ +BM, Bermuda\ +BT, Bhutan\ +BO, Bolivia\ +BQ, Bonaire, Sint Eustatius and Saba\ +BA, Bosnia and Herzegovina\ +BW, Botswana\ +BV, Bouvet Island\ +BR, Brazil\ +IO, British Indian Ocean Territory\ +BN, Brunei\ +BG, Bulgaria\ +BF, Burkina Faso\ +BI, Burundi\ +CV, Cabo Verde\ +KH, Cambodia\ +CM, Cameroon\ +CA, Canada\ +KY, Cayman Islands\ +CF, Central African Republic\ +TD, Chad\ +CL, Chile\ +CN, China\ +CX, Christmas Island\ +CC, Cocos (Keeling) Islands\ +CO, Colombia\ +KM, Comoros\ +CG, Congo\ +CD, Congo (the Democratic Republic of the)\ +CK, Cook Islands\ +CR, Costa Rica\ +CI, C\'f4te d'Ivoire\ +HR, Croatia\ +CU, Cuba\ +CW, Cura\'e7ao\ +CY, Cyprus\ +CZ, Czechia\ +DK, Denmark\ +DJ, Djibouti\ +DM, Dominica\ +DO, Dominican Republic\ +EC, Ecuador\ +EG, Egypt\ +SV, El Salvador\ +GQ, Equatorial Guinea\ +ER, Eritrea\ +EE, Estonia\ +SZ, Eswatini\ +ET, Ethiopia\ +FK, Falkland Islands (Malvinas)\ +FO, Faroe Islands\ +FJ, Fiji\ +FI, Finland\ +FR, France\ +GF, French Guiana\ +PF, French Polynesia\ +TF, French Southern Territories\ +GA, Gabon\ +GM, Gambia\ +GE, Georgia\ +DE, Germany\ +GH, Ghana\ +GI, Gibraltar\ +GR, Greece\ +GL, Greenland\ +GD, Grenada\ +GP, Guadeloupe\ +GU, Guam\ +GT, Guatemala\ +GG, Guernsey\ +GN, Guinea\ +GW, Guinea-Bissau\ +GY, Guyana\ +HT, Haiti\ +HM, Heard Island and McDonald Islands\ +VA, Holy See\ +HN, Honduras\ +HK, Hong Kong\ +HU, Hungary\ +IS, Iceland\ +IN, India\ +ID, Indonesia\ +IR, Iran\ +IQ, Iraq\ +IE, Ireland\ +IM, Isle of Man\ +IL, Israel\ +IT, Italy\ +JM, Jamaica\ +JP, Japan\ +JE, Jersey\ +JO, Jordan\ +KZ, Kazakhstan\ +KE, Kenya\ +KI, Kiribati\ +KW, Kuwait\ +KG, Kyrgyzstan\ +LA, Laos\ +LV, Latvia\ +LB, Lebanon\ +LS, Lesotho\ +LR, Liberia\ +LY, Libya\ +LI, Liechtenstein\ +LT, Lithuania\ +LU, Luxembourg\ +MO, Macao\ +MG, Madagascar\ +MW, Malawi\ +MY, Malaysia\ +MV, Maldives\ +ML, Mali\ +MT, Malta\ +MH, Marshall Islands\ +MQ, Martinique\ +MR, Mauritania\ +MU, Mauritius\ +YT, Mayotte\ +MX, Mexico\ +FM, Micronesia\ +MD, Moldova\ +MC, Monaco\ +MN, Mongolia\ +ME, Montenegro\ +MS, Montserrat\ +MA, Morocco\ +MZ, Mozambique\ +MM, Myanmar\ +NA, Namibia\ +NR, Nauru\ +NP, Nepal\ +NL, Netherlands\ +NC, New Caledonia\ +NZ, New Zealand\ +NI, Nicaragua\ +NE, Niger\ +NG, Nigeria\ +NU, Niue\ +NF, Norfolk Island\ +KP, North Korea\ +MK, North Macedonia\ +MP, Northern Mariana Islands\ +NO, Norway\ +OM, Oman\ +PK, Pakistan\ +PW, Palau\ +PS, Palestine\ +PA, Panama\ +PG, Papua New Guinea\ +PY, Paraguay\ +PE, Peru\ +PH, Philippines\ +PN, Pitcairn\ +PL, Poland\ +PT, Portugal\ +PR, Puerto Rico\ +QA, Qatar\ +RE, R\'e9union\ +RO, Romania\ +RU, Russia\ +RW, Rwanda\ +BL, Saint Barth\'e9lemy\ +SH, Saint Helena, Ascension and Tristan da Cunha\ +KN, Saint Kitts and Nevis\ +LC, Saint Lucia\ +MF, Saint Martin (French part)\ +PM, Saint Pierre and Miquelon\ +VC, Saint Vincent and the Grenadines\ +WS, Samoa\ +SM, San Marino\ +ST, Sao Tome and Principe\ +SA, Saudi Arabia\ +SN, Senegal\ +RS, Serbia\ +SC, Seychelles\ +SL, Sierra Leone\ +SG, Singapore\ +SX, Sint Maarten\ +SK, Slovakia\ +SI, Slovenia\ +SB, Solomon Islands\ +SO, Somalia\ +ZA, South Africa\ +GS, South Georgia and the South Sandwich Islands\ +KR, South Korea\ +SS, South Sudan\ +ES, Spain\ +LK, Sri Lanka\ +SD, Sudan\ +SR, Suriname\ +SJ, Svalbard and Jan Mayen\ +SE, Sweden\ +CH, Switzerland\ +SY, Syria\ +TW, Taiwan\ +TJ, Tajikistan\ +TZ, Tanzania\ +TH, Thailand\ +TL, Timor-Leste\ +TG, Togo\ +TK, Tokelau\ +TO, Tonga\ +TT, Trinidad and Tobago\ +TN, Tunisia\ +TR, T\'fcrkiye\ +TM, Turkmenistan\ +TC, Turks and Caicos Islands\ +TV, Tuvalu\ +UG, Uganda\ +UA, Ukraine\ +AE, United Arab Emirates\ +GB, United Kingdom\ +UM, United States Minor Outlying Islands\ +US, United States of America\ +UY, Uruguay\ +UZ, Uzbekistan\ +VU, Vanuatu\ +VE, Venezuela\ +VN, Vietnam\ +VG, Virgin Islands (British)\ +VI, Virgin Islands (U.S.)\ +WF, Wallis and Futuna\ +EH, Western Sahara\ +YE, Yemen\ +ZM, Zambia\ +ZW, Zimbabwe} \ No newline at end of file diff --git a/static/images/logos/users/appicon_QK3TERt.png b/static/images/logos/users/appicon_QK3TERt.png new file mode 100644 index 00000000..b8addbe8 Binary files /dev/null and b/static/images/logos/users/appicon_QK3TERt.png differ diff --git a/static/images/logos/vendors/Alamjdouie-Hyundai-logo_I8WTQve_ONSH0t8.png b/static/images/logos/vendors/Alamjdouie-Hyundai-logo_I8WTQve_ONSH0t8.png new file mode 100644 index 00000000..53525aa2 Binary files /dev/null and b/static/images/logos/vendors/Alamjdouie-Hyundai-logo_I8WTQve_ONSH0t8.png differ diff --git a/static/images/logos/vendors/Aljumaih-Automotive_9fvlGI2_n5ZjxnE.png b/static/images/logos/vendors/Aljumaih-Automotive_9fvlGI2_n5ZjxnE.png new file mode 100644 index 00000000..a1707fbc Binary files /dev/null and b/static/images/logos/vendors/Aljumaih-Automotive_9fvlGI2_n5ZjxnE.png differ