From 873880f7d1d2becc525e6679649ec5a014b15ea6 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 15 Nov 2025 19:03:23 +0000 Subject: [PATCH] 1.1 --- config/__pycache__/settings.cpython-311.pyc | Bin 4210 -> 4278 bytes config/__pycache__/urls.cpython-311.pyc | Bin 1143 -> 1446 bytes config/settings.py | 3 + config/urls.py | 5 + core/__pycache__/forms.cpython-311.pyc | Bin 2395 -> 3802 bytes core/__pycache__/models.cpython-311.pyc | Bin 6176 -> 6351 bytes core/__pycache__/urls.cpython-311.pyc | Bin 1420 -> 1955 bytes core/__pycache__/views.cpython-311.pyc | Bin 6048 -> 11701 bytes core/forms.py | 17 ++- core/migrations/0002_document_description.py | 18 +++ core/migrations/0003_document_uploaded_by.py | 21 +++ .../0002_document_description.cpython-311.pyc | Bin 0 -> 794 bytes .../0003_document_uploaded_by.cpython-311.pyc | Bin 0 -> 1141 bytes core/models.py | 2 + core/templates/core/approve_bid.html | 24 ++++ core/templates/core/delete_bid.html | 19 +++ core/templates/core/delete_document.html | 19 +++ core/templates/core/delete_tender.html | 19 +++ core/templates/core/tender_detail.html | 74 ++++++++++- core/templates/core/tender_list.html | 6 +- core/templates/core/update_bid.html | 21 +++ core/templates/core/update_tender.html | 21 +++ core/urls.py | 5 + core/views.py | 121 +++++++++++++++++- 24 files changed, 388 insertions(+), 7 deletions(-) create mode 100644 core/migrations/0002_document_description.py create mode 100644 core/migrations/0003_document_uploaded_by.py create mode 100644 core/migrations/__pycache__/0002_document_description.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0003_document_uploaded_by.cpython-311.pyc create mode 100644 core/templates/core/approve_bid.html create mode 100644 core/templates/core/delete_bid.html create mode 100644 core/templates/core/delete_document.html create mode 100644 core/templates/core/delete_tender.html create mode 100644 core/templates/core/update_bid.html create mode 100644 core/templates/core/update_tender.html diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 5c3cab0e08c4326e299e5be0e870b2c9122296d3..22166816a180be3e7e0eceb8fa3d178b5044bebf 100644 GIT binary patch delta 167 zcmeyQuuYM7IWI340}$|@mB>6gkyn!O%SQE+Oa>|XQSzyrDF#*C3=B~UK$anh1*DBY zbUIU%Vv2E;Qi@5Gatd=WgQn?bA?5`f0#)q#xv43ciTbx#!Sv)!+zyN?n?G}xG756~ zy1IBe#)k&^+~NW=CnxcSafJb`W(4Bm?VAtrHZbu&;1O-$yum9vgZ(0}d;`~HCjkQh DsQxX2 delta 102 zcmdm{_(_3xIWI340}$Apl*sIx$ScYCY@_-~CZ$x)6#Xi01_mH)0HV{GqU2KyqZCq% zq7+k@gBdi9H!CnN;Fx@Y+ksJOvmj3?{+}ZB_L2Flga~B z!wD4QgNQIN0NMO7_8QJ*OhCOr3;|U@ol!g~T)_+=tjWD`#X=@~O~zXs#i=DFnR)5O zKsH-(Nn%N6aur)*N^WMJ{w;2;u++a|+fQoJ~ zXk9=>lPy_H9Y1g}u<*3lePCc@3}n2)!au=mg4qm>D^fZ;Jg(UIUl0zsC>(GFNU{Vp Rc!S^vW{^UN@***yg#d;jfCvBp delta 145 zcmZ3+{hfn%IWI340}$Apl*m+{$ScWMGf`btgoz=QHH9UWDT@sx2Lf5_6Bmf_fW=vX zBufh0#98v}94YL<44NF9*%%ixNmQ{VrsQVk>EB|g;!MsjO4TbZ$|?5K;`*48^o9d^BDb`6vZ z9601qi5w!eCk`Bns{Sn?BjsT2l~ZqyoKsGH->la^b~kP+W#j!ayYJ1L+3$PvX8z9S za}r&D{4-zsSCypy@eo|eUZZvqaV6PH?%LdPs4>0c zFuui^?$+%3m-O-_u`HERUV6*68hZVam-?2~4aPn7Zq4vAU)a^A#pzMq(?y ze%%j8Vh=~$55}id`Gvf+q@hqu^{g<6xqgqidP((?%+hP7m)osz$F0L&av6^?K55{T%fc5BQuoaa&(^E_KCKeR1YZa!$% z-Da8n%&JY-=H;Jk{=L)CtE^nLIV%e>)*FxbRqDeF1Sz_mC4%4jf9>MRaq;GHajkW0 z=fC34K?;BTI(nenb}7kc@h}It0>HHVkn?B>*9Ell0e~py8-l?w9Ys!lCwHWHY!#M{ z3pY-L_4FJce;|x8A{=IetGh}NmlV%P+82SmlBV_&VWO9akxW3M;_Bk-1_7C>B@B5_ z?yEjM6hO%CC7>IL8zu~_dr1Z;u4;C`xjnBSql^?@nV3z6c0+DI(Nz)j78E~#RH_3|i<3Ochajud$#f_Hu zMFiq22pZDk2HpXt=ysxdY(kGPK#xu65jOaGAl#+sk)~oC5#yo@VzSrSUkoYTvZ*@7 zEQbsjSO%QS_ZnGIa3YIg7K>rwaN%NHe}F*%s9-T%FM{hZfa^tY9X9w}BiyBMJsIqI z5yHo0zabmSo}F@h;TXLlv|BS7M=^|{9yQ=K)PF=SDCy2y1jDI5FY3_KwN`QU zV(j0-U;wOu81_qGKMY{M1op!QpM8Y86!tSXXGEC4XsYLf-a+A~U@>%~p1+3fPsjyD zC6eiL=av1~sS>u`)-m2h0$Z8~12YFjw}IWa6w z;A@rv=hh39(Rb;NPuhj2U$=_yUo2@q#W(D$KipCElSi)v4TZj30iO*^0N1E zw16-iJG0nbMA;LoPo#zTq|jD$mbvX8Jn!%=>W18R{v_rH;0gd*b%7>}*u3h_t|MC8CNqsnwbiNfJ~N97Z|KOGozs!aQ16b`Q(Ey0&|(7us; zslG?yfIYV1OFKZ{NWN5!qHwrz^cj4qUiFRS8#~{F=Hp*u=1U2q%3%oNQc6XQBBfpd D5bWQ1 delta 728 zcmca5ds~QaIWI340}zCtk;qJ8n#d=?bbw)^hMo>10~3QgLkepPLke3e^D<@zhSfj} z0Z|-aId-5NM=E0$=OjjPelD;OCs2qBDm1a4jguS5=YjIsc~W?T88rDOXEAC>FyCT# z&d)7K%&WY`7LuBml3H|&*(oz6d7_}`=9`QLjBLU{tBQCg^E2yMi-DLRzMm#*kpPGz z2qJ_)1Ph1&s}%*Yz)HnIERd&)xF(k|N3wwhxh6-kdQ6sOi7`bpfeone7He*PN@`A# z2uKr}L14qBC$D2sw?a5%B@4Pku(?4JWE_a#oIIaRY_ck=KEy4OK)38OMso){NUI903Tb#b20CvkS$^{2VkpfTIklPB=*n5@mm$qG&Y06H*z*Z=?k diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index cc5595541df81d5e785b9dc52544d58e24579b57..5c123ea6c6b2f350a537946a3845645117931692 100644 GIT binary patch delta 531 zcmZ2raNdx2IWI340}!O0mB`$%k@q7H6CdLw-cm&tkRS-8a-}k7u|e1j3@MCjn3pj# zFsx>tT*odkc?-L6ATJXGPz4{73btj8Kovj?0a5&56#_6m&hB`93%Q|2bSA>)oxNLCS;&(#if{@=uA-^j^ehuC?xTQPHu5e2)$hp96e39Gu3b%2C%MBqV zkb0#RK$UtIh4iin={0z7j^W?J$Y?)VP_U0b2xJL}ums}bdZ0b(ldlRUvVw$?Ckl#g z4iHLYlnDp&G?|MyfmD$ukhsO3lA4xSno|-|vB~?z69FWVf`I@4 delta 386 zcmX?axWIsSIWI340}xy~C6Q^gk@q7H(;kLNyrq-#**UpVnAfl@V`N}h%{aM^UBW(v zaSbbk3(^1rQM@T^Eeuh7DeS=vnjE({T=J7kb5rw5LNu9+)PeH1Sn^79a&ECEi#UN)ktUG1#h#LymROntbSGnx$K*PZLPnp-A4Dt}eJ5*) zCUg6M)Ukkw*vT_Qow-6lOc3EP`Kjn-#*oQ7#Uj|iHit|W7nfyBnQSWlU5A}f?gIlR Y(NWe@^ARNe1tBRkIZi@sa-KvY0E_}=W&i*H diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5b5bea923ff486ac4555c5b96cc5edf95ee63ed6..a04f671b56f9a17637b1a717c5331018fed82428 100644 GIT binary patch delta 601 zcmeC-Ud*qtoR^o20SKJWNo3w)VPJR+;=q6~l<~QUX`{vt#)$`5`K42tvjk!CDKZl` zi1NzsnRMxO9V`gAj4a5+T&L1U~ zq8iMgskZqQqdjAYTuEwPN@|h5O=ez+6@(q1nPR73T9A@hlB$1;7s7~#NLArdo|2lA z3RVtcOqOT1tgn(t%1nV63uK^}$ptkND2_!H%qT7hBOatm62q*-f`X#_GN1#wp)8QP zTMV~YGV*g%{WQfVCo@|zicOx&>>&jT*&VJiq4K5crF~lePvC3v%;8b1^dPUWAhtCBT z&xT!vyEJ(egX1*i$0*lW@7N09DK4A47LLH$!Q8Usmu&7>SQN6;V`axcR OpQ(Wx1d9}a&I16s_^%@X delta 137 zcmZ3?-@~o3oR^o20SGeANMw32GcY^`abSQC%J{sGaihi#My_dtBTKC39>WEFO? V$xZA+{G#0aOby&1SR@UU1^{~w96|s9 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index e0828b2e302c846a4bc367d776f6f996dcd3b809..03147328857bb34703a60a5c42f12bce69f0726d 100644 GIT binary patch literal 11701 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 CN%HUj delta 1672 zcmah}-*4Mg6!x_pCrz3(Nt?u3S=aq2t(&aTBs2}E)-pte1Oh?#fC4O}v9}tvYC%2U5 zm@{Ojwv?|h@Oz3DSF>af;aJ&DBUZL|N-Y!JhL5>b(V9JGJ%QHTG3z*5M~+#au=C4` zRp7s{4%2yEol`BHf25w_KdGOy%HVCS##ou((qCsL|6PCbtvuEjCH<9molY1;#&x$F zIsoHn1r5Q{p+p`T&+%3q2v*&-0M3e-j^7KN_rpNPze&8HOy(iG9jvbSZLnd6UrStL z=HPDPX~t&wpGJ+%^0UdQDVh;#1e7PBJO}Yc;NIx1uR()*$=g`t-^o^^f)17nDT<(M zFoqB?gg9xY>)-I#!`6GQvjG?Qt<(%g-c3{o_fjvi{wz9134f#KfTc-lr^uv(-*sRH zKR3;e4^E>4PCIm39p77P_db|g4ZW@?)5<^{youGKGNJ zoKR5mrMyS^=8h(Xi~Q&GXIT9Y)hge}e3P2M2;_c>PY>7L7@=eZ0?G(e1R_a7b}1c~ z-g0fARPYSJG(wO-K#}*`lnI@l?Le#R1|iJz?}pzw$oXX-E}^rMax zzg(;s_lxFkevIF)o{{Ab{uvFKMP8y$OWz_1)Wmav}dY?z!Vo3*XEbhpge^^^uTG&w@bMGoo?L6-0_4+5} jBwN^15FToZn%|n(Q^`=|_v(}U^!ZXwG1>ecfhhD}gHmqU diff --git a/core/forms.py b/core/forms.py index fe2e8d2..c085c71 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,6 +1,6 @@ from django import forms from django.contrib.auth.forms import UserCreationForm -from .models import Company, Tender, Bid +from .models import Company, Tender, Bid, Document, Note, Approval class SignUpForm(UserCreationForm): class Meta(UserCreationForm.Meta): @@ -20,3 +20,18 @@ class BidForm(forms.ModelForm): class Meta: model = Bid fields = ['amount'] + +class DocumentForm(forms.ModelForm): + class Meta: + model = Document + fields = ['file', 'description'] + +class NoteForm(forms.ModelForm): + class Meta: + model = Note + fields = ['note'] + +class ApprovalForm(forms.ModelForm): + class Meta: + model = Approval + fields = ['status', 'comments'] diff --git a/core/migrations/0002_document_description.py b/core/migrations/0002_document_description.py new file mode 100644 index 0000000..2268275 --- /dev/null +++ b/core/migrations/0002_document_description.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-11-15 18:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='document', + name='description', + field=models.TextField(blank=True), + ), + ] diff --git a/core/migrations/0003_document_uploaded_by.py b/core/migrations/0003_document_uploaded_by.py new file mode 100644 index 0000000..5eed81e --- /dev/null +++ b/core/migrations/0003_document_uploaded_by.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.7 on 2025-11-15 18:58 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_document_description'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='document', + name='uploaded_by', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/core/migrations/__pycache__/0002_document_description.cpython-311.pyc b/core/migrations/__pycache__/0002_document_description.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5972df28092477349833797448528319a127cad5 GIT binary patch literal 794 zcmZuuzi-n(6nn&d){ zQ^un_Xm-kPdlAe$B*(zPBMdx+kpb~k1-LeBMB&v;uIR&N4>0Ylb%G{N@tuz|{a-nF ziox0_$Pni1MhVK&f;8iz)9dw~(l7~&(2s<{(x8laQixlO=Rp>Zr0hM1h*m%HlYK`; zH_`!3{Fn=^#@5@AM{LJ2g+c3eN`*x!)|C-Q-l6n;=|{3wMvRYm!gvydJkMm)gz=oQ z7xIiulcBlC4~se$RyyK~)_sGS^mn!GzDr~7mi;m*N|zt!Hi@pff_`QWoPG44-S)~0tJPVM!h z#}j+~>+-qX{b6?}cDHJ4rgZ@bml7&fxApNWIkT1KmK{eECL8+6Anme#?Y{Or*Kt>t z)wQe0u8ggD3!i28FqHo{e}hn|gb>t5oO5_`^QrU>(JQzKN7m%_qYIh1{7<&N_cy1a B$Xoyb literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0003_document_uploaded_by.cpython-311.pyc b/core/migrations/__pycache__/0003_document_uploaded_by.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..147b49c2b04159f21e9d3b8e28843ef15b89fe42 GIT binary patch literal 1141 zcmZuwNl)8A6dv2LW1FPV2(_iHS`cj^mjtyZq^fGf(hX?Ea`9#4c!uEATfK+2FB?TRrUZ*&!h2( z;{g`hg*=s3mvVidqt)q?tro8p(_A_CARY9GAad>{Ly>2> zI>HDxyX#ef6r9Mih~hdl-@+j#eCSfQno1MU(DwRNZE5*{@vEOurFusGLPn`_K35Y_eYp#ML>DI@uO zslMgfxE?hlCyeU2gN-P3{ra}+e-1p|z;(u6{c>*Vl*bpBJYFHJ=5>8Gl&N6-54u$B zTwI9+lv-b&`liP6)PWpNzdn{5vD`>RVN}{#N=iU^$YKxrcPIL{AN4DfyU*gvBN;zQYuZn(~7)&u3@i?m%S)fv3UY~OVA396mTFS9Pc@}E+Z{Ru%xMIvw< pM +
+
+

Approve Bid

+
+
+

Tender: {{ bid.tender.title }}

+

Company: {{ bid.company.name }}

+

Amount: ${{ bid.amount }}

+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+ +
+ +{% endblock %} diff --git a/core/templates/core/delete_bid.html b/core/templates/core/delete_bid.html new file mode 100644 index 0000000..63b3d4a --- /dev/null +++ b/core/templates/core/delete_bid.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Delete Bid

+
+
+

Are you sure you want to delete this bid of ${{ bid.amount }}?

+
+ {% csrf_token %} + + Cancel +
+
+
+
+{% endblock %} diff --git a/core/templates/core/delete_document.html b/core/templates/core/delete_document.html new file mode 100644 index 0000000..53fc92b --- /dev/null +++ b/core/templates/core/delete_document.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Delete Document

+
+
+

Are you sure you want to delete the document "{{ document.file.name }}"?

+
+ {% csrf_token %} + + Cancel +
+
+
+
+{% endblock %} diff --git a/core/templates/core/delete_tender.html b/core/templates/core/delete_tender.html new file mode 100644 index 0000000..dc58147 --- /dev/null +++ b/core/templates/core/delete_tender.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Delete Tender

+
+
+

Are you sure you want to delete the tender "{{ tender.title }}"?

+
+ {% csrf_token %} + + Cancel +
+
+
+
+{% endblock %} diff --git a/core/templates/core/tender_detail.html b/core/templates/core/tender_detail.html index b0f1fa3..6bc6306 100644 --- a/core/templates/core/tender_detail.html +++ b/core/templates/core/tender_detail.html @@ -14,10 +14,12 @@ -
+

Bids

Create Bid @@ -27,7 +29,23 @@
    {% for bid in bids %}
  • - {{ bid.company.name }} - ${{ bid.amount }} +
    + {{ bid.company.name }} - ${{ bid.amount }} + {% with approval=bid.approval_set.first %} + {% if approval %} + {{ approval.get_status_display }} + {% else %} + Pending + {% endif %} + {% endwith %} +
    +
    + Review + {% if request.user.membership_set.first.company == bid.company %} + Update + Delete + {% endif %} +
  • {% endfor %}
@@ -36,5 +54,57 @@ {% endif %}
+ +
+
+

Documents

+
+
+
+ {% csrf_token %} + {{ doc_form.as_p }} + +
+ + {% if documents %} +
    + {% for doc in documents %} +
  • + {{ doc.file.name }} + by {{ doc.uploaded_by.username }} on {{ doc.uploaded_at|date:"Y-m-d" }} +
  • + {% endfor %} +
+ {% else %} +

No documents yet.

+ {% endif %} +
+
+ +
+
+

Notes

+
+
+
+ {% csrf_token %} + {{ note_form.as_p }} + +
+ + {% if notes %} +
    + {% for note in notes %} +
  • +

    {{ note.note }}

    + by {{ note.user.username }} on {{ note.created_at|date:"Y-m-d" }} +
  • + {% endfor %} +
+ {% else %} +

No notes yet.

+ {% endif %} +
+
{% endblock %} diff --git a/core/templates/core/tender_list.html b/core/templates/core/tender_list.html index 5d4c84a..bd691db 100644 --- a/core/templates/core/tender_list.html +++ b/core/templates/core/tender_list.html @@ -10,8 +10,12 @@ {% if tenders %} diff --git a/core/templates/core/update_bid.html b/core/templates/core/update_bid.html new file mode 100644 index 0000000..3af3ad1 --- /dev/null +++ b/core/templates/core/update_bid.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Update Bid

+
+
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+ +
+
+{% endblock %} diff --git a/core/templates/core/update_tender.html b/core/templates/core/update_tender.html new file mode 100644 index 0000000..be3d1d8 --- /dev/null +++ b/core/templates/core/update_tender.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

Update Tender

+
+
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+ +
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 4f0e941..2048395 100644 --- a/core/urls.py +++ b/core/urls.py @@ -11,6 +11,11 @@ urlpatterns = [ path('company//tenders/', views.tender_list, name='tender_list'), path('tender//', views.tender_detail, name='tender_detail'), path('company//create_tender/', views.create_tender, name='create_tender'), + path('tender//update/', views.update_tender, name='update_tender'), + path('tender//delete/', views.delete_tender, name='delete_tender'), path('tender//create_bid/', views.create_bid, name='create_bid'), + path('bid//update/', views.update_bid, name='update_bid'), + path('bid//delete/', views.delete_bid, name='delete_bid'), + path('bid//approve/', views.approve_bid, name='approve_bid'), path('', views.home, name='home'), ] \ No newline at end of file diff --git a/core/views.py b/core/views.py index 1432241..912dfa7 100644 --- a/core/views.py +++ b/core/views.py @@ -1,8 +1,8 @@ from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth import login, authenticate, logout from django.contrib.auth.decorators import login_required -from .forms import SignUpForm, CompanyForm, TenderForm, BidForm -from .models import Company, Membership, Tender, Bid +from .forms import SignUpForm, CompanyForm, TenderForm, BidForm, DocumentForm, NoteForm, ApprovalForm +from .models import Company, Membership, Tender, Bid, Document, Note, Approval def home(request): return render(request, "core/index.html") @@ -57,9 +57,37 @@ def tender_list(request, company_id): def tender_detail(request, tender_id): tender = get_object_or_404(Tender, pk=tender_id) bids = Bid.objects.filter(tender=tender) + documents = Document.objects.filter(tender=tender) + notes = Note.objects.filter(tender=tender) + + if request.method == 'POST': + if 'submit_document' in request.POST: + doc_form = DocumentForm(request.POST, request.FILES) + if doc_form.is_valid(): + document = doc_form.save(commit=False) + document.tender = tender + document.uploaded_by = request.user + document.save() + return redirect('tender_detail', tender_id=tender.id) + elif 'submit_note' in request.POST: + note_form = NoteForm(request.POST) + if note_form.is_valid(): + note = note_form.save(commit=False) + note.tender = tender + note.user = request.user + note.save() + return redirect('tender_detail', tender_id=tender.id) + + doc_form = DocumentForm() + note_form = NoteForm() + context = { 'tender': tender, - 'bids': bids + 'bids': bids, + 'documents': documents, + 'notes': notes, + 'doc_form': doc_form, + 'note_form': note_form } return render(request, 'core/tender_detail.html', context) @@ -81,6 +109,34 @@ def create_tender(request, company_id): } return render(request, 'core/create_tender.html', context) +@login_required +def update_tender(request, tender_id): + tender = get_object_or_404(Tender, pk=tender_id) + if request.method == 'POST': + form = TenderForm(request.POST, instance=tender) + if form.is_valid(): + form.save() + return redirect('tender_detail', tender_id=tender.id) + else: + form = TenderForm(instance=tender) + context = { + 'form': form, + 'tender': tender + } + return render(request, 'core/update_tender.html', context) + +@login_required +def delete_tender(request, tender_id): + tender = get_object_or_404(Tender, pk=tender_id) + if request.method == 'POST': + company_id = tender.company.id + tender.delete() + return redirect('tender_list', company_id=company_id) + context = { + 'tender': tender + } + return render(request, 'core/delete_tender.html', context) + @login_required def create_bid(request, tender_id): tender = get_object_or_404(Tender, pk=tender_id) @@ -108,3 +164,62 @@ def create_bid(request, tender_id): } return render(request, 'core/create_bid.html', context) +@login_required +def update_bid(request, bid_id): + bid = get_object_or_404(Bid, pk=bid_id) + if request.method == 'POST': + form = BidForm(request.POST, instance=bid) + if form.is_valid(): + form.save() + return redirect('tender_detail', tender_id=bid.tender.id) + else: + form = BidForm(instance=bid) + context = { + 'form': form, + 'bid': bid + } + return render(request, 'core/update_bid.html', context) + +@login_required +def delete_bid(request, bid_id): + bid = get_object_or_404(Bid, pk=bid_id) + if request.method == 'POST': + tender_id = bid.tender.id + bid.delete() + return redirect('tender_detail', tender_id=tender_id) + context = { + 'bid': bid + } + return render(request, 'core/delete_bid.html', context) + +@login_required +def approve_bid(request, bid_id): + bid = get_object_or_404(Bid, pk=bid_id) + approval, created = Approval.objects.get_or_create(bid=bid, approver=request.user) + + if request.method == 'POST': + form = ApprovalForm(request.POST, instance=approval) + if form.is_valid(): + form.save() + return redirect('tender_detail', tender_id=bid.tender.id) + else: + form = ApprovalForm(instance=approval) + + context = { + 'form': form, + 'bid': bid + } + return render(request, 'core/approve_bid.html', context) + +@login_required +def delete_document(request, document_id): + document = get_object_or_404(Document, pk=document_id) + if request.method == 'POST': + tender_id = document.tender.id + document.delete() + return redirect('tender_detail', tender_id=tender_id) + context = { + 'document': document + } + return render(request, 'core/delete_document.html', context) +