Skip to content

Commit c1537cb

Browse files
committed
feat(validation): use items in reseller transfer validation
1 parent aec640a commit c1537cb

8 files changed

Lines changed: 2456 additions & 1915 deletions

File tree

adobe_vipm/flows/constants.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,15 +229,6 @@ class Param(StrEnum):
229229
"the automatically included item can be processed"
230230
),
231231
)
232-
233-
ERR_ADOBE_RESSELLER_CHANGE_PRODUCT_NOT_CONFIGURED = ValidationError(
234-
"VIPM0036",
235-
(
236-
"The adobe reseller change product is not configured for this product and "
237-
"cannot be added to the order."
238-
),
239-
)
240-
241232
ERR_ADOBE_GOVERNMENT_VALIDATE_IS_LGA = ValidationError(
242233
"VIPM0038",
243234
(

adobe_vipm/flows/context.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Context:
3232
adobe_returnable_orders: dict = field(default_factory=dict)
3333
adobe_return_orders: dict = field(default_factory=dict)
3434
membership_id: str | None = None
35+
adobe_transfer: dict | None = None
3536

3637
def __str__(self):
3738
due_date = self.due_date.strftime("%Y-%m-%d") if self.due_date else "-"

adobe_vipm/flows/validation/transfer.py

Lines changed: 114 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import datetime as dt
22
import logging
3+
from typing import Any
34

4-
from mpt_extension_sdk.mpt_http.mpt import get_agreement, get_product_items_by_skus
5+
from mpt_extension_sdk.mpt_http.mpt import get_agreement, get_product_items_by_skus, update_order
56

67
from adobe_vipm.adobe.client import get_adobe_client
78
from adobe_vipm.adobe.constants import (
@@ -26,17 +27,20 @@
2627
ERR_ADOBE_MEMBERSHIP_NOT_FOUND,
2728
ERR_ADOBE_MEMBERSHIP_PROCESSING,
2829
ERR_ADOBE_RESSELLER_CHANGE_LINES,
29-
ERR_ADOBE_RESSELLER_CHANGE_PRODUCT_NOT_CONFIGURED,
3030
ERR_ADOBE_UNEXPECTED_ERROR,
3131
ERR_NO_SUBSCRIPTIONS_WITHOUT_DEPLOYMENT,
3232
ERR_UPDATING_TRANSFER_ITEMS,
33-
TRANSFER_RESELLER_ITEM_SKU,
3433
Param,
3534
)
3635
from adobe_vipm.flows.context import Context
37-
from adobe_vipm.flows.errors import GovernmentLGANotValidOrderError, GovernmentNotValidOrderError
36+
from adobe_vipm.flows.errors import (
37+
GovernmentLGANotValidOrderError,
38+
GovernmentNotValidOrderError,
39+
MPTError,
40+
)
3841
from adobe_vipm.flows.helpers import (
3942
FetchResellerChangeData,
43+
UpdatePrices,
4044
ValidateGovernmentTransfer,
4145
ValidateResellerChange,
4246
)
@@ -46,6 +50,7 @@
4650
exclude_items_with_deployment_id,
4751
exclude_subscriptions_with_deployment_id,
4852
get_adobe_membership_id,
53+
get_market_segment,
4954
get_order_line_by_sku,
5055
get_ordering_parameter,
5156
get_transfer_item_sku_by_subscription,
@@ -54,8 +59,11 @@
5459
is_transferring_item_expired,
5560
set_order_error,
5661
set_ordering_parameter_error,
62+
split_downsizes_upsizes_new,
5763
)
5864
from adobe_vipm.flows.utils.validation import validate_government_lga_data
65+
from adobe_vipm.flows.validation.shared import GetPreviewOrder
66+
from adobe_vipm.notifications import send_error
5967
from adobe_vipm.utils import get_3yc_commitment, get_partial_sku
6068

6169
logger = logging.getLogger(__name__)
@@ -344,13 +352,16 @@ def __call__(self, mpt_client, context, next_step):
344352
context.order["agreement"] = get_agreement(mpt_client, context.order["agreement"]["id"])
345353

346354
product_id = context.order["agreement"]["product"]["id"]
347-
authorization_id = context.order["authorization"]["id"]
355+
context.authorization_id = context.order["authorization"]["id"]
356+
context.order_id = context.order["id"]
348357
context.membership_id = get_adobe_membership_id(context.order)
358+
context.product_id = context.order["agreement"]["product"]["id"]
359+
context.market_segment = get_market_segment(context.product_id)
349360

350361
if is_migrate_customer(context.order):
351362
context.transfer = get_transfer_by_authorization_membership_or_customer(
352363
product_id,
353-
authorization_id,
364+
context.authorization_id,
354365
context.membership_id,
355366
)
356367
next_step(mpt_client, context)
@@ -521,6 +532,8 @@ def validate_reseller_change(mpt_client, order):
521532
FetchResellerChangeData(is_validation=True),
522533
ValidateResellerChange(is_validation=True),
523534
AddResellerChangeLinesToOrder(),
535+
GetPreviewOrder(),
536+
UpdatePrices(),
524537
)
525538
context = Context(order=order)
526539
pipeline.run(mpt_client, context)
@@ -532,36 +545,105 @@ class AddResellerChangeLinesToOrder(Step):
532545

533546
def __call__(self, mpt_client, context, next_step):
534547
"""Add lines from reseller change back to the MPT order."""
535-
reseller_change_item = get_product_items_by_skus(
536-
mpt_client, context.order["agreement"]["product"]["id"], [TRANSFER_RESELLER_ITEM_SKU]
537-
)
548+
order_lines_from_transfer = self._get_order_lines_from_transfer(context, mpt_client)
549+
550+
if order_lines_from_transfer:
551+
if not context.order["lines"]:
552+
logger.info("No existing order lines, proceeding with transfer lines")
553+
context.order = update_order(
554+
mpt_client, context.order_id, lines=order_lines_from_transfer
555+
)
556+
context.validation_succeeded = True
557+
elif not self._transfer_order_lines_match(
558+
context.order["lines"], order_lines_from_transfer
559+
):
560+
logger.warning("Order lines do not match transfer lines")
561+
context.order = set_order_error(
562+
context.order, ERR_ADOBE_RESSELLER_CHANGE_LINES.to_dict()
563+
)
564+
context.validation_succeeded = False
565+
return
566+
else:
567+
logger.info(
568+
"Order lines match transfer lines successfully (%d lines)",
569+
len(order_lines_from_transfer),
570+
)
571+
context.validation_succeeded = True
572+
elif context.order["lines"]:
573+
logger.info(
574+
"No transfer lines but order has %d existing lines, processing new lines",
575+
len(context.order["lines"]),
576+
)
577+
downsize_lines, upsize_lines, new_lines = split_downsizes_upsizes_new(context.order)
578+
context.downsize_lines = downsize_lines
579+
context.upsize_lines = upsize_lines
580+
context.new_lines = new_lines
538581

539-
if not reseller_change_item:
540-
context.order = set_order_error(
541-
context.order, ERR_ADOBE_RESSELLER_CHANGE_PRODUCT_NOT_CONFIGURED.to_dict()
582+
context.validation_succeeded = True
583+
else:
584+
logger.error("No transfer lines and no order lines, validation failed")
585+
context.order = update_order(
586+
mpt_client, context.order_id, lines=order_lines_from_transfer
542587
)
543588
context.validation_succeeded = False
544589
return
545590

546-
reseller_change_item = reseller_change_item[0]
547-
context.validation_succeeded = True
591+
logger.info(
592+
"Proceeding to next step with validation_succeeded=%s", context.validation_succeeded
593+
)
594+
next_step(mpt_client, context)
548595

549-
lines = context.order["lines"]
550-
if lines:
551-
if len(lines) == 1 and lines[0]["item"]["id"] == reseller_change_item["id"]:
552-
next_step(mpt_client, context)
553-
return
554-
context.order = set_order_error(
555-
context.order, ERR_ADOBE_RESSELLER_CHANGE_LINES.to_dict()
556-
)
557-
context.validation_succeeded = False
558-
new_line = [
559-
{
560-
"item": reseller_change_item,
561-
"quantity": 1,
562-
"oldQuantity": 0,
563-
"price": {"unitPP": 0},
564-
}
596+
def _get_order_lines_from_transfer(self, context: Context, mpt_client) -> list[Any]:
597+
no_deployment_transfer_items = [
598+
item for item in context.adobe_transfer["lineItems"] if not item.get("deploymentId", "")
565599
]
566-
context.order["lines"] = new_line
567-
next_step(mpt_client, context)
600+
if not no_deployment_transfer_items:
601+
logger.info("No transfer items without deployment ID")
602+
return []
603+
logger.info(
604+
"Processing %d transfer items without deployment ID", len(no_deployment_transfer_items)
605+
)
606+
reseller_items_map = {
607+
item["externalIds"]["vendor"]: item
608+
for item in get_product_items_by_skus(
609+
mpt_client,
610+
context.order["agreement"]["product"]["id"],
611+
[get_partial_sku(item["offerId"]) for item in no_deployment_transfer_items],
612+
)
613+
}
614+
logger.debug(
615+
"Created reseller items map with %d items: %s",
616+
len(reseller_items_map),
617+
list(reseller_items_map.keys()),
618+
)
619+
order_lines_from_transfer = []
620+
for item in no_deployment_transfer_items: # TODO:filter out expired?
621+
partial_sku = get_partial_sku(item["offerId"])
622+
mapped_item = reseller_items_map.get(partial_sku)
623+
if mapped_item is None:
624+
error_msg = f"No reseller item found for partial SKU '{partial_sku}'"
625+
logger.error(error_msg)
626+
send_error("Transfer Validation - Missing reseller item", error_msg)
627+
raise MPTError(error_msg)
628+
order_lines_from_transfer.append({
629+
"item": mapped_item,
630+
"oldQuantity": item["quantity"],
631+
"quantity": item["quantity"],
632+
})
633+
634+
logger.info("Created %d order lines from transfer items", len(order_lines_from_transfer))
635+
return order_lines_from_transfer
636+
637+
@staticmethod
638+
def _transfer_order_lines_match(order_lines: list[dict], transfer_lines: list[dict]) -> bool:
639+
"""Compare order lines based on relevant fields only."""
640+
if len(order_lines) != len(transfer_lines):
641+
return False
642+
643+
order_lines_to_comp = {
644+
(line["item"]["externalIds"]["vendor"], line["quantity"]) for line in order_lines
645+
}
646+
transfer_lines_to_comp = {
647+
(line["item"]["externalIds"]["vendor"], line["quantity"]) for line in transfer_lines
648+
}
649+
return order_lines_to_comp == transfer_lines_to_comp

0 commit comments

Comments
 (0)