wip
This commit is contained in:
@@ -108,6 +108,11 @@ class Invitations(object):
|
||||
invite = Invitations._get(token)
|
||||
return Invitations._update_status(invite, InvitationStatus.REVOKED)
|
||||
|
||||
@classmethod
|
||||
def lookup_by_portfolio_and_user(cls, portfolio, user):
|
||||
portfolio_role = PortfolioRoles.get(portfolio.id, user.id)
|
||||
return portfolio_role.latest_invitation
|
||||
|
||||
@classmethod
|
||||
def resend(cls, user, portfolio_id, token):
|
||||
portfolio = Portfolios.get(user, portfolio_id)
|
||||
|
@@ -14,6 +14,10 @@ class TaskOrderError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidOfficerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TaskOrders(object):
|
||||
SECTIONS = {
|
||||
"app_info": [
|
||||
@@ -145,6 +149,15 @@ class TaskOrders(object):
|
||||
"security_officer",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def remove_officer(cls, task_order, officer_type):
|
||||
if officer_type in TaskOrders.OFFICERS:
|
||||
setattr(task_order, officer_type, None)
|
||||
db.session.add(task_order)
|
||||
db.session.commit()
|
||||
else:
|
||||
raise (InvalidOfficerError)
|
||||
|
||||
@classmethod
|
||||
def add_officer(cls, user, task_order, officer_type, officer_data):
|
||||
Authorization.check_portfolio_permission(
|
||||
|
@@ -4,15 +4,22 @@ from flask import g, redirect, render_template, url_for, request as http_request
|
||||
|
||||
from . import portfolios_bp
|
||||
from atst.database import db
|
||||
from atst.domain.task_orders import TaskOrders, DD254s
|
||||
from atst.domain.exceptions import NotFoundError, NoAccessError
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.domain.exceptions import NotFoundError, NoAccessError
|
||||
from atst.domain.invitations import Invitations
|
||||
from atst.domain.portfolios import Portfolios
|
||||
from atst.domain.task_orders import TaskOrders, DD254s
|
||||
from atst.utils.localization import translate
|
||||
from atst.forms.dd_254 import DD254Form
|
||||
from atst.forms.ko_review import KOReviewForm
|
||||
from atst.forms.officers import EditTaskOrderOfficersForm
|
||||
from atst.models.task_order import Status as TaskOrderStatus
|
||||
from atst.forms.ko_review import KOReviewForm
|
||||
from atst.forms.dd_254 import DD254Form
|
||||
from atst.services.invitation import update_officer_invitations
|
||||
from atst.utils.flash import formatted_flash as flash
|
||||
from atst.services.invitation import (
|
||||
update_officer_invitations,
|
||||
OFFICER_INVITATIONS,
|
||||
Invitation as InvitationService,
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/task_orders")
|
||||
@@ -96,6 +103,66 @@ def ko_review(portfolio_id, task_order_id):
|
||||
raise NoAccessError("task_order")
|
||||
|
||||
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/task_order/<task_order_id>/resend_invite",
|
||||
methods=["POST"],
|
||||
)
|
||||
def resend_invite(portfolio_id, task_order_id, form=None):
|
||||
form_data = {**http_request.form}
|
||||
invite_type = form_data["invite_type"][0]
|
||||
|
||||
if invite_type not in dict.keys(OFFICER_INVITATIONS):
|
||||
raise NotFoundError("invite_type")
|
||||
|
||||
invite_type_info = OFFICER_INVITATIONS[invite_type]
|
||||
|
||||
task_order = TaskOrders.get(g.current_user, task_order_id)
|
||||
portfolio = Portfolios.get(g.current_user, portfolio_id)
|
||||
|
||||
#
|
||||
# TODO: Add in authorization check
|
||||
#
|
||||
|
||||
officer = getattr(task_order, invite_type_info["role"])
|
||||
|
||||
if not officer:
|
||||
raise NotFoundError("officer")
|
||||
|
||||
invitation = Invitations.lookup_by_portfolio_and_user(portfolio, officer)
|
||||
|
||||
if not invitation:
|
||||
raise NotFoundError("invitation")
|
||||
|
||||
Invitations.resend(g.current_user, portfolio.id, invitation.token)
|
||||
|
||||
invite_service = InvitationService(
|
||||
g.current_user,
|
||||
invitation.portfolio_role,
|
||||
invitation.email,
|
||||
subject=invite_type_info["subject"],
|
||||
email_template=invite_type_info["template"],
|
||||
)
|
||||
|
||||
invite_service.invite()
|
||||
|
||||
flash(
|
||||
"invitation_resent",
|
||||
officer_type=translate(
|
||||
"common.officer_helpers.underscore_to_friendly.{}".format(
|
||||
invite_type_info["role"]
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"portfolios.task_order_invitations",
|
||||
portfolio_id=portfolio.id,
|
||||
task_order_id=task_order.id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@portfolios_bp.route(
|
||||
"/portfolios/<portfolio_id>/task_order/<task_order_id>/review", methods=["POST"]
|
||||
)
|
||||
@@ -173,11 +240,14 @@ def edit_task_order_invitations(portfolio_id, task_order_id):
|
||||
)
|
||||
)
|
||||
else:
|
||||
return render_template(
|
||||
"portfolios/task_orders/invitations.html",
|
||||
portfolio=portfolio,
|
||||
task_order=task_order,
|
||||
form=form,
|
||||
return (
|
||||
render_template(
|
||||
"portfolios/task_orders/invitations.html",
|
||||
portfolio=portfolio,
|
||||
task_order=task_order,
|
||||
form=form,
|
||||
),
|
||||
400,
|
||||
)
|
||||
|
||||
|
||||
|
@@ -5,43 +5,43 @@ from atst.queue import queue
|
||||
from atst.domain.task_orders import TaskOrders
|
||||
from atst.domain.portfolio_roles import PortfolioRoles
|
||||
|
||||
OFFICER_INVITATIONS = [
|
||||
{
|
||||
"field": "ko_invite",
|
||||
OFFICER_INVITATIONS = {
|
||||
"ko_invite": {
|
||||
"role": "contracting_officer",
|
||||
"subject": "Review a task order",
|
||||
"template": "emails/invitation.txt",
|
||||
},
|
||||
{
|
||||
"field": "cor_invite",
|
||||
"cor_invite": {
|
||||
"role": "contracting_officer_representative",
|
||||
"subject": "Help with a task order",
|
||||
"template": "emails/invitation.txt",
|
||||
},
|
||||
{
|
||||
"field": "so_invite",
|
||||
"so_invite": {
|
||||
"role": "security_officer",
|
||||
"subject": "Review security for a task order",
|
||||
"template": "emails/invitation.txt",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def update_officer_invitations(user, task_order):
|
||||
for officer_type in OFFICER_INVITATIONS:
|
||||
field = officer_type["field"]
|
||||
if getattr(task_order, field) and not getattr(task_order, officer_type["role"]):
|
||||
officer_data = task_order.officer_dictionary(officer_type["role"])
|
||||
for invite_type in dict.keys(OFFICER_INVITATIONS):
|
||||
invite_opts = OFFICER_INVITATIONS[invite_type]
|
||||
|
||||
if getattr(task_order, invite_type) and not getattr(
|
||||
task_order, invite_opts["role"]
|
||||
):
|
||||
officer_data = task_order.officer_dictionary(invite_opts["role"])
|
||||
officer = TaskOrders.add_officer(
|
||||
user, task_order, officer_type["role"], officer_data
|
||||
user, task_order, invite_opts["role"], officer_data
|
||||
)
|
||||
pf_officer_member = PortfolioRoles.get(task_order.portfolio.id, officer.id)
|
||||
invite_service = Invitation(
|
||||
user,
|
||||
pf_officer_member,
|
||||
officer_data["email"],
|
||||
subject=officer_type["subject"],
|
||||
email_template=officer_type["template"],
|
||||
subject=invite_opts["subject"],
|
||||
email_template=invite_opts["template"],
|
||||
)
|
||||
invite_service.invite()
|
||||
|
||||
|
@@ -2,6 +2,11 @@ from flask import flash, render_template_string
|
||||
from atst.utils.localization import translate
|
||||
|
||||
MESSAGES = {
|
||||
"invitation_resent": {
|
||||
"title_template": "The {{ officer_type }} invite has been resent",
|
||||
"message_template": "Invitation has been resent",
|
||||
"category": "success",
|
||||
},
|
||||
"task_order_draft": {
|
||||
"title_template": translate("task_orders.form.draft_alert_title"),
|
||||
"message_template": """
|
||||
|
Reference in New Issue
Block a user