From 2d5dec02ade8b1711062fe13f9ec54ce8ccfccd2 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 25 Jan 2026 07:42:40 +0000 Subject: [PATCH] adding nav bar --- core/__pycache__/admin.cpython-311.pyc | Bin 1225 -> 2410 bytes .../context_processors.cpython-311.pyc | Bin 763 -> 1170 bytes core/__pycache__/forms.cpython-311.pyc | Bin 4701 -> 6433 bytes core/__pycache__/models.cpython-311.pyc | Bin 5215 -> 10378 bytes core/__pycache__/views.cpython-311.pyc | Bin 6270 -> 6605 bytes core/admin.py | 29 ++++-- core/context_processors.py | 11 +- core/forms.py | 51 ++++++--- ...livery_city_parcel_pickup_city_and_more.py | 98 ++++++++++++++++++ ...arcel_pickup_city_and_more.cpython-311.pyc | Bin 0 -> 4368 bytes core/models.py | 57 ++++++++++ core/templates/base.html | 7 +- core/views.py | 10 +- 13 files changed, 240 insertions(+), 23 deletions(-) create mode 100644 core/migrations/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.py create mode 100644 core/migrations/__pycache__/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.cpython-311.pyc diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index d1dabe0910e161cb7f96af0a210118c7e7a9fb78..819c119391fc765181ab241c4d9430cac474a0b3 100644 GIT binary patch literal 2410 zcmbVNOK;mo5Z>ieZ}}0~j*}Qhg0vypMnHiyJ+uYdpv|GR8X)O~7lP()Y{sOhF2%OG z>EIvWTmFFyBd1>a8*+>R9gIB%=qWcD_9>^%P^4^23Lu0MKkZ}Y+x=!|R)1HkB?8a> z>%{+8Bji8qFG;VH=@oo0KSo(-(!D zgzJak{R-aEqmajhY5RofU{%aV4^^;%ck~>>&#Syc?9v3IF@wl?wct3|bR1c7oG@ZZfbELo{GPZ$Hd6`wxa%-K?gVa6R$~tP z+jXA%JYew=Im6w&|Dw4Yg}j+;!TlskT+2;o%U*}+3E2wBR&AYWAYGTWar9F0mLjU)B28}; zU5g4_UQ_^DM~0|jr;f0Ium~W_X#n7Gx?G4Q99~99ch)&x~zAF>+SjNolKxK z<6Xbg0rjTm3gJV;h`Vk#iDk(X9IBo zVe=}>%GiF)GFGJ0hq#wX$P~c@?&aZyA~YSd7{_#VKv#!~Mt`D1G8+%>4wi18rjzF> NA{m5phFh9R{{tsW8x{Zn delta 537 zcmZutOG`pQ6rP#;^!f-yd|+Bsme*({wAD=ol_mr&6Vr)q=`}`e+g-E@0%iU~En4*l zBng6Axo~m0tG2dM=m#{T6d0Xz<~yAE&Nt^UpZ+(O@4@TU0JFR2mXSdnUl_fDPBN}S zfB=T)5-q@(OS}OUiB)pPN^B@of{VCe@g4&3$j;Od*Z~!p36vj!T2T*y?izAlhmKUL z)C_aT1v+w+0;!)GRqV)x5-H;AAe^@LZGY6-vjdw}jynlV8faIH(5`fm zMI_2E<{9V$3A5yQ2`*#f3X`H)s$s|XTTZrNYw)hTt`INFv@HZ^PhQnRtRNgTI+i2x z6rZ_kQQ!m1Vs23+KGXGY0QZ)4r2kJ@BoHJXr^_t-#4G|h9Up} diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf2234fb21a6b62efc5cec11af9512dd0c9616..00427d218b5cd88186409c28adf308d56acb6617 100644 GIT binary patch literal 1170 zcmZ`&-%Auh9G}_UyX#Cdv!bYDmyGP9yq?7rGdb*s9%hIVEa&d1d+qI9Gqa|l%y+)`p{c0>0X_Q` zdaD{jKa~;@=ylq>2-{2KA{YB;d}qhw*wtL!*NXbMj*)9TMdOBx@1k^ULme92z`)a3 zCUT5UdOmTK9#I?w9lp&DutH|Mfag&m@}k0CEW4IO2Rsobw19(58^TDaT)R{-BI!*m zlJ@WWGkwxlpvvlArTdV10WWA{D9~s1G4u$FroU$kiAZ*J*I0pA)vX=+Ph9MuxR&Sy zLZbDrTu4Up3J9x_f)3)F$8LPtfc_>Pk2Y99y{^lx5~Bs;h%3BA9B~19y6}{r0`#+i63#?qaZA!X4(7ghvC}5*fW>eu~&3^W|}u zlzcj;klDg35-#$^l5AA2f}jzuPYO~5lp?XOwb8+Yc89n zh_ETM9Y!EbYLv@3n4k$#_W~geRVb~?1S1basK}b@$9KYAu0{|#x!swWrbUtoC&EC4 z88SpHdrR?)jwYIjle#en@Zh3Ak-Tg%?n|&3tZ#`u$kFB+Le!gB#^;A+lzpS?R zH{K1rz4h)^*}7V>u2xT(wZrkocn$5aSe&b|d)}BruP^l`^>mCK1D%}!CgVlw5}((b zUBAyqv-*>%eL{#<+@-i}h7ShZpOsiq5PZ{MC#W3^@K} I>@1}D16Hvl5&!@I delta 401 zcmbQl`J0t*IWI340}xbw%g>z1IFV1HUIxgU&XB^8!kEJl1)&+E7*m*97^0X`7=sx! znO}l5_$4!fC@5e9(#$~oc^Z(I&QO9Rmj!3108LrO#K5o`h#>%rI(?u_3Nwa6R+ttB zh7xuJJBxX88>0$G3d~>_w@CrK!aw z#UQs#W?}Xh6aX?dCo2!z2L=ei%Jvy32lQ_dD1?f5K?F$K zN`@jq5St%J{Nk|5%}*)KNwq5y2692h7aIVH56p~=j5ioK8^G`e19t-$eqiBb1e%XY HfK3DdJ9yJ#e~E44}=`c|ny@N^GVXV!m`n4a!B zp8aX&H{bWo%s1bBKrNOLRdFw&TbvvlFHL(ji84UudBwUHf@ zD5Ph=VLZ@-K*TO&Sr&9l4VF>zfE2gfX~$|;leGn-o(X+e@{K0_%knGo8|l9FTK~Ff zuM2J>jfcjErU%44memVyZmTwl2S&~+{lnVgw(Q|-I%nO(*>%pghjU=A&Ax}j`_{_? zqx8_)ReWGTK6J!JLZoI%<}#U7Uf(L}nhK%-G!w|7h;=T^<+1{oEo44LoHbdE+u5DC zO`^CBIW@CbSiovx;toFxOQz9W94A_k1Ue#?T>{-$;{ybX zZIfs}9}BZ7F%J^?R4zM`0~soe7ut!&#lJ|Z?R3#@_ozS+- zhk=#u+XEC1&Pq^9f3OFOHP30xM2;8ILvy)QQW!}IX@bb4I25I-G{=QVa2By5&M0TgZlzHZV6YHoG_p!ct&ZOow_L%J0A0an zv+KE6;d3v;=alde3=h2!SLyEMw$KTWTd+OdvjZma7zJ9%;y zJx}_?PKl(Oq(3`F$xWNdCfp;Nb!^dbAI5O&-o&Kq)BJvv8`{2im!B(qUBVMWJ`r+C z*Jy87H3={aspPG~0=JV3s0xEqf^>`a9IlEsqQFd{m+sy!VYG}~3- zvwQ;OLV)kLM#c1mJCLdml2BH9m2XWY@^miO?Au$tcJ-aIWI9hbdP#T$j* zsLCE$Gk@*eh-^;F?_H4D35A`2?1bv+UKv;&e0F!!C5I!jXH@ZwLeHq`{TVWn+sw*S z)3|aA%G7N*bzANSxOzp!D?+ch^{&ylZq*-NJNWgHjoHng%L6gle^K#Yg#L?aM`&eo z_2x$FW?1eUlRL(hj&ayAuCl#oY3}b_)qDXIPK2B&zn{mIEhuaOvV|?_ukJAIW17&$ z5tOXkL8+NJaoN+pa&%=%@$^7XkLvZUBxLry;vItCA(ic1BV~WT!VW-oK=lXKI_2OA z#eWj|PpTc>mCGwdrK1;i^i~8%6gC3ch!kbo$J=P^U<4%#{*_M$A51J{s*eZ#yz0(y z3?C_mKIu&HTjWN3(y0qJr>%V_yUsQnMp?D~^|FSwr0 z^%?8J>3iE?CdU`j!f7yv?BQ2Hd?9_+aJG0#CVo=gHXR~S)_oUH=$v_t`L{cRSIKS? X?e$%F(()VMYs|l`3^weR(>MHIO8hF) delta 1699 zcmb7E%}*Og6yGuSuI=^O#(ahHku~I_ON&!zLK=yXM2RSr1`>t#fJ8TrcSuZ)?ar

3H_1 zH}5y|=DnHU_?zC%L&5JA#VVx zPEwZ%(umY}HwM*O#lyT*1OS&tEs^g&!71o5xB1Yr%n zYMTfe@}e*iGJ*wRiWthGZz5a}X30eD6+sKx_53HPp8r@gNEH5e&0(Uf54q=wCjyHD z{5Ov-M!{m8>pkLmqPByr)!1&6rl#0oB#r=>H2|urvJQS<8X}E+Q_8pEvhl=T7BIqm zapv}`JmdY0yvv_@-w)pwrqSm#!`&?KqOXC^`Q-VB;wlhpdn6HVyC7j{bWkI%1CQU=M#^Dy0{mY$89=FJQ< z*ioD&0oYRJ+B8jA=5g4A(YQr5H|s*S8{rL{;?wm+3Rdd6>4q^BzQ8(=?d9EayVwS% zd6!?72dL-^mIL9>hwl!5I&yEMFtQn_yEpbnpyT&IN4cRHda2>aO1{((E4M{UZLuHj zl-dR=lAv^M32r60gHUn6FNEt0GQ%^~$ha0^&A8ty2raxP&?)1^>|O}h1Bc$(tDAKq z^BO`gfUWE%4`NE`x<*(ZcufLuwps*=mLcGEnNI~SDPQvb;Hi^-PklxBAK+xm{QP0? zs_cVU*tQ=L-W2+zsm3Kxui;yTeb;brrx|`84M%r5`DGjl@UKIiEdiq@NkAX}T}_t7 z37Ma&oxNb788GTVF7;1B)t57vVKcFd)QW4OEgRHKuw>RsX41BJlP0g)nMYOq`33pAC?Q#PmWdid%3=)Cg$HDW9;LMsA_h@tjv?>L>c;vhwacWld} zj=+c0*_>tjkgJ-~&7^UiS|&S)D^-Ljf+I{v17~1}N;b#g+!o;1jIbb-!;J+`xz;Q? zy&(SCqH(cdWUSoY{eUiK9;b_wA7Ix%+L&aeNme{=VsE!L+O1N%RaTF!c)kt&5-jRh zvB%8~-74u;u`h>R&2OmrlA15`uEv<6NOk^-08o%B5l$Q5;(s-^%qvm^y%B#w#9uXW z%y|?Z-*ZaGdYn>Jw4RlB8ixoPd}cpDwKBwtE8>CR`(X=xI|!{W(ACHoZ&XwGcTJ;X z&dG~28H1)w=WoKfDcg4uJ*X__!8j?raP&{S{je;Tq66$b2nZ)Hn2-471847a7KNAe oiaSc+neB%MCzj7_p?~KYIY{aV)V3eKXt;lH3;jDUA)WI60qiH9c>n+a diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 158b3118944cc0b30e2cace48b746ef91244e37e..899df19d452011d1852174bd06ab914be4ebb702 100644 GIT binary patch literal 10378 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 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index d97f88e766dee900090e276f5d2003bf113414de..ee86b231aa8c19b3646b34240decd8cb3cad78b8 100644 GIT binary patch delta 1825 zcmbVNO>7fK6rR~1Z|wE@2Rq#)fa8QfVxVaPNdZx$wmIad5=bN{5-PGn*0gp*?T(iN zuvT)&A%`B22Gkxv%AumI$fe{!54|9v_EaeZB#}A%J#s%%1ZI;Y@v{0Da!Wo*9|v115CpcYsR4VRmM!`S$dLXCd`aeAhG;Z zh6VW@f5)_s?XY_kSpHmgDpOXRM1$?e7QLtrfGYYxC2e6l(b?vz-57lZYKFY@k*3zG z(~tDNWxenB&Oi1q>3#S0!H4?beJMUOI5+-O3C&tpE-ov{B_;V}ThrBU-XB;^oO~~D zPW>O=*rR2=rV8MylEZD-Vi7aUmx3+)k{YE^{(Y>aYZMppy9tI|CEkx_qw7QV9S~v^Ff)|FVIByDDT$Lg5`i`S+7*4<25Z;9h2UmTD zJkt~JBFW>;B;n^n>xo~W?A`Cmzu<>Z5-p+bUHOmvI1MjE40cf!6HlQk7B%DrU;Pz* zg9J4i8VXP%SgYMci7pQ%HhANq1fNn{ow)}_Ny_ickw1tW4t@xIQ$ktD8myb^(H=d# z#aPFp?b9DYGV4Z#x0ro`wa!S({=x*C%uJ=B&&G5&4HljP`ynW66F`cBnRfEo0Q4A! zU-34m_slvxH|`Ak^0Lytq_i)tL|@U!r%=djWJ$ET`SaMBJsTkLDYucb?P%d2MM`pL z_&GlxKOhf*XMFLec!J6!V4hw4tFf6zkb^cjhH*B+D<8DbF|O(_PK2mhA)wYoB1C2r k6#*Sduq#4@4$}%jT?^W-4m+ySwh94Ni=m^14>le82WkLLc>n+a delta 1532 zcmcIkOH30{6n$?xpHACpr$C{VUulF`v{1kVF(J57LL?Y%!h!@F!i=G{LU|o_gJHqa zg~?0&BpSOgm}obKt#Rkl1PKe%l?&ZTQeZ=CBXNC7PwBxoC ztKV02vyNp=Zmg(I@J)SsOqa=P-WObRr*Yq0St_h#@_C)aF&U(j^2;un48WI^r94@? zm&xg6o~-3`;sYCrQ0zHBBaklkGRoj0gCv6yfPw%JWkR}{t(2zd+Cbc1Kt7`Qfxu0X&QgB z2+xdQEd?TEy580FP~fz){*MT;~0Qesa^(AmK8d=5V4|I)btH>~a+8Y=X2A|}@OlF+#2r+AW`XiWsx ztp5qKIIQ*8vsUA@iM9Iu5>hDco};)a{-M#|&MdSGWM4!4<@?>a~?6$DWAF+-R> HiH!dR$4dbH diff --git a/core/admin.py b/core/admin.py index 0dbb9bb..c589342 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,15 +1,32 @@ from django.contrib import admin -from .models import Profile, Parcel +from .models import Profile, Parcel, Country, Governate, City + +@admin.register(Country) +class CountryAdmin(admin.ModelAdmin): + list_display = ('name',) + search_fields = ('name',) + +@admin.register(Governate) +class GovernateAdmin(admin.ModelAdmin): + list_display = ('name', 'country') + list_filter = ('country',) + search_fields = ('name',) + +@admin.register(City) +class CityAdmin(admin.ModelAdmin): + list_display = ('name', 'governate') + list_filter = ('governate__country', 'governate') + search_fields = ('name',) @admin.register(Profile) class ProfileAdmin(admin.ModelAdmin): - list_display = ('user', 'role', 'phone_number') - list_filter = ('role',) + list_display = ('user', 'role', 'phone_number', 'country', 'governate', 'city') + list_filter = ('role', 'country', 'governate') search_fields = ('user__username', 'phone_number') @admin.register(Parcel) class ParcelAdmin(admin.ModelAdmin): list_display = ('tracking_number', 'shipper', 'carrier', 'status', 'created_at') - list_filter = ('status', 'created_at') - search_fields = ('tracking_number', 'receiver_name', 'receiver_phone') - readonly_fields = ('tracking_number',) \ No newline at end of file + list_filter = ('status', 'pickup_country', 'delivery_country') + search_fields = ('tracking_number', 'shipper__username', 'carrier__username', 'receiver_name') + readonly_fields = ('tracking_number', 'created_at', 'updated_at') diff --git a/core/context_processors.py b/core/context_processors.py index 0bf87c3..bfbe8d9 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -1,13 +1,22 @@ import os import time +from .models import Profile def project_context(request): """ Adds project-specific environment variables to the template context globally. """ + profile = None + if request.user.is_authenticated: + try: + profile = request.user.profile + except: + profile, created = Profile.objects.get_or_create(user=request.user) + return { "project_description": os.getenv("PROJECT_DESCRIPTION", ""), "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), # Used for cache-busting static assets "deployment_timestamp": int(time.time()), - } + "user_profile": profile, + } \ No newline at end of file diff --git a/core/forms.py b/core/forms.py index a49a99b..5345c0d 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,13 +1,17 @@ from django import forms from django.contrib.auth.models import User from django.utils.translation import gettext_lazy as _ -from .models import Profile, Parcel +from .models import Profile, Parcel, Country, Governate, City class UserRegistrationForm(forms.ModelForm): password = forms.CharField(widget=forms.PasswordInput, label=_("Password")) password_confirm = forms.CharField(widget=forms.PasswordInput, label=_("Confirm Password")) role = forms.ChoiceField(choices=Profile.ROLE_CHOICES, label=_("Register as")) phone_number = forms.CharField(max_length=20, label=_("Phone Number")) + + country = forms.ModelChoiceField(queryset=Country.objects.all(), required=False, label=_("Country")) + governate = forms.ModelChoiceField(queryset=Governate.objects.all(), required=False, label=_("Governate")) + city = forms.ModelChoiceField(queryset=City.objects.all(), required=False, label=_("City")) class Meta: model = User @@ -31,30 +35,53 @@ class UserRegistrationForm(forms.ModelForm): user.set_password(self.cleaned_data['password']) if commit: user.save() - Profile.objects.create( - user=user, - role=self.cleaned_data['role'], - phone_number=self.cleaned_data['phone_number'] - ) + # Profile is created by signal, so we update it + profile, created = Profile.objects.get_or_create(user=user) + profile.role = self.cleaned_data['role'] + profile.phone_number = self.cleaned_data['phone_number'] + profile.country = self.cleaned_data['country'] + profile.governate = self.cleaned_data['governate'] + profile.city = self.cleaned_data['city'] + profile.save() return user class ParcelForm(forms.ModelForm): class Meta: model = Parcel - fields = ['description', 'weight', 'pickup_address', 'delivery_address', 'receiver_name', 'receiver_phone'] + fields = [ + 'description', 'weight', + 'pickup_country', 'pickup_governate', 'pickup_city', 'pickup_address', + 'delivery_country', 'delivery_governate', 'delivery_city', 'delivery_address', + 'receiver_name', 'receiver_phone' + ] widgets = { 'description': forms.Textarea(attrs={'rows': 3, 'class': 'form-control', 'placeholder': _('What are you sending?')}), 'weight': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1'}), - 'pickup_address': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('123 Street, City')}), - 'delivery_address': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('456 Avenue, City')}), + + 'pickup_country': forms.Select(attrs={'class': 'form-control'}), + 'pickup_governate': forms.Select(attrs={'class': 'form-control'}), + 'pickup_city': forms.Select(attrs={'class': 'form-control'}), + 'pickup_address': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Street/Building')}), + + 'delivery_country': forms.Select(attrs={'class': 'form-control'}), + 'delivery_governate': forms.Select(attrs={'class': 'form-control'}), + 'delivery_city': forms.Select(attrs={'class': 'form-control'}), + 'delivery_address': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Street/Building')}), + 'receiver_name': forms.TextInput(attrs={'class': 'form-control'}), 'receiver_phone': forms.TextInput(attrs={'class': 'form-control'}), } labels = { 'description': _('Package Description'), 'weight': _('Weight (kg)'), - 'pickup_address': _('Pickup Address'), - 'delivery_address': _('Delivery Address'), + 'pickup_country': _('Pickup Country'), + 'pickup_governate': _('Pickup Governate'), + 'pickup_city': _('Pickup City'), + 'pickup_address': _('Pickup Address (Street/Building)'), + 'delivery_country': _('Delivery Country'), + 'delivery_governate': _('Delivery Governate'), + 'delivery_city': _('Delivery City'), + 'delivery_address': _('Delivery Address (Street/Building)'), 'receiver_name': _('Receiver Name'), 'receiver_phone': _('Receiver Phone'), - } + } \ No newline at end of file diff --git a/core/migrations/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.py b/core/migrations/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.py new file mode 100644 index 0000000..f4721b6 --- /dev/null +++ b/core/migrations/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.py @@ -0,0 +1,98 @@ +# Generated by Django 5.2.7 on 2026-01-25 07:39 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_alter_parcel_options_alter_profile_options_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='City', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Name')), + ], + options={ + 'verbose_name': 'City', + 'verbose_name_plural': 'Cities', + }, + ), + migrations.CreateModel( + name='Country', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Name')), + ], + options={ + 'verbose_name': 'Country', + 'verbose_name_plural': 'Countries', + }, + ), + migrations.AddField( + model_name='parcel', + name='delivery_city', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='delivery_parcels', to='core.city', verbose_name='Delivery City'), + ), + migrations.AddField( + model_name='parcel', + name='pickup_city', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pickup_parcels', to='core.city', verbose_name='Pickup City'), + ), + migrations.AddField( + model_name='profile', + name='city', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.city', verbose_name='City'), + ), + migrations.AddField( + model_name='parcel', + name='delivery_country', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='delivery_parcels', to='core.country', verbose_name='Delivery Country'), + ), + migrations.AddField( + model_name='parcel', + name='pickup_country', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pickup_parcels', to='core.country', verbose_name='Pickup Country'), + ), + migrations.AddField( + model_name='profile', + name='country', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.country', verbose_name='Country'), + ), + migrations.CreateModel( + name='Governate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Name')), + ('country', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.country', verbose_name='Country')), + ], + options={ + 'verbose_name': 'Governate', + 'verbose_name_plural': 'Governates', + }, + ), + migrations.AddField( + model_name='city', + name='governate', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.governate', verbose_name='Governate'), + ), + migrations.AddField( + model_name='parcel', + name='delivery_governate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='delivery_parcels', to='core.governate', verbose_name='Delivery Governate'), + ), + migrations.AddField( + model_name='parcel', + name='pickup_governate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='pickup_parcels', to='core.governate', verbose_name='Pickup Governate'), + ), + migrations.AddField( + model_name='profile', + name='governate', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.governate', verbose_name='Governate'), + ), + ] diff --git a/core/migrations/__pycache__/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.cpython-311.pyc b/core/migrations/__pycache__/0003_city_country_parcel_delivery_city_parcel_pickup_city_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2eca7900a037ec371ff27bb4d89b1048e7468d6c GIT binary patch literal 4368 zcmcInJ8%%jTt7Xp$dAGt_c!fWm&~2l zylI5_ET48DPI5MwFv4`iz2>n1uoFHm?B4NgJ0S}A8rKTKUgqhwMz9b2ciOfQc){25 z$|ArF;NUN4r+t4O2beql2j&hOI+EFE#fZZ=!ZKm2M@MPSQrlX<@>qkq8P{6SZnax2 z+=;vJsh!i?;Pu${+=(8N?Ox6^C*q6&=h+rHyK&EnIJ+>~>D>lZKDL#4Y?gf|V(i5I zc%a2B!+7vSoH5{huSL#oJai(?e%$d^m9^`Rhu`&XIhet*`tPvyPoqD#U+pU>>wp-~ zzs2tB!6W-?*8=k>9zMu?tRmz4*;?M~qj;=Eb%gQzC*q9ZQGB6A&hCR%Z81+;*<<#4 zHkc0Ki*OPph;P9Lh2@D@7xQ zcC%we(DoUdWTeDXG}Z!^j>h0pdm&}DkD7jJ2B~2cYowAf(ys=LRLowek&Rg%i`epS zw}{Pok2yhna|I|_9#VvwAE5Qyb0y--T7K1b#%=dW1nOYa-5#q(?1*d(5;BX?@Cunx z**>Xwn0N(NR6!tq0i=m5pjLqd1mW9?s8D&)9+rwy9!vQQ2tcLpXryh533Y+4ib~&? zNMKUVPJo1GsfIu>tDs0nwZZP2*S(63m_8RHpII)vm~T$ z&d-DNCKjhAre}!1P?XFUzu&pnz{I_&Bv%VLDOp(taaNMjJt+gcYI38Xu9u2pMoQ9> zB^xh{N!X@Wm_!6~A*|hM*OF?&8Z-;XhD3A2CW@OXeeGd}zBiVJ;hcJ4f9h_|x4_pO zsLTCezV7FI{XhD3Z(l7O{VRM*i;g|Kt4GJ4I-d_~X+g`YTD0^cTGFE>EmQ^{Ef#-t zT93t_EIzxd#ZoU~DLt0bLO1FzM^`(b4UW`ClF&KTUgxL@`hEUa9GG2d>ZcoYXWt3k zSZ(BTQ@S3SU3V?oUkeY^B0~?)>ye?~hMz1w`|{7*`pivz=8is-t#jN4e+|Jcg>KW^ zD%zm8&U@$;v%AdJJx-bpm|bdU@%F&?HHV+0fb-}pdRsvy2480IWkli2s0sW4l>SI~ zP1yjI&ZPnzr|Mn*1uL?L^t_}`@wbk zQ9rgGn{e*OjHiAAFDH!u;S<)QU;O~wh|iv9#|%4JjOXr06oo-eK {% trans "Dashboard" %} + {% if user.is_staff %} +

+ {% endif %} @@ -106,7 +111,7 @@