diff --git a/apply_initial_migrations.sh b/apply_initial_migrations.sh index e6e88a0f..e875517f 100755 --- a/apply_initial_migrations.sh +++ b/apply_initial_migrations.sh @@ -1,10 +1,12 @@ #!/bin/sh echo "Delete Old Migrations" find ./inventory -type f -iname "00*.py" -delete +find ./haikalbot -type f -iname "00*.py" -delete echo "Delete Old Cache" find ./car_inventory -type d -iname "__pycache__"|xargs rm -rf find ./inventory -type d -iname "__pycache__"|xargs rm -rf +find ./haikalbot -type d -iname "__pycache__"|xargs rm -rf echo "Apply Base Migrate" diff --git a/haikalbot/migrations/0001_initial.py b/haikalbot/migrations/0001_initial.py deleted file mode 100644 index 61850a1a..00000000 --- a/haikalbot/migrations/0001_initial.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.2.1 on 2025-05-25 23:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='ChatLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_message', models.TextField()), - ('chatbot_response', models.TextField()), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ], - ), - ] diff --git a/haikalbot/migrations/0002_initial.py b/haikalbot/migrations/0002_initial.py deleted file mode 100644 index d8b26a8e..00000000 --- a/haikalbot/migrations/0002_initial.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.2.1 on 2025-05-25 23:01 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('haikalbot', '0001_initial'), - ('inventory', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='chatlog', - name='dealer', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chatlogs', to='inventory.dealer'), - ), - ] diff --git a/haikalbot/migrations/0003_analysiscache.py b/haikalbot/migrations/0003_analysiscache.py deleted file mode 100644 index 5d94804a..00000000 --- a/haikalbot/migrations/0003_analysiscache.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.2.1 on 2025-05-26 00:28 - -import django.db.models.deletion -import django.utils.timezone -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('haikalbot', '0002_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='AnalysisCache', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('prompt_hash', models.CharField(db_index=True, max_length=64)), - ('dealer_id', models.IntegerField(blank=True, db_index=True, null=True)), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('expires_at', models.DateTimeField()), - ('result', models.JSONField()), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'indexes': [models.Index(fields=['prompt_hash', 'dealer_id'], name='haikalbot_a_prompt__b98e1e_idx'), models.Index(fields=['expires_at'], name='haikalbot_a_expires_e790cd_idx')], - }, - ), - ] diff --git a/haikalbot/migrations/0004_alter_analysiscache_options_alter_chatlog_options_and_more.py b/haikalbot/migrations/0004_alter_analysiscache_options_alter_chatlog_options_and_more.py deleted file mode 100644 index c61771dd..00000000 --- a/haikalbot/migrations/0004_alter_analysiscache_options_alter_chatlog_options_and_more.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 5.2.1 on 2025-05-26 08:17 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('haikalbot', '0003_analysiscache'), - ('inventory', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='analysiscache', - options={'verbose_name_plural': 'Analysis caches'}, - ), - migrations.AlterModelOptions( - name='chatlog', - options={'ordering': ['-timestamp']}, - ), - migrations.AlterField( - model_name='analysiscache', - name='expires_at', - field=models.DateTimeField(db_index=True), - ), - migrations.AlterField( - model_name='chatlog', - name='timestamp', - field=models.DateTimeField(auto_now_add=True, db_index=True), - ), - migrations.AddIndex( - model_name='chatlog', - index=models.Index(fields=['dealer', 'timestamp'], name='haikalbot_c_dealer__6f8d63_idx'), - ), - ] diff --git a/inventory/migrations/0001_initial.py b/inventory/migrations/0001_initial.py deleted file mode 100644 index cf6476e4..00000000 --- a/inventory/migrations/0001_initial.py +++ /dev/null @@ -1,845 +0,0 @@ -# Generated by Django 5.2.1 on 2025-05-25 23:01 - -import datetime -import django.core.validators -import django.db.models.deletion -import django.utils.timezone -import inventory.mixins -import inventory.models -import phonenumber_field.modelfields -import uuid -from decimal import Decimal -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('appointment', '0001_initial'), - ('auth', '0012_alter_user_first_name_max_length'), - ('contenttypes', '0002_remove_content_type_name'), - ('django_ledger', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='CarEquipment', - fields=[ - ('id_car_equipment', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('year_begin', models.IntegerField(blank=True, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ], - options={ - 'verbose_name': 'Equipment', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarMake', - fields=[ - ('id_car_make', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('logo', models.ImageField(blank=True, null=True, upload_to='car_make', verbose_name='logo')), - ('is_sa_import', models.BooleanField(default=False)), - ('car_type', models.SmallIntegerField(blank=True, choices=[(1, 'Car'), (2, 'Light Commercial'), (3, 'Heavy-Duty Tractors'), (4, 'Trailers'), (5, 'Medium Trucks'), (6, 'Buses'), (20, 'Motorcycles'), (21, 'Buggy'), (22, 'Moto ATV'), (23, 'Scooters'), (24, 'Karting'), (25, 'ATV'), (26, 'Snowmobiles')], null=True)), - ], - options={ - 'verbose_name': 'Make', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='ExteriorColors', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')), - ], - options={ - 'verbose_name': 'Exterior Colors', - 'verbose_name_plural': 'Exterior Colors', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='InteriorColors', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')), - ], - options={ - 'verbose_name': 'Interior Colors', - 'verbose_name_plural': 'Interior Colors', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='Payment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')), - ('payment_method', models.CharField(choices=[('cash', 'cash'), ('credit', 'credit'), ('transfer', 'transfer'), ('debit', 'debit'), ('sadad', 'SADAD')], max_length=50, verbose_name='method')), - ('reference_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='reference number')), - ('payment_date', models.DateField(auto_now_add=True, verbose_name='date')), - ], - options={ - 'verbose_name': 'payment', - 'verbose_name_plural': 'payments', - }, - ), - migrations.CreateModel( - name='VatRate', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rate', models.DecimalField(decimal_places=2, default=Decimal('0.15'), max_digits=5)), - ('is_active', models.BooleanField(default=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ], - ), - migrations.CreateModel( - name='AdditionalServices', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('description', models.TextField(verbose_name='Description')), - ('price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Price')), - ('taxable', models.BooleanField(default=False, verbose_name='taxable')), - ('uom', models.CharField(choices=[('EA', 'Each'), ('PR', 'Pair'), ('SET', 'Set'), ('GAL', 'Gallon'), ('L', 'Liter'), ('M', 'Meter'), ('KG', 'Kilogram'), ('HR', 'Hour'), ('BX', 'Box'), ('RL', 'Roll'), ('PKG', 'Package'), ('DZ', 'Dozen'), ('SQ_M', 'Square Meter'), ('PC', 'Piece'), ('BDL', 'Bundle')], max_length=10, verbose_name='Unit of Measurement')), - ('item', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_ledger.itemmodel', verbose_name='Item')), - ], - options={ - 'verbose_name': 'Additional Services', - 'verbose_name_plural': 'Additional Services', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='Car', - fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='Primary Key')), - ('slug', models.SlugField(blank=True, help_text='Slug for the object. If not provided, it will be generated automatically.', null=True, unique=True, verbose_name='Slug')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), - ('vin', models.CharField(max_length=17, unique=True, verbose_name='VIN')), - ('year', models.IntegerField(verbose_name='Year')), - ('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved'), ('transfer', 'Transfer')], default='available', max_length=10, verbose_name='Status')), - ('stock_type', models.CharField(choices=[('new', 'New'), ('used', 'Used')], default='new', max_length=10, verbose_name='Stock Type')), - ('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')), - ('mileage', models.IntegerField(blank=True, null=True, verbose_name='Mileage')), - ('receiving_date', models.DateTimeField(verbose_name='Receiving Date')), - ('hash', models.CharField(blank=True, max_length=64, null=True, verbose_name='Hash')), - ('item_model', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='django_ledger.itemmodel', verbose_name='Item Model')), - ('id_car_make', models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')), - ], - options={ - 'verbose_name': 'Car', - 'verbose_name_plural': 'Cars', - }, - ), - migrations.CreateModel( - name='CarFinance', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')), - ('selling_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Selling Price')), - ('discount_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount')), - ('additional_services', models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')), - ], - options={ - 'verbose_name': 'Car Financial Details', - 'verbose_name_plural': 'Car Financial Details', - }, - ), - migrations.CreateModel( - name='CarModel', - fields=[ - ('id_car_model', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')), - ], - options={ - 'verbose_name': 'Model', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.AddField( - model_name='car', - name='id_car_model', - field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'), - ), - migrations.CreateModel( - name='CarOption', - fields=[ - ('id_car_option', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')), - ], - options={ - 'verbose_name': 'Option', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarOptionValue', - fields=[ - ('id_car_option_value', models.AutoField(primary_key=True, serialize=False)), - ('value', models.CharField(max_length=500)), - ('unit', models.CharField(blank=True, max_length=255, null=True)), - ('is_base', models.IntegerField()), - ('id_car_equipment', models.ForeignKey(db_column='id_car_equipment', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carequipment')), - ('id_car_option', models.ForeignKey(db_column='id_car_option', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')), - ], - options={ - 'verbose_name': 'Option Value', - }, - ), - migrations.CreateModel( - name='CarRegistration', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('plate_number', models.IntegerField(verbose_name='Plate Number')), - ('text1', models.CharField(max_length=1, verbose_name='Text 1')), - ('text2', models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 2')), - ('text3', models.CharField(blank=True, max_length=1, null=True, verbose_name='Text 3')), - ('registration_date', models.DateTimeField(verbose_name='Registration Date')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')), - ], - options={ - 'verbose_name': 'Registration', - 'verbose_name_plural': 'Registrations', - }, - ), - migrations.CreateModel( - name='CarSerie', - fields=[ - ('id_car_serie', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('year_begin', models.IntegerField(blank=True, null=True)), - ('year_end', models.IntegerField(blank=True, null=True)), - ('generation_name', models.CharField(blank=True, max_length=255, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')), - ], - options={ - 'verbose_name': 'Series', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.AddField( - model_name='car', - name='id_car_serie', - field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'), - ), - migrations.CreateModel( - name='CarSpecification', - fields=[ - ('id_car_specification', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(max_length=255)), - ('arabic_name', models.CharField(max_length=255)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')), - ], - options={ - 'verbose_name': 'Specification', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarTrim', - fields=[ - ('id_car_trim', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=255, null=True)), - ('arabic_name', models.CharField(blank=True, max_length=255, null=True)), - ('start_production_year', models.IntegerField(blank=True, null=True)), - ('end_production_year', models.IntegerField(blank=True, null=True)), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')), - ], - options={ - 'verbose_name': 'Trim', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='CarSpecificationValue', - fields=[ - ('id_car_specification_value', models.AutoField(primary_key=True, serialize=False)), - ('value', models.CharField(max_length=500)), - ('unit', models.CharField(blank=True, max_length=255, null=True)), - ('id_car_specification', models.ForeignKey(db_column='id_car_specification', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')), - ('id_car_trim', models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim')), - ], - options={ - 'verbose_name': 'Specification Value', - }, - ), - migrations.AddField( - model_name='carequipment', - name='id_car_trim', - field=models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim'), - ), - migrations.AddField( - model_name='car', - name='id_car_trim', - field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'), - ), - migrations.CreateModel( - name='CustomCard', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')), - ('custom_date', models.DateField(verbose_name='Custom Date')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')), - ], - options={ - 'verbose_name': 'Custom Card', - 'verbose_name_plural': 'Custom Cards', - }, - ), - migrations.CreateModel( - name='Dealer', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('crn', models.CharField(blank=True, max_length=10, null=True, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(blank=True, max_length=15, null=True, verbose_name='VAT Registration Number')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('name', models.CharField(max_length=255, verbose_name='English Name')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('logo', models.ImageField(blank=True, null=True, upload_to='logos/users', verbose_name='Logo')), - ('joined_at', models.DateTimeField(auto_now_add=True, verbose_name='Joined At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True)), - ('entity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.entitymodel')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Dealer', - 'verbose_name_plural': 'Dealers', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - managers=[ - ('objects', inventory.models.DealerUserManager()), - ], - ), - migrations.CreateModel( - name='CustomGroup', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='Group')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='groups', to='inventory.dealer')), - ], - ), - migrations.CreateModel( - name='Customer', - 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')], default='na', max_length=10, 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')), - ('gender', models.CharField(choices=[('m', 'Male'), ('f', 'Female')], max_length=1, verbose_name='Gender')), - ('dob', models.DateField(blank=True, null=True, verbose_name='Date of Birth')), - ('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')), - ('national_id', models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='National ID')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', unique=True, verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('image', models.ImageField(blank=True, null=True, upload_to='customers/', verbose_name='Image')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True)), - ('customer_model', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.customermodel')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='customer_profile', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Customer', - 'verbose_name_plural': 'Customers', - }, - ), - migrations.CreateModel( - name='CarTransfer', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('transfer_date', models.DateTimeField(auto_now_add=True, verbose_name='Transfer Date')), - ('quantity', models.IntegerField(default=1, verbose_name='Quantity')), - ('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')), - ('status', models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accepted', 'Accepted'), ('success', 'Success'), ('reject', 'Reject'), ('cancelled', 'Cancelled')])), - ('is_approved', models.BooleanField(default=False)), - ('active', models.BooleanField(default=True)), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), - ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfer_logs', to='inventory.car', verbose_name='Car')), - ('from_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_out', to='inventory.dealer', verbose_name='From Dealer')), - ('to_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_in', to='inventory.dealer', verbose_name='To Dealer')), - ], - options={ - 'verbose_name': 'Car Transfer Log', - 'verbose_name_plural': 'Car Transfer Logs', - }, - ), - migrations.CreateModel( - name='CarLocation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('description', models.TextField(blank=True, help_text='Optional description about the showroom placement.', null=True, verbose_name='Description')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Updated')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='location', to='inventory.car', verbose_name='Car')), - ('owner', models.ForeignKey(help_text='Dealer who owns the car.', on_delete=django.db.models.deletion.CASCADE, related_name='owned_cars', to='inventory.dealer', verbose_name='Owner')), - ('showroom', models.ForeignKey(help_text='Dealer where the car is displayed (can be the owner).', on_delete=django.db.models.deletion.CASCADE, related_name='showroom_cars', to='inventory.dealer', verbose_name='Showroom')), - ], - options={ - 'verbose_name': 'Car Location', - 'verbose_name_plural': 'Car Locations', - }, - ), - migrations.AddField( - model_name='car', - name='dealer', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'), - ), - migrations.AddField( - model_name='additionalservices', - name='dealer', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'), - ), - 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'), ('meeting', 'Meeting'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('negotiation', 'Negotiation'), ('follow_up', 'Follow Up'), ('won', 'Won'), ('lost', 'Lost'), ('closed', 'Closed'), ('converted', 'Converted'), ('transfer', 'Transfer'), ('add_car', 'Add Car'), ('sale_car', 'Sale Car'), ('reserve_car', 'Reserve Car'), ('transfer_car', 'Transfer 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.DO_NOTHING, to='contenttypes.contenttype')), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created_by', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Activity', - 'verbose_name_plural': 'Activities', - }, - ), - migrations.CreateModel( - name='DealerSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('additional_info', models.JSONField(blank=True, default=dict, null=True)), - ('bill_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_cash', to='django_ledger.accountmodel')), - ('bill_prepaid_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_prepaid', to='django_ledger.accountmodel')), - ('bill_unearned_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bill_unearned', to='django_ledger.accountmodel')), - ('dealer', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='settings', to='inventory.dealer')), - ('invoice_cash_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_cash', to='django_ledger.accountmodel')), - ('invoice_prepaid_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_prepaid', to='django_ledger.accountmodel')), - ('invoice_unearned_account', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='invoice_unearned', to='django_ledger.accountmodel')), - ], - ), - migrations.CreateModel( - name='Email', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.UUIDField()), - ('from_email', models.TextField(blank=True, null=True, verbose_name='From Email')), - ('to_email', models.TextField(blank=True, null=True, verbose_name='To Email')), - ('subject', models.TextField(blank=True, null=True, verbose_name='Subject')), - ('message', models.TextField(blank=True, null=True, verbose_name='Message')), - ('status', models.CharField(choices=[('SENT', 'Sent'), ('FAILED', 'Failed'), ('DELIVERED', 'Delivered'), ('OPEN', 'Open'), ('DRAFT', 'Draft')], default='OPEN', max_length=20, verbose_name='Status')), - ('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='emails_created', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Email', - 'verbose_name_plural': 'Emails', - }, - ), - migrations.CreateModel( - name='Notes', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.UUIDField()), - ('note', models.TextField(verbose_name='Note')), - ('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='notes_created', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Note', - 'verbose_name_plural': 'Notes', - }, - ), - migrations.CreateModel( - name='Notification', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('message', models.CharField(max_length=255, verbose_name='Message')), - ('is_read', models.BooleanField(default=False, verbose_name='Is Read')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Notification', - 'verbose_name_plural': 'Notifications', - 'ordering': ['-created'], - }, - ), - migrations.CreateModel( - name='Organization', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('crn', models.CharField(max_length=15, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(max_length=15, verbose_name='VAT Registration Number')), - ('email', models.EmailField(max_length=254, verbose_name='Email')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('logo', models.ImageField(blank=True, null=True, upload_to='logos', verbose_name='Logo')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True)), - ('customer_model', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.customermodel')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organizations', to='inventory.dealer')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='organization_profile', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Organization', - 'verbose_name_plural': 'Organizations', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='Lead', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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(max_length=254, verbose_name='Email')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('lead_type', models.CharField(choices=[('customer', 'Customer'), ('organization', 'Organization')], default='customer', max_length=50, verbose_name='Lead Type')), - ('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')), - ('crn', models.CharField(blank=True, max_length=10, null=True, unique=True, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(blank=True, max_length=15, null=True, unique=True, verbose_name='VAT Registration Number')), - ('address', models.CharField(max_length=50, verbose_name='address')), - ('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='medium', max_length=10, verbose_name='Priority')), - ('status', models.CharField(choices=[('new', 'New'), ('follow_up', 'Follow-up'), ('negotiation', 'Negotiation'), ('won', 'Won'), ('lost', 'Lost'), ('closed', 'Closed')], db_index=True, default='new', max_length=50, verbose_name='Status')), - ('next_action', models.CharField(blank=True, max_length=255, null=True, verbose_name='Next Action')), - ('next_action_date', models.DateTimeField(blank=True, null=True, verbose_name='Next Action Date')), - ('is_converted', models.BooleanField(default=False)), - ('converted_at', models.DateTimeField(blank=True, null=True)), - ('salary', models.PositiveIntegerField(blank=True, null=True, verbose_name='Salary')), - ('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, null=True, unique=True)), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='customer_leads', to='inventory.customer')), - ('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')), - ('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_leads', to='inventory.organization')), - ], - options={ - 'verbose_name': 'Lead', - 'verbose_name_plural': 'Leads', - }, - ), - migrations.CreateModel( - name='Refund', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')), - ('reason', models.TextField(blank=True, verbose_name='reason')), - ('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')), - ('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')), - ], - options={ - 'verbose_name': 'refund', - 'verbose_name_plural': 'refunds', - }, - ), - migrations.CreateModel( - name='Representative', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('id_number', models.CharField(max_length=10, unique=True, verbose_name='ID Number')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('email', models.EmailField(max_length=255, verbose_name='Email Address')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')), - ('organization', models.ManyToManyField(related_name='representatives', to='inventory.organization')), - ], - options={ - 'verbose_name': 'Representative', - 'verbose_name_plural': 'Representatives', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.CreateModel( - name='SaleOrder', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('payment_method', models.CharField(choices=[('cash', 'Cash'), ('finance', 'Finance'), ('lease', 'Lease'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer'), ('sadad', 'SADAD')], max_length=20)), - ('comments', models.TextField(blank=True, null=True)), - ('formatted_order_id', models.CharField(editable=False, max_length=10, unique=True)), - ('created', models.DateTimeField(auto_now_add=True)), - ('estimate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.estimatemodel', verbose_name='Estimate')), - ('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.invoicemodel', verbose_name='Invoice')), - ], - options={ - 'ordering': ['-created'], - }, - ), - migrations.CreateModel( - name='Schedule', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('purpose', models.CharField(choices=[('product_demo', 'Product Demo'), ('follow_up_call', 'Follow-Up Call'), ('contract_discussion', 'Contract Discussion'), ('sales_meeting', 'Sales Meeting'), ('support_call', 'Support Call'), ('other', 'Other')], max_length=200)), - ('scheduled_at', models.DateTimeField()), - ('scheduled_type', models.CharField(choices=[('call', 'Call'), ('meeting', 'Meeting'), ('email', 'Email')], default='Call', max_length=200)), - ('duration', models.DurationField(default=datetime.timedelta(seconds=300))), - ('notes', models.TextField(blank=True, null=True)), - ('status', models.CharField(choices=[('scheduled', 'Scheduled'), ('completed', 'Completed'), ('canceled', 'Canceled')], default='Scheduled', max_length=200)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to='django_ledger.customermodel')), - ('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to='inventory.lead')), - ('scheduled_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['-scheduled_at'], - }, - ), - migrations.CreateModel( - name='Staff', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Name')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('staff_type', models.CharField(choices=[('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales')], max_length=255, verbose_name='Staff Type')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, editable=False, max_length=255, null=True, unique=True)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='inventory.dealer')), - ('staff_member', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='appointment.staffmember')), - ], - options={ - 'verbose_name': 'Staff', - 'verbose_name_plural': 'Staff', - 'permissions': [], - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - managers=[ - ('objects', inventory.models.StaffUserManager()), - ], - ), - migrations.CreateModel( - name='Opportunity', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('stage', models.CharField(choices=[('discovery', 'Discovery'), ('proposal', 'Proposal'), ('negotiation', 'Negotiation'), ('closed_won', 'Closed Won'), ('closed_lost', 'Closed Lost')], max_length=20, verbose_name='Stage')), - ('probability', models.PositiveIntegerField(validators=[inventory.models.validate_probability])), - ('expected_revenue', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Expected Revenue')), - ('closing_date', models.DateField(blank=True, null=True, verbose_name='Closing Date')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('slug', models.SlugField(blank=True, help_text='Unique slug for the opportunity.', null=True, unique=True, verbose_name='Slug')), - ('car', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.car', verbose_name='Car')), - ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.customer')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.dealer')), - ('estimate', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='opportunity', to='django_ledger.estimatemodel')), - ('lead', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunity', to='inventory.lead')), - ('staff', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owner', to='inventory.staff', verbose_name='Owner')), - ], - options={ - 'verbose_name': 'Opportunity', - 'verbose_name_plural': 'Opportunities', - }, - ), - 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'), ('follow_up', 'Follow-up'), ('negotiation', 'Negotiation'), ('won', 'Won'), ('lost', 'Lost'), ('closed', 'Closed')], max_length=50, verbose_name='Old Status')), - ('new_status', models.CharField(choices=[('new', 'New'), ('follow_up', 'Follow-up'), ('negotiation', 'Negotiation'), ('won', 'Won'), ('lost', 'Lost'), ('closed', 'Closed')], max_length=50, verbose_name='New Status')), - ('changed_at', models.DateTimeField(auto_now_add=True, verbose_name='Changed At')), - ('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='inventory.lead')), - ('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='status_changes', to='inventory.staff')), - ], - options={ - 'verbose_name': 'Lead Status History', - 'verbose_name_plural': 'Lead Status Histories', - }, - ), - migrations.AddField( - model_name='lead', - name='staff', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned', to='inventory.staff', verbose_name='Assigned'), - ), - migrations.CreateModel( - name='Tasks', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.UUIDField()), - ('title', models.CharField(max_length=255, verbose_name='Title')), - ('description', models.TextField(blank=True, null=True, verbose_name='Description')), - ('due_date', models.DateField(verbose_name='Due Date')), - ('completed', models.BooleanField(default=False, verbose_name='Completed')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), - ('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks_assigned', to=settings.AUTH_USER_MODEL)), - ('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='tasks_created', to=settings.AUTH_USER_MODEL)), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='inventory.dealer')), - ], - options={ - 'verbose_name': 'Task', - 'verbose_name_plural': 'Tasks', - }, - ), - migrations.CreateModel( - name='UserActivityLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('action', models.TextField()), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'User Activity Log', - 'verbose_name_plural': 'User Activity Logs', - 'ordering': ['-timestamp'], - }, - ), - migrations.CreateModel( - name='Vendor', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('crn', models.CharField(max_length=10, unique=True, verbose_name='Commercial Registration Number')), - ('vrn', models.CharField(max_length=15, unique=True, verbose_name='VAT Registration Number')), - ('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), - ('name', models.CharField(max_length=255, verbose_name='English Name')), - ('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')), - ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), - ('email', models.EmailField(max_length=255, verbose_name='Email Address')), - ('address', models.CharField(max_length=200, verbose_name='Address')), - ('logo', models.ImageField(blank=True, null=True, upload_to='logos/vendors', verbose_name='Logo')), - ('active', models.BooleanField(default=True, verbose_name='Active')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), - ('slug', models.SlugField(blank=True, max_length=255, null=True, unique=True, verbose_name='Slug')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')), - ('vendor_model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='django_ledger.vendormodel', verbose_name='Vendor Model')), - ], - options={ - 'verbose_name': 'Vendor', - 'verbose_name_plural': 'Vendors', - }, - bases=(models.Model, inventory.mixins.LocalizedNameMixin), - ), - migrations.AddField( - model_name='car', - name='vendor', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.vendor', verbose_name='Vendor'), - ), - migrations.CreateModel( - name='CarReservation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reserved_at', models.DateTimeField(auto_now_add=True, verbose_name='Reserved At')), - ('reserved_until', models.DateTimeField(verbose_name='Reserved Until')), - ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='inventory.car', verbose_name='Car')), - ('reserved_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to=settings.AUTH_USER_MODEL, verbose_name='Reserved By')), - ], - options={ - 'verbose_name': 'Car Reservation', - 'verbose_name_plural': 'Car Reservations', - 'ordering': ['-reserved_at'], - 'unique_together': {('car', 'reserved_until')}, - }, - ), - migrations.CreateModel( - name='DealersMake', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('added_at', models.DateTimeField(auto_now_add=True)), - ('car_make', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='car_dealers', to='inventory.carmake')), - ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dealer_makes', to='inventory.dealer')), - ], - options={ - 'unique_together': {('dealer', 'car_make')}, - }, - ), - migrations.CreateModel( - name='CarColors', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')), - ('exterior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.exteriorcolors')), - ('interior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.interiorcolors')), - ], - options={ - 'verbose_name': 'Color', - 'verbose_name_plural': 'Colors', - 'unique_together': {('car', 'exterior', 'interior')}, - }, - ), - migrations.CreateModel( - name='PaymentHistory', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_data', models.JSONField(blank=True, null=True)), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0.01)])), - ('currency', models.CharField(default='SAR', max_length=3)), - ('payment_date', models.DateTimeField(default=django.utils.timezone.now)), - ('status', models.CharField(choices=[('initiated', 'initiated'), ('pending', 'Pending'), ('completed', 'Completed'), ('paid', 'Paid'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], default='pending', max_length=10)), - ('payment_method', models.CharField(choices=[('credit_card', 'Credit Card'), ('debit_card', 'Debit Card'), ('paypal', 'PayPal'), ('bank_transfer', 'Bank Transfer'), ('crypto', 'Cryptocurrency'), ('other', 'Other')], max_length=20)), - ('transaction_id', models.CharField(blank=True, max_length=100, null=True, unique=True)), - ('invoice_number', models.CharField(blank=True, max_length=50, null=True)), - ('order_reference', models.CharField(blank=True, max_length=100, null=True)), - ('gateway_response', models.JSONField(blank=True, null=True)), - ('gateway_name', models.CharField(blank=True, max_length=50, null=True)), - ('description', models.TextField(blank=True, null=True)), - ('is_recurring', models.BooleanField(default=False)), - ('billing_email', models.EmailField(blank=True, max_length=254, null=True)), - ('billing_address', models.TextField(blank=True, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Payment History', - 'verbose_name_plural': 'Payment Histories', - 'ordering': ['-payment_date'], - 'indexes': [models.Index(fields=['transaction_id'], name='inventory_p_transac_9469f3_idx'), models.Index(fields=['user'], name='inventory_p_user_id_c31626_idx'), models.Index(fields=['status'], name='inventory_p_status_abcb77_idx'), models.Index(fields=['payment_date'], name='inventory_p_payment_b3068c_idx')], - }, - ), - ] diff --git a/inventory/migrations/0002_carfinance_is_sold.py b/inventory/migrations/0002_carfinance_is_sold.py deleted file mode 100644 index 63ced651..00000000 --- a/inventory/migrations/0002_carfinance_is_sold.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-25 14:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='carfinance', - name='is_sold', - field=models.BooleanField(default=False), - ), - ] diff --git a/inventory/migrations/0003_alter_saleorder_options_and_more.py b/inventory/migrations/0003_alter_saleorder_options_and_more.py deleted file mode 100644 index 1e914107..00000000 --- a/inventory/migrations/0003_alter_saleorder_options_and_more.py +++ /dev/null @@ -1,125 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-27 14:41 - -import django.db.models.deletion -import django.utils.timezone -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0002_carfinance_is_sold'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterModelOptions( - name='saleorder', - options={'ordering': ['-order_date'], 'verbose_name': 'Sales Order', 'verbose_name_plural': 'Sales Orders'}, - ), - migrations.RenameField( - model_name='saleorder', - old_name='created', - new_name='created_at', - ), - migrations.AddField( - model_name='saleorder', - name='actual_delivery_date', - field=models.DateTimeField(blank=True, help_text='The actual date and time the vehicle was delivered.', null=True), - ), - migrations.AddField( - model_name='saleorder', - name='agreed_price', - field=models.DecimalField(decimal_places=2, default=0, help_text='The final agreed-upon selling price of the vehicle.', max_digits=12), - preserve_default=False, - ), - migrations.AddField( - model_name='saleorder', - name='cancellation_reason', - field=models.TextField(blank=True, help_text='Reason for cancellation, if applicable.', null=True), - ), - migrations.AddField( - model_name='saleorder', - name='cancelled_date', - field=models.DateTimeField(blank=True, help_text='The date and time the order was cancelled, if applicable.', null=True), - ), - migrations.AddField( - model_name='saleorder', - name='car', - field=models.ForeignKey(default=1, help_text='The specific vehicle (VIN) being sold.', on_delete=django.db.models.deletion.PROTECT, related_name='sales_orders', to='inventory.car'), - preserve_default=False, - ), - migrations.AddField( - model_name='saleorder', - name='created_by', - field=models.ForeignKey(help_text='The user who created this sales order.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_sales_orders', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='saleorder', - name='customer', - field=models.ForeignKey(default=1, help_text='The customer making the purchase.', on_delete=django.db.models.deletion.PROTECT, related_name='sales_orders', to='inventory.customer'), - preserve_default=False, - ), - migrations.AddField( - model_name='saleorder', - name='down_payment_amount', - field=models.DecimalField(decimal_places=2, default=0.0, help_text='The initial payment made by the customer.', max_digits=12), - ), - migrations.AddField( - model_name='saleorder', - name='expected_delivery_date', - field=models.DateField(blank=True, help_text='The planned date for vehicle delivery.', null=True), - ), - migrations.AddField( - model_name='saleorder', - name='last_modified_by', - field=models.ForeignKey(help_text='The user who last modified this sales order.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modified_sales_orders', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='saleorder', - name='loan_amount', - field=models.DecimalField(decimal_places=2, default=0.0, help_text='The amount financed by a bank or third-party lender.', max_digits=12), - ), - migrations.AddField( - model_name='saleorder', - name='opportunity', - field=models.OneToOneField(default=1, help_text='The associated sales opportunity for this order.', on_delete=django.db.models.deletion.CASCADE, related_name='sales_order', to='inventory.opportunity'), - preserve_default=False, - ), - migrations.AddField( - model_name='saleorder', - name='order_date', - field=models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time the sales order was created.'), - ), - migrations.AddField( - model_name='saleorder', - name='remaining_balance', - field=models.DecimalField(decimal_places=2, default=0.0, help_text='The remaining amount due from the customer or financing.', max_digits=12), - ), - migrations.AddField( - model_name='saleorder', - name='status', - field=models.CharField(choices=[('PENDING_APPROVAL', 'Pending Approval'), ('APPROVED', 'Approved'), ('IN_FINANCING', 'In Financing'), ('PARTIALLY_PAID', 'Partially Paid'), ('FULLY_PAID', 'Fully Paid'), ('PENDING_DELIVERY', 'Pending Delivery'), ('DELIVERED', 'Delivered'), ('CANCELLED', 'Cancelled')], default='PENDING_APPROVAL', help_text='Current status of the sales order.', max_length=20), - ), - migrations.AddField( - model_name='saleorder', - name='total_paid_amount', - field=models.DecimalField(decimal_places=2, default=0.0, help_text='Sum of down payment, trade-in value, and loan amount received so far.', max_digits=12), - ), - migrations.AddField( - model_name='saleorder', - name='trade_in_value', - field=models.DecimalField(decimal_places=2, default=0.0, help_text='The value of any vehicle traded in by the customer.', max_digits=12), - ), - migrations.AddField( - model_name='saleorder', - name='trade_in_vehicle', - field=models.ForeignKey(blank=True, help_text='The vehicle traded in by the customer, if any.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='traded_in_on_orders', to='inventory.car'), - ), - migrations.AddField( - model_name='saleorder', - name='updated_at', - field=models.DateTimeField(auto_now=True), - ), - ] diff --git a/inventory/migrations/0004_alter_saleorder_car.py b/inventory/migrations/0004_alter_saleorder_car.py deleted file mode 100644 index 1990da55..00000000 --- a/inventory/migrations/0004_alter_saleorder_car.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-27 14:43 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0003_alter_saleorder_options_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='saleorder', - name='car', - field=models.ForeignKey(blank=True, help_text='The specific vehicle (VIN) being sold.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sales_orders', to='inventory.car'), - ), - ] diff --git a/inventory/migrations/0005_alter_opportunity_stage.py b/inventory/migrations/0005_alter_opportunity_stage.py deleted file mode 100644 index ef95a90a..00000000 --- a/inventory/migrations/0005_alter_opportunity_stage.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-28 13:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0004_alter_saleorder_car'), - ] - - operations = [ - migrations.AlterField( - model_name='opportunity', - name='stage', - field=models.CharField(choices=[('qualification', 'Qualification'), ('test_drive', 'Test Drive'), ('quotation', 'Quotation'), ('negotiation', 'Negotiation'), ('financing', 'Financing'), ('closed_won', 'Closed Won'), ('closed_lost', 'Closed Lost'), ('on_hold', 'On Hold')], max_length=20, verbose_name='Stage'), - ), - ] diff --git a/inventory/migrations/0006_remove_opportunity_closing_date_and_more.py b/inventory/migrations/0006_remove_opportunity_closing_date_and_more.py deleted file mode 100644 index 01113f52..00000000 --- a/inventory/migrations/0006_remove_opportunity_closing_date_and_more.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-28 13:16 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0005_alter_opportunity_stage'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.RemoveField( - model_name='opportunity', - name='closing_date', - ), - migrations.AddField( - model_name='opportunity', - name='assigned_to', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_opportunities', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='opportunity', - name='expected_close_date', - field=models.DateField(blank=True, null=True), - ), - migrations.AddField( - model_name='opportunity', - name='loss_reason', - field=models.CharField(blank=True, max_length=255, null=True), - ), - migrations.AddField( - model_name='opportunity', - name='vehicle_of_interest_make', - field=models.CharField(blank=True, max_length=50, null=True), - ), - migrations.AddField( - model_name='opportunity', - name='vehicle_of_interest_model', - field=models.CharField(blank=True, max_length=100, null=True), - ), - ] diff --git a/inventory/migrations/0007_alter_lead_status_alter_leadstatushistory_new_status_and_more.py b/inventory/migrations/0007_alter_lead_status_alter_leadstatushistory_new_status_and_more.py deleted file mode 100644 index b7ec7515..00000000 --- a/inventory/migrations/0007_alter_lead_status_alter_leadstatushistory_new_status_and_more.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-28 13:36 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0006_remove_opportunity_closing_date_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='lead', - name='status', - field=models.CharField(choices=[('new', 'New'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('unqualified', 'Unqualified'), ('converted', 'Converted')], db_index=True, default='new', max_length=50, verbose_name='Status'), - ), - migrations.AlterField( - model_name='leadstatushistory', - name='new_status', - field=models.CharField(choices=[('new', 'New'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('unqualified', 'Unqualified'), ('converted', 'Converted')], max_length=50, verbose_name='New Status'), - ), - migrations.AlterField( - model_name='leadstatushistory', - name='old_status', - field=models.CharField(choices=[('new', 'New'), ('contacted', 'Contacted'), ('qualified', 'Qualified'), ('unqualified', 'Unqualified'), ('converted', 'Converted')], max_length=50, verbose_name='Old Status'), - ), - ] diff --git a/inventory/migrations/0008_remove_lead_address_remove_lead_crn_and_more.py b/inventory/migrations/0008_remove_lead_address_remove_lead_crn_and_more.py deleted file mode 100644 index 890d7f48..00000000 --- a/inventory/migrations/0008_remove_lead_address_remove_lead_crn_and_more.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-28 13:44 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0007_alter_lead_status_alter_leadstatushistory_new_status_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='lead', - name='address', - ), - migrations.RemoveField( - model_name='lead', - name='crn', - ), - migrations.RemoveField( - model_name='lead', - name='priority', - ), - migrations.RemoveField( - model_name='lead', - name='salary', - ), - migrations.RemoveField( - model_name='lead', - name='vrn', - ), - migrations.RemoveField( - model_name='lead', - name='year', - ), - ] diff --git a/inventory/migrations/0009_lead_address_opportunity_crn_opportunity_priority_and_more.py b/inventory/migrations/0009_lead_address_opportunity_crn_opportunity_priority_and_more.py deleted file mode 100644 index 2e0a4043..00000000 --- a/inventory/migrations/0009_lead_address_opportunity_crn_opportunity_priority_and_more.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-28 13:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0008_remove_lead_address_remove_lead_crn_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='lead', - name='address', - field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Address'), - ), - migrations.AddField( - model_name='opportunity', - name='crn', - field=models.CharField(blank=True, max_length=20, null=True, verbose_name='CRN'), - ), - migrations.AddField( - model_name='opportunity', - name='priority', - field=models.CharField(choices=[('high', 'High'), ('medium', 'Medium'), ('low', 'Low')], default='medium', max_length=20, verbose_name='Priority'), - ), - migrations.AddField( - model_name='opportunity', - name='salary', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Salary'), - ), - migrations.AddField( - model_name='opportunity', - name='vrn', - field=models.CharField(blank=True, max_length=20, null=True, verbose_name='VRN'), - ), - ] diff --git a/inventory/migrations/0010_alter_opportunity_assigned_to.py b/inventory/migrations/0010_alter_opportunity_assigned_to.py deleted file mode 100644 index c13f0d99..00000000 --- a/inventory/migrations/0010_alter_opportunity_assigned_to.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-29 15:22 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0009_lead_address_opportunity_crn_opportunity_priority_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='opportunity', - name='assigned_to', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_opportunities', to='inventory.staff'), - ), - ] diff --git a/inventory/migrations/0011_remove_opportunity_assigned_to.py b/inventory/migrations/0011_remove_opportunity_assigned_to.py deleted file mode 100644 index 866e59c1..00000000 --- a/inventory/migrations/0011_remove_opportunity_assigned_to.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-29 15:23 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0010_alter_opportunity_assigned_to'), - ] - - operations = [ - migrations.RemoveField( - model_name='opportunity', - name='assigned_to', - ), - ] diff --git a/inventory/migrations/0012_opportunity_organization.py b/inventory/migrations/0012_opportunity_organization.py deleted file mode 100644 index 98d415c9..00000000 --- a/inventory/migrations/0012_opportunity_organization.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-29 16:12 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0011_remove_opportunity_assigned_to'), - ] - - operations = [ - migrations.AddField( - model_name='opportunity', - name='organization', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.organization', verbose_name='Organization'), - ), - ] diff --git a/inventory/migrations/0013_opportunity_amount_and_more.py b/inventory/migrations/0013_opportunity_amount_and_more.py deleted file mode 100644 index c3f87230..00000000 --- a/inventory/migrations/0013_opportunity_amount_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-29 23:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0012_opportunity_organization'), - ] - - operations = [ - migrations.AddField( - model_name='opportunity', - name='amount', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Amount'), - ), - migrations.AlterField( - model_name='opportunity', - name='expected_revenue', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Expected Revenue'), - ), - ] diff --git a/inventory/migrations/0014_alter_opportunity_amount.py b/inventory/migrations/0014_alter_opportunity_amount.py deleted file mode 100644 index cabd9e18..00000000 --- a/inventory/migrations/0014_alter_opportunity_amount.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-29 23:02 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0013_opportunity_amount_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='opportunity', - name='amount', - field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Amount'), - preserve_default=False, - ), - ] diff --git a/inventory/migrations/__init__.py b/inventory/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/inventory/urls.py b/inventory/urls.py index 5557e101..729b763a 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -281,6 +281,7 @@ urlpatterns = [ path( "cars//add-color/", views.CarColorCreate.as_view(), name="add_color" ), + path('car/colors//update/', views.CarColorsUpdateView.as_view(), name='car_colors_update'), path( "cars//location/add/", views.CarLocationCreateView.as_view(), @@ -855,6 +856,9 @@ path( path('management/user_management/', views.user_management, name='user_management'), path('management///activate_account/', views.activate_account, name='activate_account'), path('management///permenant_delete_account/', views.permenant_delete_account, name='permenant_delete_account'), + path('management/audit_log_dashboard/', views.AuditLogDashboardView, name='audit_log_dashboard'), + + ######### # Purchase Order path('purchase_orders/', views.PurchaseOrderListView.as_view(), name='purchase_order_list'), diff --git a/inventory/views.py b/inventory/views.py index 2971ae31..ef34571a 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -31,7 +31,7 @@ from django.forms import HiddenInput, ValidationError from django.shortcuts import HttpResponse from django.db.models import Sum, F, Count -from django.core.paginator import Paginator +from django.core.paginator import Paginator,EmptyPage, PageNotAnInteger from django.contrib.auth.models import User from django.contrib.auth.models import Group from django.db.models import Value @@ -60,6 +60,7 @@ from django.views.generic import ( ArchiveIndexView, ) + # Django Ledger from django_ledger.io import roles from django_ledger.utils import accruable_net_summary @@ -172,6 +173,10 @@ from .utils import ( ) from .tasks import create_accounts_for_make, send_email +#djago easy audit log +from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent + + logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @@ -967,37 +972,50 @@ class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView): context["car"] = get_object_or_404(models.Car, slug=self.kwargs["slug"]) return context + + class CarColorsUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView): model = models.CarColors form_class = forms.CarColorsForm template_name = "inventory/add_colors.html" - success_message = _("Car finance details updated successfully") - permission_required = ["inventory.change_carfinance"] + success_message = _("Car Colors details updated successfully") + permission_required = ["inventory.change_car"] + + + def get_object(self, queryset=None): + """ + Retrieves the CarColors instance associated with the Car slug from the URL. + This ensures we are updating the colors for the correct car. + """ + + # Get the car_slug from the URL keywords arguments + slug = self.kwargs.get('slug') + + # If no car_slug is provided, it's an invalid request + if not slug: + # You might want to raise Http404 or a more specific error here + raise ValueError("Car slug is required to identify the colors to update.") + + + return get_object_or_404(models.CarColors, car__slug=slug) def get_success_url(self): + """ + Redirects to the car's detail page using its slug after a successful update. + """ + # self.object refers to the CarColors instance that was just updated. + # self.object.car then refers to the associated Car instance. return reverse("car_detail", kwargs={"slug": self.object.car.slug}) - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - kwargs["instance"] = self.get_object() - return kwargs - - def get_initial(self): - initial = super().get_initial() - instance = self.get_object() - dealer = get_user_type(self.request) - selected_items = instance.additional_services.filter(dealer=dealer) - initial["additional_finances"] = selected_items - return initial - - def get_form(self, form_class=None): - form = super().get_form(form_class) - dealer = get_user_type(self.request) - form.fields[ - "additional_finances" - ].queryset = models.AdditionalServices.objects.filter(dealer=dealer) - return form - + def get_context_data(self, **kwargs): + """ + Adds the related Car object to the template context. + """ + context = super().get_context_data(**kwargs) + # self.object is already available here from get_object() + context['car'] = self.object.car + context['page_title'] = _("Update Colors for %(car_name)s") % {'car_name': context['car']} + return context class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): """ @@ -8341,6 +8359,123 @@ def user_management(request): return render(request, "admin_management/user_management.html", context) + +def AuditLogDashboardView(request): + """ + Displays audit logs (User Actions, Login Events, Request Events) with pagination. + Log type is determined by the 'q' query parameter (e.g., ?q=userActions). + Pagination page number is passed as a query parameter (e.g., ?page=2). + """ + q = request.GET.get('q') # Get the log type from the 'q' query parameter + current_pagination_page = request.GET.get('page', 1) + context = {} + template_name = None + logs_per_page = 30 # Define logs per page once + + # --- Determine Data Source and Template based on 'q' parameter --- + if q=='userRequests': # This block handles cases where 'q' is 'requestEvents', None, or any other invalid value. + # It defaults to Request Logs if 'q' is not 'userActions' or 'loginEvents'. + template_name = 'admin_management/request_logs.html' + context['title'] = 'Request Logs Dashboard' + request_events = RequestEvent.objects.all().order_by('-datetime') + paginator = Paginator(request_events, logs_per_page) + try: + page_obj = paginator.page(current_pagination_page) + except PageNotAnInteger: + page_obj = paginator.page(1) + except EmptyPage: + page_obj = paginator.page(paginator.num_pages) + + + elif q == 'loginEvents': + template_name = 'admin_management/auth_logs.html' + context['title'] = 'Login Events Dashboard' + auth_events = LoginEvent.objects.all().order_by('-datetime') + paginator = Paginator(auth_events, logs_per_page) + try: + page_obj = paginator.page(current_pagination_page) + except PageNotAnInteger: + page_obj = paginator.page(1) + except EmptyPage: + page_obj = paginator.page(paginator.num_pages) + + else: + template_name = 'admin_management/model_logs.html' + context['title'] = 'User Actions Dashboard' + + # OPTIMIZATION: Get the QuerySet but don't evaluate it yet + model_events_queryset = CRUDEvent.objects.all().order_by('-datetime') + + # 1. Paginate the raw QuerySet FIRST + paginator = Paginator(model_events_queryset, logs_per_page) + + try: + # Get the page object, which contains only the raw QuerySet objects for the current page + page_obj_raw = paginator.page(current_pagination_page) + except PageNotAnInteger: + page_obj_raw = paginator.page(1) + except EmptyPage: + page_obj_raw = paginator.page(paginator.num_pages) + + # 2. Now, process 'field_changes' ONLY for the events on the current page + processed_model_events_for_page = [] + for event in page_obj_raw.object_list: # Loop only through the current page's items + event_data = { + 'datetime': event.datetime, + 'user': event.user, + 'event_type_display': event.get_event_type_display(), + 'model_name': event.content_type.model, + 'object_id': event.object_id, + 'object_repr': event.object_repr, + 'field_changes': [] + } + + if event.changed_fields: + try: + changes = json.loads(event.changed_fields) + if isinstance(changes, dict): + for field_name, values in changes.items(): + old_value = values[0] if isinstance(values, list) and len(values) > 0 else None + new_value = values[1] if isinstance(values, list) and len(values) > 1 else None + event_data['field_changes'].append({ + 'field': field_name, + 'old': old_value, + 'new': new_value + }) + elif changes is None: + event_data['field_changes'].append({ + 'field': 'Info', + 'old': '', + 'new': 'No specific field changes recorded (JSON was null)' + }) + else: # Handle valid JSON but not a dictionary (e.g., "[]", 123) + event_data['field_changes'].append({ + 'field': 'Error', + 'old': '', + 'new': f'Unexpected JSON format: {type(changes).__name__}' + }) + except json.JSONDecodeError: + # Handle invalid JSON; you might log this error + event_data['field_changes'].append({ + 'field': 'Error', + 'old': '', + 'new': 'Invalid JSON in changed_fields' + }) + processed_model_events_for_page.append(event_data) + + # 3. Replace the object_list of the original page_obj with the processed data + # This keeps all pagination properties (has_next, number, etc.) intact. + page_obj_raw.object_list = processed_model_events_for_page + page_obj = page_obj_raw # This will be passed to the context + + # Pass the final page object to the context + context['page_obj'] = page_obj + + return render(request, template_name, context) + + + + def activate_account(request, content_type, slug): try: model = apps.get_model(f"inventory.{content_type}") diff --git a/requirements_dev.txt b/requirements_dev.txt index fd0f3c41..841e904f 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -94,3 +94,4 @@ urllib3==2.3.0 wcwidth==0.2.13 langchain langchain_ollama +django-easy-audit==1.3.7 \ No newline at end of file diff --git a/static/images/logos/users/li-yang-5h_dMuX_7RE-unsplash_svH1noM.jpg b/static/images/logos/users/li-yang-5h_dMuX_7RE-unsplash_svH1noM.jpg new file mode 100644 index 00000000..5b273235 Binary files /dev/null and b/static/images/logos/users/li-yang-5h_dMuX_7RE-unsplash_svH1noM.jpg differ diff --git a/staticfiles/icons/HaikalAi.ai b/staticfiles/icons/HaikalAi.ai new file mode 100644 index 00000000..42c72a1a --- /dev/null +++ b/staticfiles/icons/HaikalAi.ai @@ -0,0 +1,1356 @@ +%PDF-1.6 % +1 0 obj <>/OCGs[22 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + HaikalAi + + + Adobe Illustrator 29.5 (Macintosh) + 2025-05-28T01:01:54+03:00 + 2025-05-28T01:01:54+03:00 + 2025-05-28T01:01:54+03:00 + + + + 256 + 196 + JPEG + /9j/4AAQSkZJRgABAgEAAAAAAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAAAAAAAAEA AQAAAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAxAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FUPqGo2GnWct7qFzFaWcI5TXE7rHGg8WZiAMaW3jPm3/nKnyhpksltoFlNrcyGn1gt9Wtq/ 5LMryNT/AFAD2OWjEerUco6IT8v/APnKGLXfMlpout6QlguoSrBa3kEpdVlkIWNZEZRszGnIHbw7 4ZYqCI5bL3nKW52KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KsU/MP8AMny55F0c32qyc7mUEWOnxketO4psoPRRX4mOw9zQGUYksZSAfHP5hfmb5n886mbr VZylojH6npsRIghX2X9p/FzuflQZkxiA40pEsTybFk/5ZaLda1+YHl/T7YEu99DJIVrVYoXEsr7f yohORkaDKIsvvbMNy3Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYq7FWFfml+aWieQdEN1dEXGqXAI07TgaPK4/ab+WNf2m+gb5OELYylT4u81ea9d806zNq+tXLX N5MaCuyRpUlY41/ZRa7D+OZIFOKTaUZJDsVfTv8Azi1+XT2djP511COk18pt9JRhusAb97Lv/vxl 4r7A9mzHyy6N+KPV9AZS3OxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxVjP5heftG8keXJtY1Jub/AGLK0Bo88xFVjXY08WbsMlGNljKVB8Reb/N2t+bNduNa1icz XMxoifsRRg/DFGv7KrX+J3JzKApxSbSXJIdirPPye/LO689+aY7V1ZNFsys2rXK7UjrtEp/nkpQe Aqe2QnKgzhGy+3LS0trO1htLWJYba3RYoIUFFREHFVUDoABmI5SrirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqC1rWdN0TSbrVtTnW2sLOMy3EzdAo8ANySdgBuT sMIFoJp8P/mj+Y+p+fPMsmpXPKKwhrHpliTUQw17025vSrn6OgGZUI0HFlKyw/JsXYqmnljy1q/m XXLTRdJhM17dvxQfsqvVnc9lQbk4CaSBb7l/L3yJpPkny1b6Lp45Mv7y8uiKPPOwAeRvuoB2FBmJ KVlyoxoMkyLJ2KuxV2KuxV2KuxVI9Z88+TNEdotW1yxspl3ME1xGsv8AyLrz/DCIkoMgEm/5XV+V f/Uy2f8AwTf805LgPcjjCfaP5v8AKmtGmkaxZX7/AMlvPHI4p4qrFh92RMSEggpvgS7FXYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXYq7FXyX/zkb+a58xayfK+kzV0TS5CLqRD8NzdLsTUdUi3C+Jqf5cy ccK3cfJO9ni2WtTsVXwQTXE8cEEbSzSsEiiQFmZmNFVQNyScCvs38j/ykt/I2h/W75FfzJqCA30m x9FDuLdCNqL1cjq3sBmNknbkwhT03K2x2KuxV2KuxV2KsG/Mj84PKfkS3430hu9WdeVvpUBHqsD0 aQnaNPdvoBycYEsJTAfL/nj89vzA81ySRNfNpemOSF0+xYxLxO1JJBSSSo6gnj7DL44wGiWQl52S SanrljB2Kto7o6ujFXUgqwNCCNwQRir0fyV+f35ieWJI43vjq+nLs1lqBMu3+RMT6qUHT4ivtlcs YLOOQh9J/lx+d/k7zuEtYZDp2tkfFpdyw5MR19GTZZR8qN/k5RLGQ3xmC9CyDN2KuxV2KuxV2Kux V2KuxV2KuxV2KuxV5J/zkP8AmgfKflsaPpsvHXtZRkjZTRoLb7Mk224Zvsp71P7OWY4WWvJKg+Pc ynGdirsVfSn/ADjR+UqxxR+edbhrK9f0FbuPsruGuSD3bpH7fF3U5Rln0b8UOr6JyhudirsVdirs VdirxT86/wA/rfyz63l/yy6XPmCnG5u9nitK9R4PMPDovevTLYY73LVPJWwfKl7fXl/dy3l7O9zd zsXmnlYu7sepZjucyHHUMKuxV2KuxV2Kro5JI5FkjYpIhDI6khgwNQQR0IwK+hvyf/5yRmheDQfP E3qQGiW2uN9pP5VuafaHb1Ov81d2FM8XUN0Mne+lEdJEWSNg6OAyOpqCDuCCMob28VdirsVdirsV dirsVdirsVdiqX+YNd07QdFvdZ1KT0rKxiaaZu9F6Ko7sx2UdzhAtBNPg/zt5u1Lzd5mvte1A0lu 3/dQg1WKJdo4l6bIu3v1zLiKFOJI2UiySHYqz78lvy5fzx5xitbhT+hrEC51VwaVjB+GIHxlbb5V PbITlQZwjZfbsMMUMSQwoI4o1CRxqAFVVFAAB0AGYjlLsVdirsVdirsVeDfnx+fC6Otx5V8q3AOr kGPUtSjNRbA7GKIj/d38zfsf632bseO9y05MlbB8uMzMxZiSxNSTuSTmQ0NYq7FXYq7FXYq7FXYq 7FXtP5F/npL5Xli8u+YpWl8uStxtrlqs1mzH7zCT1H7PUdxlWTHe4bceSti+soZopokmhdZYZVDx yIQysrCoZSNiCMxnIXYq7FXYq7FXYq7FXYq7FXYq+Y/+cpfzFN3fw+StPlrb2ZW41ZlP2pyKxQmn aNTyI8SO65fij1aMsuj5+y9pdirsVfbH5DeRk8qeQLP1Y+OqaqFvr9iKMDIoMUR7/u46Cn83LxzE ySsuVjjQei5Bm7FXYq7FXYq8E/PT8/YtLS48r+UrkPqp5RajqcZ2tuzRQt3l8WH2P9b7N2PH1LTk ydA+XmZmYsxJYmpJ3JJzIaGsVdiq5EeR1jjUu7kKiKKkk7AADAr0vyx/zjt+Z2uxJO1jHpNs4qsu ouYWI6/3SrJKPpQZA5AGYxksuX/nEXzNwBbXrIPTdRHKRX50H6sj4wZ+CWOeYP8AnGX8ztKjaa1h ttXiUVIspf3gH/GOZYiT7LXCMoYnEXl9/p1/p13JZ6hbS2d3CaS286NHIp8GRgCMstrpD4VdirsV e6/84+fnWdDuIfKXmKf/AHDTtx068kP+8sjH+7didoWP/An2O1OSF7htxzrYvqjMdyHYq7FXYq7F XYq7FXYqxz8w/OVp5O8oahr1wA726cbWEn+8uJPhiT/gt2/yanJRjZpjKVB8GX9/d6hfXF/eStPd 3UjzXEz7s8kjFmY/MnMsOIoYVdiqa+U9Oi1PzTo2my0MV7fW1tIDsOMsyoa/Q2AnZIG79CAAAABQ DYAZhOY7FXYq7FVO5uba1t5Lm5lSC3hUvLNKwREUblmZqAAe+Kvmn84/+cj3vkn0DyVK0Vq1Y7vW lqjyDoUt+hVf8vqe1Opvhi6lonk6B8+Ekmp65e0uxV2Ksh8j+RfMPnTW00nRYOcn2ri4eohgj/nl YA0Hh3PbIykAmMSX1/8Alt+TXlLyNbpJbRC+1orSfVp1HqVI3ES7iJfYb+JOY0pkuTGADPcgzdir sVY153/Lvyp50sDaa5ZrLIoIt71KJcQk945KV678TVT3GSjIhjKIL5G/NP8AJzzF5BvPUlrfaHM1 LTVI1oK/77mXf03/AAPY9QMmEwXHnAhgGTYOxV2KvqP/AJxx/OFtWto/JuvT11K2Smk3LneeFB/c sT1kjUbeK+43x8kOob8c+j3rKW52KuxV2KuxV2KuxV8o/wDOUXn39LeZofK1nJysdF+O74nZ7yRd x7+kh4/MsMyMUaFuPllvTxDLmp2KuxVX0+9uLC/tr62IW4tJUnhY7gPGwZT94wK+9vI3nfQ/OWgW +r6VMrh1X6zbVHqQSkfFFIvUEHoe43G2Yko0XMjKwyDIpWySRxxtJIwSNASzsQAAOpJOKvMvO/8A zkP+X3llJIbW5/TmprUC0sWDRhh/vy43jX348iPDLI4yWuWQB80/mL+cHnDzzMY9QnFrpStyh0q3 JWEUPwmTvIw8W+gDL4wAaZTJYPk2DsVdiqdeTvKOs+bfMFtoekx87q4NWdq+nFGPtyyEA0Vf7OuR kaCQLL7f8geQ9E8k+XodH0teVPju7tgBJPMftSPT7lHYbZiylZcqMaDJMiydirsVdirsVQuqaXp+ q6fcadqNul1Y3SGO4t5BVWU9j/A9sQVIfHH5z/k3feQ9SF3aFrny1eORZ3J3eJzU+hN7gfZb9oe9 cyoTtxZwp5nljB2Kq1leXVldw3lpK0F1bOssEyGjI6HkrKfEEYFfbX5OfmdbefPLC3EhWPWrHjDq tuuw5kfDKg/kkoSPA1HauYs40XKhKwz3IM3Yq7FXYq7FWPfmD5ut/KPk/UtemoWtYj9WjP7c7/BE nyLkV9q5KIs0xkaD4JvLu5vLue8upDNc3MjTTytuzyOxZmPuSa5luIo4VdirsVdiqP0bXtb0S7F5 o9/Pp90NvWt5GjYjwPEio9jgItINMxH5+/m6IPQHmKXh4mG2L/8ABmLn+OR8OLLxJMZ13zp5t1/b WtYvL+OtRFPM7Rg+0deA+gYREBiZEpLkkOxV2KuxVdHHJJIscal5HIVEUEsWJoAAOpOBX2j+Rv5W ReR/LQmvYwfMWpqsmoydTEvVLdT4JX4qdW9gMxsk7LlQhQelZWzdirsVdirsVdirsVQOt6JpeuaT daTqkC3NheIY54W7g9wRuCDuCNwd8INIIt8Q/ml+XGpeQ/M0mmXHKawmrLpl6RtNDXvTbmnRx9PQ jMqErDiyjRYdk2LsVZX+WXny98kebbXWoOT21fR1C2B/vbZyOa/6wpyX/KAyMo2GUZUX3Vpuo2Wp afbahYyrPZ3cazW8y9GRxyUj6DmGQ5YKIxV2KuxV2KvmL/nK/wA6m51XT/KFs9YbFRe6gB0M8qkQ ofdIyW/2eX4Y9WjLLo+f8vaXYq7FXYq7FXYq7FXYq7FXYq7FXYq9x/5xj/LVdZ1t/NupRctO0iQL YIw+GS8pXlv2hBDf6xXwOU5ZVs24o3u+rMx3IdirsVdirsVdirsVdirsVYl+Z/5e6d558rT6TccY 7xKy6bdkbwzgbH/Vb7Ljw9wMlGVFjKNh8M6rpd/pWpXOm6hC1ve2cjQ3ELdVdDQj+3MsFxCELhV2 Kvpj/nFj8wzcWtx5Jv5SZbYNdaQWPWIms0Ir/Kx5qPAt4Zj5Y9W/FLo+hcpbnYq7FUJq2qWmlaXe aneP6dpYwyXE7+CRKWb8BhAtSXwB5m1688weYNQ1u8/3o1Cd53UdF5mqoPZVoo9szAKDhk2Uswod irsVdirsVdirsVdirsVdirsVTDy/od/r2t2WjaenqXl/MkEK9gWNCzeCqN2PYYCaSBb728o+WNP8 r+W9P0GwFLexiEfPoXc/FJI3u7ksfnmGTZtywKCb4EuxV2KuxV2KuxV2KuxV2KuxV89/85Rflotx aJ5402L9/bhYdZRRu8RIWKfbuh+Bvan8uXYpdGnLHq+ZsyGh2Kpl5b16/wDL+vWOtWDcbuwmWaLw PE7o3+Sy1U+xwEWEg0++vLuu2Ov6FYa1YtW11CBJ4q9QHFSrf5Sn4T75hkUXLBtMcCXYq8X/AOcp fNx0ryRBoUD8bnXZuMgHX6tblZJPvcxj3FctxDe2rKdnyVmS47sVdirsVdirsVdirsVdirsVdirs VfR//OKfkHa7863sfXlZaSCP+R8o/wCTYP8ArZRll0b8Uer6OyhudirsVdirsVdirsVdirsVdirs VUb6ytL+ynsryITWl1G8NxC32XjkUqyn5g4q+D/zI8lXXkzzhf6FNVoYm9SymP8Auy2k3if50+Fv 8oHMyMrDiSjRYxkmLsVfTP8Azif52M9hqHk+6krJaE32mgn/AHU5AnQeyyEN/sjmPmj1b8Uuj6Ey ludir4v/AOcifNf6f/My+ijflaaMo06Ch25REmY/P1WZfoGZWMUHGyGy8yyxrdirsVZD5K8heZ/O ep/o/QbQzstDcXDfBBCp6NLJ0XoaDqewORlIDmyjEl9IeTP+cWvJ2mRRz+ZJpNbvdi0Ks0Fqp8Aq ESPQ92ah/lyiWU9G6OIdXpFv+Wn5d28HoReWNLEZFGDWcDlgDUcmZCW+nIcR72fCO5hvnb/nHHyB r9rI2l2w0HU6ExXFoD6NeweCvDj/AKnE5KOQhjLGC+T/ADX5V1nytr11omrxeleWrUJG6Oh3WSNt uSsNx+O+ZANhxyKSjJIdirsVRmj6Ve6vq1npVknqXd9MlvAni8jBRX233wE0kC3395Z0Cy8veX9P 0SxH+jafAkCNQAsVHxO1P2narH3OYZNlywKCZ4EuxV2KuxV2KvF/+cofOupaD5VsNJ02d7afW5ZV nmjJVvq8Cr6iBhuObSpX2qO+W4o2WrLKg+bPJ/5gebPKF+t5od/JB8QM1qxL28o8JYieLfPqOxGX yiC0iRD7H/K380NG8/aH9btaW+p2wVdS08mrROejKf2o2oeLfQd8xZwpyYytmmRZOxV2KuxV4f8A 85TeSBqfla38z2yVvNFb07ogbtazMBv4+nJQj2ZstxS3pqyx2t8pZkuO7FWR/l15rl8qedNK11WI itZwLpRX4reT4JloOvwMae+RkLDKJovviOSOSNZI2DxuAyMDUEEVBBzDctK/Nuvw+XvLGqa3NTjp 9tLOqt0Z1U8E/wBk9FwgWUE0H5+XNxPc3EtzO5knmdpJZD1Z3NWJ+ZOZjhqeFXYqzD8r/wAttV8+ +Y1021JgsYAJNSv6VWGInb5u9KIv09AchOVBlGNl9reVfKmheVtGh0jRbZbazhFTTd5HoA0kjftO 1Nz/AAzFJtygKTfAl2KuxV4J/wA5Z+V7e48vaX5ljQC7sZ/qc7gbtBOGZeR/yJE+H/WOXYTvTTlG 1vl3MhodirsVe1f84s+URqnnW416ePlbaHDWEkbfWbgFE67fDGHPsaZVlO1NuIbvrPMZyHYq7FXY q7FXYq+av+cv4nF55WlP2HjvVHzVoSf+JDL8PVozdHzvl7SyHyF511XyZ5mtNc05iWhPG5t60WaB iPUib5joexoe2RlGwmMqL7s8va9pvmDRLPWtMl9WxvoxLC/ffYqw7MrAqw7EZiEU5YNphgS7FXYq g9Z0q01fSL3SrxeVrfQSW869+EqlTT332wg0gi359a1pV1pGsX2lXYpc2E8ltMO3OJyhp7bZmA24 hFIPCh2Kvtb/AJx980nzB+WOneq3K60otptwf+MAHpf8kWT6cxMgouVjNhj3/OVPmQ6f5CttHjfj NrV0quvjBbUlf/kp6eSxDdjlOz5KzJcd2Kq1lZXV9eQWVpE011cyLDbwpuzySEKqj3JOBX3T+Vv5 f2XkbylbaTEFe+cetqd0o/vbhh8W5/ZT7K+w8ScxJysuXGNBl2RZOxV2KuxV5p/zkbCkn5P627Vr C1o6fM3cSb/QxyzH9TDJ9L4tzKcV2KuxV9n/APOOvlU6D+WdlNMnC71hm1GavXhKAsP0ekqt9OYu Q2XJxig9OytsdirsVdirsVdirwf/AJy400y+U9E1ECv1W+aAnwFxEW/XBl2E7tWYbPlrMhx3Yq92 /wCcY/zMGlas3k7U5uOn6m/PTHc7R3ZoDHU9BMBt/lD/ACjlOWPVtxS6PqfMdyHYq7FXYq+Nf+cl NDTS/wA1LyaMBY9Ut4L4KOgLAwuf9k8LN9OZWI7ONlG7yzLGt2Kvfv8AnEnzEYNe1ny/I/7u9t0u 4FPT1LduDhfdklqf9XKcw2tuwnolH/OVPmA335g2+ko1YtHs0R08Jrj985+mMx4cQ2RlO7xjLWp2 Kva/+cXPJQ1bzfP5iuo+VnoaD0K9DdzAqnz4Jyb2PHKcsqFNuKO9vrLMdyHYq7FXYq7FXlP/ADk3 dLD+VN3Get1dW0S/MSep/wAy8sxc2vLyfHGZTjOxVN/KHl+bzF5o0vQ4ahtQuY4GYfsozfG/+xSr ZGRoJAsv0CtreC2t4raBBHBAixxRjoqIKKB8gMw3MVMVdirsVdirsVdirzz8/tEOrflTraIvKayR L6P2Fu4eQ/8AIrnk8ZosMg2fEuZbiuxVdHJJHIskbFJEIZHUkMGBqCCOhGBX2b+RX5rxed/Lws7+ QDzJpiKt6h2M8Y+FblR/ldHp0bwBGY2SFFycc7D07K2x2KuxV8qf85bOh88aQgI5jTFLDvQ3EtP1 HMjDycfNzeG5c1OxVnP5IaydI/NPy9PyISe5FnIOxF2pgFf9lIDkMgsM4HdLPzN1ZtW/MLzFfk8l lv51iP8AxXG5jj/4RBhiKCJHdjOSYuxV9y/kv5M/wl+X2m6fLHwv7lfruoilD684B4t7xoFT/Y5i TlZcqEaDOMgzdirsVdirsVeFf85b33p+TtGsa0NxqBmp4iGF1/5nZdh5tWbk+V8yHHdir3X/AJxR 8qG9803/AJkmQ+jpMPo2zEbfWLkFSQf8mIMD/rDKcx2ptxDe31RmO5DsVdirsVdirsVdiqhf2Vvf WNxY3K87a6ieCdP5kkUqw+kHFX58+YdFutD13UNHuxS40+4kt5DSlTGxXkPZqVGZoNuGRSX4UOxV NPLHmbWPLOt2utaROYL61bkjdVZTsyOv7SsNiMBFpBp9pfld+a+gefdKWS2dbbWIUBv9LZvjjPQu lftxk9GHToaHMWcCHJjO2b5Bm0zKqlmICgVJOwAGKvhz86vONv5s/MTUtSs3EmnQlbSxkHRooBx5 j2d+TD2OZcI0HFmbLBsmwdiqM0e+Nhq9jfKaNaXEU4PgY3DdvlgKQhp5nmmkmf7crF2p4sanFCzC r0b8hfI581/mBZiePnpelEX1+T9kiM/uoz2POSlR/LyyvJKgzxxsvtfMVynYq7FXYq7FXYq+c/8A nMGRxH5TjB+BjfsR7qLYD/iRy/D1ac3R83Ze0OxV9x/kp5LPlL8vtOsZk4ahdj67qAIoRNOAeB94 0CoflmJOVlyoRoM6yDN2KuxV2KuxV2KuxV2Kvlf/AJyq8lNYeZbTzVbR0tNXQQXjAbLdQrRSf+Mk QFP9U5kYpbU4+WO9vCsuanYq7FUTp2pahpl7FfadcyWl5AeUNxCxSRT02ZaHAQoL2Dy9/wA5U+fN PgSDVbW01hUoPXcNBOQP5mj+D/hMrOINoylKfzB/5yH86ebrCXS4ki0fSpxxuILYs0sqnqkkzb8T 3Cha96jDHGAiWQl5ZljW7FXYq7FVe+tJrK9uLOYcZraR4ZAdqNGxU/iMCqKqzsFUFmY0VRuST2GF X2v+Rf5dHyV5MjS7j4a1qZW61KvVDT93D/zzU7/5RbMTJKy5WONB6LkGbsVdirsVdirsVfPn/OXu nyPpPlrUAD6dvPc27HtynSNx2/4oOXYTzacz5lzIaHpv5Afl7J5s87wXNxGW0bRWS7vWIqruprDD vt8bLUj+UHK8kqDZjjZfaGYrkuxV2KuxV2KuxV2KuxV2Kse8/eTbDzj5VvtBvKJ9ZStvPSpinTeO QfJuviKjJRlRtEo2Hwjrmi6loer3ekanCYL+ykMU8R7MO4PcEbg9xvmWDbiEUgcKHYq7FXYq7FXY q7FXYqnvkPRZNb86aJpUa8vrd7Cj9dow4MjbfyoCcjI0ExFl7F+dP5Bea7nzfda55Vshf2OquZ7i 3jeNJIbh95aiRkqrt8QI7kjwrXDIK3bZ4zezIfyW/wCcd7jRNQi8x+cEjbULch9P0tWWVYZB0llZ aozr+yFJA61r0jPJewTDHW5e+5S3OxV2KuxV2KuxV2KsR/NTyLH528l3miBljvKrcafM32UuIqlK +zAlCfA5KEqLGcbD5e0L/nHT8ztR1n6head+jLZGpcahO6NEq13KcGYyHwC/SRmQcgaBjL6w8jeS NE8meX4NF0hCIo/jnnenqTTMAGlkI7mn0DbMeUrLkRjQT/IpdirsVdirsVdirsVdirsVdiryX89P yXTzrZDV9GRI/M9onFQSEW6iG/pOTsHX9hj8jtQizHOmvJC3yFe2V5Y3c1neQvb3UDGOaCVSjoy7 FWU7g5kuMo4VdirsVdirsVdirsVfR/8Azi7+WdzHM/nnU4fTQo0Gio4+Jufwy3AHYcaovjVvbKMs ujfij1fR2UNzsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirBfzH/ACc8 o+eovVvojaaui8YdUtwBLQdFkB2kUeB38CMnGZDCUAXzl5s/5xs/MfRJHfT7dNcshUrNZkCWg/mg ch6+ycsuGUFpOMh5tqWiazpcvpanYXNjLWnp3MTwtXwo4U5YCwIQWFDsVdirJ/Ln5Z+ffMboNI0O 6njc0Fw6GKD/AJHS8I/xyJkAyESXvH5cf84uWOnzxal5znj1CdCGj0qCv1cEbj1nIVpP9UADx5DK ZZe5tji73vqIkaLHGoREAVEUUAA2AAGUtzeKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2Kqdx9X9FvrHD0f2/Upx+mu2KsL1T/AJUjzP6V/wAM86iv1r6hWtNv7z2y Y4vNgeHyQH/WOf8A35//AHLMfV5r6fJkWg/8q39Rf8P/AKG9T4eH1D6rXtxp6X0UwG+qRXRkuRZO xV2Kv//Z + + + + proof:pdf + uuid:65E6390686CF11DBA6E2D887CEACB407 + xmp.did:4e1c18b4-94e0-457b-890a-5230a991a191 + uuid:4e05d1e4-9d06-054b-9f56-db9a7696cc69 + + uuid:71d3b25e-453d-1249-aa45-a002d99380b3 + xmp.did:f1cee0fc-fdf1-44c6-b699-76b3dca1e609 + uuid:65E6390686CF11DBA6E2D887CEACB407 + proof:pdf + + + + + saved + xmp.iid:4e1c18b4-94e0-457b-890a-5230a991a191 + 2025-05-27T18:21:53+03:00 + Adobe Illustrator 29.5 (Macintosh) + / + + + + Web + Document + AIRobin + 1 + False + False + + 1920.000000 + 1080.000000 + Pixels + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + RGB + PROCESS + 255 + 255 + 255 + + + Black + RGB + PROCESS + 0 + 0 + 0 + + + RGB Red + RGB + PROCESS + 255 + 0 + 0 + + + RGB Yellow + RGB + PROCESS + 255 + 255 + 0 + + + RGB Green + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blue + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + + + + Grays + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + Web Color Group + 1 + + + + R=63 G=169 B=245 + RGB + PROCESS + 63 + 169 + 245 + + + R=122 G=201 B=67 + RGB + PROCESS + 122 + 201 + 67 + + + R=255 G=147 B=30 + RGB + PROCESS + 255 + 147 + 30 + + + R=255 G=29 B=37 + RGB + PROCESS + 255 + 29 + 37 + + + R=255 G=123 B=172 + RGB + PROCESS + 255 + 123 + 172 + + + R=189 G=204 B=212 + RGB + PROCESS + 189 + 204 + 212 + + + + + + + Adobe PDF library 17.00 + 21.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 5 0 obj <>/ExtGState<>/Properties<>>>/Thumb 27 0 R/TrimBox[0.0 0.0 1920.0 1080.0]/Type/Page/PieceInfo<>>> endobj 24 0 obj <>stream +H,M1 } +_eɖB#~6 _sz㲫y,O{,߫c[M\O_Jo}&O]Z>\k> s&/fis7yxyr=›kckiyJu`fFކ<6H*^)m׮m9o]Ю喴@!/T:# c! *z4I +ۉVe εU[H 8ިɵ}j!n-AN\ GZG58\FkUh1\$j6D9W7/|B}؀ǹL2Ӏ/0.qL +g* 9EP)p !I [jl/ldbaeܽ&n>>pxy<ЈskHvwLJɦIhHNLXP>7äŦ]$ /xfqsb:$@0XsX{62x}Grڕmڞ&׼j\SH@(]դb?m7)q~@Nb5M7mصbYnd1 )W )>Mr(61TSeB[CV\e(UnNKz Tj:ϗ}< +]`,?=FKC XqG t259RVֻlŠg4?{j]7Cʺ 8bSVfu\>qIL endstream endobj 27 0 obj <>stream +8;Z]__$Rn"&4BAX9egG`Xk6CSWtA7F#U05%TXS>IFUS6M!;r"kNo[QDSB+R+f2No3 +:sGcV\XG=PB1(tM1BuATZ_`$Aj^^`bV+b'n6gT!ahq?Fmkd>JFH)>dQo!S,FTs3;$48%_:!!%E$"TSQ&rr<$!s8N0$)V+RB~> endstream endobj 8 0 obj <> endobj 9 0 obj <> endobj 10 0 obj <>stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 24.0 %%AI8_CreatorVersion: 29.5.1 %%For: (Marwan Alwali) () %%Title: (HaikalAi.ai) %%CreationDate: 28/05/2025 01:01 %%Canvassize: 16383 %%BoundingBox: 810 -656 1113 -428 %%HiResBoundingBox: 810.932355970144 -655.955100126255 1112.65976072465 -428.509843147501 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 14.0 %AI12_BuildNumber: 141 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 0 -1080 1920 0 %AI3_TemplateBox: 960.5 -540.5 960.5 -540.5 %AI3_TileBox: 557 -819.5 1340 -260.5 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI24_LargeCanvasScale: 1 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI17_Begin_Content_if_version_gt:24 4 %AI10_OpenToVie: 735.133997426618 -402.085948661937 2.79807187933183 0 8182.7219482007 8228.38741743816 1788 1022 18 0 0 6 64 0 0 0 1 1 0 1 1 0 1 %AI17_Alternate_Content %AI9_OpenToView: 735.133997426618 -402.085948661937 2.79807187933183 1788 1022 18 0 0 6 64 0 0 0 1 1 0 1 1 0 1 %AI17_End_Versioned_Content %AI5_OpenViewLayers: 7 %AI17_Begin_Content_if_version_gt:24 4 %AI17_Alternate_Content %AI17_End_Versioned_Content %%PageOrigin:560 -840 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 11 0 obj <>stream +%AI24_ZStandard_Data(/Xm.J +PRl+p +lу`^Z\qߎfP% @ FO#_J?H[.p\b|Imݜ]R%U808>p`l d:Y0q 389+(+hGՐLn׭4vJgU +JIDVN2 +` "FN՘d U)*zhBa$"nյF7g>d63!ta2,L"JӍk:VYǸ2q!a9{ʮF?G(cd@ CaO( + a3O8,"Q# +RŠP) A ,xñC146Ίa@{4$k+[q#0660d0Y]X(cZqKqHbjA +:EX+t04@ M}FKJsI̜ Z ̨cn(9³e+K n=APiki &%&@(8sX<Lļ\`XBCE B1IT`xLD4p|p}6pK\la +KvfVV[W˪ L Uim"TTS Et0t|"YQ0CѮe%J 0ӅbA9 s N ` D2L&] 1(袆BD DO4\zVa YbI bscu12`\8aP 8(4\0&8,@A,VPIࠠ:  +TA $l B$p @(@ $@` Lhhp !p!B4AB@&L`p¡ @,T Al@&8(8(T + 8:4`pP&T0l`qXhpB @ "H`2@„ +ȀA !B4(&8(8,@@$@  +0 ! 8&8(xqAC $Xp!BC&0*pB@A   &@A: $X +x .4H +*X(4dAh4 (4\aYjCo'% $X8$` 0(  \@a! (Xh0k>=G&p k%IPK(cV8y:^;#4V\aA +T<4F\Ap@ƒ .Px@A X ,42ya 4h ,@@!L``&4\P?,h!0pp`@ (PBÄ "0c&  *8@ B`'oVp 8 +.@ &𠎡Vpa!T@(T d`9,@ sX$\  d㰠PA%B2L5 d`C ."@8DPa„ .PhB +H8,B h\А*(e99j{ܑ3ܹì+ $ਸ਼<,A$\@a +r J A Fǂq44$4\PA 4L aLpP aL8 LpX`FtX@ ,4TA&1btP ,T 60!a + 4XH8p`#UA*\p@F4Ieir6y'Z] 6GDO;UJ_DrՙM~,·=4UdYs}u/g+ ;KvG9̻43INN_4D*WgVZh+<~Ϫͧ qz tuT=3Re !U==>ڝd?DrrEر^Guk{OޱΒasYQgxG&ѭ_ݣWfǪ%[Q_m&ǯb.ͷq]r7 I!ZTѐG2kv؜_fS=,W^s̶y$V#¦EMZ}, yʞx%G>y#b9ڲ5i=[%)ُ\9??h_2+ kiso)5tB'MutTg?l_!M̴MC*Lg'td˛SuiggS:R42:]V,D/*!_eވ!MQR#$Bd;N$o;ґ_%4""}}$JA JI:U߰Vba+<-] ֓"jgzϱ-8dz㻧[G\/htn+D'ϓe. IǫEm~+U=7hjԔY'}2$U̖JV;iwiz>C흷'I6sɽf^Ch Ov/EeK]=~/DxrC<#JD +g0!>g#%SXb}?ߤ53&32m{|g*Z5/bMIjjWeDR3(&ݟet,[1aRY{6彈si{%ݻ +Y=*Ց^In$*^og.?kmWdkկl1}L9%闝h/U[T$WH|O=yؔH3㑡qJS2h9L*խNG][RHG4?}*w%Lja|ZpznKц,6)f2!콤Ιa7iMZ]\vot)D=[Y>x'}u&([XGv/HGxLY~K$V)H8iKUBo+n%>SۜбHIu2S3+R>Z$D=hI/Rg4lZȵϣdO,b,mQ]DJ߆'G=z*otzajYcŬhS/M W*󓊤ļ+YX'hEܕ*nŦ|Rҭ,}NIǡ-i&{m0 z7 .I꠴$5iiK0 Os=g.~x)D[KdY|MZ@}rMُQ[SrQ>qQCC^'Ώ^ꞖhTobՄ^QC&JP CG^ eJSWuiҁX3ɡ2liRfC<ܽqtg#L7k+4K,=7w:U=zޙJXr]=|̢{١7;:/H8Tr)e=.7"Y?3UVկc ]3n)eVyc/'dKtULJ|T^[dxH:tQG_2hBC6{XVݑIJ@#?O_rN*ч-ˬd{ ȝ6WpK⁇#9yɞXw&Z=;,5G[ٽv9i7 KGo)&;vdd51c#_I Մ~<вjI+ h:,]܏~KpڐvF{Gpi-IRn0<}P,$tg"˕nfݤ,e\[7Dezdv9*GgٕjI%ox&d=j:bYN<bi|&s&ՕEKϝ-e_' V: C+$@j/,wiH^ FAMKQ},ᤒF~QN89;q&s,p [8<4D *<  4 IP3ېhn! ̹6'Ol+GLI(Rw7fncTzKgUzճyO3;Cu;0-:j,ȂDxaTGYYbO.Qƛ`cyymqXt-:jz>ӡ6QÔ>}z%tb>zQ)/MG\L<15YBFoṖdcio[+Ui9;p̊$meg|y= M8g2=%;YY\72=,:E'vdUZqe!:/utYH{QFr=75IB9}&WeԎYʏC;7Y=q ACWUQJj]ЎQ|&h:)fGmSAd3&iggrqkbzfXbXhO&Iґs*D›‘0NV'DH3"T9 qjt$\F#땻Ҋt,ɤ ˖醶t|nW)m +}IWѯBCjA9ftdd|+`L䎇$;ֳ hT^H9DeīۑrYXbU; QkAΤme#Ďv|ccŘ5QCxm,h#٣S4k kxeMGϬW'2KwLA?6JtNΤ%ٟ_U5C)^vqoRudѤy59+ґSxgW]H&Gdԕ(`p5t[J"|VE9I42E:l2u9+}$WNUR0L3dP%A# ?iv肉2V;[\kNziI%ۡI$e:$)M5$'!Jvb><7+avњov\ǿk;U3m 6;jӢihyd^sեOs眼*Ŧy.׉;Usf¾=gG7^=9j6vn|ؔFս#Z9=6YJJN-f6F;zީ~B7r}>lu)d=Ҹn&IʇhDqxDFx읺\1юw9C3w^Ir>E =h3y2xL>y7 3wqgfcXSxئ|yy\P$ήjv&éʔGX#q|N't(;f +2-^cٟ'Pi)bJwkIG8Fgjx'*XCKB$.?f=1?}6Y5]5Fx̒|:#M$cU˺̟؝#BVG>E#tĪ*?}тH,jRX{6?ex[:4lOu^bkP(ISʇfbf.:|fH2}qډ +aa +J'IIu2DKNkT)Y<RwqaSnjcEw,\B]DX{3,3YM(FfsEttQ^dIt~JyɢgaQGbeFSׯHZT")Lj1iĐ*i鯎^jemZEFR;XB4R5tv[\s&Vj&b%>oM`)3P|:̥%itPwuN\V3ԏ$W:21#\YEBRDmϰ쬏kSRJ/KZ[̵{5cIѨvI{fTc1H,3є9̿-GF4tˋou=ovCĥL&φXO+YFmH7U2;!l,>;5DŽ=^?<#U2[CLW|d/Xz)3UIg6aP|hjӫ<0OB#>)}ҘݠQ˪m߿L:"t}]Mdس⺉L\bHiH9(uib]Z֜Rc}دYeͰZ:Ҫl&sH>YަlC)UQl$ywHa[ Joeܪ&oEc֕E9˙h:/W> O%,a'㳆oI2T=t͙d<hBK$ɛK$gPM)2It\dY9Ȍ7dlVΕDXxaX v&:c eVTp<|jU|"2*s gXdn\r\v5Asm)2Mt^}:y5l)Mu`z7c:eC{u>JInQϗYK,Äy*5y$o?4ܱw\DYQPR񾏐kejjVnyoQSgue-]{'qdX)5E*)Mgm_YS$~*D]}ǖQjƅl*9#\ :UJBqv!9Y +ʌGxXh[V~*asATR&[cUKa52TyrL̺0rH23l*L˹e*WV+{4)YʍX#iV79Wu\uI_;Zq>NVSdX/)׎R<Tݙs,Ǭ.rjr|Tsj"L+\C$RQ  ""  (HpA8(_(/-486"'^ \vNs#E.:<}GK;]6#~(>ƺ#m>&7~tM!˦ٴCQ)vѲ#l_ϮmB'qh4#'w:COa^kr}lE¼W)m7Mꎻ$L6m|))Sf卫\ͬ?󔹛&g)æWAWՁY$du;&-b5V{V6_R:.bz=++DnyG_8>$;(n3R:N8x)&vG;rG ^ꠗL'F; Vf¹g&cONR^{B*SrYf -lSr&}`9>"ҹJV7#UWRND$XUe⛵I-dV9:0=iNTPSJrKWBC|-[fcW˱\Az]s4ia6*BL ZF'tL>D(cBv6y\EJ2ns4 +w"Jmo uu7Qu4XW/W4 eXf5|yMgzXVo&MnlլQz)[e,yR?$vu< c˞¿Ɗ\̼-<dC93tvRH(Gv3eGA %ΙΖrwY$Rvc&MD8;6'E('s ˈ^hV\K2wjr4|gBJ)ޠYg?ǪM--b-ЖXo&amʮ)I k[2sCvL]%Q'2!FtfD!kYݓT}Yяe%; [ֱٙ9KVIF6%fT+GTHRc )H @4 QL~DZ4"1Gr!c L~LjTaQZԤ,~ypSz_¢;E5O5E#a/-P9e -Q}Rd%O8YUq͊pMJȅznڜPB|eWQ>-=k|3`I +$ X:WZUuUIJGhk[8Y)!n$Uts LGpGDqoAW yȘxQQ}y"OT„4kP7U{BU܈ҡӠ'L"~*d5i +Z1Ֆ6L3AA<3rFQ-[9- k^8IPd^Ds@C̒=Zu9 {AX8v;Nye'L). ϖ$,\6XdUٔ媗I$*otᨦH)slzgnyq($?U;_c>GH5RѝkBwEŬ7f %%A}B[ M?mLg^ XO^nWt~۽ :"bj] ؚZԴdwp-T4aYk TZP̉ޅ +đ:CgIn- P0dܨB>r'&Ձ,-@ !N|uTI퇖S(Jҷ*5\{R \[w6^/YHN$h-VH3vt,6(4bUK^2|[L2aI8bu$Ǭg㠼ydKK *@Sו6(u;QP7$ Y5R6 xyH %ziFbƔIm`ˁ&f $buEIq"ᔚ@]OP&+tRZ߭z2~dEH8wNqd)|i u A[4T"u3]o>er3Ѣ|FKߦ0*##y;)t|* AȃAKV/,L9c:@۵&^$=VuC8AV|âwo `Ea4pa`4c!ϳ{R42F5pJ C-X)F:rmOo-Td)`S71u(6>\htK M&B풠р:k\Raazb]lRzhoqB%ڧBozp1NDE5Q?to r&r6rj,Q +x0ML$ALXvJ@܃5?{;&}G;) V`J#<- 5V1Dm7:I!21 ?Ǟ=!K_{:ޮ<)~o Џ߄{0XbfS &mjڥ$,VjY%_Z$پʤlo%svl_;g|f)dd Tӈz=ڏșr.03#bl +x0)Topd2D`"'cjmAl&eRYIr9$[Xv+&y3(53VhATFbJ&0]9c`fO<a@ +Ty?sCNunBI2j%mCE'Qyr6գ, Sw!.6eB)2fMJ<|'|8vm~^m\.IӰjeQ+d[^ v< +bhv ut? 7g 2χQ>,iPo+P~" QCW%j +eV}9lc)H`oE0LP U:P"Ҭksbf-ᦲjge;nr!d\%Ł1IWj x}\:[~<(NmrȦ #Fe$}Z568".t;{࢘:׶a +=*>0{ϛx,+߲nmxi517nI31?}:604K,TތFZY3|`ZwGѴN50 b +iZ} +9;Q&E+%Vy َՓrAMzo lz67$q o=.RS"yWO|LFJuOF*yT)FHR&bVfc-ޡ0B2:Y:~I"8O9S<̱ hO7<Oa̭-"O4qfIKv*: ~ 9JoLM硠^k3x׎Z0,ׅWAp:`lk%xN'[Ww_[7!l.h't%!8D_u'UP^pa :tXv,{;lˆ!!kM + +ն@hA{,kD PP`?x,•wzi&^.FTmzEi3E]4cAf0k̯ XS3E\EWv};范9zPdC-P4۲` XQckfTa4Th{WA URiX;;J|pE#gB ~s .%b7`ؔ0D]g\6\~>4;ֈi\1Ip +&^֗QTS8u>BEpvq 17/ 2 twF([},O7[SvOEϡ&/S +* ą&tO ϑHf1Т:JI7ޞM KꥋxBҘAJknY)=OMwuOk6ĕGt*`Z24R4\PH~7WHXcF0'2#|RrL{߱I Ta4}[,#82 !=1DgՄo皖r" :Hy2x:s[}y_F.Sx'7 kI?G̴qȩk\a~$A-˪%Z/lGkۿ-ǚv+UhsTYf\DR N\2!'-`/ ZP,(j–&q WYBj6Y whJS6#|¤5n9I4;esA&O%TM ~jlX?\ozT'VOd~FC[`~dC#GG>.Z;#>&CЕRF.#,s;N]*h8gr׋FK?kis( '[8ú&(#}yդ ]@d]|P(\2/PVe*,=Χ&99m"4w>T0ռ,,"r_yS]8H@^\JdY}ir_cB Sm: >C1c~>ebEg('V6MĔ)Xz-̄v77Pe6~HY9DkY Jp#)waiXw5u 411e6eqfs VtVC+qmN> 24k/rBQ+CIpES"Ac!uDWpJY4v +#g]+LUs.KHu3,Z9Bo;W ld"諴c VIteрea>3KB $\9𮢑۴i[q(?#/֚.9nq? ՗Iss(԰"X/[%owŁL\ǕdCH߂oW(9t-H F^GZS"!ҧYt9[ <,0Gm+\f6ORP \ԇеƱ3K{PLjNh 4{4`}`9Wp Hvٺ3{͂ Rhy]_еqW2 QׇY&@Z l\U@ +^vj&RGа*]]رxV',XN8)Eݟ7bzveA/9s͡=.Xu8/yUE/FڊmeދKstN;X r0sIe:ذ/ftpm=a'!% +QK΂aOeêdD<ړj$EnBi $H6.9r.n^D`,bGcN2ԕ$/Z ^Ġ^6|Ya(<đ5k5Do1/9E dYP|C,OE%"Fg͎OP%m*E D^g[Ʀ=Q3Թ,085 +C0=(b*ukښQZ  hަy}0u>_W<c*$u|0 +HLwquTW71-ͬ ޯs Sō}˪I_v1-ρz2b#[/g3.J>A--]5/`L50WH*O-Fen\`Q6JU^ Yvw.o:(H ?fBvص2~ CןR[|vq;։ Nweln- +Ѻ fpY6BGEli>- cȤ5(3]Zԓk{g;jyMM}2)((:3SԔ"![w^vc6 !X/̣7Mߒxa (y$9NT@p'_ .4v>ڈ*gΥ1|9 Lџ(VȈrQe!b1$HypMDqO$Fws+Ncvae3x:>&=G[KQ|#g\o6Er@/ ,WݻКEgfIM8Xam[&^#.,U8+W=D@.2ެ0~82<8+[hSHt%}( i?n" Gf[v 0zѸb"z/g="p DkfH ӭd. 7f lP0{xC 8j2ȑ*>bL$F fI>9* X IEAmO8uZ(X-<|.4-%WT |l&.qWҖ:$HhRG^cB!W?UhI|n ZX usE_͕R>ZQ?#crE^czGN0BI#bFQst*rqa iΒ7 _ R/Ym\-K3 47 Sl2lp+|i=<%%z[5oy+(zn#c#ݠiݬ2 I Q}HˏB48,@Aۺ0G +_Jw`')OBǰ Z5*[B }-`\ i!,:Q'%QdO5+.s[6fm/f{5J/nPDŽX~= *4_FW4>^+ͫib@챧ыiE=k"Ϭ߰$s3Yo_1D(~J᠑U5]J3 $"HnW nCO쒩EZI3ER11%{ϣd㑁2+G9 }{K#p0m -4,\y(`26KqZȑy ˲H:2dBU|.8ʔ)xZl#(68NgAN !ž9e#OwChT.{.s"%濳}42%޸S{bcf'a7\qD3{)uXA@!+ `p{@8 XsaixX1'iK~L$RŤ[(6q B5P3撨|yTBMnWM:nCyBࣷMsO3R A $kW + +4a-"@Ε) C0(AAV; 'e[q%m|ɺ)upe IsGBEAR[ZwtKLFk<ըHD 6l(rALҥt#gfʟmyJ9Gr$[޴f:!+ejIb[q-?pg/zh_ᑗyUON:a]<1L֓ZV#Z,+oWMD8np@/1 +*Ii\6DͭRȎL(sD`#>`q a)k'҇D +S;T84J3ayVg[O$z%/TfYRbKh~p@Vo]93|,S9b(Ƿ#`4`?5 ZwV[cA*_7@vq~CZHҏ}mw呠xً$ "YQܫ'. KN-B#08%嗳\:R?~oo>(W|ʠX`z7;V$b&+#_.LƚxWnHU5}hyWؤ&AljR5AT4 ph樲=l4M EtZӉ"Ip7s{0o=;Bs/6Ujj\ G4͌[USGW2 YXp NvRpX̎e>u!X SdM)sP|Cn; o,^^KDe3dDHh~59gK&Z-?Y+t^ajgBa(G$I"e[AH_g&SLh=C? g%A䂂l1v~2fÖU_MȲT DS {϶b-6%Nb'=,6 sP!\ԿUw = z7/DUtEr.$0.11(hزّ{l9&E[0F%T>dɒXJ6"VL\`9 9nq SzB2Yi3@+ Efb俊$4`V +gܔ2O1*s3JkNE yl3@"4T'A33w"{FYFJ$ԜА.1kBe`rՍf-i~QMp^%ϥR[`5]qDQ= $o^k6j>Pʊ"BgfTsDG +ǹh,}R3ɉ] +y L-grA4w! EOisɺĐ#/Œۭլa'HCΘ̽pFQ3㧃s-Ԩt4)M}e031Km:_%`<(>lݜ?/l=6Im餎[<JJp e ɶz=g#%ȄtTQPq򂔭>o{yF׾%~lM6@@e#jM~̹m&r"|{W%Tb.~ ;'x0;}Ɇ)HZş#NH״~:֏A[vjJѣӜ>F_'/v{ԦKÙWni8SvY,c +.xQRyvt 0׸\K#Fo21M~:]7‡TXL +z7ΡO1UL> ?%崄0eXiP3=1{c*zbJԄL<{[?I+.u=x/>I LhDBߡnEtB.ͨtQD#V f/(tZl@[Pqh6|}XWh98_t O3Ye M4in.) +_:#*H[E1Ⲻm3pd_T1Li imKղ/\O+*dKx42wZb tz8q>jF6_C:YRQSɼ:׼ Z8R/lz +'<mߢ)3MZ5(hGGϕ GGc@8u\:5TMm$~la#r궴s`(.oNbg)m,3܀;~ЭlT4`/}6S]$*S XItM +7;ݡ${C˿tzw~ cS(laKuN[!<Oh'!@X +O6 zعhDIEM0(kC>ED g=G!E@PjV@ 8zu\1/#xDV!M2&BCZ/uT@) +:Hr4B|˖P 4gʧ +5Dl"RRoMbt ´"Rs(t2iK@ghOH%ZDG'Ԉ0 (a[r"W +}=rTۆ +aiHdr\,LB,Z!)뼧qBoT}qZ8)Z ܻx)Xv b qL+mdZ@yeZwJ s@kj+ IK}1e[mEa|E<SyeLFn5lU;KQ姐=D@$q󄦨Ӆ!qZO +U=QCn>I|a3FTQY(HJqi蜳2OqaHe^U::P=e3ɋH F* 9OpWM98pi87W'QX `)r"mV,@a !%8@B3OLkyz$ye=@8<G~6@ț'9}6 :KaZ9\;1m]%٦Ts0{Q^ig +w׺=wTZ/ ;B^ sE!TyP)amnz&`پkbOӬ5d̾M^q#1XY91cM9"aDb fRږhRJ*:NH -I@r+|,5 ~)jrܱ;%_u}?&BՋl%m3)TM!MiswGǴK"%'ߝ4e-wyP2*hjz`a#p{Ӡb%$H՜\蟅[-3*`pVp5WUv|C9*9HzdR 8SQ;hdPYh"[U7-R{rQÐMs1=5ӧ"Zø0JB4cdE n.?M\;> +nDY+6c@.~V:Q`%q웡j.P Kr{aV~cQ@*/^'ڌeVY6dpBX5fp@xc(-kt B +5XZ7\.a8AjOC0rG43OY_(bGΒG8XDK,$d5~,\( DI!I&8tg2Pփa)gu%PJ?ֆo +yWu䫄`"^2i,uK'j3"3.$x0fmO;(6,Z@tS.EoKi=A*ޘI5Sas8pM2>:~Q=cK +T~//Os|8SX2G߸pJ;&w<yri.pNker$ +?\Ӯ +&ﭔ-q; %i#:-hX7fǗ%GM|i +nU'avo{lN2h&>-"XXkfc7.Fe5I69 T׉(7"QĎ"ZB}/6Tՠ΋s5ºC *zBg1-roK&/K<rD]d jb{c{m7 .Q*Zv< 0(LE՗xGpԤ%{ixA"|*)4&ҙ?6?"}8"cbik$)f6o_@ZC} $m;%72%C?ݴ[䶔P* ,T 0n^6B/  v|h_^Fq8J%hRvCUc jBD 5nPz'[-GMcv^[M` 7Yg4BܒΆK 0dVy}*̾^xrE$|h +"Mw0Ql͸m2.NR嗧^'gIA +_*95`eV h@MȱV;U5,W ,Gs"қR+??*AtI\=%CXJsG'|LW +*@Tt³HģQюIq*  +tQ]Jt}Xx^"ŗUVҟ`>M[otw=t_RO7؉NqƛNfs1dM5 BU2Sz!d{o'Zv2Y)"ץe{\aQX #iE(!/RI$9D݅WVz0I_d" ]=Lz?R%z~QQڈhdT,dD@qj/hW(>`bWDz :ʇ_=q љ IBƄ'r{ (ѓ#UjJ2NԈ0#M !Ժs+1d7.*UC_1%EJgj~˵ߣ@˦ B+<"«NTRUN2PQn$C)QUNg[BzuĞ i =u2u r9~5 ep̟vQڍh5zL?ɶLҼ87j0V2XPqbֵIz4"kh %qM,#l܎sQc-|h&qЀ؀+ vVRi{x%ZR+V)^KNAb-FHl&BN[Ύc>;`#\|Mfm4o ã`}qWr~/DF'3 P`YtT@T Z ELd%SD',TMĄIՖ~zu8$zY{ |$CJD{Æ>[꫆cSccewXUԔI6-A)E#*vzq)bz4 a]-Vywߒ6/~|ٷS0e*> -EX, $E+u jۨȍsCέ۹U#?ڈ8"WCk͂jCL.{sft%0|KxĆs} 2lA~_+cZA^ +G+ +Vn,f{@7??`d ]/>{o:p=aQE$ɘ=Er^c>r+߹7Ҁ`.O- \A9h+._me#&^A[S'0y>^B7( IVsKɅ<rԦYSGpVId6!Ml(AK;^pS,u#D fN1W2ܘ ˭86L[-QpsEHRQcppof<8B|6tI +N笨d2_+WҸJ2RAaC:^"謵y2хh,~"HPi* +MqFƟhpybՏ, ,igoysbz. ;Bh.242RJxVݘ !Ь/>L׎uy|YV,^"tֿ[{}f2I_˓o iWwHu +"GnșHm]:p3&g#uwR\KȿP4 +Zz9T +"&F(g1}!8+4Cv*CL!AoL`#ٮINOIi+P~no1fI>/2̑R@,I}jz!VFs1s0TiYk;#D%g+1wt%7;&KN Q2{]C[JqљX[^|`$ +hO&({!? .rvq5f:?0=2eKJsl;H ci֐Qi{ٶ#^FnNcjfSs2ndW殆]*y) +(ê!C-,02_{ |F>ߡx4R|q6=5"gE"sC]-C̽(`81wnhKmeNsyGk{$bȽ֮eZm1J*y@<;V^Z䜱~A 3O`S9EA1jp|_3*3~( A\UI!!rST |(:f0O&;\Pq 1H#%"?eb1w}r#n>V=_ z3L6}Oq.-j$r4a+^ &Y&~_wBS` {:Vu)7,ҠX Mb*FE_b8"~h\ĐWZԺ8|ԚcFJң]LL16}t5;_y1!`| o +~CvkN'aC F Ka^ي{[ <\6 +>R1C*Mw/=T4!-Ǵ_PECCK|{fe&PмCCb*<&c0Gzo7ZdԣָIbοTKS8VQ vxҌ'\|tDF?FAVaYXI))(:%{{֘d&Jv#\nX[ylW@+8yO~hiO8ЦSLtSY +RwSSj*֏Őb0z'?̊Vx7*ؘ.jA:[ANn!e|n\_ZIJZPVj/"ȆS1yjEly%"u2=m))HGf/MFRY_C-P OPCl̊IdwKh:Һ g,LvZWo?%%ףFCa;J5ũ2y +Y,sG]IS7JB?$N*?oJMO"ek4 23v30Ilځz*.agڜWh0{Ȫ:̠$s?` QYR2/'[Tku"|ygONgh 7.FgXBKR}T~^JMu7Rť/U|U{uX0dBR@5ED5<O&7n$|Hit=;|hf{ߛ7.u箈Rϟ2boWRG>ہӂ\vs L-NP]Gt"V,9-Ѣ΀ZS,o|?GB<T,jтa|o߂i(~^4e R8K4YYC.ZgjUu7D| ,b qx};1JV:G 1Ϝ 6Ыq/0Vi.͂73 +dfx ߌJpx[0n.k;-b@ymRgl39r5=y pˠȶ0)h{)w&`Wb +-]6{.kѸM-7SAtf`*2i7` !KEvs1fc]sD%w-w6벆сq]x=aK`7;xN"iҸD9'zVoɰ/13O˒m''&,K6*hSaJD ϭ;'S d1f2,P̐4SoԙhDdJd7 +#@.?ҩnE*so[7VKTҏQtK8*4|8=\’>MB\e**Ǎr_'Mk{Qd4.a uߕH;A([h=AS:Թ}C@Dha>,djIfTi':| Q^_wȧCT=5r1ǮPySD_#SDshty =`ZॉoTuAx>UJQ*'H̓-Hl|8c+ !3v9 +#Q y؄ѿ~H( o]1XBZAѢBUV fE2aˈ]ьjᨺS1ɂu9QIwyw::ڮ!2QL/{@' ZGlaCC=I7?CaxM36]B$zaq6V8c@3)R̩Swԕ؄kӣ/3ϯkwm)H&67 )lXO\tl,ın\2 ZG@Ri@yba 'LfEK*AWI!7"DG$p_'v,qlݔc \JnhdY|n"%Ag+RU]~Y_r*}9 0A'4dPԳ0V4 eIP;IKaG}>xu$bplij}WL>~ +d?'}fD봞u\s@א>{[j"azu 3~ ]i<5.;"#%LC(Q!WμovdSIm^WZ(nvoܯe{Q ?RY clIui䗖{ܲ= } KfqlTёt\N/)M-$[7 ~3nxf_0U;dP.N9hZMegbR^?<ț1*A;UCg1͞4{ @ ޢ`z],ԃ\t)w2}ǛT]Upy˦tp~ɉ̎yt5$[& w OpDt1Z+bX +[|a1֬mcXUUe)2\2sI (J_˙1}bS͕ImG]gJdҭ}whKsj O TQ!QoE xѥE-PK$MAFf1<\GUbıo8 A j!oE&oiUz +P*b2 O |E7I'UW cTP2Mwi +wA=R_kA0 + kX>& d41LBTrB{O+"4yTɑ\،ҩFQ K> zzC?{v#rU鄥UEmf] ܳYkk(݄>m'R 9^(+IJ/v;tyC +h) k +Y?Cː]H"IPBW2_T2<rTH<(y'K_4|3FDU/h5u)[@Hb]"H\ku6sM '_VYNw1.>nFw1&XiXR +O6ٙ-6dܕT}{+k۰ӳ{0V@σ! Bn,rtT9x^KCcZG/J'!5 )vR1mDP1@u+i$#%:ZM򷊩U@k boz/N1 Lf~VoQ&2oUS fğjǞ?ZQv_wQж 9wF( +q @mr1$%ʒ  E c7VQ{=^Q\؀%ߵD`tT>P Ŗx`*0Kf 3 JĥNz!ظ +ljqo$O\ɯ):mZW&(QAZShev +ߊ*=Ѻ)Pzh g`` A'֠P:`PPJ$ d\7ȌC9>h%zշFlHPE$/=~&߻ELZlea'FgF8wq6!7OL]@EДC8_ѥeUC@"|.4dQFlu`/??ܪT<5M96nxcHp-x2&q:iTݔލ +f>cWBcy{8{J*qE/t8Qk:'H@H7e% ]lf <$rBuy_9c$zn05')SM\umiLqZI.0!svzGQê[=Gﰮ8svS4A Yœ܆ J#bi0]Dզ gR rм|]LX݁eMm_V)~E3t*tugFRS+c5åǦ`dSf&JW~^]ow(:?ڪz Ja~R !1ȽWIDJW`M +ipzx=~L2ɔoPEobEg^Ô`Rbx6;l\O=Bh#V7 VAQPmƪcq"*byDٻͲx5U:JR9ѯ;;!UXUC)2) y& Q} |pFәṗA8iC&UE5} 1hlzX旇t|ϕ  2A&P4 /h<Ճ3ؙ$?U.S' u3îQ̓9'&SobydDz}ӗy~zNYF9*#M+q%C'7 +e"D2.34qa̽ cY|fpD cK,Hx&@̇Jʒ5|Zqy՚JDJIQQDيA)P?%2]9Zsc]=!a]&FI򰠣qUSzcQU{n93.ϱs/fշKIPQ1umSC$\8.-'kڜSZ[$.z O. +%U #WF"AyJJ*()6UsBNߘ*dPh@% +)C9L}TN+WC(׹>5*̙0N>9jxx^بa»֭3yD0qhIm2:IeN(Ȼ;:׼\& 68Oe!OK{ϮSE#MEpDC<%FuܐҌE߈]qrkAvjziS S5kA>v6 yT')r+pt=LBeoM(52r!H2"SBx=dЎ6ӮbbN$gff,ԩPn8ՋD3:4C ӬH]hڼ((ۈO*wSǎ+jC&Nz up$s$6UGsiNKZ 2-3,X~( N 8H$ԷF ivX¦J3A2r[`V/Dox'1)zu!A443#'aFȎH$9:DI2ч^Yj,Ԍ3֥h!F?EGdq[hLRufJE7\K66D QBB  +3ܴ"KKC #J>QqvZ-r(5VxmV$d*Q  :@UXTX +d.ӮEhX    џa) JX)iXE$8DZ'Lia@($heDž 6 ^UA$" "+Aj,Tڅ Ԫ:XHE9U!=BV +f# aBA, 3 ]%?/t;C 1DjFDz6$]h[ڶ҃[SP5Sr8'0 +ac202w q yVQܲmUS[EE~4S2 C5b ӑ +#ܚ +˪dAԑxڜsI9|G2C;c,INnYLKB򊖒 F#2D#hsC$-甹/4B*jRTxQ!} XnQ>w%iB|TԔⱑF fn=]Ԅ D>b;8$ QyfT.T^e?wCg 5'>0p%d%"M&g쓤q*O4Uñ1E:?koc~BqhhZaк SW r*3PNSc h.矹<\F5{3;ASwIW5gQiѾ \*^8VIUlB!NH*gbbSg6<M?pfF?n̏RSb 㵞($NbbEf7Q*024˄" ͚ {Qd# kaM B!zbC,)dnڶ"7M_HJJԉ +E8KtQ8:V=UGyAFK +U$5Izy3E.p :vQA9e9t5"bDZ i+h^)ح3ƨƫ#^3Yx)5*}4"Ti!HKSTdµ,Coc.fY3lEa6eCR0uÌbHvc:9hȑ2# kdE*қf%oq%C<*xƝ"UbY(ԢcC' (\QXj4$f")y5V{JÜg<W!QL\9D]^-X*NDJT_D+$(3+##YH,3TlXϘXf:!Qy-3T(;D$bɇQF䭩@"Aؽ_EqD.wٰALcT|Yݫ橏[\F +ϧRcCSE%rcSs4Up!ܺtb|hrBy魞Wk:j8v EHYRbWfդTkC½쨗#\\-ªZBYɌd6k\EWJ%3a%)hCm2+b'vcID˘0+BW.Hg#cCtXRlHf0 3EB(;QhB9$VIRK + nHjpԃH$ N}>eS ҼFZ|JS Zi{*.%U#$X4Ruϭ55~F.y[߳2u'PpaSyyP#SCN)4ΈTItq*%qj j0F"H!2FZ"|E"0I%Ä4P!I5.*>OVvܐKպEFE "@PFa@bIdxBQ:#‹4UW[0 +Qہk. 79Y> muT +UYuG-9EFc@CBYDi=$\[OSN<FLYB7FfKr+ZX +m-aUDgxN?s1D!! .fBTMN(T` X?`ip3ʮ?j)KMYjF$hVˬ-SU G`q(kFj_1L-sX6i&e+a>3/JJ8䄆D/j*BMIF8KIMP$PP@D|$pv A4!r#|,&.OO\Či*W/zVG]"a`@9 +Mp.Ë$| 1R}зHȈ|BJ*@V`+  a`07 d# Q{x ;qdL'[}EBl8  -dTB'`@ C|t4@C"1(*Tdx It&h2O'z$.e阭̎pg<`NĂx ԬOBII#MPҞYD – 9_$B,l&ilKBN粠*Z7W1'3V\=) >xƲÒv$ܶ& aJPr\D9A"' lJ'z4v}F8 +Jd{fC5G ;G| ex!~GR}5`İqv;M3X$ W"IW |`ˢ9\A `1Op!O?+zW[x!vAYMț(I) "@5C7mS P|[ mgjUjԎC|jflWzKWZb6l7wqJggEzlܼ)sHBd0rˋpwl"FRد!sc-f|]Iqzr〭K|,I }T́G&&RR8H,l| ]I1 Ţ`yF囐! I][87K(藭>WROUV;f!7cyZ€]Z ͖u,m nEѶϳs[ybnd|١0-R@r6BK^Hjs?c^|joܒ1L[ + 9rWu?6j$?a|C_jVm`XkC1}<֟٠+6Q +@Dz1ָ_\ +4잷#5WpG?]mъ&]%=s_K< ~hRwA?ǨP-]7F)Cս'V/D2@*_9g$}@Aa{IQl &JE2*tͦG96}fHTP AX.6v[4Ιr{(o@!^ɇ2ա1aʬ~r͗'99|` _]>o8}3z6Ŵ7 G^ +$v =\Mht*P1s(z.?;f.l2 .B߉ُH h#H0G o# y)Z4 z2 j (:OA]n?πmBIG:u#:i-1}DWD!+`-)39~w]j " ofֲ9M!;L[Fr'\4'!ỲNUcō6-H>@Z!GIui#TG<ɚ3YwsG[CN"qF :tA5:V_jz2< +N\"8iF"p H]\oW(σT CG3lw5&(A0m#lWf'hx2NpE|2ܮ)dRe[Si]i1=|jQ߆rC (7y.u=2{1|P鰐ըR_G(3z5E&K7}G:Eѹ&..<]MU,oghvuC,`Gk|-@z77A`E_"" CiفS`І..ENPfgс?<Ģɧ^#E[:Ujm?Ny qX~dG<::P(۷p<#ev9 HEӨ0g4KsㄴF7!uA*YJ6(zZ)MM?_־6RB94~nb2eȰY38yKi<.jrePL+&CO O]Xc̬ sZ͌;1l+y-3#bpx [uǙJ=%a@ϖ`=M[ p*3Ap0c$HEn(-zb%wƆf-/;<0$-_Igv*7ܔ@( ,_\_Dx3R}ܭZf FG&@h'<=d0FrBӠ7bUg9bTT1Bc7$!?Nc 5%0I+ݻqK7Nߦ`Gꓖj]8߱X"g2ފqa/&LjM05Y6gV):FRkg, ;x@7]JzF^y4{Ļ7\oAΥT/ +imŠ$tƦ#ŕybXLO/l߻`(L<L20$Cx}SwUw%$=E Z^Y3Q?QM՘SG2գ4 ZtWr{R{mRTESJ2>kaUZ4غۤlRh`m:5KQO[۝ncrñ\g?I4FD8E5gR;aBGiDӀFPuXաdz;VB:灦_>]sp]:z) dcX3)dF`㯟zg>)s,J$E¯QA2'W}>yS'Ø?EV_ZyGZ %D"yxC&%y}Q0&O=_RI;~JDuX l-+N+41T4aEәՃEBōSzBᑌ~BU'xw&8@XLz/,^ʌXdIxn}_ن=}1o(׫)L:8أQZӖc-ͣ7\ pOc|ciǜ|t'Z ]2=pEp>TC܀pQ5  +O=xw/ ǷgEcЎɷ_ZWo=j~q7q:1k,umƁ\'#䘚]NYSb5̨ПN;,Pїz~:`5uh ڍT p=sj%C(Ku9ENIB8Q%[UE8b>yPX6c.Uuخ e(]~-rRT78b@K׾HF"űo?YNe%8TĬ\Բ! 씾׷bWbai肂 n0xn](,ֱBD b֦ WGȂ 2 +O۫A A'׬`snjPFpY?Ϛj3£CJ +MHrQ `xO8ׄ[`BؼܬGV uۥG'4Z :H{D3Wq&~)뭒fژs^h3⥠(sP@F9:1H5%$GL2<}n0Lqy߷K8}dCڸ^ӵ'dMՏחbN"` fY%<|PIS $Lr;%:K ٠QYnr8%#bRL `WFt!`Ȅ@uXԃ_S^r#3#b2~pJsp22%N=qr_nc$Sg> iD¬ӉRQo0k2㷣>(ݙtPP)Nn$4(i"i62rp g[x(O(s^ XB[RsG$uyh` Ԋȁѵ5)QN~){BWRF=wXvu%> N*QMgi[Q{vlC!!^4@]&&? Im0P~m @Joʚ!dRr+BJWN;@U=@PR_@/{-bheB AEX$3knoQB0u:[cJŝҩC-*l2odCɇ9K+W0hL5 -tI |̰ir0*kt؝-nz?ɠ sn$-<f4}V!PCOZC"O=H%pEO"!RrXiMS [Cr#Vhy@j0-5fJZhb<>c(@iM4887G/pGu@^H"%)iAkVXV1+^NwQAq +(b,.FiSEcUH1!?M7nAz\KZ`0m35V-c1#u݉s ߒI_9Bļ)֮4՘SQ_FI:b#O FG?6E]>JW WНk{6 [ڣH*Ll#Y-kn#׽X(48š3<G?]8JA=FMgѷ'LY/& >Q1/-i)Q!\K* Q *P j`(R @mA`2*y>b!ׇH #>#2\IHYExʄ-֕$*s>$ƈ]lt$8MҞ+/|SKrS# U#c" nzg²ƅzDSTn-Vl]jFS_TQH!Y;¥JzGX3 #"(}->@7y1\z(Ƭ*-z +{~ eDE~IIS!mT,]k}(( ٕ&OCHpըIGow ɐ*Z#hI09

EJ*GU-m-m_$ѱqg:a< +1b#^HkYB8 i>G;ɓzX+\4b7f"8LA\fro!,+x#"L)"3?d?'.xu$ dN =ǀ+eINЅcƯr7!]6ȵ9z܅ lMԏpxyY4d82~YM!殭˸{5$!ng{wÚj#%()ζr/"GmE Xgh]QGkĿFT.ku/4 +9۷&6\c!.` yk A΁7 "kDwD%VN9 ^`z@U`nB eɇcAQtT +ؚjGmM[H3KJs~\^wr49u{Z0^1gA!|8zb?q=^H,38]?Q{?@(u8 HDo뀇#п"_ZC׹[.oףQe+ 5wBTX0#Zmw~uEuŅ KUO?dJ/fBs@t t^z)<eTM*k=tW5 B!q0lz*x:=E0Ib3ٸj^-;d)ڦE:^bU4JK^?p3 ԇ#pDd +/T}h5cIGV3** 향VT I&Zna_ȂQ  H˰Ԋ1Q=*p%oIS;sPzU9RҜ@c2ˀ Û h4y@ .ڋu7 W,zSř1 '8%x@X)FLr:Dt8ڞܱKc7Kք .-4?M'}C-@Tm*#HalW+\G +H'Rsaq,!=', I[G +tL.(B\:/X YB0X 09 &?{;ŀhcH Lɐh?e-Df(#\ /eghJG \GwX+Y5|Հ}|Q~Ӡ)Ɔ{pIt<1o u# vh,ʏErFhX7s-;\A8ѡeb:8ѪCxɳCgq;xjFJw?<`YF,0pޚ,c=@F\Y |ȅ_AlG" 탓Bm@^i>A5?E筈v1C " ćS)"Z+> ";&7DցD0NC0QC  2E"q̈وEn7D!"3CFrCJC?$GC$zHEmDEQa!62$sq#q( liL$h<]0%j!sU‰c^ +,a/d; 8"_aSaQHc =!\L3l5&&!n8ᄊ9lu;QpxlA~MPAX PA:Cg=R0HCpA̍ Ha0IySE @Ȑ)89MAeP C +OQ"*4CHF ê\kʊUX@NJZ@?Z+XW3ۆ +G+p?ç +k?Hh/g~ha-Zթ-u!-c0 LCl\0P]>|>tԺp[> +VmM|pA^ +V&f_=E{Āً. ZXap&D.A BRQzDW]aY1~F=c@z-@c\hǢȰ<2 #2^+y@2x(aF=I!3X0j̀x3 ;4 }G3zL@*ޡ(1h@;r\!l;zHZ;d@ *c;쨤~dl;l5$[ak`L7l|:$E"0 Xm:LFچ#ַӑqLt 6/a H HS^$"::5Bϡ7>9( فs4A8a4GqLsH8Q`⎃!.^9 yrra)9q~LWg9AaqspⰑe6.e&EaAqk1[G u M Q9p K.bQ)Jb8|]?%;'GFGQY h::쉟 oH5:NO[dZAH7  Ȩ o/E:nTu/G_t4+* +nX6G}FTn#@l)60'g#c@ {~ I>F_ +x^` AY8j,w:]L|Պ5c YAj$ӪQa>)]@;H D`Ӹ@@;myZ20[؂rޏ8Qh$h{%e@t<5A3e;^g<<3pjE-7D$gpDΘo3܎ qfT;&3@ h9/.cU8شxe(X2lHL@k(#Ɉ5Y;1U@P dE|CFÙ@"e(p lPȴ 1Z;7OtGZ7s*㕆4bd^!0Tyj0k|kA}>/\Pak_dszGZ~/*l |q~n/hzJ/\Bl+yT/ +xA<.TnWv!@\0.lӳ C¹H AM.JIA@>,\x.cOf4-& >E}[4!k OHlQ 3($Yf-Yj`C-$ӢXkB쭟7Y57*VCQr>,^'}'ȡ<AOX;u+؉;MŽNL+h9]D7@q&.nI=@@# ;% +$MA2!]HZ Hg"LIeb0d Gb$0AD<>}rSEBHݓrK@:2ZQ/&U)d%lSda T?,*ZK@ ߒבU'Q$/IK &.TL*".w2$ʄb&l!+$&t%}7H&` G5#fyMn, 'sjqr,Y9C)7LxNو#N5"N N̈A;dD #bmzB"->>EOJ'Xy@;J`#[Q,9aB]@ 6Pr7y?x1jtQh#gsS QE Q0R\@%| +IA[;.N2Ye],NyP8Z3j MEd*̀ h[q\6&/~`a6ASΆ>6>06"ژ߃%چl{@6փz?ApyP8nzxЊV|~sƥ;9t7P؁52oNxɐ77` sBF%# @ā1c8(H wܠ]i*AJYd ZR|INpI,S Opl<|3G8X8WATp GIEqA -1!9_ 5N`x: "CU 19Y _rE/MX1`0=s0lV89d0hf#H9!o/ȥ@3` 6cI#ny#jZ dSy!D(l +;H*Ac;- +Yۯn\ + WY3 x T3'h +Ԉ'E* ZTcR> ] +Jw% + |)So5)9~fR0@qw[q'ze &%&풣bL +KMeb%dvACNH0 KL + l&Y,)>V39.) H:LPR0&OJ +FZўh  n' +x 7Q‚LRb;V& +*7meU(!];Q Qjq5G`s{)~2I":QP "#`ķZPk8`V_tSm"x`)u{'F0[|'08U4|{+=qAdbsdRO]`'hB{Fٲ $1_v8dq!qh@^ +ǀRD!Yyo_7++B2Z)`B%$psIV/Qş!Vn3|&5 @wtI3!QP 0  L(\3 ;}49Б@U k >"FX '=PfB ASvu/[AEctӖ +0j.87ow}HT/Rz+dI@a .y15=: s@߆W=t@fPs v沱ǁؗ@+E@hm~J,I6Iͤ% z>I` Y1?Ld\38 0 k43u>A3k+*K LrwP2ܾfz +"}:hJ$b:Àܻ  +jNw!P ^:/-cuuY(Xh~<}j\fh 6G!!5f(ϲ{ i/2h"&RlNpvk( +^Z.[ +0Pv/m4;` +u׶ vZ]# +6u'0s9O'`۱uV: Vh5N *u/k@& ~l C!w6#!0;4Dp]zPH_avZ4KoxftRQ +콘LxXQ>kY'E_j\GWDvb3?aRXwki)fQh3:&A۹ B/= 0jx?Y5^5?9%A _uIKoi"haJȭdΗ(Zn6'7ӀQvG \5U^@5*hCkT*B3_q[B=dRXPџowwdpj\(ҬQ +7 +Y?cM7Qޢ`/ +zwW~z , +l 'B7q~4WDTJ4ٟe#JbY qW_ JchdWQQIn~JQ U|L?6/ ?kv4s`a'~[/$Ìpnpms$*況'i1̶9.ȚX3FPO$?=a 70 nӀT-۽a|="z0\iDQjJH6L }qv*ؼܻ9܉~`W! ÊaRƒ!*-9WDlMBE)DAgbPe# ؈ӽDpݾQm]޲(>#P8"hk}=VR +3Tn<5 O!wlmԾ I BK'<'\?EHԾa'G+W_{8JQ86by$g_n_>8;#GyIO7 +|y}ϑN鮍><ߣac0_DVaXIP>`mRO {A+oYb0ֆ7rmj +``}`[tEɍ%WaƂ6\(-0Ҋ›P=eYT+2\ЬrM$UPIŲ5S}Mn+՜x$V*bz1JGJR&$Hv鳺"0!Ƿ((<%~K~DevE+џ*3Y9C)ޝ^AACqZQmK?56qx{h0A3u7ߵH/ <^t͇^;B|(=Tc|1 2bTQWwʲ} `j-C4PUހ 4K՞g O#Uqݐfݖ>~906QzuIp3#Yn0 Y|g*Kp[ tWCKR~rɒkuՆZ>[$|& Ewԃ>q7(B""QsT05Pw|=QX4: ǟ+\箷hϢA>J-/n%gڿ 'YA4.!/ +޲*_Ro݋H;_Ĥ{A>clKlj{h#Mt=P` gnkl3qK%qY%kמSY=آ-~WASbUGvKIzJ!ڸT%q\dK`{k-ܘN*(iZ(%'.c-Ovm놈ؐɪz1Y8{m/^!!4I8:j11TP ʫ>ǷRr"Uy(9N!٧HZ4x}&=b6.Or&p9 A^7g L!mb8q&<6OwNk;Yɛ9w eZ4"=֟>}M.l+ȱIyXzNmK˧uH!AMسqAO/ ҧKϰBG}շ҈*PS*toH'yC^eC-@ǛL_,k@pHu؇div[t9F7>TPn1 th=5E9iG;nG{wCgBdi7 Π\A9ל2H~HPv=++Y6w uF鎿ڼX5o|}h÷h^9o^fVf}Pd2} AY9 ̹GW:MضRb-ޭQtf{/}2Jw@3c Fh$8ݠn]r?g X\)%oa3q<6%/k$yL +y#iD &s-{<2aҥrR5_}u?AFqPe7D<q$#ta}!^UjcUuM: tvoQA +{1pAWy)T{kA}Ŷ Oрkܞz޹9+qG  5{F{ߔt1heσ:[@;gJ eɴ +>ZuF'M!xpa_qjW6C}$h!wAv|a7%!73,C3U/}{P,@@b +񘾧OP|>qGA1}F Q+~{,Mml]Y@ޑ(U'3r3o:P#<8JEF2޻v74xRlA>]~ eSK7) i27=T>{_wN> +(~uGB01W7G:[t>vH21zDG}>zaSXuVg]3M/3|D֬\Wb!7K"v=IAp?vn8l'}ޘ_n*t 9maJ?m ;#y96RWnL;_#Ptm^ׄI#,YrVҎS2rheT8[A{92ͳץ{5x^9= 嬦]ظ$aT+gDiFp @ck%d :qa8v3\'u録aY0*=^e2>=35$ED`7wSd4Znˆ)u="{e>ŋhn绮 e +*VjdN\SYBk<:h Uv}2D"Sʁl}-a-EY )h݆W/k[{lǁ, vZ +VExXΐL1wPӒyxW1W" eIIwO%FOX=Jwz9U'CB@S  %ͰSO20/AI׺UjQ(գᵨçh A +hC=R-DPo3Y`}Oi0q =n6&,LIMZo`ʚ#k30I^:IBf@ҕ`IY,.a2qJ|t~{5^^!.%1p!]Jo+,=WЫ=z 2'G,e)u!{(Ӊ+MƦDY[{T܅n\ ϐ&]p)[I$;~6Wd^’6:i@/$' +)v!Cx)e>g9Ō>:u@ ` %^;l\U-݈G;_@ϲ=!;YC:&A@޿T?@mb9wcG,rʧ0cdb6X5;8(zy ރ~  KBuЁkٰ/ݲRAj&31u;gY4]-Y +6z +3^s:ܙuѿUeb c.5!GfbcVp;p@<9\2/*!z A]>&9qye|ѵG[P['rY~YBahuʹqL^NOp9惤r4JS@ɳ#\Uä& B16j3*$?5 -bWjX`+\{Q_zo!DZi4#>t-zLwL)Ts~7(bZu`d\9 -HuO.rF@N>n1I`Frq8Z+}FEOx /?\~jǯqaL2q?yƛsGƧb + xe*gdq琇3j_ +'?G&+Myp#@09"ڕM[!IJyLngq[.Z1$A +,>V@uTOܑu \|:*Ȟ.&eB&,xm>zqbV/o +s\< Y$';ǬtpoEa"iUW#.. 9.Nd1vyUş<.kܘ+I^r;I;hm,m+noq{wkM+ZuVqv+Mq|/)/`rщϔJEQq(?QBp'woSv8;an&΋U^:@ME\QtB?) mNU|x|3 SCKuq Kﭦm/&\ B YFDml I{yǦ <7^)^iL귶.Dѥio?,TWi}O{ӹ ˑJ +ja 3흱jxW-CroD(,_^M@ƽ@$cyEr`ƟSm0sZ}ܛ`9QHӠjyHX7u$8ޔ/ `{sX%>١ 6'{v~ }轗$8 +K_{C3-~rDwqz%K !ym²[cN|mmtktV~(]#H"" vTMk%B1#_{U[posBV;.qV5aޛ\FQ{#"L8{ MᾀQk.{#7h6N:-arYtUpo'֓7{po-{"EuwCQ]zqDC{;],_o"Ą[Q/m1o=p|rG{FG& iqoI"*(R|'1Q x7滁}OwhVeȻ`ntYw]gԵ]E2v֡egwԳG&Wbx,O!ol| c,h˯Ъyd?P7#}_aqt :XgLxv2Z_k0XR;`2=CmSi[۰]-[+7Ik2o?~iV~ "7 sɡճաWdGg vO$q +}ݒ΃giZc}.!%gځޟ2<$Qb4(ps0],?~eFW䣣# +>a!=utb;Xd/3زq-xb_sH}.Gɦ?_fT*OW$ݰBdPy.s‹˕  +x ߍ:L2)@Č/h 4ktwyC4@) ۞@ SI\ tvq$=:msR"OIO[{qMA C5 zWUsk׊z|M;Yf/#{-|Xf5/pOlN#Q7 bsf"N^| $G"oWfU~;\"XkP8{1MpmS(Zfy2տ@N!;.ZWz]dL2lIj~w@z&Qܣ0-;8Yge?)Sf͠؅=zrpǨB֋HbXB(18>=Ž_p<|26j2j@;ՏO !mcMV Ӵ^²+I(p]UmgcB^f)b)N"S ":0X]OF4{g' +55cҹJھ051\`,5{[\?BhCh b䁣M茚[Q[|-:[0 K4vt)>Ė4Q0y![jB @,_Yiy%6' endstream endobj 12 0 obj <>stream +yI(]7nF@^ԤkMzTʨq)-i>|4"57$i&B/Oŗ4ThL;HG#"~ }K􊐶ȳD\L AOY;:Z[n?8?le6VE=BƼ{1vT^t)+lTW&WXEw##+ ^DnLZ-p(ȀDc ^Dg4WѷGLJLƽMС?3&hw٢ɹa}ôHq%Kݕزx)i v-NQ +=^q.H'uК?bIFo; +E+2\а)FtZb kS cX8}Q@k00iZ{5?Ѐy?11323Y&}H@ 'y`|׺7șY'l$'t L) +y#S]a癲%8%gZM_V=MWB6{>9l㈫xSՂێ%zB$ƖljKOͺ9Nbm,8]u煨Fu.M/`s/xgz+x*{.'9y8s >G& EQ'|mbV5lǬ+H\,u!0eZAOU{CTVهMT5K%p?Cx|/xg\sKt+ +w](WɁBJr~ +j 9*k}}+)_*F06'p"X* +{6RɧV>ݍBG>bΎع@](镫b,f,klc^Pt}6y!Ydm+6FjaBlвv :e}ػd0f;%=%l 6,S:7,ɳ>2a ,)V5jh2ϦjU6/xDoiaӞZWkJwjrm[V2^еRI7vZ ۶ec+9-vNh+:kOhlnm۽܎vB'0NU[{f7+}NM#5cy|;&6hqe=-^:.K&ِݙ`rq·uB(k,ocx.ӛ@q~P:A II׫:tQY|d;uvdj?qѮ JxڮWE]vͻl%һM`\/Gv4ި0HwA\I'|͛(j}HdGm8RM|$JE%/ H(>08{K~`g{pe*q s yy *@pZgMy W7@вt|}}q,J +bzq#8U &T! 1 Oc{9;$SS_ Mԣ8x1?`6DiNۀ_, `9BiL&s8#0gFV&, +fg̠C3GϻU@^{D4'!=,:"ODePpO vt=8;V{zXm&v9  <C/JgXQŤ*hJᬄ]#G6@p:6,>v3`)"Ldd|imgQ%jb,Ȁ= r!2UE3=r +[9j}+`A>fk&f%J#JmLX~[v6fA>V +|DZ՞M$|B+'3L[gBL MpY X^)9k"rMf \Fh .ucKV\ XO nYq .K; њ?]KxL:;aa[f3(`a pdc@?z,xrHkb:m[:#v4iN ?c-`. tc@;apuI?l:س0r +>q'lոT|VwQEkOvJ N,oi0wX\OlW!`Yig2 '܏ SP8rܝI^0cfa͹ 3[3@t Âj6O3K"1₲5RB" mq:PkÝ ̊4g1ڒVJ/ ^ʚpcJt} 4`;Fd1tvWL>3<0|V,y'{7g,2pP->F040M>DaCC᲏l#3GPROSIG`Fˎ91<]3kɿ(9&ZC[z"o7_`cAQ +Bd6Oe*W<fW#RZm\ME/%㤳\ >gqN_;ڨ2eOOQ>&*1M070q}yT’ h*/*JEt錜g;r ,OHf@/&EFWa%_Iy&C +vDQP5nJemELQ) +bN +0 l2{ʕ=L,'B/9(@-/c-"|!rY>'wY sQ@v{ X$&:^̼m_0޸/%'2CN̿UO]m晋X揝Z̊i.\R_{o[LzL!q=LU_{V@@# U0ΠZYfY@^&鸎fSgOͫ -IjuʠьYfocYOʟiV01J LmѼ$ZYHOkWhFLy7U74k4+mwJm NE!sOY4 ⣫ˊF3ǕS!y2ۛRg柰>xý3aϬQN%ufh`"Όt vaEg2 =Y[1OBIYf[pBf|fE薟f5>ͧ0XMiVЄk ahgӜ\r47<5!2=ͅRlʵƚQK°yt\ƒNd3)M-(-…tbi{tiYYaz%l*3]1f2ӊM5-M'OpE!|2@=yiNAQoUF0Q }K4@WAueOQT~\XV gWtC2OZLʐYGl!BkBI-ck*L[wTVk`G59gu93 |=GJצ2 ؖ(N7aKB]Nt%6WïU 3\EN]% ݊:#^xFO b *0C>v(bQ45SlnDZ·z76?f?NY4rza<llmk.ˤg&bO<ĖqJbO~D-6& ;mz[ƺ&vC5fs[NdgEaKG'}1&HAF112m`h uTO7 5!{m,vOOhIMO'5vgJG4/*Vc#>`Fr8F[B6\EKgab 6/™~(('TYX\y 0J\l3J `jIN>PnG%vyHX_eZvr;IO@m⨻O)` j [4{+(]9eXײ/:6cW٢iX{:ݷ$}{;yUeb$xNVnٛiS1hjgܞk皀pfim&@A®_,R0yWړ))GKiَrp]Rl#^ q۞-.HelK`_ "v l0:cy#-w6 O6y}`{mۼ)#Þm 4v +m_D-8m]7]< DhACѕJUJ|Hh;rWBc46| [8?nwG\mLSt<۱S1?'LnK\m-L}v8? +AN&v|\3`;Y"`{;E&G=+rel?g\B۰DGH 2,EQRBM>6.P:Dd;"ھtoW@ Ѷp m-MXֶ1w2m8(yǪvx[WNqn?gMUxC17 {@q;2f b8rnXFB}U{nY|,-F(z}>EhP4»p&:A~yfRqsOw熊-bBr+(1Âu󍢐!b}|+SDSjFsp7?uxm|gGOp: 7iSPG;֟Tn_p,| ԓ4\G_{SW(O6Qjy&Aq',B>J]q u,R_H  xs6NY<~Zx;V,f; +D]f`DC!BO`p+A&|`pON՚\L*j/7bU6W6͍eD͟# +t /؊:h$b`dxb pN],W^UltmݪVrgud݂AGZwBz2zW~mM(h-_~w29a'X؋A*ihǃeo:QsBQ;٨K\V^fvt.AI5"i{܉g d;Q0܅8}0"y߃}Acǟŕ!OTߋs76g]ǁ|YD#)O.9B,}/x-A1 ) ?e/oEn[?0t//~@~fT_Fop+! +n1#ž|W%_._xļZ6Xƚ'ymWfey+]qm}=Z/RVQz/?*z=\Ūx*rnaZ /Tk^U~#z9Ø+FLJɆ>z=3}z'zLaԟ^ݤ`ÈԿpzkܧtd_"nߔ:Ka󅫀.űP~4Ϙ < HY釧~J dhU~X!a~&b%'D:9˥?OpLjM '=4J_vz)Y +e/?EA;4^_| +XjV +_4D1w }Kc})\h [$ZR8N?ڈZ*%[}GzrfؑVJsׂ'ٌ/xE{hBO+-B7 T6Fܡ!]&a[Q*эSNc?<% +4lpv`-78ՆM-pzYLY:DٝRv 鸃cS +v5P[RQTMp0*I-e"j&}6Lyםe[ .j1X_o",1^ؓDJd> (2CtHb"p'oiSO_Re2ȌQad%Z!DJ3MkV2gϸ( HPOEQkk'XߪܾruRtґ18(4}OP[ +!NE*M&XvJ0tf]C=NZkm(2M $"$5PWP4_J#A콚鄳[>3]JjB@ BJ՘`?')ZZUUُ9]1f Acdž\haRg,`eg9Z!tla< |a'wDcJ(Y&xm;Qf}]t b'gU1u̗vƲG":C0zm-30 7vqLȸWR#$Q +C':;WUwYݫ^uz{{w?(Ow{;ӍU:ڟsѾ~nu/Ȏѵ'.$"@4%~TQ|p2RhbbJʜ_,-˅2ƮG Í`%kXqcPG8,ȋ|5 x{7qktY.cetaP2)42G&3,.c # J-ӎ$cOC8d7yþFe|/L cHKXeͻ*sVHj\U8siAy +`M>5SS5ћDMffLDMX%Ӛ0*fT(4fjFҹW߹sn'x&y7սk-Ӧp4h^yihs{ihr  Co@nsKjR/vf|\HBD% $ 9`4!lB'NTqEn,9><$Hw ptN}φd'=شd #iXXaI:Pkd nfNB kB-jэJZssA˵ Dx?rjH%mdmF fB#H'&У7@OR+>EB!+sQCfq⟤ Ob%F@W#Kw@L*P\ 2b0߄𧈽_b#EIEZhn hJ>~鹧_ٹ'&=Z0P$N&³sKt"|b1%~%xb/߼Q#1K[b6U`IE +p!"">|cgVXNA8Ik]4W]冖Mu&(n{dk54dKgs%·02*رb8UޗE}PGY.FZcbvLBl _V#"mVsc%` ~[0d"q.]2eUVIJ93TQ杻|p6-32T$eԣ8 +XJ2F$IRJE [MJĂ涢E˔ )@!l ǏKIl:f @> "v|&\Mx @8y/{I &T(IT 䵔Gf[{w}1N vy7mfBܑ ~AM26 Ieeg*mKaew^ߘNȵ53#h)UJS \=s!oc+"-2шm9zYYY_o+Dzm&O ZL!Vr%֊hH/y98GҷHj]2:|&F36&)\9[3bqxr7YZP49Q`hתK4}Fݪ8Vqt{gD2Ayh&ޔ2j;v +%sj M5rqM7e4âhuVyYPKbE?SA!Op< E4*obpS\T!:tpHNn)&+6vz{ d=.zI>vOlvP1 ȪU ț'N΄YW^H[žczIzh~Dh$xr0rX=V0+ +$[Lw2V$0 ^@vy;6b))IU˶3,wY +n:5J@ pc'_8%CZ̄3 lUE+:I)cUw0zf򿖛 ^巡r{ hg + +[$ȖvG^z }Sw]69<쐔IN}{$@cԛKh5? y+8_?seOEiڈkNx2IC|RlTmb[x1ӡ".2软NHT_._|`=%EI\(3t/Iqr($y5 4rڱB,0pzQYӝXҭ[Gneu5)R 1]?A_WCUb2ĶGT|BMPy%t +{P:0aĈ< 2x5wB ~gڊ}VEDݝލ Ǖ^D(Uvc^Qd:lOGVl㘎1ڊnqdYUt6A1e˺3đ| +%8eM/41'SVQ9<% Lńz)ZmAָʭt9Ox@PLFY8Qzq4R3Uo<? +%44?r*cGz5w~GUc9',s 8P*IDWerƊ&HNP}H8m1x [w=sU6םU t0Hm} >GKPz󿅌L UHݺ ^AS8,O-Yg/qe`UcluJ8fg\A}:1r#nqS(: 1s͠%}h,P$o@Cu"( oK ࿝ 9Vs4۷ ,j`H*JSkʥ`A>Ox]vgFgE)8?_FU&6\b;ں6A>^(:ƒ4U! +9Nr>z`%i+I/[x nB>)h7u^u}بY!nX!id?ugԈY](>zH+@b6`$LiBS @o&xcx%[hT"SNJ\ ỷ79DT`ÅM7"b-o:o{n2)IJc7وZeW )lp.8r5l_\s('9ҟ`]f4RJIwgim-2E$r=P@$(ZRcyWřYeCˬ̎a>v͆IOӇ-M_$J9KI3낚~D ]//Oq2̪mz%Mqps}(e]2yjN~/e]ϛz+e̼*0*13x6̇3g֍cygc<1=Uבֿ3=oK*0hqy=v!'5$Ss[M n_y4Ii3t +|xK oKNze>a rl4dU^h= +ҕQ xD < Io;)~Zk[e=ի0ei4>D I=ni[}=D$ngW$HJnһB2lso&d#lx? < ?9+SF$iC2#4rWKrt%GU_bd81èy3gqqڿ{.ܤkZw- ~.QâB$)mT)/%JY^IKp[bpPA@}} oGY~8qDuBL߾?S5.ۺI?:&FT_qC|&ֳFn5ixӉk toDC~1QMvشca!j(b{7It[{ڶH6{Eđm_eyV2gM='?JS.!~Z_O*H6)D~_FEnX5_agif/FVz,cMWE 9ҹHz&=F1*IX_JVc &`0D6)Z7]Μ.Ǭ?GUJ9ӛṤj*wI =RFiˊ3tOu0)I ? ҆rz5pruᭌ7Upf^ez*pϺ^APID^"QAJ?r%F֙S9~I iU2ff-izZF]CѾ#zOa+OZ_o_ۺ=aCyU925~&oH8%}Y1п9}?8{+1(h)h `!@O +v^eM<*vsHK$(y>%r(S4g <]dgW$q1ЋIS -3t^uÞCJ ik\q#UYgڢxG m;JȭfvMYVy7-4.3϶m*,4\DHgZ~U=v\xu> E}t~K)N؏k d?yq hxbwER^T")I17w x`>d*Mٿ"Kt~=v7GIdK cG4}?d"=ӿ Χ#FOeWj'!~# zQɑP\n2$nXuk׉kēRZK47dy~ᵜ3z?7T8,]\%L0ga6frwBU~ivYkYUcz.1wBOZV%FI^P:筄2U1찫>ު؂r#濉\HK>A;)j4;"^!i3HP_9Vg3q: g:`̬ +/hlWʘUl1èA8 =8Tl"b+4= l71(^OϸF9 P!nD4$f'.y +o=̽A@o%pq`QPQbd `D~/آf'hWn57)NZN"eU$q;v fDž<#p:$x#n|=~8 m>V-XSE0CDys*'.Ut Hf 0{yfE ?A4eQJ~5϶Oo5 ou鵞72 Y#B^*~I K9kEmzjΖm5+$.10,:>Ee=4gNR)O^T5o2y_մ4ìAT 0,fy*$ rR9C*$@Dqq&JUoaf]s#Tn*{{Ta0_%0Mm*]2T#@TZ[2۸K; #5x;W!6<ГĈW{0P^K·ez@c?@,Hr]Yrո@hgQi" {Z7pU ;.PS]:D z"f|i4Զ|]v5YUcӢ=g[idgԶHڦzE fFyv%-*ps^NJXt$-tm:$PꝀ=644U}#Dӏ__oTEivN4*3϶CO"4(e}9f-i"Vf^g`#g\ +v^ǜ +70+X !CٷC7nTY>HQf$px + XBN bt*$Ǩr&JUo^0砆qoݥωSYflIvg$9J԰Q?7^z$OhY %jCL\sacKpGi.Mo9mOgd-.ӪJWtcՠQڅ'B~,b]>]g!kXu=҇]~t#DUn&}Umi"oq7Q D/mۦGK^i[bbMP Yܦ81gXM&rXaԠ0爔])zbՉk߈;=EGIz&/.x*?!u^ yzXTec0忁A2 W}29Ӌa%έ: ;COOq7o5ovcYnzzh'@ɧ8Ui^)ed]PQEן$]} jҋDiϺqfX\PЀ J=I򔍯 +IǨCj9o`<SL2xI@"H/; HhAE ([!eVIȠQ /~5ypQ} |f*4$FV֯RWj`lK+d ^+jӣ{(@),$ 㙳YM)NZn†Ndv@O+/ 6tn;MnD%B'IW 60ϲEWq+NZSzI#@Կ┥U\di)}2M +1eQD`1e; B!": ƛqh.{*g,lVus$C  !6\4se%N,bKzr|+5AF*M _xF?usqLEs `X:.x[rQ_h%wI~_t;6O=+'̕sp9e@ڂd0w^ޡ*5K#znn7*Tzl?bq.Fp#=-%ڈ"[ʔRJR)?J-kpU;wk(W1ii[4k[ܰ K)K.Dn uow kXRB53 {l;f3nnQ%, aK\m|r'ҷglz>L.`+ȏzF!߼<8zbVG(o_ ҼBOq<td삯Aؔ4B ,3r{{s9h5G6q=Gh1prϿҮETMp'$Nuz"SCĭs+o¼tF>-!ƊkjSXw ¤zxl_yâ I/\I+/b4lYV|jcR?Av>XY gUt %q~!ϫfPT/bh= +$/|=oFΡ +_xȺ@EǥQ\}tM-X5 kX_glFl7uk)&BF~.7} cީƌ/D5nBń ު8S$~usC 1"hEyÀ܁>q=ޘ vLԮۊ0Frl9E m +nP(Y|~k`=$}C5r}bo}7NFhX-y+&.ӕf^*[W.LWVs#7a;f4P/)-uɐqP"w4tEyF|WB%P?`8&ߠ?vES2j/^ NϠǭ@_6"`7[_rp=pBs"fge%qg~-S`3~ JhFp9 kX܊l7I(A<=$q +߭`*(:wxBƋ4^ |1aC8W@Je$7s$qx!Atia~3>fa^ l- fWB8& G7(VĢh eʬ~}[ȵO|3lVBZT ^ . CgPWJ@#R':9h]Bц#80&MZu5^/c6J%@)ݣ—k3<8/A/#FE;0WJ<<%¢8Ւ $7aF +dDpfax6ހl)dN=+&0 .7`ˊ4=g"ZշY1q?wQg>$ڪ8VWD -:dv'$u ⱟJ_1%,E@y ݎ8S '8i7h{zƶn>1)(:OZpBǃ:Q sf49-] s AcLەY4A 7E4zOc?Q1u~cup3XTY˒`m_ØXT5nYo! qq#eØ"cP gЮrϠ7<Gij5oJ85l[t !?/!Q5r-'Ӷ@QcU F 47|_ _+R!IuDe\;.^ᎋS+2-DFq9S`ҝϒ83x塑{NaZ2h\ܮ|^C63hCi'=ja)L(FWs]œ944sXAZ\Suv +<<1o "G8b=A7_0]WFȶcHF{04uf| 1/Ɑ̋ p.T8;&!O(wB +h tau7Uۀ.q$ ; ,s{.qih {d_C ,qXIs w@ՓE8q֓ܗA$(|E@u s\V_jA+@! z(͠;LRqA +a5tYQV"kf:``8oxk5 mIo>߄~C"㺎 m=BBT$@Nc d|@mnGj\RnPMsԣ2|gA/ !zFQ$7b0c6Dn}5 +LL±荝hb7OfõcQ&NW+bA,}˹4ʷk|D;hp1D)#zryDRKTPyB(wh-Ý: UG8m_>F'Q%rj[]ٕs?G u^t2oA΋!<&B1f r2)B[_ t]2dT^̪S+k1A4yGphܴ\ r p \+D!m~K%z9 ֟qUXe z`T 8?R +{Z|!ٶcX9R䙤jiE4BNY.ZN2fTG0 R/Dpl8q`.qh'`XWra_wҾ~=F'_Wqi14myv()LL8y]zGOz `EX1 x9V*G!,JGa)]p7S+A(QCU]Q믊Hk8+㗢Dr[#,6)9f#)I0B ,~~uYqq\*sX;L sytQW&mdX +4E2n'B*~N]4'>&+Mzer;dbQ‰(; *;\wXM##VrꟼbJT7x8^[o*P;jy=:Cbo5p @vQOrc.B6TQ'䧒ByXӦH?Mҍ\V]>bgZ9eXF6r؋T P8ۢl#VpU R*gIݟ:}`9DƩx0@fOer"uL7CXmY4ȇi -CD$"v N0Qq5<"P')4R;$Mc?v^2@9iM*ZU(,f¹WuhQR2K].7u9;lH&m maO 1(2v׿COBE,I*4D8} (qׇƴC(A  R=q$>O޶ V6ȔoYhStY||GЌUWQ= 0 m_G@22z&M?>k!uo}A >QM6#R/2r_} XFQ=GmLN +o_%eyYWUFG})"Beg!U9EYEw .>fa +,o0:DGx(eeLM"2nE{? *.3QOԯ x*I(q+_F_x/@{T>^2X5ZR`u{ +)Ĥۇ]|?pn1"u דVgl"ʷxs5 "uBO6J#i#^#v<"(B;ǝrvi2cF<`7-M:Hض5K)m@Nqd]ׇplb#x=:-?8Fh&>F5yC8-Qj7(~CaYi겶 80:G> +8.7h9)r9Auc͂Jx1Zg _ }^~"sn0uC +7!pG@՛q7ӱ_'`?o;م^6ƪ7x8uܣu>puFQ8 7)&( N0'qjeQ(&ƔЋG=$PuJ,-2};ť$:GV<&(z،ΰ6xF@4  +r[߂\\F@g#7|ݭͷ%-qќ9="  u?o=c[)uRӑ(:j+W)]C/A:0بZ>0(XREZɧ ^mfٖ~&6+SXrZY"etE"$ee@0de7¹YmrnP+Zg(w3 pJ嫳|ZeTC_"I_ OM@y X@~Hq13P4O#9%HvGPH _P|- 5:Kmt]3s@]Wɉ" X+b{oUWԕ7uZr!Q*ZEyMBJz9+ۼ%M`&KjZޅIv\шL7r%uh.{go᪗|@E[ܮU ։"st[Ju34븚_)r`:.:H+?ޞl.l_Fݭiّe^*e!biuNDf)ztDu>H lؙD#*[= V{OvMXu7~R*(F<*wMX$Ya"YxC@) & 7pICXP,Zq`=Bw0Y41+YenU);pXT**GO>ُ`l-_yJ}o%,pck^!W$R-3wqrK>XHyU%?JKJ%NM‘<?<Vj +Jvi'buh)BfM޶q2C}DV9w[ 繥N⻴ \8KДiTM#UFJz4R }-`>,qŢxE:kF,\>IT'&=yY?qRNU(V'{!BRi7<Ԭ:TReII9="ͤiH>giLVxS%Z#S0H)k?YY Y*R'\5cR{j|=k\er+YfS^*evQtNnl,?Unij[W3 B%pe"El +PpC` Dnkm+P4:IsiZud&AnAyudDO9}]􌀮|10hc<ʬ.B$q6Bj,J%KXT5a"qE5m[tN`?Y77l5u[9\q‹VW K I5`mBebٖz*\r{S$> Dή"əfM¹#c'g"jGfr*)]5JRγkHqS-3RG"mwZ}&n~LRIRNԑ.:U7X|Ukmҙ (ILgV +AA6֎Mه\CZp%ʳОoN-=!}Ki6G,3L,F|eY4߷h]S_Y0xc  O{9offGLw˷-!>U`}<3PM5 XgCag1jmocVdK|IbBykQROnM&GO?A=uid#>g]N?zB-T&.wz?,2|Δ{[wBмPGD |d߾a7E=e?qqc*>\jEdBc9Z%JKۮ"5?C)[ݬx_m][I]lvԽ/.8]< wvS黈w \x+\4\ۜ AtZo=>قŜ =l8i9Dt.͗ )/gn@tAtB#bV@gk[Qlfn;wiAd[2#3c\2T.nM>u!\x.2nnQ M2Xqf8NTi=5&,;5D@Y8\bn=*V.,$vya\]BOTL}e֫3\maxh=tXaQSq +w\Y*&i~jޣbx`Z…R`GX!ZMQ&a\Zq V%K/ (YVřf /wc wK"{7$zizi{kn8Vi"ymydw xK"YȦ :*)f'(l: ò=.2pep: &VZVfUa@-+>@y`E)VY]H/](unS,H&>2gf}*U\tn+[L`H.0pkL*4El̴0lRG0hDT|i.k+ӵTZpxp/htH֜os}/;yy՞yGS}҃:{JhiÍ9d8'9;rpz -]ed'>%a*SpS\aֱX)?>q/| '1||| W&7=/‚X?*ǭn'>4w)][F0"e.9@5n1N@sEZЇDy6D>4O9#yW_J}S@flKp%dhʿq^ 6HpMq.[utq,YCF&k:/<ƚCғ +=_g~b}Wa^is?DAz쉧HTS?*ـ/{m#?rm<;)T i=^P9n F222>?5_}>6fWz F'v*nzUhKn{4{1spIoڰ,>T}9\z a˕ih42 r&^$8QlnzB0@fz~a_y~| W}0DL3Efԑҹir@6r8F4]_~|ӼüS2_6w>蓀WT}5 2fnWx]by*Vuh+!:!D)yI.Nåritcќ%D<+DvS56no*<o6o}c^|<[o}|O0o}[e~q>1hP۔8TcP4A9x#8XxQ~R`$:W|;՛8x0%l\!)}Ly~_}Xh(RHa1]!ʁ%˗idV^;XҰ Cw|ZT7I _P`k9'1 +{jpfUlII$ZSK6W9L^!q)y||TlX҅AK,.<<,N8 [y /QP] *],=e4NX"H= }7}!@&iCq>e -=į  +f%rӯ+ingpIGb_z. !:d. SiAx4 >EGa)SHBA*,xTH@?BV) <@<| 5m;.Z7GҾ+c5Nr#/cxYv;Rw<ICYNsQ/2.ل*z? = EoF&Q_ xȆjTE4= dxȊ7_UPn -%;s@=D)e0" @BɿL *j[R,SN (*D!*#x b|<5(CWC& 0mneoWmd%ﺑ|Hǧ( Lװ8 +fC, +~A,j]EIX ++$& _?58π HC5zC@g1\BM`_</3OJ:ECw11/"A"sSlGQ ^*ZJ'ԮUP]oTM*zC?dh/AI֞gB8k>oky"@ρ'/|:ﰨ~XMB &'Jau˩=rĂxieլ*B3}+IC՜y_,u6c{,$LI.,$_S]QdEiʟ)c/ gi0),B*e59@Xu7Һ.()؊ 0 +b)8"ΚgIu)Wju.~eWT+KZߒ %u2!}RWF賖$ܙQaqy+ +%Z-juXZ8srx"PQS ŕSgb%י%7i5XQQ +CgR"C7Щ'閌7 Ðh'W XAP C-S+z@W"Gp84ex-3`|ǁyFYQ(a_\ R`*Y wh#,t y.L~fip?rMtU Y 2PTj%V[:X}KԋUہ-P Zk x\pڳz]1"^k]hMBBTs<ȨpRw '&}%K7nVqZٖ AQF&;m]cr ;beu) vM.XҴh±e7̻%XS#d@,d#͞Q,~z%wKz 6j F('P`Ak W#vŽ 4xEL0H$H 2`"Uu@@;gQJ``D +,Tftҭ$3 A +ް)*Fq@[-ʨ)}G<³(KSt\oFZ׫-iĒ4ڀW$1˅}~6?/R0_ i _q!l$0ƕ5Cx=fw ;15@=[ 35܂\뛍ʼn7>Ch3j*{G|x1աLoD!w fDBT$&>IU:tAcq9p@/b͈~x騡U/ǿ^콥)%){TY +:)Z64U:/☸.wW}z ۲ŐKD8+ %+l Jl 7P58XVnN܇CxUuMU$gT }jf ۍRzPG7vj"ٱ]dH ­_HA"'T3/vBIsva=H owj^n˽v zٵ6篩[9RUIR wi]haJbxDͦU`s;,x_qX֨)|z%)* +qՇDIW,Uׂ sX'SU1s=!qq!]Yk4_α,$#hPW5mZFOK-l6 + + +C +wr†WK VHa-RK +jIdfH"[q餷|zJbKHuI`[I^gTc+>gOEZ+tC*f',N:RJ{Aa{r?u^=60<;ypb1a\/%"48 Je61ȿb-zjmVDN!`ȸW*&ŢV…υ!Yv1נԮe-R웣 %T ftvdv &^ʊe0$ %SuwfX a7*-G:B~-Oq(Uaۼ ˓FX*(]0˴uC]Îę <GyF??#j,0j(`'p"<\f ++~Y6pٞE; +_S~cgH0$]fLvt\8$ߟ®R@S)H" BH_rm9WV̝84/Y<\* > +%-Y$'Y ڡJvەgU*aW0S(r]ϕV$RZI1EqMBs8s`HnqRW,'}pbڏJnfoxYNa(S=0B%A1:|F A۸n^m)g'%4^]AskO9;Ɍ.[$6{kK?;JeBIwouabZX]v6|j?EQc´\uųVWcx\YX\ꊊc8{)D/u+?S2P| ]$ +I q`Q\4g 4}͝ڜ(חPL XGCc]d<[#M +cACvG=&T9,5<Kh9We@O/%3) Io -Axsއю :؃*_]FcZ\8}K!H'D{"ݚ0e> D9t@w]簭++dugZzMPK>6%rWKyJ 20AO!D }JUgz"I3=;H^m~Y7)w-52GYr(LI(g!^čub߭v6yg̙s{eԼ ]&8G7Vt#Pq=8'LuY3Oa p< ,QRM Z8ab.~ŧH4q&|j&We;/0P_8 tB.R`OjS#p*w3XcI"a'N=8ſbKO.r]1^SQ#eꚹۦNf|źWlqh>_i%NIɉWIAɼye1Z-lKCg&ЕoYqx8 +jaH,6C:&Ɏ.}Hsǰyiaj.>{uG 4WKiU^.v -DR)bqAuø/$/xM:* +&͔/)qVsGP~Ǽφ & +ϡnɼ̌yv,(u+~5QB6U,rwI~rRe|%3ieJbiQye0^ Uc!0+ Ε J.tlv@=4wZn-@{44/uS*x:zo~zF<_Asߧ _4'^B#:&K*:j~eW) +\ֲn6i^"/X"<H :{ +P>UpC@5p#R$^J$XKVp& ŸڇGn + Z[ +,~(hD\tT,AeC::&Xl%1)/JL iل pay4J<:3f\[Di4B{^B߀Gfu*ߝϩkf}I7 v~ʥGJ`KE )sMZ*K&}]!Oy锏x ɼڦ/iƼKˆށPʢC1kޙLī);q>A݉ ?^S,Ah0awHQu'0BD:W?]ɔpHE\:}϶[u SKZE*_M'L m0_u̹ cb +!cxf +6Tb 4XHE^-j%(~M`[,9̘5@~Mu)g|~2wZ!ߒK1Gn1|QQ,\SY w:)lx}?Ed)XykPtb]3K +׋t@ +9 {WzeP Z=66[ø22[X+qhZMR2r^[ ˼2ahf՚iC1[K}ٔh V9e(4e˾YGM~ 'G5UῨ30 策N䇭ć{Jj))v- flJG&#Js`[W`F/Kqz]*t̙ +:ϥ53%46~k͎)[3Zf+lL7f0+ȠYF=V(dS:'t;/  EZirī}oG:@V^:i. UrD~u +n vQ\CPԳ}[6B BG Uɮ wЍk\[OW.5yI hb5Z&L #6gYl̝6grZ`b62E|}`,FeC_}l51i^qab36|v(Q_`N{"j"H>.Ο[]SgV1%0$\'$Gm#+A;bka>ie2%` < +˱S24sd4mf<$)XE ?0|*̥Ykj~'k e 0I : H2aK!Ho D.caf +ȸc%^Y&,*66 #/R垟WHK5H>~uO [¤do=nRvT(mȺCAy(#`2T)iN]g['&MHGځz+ԡe߂g`G^jr,UDrixS 򰐻ef3* yh9v5t ܠĽ.uSFBz ɈW"(qgNxC[&!5A̞yk;n7Z&-[lsm ~ygN&moN ͳWW|^k,S=q}e--ZW!\?/%* KǷOmK +H7wmJ%5U蓰Y̫%H4Wȴyu~c3kbtnwg{[sc+7h-;|+8R#&^gfbM M#wۚ=3r+*,!G:2Ϟ)ؕHX]6iYC ºGzX)wvudLct=Q Cabod6Z%=.~ ~F3Tp$8G1 4rdNS EZѯ&ܻ+pq(<_65'/FΌ2i`2 ۇQg̛l(ym>&E,Ez-OEoN"} @E?/'T>๮`&E޽4Giʾ(= +m@Zh3k``xH>1l:pM^-s-gf]38F/A N ԺDW3kXW:1H*etug[i&>ǘu1_^\͡Uu|T"6,ϏqܫE诨ߛKGM<"|pG5 APP+'2~jedZ-`=ZF-lr:A7OS'6Èq3[إwl=6{kBsxg6m-.u'Pek C)]ڜA0 N|1[֚I" 2м2ҨZfx,2cKyNP,OC +8fTJdc~ 58sĺ[q\+T3i%'t +19g./qijwX|kߦO:ot8@Z lI ~g#_40c-"C=^]a/}_h˨q#g'x;r$g "D~8w6~gT@~8Ii>byMA! 2B"0cAH0 B"H}}ݭGm2le@٥-qXO9ڞM)ulzBwv70+3ܞ;󵁴HgY{D8.©:nOm`t)W{)_3>a&Hi{ڎ+sI*y*zr煿ş=ro|O4;4/]n%s/͋f|I#PXI {YR0HUUPPPU[,րiYՍm7+z 0Löm)Xd˔RJZEV:c@#(9h[{OjVVLk+vKʴ&}M%Ģg1H` Nەn|OזbjCo _'\G,R,B9wRÇ>{Y s?Hpa͎Փ(_[%5`I(Gg U}cd[ +''G40dΝͮrw @&Ԓ®@>I_wj]#P$P4t|cK*^ȏtj*ՃoSH0[##${ErӨq-GƥuA6G=|U3 \/VZF۬QZdOɰ*eVS.-D(..+=IXq̊Ht'O|ŚY 6GD (J>;&~;C!GX4~%58ʚ0YWJ4)UIuœ1XdO,)/KL{]"Ƨ\]9r 3Bj^`p`X=P!h|J*y4~D3h9,䧭WZv5eWXtJ u]ix["7'`9S@裌&*(Bh:C%AGo< IBOP9sѥX߹~D:sT>C @i {#\ C-p#̕fyS!\B=OO0ȄZ!KoS.?#m' MuA!_2"{3H [=g,?"mM$)gYY̚\$KOG/ϋ,׀nH@:B tIWZ%%aW]ތg[{?El31n7*l;cj]>&ErŤ}mΚ,*B5cWUh#'M06@da#H2%bTˇxnA[]h登.˛hRN**n36NʏdUJr2:PF??rO>U/X(3Cnd)nS Dk:tӏ'oR/9Zr5_gmWM]SFdAB¡8oiK9g.a?gqjq^)ʖ2fE5K5mivoNd*ou{1W*M+ ^!2ڰB.U.D롐 e@[Кt[' |ZRM,>6r} Wl0/Bv]оbͿ"uP>҂I3@㢒dO 6qeE)Ē@&tt"F\6j?) 4œ<S=pgBp@DOy^D ,C.D/ԾZWJ|G#Įf = 8^Ϗc=}Aٿn+%qr;lCrߐ{z\AJZrCt]/oW4'.TcrW"hⷓfyLϞ#hQaz\ZSM>C:Է}vVњ~wܠ9C]#m0 nPޜ$D0~nZHK 9>{/;ePQ(X~VqQ+oPF#mfYOÅ+*RURO!|A'XO ‚? +$iI!'0QFkC~ʢq 7 +"6Fg o@o.!_ADv{e5 +d|?4>|+(u&u G];j- +*dM u#]ރB~?IFfj #uK:oׯC|kߡKy|jILfkz *T*9 Oa $`GwbwKhRj;ؚ1 ם yRO!lq܁et?LDAH.qdwBL;Pa?xvC@~Z9 d\K2`qm/|5KT%hdIplq>Zμ`L{ E1gYFk>Ʒ87z8bƵO(cD +cD/!O`SU\\)QY2`;&s{萻bv5iEns r h$?P0-t~j3~)GxFcHroOZ߄_L!cM˰59Ll7x+ H5´N]M mQߠyTDl^hP̓4$| ;hjmg&|t֓fu pcƗ35G50Ӱ]s{oHUpЅvkyJD!Ae=yQkNke=!|vuTC]8zGJ~)X rWW \l=6 + ڨ2Jx:e:䴏űW)W;y~A =zVgiy$į'rNC;/ׯ2^\xo9jzힽ-gzN;!zJ÷ou^\H +84 d<']i%͊q >!z{ɆmGM;9h@ﳧ c},K2V'gVJAncLgRVVPrgNe. 5L +<B9C备>e|52 @~joV@~Y$mCa1o`@G=d:j{$|!edm6nϞVדju5neY8rRc)WM u!l3ڱNF>.ӯ͂ĝۇռ!G`Lü օg)3jb]'o OZccg) +Th&c#1s$])5u0цqpeĢoA?Y ?z e_^oiUroޤL7'v? >v#yus 0 +`>Jr}r5G`q-!x#mnȰՌ +hX4ʗZ9âyמ4#)S6^\Z}n]aۋ֋.)GX8_1#iNA%~& -z?D=DP_]'%+I`@l.X(Wj; +$~="{tq5R n+h{"Cٳu\At ktR[?5ϭdEܡ9݅h ux!%` g]NXMq?8ӕ3uc<K5o#?6B^5`e8P&ǭ}a/FVU`^qDŽ%ga |  %%EndData XHRaster N0.24al (c7ea85e6-4a38-4265-9968-cda6cdfb741f) /UnicodeAI24 ImageRawDataUUIDd130b5ba-640d-4e62-8197-5fb136258cf5Alpha/XMLUID : (Layer_0_4_) ; (AI10_Art; %_ 5231 76518Lq37% 1G=L+$099lZ_.vdgYϩ"؜\  o`t/~2-|Kdzĥ^8pao1wiICŸx^cc4KEZHA^[0kd;o˨S 2ShF$IJcH`@P ƊgE` Bcp@h  + ICQK(@ +iԑCœD8%bUd>GFV hFKhr,(5!q#P~VՖ|[s-9fm$w3vL\C3wBj|{?rJ!y{ov # r9gɻZ <:2nVErqa&g_n+ T^bz 6z:g&UUZεխF5hAh +f8(P'Ʋ$UϜ1a424jErukOa]KU룀 MswŒѩvyEZ,`^Qa11BsQ<>wRn(VzXvoo i`|@N1cDs +y#[l,)bgH:7ܞ*j \8᭥]35"KҪe& ֞Le&A2u?3(Z-Um*gv״+CpdRQo'o`Sgr x:eO"W\(Tp6I|B΍q39rbk!>8Gg;]1ßci2 +PvO\7؛Tf%Cbpw„!apec#fGr +ǜMG (K0@&*処/Y(L_NI8ؿUcCm؊8dHz}x”BD/MCyf[TlN6{?4K達Ka#ލ*_v(. I]ʉ $@"v{Êv^$j!xUV-W! }4!$Fd[Z+S!z{k>&gH +ȯhcs8aC2A1(b0; +R +@TZ"H:)^)>̫@qB60DP8ĽQ2ڼec\Y@Pw +VAFrqs`G?ap;ÆLOiz@ G2,zt58$;Y݇B2RhySߵV$^8rp:` G؏tHH7WZ xZXs}1L6 No4(<1b3]PIoKP-5NSKk: +2LiZM9C}[6@ePb`< Nn&;Hp3"V @y@5@'n@Dݴ/V$ZФcʥxY9o; I+*ZrdWaXW&*R\Ԁ)DݽU>K)q慉O6p:7"oSUytH|R3 {jHQxvϼEZ qcyyH!똵M2|<1 EWX9"HNN&mF|~|jESlIa.ޝ^{u+J UU˜eB,WNbRmDrl6\$nd0*^05W7TUi?*^ϊF`EيQE)F -f3I^3.q17<G0%5GUR~> GT^BFO놬oGHRNC4(?Ypd!P򶾓Waƴ{_٤ѹgdtY>9,sLe8&lcslk::l@BT)yGcEŚ-ltip.`GQsz ZKU* g9Dij/b-*'ᢣQZxki0A 4 +6x1/0'wa h*+ ,Kɕ,R\F|]n@xIt,,-v9GzE^(R6$3 ^z>&PTiL,u|:1Mȇ# }FxZI !>S},&2 -"A@KZ LۇSę vϪ͂ERJEHRg<͛|P\n"!9x`@aW1{OBaBTAq~L /*`w]j~p&r%Ge4l_ȗNTY"ԏ0KN3N#xM[:j*3;CbL5HMJԏ(J:>v" 1,-1βןDȍ#S1|"uf  +`VT'gEUhۜl"**mM/l'V+FΛ/ *2NY2x,ʟ<-=OeG]j(CՆbf!oᗺa)0Qp\fg객s/$~}8w.QF~~RIv#+8x<y epimuɠ夦킒D-|Pj"w#p  ;afOsTѯPPCVt^ZzV8߽/FYgOjdž^ךo\Hd(5A9ýi|Z1CE6-fgSXaS5hpKPuJǒo @;6LаU K-%f5KjEA(!P`pRS'SOgYM  +Ju[PuD>fdn`ax PDk`f.7 X1JrN-WAˁ@R*Dz_IMDԴ-m=@T T [l(tn#G0|vB]`[ wHb$ OPƨL 6LE!AM+5s6 + Ea`v >RɾJw!xa ^P/S=dHICl|n#}G=c*frу@ؗ\j?ǂHX(W3U_1߷u$ D>!1R[0rIpR$ ?S̈283.(y'fHX AM5n  +b|bv WY3mbd2ܘd1(:RӳM> Ll'_p@ bZ@kwI3L< c]_!_sF4?$TUK7G=&%ov{| ty9d*ȸ7`KAAA1m߷,EPA=dEDD$!Ňq~ |hØƂS?a*̫ ׺΃ /DˀÅ + zHlvRo4XXWCE( +tdЖ(;2/j9\wwg2C]OQnNQϋNQUJaٰ6ч]d[!,Zk]nfams{2a7C\Uen>^uQwk]De[ucҜF}pDLpT4H82K c=>AJP#} teVSfA(:2R_U 4}V9U@рPEăA%tMMweֻH!RlH0t`΢|P "+t_tX4^a's)B-\tnC6ZZ$AˡTbBSB$EovҞeU9&˃d@'`@% + 8$,\ą H~a52⯦163͛c"`PFy@H8$4"B DHTXpD<4P@Dzp:l蝈Al~8ϳH K +Roni6ix:OA])[!v%(˃2ע3lƠ)|}4nZ]br +Bnh9thUjü mղk^iL>Xˢr1E@EsE2uL55r4'c:P?uA닾ͿHhy^L)ŐϪ}}t|Qb\3<ޛƏ .S_Wܱ-Q=TL/";tJEA DA֥߸d)}ƀq! =Vvc) (f\BT7=5VL$Ը2?M3m[#V=)( c9E`U}/T#CyQJlk+ EYZaDarYsǂ`0|]5׼9|o)OzP8j%=XDu<+F4pVߢW݌|mp|/YDƸvVz{`y»cLRܡ7IeF+EC_8uu]LGZ8jN㱌+5GI΃.kL FiZs۵,Y Hb>] tR 2Haz[e^\7|9V]ƀ|qCoC@$I8fEn#Cɮ· W=1 1EPhJ偛Hr֩)N3{֠ +5@ְpC: ԝ-Au771H?b \Ꝟˉ+;~o)E|P1%Lvݞ' +@h6TDr8<(V20痪f̱]k/ +[$Y_Ȗz_q{8Gh#q$,T6ZO-ގj4A K::.pt;(tﷲzA~P$Pʘ2D€n,նA2h=2֠Oٮ9]dPF)Lʂ*unUy սV7@h, ^ekk.#V5?/ز/F'RuP&pʔG; @IN)n]*\Le`4ӥQA|2bolN 63نĈXz@4g7Ck6  l\N@ w'RvZ/W~9TPB`fxyZRKG[xk4I $4|M;sah ̅=;uq u9e_^ aj>-=f(Wd&# +i)yŸ~`O`Gp(PB'\!R_e h& P(`"/`0:oJae~Aa_>8P|qDQ5%*fEV"œt4?N:ګ.5K"zVpzxP~Ua'z!'v[ĦE̽ +.;eZ+ݶ,ou@9#UN1궭m>8~L?ZgNS+y;cD)M,X* ] bU̦ WilAߵ -'pG-plDqx_|vRDW,)u<0bE}M!i]!&l]ɷ'&V ) iXVC:&tt䝁4SM Ԥ5*""1! +BL,| *;iNDBЯXnNe_qvh~2<>/6]яͼ$s31AӱҤQ)Cq1=4\Ui:g8_3ы +qNC43\?9g``V9 ,pHIDm)pX*mZFU`;ha:N4D*8cF6z;TR08%Tx}Tr4~?*S˔1],C2*݇C~ nÂS%AZz~t={D.BkU)?so&o6|2Ƶ@=M aKyQ6tw .4٢ +1,&{H^A*B*M&h?HK|9?O^dXagAfG'-a wϺ l5T7\%5'퐆4pZ̘cA-ԣ71jncy/2SQ(>?2*__}6zrPg;ߍ`@J'KS萁80:1KO%΂+H+0)-:oPQ#:ENu9+FpZͦ0Aq ʎ\c:!Qf5A9z@zAw9icb<ijk|*8NӁn8bt0h>bv<;=~$7 -݇Fh s*Vr*]NMW&svM"w㏲'[! ?P}7qY'UnC>8?0}>u>ĜHj]$Gu@q+vvKQ^,LL^cűfx]_#vθN#@2Mb cv +i`t opWH +QHRT6"(\}xd)%ՒIZԐ[z^-Qah#0{W@-A{C?EIbEq./@Vz0ǫ%Z[O%^K&9-9f&M}ڙ*y`hd@jIKd\u5o&d# ntpLJJWEa]=gZ-+Y 癜jG0-Α3]@vO7jdmWR(6Ѽ',ģ'#Ѓ|GgX3\hl"JKW?ǹL[Q&!4FhgS Dbzm~  +7ɉCCq^BP:7c^*+Ɋ%,'|J?#35RJ.T/ݐ%9c,$5 a!$AB/a9 @`)@E$"aPG7jn"93]HxFԞbxژްEDٹKš8U!{:]0c:-kbھ%}WeC`Ok[?4nTG/|HIV8zf>@݃zEe&ib̉0ՀNR?Z +FX<Jot?ǐ3|E˦]_@7-@CPxy<9h>5*l SwE4Q"tSɀ +JN;C s@SđGv %ڳD2$C, 'k!)9ġ|ZIW#@MXxjd?^gJ9~=RZt. µJ1 d p[7ڬ *CAc<@#d3BLP\=0Q^M.66 b%G4_ϵL*m⧠2])${H zscByݻ@/qjDa@}u:UTD*h,䒉 ( +,jBFq~'P5qˁƳs )74PD/>r +MA!Y%ЖI4j +h +ͤ[QkڙhNVF| +vyVdY~V-%Y۱'@3|eɏ i,QN0pb#T[ Cm4Ee}W&KRCW S``q!Wy%n7HDOZ_C-ƪ3Aǘ:K@`V{Tnl+O(W8 Ƽ*(R)bk:ne:lҤo`'}Iw/Ea=PK~ ]t!/180X{":P~!b0t"7EsWv~XJ4]-7O>OaOZq +mS"Tz`HT0>"2~#l?Z7cs2M2%.2j6z^G~O: A!<^%#!i$Pe5ftJ#׬du`f!@@0")A7.Yxh8Fg6&5imi0FhR˻UDffIE=RjlVI&?yc59Gcp#fc|bY%A9,Ix9He  j; o?bis(vlcX0VxXڼaB(Xe̛٦m-^}HdUpd(fCsrO'mz~$QeXN՜U oE8H,eD/t& +*?Aࣂf "CVQR +I4TgJ$;+.l9e*zI&VΜ,KGQy Ҭxea:p_uD"QꑦJ%DKe +،0@lDpd@Y bGYUgRdf +IBEn۞|,ixyfba 3Qex0A2(jJ[T.N&N>zkЙgǰ랱n9νt?k=ort͐5{n6{.?[7l5wcнw1x3Н_,:^73kwܘǏ3ǜ5~/^"6w^fFLVW X:kk߹2[c1:1my߭fd@ˡC!Y@ JB*e1ΊesN:IfcIV&NrCCMt?CM Lj9jQ^ O0yg6U<%"҆6"wsyrvO:DJNAۗ,D䰶sbE2Z1~ߙXm͘Ŏ>4e=yڐx#9 ,9$GjP.ԏ 2ސqPĄ5oom (}<ŭ( u 55rIa,r";c|wzeb !uݓWV%mB5==P;5%qGI|DQ̊b[CPax{o"lFDf)Qw \dm@[yz]Ůtr wH0N7<u7Θ&p$? @ʱf|aւ5-yG>zjmZ1!Ղ(mdw\DZY'jNY*̫bC'z&,#w-\_L!).8L!]1x4NvSal dS/,5Bo32g9^[{$F[qXgBJx}FOdEǵ&33aG9TӺK6)cdƪ B@I&m9+| x|z endstream endobj 13 0 obj <>stream +d6V V+;9DEFռϱ΢LS>I}3kdC+!M'$V|A$x|ȠAOǑU`醚"&kzv +ZSUwԆmj6&)&j~u ϳp]̱y7U(``@: a˳ ܍)1Al-Ek2c"K%# +qD=D1 +,Ц+er #([n@4^шnIa WQ$TZ$Q1> uBa7C~Zp^󱃲=' +nL"4n̝3`qn/*Ҽ,8ҵ*doqe[~mfOJk뗐to +] sca nUVMTpNg K +bƳH$)U#)` 7?BҐqkD5_!8{͵AfԲkJPV"m,wXr`;l|QqGwWRPfx݂DoINJjj-&j-LZH8,ZWq}JJ}㭮RӻUR0Q9-+^r3V[Ķ0ҞٕjgU\dzWJ+mąI} #]qXRɘָzkƶo7áhȳRɴZ_@̢]jY^%/s#UL5ڴ+ODfsiuH8a8,4Y 4 0h4 L@>"j$@B) #)>Zc/VKtyaRHSj-3JٮT+cm]*zqIx_~xY?ƬL_R}5ed6þ[eCc6]17^vd,]{aR-KS.-^#_xYhO^Ȭxnf»Dl¤ą63U)fV":0 ʺ i\WTąJaequݽKV +wso_tWI//Lj3U\_*6_l?ZY*ig PGe}4E8 ,&¤*j[?T+msZZ7r1e{o}ډUhZWֺK1{\4dc0eJq}lr݅I #eRɬuƺ¤Xfge}vU[j !=h@$ E2 ä+UX\o~oVu #i_ngmޟG7Kʙkߵ9ٮX po_hz_Iv<._70D̮:UWVڽ)jNQ&<@P!pNEha& + R y&=Q)I$N}$=%H"MTIH$ 5U&J"Dɉ@$CDT< @ h&"E@d"B : Y?xDу9"B n1- + +*g InU +d0u_F5hWޢ$ 00;`méfmw* `"`ŔhE} +* ~:"AWzNY%Ћ}4aK3SQW]zY )l|ۚQ<3Uto>@ 7E-sl?]hE4pbPN֮fw@|x +uJNy̥aƛ-PЯ^/̷O +d"Bh#L1TZ,R8OF*-gSOcuf|Si\w?Z3*dM1aYv/9; RHu= }@{UF)o/dܧGIyТïсRV 5e^MD;|nGT2?dv5-8G! C+@9H'ƝHgw ^>:(_Q#_0L,<xVLhÙqy~XE]ux?d䗔E-ڙQhk(XA[ϓjȯ OJ.(CB퓇!z}قhE)/ldd]mqưT'HA(24 9v(΍|no-I)V$Q84B|rDVr^!}/LěI1=2K 8Rq2Ktk%vI |;hkğL Y DGls1DL/ #o s<[;G@~OYqV%RWj2TQ4L> a ԹƕY%>`R`yv +ZrE;]V\=$da&NC !ᩪgؖ:ɠ*r  풦PK u +F#.Ej +ѳ6,(&8x %B#4^KPUi5J/b4̢bޕi d AR/j~Xm-Z 2n+89 ,3Z`t^d[Z4pj,YNg,mcC9y2Uq?ɾG{"0XqnSyЂ_>HDL4i +̗3dv;P93hІxȓt^! ;շ.[jIm gVV*klDll?!5ɴՂCz1T(^|ᆆכ`KD˕HUrdX9W;&:Ļh0+o H>xF;$>g *NЃaOhybIzz8aC + +^vO۠i':Z cv)Y=3D$4O*|l趵(vjD3f>B)c\W6(7mӷ hq +ΩIcYjVH ֶGlĺ~1Js@QmHxij,51{ҥ2L s?Y)'; uDVm'xC)ZozS 쒪0gTN/}'4D?l?l5^DT,Hpj.3mZYC8S&KLa ;ߜY{r9\'30$>Vۿ7W-=pwl.v~;vrU*ZSu,=gskGsk=;tr9rƾz߹/~E9vݮKMj\+nQijzv\xl$5w=I)GQ߾o7;ώk5SZ;\emozڎiN7tSo88168Kѡ#h4L+[1Lp: HIXG$49(q𰐘0Gذ$<H&f6 +iFz`rlkM\͍,eRG4g6 +"̮ٸhRrAJ #a 2>LR!JۄEKEThAJ%#O`8)%~(&{AJYi%' M8*R*4@nRN^;'Zio|1>0d@)O4LLzDvl@, j1`(ͪrvDT~JR0.PIr"ntIe~m1\,T뱶ASaIXtzL@cE0FAOS^=0?l; gAYeSs=Λ_guF rr̲,((MpmWU tQݕy)V)xX3c?7V "n嚛^( oe7kf*w:@!S'|2|~\O6P~MF˸Jaw(D<޹J ߃DWX"lq>\cr7&[@ei(K;hLXD]Z '#Kǘ Y)̠t.'}s:%ZT :ya,@l?$&(=7ɌέIvEw|Y +p!(8)x]E(#I!#QIȪ{99GLt5jxW67D)u|]BA6Bxf1%?P\ zI&c T]7i-5Fs(3֓Ԡ5k&´)#N/MgwCgL3G'/nLK#2/AHU ɐ[%P."BL$cik΄! +i{jdp-9QQcclt /J}z{:GJ7h<#ie#++*j^QE/1/7x|ϣ` ʼMmE}lw.)üէƪ׿#Evxr3I!ФT>PaJtjrM"HډvdmāFX܎H&Q*jzK\:7JKl)9xx!g!'[_AR I;5ESeF{+VhK +s7 -^,4RU~F.uUr{ +q0䣢ɮ<Hbcc2Wo)ULsdl;ơ4S!=l&ݤ{["ݫh BH҄"ܹb%d 1aP.6Rs+R9{ OFEU8vtgoXkB$rEI :L>IIV\D[#$WM*gp#,e՚,^,,~U&`F4ƣڳHUy`4®W恱A*{ʐ)s#2_Lom+fTc}4yB@ͦW$гBpa1ʅQlcz `[y Crr8kAiɒ!(Er4vZ=9/娢"ĺ7ܩTcVp"Wyӹ&n^:߄'M_6dMCYrN'. S޷Gn^iLdgIDqnTuK(h~2XpFU 椼+j{p a / vz#FM7SUƖ^YRr1BtbCB_8Y*L +3ןm4Ӕ?kw;[lDaU>)ۿҒ &3փ *2 Smӈڠǃ9iyIn/Zt`[+xAkny^|/d yޭF+Q݂lEG/xTq<eMOWGɚ;-,nx*6tdob&LJpm6 + !@p!/(TG +–C>Y*T4*Vi {9.9SL|t2y@ߝ۶%R9~WJcK )YvsZ4K0\~u5—h_&\Zrc|ީ=gW./PQ4Qݧ"#sp0U'S,mAMD>NZ}Pйh΄Ky,(< %$a>gI>Bµr̚aO3x +H +F +{R6\A*&$ia,4ٌ1 RN Kz9$|ON}9j^>sQT62^U7*#vc}5N!Ga_u +-ǿ~LY{Q,I}6*Gy)X,Q ^ wL +@*>zfxЬ/ +pς U63wNʆLbCj`!WiP?,TZ +uB,i*\L%R\^* Z_>[}(N Z/Z0r 8f':ױY(vŮ0ڪd2U"T!W*nl$GWjr()("HkN[uWed`ISow@60iQJ)"$!D)`F3|fh"qQ&٢HdbI"Xi5&B"6m`PL~} Ǥ&4(KGTx:>N.AyGÊ<'eldnҴ +bEh +*5dDN@af0/`RZY~XLg).)3MGoCHZR`l^f@ @BrHTJzA<Ѥ)TRj(W{2Hɏ&ϼGHּJbNM9N_밻9Rݎ{~ z *pbB h|l>-: +en%v8c ) J#BsD4[c&JÜ.ݜY(+X)+vONLE? b F( a#nybCB]5].n@$Mu{Ej(mܶ!eKD,ΡD$:M8&8l|XxQ`q-p!BGYI:|5tPN*uC0C N\1]fN -#`Y +S)SڢеPdP`A<.B'P'YBpK0J'l!!2PjWWD` ' l&} .Cx TJ$P&|gA 2WAA_G)-aZ +PhL)@LE 4aehHmA Z@*T(v[u(%I x`Ʃ[vtQ/ڑe`Sdt<}Y~YSxѿ"şCjߛyj0ES1T7Z&< T*0Bdаɋg3H \T8ؼ!54!qFcˤbݱ0C֏]aiDD4Ɉ[>9Mk<|D &,$WG* +qb>(EZm? ;n>-nR@OC-Nh i_qGnWAuU>%*,c Gȗ/#sOc /W5\ 7 +q8GaU<60lM! 0uy>K7(.Lt.u,'ґ(Eo1H ٱSn\wG^bQK\!NDK4B*88-9y.6fAʹVIHwǥ!RV#G@R$Uiߒ ?INYq xVazN }1C$ sb9S`B]rt8 ]tf ^!t5b~D{@,vS\.N,8SۊPP$i.Bg-TeK%Ȇ/mz,IMdr9!3ZņY4D5R AjNCS): +-exf29+!ԱU0h^XE!j>[9LT3_|~5 irumZ,Z}^&!Va֩7X\RLlQ +sTOX0t--=u(($]!" Fwc#o.s\+2m&;! >FBәQGvcg}q.mh/ rK")3vc$a' (7_T4]ŭ:ֹ{c=m/my{| +`a_:;lK{&i}5*>`7F9)quE܆#nc)B @pb)/WU/jU:Io$ Sk2 7&~ sa17Azw6iA"/$Y |#`V+R‚5al ` ܐ(j͆C 5#@Q̇9Z#  @y"p AL7W,@t+K&Dyi|p5A +{]"{$:wehlEK/J^)U*B"R0PSPO)[h - K + ')0\ I!B]gi&+M+PX%`qlx)NEjD?>&&*ML$r=_,&٥Tw7;&y"D''M ,-9;Ru,,G69g-.5\"l(`0?;1]A])H6Z<+Sz펡>myT~rB60Ń ؁Hi`GJn`dk &mQʤHm$_ɤ`RwfI;d?zfcl. 8!̗ޓ;2U;Q0<"_x%&Yfr̞6@i?![E ,R)ڶCUf) , ?}B%xӑ~ ~zKE}p6cd#o%FV"Ќfbu4-̄CRHdL,V fbQބ + +w#r祯HSt +lPLrW䭗UHސ!%-lYN˼ 3ց~¼fal( qeh*B{Vp&M  CI5*bC:o 3_g4),-os2<ǐD8oLhE470ZC4hƈu*VwcL¼:o4)1G69 4Ñ.2fDY=>CeA+^ G $Tϩ4[\ >i.co$UOt@tg(7"BJ"}9P#1Æ1dҩ|{.hY68PG&w̆G8_y ゛UŶ]j(čeꕣx5WQnh@@[`NkGl^OrC(ZJcqvqKnPSk(lrs~9P_ +&NgltZŏo4r`fj͡pb׶ Pg 'bn;tcg,1΍H 'B%|)0wQ> ;yIQޭ)ag͆=B=m^!}NbLb^،LhN؍k |'-w e-q}K|?1SBb"=tdۚn~۝H~j`CQ9F*c#aF M0)ý&r TH|~jx>A4:5Ou,˂761rc-_xMl7b>Kw|X 5&&Мh 9?IQ_ZRY6t}kEB; +g_)>hX-#Z'{H3 >+7>\!;%Ȕ,̑~Ā>5CL03mL iB[yyFcxh2Q$*bغWH`ʹqC}'KZwx<nB/།TD %xM3ק9c <1"pP(E'Ab"uI:4֘HVjR Iotkw/u\l͑Odg*-RCJ5O&񄺪~1kSC2Η.wR Ad_?:<* YOg;M=u6P୔#zkoq8%78ƢFM(/f-s2a lCg~Wpdd1_j+lyCl2u +|Vq¤(i!C>3m JSl[(]e2אּ5KwKKRn'Cmq۹(qR+5rhs$A 8!g*ZeCw`j$8`,AFio +&u;=p(b)'e\)5!)vNW>!y.L I:TtC-"*zD;J$?c$/: OP9ߙ/Nr/u-G?v"RQ>?ZCMozMΨ6̸j`e S8j zYp}Қ0?S~!t93Rh gв9JfV Aj^k; ;>[l1arvYӕc=MH*?q}7 +[=kbv X6(E1>cLhl \twĀ]yz8RhLӅ(^!*(<}مf}np&l}c߁Ezqg$;çؿ{q|aNb'Q[v3 \څ6H kEf_{帷6)5s<`AEbXgve($LhasqX%|&b!L01gt~WB%7OΑΪP_z~b`bQ}Ƹٰ7r3|%GaoQ 0#&lFfՀE&U@/gVL&"+Wxl +KpD:A@r 9!څe +pݩm]of~x YDs[a-I2),XCxL6Iﭟn<ž}$oZ%Ɣ˔,2I9&?k.dvsvv㰏2EjЦE#G"6B% -N)@X"و\$l<'i/ۡ~d) [(qZf;>bcC]qkyD9 +7cRR>ϽL%~Rt8(垼n'$fvTR'/ [P쵡 ks_oFH;/N8Ե}R:&<@'9K?"S{&W-F&(0DS+i5G DVDe?ʃ&bJ˜;LҡѡLѡLU3M/쐋}F Ű*(4ˢ12gSlϬ>5eɪ p-.~/.žꯊ͞^#{Kd}F=VX~5s冄 |UX~&GQPUqt/ҚTmPzu_cjzUm4-yd?+$4aP&TZk:s&4kL5f-|M>,*C8RY꯱I9&uշpYHToMHQJ2Q>p1^xEtnʑ[,XL蒄M?7`r=f0iڈFtW?W_lllvߙ:7iҝ~y祏tM++LCb~áЫgBeL,!\tZx00D -N0: L?`޳|ƚu ''M[½'Mk}-d9:E&VF]nj 32jh7oailgGm;K0aިjyt.C?ʮUmgRGa'W.a6}Mpg8/^0"-dLX3Mg_é+Ь+ccWggI¢M[6j6Z0[EV!@`$P +*)$Pk - aX-!fbZx 3 fb͆g',>)S, x%XX}uzYügVЬuȞ4)_s 8¨%fdsd%fdm̠G30G6Yb&}泑|3lQn˚8r8-U`ʧZQ>&i&I|.*)ˡ> rQy\ w;1-t\.@I pjnh!i-dv$/Nanٴ#.h320]@8劔^B +QNDO6>iyݹ4~'S3' ", +p+wj4XXOLxph BS’vSݭQj[E0.d!^HHayaDf@TjiQ'~iQOY߬%WbsiO (bصnp_? V곾% bLګdz[qe)vyy~X`9kD!C?.gf#y I^IZgO 6/!;iA($kTw| l~`J@'&_al, XSxЕYRѕRǨ;Ij&cZt^oj:#NEĺH\>&c[[qhgGԇo\ ވ@%ِ֤}ͣT3>>}7d.hgdSmᥐH9?̤{ys +g!Ʀ?9&Vr<D5h?h m@5(;X4IH=aemJeL z2&*f-?y-L }'G:P Ky#b3GWp`|BBY `, 'aӗKH%`'A~D?%SȓQOWO6/g\|HB(`dEL#hVD>H,!p JT6D'|lhcm2HtҤ(Evg@;̖xdl8McМc,E6Ԥ [L?@Bf*KWE ԇd\t$lF{``?paj5Atd`Z66\H 붪^,8S2V9@{P6)ܛsJu*mD#*KHPCihSggdORĕRJo,-d M`S|-+BeQjdƆ!D_B"!Z\*9RVʑ';g'(rO*h .!n!v|62†Ȁď,$k6ZeCQ3MU6uu?jѝ>!sma%[aсt(c>a"NG}\}VGbFDhHع;FCA`H7HF&>K(i0@zȼ,Y$jlyMm& 4IBϢ5hD&9aC4}w~!zip5"0ah>!Mâ,)ԌŹM;GBgh 4RNd^541Z b2@U"*/-xRO(,8! *f "laQ%4f6wx}yIw:%L#uQ9V$G&F1'tsR9KeE#M 9g3O H" +(ɽApm3ќY-;7¬XN,dC "aP" ~مZeð*uuh7QY[ `eU߻pkA:%}'L Hڷ,>j@x\rg·BˑͲ[]"G?r߻l8\wCK;S#Mϝ + qW6 Eb84ZvJG_[o4`z'pDid>>gC9[ IʆdEeC5>ٳU|Q|*43!Sܤw26gd:Ey}V,!JWT$`C) ++*7Q,3F32!',  G% +%#2}v>T6<\q6|l .TNP$,a` ٹ\Ͼ[կU6\<3 "B-KLp3ղ#UGeC҃Ϩ!ra5ƖuAr0fY 9ro@V#fBw`Li1 X+uQurwQ^]ZR"K+[56$ta@ >3x>3T%H Ga&u;̯R3ʭUkZIt&G9 I[XDgNs]9vAU\#3~77tI@fC(N`N M>b|<+Ndkj]ux˘0]p(E)j|3L,Y>>kOS쨄5%o<.wr0mI왔0) w5q7dUVl<9/ȟ<,Dkps9]E7?.qe-*la~IUt?D|E..FI%-TSe +jK_B?A礃ϱ7Fmir6Iv`SFwQ*ۊ7~_{K]N"u`y7 4$l@Vp#oSӳ++ih$tCWORfbҊ=7h|@X5>TIDA& bj:ysAK7~ah`sx !!p(FnQFNPfs"RU~/r6`c"G{TQeĵiYI25E:hŕ`K+8Ő +3F9Hqp=J6VYOQR, #Ye!Fә~L>x R2FFu.Ý D-j{!eۙB(Hurv=YNU'h?1ML,`;@+TʕvEaQF'?Xǵu=0{W} 8ALEzo*uk KN;Zn^]omTHi142rNZ>)9''övDnl 9hBWiij7u.lnYn:B8cxŤ'E-+ڽ솴$*>,|ԑNnB{e7£鈢̲.30 ߯[$N(iAD~tۡZT}mFqǽTo;m݂W_lIb|@9fN3sdge,GU)'Ѓ* .3`zߒtUGeŠvRO~BV=I\LP_}::D5q9WF$1j"&Ω2vo(7I.o12opCfj'k#7hg2oƔ?!: j. @`'TM-_O.o:̼qGyNOph9HU挲\QzBcB'TZ 6Jп>^W΀ pbXS3'f h򖟉7ѨN*\N&`^4x!vzXt*'+աɥa>eOoabg B5 I,"$wS,Q7&U[ +'TP3->,gEjt,haTw2GY\3㓈x_ۦ^O%p) [m v%ڒ>iH<)( ؏4$> N/kbY!NR@G\7|RŃ)9ca5j*8NOR^dA?1UxlsjăFn]DLFq. )w^UE^\ldrJqU[*1ǘ )q-,xC me '~P r:QkH5^NC>OV%_H}_X,3U%bъsG_)`Z 6qh,`J"i5ў-LjZ9F#uU3톖O,Rt ϣC{%Tj2'72o`ۓ^&/\/F."~=Jh7Wͪiań=9{Jﰘ)(&Ճ.]n`q/nx oAgoq CydE\$aA2O#ĜGVloJJvRE"V !N!S >3\LC73 4Ahcoi #0n&N.݊A ;h!@B5:1;Ff;6pD[OOa@+M4ti@_ri fwxP{DE1]`Q̊`KzȮgʇS>ű8i W!Kl}.#i}IRm/*g*Lۀ 'Ay%oQ"W\\>3Z8R !l撻6(x[5*Ex?u#.Pt?0ڲ$n^7*q."Xq=]In[ӄ쾀Θ==U/lEhnڋmq$cM~Neơ+sj׳=脌m gSWtV7|`nŪbem\(#o.%FjO: 2^U`!nk_ht[Tvm"[XBWC;#/v|xb%!W~G߽ruz~D3T"ز,O{dHGDfS,`"cs HHI3<sdƽ&hP! &Yt +-OIͧMYZ[-0u؎>WL^X7TɦK]^-ak(wfB!qkQcf7g⇘;MHKmD@֞kjdGuı#:&CW+fMk8ڌ.=!K(u z:gH|_|~8_O D//Fx4OZVke͎^S+?ģ&$Ú0 0re岆9fܿ>V&jHMƺ_qn?^4@8P=G0::=~;$ wBHd}Zٟuqf@]%UG@s8(v7nK1D#M nDEzsg,<ӣeK^xh!U=rch3*UHXfy!KGpGn +Z%D2{W1-Gs?nѲΝsEPH]9hL?[$)b/)I.g b^PQ2PzrXێkD}%<) 6Y?wUt=TVT+F3(e _ oDvI +EW5S F \GgmxxurtIZ_mT!ei9 Gc}uLkR2,hL10OJH>qدeݾ۲]=Xqu&g|D#5"x m r!X%D(Hi;Ժ98lr:昐-&D[<݋r>#.c*.YY\ "Sy6$k8G*x g&+ӜwOL @Csy^MC=-m8[׌n-;!]MץofN)B-s"ZZr" \rs@~fj+9P:!|f&Dr}̍w8vLɖ.l~URmyp3Z ղu[nn2hRgoc:n9HM{1 ׹b'_BڷT +ŠRr\̿:]?uQ.BGȦ^:3v» dwt@i",ى M1 Bq@Ή0QVXMٿe zc%z\huGNk5OFz+ʘyp[Qaf&}2Hz„tUh!'2982,/-[۩T7ӬZU' xܤzvnb&ϫD4])Fc]/}Ėmgiטj@{潠(uPøF*=.JFo$a.ŝtm&8qUF6M*EZ0ŝ3^ۖ&[Sܙj"LCL=N6]܇' +YM7,D-bRg׭bSj%'^ɡ8Pt2j "\H(? :pX,hn+/;YmFkY'T{hNB]D?6z +IOȰKm*ebVoq]Sj<0I|A4tsDp1s-B4DŽ}*, ϛI~#6N\2g("0o놈&QZYT%hpa-0'"a}rA g:ƻO֧B* os)m[v`)zC&G(o!?`*h}S$:F +k: T)A9'zX@vwef2yygO=ʣ2lŻiGz&ŸڧfZ[%7]s$u3?ޤenJ Lt-UdTuŵ5M | WNi/[M=*>Z-әқ+˧,\v9Z779D(-rg` ˅~ʦNbv9Eˊސv\8Ύs"sLOm<\nGw\[[?rQ2 ޏn İN 3ɍӱL]_(%~+rfX]%ù֐tjv +ˋ10!!xLseIӗR][ƪkBGG$я#g\3TEjϴout15oE?|q}/ okzg]{%&n][2 WY^R,f!R_b̠>PK>kReAM=-mE R<=(DSD ɁnlF@ wh8i2` mJw}!0ir̰HMYٓW+b]h"ԊHE@]&4H%nC-*B(u0νfnaT俏y;k.Wnu;=(P!{rqPQi LmNx&S3({MbKfJ./Fk%T^b{Od FA;CXmneȡyR.KG~F.񅸶٦+ AP4r~V4Nt5kwJ W4ȋ|āT'2^d/6K7O*3E;ih iobMgGX쪞}Xo*3Æ텲Ԑwl2\C5 BƱr+Nϴ  +wNsCؙd5|q*+1FB:k~;n7Q# 9%~㕐 d#MNRKBC_eEF<?R$$DBѐ_ZISr.nA-DuDY$wFx0gbLd[ӂ WxV<z C IsgI>.--9M* +@j +qNqh/DHo4L x/.f; ݩ;]]_ѭpEJp e(eO^eA*",\` !mm*X%]l,H '{sqھ?)^6 I]Y+\%:x'VgO~{6F |axuC]_v>pm0@ 1d:74!3]Ӊ4CZb [Lj\!c-@z<߫(?eN($V8WC|θ\9w@ r m˫Bjǔ0b՝XB O%OA\Qx8qPT}X4`5iɁ^c" /JEe&N +FmfTD_[@c]ګ[@vW2c@3ec/'Z.C@ӎ]goW C4bB ڜ/ DM_O*f<%S›wLNC$B{+? +Kn՗񆑪Gg59we-Bt=&;Ћp/RvGR `> uD+As4^p6iԳXN1AZ"A_!pt{Eːoz:Oja۵LR"E_u8oPL ^ ?D@0CwVBh1"),޲%G2CCQ 9SA= |XH.)M+KVꖙcKb2E07z4"x +-<%8T.7T&p_e [6sFRT +E + ᨧ#; lMw\]%"m'k0n9iKO?UՊL. o F*+^_t*ٛb*굝MN$y:_qB{6f ADP̈g[RL$6 /k#Dzn`Yp%@afHnU.oVʊF\@Ə +Q6̭4<4h6ׇJic!J-aӓs0 iNF1|X<K|yņYвV׼\\w3ơYzp%Yz(^LW?e}fr9pM=]VQTBV YhV:ԃEB,KBD;DŽe?sB3fنy{L {B3Cd_N}fdA[u~Vwya6{ ل 6TpKGx#E+~:u@9GP bĐ6ifWLwّ#FtYd V=ˋ(X "_>ǎU|I* PB#AXj>0+'B%4@N6( +0CbTqhyIC^ΣKޮ[z15,b+Ѣ*$ !q@ δ%Q(Vk_Qs8Xf/M#>Mk+A7ϴS5ܼe {V:)4-Ίj0jYE0]+]  B^vK⅂_5}+}1+cf}us0-Xv5D;=Uۢ#^o fz6Fئ'ǩ G*F *Ps`gٮX;RPiv8/71}s{2bRˣ s.ɜ& tWk.'FE4 ;d*p&1y_CXea[4sRfSur/ I.Y f7 QVBKge:tumlAͿ|90zpGk^ROd5P9%gC3e{QVV:!vQf W»=DLf0ẽl=QjrI2|/z9b6o!ܒI̤܄B1k;P]?_ Z|KY1Gh̹ +5haGf5 + ++9;.X/?|z-?u2׍`ϭG~< 6Zn1=1HZ~GòH":nl'mDt̶LEG=րף^תW0gT&zq)ۮwLrIa=*=9~.@ub(䜛5J +MC_}]+eH0zB}ypȏL)D2,Xlrn}wȾΣzw̜Ysj@2X^E-:*xڝ@/ZpN٤[5O m`63g8xpOw}ɍ$_EE4 ):?QMY9Y xk'{)p!Qnhe1z&g2r{S!g.A'2=s|;H#~:-,ljO(tj4$o_hwGIeAJ0TcM|Kˑ WA3ٷ>gŲ4N\gvTQ iRǁƐ\ĮL+|EׯU*ϦóvHXWh`F lfb‘T +C L0 Α(2 G4a ΍P> +`˅zHx輈6D1`3 d'FJj)cDI <B˃GBX8 + + + FG +b6 >&.  ЅNC& Q8 C> +\0)$<@!H􄼠kD*F¦C93Jx( 8)z& >. H6"v%qxlDЄsP.RNqP: CqPBI*Z>D@@0^|CBu(QNXED(@IDIDID h*sJ ltD +R6NCB<<# yAY'#/eᑱX0!A<45@MI$DRRABbl`ipt|&p#ВQNQ@HpC 00HL\(a4jqP '." 8^8"!IB$@3"8+ +BsRX }Oes F(f\4#,gCd HdmBB4T#"$D#46 6ʼnXUrb:&hL, H/27Jba`BH%6 3,@1b=`F3 + Y @ZX`!a$&gz `@$&;`Ё'3<3 6 + rD + CL,Qy``Hd Z0AAЉ@1dž + T:Xy9 JfF8  'I-J T 2Q5R<, J8mt6R.SY0cdD+ !1("!"![+mjZϼO|)e7jT3]|zgɾLyOɽZMʨ Uf7/?UvjESs]v7M_oo5e\\hn:D\hXQKb!BG#JR ) yYЁ"4YDI$$ F( XNd%G +"X"Dadildz,ƂT g388 |L 0D(kDaѱ ݂XgcBFb;FI+"Á $(" §#ht&Q*; +$nAGI$%uq 3A("LZ!q`*1 Cb!t;2,1 c%XEiQ Є6"pbf >M !E Jzh lcaIGCbbQs.BzPB. d((6RXaC!($|2TD +4h9ƀ K"I%m(BbfiB"d*2b"Ylᰭ=5MO5n6n͵Ⱦx _78wMo/M_̅i7%u'o:*nzo{>ut]{/QLnY߭ڝ=_'D6g[n|l}~|]qĩk_6[_wf:u2->;b< uyڹ-cE}oϖ=VoQVS궛Wn7=d_ZjcgXϔ]-:-WK)s=tKeS3~7V'Ldt_[M߆h}zEv\5[}:Vu]hl+?f훈ʮw-YK)mUOyߪٽξߟ_?Ok~Zg6i~kS)דK)]f}55GRL<]x ϧ;ncm_z\J8 'tKd&<[Gecs>bc/TR&cWӕՔ疦ƽ^}WC[Oku_߼ݯͶ}Evg\ڦʫ_meoeRʤoںط>DLմwo9{I)k޻Й)m~Bդ|ϸR5Mm۪n>W;TvՄ q1ח/l;5RG(JRY9y K̤2rQL*+'/*2'JNbV'#TUF2oOǫ}{1яrVSk#ޭ&Ӕ(ִ{Ysj.]跨z؋Y_}}.k}>S̝ qYضywtaOh܏kq^P>goKuvne~+]{}n5-fOjjlk'uj϶/woho[Edݻlלwij5w#/՟2Vgr%r5g^0E>VS''>e6ʽg|w_>=O5L74մ{yOoY%gM\]n߾ɸL<}mf}ȏx>)&^nsv[M>}-w}1o/dtCUp;_r+Ϫy۹'<ϥ_kjLױ5}+ROzʙhk[~4e5?co^?-icR|bZED륞j=Mhhi q|=s9bwݻn1{z/Qp7/4Lmdӵj<>]jh}[^>h712Do[۱5l]ֹޣSlZR7Kc5Եzuc5 +2ea0c EclMX.>4>$( $" a@$pHq(Kb$hHf 6]]Q+өˆẀ̨{,xoiP_0]dCyaK`WtXE~+^cVZuJ_x~S﨧"Lnxݴ)6tQT>C$EQ2i{v"\7h@GSRg?Y6O z EuMqǣ]g@'VFZJemjt#ls_Q.CLdBDC|]7чˣӄ@Ɂ#]6$\v| (~Vhߘn}hYTz-ږlo65E:qRN=VvW1ß3I !%a[!G\F:z:@ p82@iFNHuwp̈qdD1w$16q `z*M蚗ͶK FFZLJoڧn/zQl]V;]ׄVPÏR '}ԉVK12S-Hya2bL0Z>g +1o=} s, +<}/C?q-0:S_tR::WANo 24<8+Jץ+ / +q3љDZ#mE;h++הw}X?.>Vj(X[Zy5z؏ܬYJ*cQfhdqiXd,tIKЮ/L~?Ex1u( P51V) ]1x.#ߺ;ɽI%lv9=㔬>W_2.s|l"4wf4\.bAm$ $8:a8wf +u>Ь 4f{01*Q#ZA} 0ՅUk-z$c3>k_<vi="AjXڪMhFݖǏa1v!H?<[0($$ƛR+=;{Zm5"0Y Cr.9uAwAAcG ;B7E+k_F._c.q>nbvoMo8R88THetbC!ydznSMZ1O'֦0.zX7|kYWUz:WB?T3qLYg;/tcdfwTKZ_O[lqUpUK9th18sEs`x *}x7բ~'0bޫoE;{f5.,qxcnܢcЬzBe7/8ꤕb++wc厙8kY%q~mB+Нe ;Q꒰fR:E lڥ3OiJfEJF_4M;u^:֪CE-BEvO\A湣x~w=^ܦm9oVLjUTхQ(V%-It',8D7 _Kq`h 63&lAn* +:)2;]ƺKn]w +F. +jUH{Ҟy"`_}NJR/4Q(F;3DA0SC<9pk0v`ZEq⼙_j2gjb7_h6"[i+E*LF!Uζ&U Zx`| rs39h(ՁVYĥ?q~F6;uUgؚ) +E +V6Ը✮z7ᗛ뎶 d2[+"fSeJ˧W-::gj?Y2rqICYK3~#) dd`xipgX.B5U@mSTIOPh.(A + $CeF_kR mA!bÈa#왜"ԮG[/96$vԴ˸'bxjPԌ~-BmTL}ȼ\s*ZP-$`@왨Oc֟#e)P)J+{:y*[?B"T[Frznw]5e{$}_I&ٌ*F2N7n< b_4(b1%:*D0uݸ6(X>d HZ%C9>#x&|8 :qp8AxUĮGZ\ !Y$XĤoSJ F1_w'tC\"Qex˽_yPJT½*E\-`v_%ڤC֗#Mx$&Tewusu(k8)H"0]cZNt=?T, P|;wEиDP#BVυu0 ="s'g>uͻ 67{V։&)*c*%-yp_w11w>(Y(3{Ed'hpwY^xW0Cd"B{ ngC CO0 +\],Cq~o6fC||cקZ6sVnɥyEn՗H i mH'S:#xG'qEPfeab/"=Zy?e_[M;1g36#*veرdWwIX"K7!$RК첋Q9dg(SCMҴ8ĜɊ6uWǾMsR촰暱 r*NqŒJ9DUу0 ϗfǤBgFP><먐L<@1J,zx9_xpNm W^sIJ33$ @"1Y$$pn$kMgs}~MfpG>)" GwvP4XS鿞] o@>S'vZSQGy}|rY=8Q{#^Ȯ\oAڿH:yԖ Rww*5\'*VOvjKn/x/:D|Ub_"9w+h"^s*S[%0(W 7+f[En&rgӰZ[}MZl$ QP۾ym^݀~u?v[_9Z04vh0.T/| gw-iuLC?HޢҪ\dMx&W+$5\´Z+=)Z"*쥚9%a]`i2K^~Liks\w$rxVQh\^YO-ekzWA0ѽm < +nY>M$?\?Y9nQfCskJ\BfN{>"ײt݀"Y6%g5_3ӢgyHǷEzWj~\ip0!Y(p5j˪iNf18 ! |~'2$.rl e7 r8XeqUwHuX8n VbJJ,Sł'"vi~7SDӧyRAdR=&ji[cKgΩBŅcX[v;vIGf`gfc`8'}CڻX4饳'l7Lu@W^Z0e@ĥ2iW`r_\ψK NFg:}I"H-k% +p[͛=TuLM1R۵N{8w\[jXуB{Y(e:qy7 OwJɥLk2QXIቶ[}ew&mh9&CEtC>dr@~U*  +Xf F/8WG_֗+R,7\nO,•GeBT8u a%$Y?#ܱeݥ? 3MB7zni*K"4vVG"t_THHDe-({K+\:+9'ԿDkׯ NiLe_аyR/K9 [#Sz=X ![_TcXM*.h4qǍŦ^钂7~~Fqu}I:vgڦGĝ*sQpi-q͘킵)EL17!b+⦩SbV/J" hX)oP=jǝdUA}CXĔD-2|ײcY/]`nmC XN\ e>_OHQQ35uߙ]/`z9|Ljm1(,=%un/NPFsśb&3=& +|tP2R7ܤ-$s$2un(2 wd^H汁uRW>'};OHK-KUuo8ȽP߉6h4386r}= y>QT\ +0*QR,=2͑rA0\ϰAY$7ߠ9b7zߠ{:j4OPN/W& 7FrjqM *ˊDkۺZo m-n%q;-0yv%"MIqaK鄖G%ZjɅh;Dbj_Z!%Y#hytӘҮ?Z[t-ƎȎHSf L HOj-f]b$!qN 6S8 5K\т%""ix5uG)jM] +@E-t WΈoYa©o4H~eŝ`3rq |3Z -/3DA"jj.'+Q| qhyv|cL+kW"-);--fCB&#俁 8l@{r@\*Vrjʗ]Iv V=DLP̪1 -~O9^ca(+wZG`A ٘ btːgT5P_;bz CZ*6-۷ n%%E/q5Z"\Pɽȉ隥*R +_ '#>x-(_3ƜCc6ZE|\ER!` _R!IV,uaܷUK9!roF`z,w!]ٲ|e?*gj&t%UUigsgO^ Aff;.'a;Y0l"/M9i^NgS*DI $#yX5~Ujv +| CAC>9~>P>nu!C]/:9!b$N{ܦeÜ Q* pXd^H +@(:=rU  D*Ao}cA:<*J]ڹj,x ?n;":_X|},soݻTXy&4ю ٷCW/G,t嶩?P2uVB(2ѕC!-Ƀ]om FmMή\+ߚbe~5ѕy2IrM)_Wr,[x֦d-!G7+M\| XW6{pMz=N+'d^,t"FGC4\N,Sm>ӕ /Oخ|IW^*AWjەҕ:fOfŴ\KS3e +)1_rtlv1񡒡BWePĮQ _d#F=D׳}W#V4qǿ;r%e>7/m^lr,˘[ҕE]mW+RM+GoR=r^3ll+[2ʯ@]9!c6)IDWͶو&l9hV+XPVTF0 h|ѝB)Čhit&Xz-8k<3%(^e: ()_%ug]K$-s3Fn뿶uȕ`8e { cG̮\7qd ތ}7xJ[W ,wnS7 ti9]T +U?upf1KJ!/s&8^?X8"zg+ +3ö< ZE98{B\x嬃["*0Ch/.pj$c}#y>83U: u}Hߐe@?Br^j

h0 c7 "Yv|c?#hELx]h%3{Ph%g5B3dW׃Sf͠ &4w3¯J̑;iqc[ھ/4M03K24qy3n.4#rVgG +u6cn 0y +??3{mT |{Kh4(L+ >4[ ?>e0mIqd4ڎ(MR3 ,p=eX|6/5(BLz̙Ӈ9"ܓc$TѠ,f2+%\Gfi],]M\P}S_@TfAn8ׁڶ&y[%?gˬ,& iG &\l?F){;lxkł] Z_=v\}]F]5Hyǽdb0X ˾eˎajIUW"^͞LDz2}'ְ6ixQ̩&] 9r^WtEdA  +Wl'Pr){T1]GĂYVSN `\{VV,C*޾85Ty\$YSd3KKeDxhM6D;P#~E|m{7) ̈́4ͺ\ٝQ.fW+b3y@&d)^EM$)HoK>f%Rcʫ:;ĕ4oY8*>9Ms \ׅ)R@'%2ޓtq;8BhM +yaxkx`=(/EB&MQE8ED. a(a0/8:ŤeF|+f, 82ʼnAWVFZq2NEyfn?ْZ-KfAp6l[t+ye߅hs7ĢDY;6l\mHi$]bӜV1ܧ>X:TPvFlk25kR EߒoY%G?l[c[T$$n0QQۑмLD\ S6q67? +Pc +8dz]T?3a$onԂ+*{)-Ƒ'%_a%NL$n.4yjUFZ3cAw +'r9|"*@YFGU-qJ^DYΩIj?vβk {(^ChmMYj8* jCִ۔Gn%.̚a+I{4e6B?#N?ƺly%no\ط `%}< ir?j|d%-[ : +HxԆ6n%(vIÖVOi,Dx/d<ʘnU\[j٪Ҷ.P}-Y<Xy4d}HCRJOLL%4 zwLb%i?* +M}k )O8Y8? `qO'IOgT1-;0ܻ$&.6bzߊ<~;f= +!WfE{>έ-aE\@JE_qFQSK @ÆmE)4$&V7KEm=LV+6Um}QނC V)M(+bKLӢl~pf=+#Y;{1G&ъC%2n\mг{yrR܊=ǫöL>~jwn 9F9;IPR*T_1oLYױWrAhm慳cSU>\>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX$6Ra!<<'!!!*'!!rrmPX()~> endstream endobj 22 0 obj <> endobj 30 0 obj [/View/Design] endobj 31 0 obj <>>> endobj 26 0 obj <> endobj 25 0 obj [/ICCBased 32 0 R] endobj 32 0 obj <>stream +HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  + 2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 +V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= +x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- +ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 +N')].uJr + wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 +n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! +zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 23 0 obj [22 0 R] endobj 33 0 obj <> endobj xref +0 34 +0000000004 65535 f +0000000016 00000 n +0000000147 00000 n +0000044956 00000 n +0000000000 00000 f +0000045007 00000 n +0000000000 00000 f +0000000000 00000 f +0000046575 00000 n +0000046647 00000 n +0000046842 00000 n +0000048369 00000 n +0000113957 00000 n +0000179545 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000229550 00000 n +0000232542 00000 n +0000045406 00000 n +0000229850 00000 n +0000229737 00000 n +0000046259 00000 n +0000228976 00000 n +0000229024 00000 n +0000229621 00000 n +0000229652 00000 n +0000229885 00000 n +0000232567 00000 n +trailer <<8A56A6F188E7499DAAF7B91AC94B751D>]>> startxref 232778 %%EOF \ No newline at end of file diff --git a/staticfiles/images/favicons/haikalbot_v1.png b/staticfiles/images/favicons/haikalbot_v1.png new file mode 100644 index 00000000..361b5247 Binary files /dev/null and b/staticfiles/images/favicons/haikalbot_v1.png differ diff --git a/staticfiles/images/favicons/haikalbot_v2.png b/staticfiles/images/favicons/haikalbot_v2.png new file mode 100644 index 00000000..cab82cbb Binary files /dev/null and b/staticfiles/images/favicons/haikalbot_v2.png differ diff --git a/staticfiles/images/logos/users/li-yang-5h_dMuX_7RE-unsplash.jpg b/staticfiles/images/logos/users/li-yang-5h_dMuX_7RE-unsplash.jpg new file mode 100644 index 00000000..5b273235 Binary files /dev/null and b/staticfiles/images/logos/users/li-yang-5h_dMuX_7RE-unsplash.jpg differ diff --git a/staticfiles/images/logos/users/pexels-eberhardgross-443446.jpg b/staticfiles/images/logos/users/pexels-eberhardgross-443446.jpg new file mode 100644 index 00000000..2e62ff47 Binary files /dev/null and b/staticfiles/images/logos/users/pexels-eberhardgross-443446.jpg differ diff --git a/templates/admin_management/auth_logs.html b/templates/admin_management/auth_logs.html new file mode 100644 index 00000000..26a2643c --- /dev/null +++ b/templates/admin_management/auth_logs.html @@ -0,0 +1,75 @@ + +{% extends "base.html" %} +{% load i18n custom_filters %} +{% block title %}{% trans "Accounts" %}{% endblock title %} +{% block accounts %} + + {% trans "Accounts"|capfirst %} + (current) + +{% endblock %} +{% block content %} +

+ +
+

{% trans "Audit Log Dashboard" %}

+
+ + +
+ {% include 'admin_management/nav.html' %} + +
+ + + {% if page_obj %} +
+ + + + + + + + + + + + {% for event in page_obj.object_list %} + + + + + + + + + + {% endfor %} + +
{{ _("Timestamp") |capfirst }}{{ _("User") |capfirst }}{{ _("Event Type") }}{{ _("username") |capfirst }}{{ _("IP Address") |capfirst }}
{{event.datetime}}{{ event.user.username|default:"N/A" }}{{ event.get_login_type_display}}{{ event.username}}{{ event.remote_ip}}
+
+
+
+ {% if is_paginated %} + {% include 'partials/pagination.html' %} + {% endif %} +
+
+ {% include 'partials/pagination_audit.html' with q='loginEvents' %} + + {% else %} +

No authentication audit events found.

+ {% endif %} + + + +
+
+
+ + + + +{% endblock %} + diff --git a/templates/admin_management/management.html b/templates/admin_management/management.html index 8ce5ecff..688d9473 100644 --- a/templates/admin_management/management.html +++ b/templates/admin_management/management.html @@ -2,17 +2,28 @@ {% load i18n %} {%block title%} {%trans 'Admin Management' %} {%endblock%} {% block content %} -

{{ _("Admin Management")}}

- + + {% endblock content %} \ No newline at end of file diff --git a/templates/admin_management/model_logs.html b/templates/admin_management/model_logs.html new file mode 100644 index 00000000..3362cb52 --- /dev/null +++ b/templates/admin_management/model_logs.html @@ -0,0 +1,128 @@ + +{% extends "base.html" %} +{% load i18n custom_filters %} +{% block title %}{% trans "Accounts" %}{% endblock title %} +{% block accounts %} + + {% trans "Accounts"|capfirst %} + (current) + +{% endblock %} +{% block content %} +
+ +
+

{% trans "Audit Log Dashboard" %}

+
+ + +
+ {% include 'admin_management/nav.html' %} + +
+ + + {% if page_obj %} +
+ + + + + + + + + + {# Dedicated column for field name #} + {# Dedicated column for old value #} + {# Dedicated column for new value #} + + + + {% for event in page_obj.object_list %} + {% if event.field_changes %} + {# Loop through each individual field change for this event #} + {% for change in event.field_changes %} + + {# Display common event details using rowspan for the first change #} + {% if forloop.first %} + + + + + + + {% endif %} + + {# Display the specific field change details in their own columns #} + + + + + {% endfor %} + {% else %} + {# Fallback for events with no specific field changes (e.g., CREATE, DELETE) #} + + + + + + + + {# Span the 'Field', 'Old Value', 'New Value' columns #} + + + {% endif %} + {% endfor %} + +
{% trans "Timestamp" %}{% trans "User" %}{% trans "Action" %}{% trans "Model" %}{% trans "Object ID" %}{% trans "Object Representation" %}{% trans "Field" %}{% trans "Old Value" %}{% trans "New Value" %}
+ {{ event.datetime|date:"Y-m-d H:i:s" }} + + {{ event.user.username|default:"Anonymous" }} + + {{ event.event_type_display }} + + {{ event.model_name|title }} + + {{ event.object_id }} + + {{ event.object_repr }} + {{ change.field }} + {% if change.old is not None %} +
{{ change.old }}
+ {% else %} + (None) + {% endif %} +
+ {% if change.new is not None %} +
{{ change.new }}
+ {% else %} + (None) + {% endif %} +
{{ event.datetime|date:"Y-m-d H:i:s" }}{{ event.user.username|default:"Anonymous" }}{{ event.event_type_display }}{{ event.model_name|title }}{{ event.object_id }}{{ event.object_repr }} + {% if event.event_type_display == "Create" %} + {% trans "Object created." %} + {% elif event.event_type_display == "Delete" %} + {% trans "Object deleted." %} + {% else %} + {% trans "No specific field changes recorded." %} + {% endif %} +
+ +
+{% include 'partials/pagination_audit.html' with q='userActions' %} + +{% else %} +

{% trans "No model change audit events found." %}

+{% endif %} + + +
+
+
+ + + + +{% endblock %} + diff --git a/templates/admin_management/nav.html b/templates/admin_management/nav.html new file mode 100644 index 00000000..ac379dea --- /dev/null +++ b/templates/admin_management/nav.html @@ -0,0 +1,19 @@ +{% load i18n %} + diff --git a/templates/admin_management/request_logs.html b/templates/admin_management/request_logs.html new file mode 100644 index 00000000..1168c676 --- /dev/null +++ b/templates/admin_management/request_logs.html @@ -0,0 +1,64 @@ + +{% extends "base.html" %} +{% load i18n custom_filters %} +{% block title %}{% trans "Accounts" %}{% endblock title %} +{% block accounts %} + + {% trans "Accounts"|capfirst %} + (current) + +{% endblock %} +{% block content %} +
+ +
+

{% trans "Audit Log Dashboard" %}

+
+ + +
+ {% include 'admin_management/nav.html' %} + +
+ + {% if page_obj %} +
+ + + + + + + + + + + + {% for event in page_obj.object_list %} + + + + + + + + + + {% endfor %} + +
{{ _("Timestamp") |capfirst }}{{ _("User") |capfirst }}{{ _("URL") }}{{ _("Method") |capfirst }}{{ _("IP Address") |capfirst }}
{{event.datetime}}{{ event.user.username|default:"Anonymous" }}{{ event.url }}{{ event.method}}{{ event.remote_ip}}
+
+ {% include 'partials/pagination_audit.html' with q='userRequests' %} + {% else %} +

No request audit events found.

+ {% endif %} + +
+
+
+ + + + +{% endblock %} + diff --git a/templates/crm/opportunities/opportunity_detail.html b/templates/crm/opportunities/opportunity_detail.html index e1d90f7f..7eb7ee29 100644 --- a/templates/crm/opportunities/opportunity_detail.html +++ b/templates/crm/opportunities/opportunity_detail.html @@ -1,14 +1,6 @@ {% extends 'base.html' %} {% load i18n static humanize %} {% block title %}{{ _("Opportunity Detail") }}{% endblock title %} -{% block customCSS %} - -{% endblock customCSS %} {% block content %}
@@ -87,12 +79,12 @@
@@ -450,7 +432,7 @@
- {% for metting in opportunity.get_meetings %} + {% for metting in opportunity.lead.get_meetings %}
@@ -472,10 +454,12 @@

Call

+
+
{{opportunity.get_all_notes}}
@@ -487,7 +471,7 @@ - {% for call in opportunity.get_calls %} + {% for call in opportunity.lead.get_calls %} @@ -559,7 +543,7 @@ - {% for email in opportunity.get_emails %} + {% for email in opportunity.lead.get_emails %} - + @@ -285,6 +295,8 @@
{{call.purpose}} {{call.scheduled_by}}
diff --git a/templates/header.html b/templates/header.html index 5c906393..16d52a4a 100644 --- a/templates/header.html +++ b/templates/header.html @@ -416,7 +416,7 @@
{% trans "Discount Amount"|capfirst %}{{ car.finances.discount_amount|floatformat:2 }} -{{ car.finances.discount_amount|floatformat:2 }}
{% trans "Additional Fee"|capfirst %}
+ + {% if car.colors %} @@ -309,32 +321,27 @@ - {% comment %} - - + {% else %} - - {% endcomment %} + {% endif %} + +
{% trans 'Exterior' %}
- {% comment %} {% if not car.get_transfer %} - {% trans "Edit" %} {% else %} {% trans "Cannot Edit, Car in Transfer." %} {% endif %} - {% else %} -

{% trans "No finance details available." %}

- {% if perms.inventory.add_carfinance %} - {% trans "Add" %} - {% endif %} {% endcomment %}
{% trans "No colors available for this car." %}
- {% if perms.inventory.change_carcolors %} - {% trans "Add" %} + + +

{% trans "No color details available." %}

+ {% if perms.inventory.add_carcolors %} + {{ _("Add Color") }} {% endif %}
diff --git a/templates/partials/pagination_audit.html b/templates/partials/pagination_audit.html new file mode 100644 index 00000000..ff544e2f --- /dev/null +++ b/templates/partials/pagination_audit.html @@ -0,0 +1,32 @@ +{% load i18n static %} + +
+
+ {# Previous Button #} + {% if page_obj.has_previous %} + + + {% trans "Previous" %} + + {% else %} + + + {% trans "Previous" %} + + {% endif %} + + + {# Next Button #} + {% if page_obj.has_next %} + + {% trans "Next" %} + + + {% else %} + + {% trans "Next" %} + + + {% endif %} +
+
\ No newline at end of file diff --git a/templates/purchase_orders/includes/card_po.html b/templates/purchase_orders/includes/card_po.html index ed963fea..8ccb4492 100644 --- a/templates/purchase_orders/includes/card_po.html +++ b/templates/purchase_orders/includes/card_po.html @@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById('POModalBody').innerHTML = `
- + ${buttonText} diff --git a/templates/purchase_orders/includes/po_item_formset.html b/templates/purchase_orders/includes/po_item_formset.html index b2d70c45..5aa8289d 100644 --- a/templates/purchase_orders/includes/po_item_formset.html +++ b/templates/purchase_orders/includes/po_item_formset.html @@ -95,7 +95,7 @@
- +
\ No newline at end of file diff --git a/templates/purchase_orders/inventory_item_form.html b/templates/purchase_orders/inventory_item_form.html index 60d3547a..05e18f09 100644 --- a/templates/purchase_orders/inventory_item_form.html +++ b/templates/purchase_orders/inventory_item_form.html @@ -2,7 +2,7 @@ {% load static i18n crispy_forms_tags %} {% block content %} -
+
{% csrf_token %}
@@ -13,11 +13,11 @@
- +
{% endblock content %} \ No newline at end of file diff --git a/templates/purchase_orders/po_list.html b/templates/purchase_orders/po_list.html index e9359b72..f25e0080 100644 --- a/templates/purchase_orders/po_list.html +++ b/templates/purchase_orders/po_list.html @@ -1,74 +1,76 @@ {% extends "base.html" %} - +{% load i18n static %} {% block title %} Purchase Orders - {{ block.super }} {% endblock %} {% block content %} -
-

Purchase Orders

- - - {% if messages %} - {% for message in messages %} +
+ + {% if messages %} + {% for message in messages %}
{{ message }}
- {% endfor %} - {% endif %} - - - - -
- - - - - - - - - - - + {% endfor %} + {% endif %} + +
+

+ {{ _("Purchase Orders") |capfirst }} +

+ {{ _("Create New PO") }} +
+ {% include "partials/search_box.html" %} + +
+
PO NumberDescriptionStatusCreated AtActions
+ + + + + + + + + + {% if purchase_orders %} {% for po in purchase_orders %} - - - - + + + - - + - - {% endfor %} - {% else %} - + + {%endfor%} + {% else%} + {% endif %} - -
PO NumberDescriptionStatusCreated AtActions
{{ po.po_number }}{{ po.po_title }} + +
{{ po.po_number }}{{ po.po_title }} {{ po.po_status|capfirst }} {{ po.created|date:"M d, Y" }} - - View - - {% comment %} - View + {{ po.created|date:"M d, Y" }} + + + {% trans "view"|capfirst %} - - Edit - - - Delete - {% endcomment %}
No purchase orders found.
+ + +
+
+
+ {% if is_paginated %} + {% include 'partials/pagination.html' %} + {% endif %} +
+
-
+ {% include 'modal/delete_modal.html' %} {% endblock %} \ No newline at end of file diff --git a/templates/purchase_orders/po_update.html b/templates/purchase_orders/po_update.html index e9a4e16a..d5e841fa 100644 --- a/templates/purchase_orders/po_update.html +++ b/templates/purchase_orders/po_update.html @@ -24,12 +24,13 @@ {% csrf_token %} {{ form|crispy }} {% trans 'Back to PO Detail' %} + class="btn btn-outline-secondary w-100 my-2">{% trans 'Back to PO Detail' %} {% trans 'PO List' %} + class="btn btn-outline-info + info w-100 my-2">{% trans 'PO List' %}
diff --git a/templates/purchase_orders/tags/po_item_table.html b/templates/purchase_orders/tags/po_item_table.html index f4109ad9..1a82cc16 100644 --- a/templates/purchase_orders/tags/po_item_table.html +++ b/templates/purchase_orders/tags/po_item_table.html @@ -4,7 +4,7 @@
- +
{% trans 'Item' %} {% trans 'Unit Cost' %} {% trans 'PO Qty' %}