Skip to content

fix: update configuration <> expense pull#570

Open
ashwin1111 wants to merge 1 commit intomasterfrom
fix-update-config-expense-pull
Open

fix: update configuration <> expense pull#570
ashwin1111 wants to merge 1 commit intomasterfrom
fix-update-config-expense-pull

Conversation

@ashwin1111
Copy link
Contributor

@coderabbitai
Copy link

coderabbitai bot commented Oct 15, 2025

Walkthrough

Switched ExpenseGroupSettings signal handling from pre_save to post_save in apps/fyle/signals.py, renaming the receiver accordingly and updating imports and docstrings. Core logic remains, now executed after model save.

Changes

Cohort / File(s) Summary
Signal shift to post_save
apps/fyle/signals.py
Replaced pre_save with post_save receiver for ExpenseGroupSettings; renamed handler to run_post_save_expense_group_setting_triggers; adjusted imports (added post_save; reordered enums); updated docstring; preserved conditional logic under post-save execution.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Model as ExpenseGroupSettings (Model)
  participant ORM as Django ORM
  participant Signal as post_save Receiver

  User->>Model: create/update instance
  Model->>ORM: save()
  ORM-->>Model: persist changes
  Note over ORM,Signal: post_save emitted after successful save
  ORM-->>Signal: post_save(sender=ExpenseGroupSettings, instance)
  activate Signal
  Signal->>Signal: run_post_save_expense_group_setting_triggers(instance)
  alt Conditions met
    Signal-->>ORM: trigger related actions
  else Conditions not met
    Signal-->>ORM: no-op
  end
  deactivate Signal
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

Thump-thump goes my reviewy heart,
From pre to post, a tidy art.
Save now, then signals leap—
After the write, promises keep.
Carrot logs crisp, triggers toast—
A bunny nods: post_save host. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description only contains a ClickUp URL and does not describe any part of the changeset or its rationale, providing no context for reviewers. This is off-topic and fails to meet the description requirements. Please add a concise description summarizing the changes made—such as updating the ExpenseGroupSettings signal from pre_save to post_save and related import adjustments—to give reviewers clear context.
Title Check ❓ Inconclusive The title “fix: update configuration <> expense pull” is vague and does not clearly summarize the main change of switching the ExpenseGroupSettings signal from pre_save to post_save, nor does it reference the specific code modifications. It uses placeholder characters and generic phrasing that fail to convey the intent of this pull request. Therefore the title check is inconclusive. Please revise the title to succinctly describe the core change, for example “fix: switch ExpenseGroupSettings signal from pre_save to post_save,” so that reviewers can immediately understand the purpose of this pull request.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-update-config-expense-pull

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.

@github-actions
Copy link

Coverage

Coverage Report
FileStmtsMissCoverMissing
apps
   exceptions.py54983%30, 42, 67, 73, 91, 104, 122–124
apps/fyle
   actions.py141795%92, 314, 353–357
   enums.py270100% 
   helpers.py125894%59, 220, 227–230, 233–234
   models.py2701096%374–376, 380–382, 401, 405, 461, 588, 610, 716
   queue.py280100% 
   serializers.py210100% 
   signals.py22482%30–31, 34–35
   tasks.py3492393%146–147, 154–156, 346, 356–365, 373, 377, 648–650, 668–685, 697–699
   views.py610100% 
apps/mappings
   actions.py36489%46–47, 50–51
   constants.py20100% 
   exceptions.py1041288%38–40, 43–44, 47–48, 55–57, 60–61
   models.py310100% 
   queue.py31390%93–95
   schedules.py120100% 
   serializers.py100100% 
   signals.py74988%38–39, 107–109, 134–140
   tasks.py31197%61
   utils.py342041%46–113
   views.py330100% 
apps/tasks
   enums.py150100% 
   models.py610100% 
   serializers.py60100% 
   views.py90100% 
apps/users
   helpers.py120100% 
   models.py530100% 
   views.py110100% 
apps/workspaces
   actions.py1451788%129–130, 151–160, 196–207, 248
   email.py360100% 
   helpers.py45198%25
   models.py112199%124
   permissions.py20195%23
   queue.py41685%60–64, 87
   serializers.py260100% 
   signals.py280100% 
   tasks.py84298%100–101
   utils.py1003763%94–231, 263–264
   views.py107199%77
apps/workspaces/apis/advanced_settings
   serializers.py68593%156, 159, 165, 171, 174
   triggers.py120100% 
   views.py110100% 
apps/workspaces/apis/clone_settings
   helpers.py90100% 
   serializers.py440100% 
   views.py22291%14–16
apps/workspaces/apis/errors
   serializers.py200100% 
   views.py190100% 
apps/workspaces/apis/export_settings
   helpers.py72396%132–134
   serializers.py94694%211, 214, 217, 224, 231, 240
   triggers.py190100% 
   views.py110100% 
apps/workspaces/apis/import_settings
   serializers.py79396%226, 229, 232
   triggers.py63395%36–37, 89
   views.py110100% 
apps/workspaces/templatetags
   custom_filters.py50100% 
apps/xero
   actions.py301743%23–33, 37–49
   exceptions.py1412682%54–55, 98–135, 163–169, 181, 189–195, 281
   models.py217598%40–44, 55, 376, 395
   queue.py112199%37
   serializers.py150100% 
   tasks.py4818682%140–164, 177, 269–273, 284–285, 296, 326, 349–350, 457–461, 472–473, 528, 555–556, 694, 857–862, 875–885, 888–899, 903–934, 967–971, 998–1002, 1021, 1028–1032
   utils.py3382692%100, 139–184, 215, 319, 328–349, 364, 752, 831, 882
   views.py850100% 
workers/export
   actions.py11191%18
   worker.py46589%28–29, 54–55, 82
TOTAL444236592% 

Tests Skipped Failures Errors Time
231 0 💤 2 ❌ 0 🔥 1m 8s ⏱️

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: 1

Caution

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

⚠️ Outside diff range comments (1)
apps/fyle/signals.py (1)

21-35: Logic is unreachable due to post_save timing.

The existence check on line 25 (if existing_expense_group_setting:) will always be True in a post_save context because instance was just saved. More critically, the state-change comparisons on lines 29 and 33 will always evaluate to False because both existing_expense_group_setting and instance reference the same saved state.

Consequence: The async expense pull tasks on lines 31 and 35 will never execute, breaking the configuration-triggered expense import functionality described in the PR title ("fix: update configuration <> expense pull").

This confirms the critical issue flagged in the previous comment: the signal handler must be reverted to pre_save or substantially refactored to work with post_save.

Note: The business logic itself (checking for PAID → PAYMENT_PROCESSING and PAID → APPROVED transitions) appears correct and well-structured, but it's incompatible with the current signal timing.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6a85e4 and b4221d9.

📒 Files selected for processing (1)
  • apps/fyle/signals.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/fyle/signals.py (2)
apps/fyle/enums.py (1)
  • FundSourceEnum (6-11)
apps/fyle/models.py (1)
  • ExpenseGroupSettings (295-445)
🪛 Ruff (0.14.0)
apps/fyle/signals.py

17-17: Unused function argument: sender

(ARG001)


17-17: Unused function argument: kwargs

(ARG001)

⏰ 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: pytest
🔇 Additional comments (1)
apps/fyle/signals.py (1)

3-6: Verify library enum definitions in apps/fyle/signals.py lines 3–6
Ensure that FundSourceEnum and ExpenseImportSourceEnum imported from fyle_accounting_library.fyle_platform.enums match your local enum values (e.g. PERSONAL, CCC) to prevent runtime mismatches.

Comment on lines +16 to +17
@receiver(post_save, sender=ExpenseGroupSettings)
def run_post_save_expense_group_setting_triggers(sender, instance: ExpenseGroupSettings, **kwargs):
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: post_save breaks change detection logic.

Switching from pre_save to post_save fundamentally breaks the change-detection logic on lines 29 and 33. In a post_save signal:

  1. instance is the already-saved object with new values
  2. Querying the database (lines 21-23) returns the same object with the same new values
  3. Comparisons like existing_expense_group_setting.reimbursable_expense_state != instance.reimbursable_expense_state will always be False
  4. The async tasks on lines 31 and 35 will never be triggered

In pre_save, the logic worked because:

  • instance had the new values (not yet saved)
  • The database query returned the old values
  • The comparison detected actual changes

Solution options:

Option 1 (recommended): Revert to pre_save if there's no compelling reason for post_save.

-from django.db.models.signals import post_save
+from django.db.models.signals import pre_save
 from django.dispatch import receiver
-@receiver(post_save, sender=ExpenseGroupSettings)
-def run_post_save_expense_group_setting_triggers(sender, instance: ExpenseGroupSettings, **kwargs):
+@receiver(pre_save, sender=ExpenseGroupSettings)
+def run_pre_save_expense_group_setting_triggers(sender, instance: ExpenseGroupSettings, **kwargs):
     """
-    Run post save expense group setting triggers
+    Run pre save expense group setting triggers
     """

Option 2: If post_save is required, track the previous state using update_fields:

 @receiver(post_save, sender=ExpenseGroupSettings)
 def run_post_save_expense_group_setting_triggers(sender, instance: ExpenseGroupSettings, **kwargs):
     """
     Run post save expense group setting triggers
     """
+    # Skip if this is a new instance
+    if kwargs.get('created', False):
+        return
+    
+    # Only proceed if reimbursable_expense_state or ccc_expense_state were updated
+    update_fields = kwargs.get('update_fields', None)
+    if update_fields and not {'reimbursable_expense_state', 'ccc_expense_state'} & set(update_fields):
+        return
+    
-    existing_expense_group_setting = ExpenseGroupSettings.objects.filter(
-        workspace_id=instance.workspace_id
-    ).first()
+    # Retrieve the old state from the database before the save
+    # Note: This won't work in post_save as shown - you'd need to cache old values in __init__
+    # or use a django-dirtyfields library or similar approach

Note: Option 2 requires additional refactoring to properly track previous state. The update_fields check alone isn't sufficient because you still need the old values for comparison.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.0)

17-17: Unused function argument: sender

(ARG001)


17-17: Unused function argument: kwargs

(ARG001)

🤖 Prompt for AI Agents
apps/fyle/signals.py lines 16-17: switching the signal to post_save breaks
change-detection because instance and DB both reflect new values so comparisons
never detect changes; restore the original behavior by reverting the decorator
to pre_save so instance contains new values and DB returns old values for
correct diffing, or if post_save is mandatory, add a pre_save handler that
snapshots the existing DB state onto the instance (e.g.,
instance._pre_save_snapshot = ExpenseGroupSettings.objects.get(pk=instance.pk)
when pk exists) and then in the post_save handler compare instance against
instance._pre_save_snapshot to trigger the async tasks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants