Skip to content

Conversation

@jusdino
Copy link
Collaborator

@jusdino jusdino commented Nov 13, 2025

Description List

  • Updated batch query logic to start time window just after most recent collected batch settlement time
  • Moved collector timing back by one more hour
  • Updated related code to leverage newer CCDataClass and TestDataGenerator patterns

Testing List

  • Run transaction collection in sandbox
  • Code review

Closes #1192

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced transaction history processing with dynamic time window retrieval for improved accuracy.
    • Improved handling of settled and unsettled transaction reconciliation.
  • Bug Fixes

    • Declined transactions from payment processors are now properly skipped.
    • Better detection and handling of old unsettled transactions.
  • Chores

    • Adjusted transaction processing schedule to account for settlement delays.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 13, 2025

Walkthrough

Introduces a new TransactionData model to replace dict-based transaction handling, refactors TransactionClient with a new method to retrieve the most recent transaction, updates the transaction history handler to dynamically compute time windows based on prior settlements and support pagination, adds reconciliation logic, and shifts the processing schedule from 1 AM to 2 AM UTC to accommodate API reporting delays.

Changes

Cohort / File(s) Summary
Data Model Introduction
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py
Adds new TransactionData class wrapping marshmallow schema with read-only typed properties (transactionProcessor, transactionId, batch, lineItems, etc.) for transaction records.
TransactionClient Refactor
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py
Converts dict-based transaction handling to TransactionData objects. Updates store_transactions, add_privilege_information_to_transactions, and reconcile_unsettled_transactions signatures. Adds new get_most_recent_transaction_for_compact method querying current and prior 3 months.
Purchase & Payment Client Updates
backend/compact-connect/lambdas/python/purchases/purchase_client.py
Adds TransactionData usage in settled-transaction construction. Introduces AuthorizeNetTransactionIgnoreStates enum to skip declined transactions. Threads compact parameter through PaymentProcessorClient.get_settled_transactions, AuthorizeNetPaymentProcessorClient.get_settled_transactions, and PurchaseClient.get_settled_transactions.
Transaction History Handler
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
Adds live-status validation, dynamic time-window derivation via _get_time_window_strings, pagination support with lastProcessedTransactionId, reconciliation after each batch, handling of old unsettled transactions (48+ hours), and error propagation via batchFailureErrorMessage.
Schedule Update
backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py
Shifts DailyTransactionProcessingRule from 1 AM to 2 AM UTC to allow Authorize.net time to report settled batches.
Test Constants & Generator
backend/compact-connect/lambdas/python/common/common_test/test_constants.py, backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py
Adds DEFAULT_COMPACT_TRANSACTION_* constants and new TestDataGenerator.generate_default_transaction() method for centralized test data creation.
Test Infrastructure
backend/compact-connect/lambdas/python/common/tests/__init__.py
Exposes TestDataGenerator on test class setup.
TransactionClient Tests
backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
Adds comprehensive tests for get_most_recent_transaction_for_compact with month-spanning queries, error handling, and extensive mocking. Refactors existing tests to use TestDataGenerator and updates _generate_mock_transaction signature.
Transaction Schema Unit Tests
backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_transaction.py
Adds unit tests for TransactionRecordSchema and TransactionData serialization/deserialization, validation, and property access.
TransactionClient Unit Tests
backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py
Refactors to use generate_default_transaction and attribute-based access instead of dict indexing. Updates assertions for transaction properties.
PurchaseClient Unit Tests
backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py
Converts dictionary-style transaction access to attribute-based. Adds test for declined-transaction filtering via AuthorizeNetTransactionIgnoreStates.
Transaction History Handler Tests
backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
Refactors test data generation to use TestDataGenerator. Adds _add_previous_transaction_to_history helper. Expands tests for time-window derivation, pagination, idempotency, and old unsettled-transaction handling. Introduces time-mocking patches for stability.
Schema Import Consolidation
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/purchase/api.py
Reorganizes marshmallow imports to top-level; no behavioral change.

Sequence Diagram(s)

sequenceDiagram
    participant Handler as transaction_history Handler
    participant CompactClient as CompactClient
    participant TransactionClient as TransactionClient
    participant PurchaseClient as PurchaseClient
    participant DynamoDB as DynamoDB

    Handler->>CompactClient: Check compact live status
    alt Compact not live
        Handler-->>Handler: Return COMPLETE (skip)
    else Compact is live
        Handler->>TransactionClient: get_most_recent_transaction_for_compact()
        TransactionClient->>DynamoDB: Query current + 3 prior months
        DynamoDB-->>TransactionClient: Most recent transaction (or error)
        TransactionClient-->>Handler: TransactionData
        
        rect rgb(200, 220, 255)
            note right of Handler: Derive time window from most recent transaction
        end
        
        Handler->>PurchaseClient: get_settled_transactions(compact, start_time, end_time, limit, ...)
        PurchaseClient-->>Handler: SettledTransactions (up to limit)
        
        alt Transactions found
            Handler->>TransactionClient: add_privilege_information_to_transactions()
            TransactionClient-->>Handler: EnrichedTransactions
            Handler->>TransactionClient: store_transactions()
            TransactionClient->>DynamoDB: Save transactions
            
            rect rgb(200, 255, 220)
                note right of Handler: Reconcile settled vs unsettled
            end
            
            Handler->>TransactionClient: reconcile_unsettled_transactions()
            TransactionClient-->>Handler: Unmatched settled IDs
            
            alt All transactions processed
                Handler-->>Handler: status = COMPLETE
            else More transactions exist
                Handler-->>Handler: status = IN_PROGRESS + pagination fields
            end
        end
        
        rect rgb(255, 220, 200)
            note right of Handler: Handle settlement errors & old unsettled
        end
        
        alt Settlement errors or old unsettled (48+ hrs)
            Handler-->>Handler: status = BATCH_FAILURE + batchFailureErrorMessage
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Critical areas for attention:
    • TransactionData data model class and schema integration; ensure property access matches underlying _data dict structure and marshmallow schema alignment
    • get_most_recent_transaction_for_compact month-spanning query logic; verify DynamoDB query boundaries and error handling across month boundaries
    • Dynamic time-window derivation in transaction_history.py (_get_time_window_strings); ensure edge cases (first run, no prior transactions) are handled correctly
    • Reconciliation logic (reconcile_unsettled_transactions); verify correctness of unsettled/settled transaction matching and age-based filtering (48-hour threshold)
    • Pagination propagation (lastProcessedTransactionId, currentBatchId, processedBatchIds); ensure state is correctly threaded through multiple API calls and stored for resumption
    • AuthorizeNetTransactionIgnoreStates enum application; confirm declined transactions are correctly filtered at all call sites
    • Test coverage for get_most_recent_transaction_for_compact with cross-month boundaries and empty result scenarios

Possibly related PRs

Suggested reviewers

  • jlkravitz
  • landonshumway-ia

Poem

🐰 Transactions hop through time with grace,
Month-spanning queries at a faster pace,
No more delays in Authorize's sight,
We reconcile at two AM at night!
Data models typed, the dicts now retire,
Settled batches rise, our systems inspire! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Bug/handle authorizenet delayed reporting' accurately reflects the main objective of handling Authorize.net API reporting delays, which is the core purpose of this PR.
Description check ✅ Passed The description includes key implementation details and testing instructions, but lacks the complete template structure with Requirements List and full Testing List sections from the template.
Linked Issues check ✅ Passed All main requirements from issue #1192 are addressed: dynamic time window based on most recent transaction [#1192], cron job moved to 2 AM UTC [#1192], and code leverages CCDataClass/TestDataGenerator patterns [#1192].
Out of Scope Changes check ✅ Passed All changes align with addressing Authorize.net delayed reporting: time window logic, cron scheduling, transaction data modeling, and comprehensive test coverage for the new functionality are all in-scope.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jusdino jusdino marked this pull request as ready for review November 13, 2025 21:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py (1)

219-220: Consider using consistent timezone terminology.

The comment mentions "4:00pm Pacific Time" but then refers to "6pm PST" and "2am UTC". For clarity, consider using "Pacific Time" consistently throughout, since the offset between Pacific Time and UTC varies between PST (UTC-8) and PDT (UTC-7) depending on daylight saving time. The cron schedule correctly uses 2am UTC, which corresponds to 6pm Pacific Time during winter and 7pm Pacific Time during summer.

Apply this diff for consistency:

-        # In practice, we've seen settlements running up to an hour late, so this daily collector runs two hours later
-        # (6pm PST, which is 2am UTC) to collect all settled transactions since the previous run.
+        # In practice, we've seen settlements running up to an hour late, so this daily collector runs two hours later
+        # (2am UTC) to collect all settled transactions since the previous run.
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1)

8-17: Update docstring to reflect the lineItems setter.

The docstring states "this one does not include setters," but a setter is defined for lineItems at lines 46-48. Please update the docstring to accurately reflect this exception.

Apply this diff:

     Class representing a Transaction with read-only properties.
 
-    Unlike several other CCDataClass subclasses, this one does not include setters. This is because
+    Unlike several other CCDataClass subclasses, this one includes only a single setter (lineItems). This is because
     transaction records are only created during transaction processing, so we can pass the entire record
     from the processing into the constructor.
backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (1)

207-243: Previous-transaction seeding mirrors real history behavior

_add_previous_transaction_to_history correctly:

  • defaults to a settlement time relative to MOCK_SCHEDULED_TIME,
  • calculates a plausible local time, and
  • stores the transaction through TransactionClient.store_transactions.

This gives the new “query from last settlement forward” path realistic state without test duplication. The hard‑coded EST offset is fine for test expectations since only the UTC timestamp actually drives the DynamoDB sort key.

If you later add tests for non‑EST compacts, consider parameterizing the local offset instead of inlining -5 here.

backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (1)

162-167: Privilege mapping on TransactionData looks consistent; consider guarding empty provider_ids

Updating _set_privilege_id_in_line_item to use line_item.get('itemId') avoids KeyErrors on malformed items, and the rest of add_privilege_information_to_transactions correctly:

  • derives jurisdictions from privilege line items,
  • queries the GSI with the compact/transactionId key,
  • sets licenseeId from the privilege providerId, and
  • writes privilege IDs back into transaction.lineItems.

One minor defensive improvement would be to handle the case where records_for_transaction_id is non-empty but contains no type in {'privilege','privilegeUpdate'}, which would currently cause provider_ids.pop() to raise. Logging and defaulting licenseeId to 'UNKNOWN' (similar to how privilegeId is handled) would make this path safer if the provider table ever contains unexpected record types.

Also applies to: 168-276

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8625c and bce9321.

📒 Files selected for processing (16)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/purchase/api.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (12 hunks)
  • backend/compact-connect/lambdas/python/common/common_test/test_constants.py (2 hunks)
  • backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (2 hunks)
  • backend/compact-connect/lambdas/python/common/tests/__init__.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (7 hunks)
  • backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_transaction.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py (8 hunks)
  • backend/compact-connect/lambdas/python/compact-configuration/tests/function/test_compact_configuration.py (1 hunks)
  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (1 hunks)
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py (9 hunks)
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (21 hunks)
  • backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (4 hunks)
  • backend/compact-connect/stacks/disaster_recovery_stack/__init__.py (1 hunks)
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py (1 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:05:36.255Z
Learning: In CompactConnect PR #848, the user landonshumway-ia decided to leave timezone handling code in _should_allow_reregistration function as-is after testing in sandbox environment confirmed it works correctly. The user's reasoning was that reregistration is an edge case, the code has been tested and verified, and AWS is unlikely to change behavior that would break many clients. This represents a pragmatic engineering decision based on testing and risk assessment.
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property (not a method), and can be properly patched in tests by directly supplying a datetime value rather than wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/compact-configuration/tests/function/test_compact_configuration.py
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property rather than a method, and should be patched in tests by directly providing a datetime value: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/compact-configuration/tests/function/test_compact_configuration.py
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-05-23T14:15:40.107Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 796
File: backend/compact-connect/lambdas/python/purchases/tests/function/__init__.py:27-31
Timestamp: 2025-05-23T14:15:40.107Z
Learning: The TestDataGenerator class in CompactConnect contains only static methods and should be used directly without instantiation (e.g., TestDataGenerator.method_name() rather than creating an instance first).

Applied to files:

  • backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py
  • backend/compact-connect/lambdas/python/common/tests/__init__.py
📚 Learning: 2025-09-03T22:29:24.535Z
Learnt from: ChiefStief
Repo: csg-org/CompactConnect PR: 1036
File: backend/compact-connect/lambdas/python/data-events/handlers/encumbrance_events.py:0-0
Timestamp: 2025-09-03T22:29:24.535Z
Learning: The EncumbranceEventDetailSchema in backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/data_event/api.py is used across multiple instances/contexts where adverseActionCategory and adverseActionId fields may be required in some cases and not present in others. This is an intentional design pattern for handling different event variants with a single schema.

Applied to files:

  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py
📚 Learning: 2025-07-08T18:40:24.408Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 882
File: backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py:287-359
Timestamp: 2025-07-08T18:40:24.408Z
Learning: In the CompactConnect codebase, landonshumway-ia prefers to avoid extraneous unit tests when existing test coverage is already sufficient to catch bugs. For the get_privilege_purchase_options method's live-jurisdiction filtering logic, the existing tests in the purchases test suite provide adequate coverage without needing additional edge case tests.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is implemented as a property that returns `datetime.now(tz=UTC).replace(microsecond=0)`. The correct way to patch it in tests is by directly providing the datetime value, not wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-08-21T02:51:28.199Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 1014
File: backend/compact-connect/lambdas/python/common/requirements.in:4-4
Timestamp: 2025-08-21T02:51:28.199Z
Learning: In CompactConnect, the purchases lambda contains requests as a transitive dependency from the Authorize.net SDK, which is automatically resolved by pip-compile. This should not be manually removed even when requests is also available in the common layer, as it's managed automatically by the dependency resolver.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
🧬 Code graph analysis (12)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (3)
backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py (1)
  • get_privilege_purchase_options (325-400)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (4)
  • get_most_recent_transaction_for_compact (81-120)
  • add_privilege_information_to_transactions (168-276)
  • store_transactions (18-32)
  • reconcile_unsettled_transactions (315-383)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
  • get_settled_transactions (223-244)
  • get_settled_transactions (734-913)
  • get_settled_transactions (1081-1114)
backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py (4)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (1)
  • store_transactions (18-32)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • update (182-203)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (3)
  • lineItems (39-44)
  • lineItems (47-48)
  • licenseeId (55-56)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (2)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1)
  • TransactionData (7-76)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (2)
  • update (182-203)
  • create_new (96-121)
backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_transaction.py (5)
backend/compact-connect/lambdas/python/common/tests/__init__.py (1)
  • TstLambdas (9-111)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (2)
  • TestDataGenerator (23-719)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • TransactionRecordSchema (34-73)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (2)
  • serialize_to_database_record (205-208)
  • from_database_record (124-139)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (13)
  • TransactionData (7-76)
  • transactionProcessor (26-27)
  • transactionId (30-31)
  • batch (34-36)
  • lineItems (39-44)
  • lineItems (47-48)
  • compact (51-52)
  • licenseeId (55-56)
  • responseCode (59-60)
  • settleAmount (63-64)
  • submitTimeUTC (67-68)
  • transactionStatus (71-72)
  • transactionType (75-76)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (2)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • CCDataClass (46-208)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • TransactionRecordSchema (34-73)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (8)
  • TransactionData (7-76)
  • batch (34-36)
  • transactionProcessor (26-27)
  • compact (51-52)
  • lineItems (39-44)
  • lineItems (47-48)
  • transactionId (30-31)
  • licenseeId (55-56)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • UnsettledTransactionRecordSchema (77-102)
backend/compact-connect/lambdas/python/common/cc_common/config.py (2)
  • transaction_history_table (274-275)
  • current_standard_datetime (253-257)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (3)
  • serialize_to_database_record (205-208)
  • from_database_record (124-139)
  • update (182-203)
backend/compact-connect/stacks/disaster_recovery_stack/__init__.py (1)
backend/compact-connect/lambdas/python/common/cc_common/config.py (1)
  • ssn_table (92-93)
backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (2)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (8)
  • TestDataGenerator (23-719)
  • generate_default_transaction (653-682)
  • put_default_jurisdiction_configuration_in_configuration_table (634-650)
  • put_default_compact_configuration_in_configuration_table (588-604)
  • put_default_privilege_record_in_provider_table (342-352)
  • generate_default_privilege (311-333)
  • generate_default_privilege_update (361-388)
  • store_record_in_provider_table (336-339)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (2)
  • TransactionClient (12-383)
  • store_transactions (18-32)
backend/compact-connect/lambdas/python/common/tests/__init__.py (2)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)
  • TestDataGenerator (23-719)
backend/compact-connect/lambdas/python/common/cc_common/config.py (1)
  • _Config (21-321)
backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (3)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • serialize_to_database_record (205-208)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (2)
  • store_transactions (18-32)
  • get_most_recent_transaction_for_compact (81-120)
backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (2)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (7)
  • transactionId (30-31)
  • compact (51-52)
  • licenseeId (55-56)
  • batch (34-36)
  • lineItems (39-44)
  • lineItems (47-48)
  • transactionStatus (71-72)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
  • get_settled_transactions (223-244)
  • get_settled_transactions (734-913)
  • get_settled_transactions (1081-1114)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (8)
  • TransactionData (7-76)
  • compact (51-52)
  • transactionStatus (71-72)
  • submitTimeUTC (67-68)
  • transactionType (75-76)
  • responseCode (59-60)
  • settleAmount (63-64)
  • batch (34-36)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • create_new (96-121)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/compact/common.py (1)
  • PaymentProcessorType (13-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: TestApp
🔇 Additional comments (18)
backend/compact-connect/stacks/disaster_recovery_stack/__init__.py (1)

76-78: Formatting consistency improvement, but disconnected from PR objectives.

The multi-line reformatting aligns with the existing code style used elsewhere in the file (e.g., lines 71–73, 87–93) and improves readability. However, this formatting change appears unrelated to the stated PR objectives around handling Authorize.net delayed batch reporting. Confirm this is intentional as part of a broader code-quality pass and not an accidental inclusion.

backend/compact-connect/lambdas/python/compact-configuration/tests/function/test_compact_configuration.py (1)

213-221: LGTM! Syntactic correction to test data structure.

The closing bracket properly terminates the configuredStates list for the octp compact configuration, fixing the dictionary structure. This aligns with the formatting of similar test data throughout the file.

backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (1)

1299-1376: LGTM! Good test coverage for declined transaction handling.

The new test validates that declined transactions are correctly filtered out from the results, ensuring only successfully settled transactions are returned. The test setup is clear and comprehensive.

backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (3)

44-67: Compact live/jurisdiction gate looks solid

Using get_privilege_purchase_options plus the compact/jurisdiction type filtering to short‑circuit when either the compact config or live jurisdictions are missing is a clean way to avoid unnecessary Authorize.net calls and keeps the step function behavior explicit (status: COMPLETE and preserved scheduledTime).


114-199: Settlement error aggregation and unsettled reconciliation are well structured

The flow that:

  • enriches transactions with privilege IDs, stores them via TransactionClient.store_transactions,
  • accumulates settlementErrorTransactionIds across iterations into batchFailureErrorMessage,
  • flips status to BATCH_FAILURE only after all transactions are processed, and
  • reconciles unsettled transactions every iteration, flagging >48‑hour ones when done,

is coherent and matches the intended alerting behavior. The pagination/status handling around _all_transactions_processed is also consistent with the new PurchaseClient.get_settled_transactions contract.


69-94: The review comment is incorrect; no refactoring is needed

The original concern about timezone handling is unfounded. In Python 3.13, datetime.fromisoformat() accepts trailing "Z" timestamps and returns timezone-aware UTC datetimes. Critically, the .replace() method on a timezone-aware datetime preserves tzinfo rather than removing it—all datetime objects in this code remain timezone-aware throughout the operations. The max() comparison between most_recent_settlement and oldest_allowed_start operates on compatible tz-aware datetimes without error.

The code is robust as written; no normalization or explicit UTC coercion is necessary.

backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (3)

34-74: Test transaction generation via TestDataGenerator is a good fit

Switching _generate_mock_transaction to build line items in a loop and delegate the actual transaction construction to TestDataGenerator.generate_default_transaction keeps the tests aligned with the production schema and avoids hand‑rolled dicts. It will make future schema changes less brittle for these tests.


81-107: Compact/jurisdiction config setup helper is clear and reusable

_add_compact_configuration_data now populates jurisdiction and compact configuration rows via the shared generator, using configuredStates derived from the provided jurisdictions. This mirrors how the handler consumes get_privilege_purchase_options and gives a single, readable setup path for all tests that need a “live compact”.


876-932: New start_time tests precisely exercise the updated window logic

The two tests:

  • test_process_settled_transactions_uses_previous_transaction_settlement_time_for_start_time, and
  • test_process_settled_transactions_uses_30_day_fallback_when_no_previous_transactions

nicely pin down the intended behavior of start_time both when a prior settlement exists and when get_most_recent_transaction_for_compact raises. Patching _Config.current_standard_datetime directly matches the existing pattern in this repo and keeps the queries deterministic relative to the seeded data.

backend/compact-connect/lambdas/python/purchases/purchase_client.py (2)

734-755: TransactionData-based get_settled_transactions wiring looks correct

The refactor to:

  • build TransactionData via TransactionData.create_new(...) with compact and transactionProcessor set, and
  • have PurchaseClient.get_settled_transactions delegate directly to the processor client while threading compact,

lines up with the new TransactionClient.store_transactions / add_privilege_information_to_transactions expectations. This should make the storage and enrichment layers more robust than the previous dict-based flow, while preserving the same response shape (transactions list plus pagination and settlement error IDs).

Also applies to: 858-881, 1081-1114


43-45: Status string is correct; consider whether other non-settlement statuses should be ignored

Verified: Authorize.net's API returns "declined" (lowercase) for declined transactions, so the enum value is correct.

However, other non-settlement / non-final statuses exist (e.g., authorizedPendingCapture, capturedPendingSettlement, refundPendingSettlement, approvedReview, FDSPendingReview, FDSAuthorizedPendingReview, communicationError, couldNotVoid, expired, voided). Confirm whether ignoring only declined is intentional, or if other non-final statuses should also be added to AuthorizeNetTransactionIgnoreStates to prevent them from surfacing as settlement errors.

backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (3)

18-32: store_transactions now matches the TransactionData model and schema

Switching store_transactions to accept list[TransactionData] and delegating serialization to serialize_to_database_record() is a clean separation of concerns and ensures pk/sk and other derived fields stay in sync with the schema. The explicit processor-type check and ValueError on unsupported types are appropriate.


81-120: get_most_recent_transaction_for_compact implements the desired month-scoped lookup

The new get_most_recent_transaction_for_compact:

  • uses current_standard_datetime to pick the current month partition,
  • walks back up to three months, and
  • queries each month with ScanIndexForward=False and Limit=1 to fetch only the most recent record,

which is exactly what the handler needs to anchor its start_time. The behavior when no records are found (raising ValueError) is clear, and the tests in test_transaction_client.py cover current-month, previous-month, and no‑data cases.


278-314: Unsettled transaction storage and reconciliation integrate cleanly with the handler

store_unsettled_transaction and reconcile_unsettled_transactions now:

  • use UnsettledTransactionRecordSchema to generate pk/sk for unsettled records,
  • swallow validation/write errors for this monitoring-only path, and
  • reconcile by matching settled TransactionData.transactionIds, deleting matches, and flagging >48‑hour unmatched transactions.

This matches the new handler behavior around unsettled transaction alerts and is well covered by the tests in both the purchases and common test suites.

Also applies to: 315-383

backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (4)

11-24: Local TransactionData helper is consistent with the shared generator

The _generate_mock_transaction helper wraps test_data_generator.generate_default_transaction and then overrides batch fields as needed, which keeps these tests in sync with the data model while allowing explicit control over transactionId, compact, settlementTimeUTC, and batchId. This is a good way to avoid duplicating schema details in the tests.


41-52: Edge-time test now validates both the stored shape and range behavior

Rewriting test_transaction_history_edge_times to:

  • create edge transactions via generate_default_transaction with overridden batch data,
  • reuse serialize_to_database_record() for the expected record, and
  • strip dateOfUpdate before comparison

ensures the test is checking the exact DynamoDB record shape produced by store_transactions as well as the range filtering behavior. This is a solid regression guard for subtle changes in transaction serialization.

Also applies to: 79-93


173-185: Reconciliation tests now exercise the TransactionData-based path

Updating the reconciliation tests to pass TransactionData instances (from the generator) into reconcile_unsettled_transactions matches the new method signature and how the handler calls it. The remaining “no unsettled” test still passes dicts but never dereferences them (it returns early), so it remains safe.

Also applies to: 243-246


263-379: get_most_recent_transaction_for_compact tests thoroughly cover new behavior

The three new tests:

  • confirm current-month selection when records exist,
  • confirm fallback to previous months when the current month is empty, and
  • verify that a ValueError is raised (with correct message) and that exactly three monthly queries are performed when no records exist,

provide strong coverage for the new lookup logic. Patching _Config.current_standard_datetime and mocking the DynamoDB Table in the no-data case are both done cleanly and in line with existing practices for this codebase.

Copy link
Collaborator

@landonshumway-ia landonshumway-ia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Barring the dependency check failure, the python code looks good.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (1)

170-278: Harden privilege mapping around empty provider_ids and fix logging field

In add_privilege_information_to_transactions:

  • You build provider_ids from privilege records and then unconditionally call provider_ids.pop() to set licenseeId. If the GSI returns items but none with type in {'privilege','privilegeUpdate'}, provider_ids will be empty and pop() will raise, aborting the whole batch.
  • In the len(provider_ids) > 1 case, the log argument provider_ids=transaction.licenseeId appears to be a copy/paste bug; it should log the ambiguous provider_ids set, not the (possibly masked) licenseeId.

Consider something along these lines:

-        provider_ids = {
-            item['providerId']
-            for item in records_for_transaction_id
-            if item['type'] == 'privilege' or item['type'] == 'privilegeUpdate'
-        }
+        provider_ids = {
+            item['providerId']
+            for item in records_for_transaction_id
+            if item['type'] in ('privilege', 'privilegeUpdate')
+        }
         # there should only be one provider id in the set
         if len(provider_ids) > 1:
             logger.error(
                 'More than one matching provider id found for a transaction id.',
                 compact=compact,
-                transaction_id=transaction.transactionId,
-                # attempt to grab the licensee id from the authorize.net data, which may be invalid if it was masked
-                provider_ids=transaction.licenseeId,
+                transaction_id=transaction.transactionId,
+                provider_ids=list(provider_ids),
             )
 
-        # The licensee id recorded in Authorize.net cannot be trusted...
-        transaction.update({'licenseeId': provider_ids.pop()})
+        if provider_ids:
+            # The licensee id recorded in Authorize.net cannot be trusted...
+            transaction.update({'licenseeId': provider_ids.pop()})
+        else:
+            logger.error(
+                'No providerId found in privilege records for this transaction id.',
+                compact=compact,
+                transaction_id=transaction.transactionId,
+            )
+            # Optionally fall back to UNKNOWN or leave licenseeId unchanged, depending on reporting needs.

This avoids an unhandled exception and makes the error log more informative.

🧹 Nitpick comments (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (1)

81-122: get_most_recent_transaction_for_compact behavior is well‑defined but has a tight 3‑month horizon

The month‑by‑month descending query pattern with Limit=1 and ScanIndexForward=False correctly returns the latest transaction within the last up‑to‑three months and surfaces “no history” via ValueError.

The 3‑month cap is a policy choice: for compacts that have gone quiet for longer than that, you will intentionally behave as “no transactions found” and let callers fall back to their oldest‑allowed window. That’s reasonable, but it’s worth being sure this horizon matches your operational expectations for very low volume compacts.

backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1)

1-72: TransactionData wrapper is consistent and type‑safe; consider clarifying mutability semantics

The TransactionData CCDataClass is wired correctly to TransactionRecordSchema and exposes the expected read‑only properties, which makes downstream code much clearer than raw dicts.

Note that while the properties themselves are read‑only, batch and lineItems return the underlying mutable structures. That’s useful (tests and privilege enrichment mutate them), but it means callers can bypass the update() validation path if they mutate in place. If you want to enforce stricter invariants later, it may be worth documenting this explicitly in the class docstring.

backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (2)

11-24: Helper _generate_mock_transaction matches the new TransactionData model

Using generate_default_transaction with value_overrides to control transactionId/compact and then mutating transaction.batch for optional settlement_time_utc/batch_id is a pragmatic way to build tailored TransactionData instances for tests.

If you want to stay closer to the schema‑driven creation path, you could instead pass a batch override into value_overrides (as you do in other tests), but that’s a stylistic choice rather than a correctness issue.


143-157: Minor type inconsistency in test_reconcile_unsettled_transactions_no_unsettled_passes

Here you still pass settled_transactions as a list of dicts, while the production method now expects list[TransactionData]. The test passes only because the function returns early when there are no unsettled records and never touches settled_transactions.

For consistency with the other reconciliation tests (and to guard against future refactors that might use this argument earlier), consider switching this test to use generate_default_transaction as well.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bce9321 and d19c22b.

📒 Files selected for processing (4)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (13 hunks)
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (7 hunks)
  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py:224-224
Timestamp: 2025-11-13T22:07:29.172Z
Learning: In the CompactConnect transaction history collection workflow (backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py and backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), the cron schedule runs at 2 AM UTC but the handler queries for transactions up to 1 AM UTC. This one-hour gap is intentional to accommodate a delay between when Authorize.net reports a settlement time and when the batch actually becomes visible in their API.
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:05:36.255Z
Learning: In CompactConnect PR #848, the user landonshumway-ia decided to leave timezone handling code in _should_allow_reregistration function as-is after testing in sandbox environment confirmed it works correctly. The user's reasoning was that reregistration is an edge case, the code has been tested and verified, and AWS is unlikely to change behavior that would break many clients. This represents a pragmatic engineering decision based on testing and risk assessment.
📚 Learning: 2025-11-13T22:07:29.172Z
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py:224-224
Timestamp: 2025-11-13T22:07:29.172Z
Learning: In the CompactConnect transaction history collection workflow (backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py and backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), the cron schedule runs at 2 AM UTC but the handler queries for transactions up to 1 AM UTC. This one-hour gap is intentional to accommodate a delay between when Authorize.net reports a settlement time and when the batch actually becomes visible in their API.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is implemented as a property that returns `datetime.now(tz=UTC).replace(microsecond=0)`. The correct way to patch it in tests is by directly providing the datetime value, not wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property (not a method), and can be properly patched in tests by directly supplying a datetime value rather than wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property rather than a method, and should be patched in tests by directly providing a datetime value: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-09-03T22:29:24.535Z
Learnt from: ChiefStief
Repo: csg-org/CompactConnect PR: 1036
File: backend/compact-connect/lambdas/python/data-events/handlers/encumbrance_events.py:0-0
Timestamp: 2025-09-03T22:29:24.535Z
Learning: The EncumbranceEventDetailSchema in backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/data_event/api.py is used across multiple instances/contexts where adverseActionCategory and adverseActionId fields may be required in some cases and not present in others. This is an intentional design pattern for handling different event variants with a single schema.

Applied to files:

  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py
🧬 Code graph analysis (4)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (2)
  • compact (47-48)
  • batch (34-36)
backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py (1)
  • get_privilege_purchase_options (325-400)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (4)
  • get_most_recent_transaction_for_compact (81-122)
  • add_privilege_information_to_transactions (170-278)
  • store_transactions (18-32)
  • reconcile_unsettled_transactions (317-395)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
  • get_settled_transactions (223-244)
  • get_settled_transactions (734-913)
  • get_settled_transactions (1081-1114)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (7)
  • TransactionData (7-72)
  • batch (34-36)
  • transactionProcessor (26-27)
  • compact (47-48)
  • lineItems (39-44)
  • transactionId (30-31)
  • licenseeId (51-52)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • UnsettledTransactionRecordSchema (77-102)
backend/compact-connect/lambdas/python/common/cc_common/config.py (2)
  • transaction_history_table (274-275)
  • current_standard_datetime (253-257)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (3)
  • serialize_to_database_record (205-208)
  • from_database_record (124-139)
  • update (182-203)
backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (3)
  • compact (47-48)
  • batch (34-36)
  • transactionId (30-31)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • serialize_to_database_record (205-208)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (5)
  • TransactionClient (12-395)
  • store_unsettled_transaction (280-315)
  • reconcile_unsettled_transactions (317-395)
  • store_transactions (18-32)
  • get_most_recent_transaction_for_compact (81-122)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (2)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • CCDataClass (46-208)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • TransactionRecordSchema (34-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: TestApp
🔇 Additional comments (7)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (2)

44-67: Contextual logging and live-config gating look solid

Wrapping the handler in logger.append_context_keys and short‑circuiting when there is no compact/jurisdiction configuration is a good addition; it keeps logs rich and avoids unnecessary Authorize.Net calls for compacts that are not yet live.


143-199: Settlement error and 48‑hour unsettled detection flow looks consistent

The way you:

  • accumulate settlementErrorTransactionIds across iterations via batchFailureErrorMessage,
  • only flip to BATCH_FAILURE once _all_transactions_processed(transaction_response) is true, and
  • then augment the same error payload when unreconciled unsettled transactions older than 48 hours are detected

gives a clear, single failure surface at the end of the run while still preserving all failure IDs. This matches the operational intent for compact ops alerts.

backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (2)

18-32: Typed store_transactions implementation looks correct

Switching store_transactions to accept list[TransactionData] and using serialize_to_database_record() under a processor guard is clean and keeps DynamoDB writes consistent with the schema. Raising on unsupported processors is also appropriate here.


317-381: Unsettled reconciliation enhancements align with the new data model

Using settled_transaction_ids = {tx.transactionId for tx in settled_transactions} and logging unreconciled_transactions when there are settled IDs with no matching unsettled records is a good improvement. The rest of the flow (batch delete matched unsettled, 48‑hour cutoff for old unsettled, and warning log) remains intact and reads correctly with the TransactionData type.

backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (3)

44-93: Edge-time test refactor correctly tracks the serialized representation

Switching test_transaction_history_edge_times to:

  • create transactions via generate_default_transaction(...),
  • rely on store_transactions to serialize via the schema, and
  • build expected_transaction using TransactionData.serialize_to_database_record() and stripping dateOfUpdate

keeps the test aligned with the actual storage format and removes hand‑rolled dicts. This is a solid improvement.


263-317: New log-verification test for unreconciled settled transactions is effective

test_reconcile_unsettled_transactions_logs_error_when_settled_transactions_not_matched cleanly validates that:

  • an error is logged when settled transactions can’t be matched to unsettled ones, and
  • the unreconciled_transactions payload contains exactly the unmatched transaction IDs,

while still ensuring functional behavior (no old unsettled IDs returned and matched unsettled records deleted). This gives good coverage of the new logging branch in reconcile_unsettled_transactions.


318-434: get_most_recent_transaction_for_compact tests cover key behaviors; note boto3 internal coupling

The three new tests:

  • verify selection of the newest transaction within the current month,
  • verify fallback to the previous month when the current month is empty, and
  • verify that a ValueError is raised after three months of empty queries, including checking the partition keys used,

which together exercise the main paths of get_most_recent_transaction_for_compact. Patching _Config.current_standard_datetime to a fixed datetime matches the established pattern for this property. Based on learnings.

The only caveat is that asserting against KeyConditionExpression._values couples the test to boto3 internals; acceptable for now, but something to keep in mind if boto3 changes its implementation.

Copy link
Collaborator

@landonshumway-ia landonshumway-ia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See CodeRabbit finding

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (1)

121-178: Settlement error propagation and unsettled reconciliation logic is coherent

  • Accumulating settlementErrorTransactionIds across iterations, persisting batchFailureErrorMessage as JSON, and only flipping to BATCH_FAILURE once _all_transactions_processed is true yields the right alerting semantics without prematurely failing in the middle of pagination.
  • Calling reconcile_unsettled_transactions every iteration and then augmenting batchFailureErrorMessage when there are >48-hour unsettled transactions (and all processing is complete) neatly captures the “old unsettled” error case without double-counting or masking prior failures.
  • The overall behavior—status COMPLETE vs BATCH_FAILURE, plus inclusion of batchFailureErrorMessage—matches the issue description and the test coverage in the updated suite.

You might consider wrapping the batchFailureErrorMessage JSON parse/merge logic into a tiny helper to avoid repeating the json.loads/json.dumps pattern and to centralize any future schema changes. Based on learnings

backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (2)

81-107: Compact/jurisdiction configuration seeding is realistic and reusable

_add_compact_configuration_data now seeds jurisdiction and compact configuration via the shared TestDataGenerator helpers, and constructs configuredStates from the provided jurisdiction list. This mirrors the real DynamoDB layout and keeps tests robust against future schema tweaks.

If you find yourself adding more tests with the same default jurisdiction set, consider lifting the “Ohio-only” case into a constant list (e.g., DEFAULT_JURISDICTIONS) to reduce repetition. Based on learnings


207-243: Previous-transaction seeding helper matches production storage pattern

_add_previous_transaction_to_history builds a TransactionData via _generate_mock_transaction, adjusts the batch metadata, and persists via TransactionClient.store_transactions, which accurately mimics how prior settled transactions exist in DynamoDB.

Two minor notes:

  • The default “2 days before scheduled time” behavior is a nice, simple baseline for tests validating start-time selection.
  • You hard-code settlementTimeLocal as UTC−5 (“assuming EST”). That’s fine for deterministic tests, but if tests ever start asserting on local times across DST boundaries, you may want to centralize a small helper that mirrors whatever timezone logic production uses for settlementTimeLocal.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d19c22b and 1a9e9e4.

📒 Files selected for processing (3)
  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (1 hunks)
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py (9 hunks)
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (24 hunks)
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py:224-224
Timestamp: 2025-11-13T22:07:29.172Z
Learning: In the CompactConnect transaction history collection workflow (backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py and backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), the cron schedule runs at 2 AM UTC but the handler queries for transactions up to 1 AM UTC. This one-hour gap is intentional to accommodate a delay between when Authorize.net reports a settlement time and when the batch actually becomes visible in their API.
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:05:36.255Z
Learning: In CompactConnect PR #848, the user landonshumway-ia decided to leave timezone handling code in _should_allow_reregistration function as-is after testing in sandbox environment confirmed it works correctly. The user's reasoning was that reregistration is an edge case, the code has been tested and verified, and AWS is unlikely to change behavior that would break many clients. This represents a pragmatic engineering decision based on testing and risk assessment.
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py:69-112
Timestamp: 2025-11-14T18:44:41.614Z
Learning: In the CompactConnect transaction history processing workflow (backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), when pagination occurs across multiple lambda invocations, the start_time and end_time for the query window must remain stable. The time window should only be computed on the first invocation (when lastProcessedTransactionId and currentBatchId are absent) and then passed through the event dictionary (startTime and endTime fields) for subsequent invocations. Recomputing the window on each iteration would cause transactions to be skipped when the transaction_limit is reached mid-batch.
<!--
📚 Learning: 2025-11-14T18:44:41.614Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py:69-112
Timestamp: 2025-11-14T18:44:41.614Z
Learning: In the CompactConnect transaction history processing workflow (backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), when pagination occurs across multiple lambda invocations, the start_time and end_time for the query window must remain stable. The time window should only be computed on the first invocation (when lastProcessedTransactionId and currentBatchId are absent) and then passed through the event dictionary (startTime and endTime fields) for subsequent invocations. Recomputing the window on each iteration would cause transactions to be skipped when the transaction_limit is reached mid-batch.
<!--

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
📚 Learning: 2025-11-13T22:07:29.172Z
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py:224-224
Timestamp: 2025-11-13T22:07:29.172Z
Learning: In the CompactConnect transaction history collection workflow (backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py and backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), the cron schedule runs at 2 AM UTC but the handler queries for transactions up to 1 AM UTC. This one-hour gap is intentional to accommodate a delay between when Authorize.net reports a settlement time and when the batch actually becomes visible in their API.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
📚 Learning: 2025-06-17T19:05:36.255Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:05:36.255Z
Learning: In CompactConnect PR #848, the user landonshumway-ia decided to leave timezone handling code in _should_allow_reregistration function as-is after testing in sandbox environment confirmed it works correctly. The user's reasoning was that reregistration is an edge case, the code has been tested and verified, and AWS is unlikely to change behavior that would break many clients. This represents a pragmatic engineering decision based on testing and risk assessment.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
📚 Learning: 2025-08-26T15:30:29.956Z
Learnt from: ChiefStief
Repo: csg-org/CompactConnect PR: 1012
File: backend/compact-connect/docs/design/README.md:503-512
Timestamp: 2025-08-26T15:30:29.956Z
Learning: In CompactConnect, the use of UTC-4:00 for privilege timeline events (encumbrances and expirations) was an agreed-upon business decision, not a technical choice made by individual developers. This fixed offset approach should be respected as an intentional organizational requirement.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
📚 Learning: 2025-06-17T19:02:12.761Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:02:12.761Z
Learning: In the CompactConnect codebase, the datetime comparison between config.current_standard_datetime and Cognito's UserLastModifiedDate works without timezone conversion in sandbox environment, suggesting both datetime objects are already compatible in their timezone awareness.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
📚 Learning: 2025-08-25T22:31:44.837Z
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1005
File: backend/compact-connect/lambdas/python/common/cc_common/dsa_auth.py:212-228
Timestamp: 2025-08-25T22:31:44.837Z
Learning: The datetime.fromisoformat() method in the CompactConnect DSA authentication module already supports both 'Z' and '+00:00' ISO 8601 timestamp formats, as verified by existing tests. The timezone-naive timestamp handling should be added as an additional safety measure.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
📚 Learning: 2025-07-08T18:40:24.408Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 882
File: backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py:287-359
Timestamp: 2025-07-08T18:40:24.408Z
Learning: In the CompactConnect codebase, landonshumway-ia prefers to avoid extraneous unit tests when existing test coverage is already sufficient to catch bugs. For the get_privilege_purchase_options method's live-jurisdiction filtering logic, the existing tests in the purchases test suite provide adequate coverage without needing additional edge case tests.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is implemented as a property that returns `datetime.now(tz=UTC).replace(microsecond=0)`. The correct way to patch it in tests is by directly providing the datetime value, not wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property (not a method), and can be properly patched in tests by directly supplying a datetime value rather than wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property rather than a method, and should be patched in tests by directly providing a datetime value: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
📚 Learning: 2025-08-21T02:51:28.199Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 1014
File: backend/compact-connect/lambdas/python/common/requirements.in:4-4
Timestamp: 2025-08-21T02:51:28.199Z
Learning: In CompactConnect, the purchases lambda contains requests as a transitive dependency from the Authorize.net SDK, which is automatically resolved by pip-compile. This should not be manually removed even when requests is also available in the common layer, as it's managed automatically by the dependency resolver.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
🧬 Code graph analysis (3)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (4)
backend/compact-connect/lambdas/python/common/cc_common/config.py (2)
  • compact_configuration_client (48-51)
  • transaction_client (260-263)
backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py (1)
  • get_privilege_purchase_options (325-400)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
  • get_settled_transactions (224-245)
  • get_settled_transactions (735-914)
  • get_settled_transactions (1082-1115)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (3)
  • add_privilege_information_to_transactions (170-278)
  • store_transactions (18-32)
  • reconcile_unsettled_transactions (317-395)
backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (2)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (8)
  • TestDataGenerator (23-719)
  • generate_default_transaction (653-682)
  • put_default_jurisdiction_configuration_in_configuration_table (634-650)
  • put_default_compact_configuration_in_configuration_table (588-604)
  • put_default_privilege_record_in_provider_table (342-352)
  • generate_default_privilege (311-333)
  • generate_default_privilege_update (361-388)
  • store_record_in_provider_table (336-339)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (2)
  • TransactionClient (12-395)
  • store_transactions (18-32)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (8)
  • TransactionData (7-72)
  • compact (47-48)
  • transactionStatus (67-68)
  • submitTimeUTC (63-64)
  • transactionType (71-72)
  • responseCode (55-56)
  • settleAmount (59-60)
  • batch (34-36)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • create_new (96-121)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/compact/common.py (1)
  • PaymentProcessorType (13-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: TestApp
🔇 Additional comments (10)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (2)

44-88: End-to-end flow, compact live check, and pagination wiring look correct

  • The logger context enrichment, live-compact gating via get_privilege_purchase_options, and early COMPLETE return when no live jurisdictions are present all look sound and match the intended “skip non-live compacts” behavior.
  • Using _get_time_window_strings(event, compact) before invoking PurchaseClient.get_settled_transactions keeps the time-window logic centralized and correctly threads lastProcessedTransactionId, currentBatchId, and processedBatchIds through to the client.
  • The new response shape (including startTime/endTime, pagination fields only when needed, and always including compact/scheduledTime) matches the workflow requirements and supports stable re-invocation.

Also applies to: 90-120


181-213: Time-window helper correctly enforces 30‑day cap and stable pagination window

  • The helper now:
    • Reuses startTime/endTime from the event when present, which keeps the time window stable across paginated lambda invocations.
    • On first invocation, computes end_time from scheduledTime at 01:00 UTC and clamps start_time between “most recent settled transaction + 1s” and “end_time - 30 days”, honoring the Authorize.net 31‑day limit and the intentional 1‑hour delay window.
    • Handles the “no previous transactions” case by logging a warning and falling back cleanly to the 30‑day window.
  • The returned '%Y-%m-%dT%H:%M:%SZ' strings align with what the purchase client expects, and tests verify both the previous-transaction and 30‑day fallback behavior.
backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (6)

31-74: Test transaction generation now aligned with shared TestDataGenerator

Switching _generate_mock_transaction to call TestDataGenerator.generate_default_transaction with overrides (including the hand-built lineItems) is a good move: it keeps tests in sync with the canonical TransactionData schema and reduces hand-crafted dict drift. The parametrization of transaction status, batch settlement state, and jurisdictions is clear.


109-157: Privilege and privilege‑update helpers cover both mapping paths cleanly

The _add_mock_privilege_to_database and _add_mock_privilege_update_to_database helpers now lean on TestDataGenerator to seed provider-table records, including the previous payload for updates. This gives good coverage for both “privilege” and “privilegeUpdate” mapping paths in TransactionClient.add_privilege_information_to_transactions while keeping fixture code concise.


244-279: Existing happy‑path and pagination tests now validate computed window as well

Adding the current-standard datetime patch and seeding a previous transaction in the “complete” and “IN_PROGRESS with pagination” tests, then asserting startTime and endTime against the expected “most recent settlement + 1s” → “scheduled 01:00 UTC” window, gives strong regression coverage for the new time-window logic without overcomplicating the fixtures.

Also applies to: 417-453


455-566: Batch‑failure test accurately exercises multi‑iteration error aggregation

The updated test_process_settled_transactions_returns_batch_failure_status_after_processing_all_transaction now:

  • Seeds a previous transaction so the same time-window logic is used.
  • Simulates settlement errors across two iterations with side_effect.
  • Verifies startTime/endTime stability, IN_PROGRESSBATCH_FAILURE transition, and concatenated failedTransactionIds in batchFailureErrorMessage.

This is a solid, realistic scenario for the new workflow behavior.


811-910: Old/unsettled reconciliation tests nicely validate error and cleanup paths

The tests for “detects_old_unsettled_transactions” and “reconciles_unsettled_transactions” exercise:

  • Detection of >48‑hour unsettled transactions with a BATCH_FAILURE response and unsettledTransactionIds populated.
  • Deletion of matched unsettled entries when a settled transaction arrives, with COMPLETE status and no failure message.

They accurately mirror the intended semantics of TransactionClient.reconcile_unsettled_transactions.


911-1058: New window‑selection and persistence tests thoroughly cover edge cases

The four new tests at the bottom collectively validate the core time-window behavior:

  • uses_previous_transaction_settlement_time_for_start_time asserts that start_time is “most recent settlement + 1s”.
  • uses_30_day_fallback_when_no_previous_transactions asserts the 30‑day fallback when history is empty.
  • uses_provided_start_and_end_times ensures event-supplied startTime/endTime override recomputation and are echoed back.
  • persists_start_and_end_times_across_pagination_iterations confirms that once computed, the same window is reused across paginated iterations and passed through both the response and subsequent get_settled_transactions calls.

This aligns perfectly with the pagination stability requirement captured in the previous learning.

backend/compact-connect/lambdas/python/purchases/purchase_client.py (2)

43-45: Compact threading and TransactionData usage look good

  • Adding compact: str to both PaymentProcessorClient.get_settled_transactions and AuthorizeNetPaymentProcessorClient.get_settled_transactions, then threading it through PurchaseClient.get_settled_transactions, cleanly scopes queries per compact and matches the handler’s call-site.
  • Constructing TransactionData via TransactionData.create_new(...) with a fully shaped dict (including batch, lineItems, transactionProcessor, and compact) is the right way to align with the shared data model; tests asserting stored DB records confirm the shape.
  • The new AuthorizeNetTransactionIgnoreStates enum is a reasonable hook for centralizing ignorable Authorize.net statuses.

If you haven’t already, it’d be worth running the updated tests that hit TransactionClient.store_transactions to confirm that transaction.transactionProcessor still compares equal to AUTHORIZE_DOT_NET_CLIENT_TYPE after going through the schema (i.e., the enum value continues to serialize to the expected string).

Also applies to: 735-756, 1082-1115


818-835: The review comment is incorrect for this codebase; the current code is correct as-is.

The review assumes that in Python, x in EnumClass only matches enum members, not raw strings. However, this understanding does not apply to the CompactConnect codebase, which runs on Python 3.12.

In Python 3.12+, StrEnum membership checking with strings works correctly, meaning the current code at lines 819 and 827—str(tx.transactionStatus) in AuthorizeNetTransactionErrorStates—functions as intended and will match the enum values like 'settlementError', 'generalError', and 'declined'.

No changes are required.

Likely an incorrect or invalid review comment.

@jusdino jusdino changed the base branch from development to main November 14, 2025 20:06
@jusdino jusdino force-pushed the bug/handle-authorizenet-delayed-reporting branch from 1a9e9e4 to c6d2ac4 Compare November 14, 2025 20:06
@jusdino jusdino requested a review from jlkravitz November 14, 2025 20:09
@jusdino
Copy link
Collaborator Author

jusdino commented Nov 14, 2025

@jlkravitz , this one is ready for you. The node failure looks like a new dependency vulnerability, not related to this PR, since I didn't touch anything NodeJS. We'll make a point to mop that up in one of our next PRs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (1)

225-240: Avoid KeyError when provider_ids is empty; fix logged field

If no 'privilege'/'privilegeUpdate' items exist, provider_ids is empty and pop() raises. Also the error log prints licenseeId under provider_ids.

-            if len(provider_ids) > 1:
-                logger.error(
-                    'More than one matching provider id found for a transaction id.',
-                    compact=compact,
-                    transaction_id=transaction.transactionId,
-                    # attempt to grab the licensee id from the authorize.net data, which may be invalid if it was masked
-                    provider_ids=transaction.licenseeId,
-                )
-
-            # The licensee id recorded in Authorize.net cannot be trusted, as Authorize.net masks any values that look
+            if len(provider_ids) > 1:
+                logger.error(
+                    'More than one matching provider id found for a transaction id.',
+                    compact=compact,
+                    transaction_id=transaction.transactionId,
+                    provider_ids=list(provider_ids),
+                )
+
+            # The licensee id recorded in Authorize.net cannot be trusted, as Authorize.net masks any values that look
             # like a credit card number (consecutive digits separated by dashes). We need to grab the provider id from
             # the privileges associated with this transaction and set the licensee id on the transaction to that value
             # to ensure it is valid.
-            transaction.update({'licenseeId': provider_ids.pop()})
+            provider_id = next(iter(provider_ids), None)
+            if not provider_id:
+                logger.error(
+                    'No provider id found while adding privilege info for transaction',
+                    compact=compact,
+                    transaction_id=transaction.transactionId,
+                )
+                continue
+            transaction.update({'licenseeId': provider_id})
🧹 Nitpick comments (4)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)

652-682: Avoid mutating value_overrides in generate_default_transaction

value_overrides.pop('batch') mutates the caller-provided dict, which can surprise tests that reuse the same overrides or inspect them afterward. You can avoid side effects by copying first and working on the copy:

-    def generate_default_transaction(value_overrides: dict | None = None):
+    def generate_default_transaction(value_overrides: dict | None = None):
@@
-        default_batch = deepcopy(DEFAULT_COMPACT_TRANSACTION_BATCH)
-        if value_overrides and 'batch' in value_overrides.keys():
-            default_batch.update(value_overrides.pop('batch'))
+        default_batch = deepcopy(DEFAULT_COMPACT_TRANSACTION_BATCH)
+        overrides = dict(value_overrides) if value_overrides else {}
+        if 'batch' in overrides:
+            default_batch.update(overrides.pop('batch'))
@@
-        if value_overrides:
-            default_transaction.update(value_overrides)
+        if overrides:
+            default_transaction.update(overrides)

This keeps the external API safer without changing behavior.

backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (1)

959-964: Align fallback window comment with the asserted start time

In test_process_settled_transactions_uses_30_day_fallback_when_no_previous_transactions, the comment says:

# oldest_allowed_start = end_time - timedelta(days=30) + timedelta(seconds=1)

but expected_start_time is computed as end_time - timedelta(days=30) (no extra second). Either the comment or the expectation is stale; updating one of them to match the intended behavior will avoid confusion for future readers.

backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (1)

70-85: Optional: tighten MagicMock spec

Using spec=json_obj on a dict doesn’t constrain accidental attribute typos. Consider spec_set with explicit keys to catch test drift.

-    if isinstance(json_obj, dict):
-        # set to the spec of the object so that extra attributes do not return True for hasattr
-        mock = MagicMock(spec=json_obj)
+    if isinstance(json_obj, dict):
+        # restrict attributes to the JSON keys to catch typos in tests
+        mock = MagicMock(spec_set=list(json_obj.keys()))
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (1)

121-146: Batch failure aggregation works; consider de-duplication

Merging failedTransactionIds across iterations is good. Optionally de-duplicate to avoid repeats on retries.

-                batch_failure_error_message['failedTransactionIds'].extend(failed_transactions_ids)
+                existing = set(batch_failure_error_message.get('failedTransactionIds', []))
+                existing.update(failed_transactions_ids)
+                batch_failure_error_message['failedTransactionIds'] = sorted(existing)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a9e9e4 and c6d2ac4.

📒 Files selected for processing (14)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/purchase/api.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (12 hunks)
  • backend/compact-connect/lambdas/python/common/common_test/test_constants.py (2 hunks)
  • backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (2 hunks)
  • backend/compact-connect/lambdas/python/common/tests/__init__.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (7 hunks)
  • backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_transaction.py (1 hunks)
  • backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py (8 hunks)
  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (1 hunks)
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py (9 hunks)
  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (24 hunks)
  • backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (4 hunks)
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/init.py
  • backend/compact-connect/lambdas/python/common/tests/init.py
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/purchase/api.py
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py:224-224
Timestamp: 2025-11-13T22:07:29.172Z
Learning: In the CompactConnect transaction history collection workflow (backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py and backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), the cron schedule runs at 2 AM UTC but the handler queries for transactions up to 1 AM UTC. This one-hour gap is intentional to accommodate a delay between when Authorize.net reports a settlement time and when the batch actually becomes visible in their API.
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:05:36.255Z
Learning: In CompactConnect PR #848, the user landonshumway-ia decided to leave timezone handling code in _should_allow_reregistration function as-is after testing in sandbox environment confirmed it works correctly. The user's reasoning was that reregistration is an edge case, the code has been tested and verified, and AWS is unlikely to change behavior that would break many clients. This represents a pragmatic engineering decision based on testing and risk assessment.
📚 Learning: 2025-11-14T18:44:41.614Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py:69-112
Timestamp: 2025-11-14T18:44:41.614Z
Learning: In the CompactConnect transaction history processing workflow (backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), when pagination occurs across multiple lambda invocations, the start_time and end_time for the query window must remain stable. The time window should only be computed on the first invocation (when lastProcessedTransactionId and currentBatchId are absent) and then passed through the event dictionary (startTime and endTime fields) for subsequent invocations. Recomputing the window on each iteration would cause transactions to be skipped when the transaction_limit is reached mid-batch.
<!--

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
📚 Learning: 2025-11-13T22:07:29.172Z
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1201
File: backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py:224-224
Timestamp: 2025-11-13T22:07:29.172Z
Learning: In the CompactConnect transaction history collection workflow (backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py and backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py), the cron schedule runs at 2 AM UTC but the handler queries for transactions up to 1 AM UTC. This one-hour gap is intentional to accommodate a delay between when Authorize.net reports a settlement time and when the batch actually becomes visible in their API.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py
  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
📚 Learning: 2025-07-08T18:40:24.408Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 882
File: backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py:287-359
Timestamp: 2025-07-08T18:40:24.408Z
Learning: In the CompactConnect codebase, landonshumway-ia prefers to avoid extraneous unit tests when existing test coverage is already sufficient to catch bugs. For the get_privilege_purchase_options method's live-jurisdiction filtering logic, the existing tests in the purchases test suite provide adequate coverage without needing additional edge case tests.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is implemented as a property that returns `datetime.now(tz=UTC).replace(microsecond=0)`. The correct way to patch it in tests is by directly providing the datetime value, not wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property (not a method), and can be properly patched in tests by directly supplying a datetime value rather than wrapping it in a lambda: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-04-29T21:14:36.205Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 769
File: backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_encumbrance.py:38-40
Timestamp: 2025-04-29T21:14:36.205Z
Learning: In the CompactConnect codebase, `cc_common.config._Config.current_standard_datetime` is a property rather than a method, and should be patched in tests by directly providing a datetime value: `patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat(DEFAULT_DATE_OF_UPDATE_TIMESTAMP))`.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py
  • backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py
📚 Learning: 2025-06-17T19:05:36.255Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:05:36.255Z
Learning: In CompactConnect PR #848, the user landonshumway-ia decided to leave timezone handling code in _should_allow_reregistration function as-is after testing in sandbox environment confirmed it works correctly. The user's reasoning was that reregistration is an edge case, the code has been tested and verified, and AWS is unlikely to change behavior that would break many clients. This represents a pragmatic engineering decision based on testing and risk assessment.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py
📚 Learning: 2025-08-26T15:30:29.956Z
Learnt from: ChiefStief
Repo: csg-org/CompactConnect PR: 1012
File: backend/compact-connect/docs/design/README.md:503-512
Timestamp: 2025-08-26T15:30:29.956Z
Learning: In CompactConnect, the use of UTC-4:00 for privilege timeline events (encumbrances and expirations) was an agreed-upon business decision, not a technical choice made by individual developers. This fixed offset approach should be respected as an intentional organizational requirement.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py
📚 Learning: 2025-06-17T19:02:12.761Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 848
File: backend/compact-connect/lambdas/python/provider-data-v1/handlers/registration.py:111-117
Timestamp: 2025-06-17T19:02:12.761Z
Learning: In the CompactConnect codebase, the datetime comparison between config.current_standard_datetime and Cognito's UserLastModifiedDate works without timezone conversion in sandbox environment, suggesting both datetime objects are already compatible in their timezone awareness.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
  • backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py
📚 Learning: 2025-08-25T22:31:44.837Z
Learnt from: jusdino
Repo: csg-org/CompactConnect PR: 1005
File: backend/compact-connect/lambdas/python/common/cc_common/dsa_auth.py:212-228
Timestamp: 2025-08-25T22:31:44.837Z
Learning: The datetime.fromisoformat() method in the CompactConnect DSA authentication module already supports both 'Z' and '+00:00' ISO 8601 timestamp formats, as verified by existing tests. The timezone-naive timestamp handling should be added as an additional safety measure.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py
📚 Learning: 2025-08-21T02:51:28.199Z
Learnt from: landonshumway-ia
Repo: csg-org/CompactConnect PR: 1014
File: backend/compact-connect/lambdas/python/common/requirements.in:4-4
Timestamp: 2025-08-21T02:51:28.199Z
Learning: In CompactConnect, the purchases lambda contains requests as a transitive dependency from the Authorize.net SDK, which is automatically resolved by pip-compile. This should not be manually removed even when requests is also available in the common layer, as it's managed automatically by the dependency resolver.

Applied to files:

  • backend/compact-connect/lambdas/python/purchases/purchase_client.py
🧬 Code graph analysis (9)
backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (2)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (8)
  • TestDataGenerator (23-719)
  • generate_default_transaction (653-682)
  • put_default_jurisdiction_configuration_in_configuration_table (634-650)
  • put_default_compact_configuration_in_configuration_table (588-604)
  • put_default_privilege_record_in_provider_table (342-352)
  • generate_default_privilege (311-333)
  • generate_default_privilege_update (361-388)
  • store_record_in_provider_table (336-339)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (2)
  • TransactionClient (12-395)
  • store_transactions (18-32)
backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py (4)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (1)
  • store_transactions (18-32)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • update (182-203)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (2)
  • lineItems (39-44)
  • licenseeId (51-52)
backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (3)
backend/compact-connect/lambdas/python/common/cc_common/data_model/compact_configuration_client.py (1)
  • get_privilege_purchase_options (325-400)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
  • get_settled_transactions (224-245)
  • get_settled_transactions (735-914)
  • get_settled_transactions (1082-1115)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (4)
  • add_privilege_information_to_transactions (170-278)
  • store_transactions (18-32)
  • reconcile_unsettled_transactions (317-395)
  • get_most_recent_transaction_for_compact (81-122)
backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_transaction.py (5)
backend/compact-connect/lambdas/python/common/tests/__init__.py (1)
  • TstLambdas (9-112)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (2)
  • TestDataGenerator (23-719)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • TransactionRecordSchema (34-73)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (2)
  • serialize_to_database_record (205-208)
  • from_database_record (124-139)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (12)
  • TransactionData (7-72)
  • transactionProcessor (26-27)
  • transactionId (30-31)
  • batch (34-36)
  • lineItems (39-44)
  • compact (47-48)
  • licenseeId (51-52)
  • responseCode (55-56)
  • settleAmount (59-60)
  • submitTimeUTC (63-64)
  • transactionStatus (67-68)
  • transactionType (71-72)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (7)
  • TransactionData (7-72)
  • batch (34-36)
  • transactionProcessor (26-27)
  • compact (47-48)
  • lineItems (39-44)
  • transactionId (30-31)
  • licenseeId (51-52)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/record.py (1)
  • UnsettledTransactionRecordSchema (77-102)
backend/compact-connect/lambdas/python/common/cc_common/config.py (2)
  • transaction_history_table (274-275)
  • current_standard_datetime (253-257)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (3)
  • serialize_to_database_record (205-208)
  • from_database_record (124-139)
  • update (182-203)
backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (4)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (3)
  • compact (47-48)
  • batch (34-36)
  • transactionId (30-31)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (1)
  • generate_default_transaction (653-682)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • serialize_to_database_record (205-208)
backend/compact-connect/lambdas/python/common/cc_common/data_model/transaction_client.py (5)
  • TransactionClient (12-395)
  • store_unsettled_transaction (280-315)
  • reconcile_unsettled_transactions (317-395)
  • store_transactions (18-32)
  • get_most_recent_transaction_for_compact (81-122)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (8)
  • TransactionData (7-72)
  • compact (47-48)
  • transactionStatus (67-68)
  • submitTimeUTC (63-64)
  • transactionType (71-72)
  • responseCode (55-56)
  • settleAmount (59-60)
  • batch (34-36)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (1)
  • create_new (96-121)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/compact/common.py (1)
  • PaymentProcessorType (13-14)
backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (2)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (6)
  • transactionId (30-31)
  • compact (47-48)
  • licenseeId (51-52)
  • batch (34-36)
  • lineItems (39-44)
  • transactionStatus (67-68)
backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)
  • get_settled_transactions (224-245)
  • get_settled_transactions (735-914)
  • get_settled_transactions (1082-1115)
backend/compact-connect/lambdas/python/common/common_test/test_data_generator.py (2)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/transaction/__init__.py (1)
  • TransactionData (7-72)
backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py (2)
  • update (182-203)
  • create_new (96-121)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: TestApp
🔇 Additional comments (19)
backend/compact-connect/stacks/transaction_monitoring_stack/transaction_history_processing_workflow.py (1)

219-224: LGTM! Schedule change correctly implements the timing adjustment.

The schedule change from 1 AM to 2 AM UTC and the updated comment accurately reflect the PR objective to accommodate Authorize.net's settlement reporting delays. The timing calculation is correct (6pm PST = 2am UTC), and the 2-hour buffer from the default 4pm Pacific settlement time provides adequate coverage for late settlements.

Based on learnings.

backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_transaction.py (1)

12-47: Good coverage of transaction schema and data class behavior

These tests nicely cover happy-path serde, validation failures (missing transactionId, invalid transactionProcessor), the data-class getters, and the exact DB serialization shape (minus the dynamic dateOfUpdate). This should catch regressions in both the Marshmallow schema and TransactionData behavior.

Also applies to: 55-127

backend/compact-connect/lambdas/python/common/common_test/test_constants.py (1)

32-62: Transaction constants align with schema and test expectations

The new batch and line-item defaults (including quantities, prices, and optional privilegeId) and TRANSACTION_RECORD_TYPE match the structures exercised in the transaction schema and client tests. They provide a solid, centralized fixture for transaction-related tests.

Also applies to: 91-91

backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_transaction_client.py (1)

1-9: Tests correctly exercise TransactionClient with the new TransactionData model

Using TestDataGenerator.generate_default_transaction and the shared line-item constants keeps these tests consistent with the schema and other suites. The assertions on stored items (including pk/sk and dynamic dateOfUpdate), unsupported processor handling, and privilege/ provider-id mapping all look accurate for the current TransactionClient implementation.

Also applies to: 29-60, 64-77, 107-136, 155-167, 179-182

backend/compact-connect/lambdas/python/purchases/tests/function/test_handlers/test_transaction_history.py (2)

34-74: Time-window & pagination tests look consistent with the new transaction-history behavior

The refactor to:

  • Generate transactions via TestDataGenerator.generate_default_transaction,
  • Seed compact/jurisdiction/privilege data through shared helpers, and
  • Use _add_previous_transaction_to_history + TransactionClient to simulate prior settlements,

produces clear, high-signal tests around:

  • Computing startTime from the most recent prior settlement (plus one second).
  • Falling back to a bounded lookback window when no prior transactions exist.
  • Respecting provided startTime/endTime in the event.
  • Persisting startTime and endTime across paginated iterations so the window stays stable.

Combined with the existing assertions on DynamoDB contents and status values (COMPLETE, IN_PROGRESS, BATCH_FAILURE), this gives strong coverage of the updated handler semantics.

Also applies to: 81-107, 109-157, 207-243, 911-1058


223-232: Python runtime confirmed; no compatibility issue

Verification shows Python 3.11.2 is in use, which fully supports datetime.fromisoformat with the 'Z' suffix. The code across all flagged lines (223–232, 261–267, 433–439, 515–520, 920–923, 961–964) is compatible with the runtime. Additionally, the learnings confirm this pattern is already tested and proven in the codebase.

backend/compact-connect/lambdas/python/purchases/tests/unit/test_purchase_client.py (4)

1115-1121: Attribute-style assertions look correct

Switch to TransactionData properties reads cleanly and matches the model API.


1200-1203: Pagination assertions align with new data model

Verifies IDs via TransactionData; good coverage for batched paging.

Also applies to: 1221-1223


1293-1298: Settlement error assertions updated correctly

Using transaction.transactionId/transactionStatus is consistent with TransactionData.


1299-1376: Nice addition: declined transactions are skipped

Test precisely captures the ignore-state behavior; this protects reports from noise.

backend/compact-connect/lambdas/python/purchases/handlers/transaction_history.py (3)

69-76: Stable window computation is correct

reuses start/end across invocations; prevents mid-batch skipping. Matches the 2:00→1:00 UTC gap design.


101-120: Response threading is complete

startTime/endTime returned with pagination fields only when needed; good for Step Functions replayability.


181-213: No action needed—code is compatible with Python 3.12

Python 3.11 and 3.12 both support parsing timestamps with a trailing "Z" via datetime.fromisoformat(). Since the repository targets Python 3.12, the code correctly relies on this support without requiring normalization to +00:00.

backend/compact-connect/lambdas/python/common/tests/function/test_data_model/test_transaction_client.py (3)

318-356: Good coverage: current-month selection

Validates the “most recent in current month” path with deterministic timestamps.


356-386: Good coverage: previous-month fallback

Exercises month-walkback logic and ensures correct PK selection.


387-435: Negative path is well-tested

Mocks 3 queries and asserts correct PKs; failure message verified.

backend/compact-connect/lambdas/python/purchases/purchase_client.py (3)

859-881: TransactionData construction is correct

Fields are string-cast for JSON safety; compact is included; good alignment with schema.


790-899: Reject offset calculation; original code is correct for page-based pagination

The web search confirms Authorize.Net's Paging.offset is a 1-based page number (not a record offset). The API docs explicitly state that "offset — The number of the page to return results from. For example, if you use a limit of 100, setting offset to 1 will return the first 100 transactions, setting offset to 2 will return the second 100 transactions".

The proposed diff calculating (page_offset - 1) * MAXIMUM_TRANSACTION_API_LIMIT + 1 is incorrect and would break pagination. The original code correctly treats page_offset as a page number and should not be modified with that formula.

However, the loop logic concern regarding transactions_in_page being set from totalNumInResultSet (whether that represents the total overall or count in the current page) and the while condition termination still requires verification against the API response schema to confirm if the pagination loop has a genuine issue.


827-835: Runtime verified as Python 3.12; code is correct

The Lambda runtime is Python 3.12, which satisfies the requirement and supports StrEnum membership checks with strings. The pattern str(tx.transactionStatus) in AuthorizeNetTransactionIgnoreStates will work correctly at runtime.

Copy link
Collaborator

@jlkravitz jlkravitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! @isabeleliassen Good to merge.

@jlkravitz jlkravitz closed this Nov 18, 2025
@jlkravitz jlkravitz reopened this Nov 18, 2025
@isabeleliassen isabeleliassen merged commit 8135251 into csg-org:main Nov 18, 2025
5 of 9 checks passed
@jusdino jusdino deleted the bug/handle-authorizenet-delayed-reporting branch November 18, 2025 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gracefully handle delays in Authorize.net API settled batch reporting

4 participants