Update TO form based on new model
This commit is contained in:
parent
c90e574e83
commit
91e41199b7
@ -18,8 +18,10 @@ class TaskOrders(BaseDomainClass):
|
|||||||
UNCLASSIFIED_FUNDING = []
|
UNCLASSIFIED_FUNDING = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, creator, portfolio):
|
def create(cls, creator, portfolio_id, **kwargs):
|
||||||
task_order = TaskOrder(portfolio=portfolio, creator=creator)
|
task_order = TaskOrder(portfolio_id=portfolio_id, creator=creator)
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
setattr(task_order, key, value)
|
||||||
|
|
||||||
db.session.add(task_order)
|
db.session.add(task_order)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -27,7 +29,8 @@ class TaskOrders(BaseDomainClass):
|
|||||||
return task_order
|
return task_order
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update(cls, task_order, **kwargs):
|
def update(cls, task_order_id, **kwargs):
|
||||||
|
task_order = TaskOrders.get(task_order_id)
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(task_order, key, value)
|
setattr(task_order, key, value)
|
||||||
|
|
||||||
|
@ -1,131 +1,25 @@
|
|||||||
from wtforms.fields import (
|
from wtforms.fields import (
|
||||||
BooleanField,
|
BooleanField,
|
||||||
DecimalField,
|
|
||||||
RadioField,
|
|
||||||
SelectField,
|
|
||||||
SelectMultipleField,
|
|
||||||
StringField,
|
StringField,
|
||||||
TextAreaField,
|
|
||||||
FileField,
|
|
||||||
)
|
)
|
||||||
from wtforms.fields.html5 import DateField, TelField
|
from wtforms.fields.html5 import DateField
|
||||||
from wtforms.widgets import ListWidget, CheckboxInput
|
from wtforms.validators import Required, Optional
|
||||||
from wtforms.validators import Email, Length, Required, Optional
|
|
||||||
from flask_wtf.file import FileAllowed
|
|
||||||
|
|
||||||
from atst.forms.validators import IsNumber, PhoneNumber, RequiredIf
|
|
||||||
|
|
||||||
from .forms import BaseForm
|
from .forms import BaseForm
|
||||||
from .data import (
|
|
||||||
SERVICE_BRANCHES,
|
|
||||||
APP_MIGRATION,
|
|
||||||
APPLICATION_COMPLEXITY,
|
|
||||||
DEV_TEAM,
|
|
||||||
TEAM_EXPERIENCE,
|
|
||||||
PERIOD_OF_PERFORMANCE_LENGTH,
|
|
||||||
)
|
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
|
||||||
|
|
||||||
class AppInfoWithExistingPortfolioForm(BaseForm):
|
class TaskOrderForm(BaseForm):
|
||||||
scope = TextAreaField(
|
number = StringField(validators=[Required()])
|
||||||
translate("forms.task_order.scope_label"),
|
|
||||||
description=translate("forms.task_order.scope_description"),
|
|
||||||
)
|
|
||||||
app_migration = RadioField(
|
|
||||||
translate("forms.task_order.app_migration.label"),
|
|
||||||
description=translate("forms.task_order.app_migration.description"),
|
|
||||||
choices=APP_MIGRATION,
|
|
||||||
default="",
|
|
||||||
validators=[Optional()],
|
|
||||||
)
|
|
||||||
native_apps = RadioField(
|
|
||||||
translate("forms.task_order.native_apps.label"),
|
|
||||||
description=translate("forms.task_order.native_apps.description"),
|
|
||||||
choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")],
|
|
||||||
default="",
|
|
||||||
validators=[Optional()],
|
|
||||||
)
|
|
||||||
complexity = SelectMultipleField(
|
|
||||||
translate("forms.task_order.complexity.label"),
|
|
||||||
description=translate("forms.task_order.complexity.description"),
|
|
||||||
choices=APPLICATION_COMPLEXITY,
|
|
||||||
default=None,
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
widget=ListWidget(prefix_label=False),
|
|
||||||
option_widget=CheckboxInput(),
|
|
||||||
)
|
|
||||||
complexity_other = StringField(
|
|
||||||
translate("forms.task_order.complexity_other_label"),
|
|
||||||
default=None,
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
dev_team = SelectMultipleField(
|
|
||||||
translate("forms.task_order.dev_team.label"),
|
|
||||||
description=translate("forms.task_order.dev_team.description"),
|
|
||||||
choices=DEV_TEAM,
|
|
||||||
default=None,
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
widget=ListWidget(prefix_label=False),
|
|
||||||
option_widget=CheckboxInput(),
|
|
||||||
)
|
|
||||||
dev_team_other = StringField(
|
|
||||||
translate("forms.task_order.dev_team_other_label"),
|
|
||||||
default=None,
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
team_experience = RadioField(
|
|
||||||
translate("forms.task_order.team_experience.label"),
|
|
||||||
description=translate("forms.task_order.team_experience.description"),
|
|
||||||
choices=TEAM_EXPERIENCE,
|
|
||||||
default="",
|
|
||||||
validators=[Optional()],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AppInfoForm(AppInfoWithExistingPortfolioForm):
|
|
||||||
portfolio_name = StringField(
|
|
||||||
translate("forms.task_order.portfolio_name_label"),
|
|
||||||
description=translate("forms.task_order.portfolio_name_description"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[
|
|
||||||
Required(),
|
|
||||||
Length(
|
|
||||||
min=4,
|
|
||||||
max=100,
|
|
||||||
message=translate("forms.portfolio.name_length_validation_message"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
defense_component = SelectField(
|
|
||||||
translate("forms.task_order.defense_component_label"),
|
|
||||||
choices=SERVICE_BRANCHES,
|
|
||||||
default="",
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FundingForm(BaseForm):
|
class FundingForm(BaseForm):
|
||||||
performance_length = SelectField(
|
|
||||||
translate("forms.task_order.performance_length.label"),
|
|
||||||
choices=PERIOD_OF_PERFORMANCE_LENGTH,
|
|
||||||
)
|
|
||||||
start_date = DateField(
|
start_date = DateField(
|
||||||
translate("forms.task_order.start_date_label"), format="%m/%d/%Y"
|
translate("forms.task_order.start_date_label"), format="%m/%d/%Y"
|
||||||
)
|
)
|
||||||
end_date = DateField(
|
end_date = DateField(
|
||||||
translate("forms.task_order.end_date_label"), format="%m/%d/%Y"
|
translate("forms.task_order.end_date_label"), format="%m/%d/%Y"
|
||||||
)
|
)
|
||||||
csp_estimate = FileField(
|
|
||||||
translate("forms.task_order.csp_estimate_label"),
|
|
||||||
description=translate("forms.task_order.csp_estimate_description"),
|
|
||||||
validators=[
|
|
||||||
FileAllowed(
|
|
||||||
["pdf", "png"], translate("forms.task_order.file_format_not_allowed")
|
|
||||||
)
|
|
||||||
],
|
|
||||||
render_kw={"accept": ".pdf,.png,application/pdf,image/png"},
|
|
||||||
)
|
|
||||||
clin_01 = DecimalField(
|
clin_01 = DecimalField(
|
||||||
translate("forms.task_order.clin_01_label"), validators=[Optional()]
|
translate("forms.task_order.clin_01_label"), validators=[Optional()]
|
||||||
)
|
)
|
||||||
@ -151,135 +45,7 @@ class UnclassifiedFundingForm(FundingForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class OversightForm(BaseForm):
|
|
||||||
ko_first_name = StringField(
|
|
||||||
translate("forms.task_order.oversight_first_name_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
ko_last_name = StringField(
|
|
||||||
translate("forms.task_order.oversight_last_name_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
ko_email = StringField(
|
|
||||||
translate("forms.task_order.oversight_email_label"),
|
|
||||||
validators=[Optional(), Email()],
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
ko_phone_number = TelField(
|
|
||||||
translate("forms.task_order.oversight_phone_label"),
|
|
||||||
validators=[Optional(), PhoneNumber()],
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
ko_dod_id = StringField(
|
|
||||||
translate("forms.task_order.oversight_dod_id_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[
|
|
||||||
RequiredIf(lambda form: form._fields.get("ko_invite").data),
|
|
||||||
Length(min=10),
|
|
||||||
IsNumber(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
am_cor = BooleanField(translate("forms.task_order.oversight_am_cor_label"))
|
|
||||||
cor_first_name = StringField(
|
|
||||||
translate("forms.task_order.oversight_first_name_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
cor_last_name = StringField(
|
|
||||||
translate("forms.task_order.oversight_last_name_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
cor_email = StringField(
|
|
||||||
translate("forms.task_order.oversight_email_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[Optional(), Email()],
|
|
||||||
)
|
|
||||||
cor_phone_number = TelField(
|
|
||||||
translate("forms.task_order.oversight_phone_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[
|
|
||||||
RequiredIf(lambda form: not form._fields.get("am_cor").data),
|
|
||||||
Optional(),
|
|
||||||
PhoneNumber(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
cor_dod_id = StringField(
|
|
||||||
translate("forms.task_order.oversight_dod_id_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[
|
|
||||||
RequiredIf(
|
|
||||||
lambda form: not form._fields.get("am_cor").data
|
|
||||||
and form._fields.get("cor_invite").data
|
|
||||||
),
|
|
||||||
Length(min=10),
|
|
||||||
IsNumber(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
so_first_name = StringField(
|
|
||||||
translate("forms.task_order.oversight_first_name_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
so_last_name = StringField(
|
|
||||||
translate("forms.task_order.oversight_last_name_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
)
|
|
||||||
so_email = StringField(
|
|
||||||
translate("forms.task_order.oversight_email_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[Optional(), Email()],
|
|
||||||
)
|
|
||||||
so_phone_number = TelField(
|
|
||||||
translate("forms.task_order.oversight_phone_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[Optional(), PhoneNumber()],
|
|
||||||
)
|
|
||||||
so_dod_id = StringField(
|
|
||||||
translate("forms.task_order.oversight_dod_id_label"),
|
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
validators=[
|
|
||||||
RequiredIf(lambda form: form._fields.get("so_invite").data),
|
|
||||||
Length(min=10),
|
|
||||||
IsNumber(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
ko_invite = BooleanField(
|
|
||||||
translate("forms.task_order.ko_invite_label"),
|
|
||||||
description=translate("forms.task_order.skip_invite_description"),
|
|
||||||
)
|
|
||||||
cor_invite = BooleanField(
|
|
||||||
translate("forms.task_order.cor_invite_label"),
|
|
||||||
description=translate("forms.task_order.skip_invite_description"),
|
|
||||||
)
|
|
||||||
so_invite = BooleanField(
|
|
||||||
translate("forms.task_order.so_invite_label"),
|
|
||||||
description=translate("forms.task_order.skip_invite_description"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ReviewForm(BaseForm):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SignatureForm(BaseForm):
|
class SignatureForm(BaseForm):
|
||||||
level_of_warrant = DecimalField(
|
|
||||||
translate("task_orders.sign.level_of_warrant_label"),
|
|
||||||
description=translate("task_orders.sign.level_of_warrant_description"),
|
|
||||||
validators=[
|
|
||||||
RequiredIf(
|
|
||||||
lambda form: (
|
|
||||||
form._fields.get("unlimited_level_of_warrant").data is not True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
unlimited_level_of_warrant = BooleanField(
|
|
||||||
translate("task_orders.sign.unlimited_level_of_warrant_description"),
|
|
||||||
validators=[Optional()],
|
|
||||||
)
|
|
||||||
|
|
||||||
signature = BooleanField(
|
signature = BooleanField(
|
||||||
translate("task_orders.sign.digital_signature_label"),
|
translate("task_orders.sign.digital_signature_label"),
|
||||||
description=translate("task_orders.sign.digital_signature_description"),
|
description=translate("task_orders.sign.digital_signature_description"),
|
||||||
|
@ -1,355 +1,59 @@
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
request as http_request,
|
|
||||||
render_template,
|
|
||||||
g,
|
g,
|
||||||
redirect,
|
redirect,
|
||||||
|
render_template,
|
||||||
|
request as http_request,
|
||||||
url_for,
|
url_for,
|
||||||
current_app as app,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import task_orders_bp
|
from . import task_orders_bp
|
||||||
from atst.domain.task_orders import TaskOrders
|
|
||||||
from atst.domain.portfolios import Portfolios
|
|
||||||
from atst.utils.flash import formatted_flash as flash
|
|
||||||
import atst.forms.task_order as task_order_form
|
|
||||||
from atst.domain.authz.decorator import user_can_access_decorator as user_can
|
from atst.domain.authz.decorator import user_can_access_decorator as user_can
|
||||||
|
from atst.domain.task_orders import TaskOrders
|
||||||
|
from atst.forms.task_order import TaskOrderForm
|
||||||
from atst.models.permissions import Permissions
|
from atst.models.permissions import Permissions
|
||||||
|
from atst.utils.flash import formatted_flash as flash
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
|
||||||
|
|
||||||
TASK_ORDER_SECTIONS = [
|
|
||||||
{
|
|
||||||
"section": "app_info",
|
|
||||||
"title": translate("forms.task_order.first_step_title"),
|
|
||||||
"template": "task_orders/new/app_info.html",
|
|
||||||
"form": task_order_form.AppInfoForm,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"section": "funding",
|
|
||||||
"title": "Funding",
|
|
||||||
"template": "task_orders/new/funding.html",
|
|
||||||
"form": task_order_form.FundingForm,
|
|
||||||
"unclassified_form": task_order_form.UnclassifiedFundingForm,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"section": "oversight",
|
|
||||||
"title": "Oversight",
|
|
||||||
"template": "task_orders/new/oversight.html",
|
|
||||||
"form": task_order_form.OversightForm,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"section": "review",
|
|
||||||
"title": "Review",
|
|
||||||
"template": "task_orders/new/review.html",
|
|
||||||
"form": task_order_form.ReviewForm,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ShowTaskOrderWorkflow:
|
|
||||||
def __init__(self, user, screen=1, task_order_id=None, portfolio_id=None):
|
|
||||||
self.user = user
|
|
||||||
self.screen = screen
|
|
||||||
self.task_order_id = task_order_id
|
|
||||||
self._task_order = None
|
|
||||||
self.portfolio_id = portfolio_id
|
|
||||||
self._portfolio = None
|
|
||||||
self._section = TASK_ORDER_SECTIONS[screen - 1]
|
|
||||||
self._form = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def task_order(self):
|
|
||||||
if not self._task_order and self.task_order_id:
|
|
||||||
if self.portfolio_id:
|
|
||||||
self._task_order = TaskOrders.get(
|
|
||||||
self.task_order_id, portfolio_id=self.portfolio_id
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._task_order = TaskOrders.get(self.task_order_id)
|
|
||||||
|
|
||||||
return self._task_order
|
|
||||||
|
|
||||||
@property
|
|
||||||
def portfolio(self):
|
|
||||||
if not self._portfolio:
|
|
||||||
if self.task_order:
|
|
||||||
self._portfolio = self.task_order.portfolio
|
|
||||||
elif self.portfolio_id:
|
|
||||||
self._portfolio = Portfolios.get(self.user, self.portfolio_id)
|
|
||||||
|
|
||||||
return self._portfolio
|
|
||||||
|
|
||||||
@property
|
|
||||||
def form(self):
|
|
||||||
form_type = (
|
|
||||||
"unclassified_form"
|
|
||||||
if "unclassified_form" in self._section and not app.config.get("CLASSIFIED")
|
|
||||||
else "form"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._form:
|
|
||||||
pass
|
|
||||||
elif self.task_order:
|
|
||||||
if self.pf_attributes_read_only and self.screen == 1:
|
|
||||||
self._form = task_order_form.AppInfoWithExistingPortfolioForm(
|
|
||||||
obj=self.task_order
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._form = self._section[form_type](obj=self.task_order)
|
|
||||||
# manually set SelectMultipleFields
|
|
||||||
if self._section["section"] == "app_info":
|
|
||||||
self._form.complexity.data = self.task_order.complexity
|
|
||||||
self._form.dev_team.data = self.task_order.dev_team
|
|
||||||
elif self._section["section"] == "oversight":
|
|
||||||
if self.user.dod_id == self.task_order.cor_dod_id:
|
|
||||||
self._form.am_cor.data = True
|
|
||||||
if self.task_order.contracting_officer or self.task_order.ko_invite:
|
|
||||||
self._form.ko_invite.data = True
|
|
||||||
if (
|
|
||||||
self.task_order.contracting_officer_representative
|
|
||||||
or self.task_order.cor_invite
|
|
||||||
):
|
|
||||||
self._form.cor_invite.data = True
|
|
||||||
if self.task_order.security_officer or self.task_order.so_invite:
|
|
||||||
self._form.so_invite.data = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._form = self._section[form_type]()
|
|
||||||
return self._form
|
|
||||||
|
|
||||||
@property
|
|
||||||
def template(self):
|
|
||||||
return self._section["template"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def display_screens(self):
|
|
||||||
screen_info = deepcopy(TASK_ORDER_SECTIONS)
|
|
||||||
|
|
||||||
if self.task_order:
|
|
||||||
for section in screen_info:
|
|
||||||
section["completion"] = TaskOrders.section_completion_status(
|
|
||||||
self.task_order, section["section"]
|
|
||||||
)
|
|
||||||
|
|
||||||
return screen_info
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_complete(self):
|
|
||||||
if self.task_order and TaskOrders.all_sections_complete(self.task_order):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pf_attributes_read_only(self):
|
|
||||||
if self.task_order and self.portfolio.num_task_orders > 1:
|
|
||||||
return True
|
|
||||||
elif self.portfolio_id:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow):
|
|
||||||
def __init__(
|
|
||||||
self, user, form_data, screen=1, task_order_id=None, portfolio_id=None
|
|
||||||
):
|
|
||||||
self.user = user
|
|
||||||
self.form_data = form_data
|
|
||||||
self.screen = screen
|
|
||||||
self.task_order_id = task_order_id
|
|
||||||
self.portfolio_id = portfolio_id
|
|
||||||
self._task_order = None
|
|
||||||
self._section = TASK_ORDER_SECTIONS[screen - 1]
|
|
||||||
self._form = None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def form(self):
|
|
||||||
if not self._form:
|
|
||||||
form_type = (
|
|
||||||
"unclassified_form"
|
|
||||||
if "unclassified_form" in self._section
|
|
||||||
and not app.config.get("CLASSIFIED")
|
|
||||||
else "form"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.pf_attributes_read_only and self.screen == 1:
|
|
||||||
self._form = task_order_form.AppInfoWithExistingPortfolioForm(
|
|
||||||
self.form_data
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._form = self._section[form_type](
|
|
||||||
self.form_data, obj=self.task_order
|
|
||||||
)
|
|
||||||
|
|
||||||
return self._form
|
|
||||||
|
|
||||||
@property
|
|
||||||
def portfolio(self):
|
|
||||||
if self.task_order:
|
|
||||||
return self.task_order.portfolio
|
|
||||||
|
|
||||||
@property
|
|
||||||
def task_order_form_data(self):
|
|
||||||
to_data = self.form.data.copy()
|
|
||||||
if "portfolio_name" in to_data:
|
|
||||||
to_data.pop("portfolio_name")
|
|
||||||
if "defense_component" in to_data:
|
|
||||||
to_data.pop("defense_component")
|
|
||||||
|
|
||||||
# don't save other text in DB unless "other" is checked
|
|
||||||
if (
|
|
||||||
"complexity" in to_data
|
|
||||||
and bool(to_data["complexity"])
|
|
||||||
and "other" not in to_data["complexity"]
|
|
||||||
):
|
|
||||||
to_data["complexity_other"] = None
|
|
||||||
if (
|
|
||||||
"dev_team" in to_data
|
|
||||||
and bool(to_data["dev_team"])
|
|
||||||
and "other" not in to_data["dev_team"]
|
|
||||||
):
|
|
||||||
to_data["dev_team_other"] = None
|
|
||||||
|
|
||||||
if self.form_data.get("am_cor"):
|
|
||||||
cor_data = {
|
|
||||||
"cor_first_name": self.user.first_name,
|
|
||||||
"cor_last_name": self.user.last_name,
|
|
||||||
"cor_email": self.user.email,
|
|
||||||
"cor_phone_number": self.user.phone_number,
|
|
||||||
"cor_dod_id": self.user.dod_id,
|
|
||||||
"cor_id": self.user.id,
|
|
||||||
}
|
|
||||||
to_data = {**to_data, **cor_data}
|
|
||||||
|
|
||||||
return to_data
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
return self.form.validate()
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
if self.task_order:
|
|
||||||
if "portfolio_name" in self.form.data:
|
|
||||||
new_name = self.form.data["portfolio_name"]
|
|
||||||
old_name = self.task_order.portfolio_name
|
|
||||||
if not new_name == old_name:
|
|
||||||
Portfolios.update(self.task_order.portfolio, {"name": new_name})
|
|
||||||
TaskOrders.update(self.task_order, **self.task_order_form_data)
|
|
||||||
else:
|
|
||||||
if self.portfolio_id:
|
|
||||||
pf = Portfolios.get(self.user, self.portfolio_id)
|
|
||||||
else:
|
|
||||||
pf = Portfolios.create(
|
|
||||||
user=self.user,
|
|
||||||
portfolio_attrs={
|
|
||||||
"name": self.form.portfolio_name.data,
|
|
||||||
"defense_component": self.form.defense_component.data,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self._task_order = TaskOrders.create(portfolio=pf, creator=self.user)
|
|
||||||
TaskOrders.update(self.task_order, **self.task_order_form_data)
|
|
||||||
|
|
||||||
return self.task_order
|
|
||||||
|
|
||||||
|
|
||||||
@task_orders_bp.route("/task_orders/new/get_started")
|
@task_orders_bp.route("/task_orders/new/get_started")
|
||||||
|
# TODO: see if this route still exists in new design
|
||||||
def get_started():
|
def get_started():
|
||||||
return render_template("task_orders/new/get_started.html") # pragma: no cover
|
return render_template("task_orders/new/get_started.html") # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def is_new_task_order(*_args, **kwargs):
|
@task_orders_bp.route("/portfolios/<portfolio_id>/task_orders/new")
|
||||||
return (
|
@user_can(Permissions.CREATE_TASK_ORDER, message="view new task order form")
|
||||||
"screen" in kwargs
|
def new(portfolio_id):
|
||||||
and kwargs["screen"] == 1
|
return render_template("task_orders/new", form=TaskOrderForm())
|
||||||
and "task_order_id" not in kwargs
|
|
||||||
and "portfolio_id" not in kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: /task_orders/new/<int:screen>/<task_order_id> should not exist
|
@task_orders_bp.route("/portfolios/<portfolio_id>/task_orders/new", methods=["POST"])
|
||||||
@task_orders_bp.route("/task_orders/new/<int:screen>")
|
@user_can(Permissions.CREATE_TASK_ORDER, message="create new task order")
|
||||||
@task_orders_bp.route("/task_orders/new/<int:screen>/<task_order_id>")
|
def create(portfolio_id):
|
||||||
@task_orders_bp.route("/portfolios/<portfolio_id>/task_orders/new/<int:screen>")
|
form_data = http_request.form
|
||||||
@user_can(
|
form = TaskOrderForm(form_data)
|
||||||
Permissions.CREATE_TASK_ORDER,
|
|
||||||
override=is_new_task_order,
|
|
||||||
message="view new task order form",
|
|
||||||
)
|
|
||||||
def new(screen, task_order_id=None, portfolio_id=None):
|
|
||||||
workflow = ShowTaskOrderWorkflow(
|
|
||||||
g.current_user, screen, task_order_id, portfolio_id
|
|
||||||
)
|
|
||||||
template_args = {
|
|
||||||
"current": screen,
|
|
||||||
"task_order_id": task_order_id,
|
|
||||||
"screens": workflow.display_screens,
|
|
||||||
"form": workflow.form,
|
|
||||||
"complete": workflow.is_complete,
|
|
||||||
}
|
|
||||||
|
|
||||||
if task_order_id and screen is 4:
|
if form.validate():
|
||||||
if not TaskOrders.all_sections_complete(workflow.task_order):
|
TaskOrders.create(g.current_user, portfolio_id, **form.data)
|
||||||
flash("task_order_draft")
|
# TODO: ask UX where do you go after save
|
||||||
|
|
||||||
if workflow.pf_attributes_read_only:
|
|
||||||
template_args["portfolio"] = workflow.portfolio
|
@task_orders_bp.route("/portfolios/<portfolio_id>/task_orders/<task_order_id>/edit")
|
||||||
|
@user_can(Permissions.CREATE_TASK_ORDER, message="update task order")
|
||||||
url_args = {"screen": screen}
|
def edit(portfolio_id, taks_order_id):
|
||||||
if task_order_id:
|
return render_template("task_orders/edit", form=TaskOrderForm())
|
||||||
url_args["task_order_id"] = task_order_id
|
|
||||||
else:
|
|
||||||
url_args["portfolio_id"] = portfolio_id
|
|
||||||
|
|
||||||
if workflow.task_order:
|
|
||||||
template_args["task_order"] = workflow.task_order
|
|
||||||
if http_request.args.get("ko_edit"):
|
|
||||||
template_args["ko_edit"] = True
|
|
||||||
template_args["next"] = url_for(
|
|
||||||
"task_orders.ko_review", task_order_id=task_order_id
|
|
||||||
)
|
|
||||||
url_args["next"] = template_args["next"]
|
|
||||||
|
|
||||||
template_args["action_url"] = url_for("task_orders.update", **url_args)
|
|
||||||
|
|
||||||
return render_template(workflow.template, **template_args)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: /task_orders/new/<int:screen>/<task_order_id> should not exist
|
|
||||||
@task_orders_bp.route("/task_orders/new/<int:screen>", methods=["POST"])
|
|
||||||
@task_orders_bp.route("/task_orders/new/<int:screen>/<task_order_id>", methods=["POST"])
|
|
||||||
@task_orders_bp.route(
|
@task_orders_bp.route(
|
||||||
"/portfolios/<portfolio_id>/task_orders/new/<int:screen>", methods=["POST"]
|
"/portfolios/<portfolio_id>/task_orders/<task_order_id>", methods=["POST"]
|
||||||
)
|
)
|
||||||
@user_can(
|
@user_can(Permissions.CREATE_TASK_ORDER, message="update task order")
|
||||||
Permissions.CREATE_TASK_ORDER,
|
def update(portfolio_id, task_order_id=None):
|
||||||
override=is_new_task_order,
|
form_data = http_request.form
|
||||||
message="update task order",
|
form = TaskOrderForm(form_data)
|
||||||
)
|
|
||||||
def update(screen, task_order_id=None, portfolio_id=None):
|
|
||||||
form_data = {**http_request.form, **http_request.files}
|
|
||||||
workflow = UpdateTaskOrderWorkflow(
|
|
||||||
g.current_user, form_data, screen, task_order_id, portfolio_id
|
|
||||||
)
|
|
||||||
|
|
||||||
if workflow.validate():
|
if form.validate():
|
||||||
workflow.update()
|
TaskOrders.update(task_order_id, **form.data)
|
||||||
if http_request.args.get("next"):
|
# TODO: ask UX where do you go after save
|
||||||
redirect_url = http_request.args.get("next")
|
|
||||||
else:
|
|
||||||
redirect_url = url_for(
|
|
||||||
"task_orders.new",
|
|
||||||
screen=screen + 1,
|
|
||||||
task_order_id=workflow.task_order.id,
|
|
||||||
)
|
|
||||||
return redirect(redirect_url)
|
|
||||||
else:
|
|
||||||
return render_template(
|
|
||||||
workflow.template,
|
|
||||||
current=screen,
|
|
||||||
task_order_id=task_order_id,
|
|
||||||
portfolio_id=portfolio_id,
|
|
||||||
screens=workflow.display_screens,
|
|
||||||
form=workflow.form,
|
|
||||||
)
|
|
||||||
|
@ -6,48 +6,6 @@
|
|||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
|
||||||
{% macro officer_name(officer) -%}
|
|
||||||
{%- if not officer -%}
|
|
||||||
not yet invited
|
|
||||||
{%- elif officer == g.current_user -%}
|
|
||||||
you
|
|
||||||
{%- else -%}
|
|
||||||
{{ officer.full_name }}
|
|
||||||
{%- endif -%}
|
|
||||||
{%- endmacro -%}
|
|
||||||
|
|
||||||
{% macro Step(description="", complete=True, button_text=None, button_url=None) %}
|
|
||||||
<div class="task-order-next-steps__step panel__content">
|
|
||||||
<div class="row">
|
|
||||||
<div class="task-order-next-steps__icon col">
|
|
||||||
{% if complete %}
|
|
||||||
<span class="label label--success">Completed</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="label">Not started</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="task-order-next-steps__text col col--grow">
|
|
||||||
<div class="task-order-next-steps__heading row">
|
|
||||||
<span>{{ description }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="task-order-next-steps__action col">
|
|
||||||
{% if not task_order.is_active and button_text and button_url %}
|
|
||||||
<a
|
|
||||||
href="{{ button_url }}"
|
|
||||||
class="usa-button usa-button-primary">
|
|
||||||
{{ button_text }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if caller %}
|
|
||||||
{{ caller() }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro DocumentLink(title="", link_url="", description="") %}
|
{% macro DocumentLink(title="", link_url="", description="") %}
|
||||||
{% set disabled = not link_url %}
|
{% set disabled = not link_url %}
|
||||||
<div class="task-order-document-link">
|
<div class="task-order-document-link">
|
||||||
@ -99,16 +57,6 @@
|
|||||||
<div id="next-steps" class="task-order-next-steps">
|
<div id="next-steps" class="task-order-next-steps">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h3 class="task-order-next-steps__panel-head panel__content">{{ "task_orders.view.whats_next" | translate }}</h3>
|
<h3 class="task-order-next-steps__panel-head panel__content">{{ "task_orders.view.whats_next" | translate }}</h3>
|
||||||
{{
|
|
||||||
Step(
|
|
||||||
button_text='Edit',
|
|
||||||
button_url=url_for("task_orders.new", screen=1, task_order_id=task_order.id),
|
|
||||||
complete=to_form_complete,
|
|
||||||
description="task_orders.view.steps.draft" | translate({
|
|
||||||
"contact": officer_name(task_order.creator)
|
|
||||||
})| safe,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="task-order-sidebar col">
|
<div class="task-order-sidebar col">
|
||||||
|
0
templates/task_orders/edit.html
Normal file
0
templates/task_orders/edit.html
Normal file
0
templates/task_orders/new.html
Normal file
0
templates/task_orders/new.html
Normal file
@ -2,21 +2,21 @@ import datetime
|
|||||||
import pytest
|
import pytest
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from atst.domain.invitations import (
|
|
||||||
PortfolioInvitations,
|
|
||||||
InvitationError,
|
|
||||||
WrongUserError,
|
|
||||||
ExpiredError,
|
|
||||||
NotFoundError,
|
|
||||||
)
|
|
||||||
from atst.domain.audit_log import AuditLog
|
from atst.domain.audit_log import AuditLog
|
||||||
|
from atst.domain.invitations import (
|
||||||
|
ExpiredError,
|
||||||
|
InvitationError,
|
||||||
|
NotFoundError,
|
||||||
|
PortfolioInvitations,
|
||||||
|
WrongUserError,
|
||||||
|
)
|
||||||
from atst.models import InvitationStatus
|
from atst.models import InvitationStatus
|
||||||
|
|
||||||
from tests.factories import (
|
from tests.factories import (
|
||||||
PortfolioFactory,
|
PortfolioFactory,
|
||||||
|
PortfolioInvitationFactory,
|
||||||
PortfolioRoleFactory,
|
PortfolioRoleFactory,
|
||||||
UserFactory,
|
UserFactory,
|
||||||
PortfolioInvitationFactory,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,20 +1,28 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
from atst.domain.task_orders import TaskOrders
|
|
||||||
from atst.domain.permission_sets import PermissionSets
|
from atst.domain.permission_sets import PermissionSets
|
||||||
|
from atst.domain.task_orders import TaskOrders
|
||||||
from atst.models.attachment import Attachment
|
from atst.models.attachment import Attachment
|
||||||
from atst.routes.task_orders.new import ShowTaskOrderWorkflow, UpdateTaskOrderWorkflow
|
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
|
||||||
from tests.factories import (
|
from tests.factories import (
|
||||||
UserFactory,
|
|
||||||
TaskOrderFactory,
|
|
||||||
PortfolioFactory,
|
PortfolioFactory,
|
||||||
PortfolioRoleFactory,
|
PortfolioRoleFactory,
|
||||||
|
TaskOrderFactory,
|
||||||
|
UserFactory,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def task_order():
|
||||||
|
user = UserFactory.create()
|
||||||
|
portfolio = PortfolioFactory.create(owner=user)
|
||||||
|
attachment = Attachment(filename="sample_attachment", object_name="sample")
|
||||||
|
|
||||||
|
return TaskOrderFactory.create(creator=user, portfolio=portfolio)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def portfolio():
|
def portfolio():
|
||||||
return PortfolioFactory.create()
|
return PortfolioFactory.create()
|
||||||
@ -25,143 +33,20 @@ def user():
|
|||||||
return UserFactory.create()
|
return UserFactory.create()
|
||||||
|
|
||||||
|
|
||||||
class TestShowTaskOrderWorkflow:
|
def test_task_orders_new():
|
||||||
def test_portfolio_when_task_order_exists(self):
|
pass
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
task_order = TaskOrderFactory(portfolio=portfolio)
|
|
||||||
assert portfolio.num_task_orders > 0
|
|
||||||
|
|
||||||
workflow = ShowTaskOrderWorkflow(
|
|
||||||
user=task_order.creator, task_order_id=task_order.id
|
|
||||||
)
|
|
||||||
assert portfolio == workflow.portfolio
|
|
||||||
|
|
||||||
def test_portfolio_with_portfolio_id(self):
|
|
||||||
user = UserFactory.create()
|
|
||||||
portfolio = PortfolioFactory.create(owner=user)
|
|
||||||
workflow = ShowTaskOrderWorkflow(
|
|
||||||
user=portfolio.owner, portfolio_id=portfolio.id
|
|
||||||
)
|
|
||||||
assert portfolio == workflow.portfolio
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_task_order(client, user_session):
|
def test_task_orders_create():
|
||||||
creator = UserFactory.create()
|
pass
|
||||||
user_session()
|
|
||||||
response = client.get(url_for("task_orders.new", screen=1))
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
def post_to_task_order_step(client, data, screen, task_order_id=None):
|
def test_task_orders_edit():
|
||||||
return client.post(
|
pass
|
||||||
url_for("task_orders.update", screen=screen, task_order_id=task_order_id),
|
|
||||||
data=data,
|
|
||||||
follow_redirects=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def slice_data_for_section(task_order_data, section):
|
def test_task_orders_update():
|
||||||
attrs = TaskOrders.SECTIONS[section]
|
pass
|
||||||
return {k: v for k, v in task_order_data.items() if k in attrs}
|
|
||||||
|
|
||||||
|
|
||||||
def serialize_dates(data):
|
|
||||||
if not data:
|
|
||||||
return data
|
|
||||||
|
|
||||||
dates = {
|
|
||||||
k: v.strftime("%m/%d/%Y") for k, v in data.items() if hasattr(v, "strftime")
|
|
||||||
}
|
|
||||||
|
|
||||||
data.update(dates)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_to_can_edit_pf_attributes_screen_1():
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
workflow = ShowTaskOrderWorkflow(user=portfolio.owner)
|
|
||||||
assert not workflow.pf_attributes_read_only
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_pf_can_edit_pf_attributes_on_back_navigation():
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
pf_task_order = TaskOrderFactory(portfolio=portfolio)
|
|
||||||
pf_workflow = ShowTaskOrderWorkflow(
|
|
||||||
user=pf_task_order.creator, task_order_id=pf_task_order.id
|
|
||||||
)
|
|
||||||
assert not pf_workflow.pf_attributes_read_only
|
|
||||||
|
|
||||||
|
|
||||||
def test_to_on_pf_cannot_edit_pf_attributes():
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
pf_task_order = TaskOrderFactory(portfolio=portfolio)
|
|
||||||
|
|
||||||
workflow = ShowTaskOrderWorkflow(user=portfolio.owner, portfolio_id=portfolio.id)
|
|
||||||
assert portfolio.num_task_orders == 1
|
|
||||||
assert workflow.pf_attributes_read_only
|
|
||||||
|
|
||||||
second_task_order = TaskOrderFactory(portfolio=portfolio)
|
|
||||||
second_workflow = ShowTaskOrderWorkflow(
|
|
||||||
user=portfolio.owner, task_order_id=second_task_order.id
|
|
||||||
)
|
|
||||||
assert portfolio.num_task_orders > 1
|
|
||||||
assert second_workflow.pf_attributes_read_only
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Reimplement after TO form is updated")
|
|
||||||
def test_create_new_task_order(client, user_session, pdf_upload):
|
|
||||||
creator = UserFactory.create()
|
|
||||||
user_session(creator)
|
|
||||||
|
|
||||||
task_order_data = TaskOrderFactory.dictionary()
|
|
||||||
app_info_data = slice_data_for_section(task_order_data, "app_info")
|
|
||||||
|
|
||||||
response = client.post(
|
|
||||||
url_for("task_orders.update", screen=1),
|
|
||||||
data=app_info_data,
|
|
||||||
follow_redirects=False,
|
|
||||||
)
|
|
||||||
assert url_for("task_orders.new", screen=2) in response.headers["Location"]
|
|
||||||
|
|
||||||
created_task_order_id = response.headers["Location"].split("/")[-1]
|
|
||||||
created_task_order = TaskOrders.get(created_task_order_id)
|
|
||||||
assert created_task_order.portfolio is not None
|
|
||||||
|
|
||||||
funding_data = slice_data_for_section(task_order_data, "funding")
|
|
||||||
funding_data = serialize_dates(funding_data)
|
|
||||||
response = client.post(
|
|
||||||
response.headers["Location"], data=funding_data, follow_redirects=False
|
|
||||||
)
|
|
||||||
assert url_for("task_orders.new", screen=3) in response.headers["Location"]
|
|
||||||
|
|
||||||
oversight_data = slice_data_for_section(task_order_data, "oversight")
|
|
||||||
response = client.post(
|
|
||||||
response.headers["Location"], data=oversight_data, follow_redirects=False
|
|
||||||
)
|
|
||||||
assert url_for("task_orders.new", screen=4) in response.headers["Location"]
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_new_task_order_for_portfolio(client, user_session):
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
creator = portfolio.owner
|
|
||||||
user_session(creator)
|
|
||||||
|
|
||||||
task_order_data = TaskOrderFactory.dictionary()
|
|
||||||
app_info_data = slice_data_for_section(task_order_data, "app_info")
|
|
||||||
app_info_data["portfolio_name"] = portfolio.name
|
|
||||||
|
|
||||||
response = client.post(
|
|
||||||
url_for("task_orders.update", screen=1, portfolio_id=portfolio.id),
|
|
||||||
data=app_info_data,
|
|
||||||
follow_redirects=False,
|
|
||||||
)
|
|
||||||
assert url_for("task_orders.new", screen=2) in response.headers["Location"]
|
|
||||||
|
|
||||||
created_task_order_id = response.headers["Location"].split("/")[-1]
|
|
||||||
created_task_order = TaskOrders.get(created_task_order_id)
|
|
||||||
assert created_task_order.portfolio_name == portfolio.name
|
|
||||||
assert created_task_order.portfolio == portfolio
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Update after implementing new TO form")
|
@pytest.mark.skip(reason="Update after implementing new TO form")
|
||||||
@ -186,87 +71,15 @@ def test_task_order_form_shows_errors(client, user_session, task_order):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Update after implementing new TO form")
|
@pytest.mark.skip(reason="Update after implementing new TO form")
|
||||||
def test_review_screen_when_all_sections_complete(client, user_session, task_order):
|
def test_task_order_review_when_complete(client, user_session, task_order):
|
||||||
user_session(task_order.creator)
|
pass
|
||||||
response = client.get(
|
|
||||||
url_for("task_orders.new", screen=4, task_order_id=task_order.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
body = response.data.decode()
|
|
||||||
assert translate("task_orders.form.draft_alert_title") not in body
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Update after implementing new TO form")
|
@pytest.mark.skip(reason="Update after implementing new TO form")
|
||||||
def test_review_screen_when_not_all_sections_complete(client, user_session, task_order):
|
def test_task_order_review_when_not_complete(client, user_session, task_order):
|
||||||
TaskOrders.update(task_order, clin_01=None)
|
pass
|
||||||
user_session(task_order.creator)
|
|
||||||
response = client.get(
|
|
||||||
url_for("task_orders.new", screen=4, task_order_id=task_order.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
body = response.data.decode()
|
|
||||||
assert translate("task_orders.form.draft_alert_title") in body
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def task_order():
|
|
||||||
user = UserFactory.create()
|
|
||||||
portfolio = PortfolioFactory.create(owner=user)
|
|
||||||
attachment = Attachment(filename="sample_attachment", object_name="sample")
|
|
||||||
|
|
||||||
return TaskOrderFactory.create(creator=user, portfolio=portfolio)
|
|
||||||
|
|
||||||
|
|
||||||
def test_show_task_order(task_order):
|
|
||||||
workflow = ShowTaskOrderWorkflow(task_order.creator)
|
|
||||||
assert workflow.task_order is None
|
|
||||||
another_workflow = ShowTaskOrderWorkflow(
|
|
||||||
task_order.creator, task_order_id=task_order.id
|
|
||||||
)
|
|
||||||
assert another_workflow.task_order == task_order
|
|
||||||
|
|
||||||
|
|
||||||
def test_show_task_order_display_screen(task_order):
|
|
||||||
workflow = ShowTaskOrderWorkflow(task_order.creator, task_order_id=task_order.id)
|
|
||||||
screens = workflow.display_screens
|
|
||||||
# every form section is complete
|
|
||||||
for i in range(2):
|
|
||||||
assert screens[i]["completion"] == "complete"
|
|
||||||
# the review section is not
|
|
||||||
assert screens[3]["completion"] == "incomplete"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Update after implementing new TO form")
|
@pytest.mark.skip(reason="Update after implementing new TO form")
|
||||||
def test_update_task_order_with_existing_task_order(task_order):
|
def test_task_order_review_and_sign(client, user_session, task_order):
|
||||||
to_data = serialize_dates(TaskOrderFactory.dictionary())
|
pass
|
||||||
workflow = UpdateTaskOrderWorkflow(
|
|
||||||
task_order.creator, to_data, screen=2, task_order_id=task_order.id
|
|
||||||
)
|
|
||||||
assert workflow.task_order.start_date != to_data["start_date"]
|
|
||||||
workflow.update()
|
|
||||||
assert workflow.task_order.start_date.strftime("%m/%d/%Y") == to_data["start_date"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Update after implementing new TO form")
|
|
||||||
def test_review_task_order_form(client, user_session, task_order):
|
|
||||||
user_session(task_order.creator)
|
|
||||||
|
|
||||||
for idx, section in enumerate(TaskOrders.SECTIONS):
|
|
||||||
response = client.get(
|
|
||||||
url_for("task_orders.new", screen=idx + 1, task_order_id=task_order.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="Reimplement after TO form is updated")
|
|
||||||
def test_mo_redirected_to_build_page(client, user_session, portfolio):
|
|
||||||
user_session(portfolio.owner)
|
|
||||||
task_order = TaskOrderFactory.create(portfolio=portfolio)
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
url_for("task_orders.new", screen=1, task_order_id=task_order.id)
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
@ -3,11 +3,11 @@ from urllib.parse import quote
|
|||||||
|
|
||||||
from tests.factories import UserFactory
|
from tests.factories import UserFactory
|
||||||
|
|
||||||
|
# TODO: update w/ new home url
|
||||||
PROTECTED_URL = "/task_orders/new/get_started"
|
PROTECTED_URL = "/home"
|
||||||
|
|
||||||
|
|
||||||
def test_request_page_with_complete_profile(client, user_session):
|
def test_home_page_with_complete_profile(client, user_session):
|
||||||
user = UserFactory.create()
|
user = UserFactory.create()
|
||||||
user_session(user)
|
user_session(user)
|
||||||
response = client.get(PROTECTED_URL, follow_redirects=False)
|
response = client.get(PROTECTED_URL, follow_redirects=False)
|
||||||
|
@ -13,21 +13,20 @@ from atst.models import CSPRole, PortfolioRoleStatus, ApplicationRoleStatus
|
|||||||
from tests.factories import *
|
from tests.factories import *
|
||||||
|
|
||||||
_NO_ACCESS_CHECK_REQUIRED = _NO_LOGIN_REQUIRED + [
|
_NO_ACCESS_CHECK_REQUIRED = _NO_LOGIN_REQUIRED + [
|
||||||
"task_orders.get_started", # all users can start a new TO
|
|
||||||
"atst.csp_environment_access", # internal redirect
|
|
||||||
"atst.jedi_csp_calculator", # internal redirect
|
|
||||||
"atst.styleguide", # dev reference
|
|
||||||
"dev.test_email", # dev tool
|
|
||||||
"dev.messages", # dev tool
|
|
||||||
"atst.home", # available to all users
|
|
||||||
"users.user", # available to all users
|
|
||||||
"users.update_user", # available to all users
|
|
||||||
"portfolios.accept_invitation", # available to all users; access control is built into invitation logic
|
|
||||||
"applications.accept_invitation", # available to all users; access control is built into invitation logic
|
"applications.accept_invitation", # available to all users; access control is built into invitation logic
|
||||||
"atst.catch_all", # available to all users
|
"atst.catch_all", # available to all users
|
||||||
"portfolios.portfolios", # the portfolios list is scoped to the user separately
|
"atst.csp_environment_access", # internal redirect
|
||||||
|
"atst.home", # available to all users
|
||||||
|
"atst.jedi_csp_calculator", # internal redirect
|
||||||
|
"atst.styleguide", # dev reference
|
||||||
|
"dev.messages", # dev tool
|
||||||
|
"dev.test_email", # dev tool
|
||||||
|
"portfolios.accept_invitation", # available to all users; access control is built into invitation logic
|
||||||
|
"portfolios.create_portfolio", # create a portfolio"portfolios.portfolios", # the portfolios list is scoped to the user separately
|
||||||
"portfolios.new_portfolio", # all users can create a portfolio
|
"portfolios.new_portfolio", # all users can create a portfolio
|
||||||
"portfolios.create_portfolio", # create a portfolio
|
"task_orders.get_started", # all users can start a new TO
|
||||||
|
"users.update_user", # available to all users
|
||||||
|
"users.user", # available to all users
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -479,25 +478,14 @@ def test_task_orders_download_task_order_pdf_access(get_url_assert_status, monke
|
|||||||
|
|
||||||
|
|
||||||
# task_orders.new
|
# task_orders.new
|
||||||
|
@pytest.mark.skip(reason="Update after new TO form implemented")
|
||||||
def test_task_orders_new_access(get_url_assert_status):
|
def test_task_orders_new_access(get_url_assert_status):
|
||||||
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_FUNDING)
|
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_FUNDING)
|
||||||
owner = user_with()
|
owner = user_with()
|
||||||
rando = user_with()
|
rando = user_with()
|
||||||
|
|
||||||
url = url_for("task_orders.new", screen=1)
|
|
||||||
get_url_assert_status(owner, url, 200)
|
|
||||||
get_url_assert_status(ccpo, url, 200)
|
|
||||||
get_url_assert_status(rando, url, 200)
|
|
||||||
|
|
||||||
portfolio = PortfolioFactory.create(owner=owner)
|
portfolio = PortfolioFactory.create(owner=owner)
|
||||||
task_order = TaskOrderFactory.create(portfolio=portfolio)
|
|
||||||
|
|
||||||
url = url_for("task_orders.new", screen=2, task_order_id=task_order.id)
|
url = url_for("task_orders.new", portfolio_id=portfolio.id)
|
||||||
get_url_assert_status(owner, url, 200)
|
|
||||||
get_url_assert_status(ccpo, url, 200)
|
|
||||||
get_url_assert_status(rando, url, 404)
|
|
||||||
|
|
||||||
url = url_for("task_orders.new", screen=1, portfolio_id=portfolio.id)
|
|
||||||
get_url_assert_status(owner, url, 200)
|
get_url_assert_status(owner, url, 200)
|
||||||
get_url_assert_status(ccpo, url, 200)
|
get_url_assert_status(ccpo, url, 200)
|
||||||
get_url_assert_status(rando, url, 404)
|
get_url_assert_status(rando, url, 404)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user