Skip to content

feat: add charge on transaction#23

Merged
AndrewHanasiro merged 1 commit intomainfrom
feature/update-transaction-by-charge
Jan 19, 2025
Merged

feat: add charge on transaction#23
AndrewHanasiro merged 1 commit intomainfrom
feature/update-transaction-by-charge

Conversation

@AndrewHanasiro
Copy link
Member

@AndrewHanasiro AndrewHanasiro commented Jan 17, 2025

Summary by CodeRabbit

  • Database Changes

    • Added charge_id column to the ledger table as a UUID type
  • New Features

    • Introduced new transaction updating functionality
    • Added ability to track and associate charges with transactions
  • Refactoring

    • Renamed UpdateAccount to UpdatingAccount
    • Updated import statements and class references across multiple files
    • Simplified worker automation process for post-paid charges
  • Bug Fixes

    • Corrected invoice status string comparison (from "draft" to "Draft")
  • Testing

    • Updated test cases to reflect new class names and functionality

@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2025

Walkthrough

The pull request introduces several significant changes across the project, focusing on enhancing transaction and account management. The primary modifications include adding a charge_id column to the ledger table, renaming UpdateAccount to UpdatingAccount, introducing a new UpdatingTransaction abstract class, and refactoring the charge debit process. These changes improve the system's ability to track and manage charges, transactions, and account updates with more precise and flexible mechanisms.

Changes

File Change Summary
db/migrations/20230327184125_init-structure.sql Added charge_id UUID column to ledger table
src/core/__init__.py Renamed update_account to updating_account, added updating_transaction
src/core/repository/account.py Updated to use UpdatingAccount instead of UpdateAccount
src/core/repository/billing.py Modified invoice status string comparison to "Draft"
src/core/repository/ledger.py Added UpdatingTransaction, new add_charge method
src/core/usecase/driven/update_transaction.py Created new abstract UpdatingTransaction class
src/presentation/worker.py Refactored post_paid_automation_charge to use Core class
Multiple test files Updated imports and mocks to reflect new class names and behaviors

Sequence Diagram

sequenceDiagram
    participant Core
    participant ChargeDebit
    participant BillingService
    participant LedgerRepository
    
    Core->>ChargeDebit: charge_debit()
    ChargeDebit->>BillingService: Get draft invoice
    BillingService-->>ChargeDebit: Return invoice
    ChargeDebit->>LedgerRepository: Add charge to transaction
    LedgerRepository-->>ChargeDebit: Confirm charge added
    ChargeDebit-->>Core: Charge completed
Loading

Poem

🐰 A Rabbit's Charge of Delight

In ledgers where transactions dance,
A charge_id joins the financial prance
Updating accounts with rabbit-like might
Transforming systems, making data bright!

🎉 Hop, hop, hooray! 🎊

Finishing Touches

  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sonarqubecloud
Copy link

Copy link

@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: 2

🧹 Nitpick comments (3)
src/core/usecase/charge_debit.py (2)

10-10: Verify the import statement for UpdatingTransaction.

Ensure that the import path from src.core.usecase.driven.update_transaction import UpdatingTransaction is correct. Misplaced or incorrect imports can lead to ModuleNotFoundError.


40-43: Handle potential case sensitivity in invoice status check.

The condition if current_invoice.status != "Draft": may fail if the status string has different casing. To make the check case-insensitive, consider normalizing the status string.

Apply this diff to normalize the status comparison:

 def _charge_single_user(self, user: Account) -> None:
     if user.type is AccountType.PRE_PAID:
         return
     current_invoice = self.billing_fetching_invoice.get_current(user.external_id)
-    if current_invoice.status != "Draft":
+    if current_invoice.status.lower() != "draft":
         raise InvoicePostPaidError()
     charge = self.billing_updating_invoice.charge(current_invoice.id)
     self.updating_transaction.add_charge(user.id, charge.id)
tests/core/usecase/test_charge_debit.py (1)

29-30: Consider using enums for status values.

String literals like "Draft" and "Pending" are used for status. Consider using enums to prevent typos and ensure consistency.

from enum import Enum

class InvoiceStatus(Enum):
    DRAFT = "Draft"
    # Add other statuses...

class ChargeStatus(Enum):
    PENDING = "Pending"
    # Add other statuses...
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0c797f and 918ea0a.

📒 Files selected for processing (15)
  • db/migrations/20230327184125_init-structure.sql (1 hunks)
  • src/core/__init__.py (4 hunks)
  • src/core/repository/account.py (2 hunks)
  • src/core/repository/billing.py (1 hunks)
  • src/core/repository/ledger.py (3 hunks)
  • src/core/usecase/charge_debit.py (3 hunks)
  • src/core/usecase/driven/update_account.py (1 hunks)
  • src/core/usecase/driven/update_transaction.py (1 hunks)
  • src/core/usecase/transform_to_post_paid.py (2 hunks)
  • src/core/usecase/transform_to_pre_paid.py (2 hunks)
  • src/presentation/worker.py (1 hunks)
  • tests/core/usecase/test_charge_debit.py (3 hunks)
  • tests/core/usecase/test_transform_to_post_paid.py (2 hunks)
  • tests/core/usecase/test_transform_to_pre_paid.py (2 hunks)
  • tests/presentation/test_worker.py (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/core/usecase/driven/update_account.py
  • src/core/usecase/transform_to_pre_paid.py
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Test
🔇 Additional comments (17)
src/presentation/worker.py (2)

10-11: Ensure proper initialization and dependency injection of the Core class.

The Core class is instantiated without any parameters. Verify that Core() does not require any arguments or specific configuration. If Core depends on external resources or configurations, ensure that they are properly initialized to avoid runtime issues.


15-15: Confirm the scheduling time format and correctness.

The job is scheduled to run daily at "02:00". Ensure that the schedule library accepts this time format and that it aligns with the desired execution time. The time should be in 24-hour format as a string.

src/core/usecase/charge_debit.py (1)

24-29: ⚠️ Potential issue

Update all instantiations of ChargeDebit with the new dependency.

The constructor of ChargeDebit now requires an additional parameter updating_transaction. Verify that all instances where ChargeDebit is instantiated throughout the codebase are updated to pass this new dependency to prevent TypeError exceptions.

Run the following script to locate all instantiations of ChargeDebit:

✅ Verification successful

All ChargeDebit instantiations are properly updated

Both instances of ChargeDebit in the codebase already include the new updating_transaction parameter:

  • src/core/__init__.py: Correctly includes all dependencies
  • tests/core/usecase/test_charge_debit.py: Properly set up with mocked dependency
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all instantiations of ChargeDebit that need updating.

# Search for instances of ChargeDebit being instantiated.
rg -F 'ChargeDebit(' --glob '!src/core/usecase/charge_debit.py'

Length of output: 193


Script:

#!/bin/bash
# Get context around ChargeDebit instantiations
echo "=== Core initialization context ==="
rg -B 2 -A 4 'ChargeDebit\(' src/core/__init__.py

echo -e "\n=== Test context ==="
rg -B 2 -A 4 'ChargeDebit\(' tests/core/usecase/test_charge_debit.py

Length of output: 734

tests/presentation/test_worker.py (3)

22-23: LGTM! Good improvement in test readability.

Moving the external_id generation outside create_account makes the test more explicit and allows reusing the ID in the URL assertion.


53-55: LGTM! Assertion updated to match new endpoint.

The assertion correctly validates the new endpoint URL format with user_id parameter.


30-31: Verify API contract changes.

The endpoint has changed from POST to GET and now requires a user_id query parameter. Please ensure:

  1. These changes are documented in the API documentation
  2. All clients are updated to use the new endpoint format
src/core/usecase/transform_to_post_paid.py (1)

9-9: LGTM! Consistent class renaming.

The rename from UpdateAccount to UpdatingAccount maintains consistency with the broader refactoring changes.

Also applies to: 25-25

tests/core/usecase/test_charge_debit.py (2)

8-8: LGTM! New imports support charge tracking.

Added imports for Charge entity and LedgerRepository align with the new charge tracking functionality.

Also applies to: 11-11


37-39: LGTM! Comprehensive test coverage for charge tracking.

The test properly validates:

  1. Charge creation through billing service
  2. Charge association with ledger entries
  3. Correct parameters passed to add_charge

Also applies to: 52-52

tests/core/usecase/test_transform_to_post_paid.py (1)

16-16: LGTM! Consistent class renaming.

The rename from UpdateAccount to UpdatingAccount is consistently applied in both import and variable type annotation.

Also applies to: 46-46

src/core/repository/billing.py (1)

36-36: Verify case sensitivity change impact.

Changing the invoice status check from "draft" to "Draft" is a breaking change that could affect other parts of the system.

Let's verify the consistency of invoice status case usage across the codebase:

src/core/repository/ledger.py (1)

26-26: LGTM! Good addition of charge tracking.

The charge_id column addition to the ledger table enables proper tracking of charges for transactions.

tests/core/usecase/test_transform_to_pre_paid.py (1)

21-21: LGTM! Consistent renaming applied.

The update from UpdateAccount to UpdatingAccount is consistently applied in both import and variable type annotation.

Also applies to: 63-63

src/core/__init__.py (1)

25-26: LGTM! Well-structured dependency injection.

The changes properly integrate the new UpdatingTransaction functionality while maintaining clean dependency injection patterns. The renaming from update_account to updating_account is consistently applied.

Also applies to: 47-48, 57-57, 76-76, 83-83

src/core/repository/account.py (1)

17-17: LGTM! Verify rename impact across codebase.

The rename from UpdateAccount to UpdatingAccount looks good and maintains the existing functionality.

Let's verify that this rename has been consistently applied across the codebase:

Also applies to: 42-42

✅ Verification successful

Rename verification successful ✓

The rename from UpdateAccount to UpdatingAccount has been consistently applied across all files in the codebase, with no remaining references to the old name.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining references to the old name
rg "UpdateAccount" --type py

# Check for consistent usage of the new name
rg "UpdatingAccount" --type py

Length of output: 1379

db/migrations/20230327184125_init-structure.sql (2)

38-38: Consider adding foreign key constraint for data integrity.

The charge_id column appears to reference a charge entity, but there's no foreign key constraint. This could lead to data integrity issues if charges can be deleted while referenced by ledger entries.

Consider either:

  1. Adding a foreign key constraint if there's a charges table
  2. Documenting why the constraint is intentionally omitted

Let's check if a charges table exists or is defined elsewhere:


Line range hint 1-56: Verify deployment strategy for init structure changes.

This appears to be an initialization migration script, but it's being modified to add a new feature. This approach could cause issues:

  1. For new deployments: No problems, they'll get the complete structure
  2. For existing deployments: They might already have run this migration, making this change ineffective

Consider creating a new migration file for this change instead of modifying the init structure. Let's verify the migration strategy:

✅ Verification successful

Modifying init structure is acceptable in this case

Based on the evidence, this appears to be an early-stage project with minimal migration history. The current approach of modifying the initialization migration is acceptable, though for future changes, consider creating new migration files to maintain a clear upgrade path as the project matures.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for existing migrations
fd -e sql -p "db/migrations"

# Check if this migration has been applied in any environment
rg "20230327184125" --type yaml --type json

Length of output: 171


Script:

#!/bin/bash
# Look for schema references and DB configuration
rg -g '!*.sql' -l "CREATE TABLE|CREATE TYPE" -A 2

# Check for migration tracking or database configuration files
fd -e yaml -e json -e conf -e config -e env . | grep -i "db\|database\|migrate"

Length of output: 133

Comment on lines +5 to +8
class UpdatingTransaction(metaclass=ABCMeta):
@abstractmethod
def add_charge(self, account_id: UUID, charge_id: UUID) -> None:
pass
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement the UpdatingTransaction interface in concrete classes.

An abstract class UpdatingTransaction with the method add_charge has been introduced. Ensure that all concrete classes that should update transactions implement this interface and provide a concrete implementation of the add_charge method.

Comment on lines +80 to +90
def add_charge(self, account_id: UUID, charge_id: UUID) -> None:
query = (
update(ledger_table)
.values(charge_id=charge_id)
.where(
ledger_table.c.account_id == account_id,
ledger_table.c.charge_id.__eq__(None),
)
)
self.session.execute(query)
self.session.commit()
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for charge_id existence.

The add_charge method should validate that the charge_id exists before updating transactions.

Consider adding validation:

 def add_charge(self, account_id: UUID, charge_id: UUID) -> None:
+    if not charge_id:
+        raise ValueError("charge_id cannot be None or empty")
+
     query = (
         update(ledger_table)
         .values(charge_id=charge_id)
         .where(
             ledger_table.c.account_id == account_id,
             ledger_table.c.charge_id.__eq__(None),
         )
     )
     self.session.execute(query)
     self.session.commit()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def add_charge(self, account_id: UUID, charge_id: UUID) -> None:
query = (
update(ledger_table)
.values(charge_id=charge_id)
.where(
ledger_table.c.account_id == account_id,
ledger_table.c.charge_id.__eq__(None),
)
)
self.session.execute(query)
self.session.commit()
def add_charge(self, account_id: UUID, charge_id: UUID) -> None:
if not charge_id:
raise ValueError("charge_id cannot be None or empty")
query = (
update(ledger_table)
.values(charge_id=charge_id)
.where(
ledger_table.c.account_id == account_id,
ledger_table.c.charge_id.__eq__(None),
)
)
self.session.execute(query)
self.session.commit()

@codacy-production
Copy link

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+0.00% 97.92%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (b0c797f) 1248 1153 92.39%
Head commit (918ea0a) 1275 (+27) 1178 (+25) 92.39% (+0.00%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#23) 48 47 97.92%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Codacy stopped sending the deprecated coverage status on June 5th, 2024. Learn more

@AndrewHanasiro AndrewHanasiro merged commit 0cd82a4 into main Jan 19, 2025
9 checks passed
@AndrewHanasiro AndrewHanasiro deleted the feature/update-transaction-by-charge branch January 19, 2025 17:14
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.

1 participant