Skip to content

Commit af68c56

Browse files
committed
MPT-8098 Add draft validation for Purchase Order
1 parent 8cc1ac1 commit af68c56

File tree

13 files changed

+186
-27
lines changed

13 files changed

+186
-27
lines changed

ffc/flows/fulfillment.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
CreateEmployee,
1616
CreateOrganization,
1717
CreateSubscription,
18+
QueryIfInvalid,
1819
ResetDueDate,
20+
ResetOrderErrors,
1921
SetupAgreementExternalId,
2022
SetupDueDate,
2123
)
@@ -25,9 +27,11 @@
2527

2628

2729
purchase = Pipeline(
30+
ResetOrderErrors(),
2831
SetupDueDate(),
2932
CheckDueDate(),
3033
CheckOrderParameters(),
34+
QueryIfInvalid(),
3135
CreateEmployee(),
3236
CreateOrganization(),
3337
SetupAgreementExternalId(),

ffc/flows/order.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def is_purchase_order(order):
2626
@dataclass
2727
class OrderContext(BaseContext):
2828
order: dict
29+
validation_succeeded: bool = True
2930
employee: dict = field(init=False, default=None)
3031
organization: dict = field(init=False, default=None)
3132

ffc/flows/steps/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from ffc.flows.steps.order import (
44
CheckOrderParameters,
55
CompleteOrder,
6+
QueryIfInvalid,
7+
ResetOrderErrors,
68
SetupAgreementExternalId,
79
)
810
from ffc.flows.steps.subscription import CreateSubscription
@@ -11,7 +13,9 @@
1113
"CompleteOrder",
1214
"CreateSubscription",
1315
"CheckDueDate",
16+
"QueryIfInvalid",
1417
"ResetDueDate",
18+
"ResetOrderErrors",
1519
"SetupDueDate",
1620
"CreateEmployee",
1721
"CreateOrganization",

ffc/flows/steps/order.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
ERR_ORGANIZATION_NAME,
1515
)
1616
from ffc.flows.order import MPT_ORDER_STATUS_COMPLETED, MPT_ORDER_STATUS_QUERYING
17+
from ffc.flows.steps.utils import reset_order_error
1718
from ffc.notifications import send_email_notification
1819
from ffc.parameters import (
1920
PARAM_ADMIN_CONTACT,
2021
PARAM_CURRENCY,
2122
PARAM_ORGANIZATION_NAME,
2223
get_ordering_parameter,
24+
reset_ordering_parameters_error,
2325
set_ordering_parameter_error,
2426
)
2527

@@ -73,8 +75,8 @@ def __call__(self, client, context, next_step):
7375

7476
class CheckOrderParameters(Step):
7577
"""
76-
Check if all required parameters are submitted, if not
77-
query order
78+
Check if all required parameters are submitted
79+
If not sets `validation_succeeded` to False
7880
"""
7981

8082
def __call__(self, client, context, next_step):
@@ -84,7 +86,6 @@ def __call__(self, client, context, next_step):
8486
PARAM_ADMIN_CONTACT: ERR_ADMIN_CONTACT,
8587
}
8688
order = context.order
87-
empty_parameters = []
8889

8990
for param_name in [
9091
PARAM_ORGANIZATION_NAME,
@@ -96,9 +97,20 @@ def __call__(self, client, context, next_step):
9697
order = set_ordering_parameter_error(
9798
order, param_name, errors[param_name]
9899
)
99-
empty_parameters.append(param_name)
100+
context.validation_succeeded = False
100101

101-
if empty_parameters:
102+
next_step(client, context)
103+
104+
105+
class QueryIfInvalid(Step):
106+
"""
107+
Check if `validation_succeeded` context parameter is True
108+
If not - query order
109+
"""
110+
111+
def __call__(self, client, context, next_step):
112+
order = context.order
113+
if not context.validation_succeeded:
102114
template = get_product_template_or_default(
103115
client,
104116
context.product_id,
@@ -109,9 +121,22 @@ def __call__(self, client, context, next_step):
109121
order["agreement"] = agreement
110122
context.order = order
111123
logger.info(
112-
f"{context}: parameters {', '.join(empty_parameters)} are empty, move to querying",
124+
f"{context}: ordering parameters are invalid, move to querying",
113125
)
114126
send_email_notification(client, order)
115127
return
116128

117129
next_step(client, context)
130+
131+
132+
class ResetOrderErrors(Step):
133+
"""
134+
Reset order errors and parameter errors. Is used before processing
135+
to not to show errors during procesing or after validation is succeseed
136+
"""
137+
138+
def __call__(self, client, context, next_step):
139+
context.order = reset_order_error(context.order)
140+
context.order = reset_ordering_parameters_error(context.order)
141+
142+
next_step(client, context)

ffc/flows/steps/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from copy import deepcopy
2+
13
from swo.mpt.client.mpt import fail_order
24

35
from ffc.notifications import send_email_notification
@@ -24,3 +26,9 @@ def switch_order_to_failed(client, order, status_notes):
2426
order["agreement"] = agreement
2527
send_email_notification(client, order)
2628
return order
29+
30+
31+
def reset_order_error(order):
32+
updated_order = deepcopy(order)
33+
updated_order["error"] = None
34+
return updated_order
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import logging
22
import traceback
33

4+
from swo.mpt.extensions.flows.pipeline import Pipeline
5+
46
from ffc.flows.error import strip_trace_id
5-
from ffc.flows.order import is_purchase_order
7+
from ffc.flows.order import OrderContext, is_purchase_order
8+
from ffc.flows.steps.order import CheckOrderParameters
69
from ffc.notifications import notify_unhandled_exception_in_teams
710

811
logger = logging.getLogger(__name__)
@@ -23,7 +26,7 @@ def validate_order(client, order):
2326
has_errors = False
2427

2528
if is_purchase_order(order):
26-
pass
29+
has_errors, order = validate_purchase_order(client, order)
2730

2831
logger.info(
2932
f"Validation of order {order['id']} succeeded "
@@ -37,3 +40,12 @@ def validate_order(client, order):
3740
strip_trace_id(traceback.format_exc()),
3841
)
3942
raise
43+
44+
45+
def validate_purchase_order(client, order):
46+
pipeline = Pipeline(
47+
CheckOrderParameters(),
48+
)
49+
context = OrderContext(order=order)
50+
pipeline.run(client, context)
51+
return not context.validation_succeeded, context.order

ffc/flows/validation/__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

ffc/parameters.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,21 @@ def set_due_date(order, due_date):
9393
param["value"] = due_date
9494

9595
return updated_order
96+
97+
98+
def reset_ordering_parameters_error(order):
99+
"""
100+
Reset errors for all ordering parameters
101+
102+
Args:
103+
order (dict): The order that contains the parameter.
104+
105+
Returns:
106+
dict: The order updated.
107+
"""
108+
updated_order = copy.deepcopy(order)
109+
110+
for param in updated_order["parameters"][PARAM_PHASE_ORDERING]:
111+
param["error"] = None
112+
113+
return updated_order

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ filterwarnings = [
8787

8888
[tool.coverage.run]
8989
branch = true
90+
relative_files = true
9091

9192
[tool.ruff]
9293
extend-exclude = [".vscode", ".devcontainer", "swo"]

tests/conftest.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,17 @@ def querying_purchase_order(order_factory):
529529
return order_factory(status="Querying")
530530

531531

532+
@pytest.fixture()
533+
def draft_purchase_valid_order(order_factory):
534+
return order_factory(status="Draft")
535+
536+
537+
@pytest.fixture()
538+
def draft_purchase_invalid_order(draft_purchase_valid_order):
539+
draft_purchase_valid_order["parameters"]["ordering"][0]["value"] = None
540+
return draft_purchase_valid_order
541+
542+
532543
@pytest.fixture()
533544
def webhook(settings):
534545
return {

0 commit comments

Comments
 (0)