Merge pull request #323 from dod-ccpo/internal-comments
Hook up internal request comments
This commit is contained in:
commit
6ac19d315d
@ -225,8 +225,8 @@ class Requests(object):
|
||||
return Requests._add_review(user, request, review_data)
|
||||
|
||||
@classmethod
|
||||
def update_internal_comments(cls, user, request, comment_text):
|
||||
def add_internal_comment(cls, user, request, comment_text):
|
||||
Authorization.check_can_approve_request(user)
|
||||
request.internal_comments = RequestInternalComment(text=comment_text, user=user)
|
||||
request = RequestsQuery.add_and_commit(request)
|
||||
comment = RequestInternalComment(request=request, text=comment_text, user=user)
|
||||
RequestsQuery.add_and_commit(comment)
|
||||
return request
|
||||
|
@ -1,5 +1,5 @@
|
||||
from wtforms.fields import TextAreaField
|
||||
from wtforms.validators import Optional
|
||||
from wtforms.validators import InputRequired
|
||||
|
||||
from .forms import ValidatedForm
|
||||
|
||||
@ -7,6 +7,7 @@ from .forms import ValidatedForm
|
||||
class InternalCommentForm(ValidatedForm):
|
||||
text = TextAreaField(
|
||||
"CCPO Internal Notes",
|
||||
description="Add comments or notes for internal CCPO reference and follow-up here.<strong>These comments <em>will not</em> be visible to the person making the JEDI request.</strong>",
|
||||
validators=[Optional()],
|
||||
default="",
|
||||
description="Add comments or notes for internal CCPO reference and follow-up here. <strong>These comments <em>will not</em> be visible to the person making the JEDI request.</strong>",
|
||||
validators=[InputRequired()],
|
||||
)
|
||||
|
@ -45,7 +45,7 @@ class Request(Base, mixins.TimestampsMixin):
|
||||
"RequestRevision", back_populates="request", order_by="RequestRevision.sequence"
|
||||
)
|
||||
|
||||
internal_comments = relationship("RequestInternalComment", uselist=False)
|
||||
internal_comments = relationship("RequestInternalComment")
|
||||
|
||||
@property
|
||||
def latest_revision(self):
|
||||
@ -167,10 +167,6 @@ class Request(Base, mixins.TimestampsMixin):
|
||||
def reviews(self):
|
||||
return [status.review for status in self.status_events if status.review]
|
||||
|
||||
@property
|
||||
def internal_comments_text(self):
|
||||
return self.internal_comments.text if self.internal_comments else ""
|
||||
|
||||
@property
|
||||
def is_pending_financial_verification(self):
|
||||
return self.status == RequestStatus.PENDING_FINANCIAL_VERIFICATION
|
||||
|
@ -14,3 +14,4 @@ class RequestInternalComment(Base, mixins.TimestampsMixin):
|
||||
user = relationship("User")
|
||||
|
||||
request_id = Column(ForeignKey("requests.id", ondelete="CASCADE"), nullable=False)
|
||||
request = relationship("Request")
|
||||
|
@ -14,14 +14,12 @@ from atst.domain.exceptions import NotFoundError
|
||||
from atst.forms.ccpo_review import CCPOReviewForm
|
||||
from atst.forms.internal_comment import InternalCommentForm
|
||||
|
||||
from datetime import date
|
||||
|
||||
|
||||
def map_ccpo_authorizing(user):
|
||||
return {"fname_ccpo": user.first_name, "lname_ccpo": user.last_name}
|
||||
|
||||
|
||||
def render_approval(request, form=None):
|
||||
def render_approval(request, form=None, internal_comment_form=None):
|
||||
data = request.body
|
||||
if request.has_financial_data:
|
||||
data["task_order"] = request.task_order.to_dictionary()
|
||||
@ -30,21 +28,8 @@ def render_approval(request, form=None):
|
||||
mo_data = map_ccpo_authorizing(g.current_user)
|
||||
form = CCPOReviewForm(data=mo_data)
|
||||
|
||||
internal_comment_form = InternalCommentForm(text=request.internal_comments_text)
|
||||
|
||||
# Dummy internal comments
|
||||
comments = [
|
||||
{
|
||||
"time_created": date(2018, 9, 18),
|
||||
"full_name_commenter": "Darth Vader",
|
||||
"message": "We'll have no more of this Obi-Wan Kenobi jibberish...and don't talk to me about your mission, either. You're fortunate he doesn't blast you into a million pieces right here. ",
|
||||
},
|
||||
{
|
||||
"time_created": date(2018, 9, 20),
|
||||
"full_name_commenter": "Grand Moff Tarkin",
|
||||
"message": "We'll have no more of this Obi-Wan Kenobi jibberish...and don't talk to me about your mission, either. You're fortunate he doesn't blast you into a million pieces right here. ",
|
||||
},
|
||||
]
|
||||
if not internal_comment_form:
|
||||
internal_comment_form = InternalCommentForm()
|
||||
|
||||
return render_template(
|
||||
"requests/approval.html",
|
||||
@ -52,9 +37,9 @@ def render_approval(request, form=None):
|
||||
reviews=list(reversed(request.reviews)),
|
||||
request=request,
|
||||
current_status=request.status.value,
|
||||
f=form or CCPOReviewForm(),
|
||||
review_form=form or CCPOReviewForm(),
|
||||
internal_comment_form=internal_comment_form,
|
||||
comments=comments,
|
||||
comments=request.internal_comments,
|
||||
)
|
||||
|
||||
|
||||
@ -101,10 +86,12 @@ def task_order_pdf_download(request_id):
|
||||
|
||||
@requests_bp.route("/requests/internal_comments/<string:request_id>", methods=["POST"])
|
||||
def create_internal_comment(request_id):
|
||||
# form = InternalCommentForm(http_request.form)
|
||||
# if form.validate():
|
||||
# request = Requests.get(g.current_user, request_id)
|
||||
# Requests.update_internal_comments(g.current_user, request, form.data["text"])
|
||||
return redirect(
|
||||
url_for("requests.approval", request_id=request_id, _anchor="ccpo-notes")
|
||||
)
|
||||
form = InternalCommentForm(http_request.form)
|
||||
request = Requests.get(g.current_user, request_id)
|
||||
if form.validate():
|
||||
Requests.add_internal_comment(g.current_user, request, form.data.get("text"))
|
||||
return redirect(
|
||||
url_for("requests.approval", request_id=request_id, _anchor="ccpo-notes")
|
||||
)
|
||||
else:
|
||||
return render_approval(request, internal_comment_form=form)
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<article class='col col--grow request-approval'>
|
||||
|
||||
{% if f.errors %}
|
||||
{% if review_form.errors or internal_comment_form.errors %}
|
||||
{{ Alert('There were some errors',
|
||||
message="<p>Please see below.</p>",
|
||||
level='error'
|
||||
@ -32,11 +32,6 @@
|
||||
|
||||
</section>
|
||||
|
||||
{{ Alert('Comments and comment form are fake!',
|
||||
message="<p>Please note, the comments and comment form below are just mocked out. Submitting it will do nothing. These will be hooked up to real functionality shortly. </p><p><strong>Engineer: please remove this alert when you do your thing.</strong></p>",
|
||||
level='warning'
|
||||
) }}
|
||||
|
||||
<section class='internal-notes' id='ccpo-notes'>
|
||||
<form method="POST" action="{{ url_for('requests.create_internal_comment', request_id=request.id) }}">
|
||||
<div class='panel'>
|
||||
@ -51,8 +46,8 @@
|
||||
<li>
|
||||
<article class='comment-log__log-item'>
|
||||
<div>
|
||||
<h3 class='comment-log__log-item__header'>{{ comment.full_name_commenter }}</h3>
|
||||
<p>{{ comment.message }}</p>
|
||||
<h3 class='comment-log__log-item__header'>{{ comment.user.full_name }}</h3>
|
||||
<p>{{ comment.text }}</p>
|
||||
</div>
|
||||
{% set timestamp=comment.time_created | formattedDate("%Y-%m-%d %H:%M:%S %Z") %}
|
||||
<footer class='comment-log__log-item__timestamp'>
|
||||
@ -88,7 +83,7 @@
|
||||
|
||||
<section class='request-approval__review'>
|
||||
<form method="POST" action="{{ url_for("requests.submit_approval", request_id=request.id) }}" autocomplete="off">
|
||||
{{ f.csrf_token }}
|
||||
{{ review_form.csrf_token }}
|
||||
|
||||
<ccpo-approval inline-template>
|
||||
<div>
|
||||
@ -165,7 +160,7 @@
|
||||
<h3>Message to Requestor <span class='subtitle'>(optional)</span></h3>
|
||||
<div v-if='approving' key='approving' v-cloak>
|
||||
{{ TextInput(
|
||||
f.comment,
|
||||
review_form.comment,
|
||||
label='Approval comments or notes',
|
||||
description='Provide any comments or notes regarding the approval of this request. <strong>This message will be shared with the person making the JEDI request.</strong>.',
|
||||
paragraph=True,
|
||||
@ -175,7 +170,7 @@
|
||||
|
||||
<div v-else key='denying' v-cloak>
|
||||
{{ TextInput(
|
||||
f.comment,
|
||||
review_form.comment,
|
||||
label='Revision instructions or notes',
|
||||
paragraph=True,
|
||||
noMaxWidth=True
|
||||
@ -194,21 +189,21 @@
|
||||
|
||||
<div class='form-row'>
|
||||
<div class='form-col form-col--half'>
|
||||
{{ TextInput(f.fname_mao, placeholder="First name of mission authorizing official") }}
|
||||
{{ TextInput(review_form.fname_mao, placeholder="First name of mission authorizing official") }}
|
||||
</div>
|
||||
|
||||
<div class='form-col form-col--half'>
|
||||
{{ TextInput(f.lname_mao, placeholder="Last name of mission authorizing official") }}
|
||||
{{ TextInput(review_form.lname_mao, placeholder="Last name of mission authorizing official") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='form-row'>
|
||||
<div class='form-col form-col--half'>
|
||||
{{ TextInput(f.email_mao, placeholder="name@mail.mil", validation='email') }}
|
||||
{{ TextInput(review_form.email_mao, placeholder="name@mail.mil", validation='email') }}
|
||||
</div>
|
||||
|
||||
<div class='form-col form-col--half'>
|
||||
{{ TextInput(f.phone_mao, placeholder="(123) 456-7890", validation='usPhone') }}
|
||||
{{ TextInput(review_form.phone_mao, placeholder="(123) 456-7890", validation='usPhone') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -218,11 +213,11 @@
|
||||
|
||||
<div class='form-row'>
|
||||
<div class='form-col form-col--half'>
|
||||
{{ TextInput(f.fname_ccpo, placeholder="First name of CCPO authorizing official") }}
|
||||
{{ TextInput(review_form.fname_ccpo, placeholder="First name of CCPO authorizing official") }}
|
||||
</div>
|
||||
|
||||
<div class='form-col form-col--half'>
|
||||
{{ TextInput(f.lname_ccpo, placeholder="Last name of CCPO authorizing official") }}
|
||||
{{ TextInput(review_form.lname_ccpo, placeholder="Last name of CCPO authorizing official") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -223,10 +223,13 @@ def test_request_changes_to_financial_verification_info():
|
||||
assert current_review.fname_mao == review_data["fname_mao"]
|
||||
|
||||
|
||||
def test_update_internal_comments():
|
||||
def test_add_internal_comment():
|
||||
request = RequestFactory.create()
|
||||
ccpo = UserFactory.from_atat_role("ccpo")
|
||||
|
||||
request = Requests.update_internal_comments(ccpo, request, "this is my comment")
|
||||
assert len(request.internal_comments) == 0
|
||||
|
||||
assert request.internal_comments.text == "this is my comment"
|
||||
request = Requests.add_internal_comment(ccpo, request, "this is my comment")
|
||||
|
||||
assert len(request.internal_comments) == 1
|
||||
assert request.internal_comments[0].text == "this is my comment"
|
||||
|
@ -108,3 +108,56 @@ def test_can_submit_request_denial(client, user_session):
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert request.status == RequestStatus.CHANGES_REQUESTED
|
||||
|
||||
|
||||
def test_ccpo_user_can_comment_on_request(client, user_session):
|
||||
user = UserFactory.from_atat_role("ccpo")
|
||||
user_session(user)
|
||||
request = RequestFactory.create_with_status(
|
||||
status=RequestStatus.PENDING_CCPO_ACCEPTANCE
|
||||
)
|
||||
assert len(request.internal_comments) == 0
|
||||
|
||||
comment_text = "This is the greatest request in the history of requests"
|
||||
comment_form_data = {"text": comment_text}
|
||||
response = client.post(
|
||||
url_for("requests.create_internal_comment", request_id=request.id),
|
||||
data=comment_form_data,
|
||||
)
|
||||
assert response.status_code == 302
|
||||
assert len(request.internal_comments) == 1
|
||||
assert request.internal_comments[0].text == comment_text
|
||||
|
||||
|
||||
def test_comment_text_is_required(client, user_session):
|
||||
user = UserFactory.from_atat_role("ccpo")
|
||||
user_session(user)
|
||||
request = RequestFactory.create_with_status(
|
||||
status=RequestStatus.PENDING_CCPO_ACCEPTANCE
|
||||
)
|
||||
assert len(request.internal_comments) == 0
|
||||
|
||||
comment_form_data = {"text": ""}
|
||||
response = client.post(
|
||||
url_for("requests.create_internal_comment", request_id=request.id),
|
||||
data=comment_form_data,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert len(request.internal_comments) == 0
|
||||
|
||||
|
||||
def test_other_user_cannot_comment_on_request(client, user_session):
|
||||
user = UserFactory.create()
|
||||
user_session(user)
|
||||
request = RequestFactory.create_with_status(
|
||||
status=RequestStatus.PENDING_CCPO_ACCEPTANCE
|
||||
)
|
||||
|
||||
comment_text = "What is this even"
|
||||
comment_form_data = {"text": comment_text}
|
||||
response = client.post(
|
||||
url_for("requests.create_internal_comment", request_id=request.id),
|
||||
data=comment_form_data,
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
Loading…
x
Reference in New Issue
Block a user