Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Journal Entry Export #52

Merged
merged 17 commits into from
Jan 17, 2024
Merged

Journal Entry Export #52

merged 17 commits into from
Jan 17, 2024

Conversation

ruuushhh
Copy link
Contributor

@ruuushhh ruuushhh commented Dec 15, 2023

Summary by CodeRabbit

  • New Features

    • Introduced new models for managing accounting exports, journal entries, purchase invoices, and expense filtering.
    • Implemented export functionality for accounting data to external systems.
    • Added error handling and logging for business central operations.
    • Created functionality for updating accounting export summaries.
  • Enhancements

    • Improved type choices for accounting exports.
    • Enhanced expense import and synchronization processes.
    • Updated URL paths for better clarity and consistency.
  • Bug Fixes

    • Fixed method usage for checking the existence of related accounting exports.
    • Corrected the retrieval of item identifiers during data synchronization.
  • Documentation

    • Updated API endpoints documentation to reflect new views and changes.
  • Refactor

    • Reorganized imports and removed unused code across various modules.
    • Streamlined code for importing and mapping employee data.
  • Dependencies

    • Upgraded ms-dynamics-business-central-sdk and fyle-accounting-mappings to newer versions.
  • Tests

    • Modified test configurations and fixtures to align with the latest changes.
    • Enhanced test cases for improved coverage and error handling.

Copy link

coderabbitai bot commented Dec 15, 2023

Walkthrough

The updates primarily involve enhancements to the accounting export functionality, with a focus on refining the data models and export processes for accounting data. Changes include adjustments to model choices, error handling, and data export tasks, as well as the introduction of new models for managing journal entries and purchase invoices. Additionally, there's a new emphasis on syncing and updating settings related to Business Central, and the handling of employee and expense data has been improved.

Changes

File Path Change Summary
apps/accounting_exports/migrations/... Introduced initial migrations for accounting export models.
apps/accounting_exports/models.py Modified TYPE_CHOICES and updated create_accounting_export function.
apps/business_central/actions.py Added function to update accounting export summaries.
apps/business_central/exceptions.py Introduced error handling for business central operations.
apps/business_central/exports/... Added classes and methods for exporting accounting data and handling related models.
apps/business_central/migrations/... Created and updated migrations for journal entries and purchase invoices.
apps/business_central/models.py Imported new models for journal entries and purchase invoices.
apps/business_central/utils.py Updated methods for syncing and posting data, and added new helper methods.
apps/fyle/migrations/0001_initial.py Introduced models for managing and filtering expenses.
apps/fyle/models.py Modified create_expense_objects function.
apps/fyle/tasks.py Reorganized code in import_expenses function.
apps/fyle/urls.py Updated URL path for AccoutingExportSyncView and added SkippedExpenseView.
apps/mappings/migrations/0001_initial.py Introduced migration for the "ImportLog" model.
apps/mappings/imports/modules/... Updated import and mapping related modules for employees and base functionality.
apps/workspaces/migrations/... Altered and added fields related to syncing, export, and import settings.
apps/workspaces/models.py Added new fields and class for export settings and last export details.
apps/workspaces/serializers.py Added logic for handling export settings and new serializers.
apps/workspaces/tasks.py, triggers.py, views.py Added functions for export and synchronization and updated views.
requirements.txt Updated versions of dependencies.
tests/... Updated test functions and fixtures to align with changes.

Poem

🐇✨
In the land of code where the data hops,
A rabbit fine-tuned the accounting ops.
With models tweaked and exports aligned,
The numbers dance, perfectly designed.
🎉📈

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

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>.
    • Generate unit-tests for this file.
  • 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 tests 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 generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

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 as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • 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/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

Tests Skipped Failures Errors Time
47 0 💤 0 ❌ 0 🔥 4.685s ⏱️

Copy link

github-actions bot commented Jan 4, 2024

Tests Skipped Failures Errors Time
47 0 💤 3 ❌ 0 🔥 4.393s ⏱️

Copy link

github-actions bot commented Jan 9, 2024

Tests Skipped Failures Errors Time
47 0 💤 1 ❌ 0 🔥 5.465s ⏱️

@ruuushhh ruuushhh changed the base branch from export-base-files to master January 9, 2024 04:15
Copy link

github-actions bot commented Jan 9, 2024

Tests Skipped Failures Errors Time
48 0 💤 1 ❌ 0 🔥 5.975s ⏱️

Copy link

github-actions bot commented Jan 9, 2024

Tests Skipped Failures Errors Time
48 0 💤 1 ❌ 0 🔥 5.709s ⏱️

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.

Review Status

Actionable comments generated: 6

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 9d2d9bc and 52cab90.
Files selected for processing (28)
  • apps/accounting_exports/migrations/0001_initial.py (1 hunks)
  • apps/accounting_exports/models.py (2 hunks)
  • apps/business_central/actions.py (1 hunks)
  • apps/business_central/exceptions.py (1 hunks)
  • apps/business_central/exports/accounting_export.py (1 hunks)
  • apps/business_central/exports/base_model.py (1 hunks)
  • apps/business_central/exports/helpers.py (1 hunks)
  • apps/business_central/exports/journal_entry/models.py (1 hunks)
  • apps/business_central/exports/journal_entry/queues.py (1 hunks)
  • apps/business_central/exports/journal_entry/tasks.py (1 hunks)
  • apps/business_central/exports/purchase_invoice/models.py (1 hunks)
  • apps/business_central/migrations/0001_initial.py (1 hunks)
  • apps/business_central/models.py (1 hunks)
  • apps/business_central/serializers.py (1 hunks)
  • apps/business_central/utils.py (6 hunks)
  • apps/fyle/migrations/0001_initial.py (1 hunks)
  • apps/fyle/models.py (2 hunks)
  • apps/fyle/tasks.py (2 hunks)
  • apps/mappings/migrations/0001_initial.py (1 hunks)
  • apps/workspaces/migrations/0002_alter_workspace_ms_business_central_accounts_last_synced_at_and_more.py (1 hunks)
  • apps/workspaces/models.py (4 hunks)
  • apps/workspaces/serializers.py (3 hunks)
  • apps/workspaces/tasks.py (1 hunks)
  • apps/workspaces/urls.py (1 hunks)
  • apps/workspaces/views.py (2 hunks)
  • ms_business_central_api/exceptions.py (1 hunks)
  • ms_business_central_api/models/fields.py (2 hunks)
  • requirements.txt (1 hunks)
Files skipped from review due to trivial changes (2)
  • apps/business_central/models.py
  • apps/fyle/models.py
Additional comments: 63
ms_business_central_api/exceptions.py (1)
  • 7-22: The BulkError class is well-defined and documented. It correctly inherits from the base Exception class and overrides the __str__ method to return the message representation. This should work well for representing bulk operation errors in the application.
requirements.txt (2)
  • 31-31: The version of ms-dynamics-business-central-sdk has been updated to 1.3.3. Ensure that this version is compatible with the application and that all necessary testing has been performed to confirm that the update does not introduce any breaking changes.

  • 35-35: The version of fyle-accounting-mappings has been updated to 1.29.0. Similar to the previous comment, verify that this version is compatible with the application and that all necessary testing has been performed to confirm that the update does not introduce any breaking changes.

apps/business_central/actions.py (1)
  • 6-21: The update_accounting_export_summary function correctly updates the summary counts for failed and successful accounting exports. It's good to see that the function is concise and uses Django's ORM effectively to perform the necessary queries and updates.
apps/workspaces/urls.py (2)
  • 9-9: The TriggerExportsView has been added to the list of imported views. This is necessary for the new URL path that has been introduced.

  • 17-17: A new URL path <int:workspace_id>/exports/trigger/ has been added, which is set to use the TriggerExportsView. This change aligns with the PR objectives to enhance the application's functionality for triggering exports. Ensure that the corresponding view handles the requests as expected and that appropriate permissions are enforced.

apps/business_central/exports/journal_entry/queues.py (1)
  • 10-45: The check_accounting_export_and_start_import function is designed to enqueue accounting exports for processing. It filters out exports that are already in progress, complete, or queued, and only includes those that are related to journal entries and have not been exported yet. The function then creates a chain of tasks to be executed. This is a good use of Django Q's chaining feature to manage task execution.
apps/mappings/migrations/0001_initial.py (1)
  • 17-36: The initial migration for the mappings app is well-structured and includes all necessary fields for the ImportLog model. The unique constraint on the combination of workspace and attribute_type is a good practice to avoid duplicate entries.
apps/business_central/exports/accounting_export.py (1)
  • 14-79: The AccountingDataExporter class provides a clear structure for subclasses to implement the post method for exporting data to external systems. The use of Django's ORM and transactions is appropriate, and the error handling within the create_business_central_object method is comprehensive. Ensure that subclasses properly implement the post method and that the error handling is consistent with the application's standards.
apps/business_central/exceptions.py (1)
  • 16-80: The error handling functions and decorator in exceptions.py are well-implemented. They log the errors, update the AccountingExport object's status and details, and call update_accounting_export_summary to reflect the changes in the summary. This is a robust approach to handling exceptions that may occur during the export process.
apps/fyle/tasks.py (1)
  • 78-89: The import_expenses function has been updated to include a transaction block, which is a good practice to ensure atomicity of operations. The changes also include the use of the construct_expense_filter_query helper function and the reordering of operations within the function. Ensure that the transaction block is tested thoroughly to confirm that it behaves as expected in all scenarios, including error conditions.
apps/workspaces/views.py (2)
  • 18-18: The import of export_to_business_central is necessary for the new TriggerExportsView to function correctly. This change is consistent with the PR objectives to enhance the application's export functionality.

  • 144-154: The new TriggerExportsView class with a post method has been introduced to trigger exports creation. This view calls the export_to_business_central function, which should encapsulate the logic for initiating the export process. Ensure that this view is properly secured and that only authorized users can trigger exports.

apps/business_central/migrations/0001_initial.py (1)
  • 19-58: The initial migration for the business_central app correctly sets up the JournalEntry and JournalEntryLineItems models with appropriate fields and database table names. The relationships between models are established using foreign keys, and the fields are well-documented with help texts.
apps/business_central/serializers.py (1)
  • 81-81: The addition of "COMPANY" to the list of fields in the format_business_central_fields function is consistent with the PR objectives to enhance model structures and data handling. This change will affect the control flow and logic related to this function, so ensure that the inclusion of the "COMPANY" attribute type is tested and works as intended in the application.
apps/business_central/exports/journal_entry/tasks.py (3)
  • 13-17: The class ExportJournalEntry is well-documented and extends AccountingDataExporter as expected.

  • 24-28: The method trigger_export is concise and delegates the import process to another function. Ensure that check_accounting_export_and_start_import is handling exceptions properly.

  • 64-87: The post method is responsible for exporting the journal entry to Business Central. It uses the bulk_post_journal_lineitems method from the BusinessCentralConnector. Ensure that the bulk_post_journal_lineitems method has proper error handling and logging.

apps/business_central/exports/purchase_invoice/models.py (4)
  • 11-24: The PurchaseInvoice model is well-defined with appropriate fields and help texts. The use of custom fields like CustomDateTimeField and FloatNullField is noted.

  • 28-52: The class method create_or_update_object is used to create or update a PurchaseInvoice object. It uses the update_or_create method which is a good practice to avoid duplicates. Ensure that the update_or_create method is used correctly and handles exceptions as expected.

  • 55-69: The PurchaseInvoiceLineitems model is also well-defined with appropriate fields and help texts. It maintains a foreign key relationship with the PurchaseInvoice model.

  • 71-103: The class method create_or_update_object for PurchaseInvoiceLineitems is used to create or update line items. It uses the update_or_create method which is a good practice to avoid duplicates. Ensure that the update_or_create method is used correctly and handles exceptions as expected.

apps/business_central/exports/helpers.py (4)
  • 17-30: The function get_filtered_mapping retrieves a single mapping based on the provided criteria. It uses a dynamic filter which is a good practice for code reusability.

  • 33-78: The function validate_accounting_export checks for missing mappings and raises a BulkError if any are found. It also creates Error objects for each missing mapping. Ensure that the BulkError exception is handled properly where this function is called.

  • 81-86: The function resolve_errors_for_exported_accounting_export marks errors as resolved for a given accounting export. It's a straightforward update operation on the Error model.

  • 89-128: The function load_attachments is responsible for loading attachments from Fyle and posting them to Business Central. Ensure that the post_attachments method of BusinessCentralConnector has proper error handling and logging.

apps/business_central/exports/base_model.py (4)
  • 11-17: The BaseExportModel class provides common fields and methods for export models. It's abstract and not meant to be instantiated directly.

  • 22-55: The method get_expense_comment constructs a comment for an expense. It uses the expense_memo_structure from AdvancedSetting to build the comment. Ensure that the expense_memo_structure is configured correctly and that the method handles any potential exceptions.

  • 57-73: The method get_total_amount calculates the total amount of expenses associated with an AccountingExport. It uses Django's aggregate function which is a good practice for such calculations.

  • 75-98: The method get_invoice_date retrieves the invoice date from an AccountingExport. It checks for various keys in the description field. Ensure that the description field is structured correctly and contains the expected keys.

apps/business_central/exports/journal_entry/models.py (4)
  • 17-28: The JournalEntry model is well-defined with appropriate fields and help texts. The use of custom fields like CustomDateTimeField and FloatNullField is noted.

  • 33-62: The class method create_or_update_object is used to create or update a JournalEntry object. It uses the update_or_create method which is a good practice to avoid duplicates. Ensure that the update_or_create method is used correctly and handles exceptions as expected.

  • 65-77: The JournalEntryLineItems model is also well-defined with appropriate fields and help texts. It maintains a foreign key relationship with the JournalEntry model.

  • 82-121: The class method create_or_update_object for JournalEntryLineItems is used to create or update line items. It uses the update_or_create method which is a good practice to avoid duplicates. Ensure that the update_or_create method is used correctly and handles exceptions as expected.

apps/accounting_exports/migrations/0001_initial.py (3)
  • 20-41: The AccountingExport model migration is created with appropriate fields and options. Ensure that the migration is applied without issues and that the database schema matches the model definitions.

  • 42-59: The Error model migration is created with appropriate fields and options. Ensure that the migration is applied without issues and that the database schema matches the model definitions.

  • 60-77: The AccountingExportSummary model migration is created with appropriate fields and options. Ensure that the migration is applied without issues and that the database schema matches the model definitions.

apps/workspaces/serializers.py (3)
  • 10-11: The imports of AccountingExportSummary and BusinessCentralConnector are new additions to this file. Ensure that these classes are used appropriately within the serializers and that their methods are called with the correct parameters.

  • 67-67: The creation of an AccountingExportSummary instance upon workspace creation is a new addition. This ensures that an export summary is available for each workspace, which is likely a part of the new feature set for accounting exports.

  • 113-122: The conditional block starting at line 113 checks if the export settings require a default journal entry folder and if it's not already set. If these conditions are met, it uses the BusinessCentralConnector to create one. This is a new feature that integrates with Business Central for journal entries. Ensure that the create_default_journal_entry_folder method handles exceptions and errors appropriately, as it interacts with an external system.

apps/business_central/utils.py (5)
  • 1-2: The import of the base64 and typing modules suggests new functionality related to encoding and type annotations. This is likely used in the new methods for handling attachments and bulk operations.

  • 75-75: The change in the _sync_data method to use item['number'] if available, otherwise item['id'], for the destination_id is a logical update that provides more specific identification for synced items when available.

  • 97-97: The replacement of Workspace.objects.get(id=self.workspace_id) with self.connection.set_company_id(workspace.business_central_company_id) in various sync methods is a refactoring that centralizes the setting of the company ID for the Business Central connection. This is a cleaner approach and reduces code duplication.

Also applies to: 109-109, 121-121, 133-133

  • 140-150: The addition of the create_default_journal_entry_folder method is part of the new functionality for handling journal entries. It's important to ensure that this method includes error handling for the external API call to Business Central.

  • 152-162: The bulk_post_journal_lineitems method is a new addition that allows for bulk posting of journal line items to Business Central. This method should be reviewed to ensure that it correctly formats the payload and handles any potential errors from the external service.

apps/workspaces/tasks.py (3)
  • 15-75: The run_import_export task has been modified to include a new export mode parameter and to update the AccountingExportSummary with the last exported time and mode. This is part of the new functionality to track export operations. Ensure that the export_mode parameter is always passed correctly where this task is triggered.

  • 78-114: The schedule_sync task has been updated to handle scheduling synchronization tasks. It now accepts additional parameters for email notifications. Ensure that the email lists are being used correctly and that the schedule is being set or deleted as per the schedule_enabled flag.

  • 117-168: The export_to_business_central task is a new addition that seems to handle the export of expenses to Business Central. It's important to ensure that this task is scheduled and executed correctly, and that it handles any exceptions that may occur during the export process.

apps/accounting_exports/models.py (2)
  • 23-24: The TYPE_CHOICES in the AccountingExport model have been updated to replace 'INVOICES' with 'PURCHASE_INVOICES' and 'DIRECT_COST' with 'JOURNAL_ENTRY'. This change reflects the new types of accounting data that the application will handle. Ensure that all references to these choices throughout the application are updated accordingly.

  • 126-128: The create_accounting_export function has been updated to change 'last_spent_at' to 'LAST_SPENT_AT'. This seems to be a case normalization change. Ensure that all parts of the application that rely on this field are updated to use the new case.

apps/fyle/migrations/0001_initial.py (1)
  • 10-87: The migration file creates new models ExpenseFilter and Expense. Ensure that these models and their fields match the intended design and that the migration is consistent with the current state of the database schema.
apps/workspaces/models.py (4)
  • 133-136: The addition of EXPORT_MODE_CHOICES with options 'MANUAL' and 'AUTO' seems appropriate for specifying the mode of export. This is a straightforward enumeration that should not introduce issues.

  • 200-200: The addition of the default_journal_entry_folder_id field to the ExportSetting model is consistent with the PR objectives to enhance export functionalities. Ensure that any existing data is migrated appropriately to accommodate this new field.

Verification successful

The output from the executed script provides the contents of a migration file that includes the addition of the default_journal_entry_folder_id field to the ExportSetting model. This confirms that a migration has been created to accommodate the new field in the database schema, which aligns with the PR objectives to enhance export functionalities.

Based on the information provided, the review comment regarding the addition of the default_journal_entry_folder_id field and the need for a migration to accommodate this new field is verified as accurate.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that a migration file exists for adding the default_journal_entry_folder_id field
fd '0003_add_default_journal_entry_folder_id.py' --exec cat {}

Length of output: 64



Script:

#!/bin/bash
# Search for migration files related to the ExportSetting model that might include the new field
fd '.*exportsetting.*\.py' apps/workspaces/migrations/ --exec cat {}

Length of output: 72



Script:

#!/bin/bash
# Search for any migration files in the apps/workspaces/migrations directory
fd '.*\.py' apps/workspaces/migrations/ --exec cat {}

Length of output: 15253

* 227-227: Renaming `schedule_start_datetime` to `start_datetime` in the `AdvancedSetting` model is a simplification that could improve readability. However, it's important to ensure that this change is reflected everywhere in the codebase, including any background tasks or services that rely on this field.
  • 238-251: The addition of the LastExportDetail model aligns with the PR objectives to store details of the last export. This model includes fields for timestamps, mode, and counts of exports, which are relevant for tracking export operations. Ensure that the fields are being populated correctly during the export process.
apps/workspaces/migrations/0002_alter_workspace_ms_business_central_accounts_last_synced_at_and_more.py (8)
  • 17-21: The migration operation to alter the ms_business_central_accounts_last_synced_at field in the Workspace model to allow null values is consistent with the need to handle cases where accounts may not have been synced yet.

  • 22-25: The migration operation to alter the onboarding_state field in the Workspace model to include a default function and allow null values is a good practice for ensuring data integrity and handling new workspaces that have not yet set an onboarding state.

  • 27-42: The creation of the LastExportDetail model in the migration file is consistent with the changes in the models.py file. It's important to ensure that this migration is applied without issues and that the table is created successfully in the database.

  • 44-57: The creation of the ImportSetting model in the migration file is consistent with the changes in the models.py file. Ensure that the default values for the boolean fields are set correctly and that they reflect the intended functionality.

  • 58-71: The creation of the FyleCredential model in the migration file is consistent with the changes in the models.py file. Ensure that the refresh token and cluster domain fields are handled securely and that sensitive information is not logged or exposed.

  • 73-102: The creation of the ExportSetting model in the migration file is consistent with the changes in the models.py file. Ensure that the new fields and choices are consistent with the application's requirements and that any necessary data migrations are handled.

Verification successful

The migration file 0002_alter_workspace_ms_business_central_accounts_last_synced_at_and_more.py includes the creation of the ExportSetting model with the new fields as described in the review comment. There are no explicit data migration operations within this migration file, which suggests that either there is no existing data to migrate or that data migrations are handled elsewhere. The review comment is partially verified; the creation of the ExportSetting model is consistent with the changes in the models.py file, but without further context, it's not possible to fully verify the handling of data migrations.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the new fields in ExportSetting are used correctly and that data migrations are handled.
rg 'ExportSetting' --type python

Length of output: 63



Script:

#!/bin/bash
# Correcting the file type to 'py' and searching for 'ExportSetting' usage in Python files.
rg 'ExportSetting' --type py

Length of output: 3658



Script:

#!/bin/bash
# Search for data migration operations related to the 'ExportSetting' model.
rg --type py 'ExportSetting' | rg 'migrations'

Length of output: 182



Script:

#!/bin/bash
# View the contents of the migration file that mentions 'ExportSetting'.
cat apps/workspaces/migrations/0002_alter_workspace_ms_business_central_accounts_last_synced_at_and_more.py

Length of output: 12444

* 104-117: The creation of the `BusinessCentralCredentials` model in the migration file is consistent with the changes in the `models.py` file. Ensure that the handling of the refresh token and the `is_expired` flag is implemented correctly in the application logic.
Verification successful

The search results indicate that the refresh_token and is_expired fields of the BusinessCentralCredentials model are indeed being used throughout the application. The refresh_token is being set and updated in various places, such as in the helpers.py file within the workspaces app. The is_expired flag is also being managed, specifically in the helpers.py file where it is reset to False when the refresh token is updated.

The presence of these fields in the migration file, model definitions, and their usage in application logic suggests that the handling of the refresh token and the is_expired flag has been implemented as part of the application's functionality.

Based on the information gathered, it appears that the handling of the BusinessCentralCredentials model, particularly the refresh token and the is_expired flag, is implemented correctly in the application logic.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that BusinessCentralCredentials is used correctly in the application logic.
rg 'BusinessCentralCredentials' --type python

Length of output: 76



Script:

#!/bin/bash
# Correcting the file type and re-running the search for 'BusinessCentralCredentials'
rg 'BusinessCentralCredentials' --type py

Length of output: 4767



Script:

#!/bin/bash
# Search for methods that handle the refresh token and the is_expired flag in BusinessCentralCredentials
rg 'refresh_token|is_expired' --type py

Length of output: 5082

* 119-136: The creation of the `AdvancedSetting` model in the migration file is consistent with the changes in the `models.py` file. Ensure that the schedule-related fields are used correctly and that the migration does not disrupt any existing scheduling functionality.
Verification successful

The search results indicate that the schedule_is_enabled, start_datetime, and interval_hours fields of the AdvancedSetting model are being referenced and potentially modified within the tasks.py file. This suggests that the schedule-related fields are actively used in the application's task scheduling logic.

Based on the output, there are no indications of issues with the usage of these fields. The migration should not disrupt existing scheduling functionality as long as it correctly reflects these fields and their intended use within the application logic, which seems to be the case here.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that AdvancedSetting's schedule-related fields are used correctly.
rg 'AdvancedSetting' --type python -C 5 | rg 'schedule'

Length of output: 84



Script:

#!/bin/bash
# Search for references to the schedule-related fields within the AdvancedSetting model across Python files.
rg 'AdvancedSetting' -C 5 | rg 'schedule_is_enabled|start_datetime|schedule_id|interval_hours'

Length of output: 408

apps/business_central/utils.py Outdated Show resolved Hide resolved
Comment on lines 38 to 40
"""
Todo: Add last export details
"""
Copy link

Choose a reason for hiding this comment

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

There is a TODO comment within the check_accounting_export_and_start_import function regarding adding last export details. This should be addressed before the final release to ensure that the application correctly tracks the last export details.

Comment on lines 30 to 62
def __construct_journal_entry(self, body: JournalEntry, lineitems: JournalEntryLineItems) -> Dict:
'''
Construct the payload for the direct invoice.
:param expense_report: ExpenseReport object extracted from database
:param expense_report_lineitems: ExpenseReportLineitem objects extracted from database
:return: constructed expense_report
'''
batch_journal_entry_payload = []

journal_entry_payload = {
'accountNumber': body.accounts_payable_account_id,
'postingDate': body.invoice_date,
'documentNumber': body.document_number,
'amount': body.amount,
'comment': body.comment,
'description': body.description
}

batch_journal_entry_payload.append(journal_entry_payload)

for lineitem in lineitems:
journal_entry_lineitem_payload = {
'accountNumber': lineitem.accounts_payable_account_id,
'postingDate': lineitem.invoice_date,
'documentNumber': lineitem.document_number,
'amount': lineitem.amount,
'comment': lineitem.comment,
'description': lineitem.description if lineitem.description else ''
}

batch_journal_entry_payload.append(journal_entry_lineitem_payload)

return batch_journal_entry_payload
Copy link

Choose a reason for hiding this comment

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

The method __construct_journal_entry constructs the payload for journal entries. It's private and seems to be used within the class. However, the method name __construct_journal_entry suggests it's a constructor, which it is not. Consider renaming it to construct_journal_entry_payload for clarity.

apps/business_central/exports/journal_entry/tasks.py Outdated Show resolved Hide resolved
Comment on lines 100 to 104
def get_vendor_id(accounting_export: AccountingExport) -> str:
return ""

def get_employee_id(accounting_export: AccountingExport) -> str:
return ""
Copy link

Choose a reason for hiding this comment

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

The methods get_vendor_id and get_employee_id are placeholders returning empty strings. If these methods are meant to be implemented, add a TODO comment or implement them. If they are not needed, consider removing them to avoid confusion.

ms_business_central_api/models/fields.py Show resolved Hide resolved
Copy link

github-actions bot commented Jan 9, 2024

Tests Skipped Failures Errors Time
48 0 💤 0 ❌ 0 🔥 4.686s ⏱️

@codecov-commenter
Copy link

Codecov Report

Attention: 282 lines in your changes are missing coverage. Please review.

Comparison is base (9d2d9bc) 90.96% compared to head (f3b5841) 82.81%.

Files Patch % Lines
apps/workspaces/tasks.py 14.63% 70 Missing ⚠️
apps/business_central/exceptions.py 28.07% 41 Missing ⚠️
apps/business_central/exports/helpers.py 30.00% 35 Missing ⚠️
apps/business_central/exports/base_model.py 35.41% 31 Missing ⚠️
apps/business_central/exports/accounting_export.py 30.76% 27 Missing ⚠️
...ps/business_central/exports/journal_entry/tasks.py 42.10% 22 Missing ⚠️
...s/business_central/exports/journal_entry/models.py 64.00% 18 Missing ⚠️
...s/business_central/exports/journal_entry/queues.py 31.57% 13 Missing ⚠️
apps/business_central/utils.py 48.00% 13 Missing ⚠️
apps/business_central/actions.py 27.27% 8 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #52      +/-   ##
==========================================
- Coverage   90.96%   82.81%   -8.16%     
==========================================
  Files          51       61      +10     
  Lines        2513     2950     +437     
==========================================
+ Hits         2286     2443     +157     
- Misses        227      507     +280     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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.

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 52cab90 and f3b5841.
Files selected for processing (1)
  • tests/test_workspaces/test_view.py (4 hunks)
Additional comments: 1
tests/test_workspaces/test_view.py (1)
  • 75-92: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [78-110]

The test_export_settings function has been updated to include additional parameters and a mocker.patch call. It's important to ensure that the mocker.patch is correctly targeting the intended method and that the mock return value aligns with the expected data structure from the actual API call. Additionally, the hardcoded workspace_id of 1 is used, which may not be reliable for all test environments. Consider using a dynamic retrieval of workspace_id from the create_temp_workspace fixture if it's designed to provide a valid workspace for testing.

Verification successful

The mocker.patch call in the test_export_settings function is correctly targeting the intended method, as confirmed by the search results showing a single match in the file under review.

The hardcoded workspace_id of 1 is used extensively throughout the test suite, suggesting that the tests are designed with this assumption. While this is a common pattern in the test suite, it's important to ensure that the test environment is set up to handle this correctly to avoid brittle tests.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the mocker.patch is correctly targeting the intended method
rg --type py "dynamics.apis.Journals.post"

# Verify that the hardcoded workspace_id of 1 is not used elsewhere in a way that could cause conflicts or unreliable tests
rg --type py "workspace_id=1"

Length of output: 14228

Comment on lines 127 to 136
assert export_settings.credit_card_expense_state == 'PAID'
assert export_settings.credit_card_expense_grouped_by == 'EXPENSE'
assert export_settings.credit_card_expense_date == 'CREATED_AT'
assert export_settings.default_reimbursable_account_name == 'reimbursable account'
assert export_settings.default_reimbursable_account_id == '123'
assert export_settings.default_ccc_credit_card_account_name == 'CCC credit card account'
assert export_settings.default_ccc_credit_card_account_id == '123'
assert export_settings.default_reimbursable_account_name == 'reimbursable account'
assert export_settings.default_reimbursable_account_id == '123'
assert export_settings.default_reimbursable_credit_card_account_name == 'reimbursable credit card account'
assert export_settings.default_reimbursable_credit_card_account_id == '342'
assert export_settings.default_vendor_name == 'Nilesh'
Copy link

Choose a reason for hiding this comment

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

Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [119-133]

The assertions for export_settings are well-placed to ensure that the settings are correctly updated. However, the hardcoded workspace_id of 1 is used again for retrieving the export_settings object. This could lead to flaky tests if the workspace with ID 1 does not exist or is not the intended workspace for the test. It would be better to use the workspace_id from the create_temp_workspace fixture or from the response of a workspace creation within the test.

- export_settings = ExportSetting.objects.filter(workspace_id=1).first()
+ # Assuming create_temp_workspace returns a workspace object with an id
+ workspace = create_temp_workspace()
+ export_settings = ExportSetting.objects.filter(workspace_id=workspace.id).first()

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.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between f3b5841 and eb9343f.
Files selected for processing (3)
  • apps/business_central/exports/journal_entry/tasks.py (1 hunks)
  • apps/business_central/utils.py (6 hunks)
  • ms_business_central_api/models/fields.py (2 hunks)
Files skipped from review as they are similar to previous changes (3)
  • apps/business_central/exports/journal_entry/tasks.py
  • apps/business_central/utils.py
  • ms_business_central_api/models/fields.py

@ruuushhh ruuushhh changed the title Export Models Journal Entry Export Jan 9, 2024
Copy link

github-actions bot commented Jan 9, 2024

Tests Skipped Failures Errors Time
48 0 💤 0 ❌ 0 🔥 4.683s ⏱️

Copy link

Tests Skipped Failures Errors Time
48 0 💤 1 ❌ 2 🔥 4.593s ⏱️

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.

Review Status

Actionable comments generated: 6

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between eb9343f and dbc13b0.
Files selected for processing (7)
  • apps/business_central/exports/base_model.py (1 hunks)
  • apps/business_central/exports/helpers.py (1 hunks)
  • apps/business_central/exports/journal_entry/models.py (1 hunks)
  • apps/business_central/exports/journal_entry/tasks.py (1 hunks)
  • apps/business_central/utils.py (6 hunks)
  • apps/workspaces/models.py (6 hunks)
  • apps/workspaces/serializers.py (3 hunks)
Additional comments: 23
apps/business_central/exports/helpers.py (2)
  • 17-30: The function get_filtered_mapping is designed to retrieve a mapping based on various filters. It correctly checks for the presence of source_id before deciding which filter to apply. The use of **filters to unpack the dictionary into filter arguments is a Pythonic way to handle dynamic query parameters.

  • 81-86: The function resolve_errors_for_exported_accounting_export marks all unresolved errors related to a specific accounting_export as resolved. This is a straightforward operation that uses Django's ORM capabilities effectively.

apps/business_central/exports/journal_entry/tasks.py (4)
  • 13-24: The ExportJournalEntry class is a subclass of AccountingDataExporter and is responsible for exporting journal entries to Business Central. The constructor initializes the body_model and lineitem_model which are used in the export process. The trigger_export method is a wrapper around the check_accounting_export_and_start_import function, which is not defined in this context but is assumed to be correct.

  • 30-69: The __construct_journal_entry method constructs the payload for journal entries. It creates a batch payload for both the journal entry and its line items. The method uses the provided JournalEntry and List[JournalEntryLineItems] objects to build the payload. The method is private, which is indicated by the double underscore prefix, and is only meant to be used within the class. The logic appears to be correct, and it properly handles the case where a line item's description might be None.

  • 71-95: The post method is responsible for posting the journal entry to Business Central. It constructs the payload, establishes a connection to Business Central, and posts the journal entry. It then iterates over the responses to upload attachments. The method assumes that the bulk_post_journal_lineitems method of the BusinessCentralConnector returns a response with a specific structure. It is important to ensure that this assumption is correct and that the BusinessCentralConnector is handling any exceptions that may occur during the posting process.

  • 98-108: The create_journal_entry function is a helper function that creates and exports a journal entry. It uses the ExportJournalEntry class to perform the export. The function is decorated with @handle_business_central_exceptions(), which is assumed to handle any exceptions that may occur during the export process. The function is straightforward and delegates the creation and export process to the ExportJournalEntry class.

apps/business_central/exports/base_model.py (4)
  • 11-21: The BaseExportModel class is an abstract Django model that provides common fields for export-related models. It includes created_at, updated_at, and a foreign key to the Workspace model. The class is marked as abstract, which is correct as it should not be instantiated on its own.

  • 57-73: The get_total_amount method calculates the total amount of expenses associated with an AccountingExport. It uses Django's ORM aggregate function to sum the amounts, which is an efficient way to perform this calculation. The method correctly handles the case where there may be no expenses by returning 0.0 if the total amount is None.

  • 75-98: The get_invoice_date method extracts the invoice date from the AccountingExport description. It checks for various keys and returns the corresponding value. The method falls back to the current date and time if none of the keys are present. This method assumes that the description is a dictionary with specific keys, which should be validated elsewhere in the code to ensure correctness.

  • 100-104: The get_vendor_id and get_journal_entry_account_id_type methods are hardcoded to return specific values. This is likely a placeholder or a default value. It's important to ensure that these values are correct and that there is a mechanism to configure them if necessary.

apps/business_central/exports/journal_entry/models.py (3)
  • 17-30: The JournalEntry model extends BaseExportModel and defines fields specific to a journal entry. The fields are well-defined with appropriate help texts. The accounting_export field is a one-to-one relationship with the AccountingExport model, which is correct for the intended use case.

  • 35-68: The create_or_update_object class method in the JournalEntry model is responsible for creating or updating a journal entry object. It calculates the total amount by summing the amounts of all expenses and negates it, which is a specific business logic. The method uses update_or_create to ensure that an existing object is updated if it matches the given criteria. The method assumes that the description field of accounting_export contains certain keys like claim_number or expense_number, which should be validated elsewhere in the code.

  • 71-133: The JournalEntryLineItems model also extends BaseExportModel and defines fields for individual line items of a journal entry. The create_or_update_object class method creates or updates line items for a journal entry. It retrieves the category mapping for each expense and constructs a comment using the get_expense_comment method. The method uses update_or_create similarly to the JournalEntry model and assumes the same structure for the description field of accounting_export.

apps/workspaces/serializers.py (2)
  • 67-68: The create method in the WorkspaceSerializer class creates an AccountingExportSummary object after creating a workspace. This is a straightforward creation of a related object upon the creation of a workspace.

  • 113-121: In the ExportSettingsSerializer class, the create method checks if the export settings are for journal entries and if the journal_entry_folder_id is not set. If these conditions are met, it creates a journal entry folder using the BusinessCentralConnector. This is a logical extension of the export settings creation process to ensure that necessary resources are created in Business Central.

apps/business_central/utils.py (5)
  • 75-75: The _sync_data method in the BusinessCentralConnector class has been updated to use item['number'] if available, otherwise item['id']. This change accommodates a potential difference in the data structure coming from Business Central. The conditional logic seems correct and should handle the data as expected.

  • 97-97: The methods sync_accounts, sync_vendors, sync_employees, and sync_locations have been updated to use set_company_id instead of directly accessing the company_id property. This change is likely due to a change in the Dynamics class API. It's important to ensure that the set_company_id method exists and functions as expected.

Also applies to: 109-109, 121-121, 133-133

  • 140-150: The create_journal_entry_folder method is a new addition that creates a default journal entry folder in Business Central. The payload and the POST request seem to be correctly formed. The method returns the ID of the created folder, which is the expected behavior.

  • 152-162: The bulk_post_journal_lineitems method is responsible for posting journal line items in bulk to Business Central. It retrieves the export settings and uses the journal_entry_folder_id to post the payload. The method assumes that the bulk_post method of the journal_line_items object in the Dynamics class exists and works as expected.

  • 164-188: The post_purchase_invoice method posts a purchase invoice and its line items to Business Central. It handles exceptions by deleting the created purchase invoice if an error occurs during the posting of line items. This is a good use of a try-except block to ensure clean-up in case of a failure. The method assumes that the purchase_invoices and purchase_invoice_line_items objects in the Dynamics class have post and bulk_post methods, respectively.

apps/workspaces/models.py (3)
  • 132-135: The EXPORT_MODE_CHOICES tuple has been added to provide choices for the export mode. This is a standard Django practice for defining choices for a model field.

  • 220-220: The AdvancedSetting model has been updated to rename schedule_start_datetime to start_datetime. This change is likely for consistency and clarity. The field definition remains the same, which is appropriate.

  • 231-244: The LastExportDetail class is a new addition to the models. It stores details about the last export, including the time, mode, and counts of successful and failed exports. This is a useful addition for tracking export operations and their outcomes.

Comment on lines 33 to 78
def validate_accounting_export(accounting_export: AccountingExport):
bulk_errors = []
row = 0

expenses = accounting_export.expenses.all()

for lineitem in expenses:
category = lineitem.category if (lineitem.category == lineitem.sub_category or lineitem.sub_category == None) else '{0} / {1}'.format(
lineitem.category, lineitem.sub_category)

category_attribute = ExpenseAttribute.objects.filter(
value=category,
workspace_id=accounting_export.workspace_id,
attribute_type='CATEGORY'
).first()

account = CategoryMapping.objects.filter(
source_category_id=category_attribute.id,
workspace_id=accounting_export.workspace_id
).first()

if not account:
bulk_errors.append({
'row': row,
'accounting_export_id': accounting_export.id,
'value': category,
'type': 'Category Mapping',
'message': 'Category Mapping not found'
})

if category_attribute:
Error.objects.update_or_create(
workspace_id=accounting_export.workspace_id,
expense_attribute=category_attribute,
defaults={
'type': 'CATEGORY_MAPPING',
'error_title': category_attribute.value,
'error_detail': 'Category mapping is missing',
'is_resolved': False
}
)

row = row + 1

if bulk_errors:
raise BulkError('Mappings are missing', bulk_errors)
Copy link

Choose a reason for hiding this comment

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

The function validate_accounting_export is responsible for validating the accounting_export object. It checks if the category mapping exists for each expense and logs an error if not found. The use of a bulk_errors list to collect all errors before raising a BulkError exception is a good practice as it allows for batch processing of errors. However, the variable row is incremented outside the conditional block, which means it will increment even if there is no error. This could lead to misleading row indices in the error messages.

- row = row + 1
+ if not account:
+     row = row + 1

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.

Suggested change
def validate_accounting_export(accounting_export: AccountingExport):
bulk_errors = []
row = 0
expenses = accounting_export.expenses.all()
for lineitem in expenses:
category = lineitem.category if (lineitem.category == lineitem.sub_category or lineitem.sub_category == None) else '{0} / {1}'.format(
lineitem.category, lineitem.sub_category)
category_attribute = ExpenseAttribute.objects.filter(
value=category,
workspace_id=accounting_export.workspace_id,
attribute_type='CATEGORY'
).first()
account = CategoryMapping.objects.filter(
source_category_id=category_attribute.id,
workspace_id=accounting_export.workspace_id
).first()
if not account:
bulk_errors.append({
'row': row,
'accounting_export_id': accounting_export.id,
'value': category,
'type': 'Category Mapping',
'message': 'Category Mapping not found'
})
if category_attribute:
Error.objects.update_or_create(
workspace_id=accounting_export.workspace_id,
expense_attribute=category_attribute,
defaults={
'type': 'CATEGORY_MAPPING',
'error_title': category_attribute.value,
'error_detail': 'Category mapping is missing',
'is_resolved': False
}
)
row = row + 1
if bulk_errors:
raise BulkError('Mappings are missing', bulk_errors)
def validate_accounting_export(accounting_export: AccountingExport):
bulk_errors = []
row = 0
expenses = accounting_export.expenses.all()
for lineitem in expenses:
category = lineitem.category if (lineitem.category == lineitem.sub_category or lineitem.sub_category == None) else '{0} / {1}'.format(
lineitem.category, lineitem.sub_category)
category_attribute = ExpenseAttribute.objects.filter(
value=category,
workspace_id=accounting_export.workspace_id,
attribute_type='CATEGORY'
).first()
account = CategoryMapping.objects.filter(
source_category_id=category_attribute.id,
workspace_id=accounting_export.workspace_id
).first()
if not account:
bulk_errors.append({
'row': row,
'accounting_export_id': accounting_export.id,
'value': category,
'type': 'Category Mapping',
'message': 'Category Mapping not found'
})
if category_attribute:
Error.objects.update_or_create(
workspace_id=accounting_export.workspace_id,
expense_attribute=category_attribute,
defaults={
'type': 'CATEGORY_MAPPING',
'error_title': category_attribute.value,
'error_detail': 'Category mapping is missing',
'is_resolved': False
}
)
row = row + 1
if bulk_errors:
raise BulkError('Mappings are missing', bulk_errors)

Comment on lines 89 to 130
def load_attachments(
business_central_connection: BusinessCentralConnector,
ref_id: str,
ref_type: str,
expense: Expense,
accounting_export: AccountingExport,
):
"""
Get attachments from fyle
:param business_central_connection: Business Central Connection
:param ref_id: object id
:param ref_type: type of object
:param expense_group: Expense group
"""
try:
fyle_credentials = FyleCredential.objects.get(
workspace_id=accounting_export.workspace_id
)
platform = PlatformConnector(fyle_credentials)

files_list = []

if expense.file_ids and len(expense.file_ids):
for file_id in expense.file_ids:
if file_id:
file_object = {"id": file_id}
files_list.append(file_object)

if files_list:
attachments = platform.files.bulk_generate_file_urls(files_list)

response = business_central_connection.post_attachments(ref_type, ref_id, attachments)
print(response)

except Exception:
error = traceback.format_exc()
logger.info(
"Attachment failed for accounting export id %s / workspace id %s \n Error: %s",
accounting_export.id,
accounting_export.workspace_id,
{"error": error},
)
Copy link

Choose a reason for hiding this comment

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

The function load_attachments is designed to upload attachments to Business Central. It retrieves Fyle credentials, constructs a list of file objects, and then uses the PlatformConnector to generate URLs for these files. The attachments are then posted to Business Central. The function includes error handling that logs the exception and uses a try-except block to catch any issues during the process. However, the print(response) statement on line 121 should be replaced with a logger call to ensure that the response is logged appropriately in production environments.

- print(response)
+ logger.info("Attachment upload response: %s", response)

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.

Suggested change
def load_attachments(
business_central_connection: BusinessCentralConnector,
ref_id: str,
ref_type: str,
expense: Expense,
accounting_export: AccountingExport,
):
"""
Get attachments from fyle
:param business_central_connection: Business Central Connection
:param ref_id: object id
:param ref_type: type of object
:param expense_group: Expense group
"""
try:
fyle_credentials = FyleCredential.objects.get(
workspace_id=accounting_export.workspace_id
)
platform = PlatformConnector(fyle_credentials)
files_list = []
if expense.file_ids and len(expense.file_ids):
for file_id in expense.file_ids:
if file_id:
file_object = {"id": file_id}
files_list.append(file_object)
if files_list:
attachments = platform.files.bulk_generate_file_urls(files_list)
response = business_central_connection.post_attachments(ref_type, ref_id, attachments)
print(response)
except Exception:
error = traceback.format_exc()
logger.info(
"Attachment failed for accounting export id %s / workspace id %s \n Error: %s",
accounting_export.id,
accounting_export.workspace_id,
{"error": error},
)
def load_attachments(
business_central_connection: BusinessCentralConnector,
ref_id: str,
ref_type: str,
expense: Expense,
accounting_export: AccountingExport,
):
"""
Get attachments from fyle
:param business_central_connection: Business Central Connection
:param ref_id: object id
:param ref_type: type of object
:param expense_group: Expense group
"""
try:
fyle_credentials = FyleCredential.objects.get(
workspace_id=accounting_export.workspace_id
)
platform = PlatformConnector(fyle_credentials)
files_list = []
if expense.file_ids and len(expense.file_ids):
for file_id in expense.file_ids:
if file_id:
file_object = {"id": file_id}
files_list.append(file_object)
if files_list:
attachments = platform.files.bulk_generate_file_urls(files_list)
response = business_central_connection.post_attachments(ref_type, ref_id, attachments)
logger.info("Attachment upload response: %s", response)
except Exception:
error = traceback.format_exc()
logger.info(
"Attachment failed for accounting export id %s / workspace id %s \n Error: %s",
accounting_export.id,
accounting_export.workspace_id,
{"error": error},
)

Comment on lines +22 to +55
def get_expense_comment(workspace_id, lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
workspace = Workspace.objects.get(id=workspace_id)
org_id = workspace.org_id

fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
cluster_domain = fyle_credentials.cluster_domain
workspace.cluster_domain = cluster_domain
workspace.save()

expense_link = '{0}/app/main/#/enterprise/view_expense/{1}?org_id={2}'.format(
cluster_domain, lineitem.expense_id, org_id
)

memo_structure = advance_setting.expense_memo_structure

details = {
'employee_email': lineitem.employee_email,
'merchant': '{0}'.format(lineitem.vendor) if lineitem.vendor else '',
'category': '{0}'.format(category) if lineitem.category else '',
'purpose': '{0}'.format(lineitem.purpose) if lineitem.purpose else '',
'report_number': '{0}'.format(lineitem.claim_number),
'spent_on': '{0}'.format(lineitem.spent_at.date()) if lineitem.spent_at else '',
'expense_link': expense_link
}

purpose = ''

for id, field in enumerate(memo_structure):
if field in details:
purpose += details[field]
if id + 1 != len(memo_structure):
purpose = '{0} - '.format(purpose)

return purpose
Copy link

Choose a reason for hiding this comment

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

The get_expense_comment method constructs a comment for an expense based on the expense_memo_structure from AdvancedSetting. It dynamically builds the comment by iterating over the memo structure and appending details from the expense. The method correctly handles the case where certain details may not be present. However, there is a bug in the loop that concatenates the fields; it appends a hyphen after every field, which will result in an incorrect comment format.

- purpose = '{0} - '.format(purpose)
+ if id + 1 != len(memo_structure):
+     purpose += ' - '

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.

Suggested change
def get_expense_comment(workspace_id, lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
workspace = Workspace.objects.get(id=workspace_id)
org_id = workspace.org_id
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
cluster_domain = fyle_credentials.cluster_domain
workspace.cluster_domain = cluster_domain
workspace.save()
expense_link = '{0}/app/main/#/enterprise/view_expense/{1}?org_id={2}'.format(
cluster_domain, lineitem.expense_id, org_id
)
memo_structure = advance_setting.expense_memo_structure
details = {
'employee_email': lineitem.employee_email,
'merchant': '{0}'.format(lineitem.vendor) if lineitem.vendor else '',
'category': '{0}'.format(category) if lineitem.category else '',
'purpose': '{0}'.format(lineitem.purpose) if lineitem.purpose else '',
'report_number': '{0}'.format(lineitem.claim_number),
'spent_on': '{0}'.format(lineitem.spent_at.date()) if lineitem.spent_at else '',
'expense_link': expense_link
}
purpose = ''
for id, field in enumerate(memo_structure):
if field in details:
purpose += details[field]
if id + 1 != len(memo_structure):
purpose = '{0} - '.format(purpose)
return purpose
def get_expense_comment(workspace_id, lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
workspace = Workspace.objects.get(id=workspace_id)
org_id = workspace.org_id
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
cluster_domain = fyle_credentials.cluster_domain
workspace.cluster_domain = cluster_domain
workspace.save()
expense_link = '{0}/app/main/#/enterprise/view_expense/{1}?org_id={2}'.format(
cluster_domain, lineitem.expense_id, org_id
)
memo_structure = advance_setting.expense_memo_structure
details = {
'employee_email': lineitem.employee_email,
'merchant': '{0}'.format(lineitem.vendor) if lineitem.vendor else '',
'category': '{0}'.format(category) if lineitem.category else '',
'purpose': '{0}'.format(lineitem.purpose) if lineitem.purpose else '',
'report_number': '{0}'.format(lineitem.claim_number),
'spent_on': '{0}'.format(lineitem.spent_at.date()) if lineitem.spent_at else '',
'expense_link': expense_link
}
purpose = ''
for id, field in enumerate(memo_structure):
if field in details:
purpose += details[field]
if id + 1 != len(memo_structure):
purpose += ' - '
return purpose

Comment on lines +190 to +217
def post_attachments(
self, ref_type: str, ref_id: str, attachments: List[Dict]
) -> List:
"""
Link attachments to objects Business Central
:param ref_id: object id
:param ref_type: type of object
:param attachments: attachment[dict()]
"""
responses = []
if len(attachments):
for attachment in attachments:
data = {
"parentId": ref_id,
"fileName": "{0}_{1}".format(attachment["id"], attachment["name"]),
"parentType": ref_type
}
post_response = self.connection.attachments.post(data)

self.connection.attachments.upload(
post_response["id"],
attachment["content_type"],
base64.b64decode(attachment["download_url"])
)

responses.append(post_response)

return post_response
Copy link

Choose a reason for hiding this comment

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

The post_attachments method uploads attachments to Business Central. It iterates over the provided attachments and posts each one. The method uses base64 decoding to handle the attachment content, which is correct. However, the method should return responses instead of post_response to return all responses for the attachments.

- return post_response
+ return responses

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.

Suggested change
def post_attachments(
self, ref_type: str, ref_id: str, attachments: List[Dict]
) -> List:
"""
Link attachments to objects Business Central
:param ref_id: object id
:param ref_type: type of object
:param attachments: attachment[dict()]
"""
responses = []
if len(attachments):
for attachment in attachments:
data = {
"parentId": ref_id,
"fileName": "{0}_{1}".format(attachment["id"], attachment["name"]),
"parentType": ref_type
}
post_response = self.connection.attachments.post(data)
self.connection.attachments.upload(
post_response["id"],
attachment["content_type"],
base64.b64decode(attachment["download_url"])
)
responses.append(post_response)
return post_response
def post_attachments(
self, ref_type: str, ref_id: str, attachments: List[Dict]
) -> List:
"""
Link attachments to objects Business Central
:param ref_id: object id
:param ref_type: type of object
:param attachments: attachment[dict()]
"""
responses = []
if len(attachments):
for attachment in attachments:
data = {
"parentId": ref_id,
"fileName": "{0}_{1}".format(attachment["id"], attachment["name"]),
"parentType": ref_type
}
post_response = self.connection.attachments.post(data)
self.connection.attachments.upload(
post_response["id"],
attachment["content_type"],
base64.b64decode(attachment["download_url"])
)
responses.append(post_response)
return responses

apps/workspaces/models.py Outdated Show resolved Hide resolved
Copy link

Tests Skipped Failures Errors Time
48 0 💤 1 ❌ 2 🔥 4.499s ⏱️

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.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between dbc13b0 and 95022ca.
Files selected for processing (3)
  • apps/business_central/exceptions.py (1 hunks)
  • apps/business_central/exports/journal_entry/queues.py (1 hunks)
  • apps/business_central/exports/journal_entry/tasks.py (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • apps/business_central/exports/journal_entry/tasks.py

Comment on lines +10 to +45
def check_accounting_export_and_start_import(workspace_id: int, accounting_export_ids: List[str]):
"""
Check accounting export group and start export
"""

fyle_credentials = FyleCredential.objects.filter(workspace_id=workspace_id).first()

accounting_exports = AccountingExport.objects.filter(~Q(status__in=['IN_PROGRESS', 'COMPLETE', 'EXPORT_QUEUED']),
workspace_id=workspace_id, id__in=accounting_export_ids, journal_entry__id__isnull=True,
exported_at__isnull=True).all()

chain = Chain()
chain.append('apps.fyle.helpers.sync_dimensions', fyle_credentials)

for index, accounting_export_group in enumerate(accounting_exports):
accounting_export, _ = AccountingExport.objects.update_or_create(
workspace_id=accounting_export_group.workspace_id,
id=accounting_export_group.id,
defaults={
'status': 'ENQUEUED',
'type': 'JOURNAL_ENTRY'
}
)

if accounting_export.status not in ['IN_PROGRESS', 'ENQUEUED']:
accounting_export.status = 'ENQUEUED'
accounting_export.save()

last_export = False
if accounting_exports.count() == index + 1:
last_export = True

chain.append('apps.business_central.exports.journal_entry.tasks.create_journal_entry', accounting_export, last_export)

if chain.length() > 1:
chain.run()
Copy link

Choose a reason for hiding this comment

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

The function check_accounting_export_and_start_import is designed to enqueue accounting exports for processing. However, there are a few points to consider:

  • Line 17-19: The filter condition is complex and may benefit from being broken out for clarity.
  • Line 21-45: The use of Chain from django_q is appropriate for creating a sequence of tasks. However, the condition on line 34 seems redundant since the status is set to 'ENQUEUED' in the update_or_create defaults.
  • Line 38-40: The logic to determine last_export is correct, but it's inefficient to call accounting_exports.count() inside the loop. This should be calculated once before the loop starts.
  • Line 42: The task create_journal_entry is appended to the chain correctly, but it's important to ensure that the task is defined to handle the last_export flag appropriately.

Consider refactoring the filter condition for readability and efficiency. Also, review the necessity of the status check on line 34, and calculate accounting_exports.count() once before the loop to avoid repeated database calls.

Comment on lines +16 to +72
def handle_business_central_error(exception, accounting_export: AccountingExport, export_type: str):
logger.info(exception.response)

business_central_error = exception.response
error_msg = 'Failed to create {0}'.format(export_type)

Error.objects.update_or_create(workspace_id=accounting_export.workspace_id, accounting_export=accounting_export, defaults={'error_title': error_msg, 'type': 'BUSINESS_CENTRAL_ERROR', 'error_detail': business_central_error, 'is_resolved': False})

accounting_export.status = 'FAILED'
accounting_export.detail = None
accounting_export.business_central_errors = business_central_error
accounting_export.save()


def handle_business_central_exceptions():
def decorator(func):
def new_fn(*args):

accounting_export = args[0]

try:
return func(*args)
except (FyleCredential.DoesNotExist):
logger.info('Fyle credentials not found %s', accounting_export.workspace_id)
accounting_export.detail = {'message': 'Fyle credentials do not exist in workspace'}
accounting_export.status = 'FAILED'

accounting_export.save()

except BusinessCentralCredentials.DoesNotExist:
logger.info('Sage300 Account not connected / token expired for workspace_id %s / accounting export %s', accounting_export.workspace_id, accounting_export.id)
detail = {'accounting_export_id': accounting_export.id, 'message': 'Sage300 Account not connected / token expired'}
accounting_export.status = 'FAILED'
accounting_export.detail = detail

accounting_export.save()

except WrongParamsError as exception:
handle_business_central_error(exception, accounting_export, 'Purchase Invoice')

except BulkError as exception:
logger.info(exception.response)
detail = exception.response
accounting_export.status = 'FAILED'
accounting_export.detail = detail

accounting_export.save()

except Exception as error:
error = traceback.format_exc()
accounting_export.detail = {'error': error}
accounting_export.status = 'FATAL'

accounting_export.save()
logger.error('Something unexpected happened workspace_id: %s %s', accounting_export.workspace_id, accounting_export.detail)

update_accounting_export_summary(accounting_export.workspace_id)
Copy link

Choose a reason for hiding this comment

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

The function handle_business_central_exceptions is a decorator designed to handle various exceptions that can occur during the export process. The following points need attention:

  • Line 16-27: The function handle_business_central_error logs the error and updates the AccountingExport object. It's important to ensure that the exception.response contains the expected format to be stored in business_central_errors.
  • Line 30-76: The decorator handle_business_central_exceptions is well-structured to catch different exceptions. However, the error messages and handling seem to be specific to Business Central and Sage300, which might be a copy-paste error (line 46 mentions Sage300 instead of Business Central).
  • Line 72: The call to update_accounting_export_summary should be inside a finally block to ensure it's called even if an exception is not raised.

Correct the potential copy-paste error in the exception message on line 46 to reflect the correct system (Business Central). Also, consider moving the update_accounting_export_summary call to a finally block to ensure it's always executed.

* Purchase Invoice Export

* Tests resolved

* Comments resolved

* Vendor Employee Id export logic (#64)

* Vendor Employee Id export logic

* Test resolved

* Validate employee mapping

* Validate employee mapping

* Validate employee mapping

* Fix comments

* Comments resolved

* Import Employee mapping Module (#65)

* Import Employee mapping Module

* Added triggers for import and advanced settings (#66)

* Added triggers from import and advanced settings

* Tests Fixed

* location support and import account bug fix (#68)

* location support and import account bug fix

* location support and import account bug fix

* comments resolved
Copy link

Tests Skipped Failures Errors Time
48 0 💤 0 ❌ 0 🔥 4.627s ⏱️

@ruuushhh ruuushhh merged commit e06aebc into master Jan 17, 2024
1 check passed
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.

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 95022ca and af0da4f.
Files selected for processing (33)
  • apps/business_central/exports/accounting_export.py (1 hunks)
  • apps/business_central/exports/base_model.py (1 hunks)
  • apps/business_central/exports/helpers.py (1 hunks)
  • apps/business_central/exports/journal_entry/models.py (1 hunks)
  • apps/business_central/exports/purchase_invoice/models.py (1 hunks)
  • apps/business_central/exports/purchase_invoice/queues.py (1 hunks)
  • apps/business_central/exports/purchase_invoice/tasks.py (1 hunks)
  • apps/business_central/migrations/0002_purchaseinvoice_journalentry_employee_account_id_and_more.py (1 hunks)
  • apps/business_central/migrations/0003_remove_purchaseinvoice_description.py (1 hunks)
  • apps/business_central/migrations/0004_alter_purchaseinvoice_accounting_export.py (1 hunks)
  • apps/business_central/migrations/0005_rename_employee_account_id_journalentry_account_id_and_more.py (1 hunks)
  • apps/business_central/migrations/0006_purchaseinvoicelineitems_location_id.py (1 hunks)
  • apps/business_central/models.py (1 hunks)
  • apps/business_central/utils.py (6 hunks)
  • apps/fyle/urls.py (1 hunks)
  • apps/mappings/imports/modules/base.py (1 hunks)
  • apps/mappings/imports/modules/employees.py (1 hunks)
  • apps/mappings/imports/queues.py (1 hunks)
  • apps/mappings/imports/tasks.py (2 hunks)
  • apps/workspaces/migrations/0003_rename_default_journal_entry_folder_id_exportsetting_journal_entry_folder_id_and_more.py (1 hunks)
  • apps/workspaces/migrations/0004_exportsetting_import_vendors_as_merchants_and_more.py (1 hunks)
  • apps/workspaces/migrations/0005_rename_emoloyee_mapping_exportsetting_employee_mapping.py (1 hunks)
  • apps/workspaces/migrations/0006_alter_importsetting_workspace.py (1 hunks)
  • apps/workspaces/migrations/0006_rename_employee_mapping_exportsetting_employee_field_mapping.py (1 hunks)
  • apps/workspaces/models.py (7 hunks)
  • apps/workspaces/serializers.py (7 hunks)
  • apps/workspaces/tasks.py (1 hunks)
  • apps/workspaces/triggers.py (1 hunks)
  • apps/workspaces/views.py (4 hunks)
  • requirements.txt (1 hunks)
  • tests/conftest.py (1 hunks)
  • tests/test_fyle/fixtures.py (1 hunks)
  • tests/test_workspaces/test_view.py (8 hunks)
Files skipped from review as they are similar to previous changes (5)
  • apps/business_central/exports/helpers.py
  • apps/business_central/exports/journal_entry/models.py
  • apps/business_central/utils.py
  • apps/workspaces/models.py
  • apps/workspaces/serializers.py
Additional comments: 35
apps/business_central/models.py (1)
  • 1-2: The imports for JournalEntry, JournalEntryLineItems, PurchaseInvoice, and PurchaseInvoiceLineitems have been added. Ensure that these models are indeed located in the specified modules and that they are used within this file or its related files.
Verification successful

The search results confirm that the models JournalEntry, JournalEntryLineItems, PurchaseInvoice, and PurchaseInvoiceLineitems are indeed used within the apps/business_central/ directory. The models are referenced in migration files, which indicates that they are part of the database schema changes. Additionally, they are used in the tasks.py and models.py files within the exports/journal_entry and exports/purchase_invoice subdirectories, suggesting that these models are part of the export functionality for journal entries and purchase invoices.

Given this information, the imports in apps/business_central/models.py are justified as these models are utilized in related files within the business_central app.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the imported models are used in the current file or related files.
rg --type python "JournalEntry|JournalEntryLineItems|PurchaseInvoice|PurchaseInvoiceLineitems" apps/business_central/models.py

Length of output: 159



Script:

#!/bin/bash
# Search for the usage of the imported models in the apps/business_central/models.py file and related files within the apps/business_central/ directory.
rg "JournalEntry|JournalEntryLineItems|PurchaseInvoice|PurchaseInvoiceLineitems" apps/business_central/

Length of output: 4264

apps/business_central/migrations/0003_remove_purchaseinvoice_description.py (1)
  • 13-16: The migration removes the description field from the PurchaseInvoice model. Ensure that this change is intentional and that the removal of this field does not affect any existing functionality or data integrity.
apps/workspaces/migrations/0005_rename_emoloyee_mapping_exportsetting_employee_mapping.py (1)
  • 13-16: The migration renames the field emoloyee_mapping to employee_mapping in the ExportSetting model. This is likely a typo correction. Ensure that all references to the old field name are updated across the codebase.
Verification successful

The occurrences of the old field name emoloyee_mapping are found within the migration files, which is expected as they document the history of database schema changes. There is no indication that the old field name is used in the current operational code. The migration process in Django does not require updating old migration files when renaming fields, as these files serve as a historical record.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the old field name is not used anywhere in the codebase.
rg --type python "emoloyee_mapping" .

Length of output: 68



Script:

#!/bin/bash
# Verify that the old field name is not used anywhere in the codebase.
rg "emoloyee_mapping" .

Length of output: 780

apps/business_central/migrations/0006_purchaseinvoicelineitems_location_id.py (1)
  • 14-17: The migration adds a new field location_id to the PurchaseInvoiceLineitems model. Ensure that this field is appropriately used in the application and that any necessary validations or business logic related to this field are implemented.
apps/workspaces/migrations/0006_alter_importsetting_workspace.py (1)
  • 14-17: The migration alters the workspace field in the ImportSetting model to use a OneToOneField with a PROTECT on delete. Confirm that this change is in line with the application's data model and that it won't cause any issues with existing data.
apps/business_central/migrations/0004_alter_purchaseinvoice_accounting_export.py (1)
  • 15-18: The migration alters the accounting_export field in the PurchaseInvoice model to use a OneToOneField with a PROTECT on delete. Ensure that this change is consistent with the application's requirements and that it won't negatively impact existing data or functionality.
Verification successful

The shell script has successfully found references to accounting_export in the models.py file within the apps/business_central directory. The output shows that accounting_export is indeed used as a OneToOneField related to the AccountingExport model, and there are several functions that interact with this field, indicating that the change to a OneToOneField is already integrated into the application's logic.

Based on this information, it can be inferred that the migration altering the accounting_export field to a OneToOneField with a PROTECT on delete is consistent with the application's requirements, as the application logic is already using this field in the expected manner.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the change to a OneToOneField is consistent with the application's requirements.
rg --type python "accounting_export" apps/business_central/models.py

Length of output: 99



Script:

#!/bin/bash
# Re-run the search for "accounting_export" in the models.py file within the apps/business_central directory
rg "accounting_export" apps/business_central/models.py

Length of output: 54



Script:

#!/bin/bash
# Find the models.py file within the apps/business_central directory and search for "accounting_export"
fd models.py apps/business_central --exec rg "accounting_export" {}

Length of output: 3421

requirements.txt (2)
  • 31-31: The ms-dynamics-business-central-sdk package has been updated to version 1.3.3. Confirm that this version is compatible with the application and that all necessary changes related to the update have been made.
  • 35-35: The fyle-accounting-mappings package has been updated to version 1.29.1. Ensure that this version is compatible with the application and that any necessary updates have been made to accommodate changes in the package.
apps/business_central/migrations/0005_rename_employee_account_id_journalentry_account_id_and_more.py (3)
  • 14-22: The migration renames the employee_account_id field to account_id in both JournalEntry and JournalEntryLineitems models. Confirm that this renaming is consistent with the intended usage and that all references to the old field name are updated.
Verification successful

The search results indicate that the term employee_account_id appears only within migration files. This is expected as migrations keep a record of historical changes to the database schema. The presence of employee_account_id in these files does not imply that the field is still being referenced in the active codebase; it merely reflects the state of the database before the migration was applied.

Given this context, it seems that the renaming of employee_account_id to account_id has been carried out correctly in the active codebase, as there are no references to the old field name outside of the migration files.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the old field name is not used anywhere in the codebase.
rg --type python "employee_account_id" apps/business_central/

Length of output: 92



Script:

#!/bin/bash
# Correct the previous script to search for the old field name without specifying the file type
rg "employee_account_id" apps/business_central/

Length of output: 963

* 24-27: The migration adds a new field `account_type` to the `JournalEntry` model. Ensure that this field is appropriately used in the application and that any necessary validations or business logic related to this field are implemented. * 29-32: The migration adds a new field `account_type` to the `JournalEntryLineitems` model. Ensure that this field is appropriately used in the application and that any necessary validations or business logic related to this field are implemented.
Verification successful

The search results indicate that the account_type field is present in the journal_entry/models.py and journal_entry/tasks.py files within the apps/business_central/exports/ directory. The field is defined in the JournalEntryLineitems model and is used in methods that seem to be related to setting up and exporting data. It is also referenced in the tasks file, where it appears to be included in the data being prepared for export.

Given this information, it seems that the account_type field is indeed being utilized in the application, specifically within the context of journal entry exports. However, without further context on the business logic or validations, we cannot fully verify if it is used "correctly" as per the review comment's request.

To ensure that the field is used with the necessary validations or business logic, we would need to examine the methods where account_type is used, such as get_account_id_type and the tasks where it is included in the export payload. If more detailed verification is required, additional scripts could be run to inspect these methods and their usage of account_type.

For now, based on the search results, we can conclude that the account_type field is actively used in the application's journal entry export functionality.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the new account_type field is being used correctly in the application.
rg --type python "account_type" apps/business_central/

Length of output: 85



Script:

#!/bin/bash
# Verify that the new account_type field is being used correctly in the application.
# Using rg with a glob pattern to search for Python files
rg "account_type" --glob "*.py" apps/business_central/

Length of output: 1404

apps/workspaces/migrations/0004_exportsetting_import_vendors_as_merchants_and_more.py (3)
  • 14-17: The migration adds a new field import_vendors_as_merchants to the ExportSetting model. Confirm that this field is used correctly in the application and that any related functionality is implemented.
  • 19-22: The migration adds a new field name_in_journal_entry to the ExportSetting model. Ensure that this field is used correctly in the application and that any related functionality is implemented.
Verification successful

The name_in_journal_entry field is referenced in the following files:

  • apps/workspaces/serializers.py: It appears in a conditional statement, which suggests that there is logic that depends on the value of this field.
  • apps/workspaces/models.py: The field is defined in the ExportSetting model.
  • apps/workspaces/migrations/0004_exportsetting_import_vendors_as_merchants_and_more.py: The field is added in a migration file.

Based on the output, the field is indeed being used in the application, specifically within the workspaces app. The usage in serializers.py indicates that there is some functionality that relies on the value of this field.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the new name_in_journal_entry field is being used correctly in the application.
rg --type python "name_in_journal_entry" apps/workspaces/

Length of output: 88



Script:

#!/bin/bash
# Verify that the new name_in_journal_entry field is being used correctly in the application.
# Correcting the previous script by removing the incorrect --type flag
rg "name_in_journal_entry" apps/workspaces/

Length of output: 423

* 24-27: The migration alters the `emoloyee_mapping` field in the `ExportSetting` model. This seems to be a typo and should be `employee_mapping`. Confirm that this is a typo and that it is corrected in the codebase.
Verification successful

The typo in the field name emoloyee_mapping was indeed present in the codebase. However, it has been corrected in a subsequent migration, as indicated by the presence of a migration file named 0005_rename_emoloyee_mapping_exportsetting_employee_mapping.py which changes the field name from emoloyee_mapping to employee_mapping. This correction is also referenced in other migration files, confirming that the typo has been addressed.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the typo in the field name is corrected in the codebase.
rg --type python "emoloyee_mapping" apps/workspaces/

Length of output: 83



Script:

#!/bin/bash
# Re-run the search for "emoloyee_mapping" without specifying the file type.
rg "emoloyee_mapping" apps/workspaces/

Length of output: 785

apps/mappings/imports/tasks.py (2)
  • 3-3: The import for Employee from the employees module has been added. Ensure that the Employee model is indeed located in the specified module and that it is used within this file or its related files.
Verification successful

The shell script output confirms that the Employee model is imported in apps/mappings/imports/tasks.py and is also referenced within the ENTITY_MAP dictionary in the same file. This verifies that the import is indeed used within the file.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the imported Employee model is used in the current file or related files.
rg --type python "Employee" apps/mappings/imports/tasks.py

Length of output: 89



Script:

#!/bin/bash
# Verify that the imported Employee model is used in the current file or related files.
rg "Employee" apps/mappings/imports/tasks.py

Length of output: 131

* 14-14: The `ENTITY_MAP` dictionary has been updated to include `'EMPLOYEE': Employee`. Confirm that this addition is consistent with the application's data model and that the `Employee` entity is handled correctly where this map is used.
apps/mappings/imports/queues.py (1)
  • 34-34: The condition for triggering an import via schedule in the chain_import_fields_to_fyle function has been updated to include the 'EMPLOYEE' field. Confirm that this change is intended and that the 'EMPLOYEE' field is handled correctly in the import process.
Verification successful

The extended script output provides more context on the chain_import_fields_to_fyle function. It shows that the function is setting up a chain of import tasks based on the import_settings and mapping_settings for a given workspace_id. The EMPLOYEE field is included in a conditional check alongside PROJECT and COST_CENTER, and if this condition is met, a task is appended to the chain to trigger an import via schedule.

The output, however, is still truncated and does not show the complete handling of the 'EMPLOYEE' field within the function. To ensure that the 'EMPLOYEE' field is handled correctly, we need to see the complete logic after the condition that includes the 'EMPLOYEE' field. This would typically involve checking if there are any specific actions or methods called for the 'EMPLOYEE' field that differ from the other fields, or if there's additional logic further down in the function that pertains to the 'EMPLOYEE' field.

Given the current output, we can infer that the 'EMPLOYEE' field is considered for scheduling an import task, but without the complete function body, we cannot fully verify the correct handling of the 'EMPLOYEE' field in the import process. Therefore, we need to extend the script further to capture the rest of the function's content.


The script output now provides the complete content of the chain_import_fields_to_fyle function. The function sets up a chain of import tasks based on the settings for a given workspace_id. The handling of the 'EMPLOYEE' field is part of a loop that iterates over mapping_settings. If the source_field of a mapping_setting is in the list ['PROJECT', 'COST_CENTER', 'EMPLOYEE'], the function appends a task to the chain to trigger an import via schedule, using the mapping_setting.destination_field and mapping_setting.source_field as parameters.

The function also handles custom field mapping settings in a similar manner, appending tasks to the chain for each custom field that is set to be imported to Fyle.

Finally, if the chain has tasks appended to it (i.e., its length is greater than 0), the chain is executed with the chain.run() call.

Based on this information, we can verify that the 'EMPLOYEE' field is included in the import process in the same way as 'PROJECT' and 'COST_CENTER' fields. There is no special or different handling for the 'EMPLOYEE' field indicated in the function's logic. The change to include the 'EMPLOYEE' field in the condition for triggering an import via schedule is consistent with the rest of the function's implementation.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the 'EMPLOYEE' field is handled correctly in the import process.
rg --type python "chain_import_fields_to_fyle" apps/mappings/imports/

Length of output: 100



Script:

#!/bin/bash
# Verify that the 'EMPLOYEE' field is handled correctly in the import process.
# Removing the --type flag to avoid the previous error and ensure the search is conducted.
rg "chain_import_fields_to_fyle" apps/mappings/imports/

Length of output: 358



Script:

#!/bin/bash
# Examine the implementation of the function 'chain_import_fields_to_fyle' in 'queues.py'
# to understand how the 'EMPLOYEE' field is handled.
ast-grep --lang python --pattern $'def chain_import_fields_to_fyle($_) { $$$ }' apps/mappings/imports/queues.py

Length of output: 578



Script:

#!/bin/bash
# Correcting the pattern syntax for ast-grep to examine the 'chain_import_fields_to_fyle' function.
ast-grep --lang python --pattern 'def chain_import_fields_to_fyle($workspace_id) { $$$ }' apps/mappings/imports/queues.py

Length of output: 600



Script:

#!/bin/bash
# Using rg to extract the content of 'chain_import_fields_to_fyle' function from 'queues.py'.
rg -A 10 "def chain_import_fields_to_fyle" apps/mappings/imports/queues.py

Length of output: 578



Script:

#!/bin/bash
# Extending the range of lines to capture more of the 'chain_import_fields_to_fyle' function's content.
rg -A 30 "def chain_import_fields_to_fyle" apps/mappings/imports/queues.py

Length of output: 1240



Script:

#!/bin/bash
# Retrieve the full content of the 'chain_import_fields_to_fyle' function to verify the handling of the 'EMPLOYEE' field.
rg -A 50 "def chain_import_fields_to_fyle" apps/mappings/imports/queues.py

Length of output: 1734

apps/fyle/urls.py (2)
  • 29-29: The SkippedExpenseView has been added to the list of exported entities. Ensure that this view is implemented correctly and that the corresponding URL path is properly configured.
Verification successful

The SkippedExpenseView is implemented in apps/fyle/views.py as a ListAPIView and is correctly referenced in apps/fyle/urls.py, where it is also associated with the URL path 'expenses/'. This confirms that the view is implemented and has a corresponding URL path configured.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the SkippedExpenseView is implemented and configured correctly.
rg --type python "SkippedExpenseView" apps/fyle/

Length of output: 79



Script:

#!/bin/bash
# Verify that the SkippedExpenseView is implemented and configured correctly.
# Removing the --type option as it caused an unrecognized file type error.
rg "SkippedExpenseView" apps/fyle/

Length of output: 231

* 34-34: The URL path for the `AccoutingExportSyncView` has been modified from `'expense_groups/sync/'` to `'accounting_exports/sync/'`. Confirm that this change is reflected in the frontend or any other services that might consume this endpoint.
apps/business_central/exports/purchase_invoice/queues.py (1)
  • 10-10: The function check_accounting_export_and_start_import is called to start the import process for accounting exports. Ensure that this function is implemented correctly and that it handles the accounting_export_ids parameter appropriately.
Verification successful

The function check_accounting_export_and_start_import in queues.py is implemented to handle the accounting_export_ids parameter by filtering AccountingExport objects and queuing tasks for each export. The function updates the status of the exports and initiates the export process as expected.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the check_accounting_export_and_start_import function is implemented correctly.
rg --type python "check_accounting_export_and_start_import" apps/business_central/exports/purchase_invoice/

Length of output: 138



Script:

#!/bin/bash
# Verify that the check_accounting_export_and_start_import function is implemented correctly by searching for its usage and definition.
rg "check_accounting_export_and_start_import" apps/business_central/exports/purchase_invoice/

Length of output: 554



Script:

#!/bin/bash
# Review the implementation of the function check_accounting_export_and_start_import in queues.py
cat apps/business_central/exports/purchase_invoice/queues.py

Length of output: 1707

apps/workspaces/migrations/0003_rename_default_journal_entry_folder_id_exportsetting_journal_entry_folder_id_and_more.py (2)
  • 14-17: The migration renames the field default_journal_entry_folder_id to journal_entry_folder_id in the ExportSetting model. Confirm that this renaming is consistent with the intended usage and that all references to the old field name are updated.
Verification successful

The migration renaming default_journal_entry_folder_id to journal_entry_folder_id has been correctly reflected in the codebase. The old field name only appears within the migration files, which is expected as they document the schema evolution. There are no references to the old field name in the active application code.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the old field name is not used anywhere in the codebase.
rg --type python "default_journal_entry_folder_id" apps/workspaces/

Length of output: 98



Script:

#!/bin/bash
# Correct the previous script by using the proper file type for Python files.
rg "default_journal_entry_folder_id" apps/workspaces/ --type py

Length of output: 726

* 19-45: Multiple fields are being removed from the `ExportSetting` model. Ensure that the removal of these fields is intentional and that it does not affect any existing functionality or data integrity.
apps/workspaces/triggers.py (2)
  • 19-41: The post_save_mapping_settings method in the ImportSettingsTrigger class deletes mapping settings that are not required. Ensure that this deletion logic is correct and does not inadvertently remove necessary settings.
  • 53-63: The run_post_advance_settings_triggers method in the AdvancedSettingsTriggers class schedules a sync based on advanced settings. Confirm that the scheduling logic is correct and that it respects the user's configuration.
apps/business_central/exports/accounting_export.py (1)
  • 30-75: The create_business_central_object method in the AccountingDataExporter class creates an accounting object and posts it to an external system. Ensure that the implementation of this method is correct and that it handles transactions and error resolution appropriately.
Verification successful

The create_business_central_object method in the AccountingDataExporter class has been successfully extracted from the accounting_export.py file. The method includes the following operations:

  1. Retrieval of advanced settings for the current workspace.
  2. Checking and updating the status of the accounting export.
  3. Validation of the accounting export.
  4. Use of a database transaction to ensure atomic operations for:
    • Creating or updating the main body of the accounting object.
    • Creating or updating line items for the accounting object, if applicable.
    • Posting the data to the external accounting system.
    • Updating the accounting export details and status.

The method concludes by saving the updated accounting export object and resolving any errors related to the exported accounting export.

Based on the provided code, the method appears to handle transactions and error resolution appropriately, as it uses a database transaction to ensure that the operations are atomic and includes error resolution at the end of the process.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the create_business_central_object method is implemented correctly.
rg --type python "create_business_central_object" apps/business_central/exports/accounting_export.py

Length of output: 131



Script:

#!/bin/bash
# Verify that the create_business_central_object method is implemented correctly.
rg "create_business_central_object" apps/business_central/exports/accounting_export.py

Length of output: 169



Script:

#!/bin/bash
# Display the contents of the accounting_export.py file starting from the definition of create_business_central_object method.
cat apps/business_central/exports/accounting_export.py | sed -n '/def create_business_central_object/,/^$/p'

Length of output: 277



Script:

#!/bin/bash
# Attempt to display the entire body of the create_business_central_object method.
# Using awk to print lines from the start of create_business_central_object method until the next def or end of file.
awk '/def create_business_central_object/,/def |^$/' apps/business_central/exports/accounting_export.py

Length of output: 188



Script:

#!/bin/bash
# Attempt to display the entire body of the create_business_central_object method.
# Using sed to print lines from the start of create_business_central_object method until the next def or an empty line that indicates the end of the method.
sed -n '/def create_business_central_object/,/^$/p' apps/business_central/exports/accounting_export.py

Length of output: 271



Script:

#!/bin/bash
# Display the entire content of the accounting_export.py file and then extract the create_business_central_object method.
cat apps/business_central/exports/accounting_export.py | sed -n '/def create_business_central_object/,/def/p'

Length of output: 2340

apps/workspaces/views.py (2)
  • 112-119: The ImportSettingView class has been modified to inherit from generics.RetrieveUpdateAPIView and the get_object method has been customized. Confirm that this change is consistent with the application's requirements and that the ImportSetting model is correctly retrieved and updated.
  • 144-150: The TriggerExportsView class has been added to trigger export creation. Ensure that the export_to_business_central task is called correctly and that the export process is initiated as expected.
Verification successful

The TriggerExportsView class's post method correctly calls the export_to_business_central function with the workspace_id parameter extracted from kwargs. This indicates that the export process is initiated as expected when this view's post method is called.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the TriggerExportsView class correctly initiates the export process.
rg --type python "TriggerExportsView" apps/workspaces/views.py

Length of output: 93



Script:

#!/bin/bash
# Re-run the verification for TriggerExportsView class without specifying the file type.
rg "class TriggerExportsView" apps/workspaces/views.py

Length of output: 107



Script:

#!/bin/bash
# Verify the implementation of the post method within the TriggerExportsView class.
ast-grep --lang python --pattern $'class TriggerExportsView {
  def post($_, $_, $_, $_) {
    $$$
  }
}'

Length of output: 598



Script:

#!/bin/bash
# Correct the ast-grep pattern to match the post method within the TriggerExportsView class.
ast-grep --lang python --pattern $'class $_ { def post($$$) { $$$ } }' --source 'apps/workspaces/views.py'

Length of output: 305



Script:

#!/bin/bash
# Adjust the ast-grep command to correctly search for the post method within the TriggerExportsView class in the views.py file.
ast-grep --lang python --pattern $'class $_ { def post($$$) { $$$ } }' apps/workspaces/views.py

Length of output: 441



Script:

#!/bin/bash
# Use a simpler grep command to verify the presence and correct usage of the export_to_business_central function within the post method of the TriggerExportsView class.
grep -A 5 "class TriggerExportsView" apps/workspaces/views.py

Length of output: 206



Script:

#!/bin/bash
# Extend the grep command to capture more lines following the class definition to see the complete implementation of the post method.
grep -A 10 "class TriggerExportsView" apps/workspaces/views.py

Length of output: 353

apps/business_central/migrations/0002_purchaseinvoice_journalentry_employee_account_id_and_more.py (3)
  • 18-32: The migration creates a new PurchaseInvoice model with various fields. Confirm that the fields and their types are appropriate for the data they are meant to store and that they align with the application's data model.
  • 37-45: The migration adds new fields to the JournalEntry and JournalEntryLineitems models. Ensure that these fields are used correctly in the application and that any necessary validations or business logic related to these fields are implemented.
  • 47-63: The migration creates a new PurchaseInvoiceLineitems model with various fields. Confirm that the fields and their types are appropriate for the data they are meant to store and that they align with the application's data model.
Verification successful

The fields and their types for the PurchaseInvoiceLineitems model have been successfully located in the apps/business_central/exports/purchase_invoice/models.py file. The fields are as follows:

  • accounts_payable_account_id: Uses StringNullField, which seems appropriate for storing an ID as a string and allows null values.
  • purchase_invoice: Uses models.ForeignKey, referencing PurchaseInvoice, which is standard for establishing a many-to-one relationship.
  • expense: Uses models.OneToOneField, referencing Expense, which is standard for a one-to-one relationship with the Expense model.
  • amount: Uses FloatNullField, which is suitable for storing numerical amounts with the possibility of null values.
  • description: Uses TextNotNullField, which is suitable for storing text descriptions and does not allow null values.
  • location_id: Uses StringNullField, similar to accounts_payable_account_id, suitable for storing an ID as a string with null values allowed.

The types of fields used are consistent with common practices for modeling such data in Django. The use of null-allowed fields where appropriate suggests consideration for cases where data may not be present.

Based on the information provided, the fields and their types in the PurchaseInvoiceLineitems model appear to be appropriate and align with the application's data model.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the fields in the PurchaseInvoiceLineitems model are appropriate and align with the application's data model.
rg --type python "PurchaseInvoiceLineitems" apps/business_central/models.py

Length of output: 106



Script:

#!/bin/bash
# Verify that the fields in the PurchaseInvoiceLineitems model are appropriate and align with the application's data model.
rg "PurchaseInvoiceLineitems" apps/business_central/models.py

Length of output: 177



Script:

#!/bin/bash
# Locate the definition of the PurchaseInvoiceLineitems model to verify the fields and their types.
rg "class PurchaseInvoiceLineitems" -A 20 apps/business_central/exports/purchase_invoice/models.py

Length of output: 1096

apps/business_central/exports/base_model.py (4)
  • 23-56: The get_expense_comment method has been updated to include logic for constructing a URL to view an expense in Fyle and to build a comment based on a memo structure. The method now saves the cluster_domain to the workspace and uses it to construct the expense_link. The loop for building the comment has been corrected to append a hyphen only between fields, not after every field, which addresses the previous issue raised in the comments.
  • 58-74: The get_total_amount method correctly calculates the total amount of expenses associated with an AccountingExport object. It handles the case where there are no expenses by returning 0.0, which is a good practice for avoiding NoneType errors in arithmetic operations.
  • 101-129: The get_account_id_type method determines the account ID and type based on the AccountingExport object and ExportSetting. It handles different scenarios for reimbursable expenses and credit card expenses, including default values and merchant-based logic. Ensure that all possible paths are covered by tests, especially since this method involves multiple conditional branches.
  • 152-178: The get_location_id method retrieves the location ID based on the AccountingExport object and a line item's project or cost center. It uses MappingSetting to determine the source field and then finds the corresponding Mapping object. This method should be tested to ensure that it handles all possible configurations and returns the correct location ID.
apps/mappings/imports/modules/base.py (1)
  • 173-173: The inclusion of 'employees' in the sync_expense_attributes method's condition is a logical update to ensure that employee data is synchronized along with other expense attributes. This change aligns with the PR's objective to enhance the export functionality.

Comment on lines +76 to +99
def get_invoice_date(accounting_export: AccountingExport) -> str:
"""
Get the invoice date from the provided AccountingExport.

Parameters:
- accounting_export (AccountingExport): The AccountingExport instance containing the description field.

Returns:
- str: The invoice date as a string in the format '%Y-%m-%dT%H:%M:%S'.
"""
# Check for specific keys in the 'description' field and return the corresponding value
if 'spent_at' in accounting_export.description and accounting_export.description['spent_at']:
return accounting_export.description['spent_at']
elif 'approved_at' in accounting_export.description and accounting_export.description['approved_at']:
return accounting_export.description['approved_at']
elif 'verified_at' in accounting_export.description and accounting_export.description['verified_at']:
return accounting_export.description['verified_at']
elif 'last_spent_at' in accounting_export.description and accounting_export.description['last_spent_at']:
return accounting_export.description['last_spent_at']
elif 'posted_at' in accounting_export.description and accounting_export.description['posted_at']:
return accounting_export.description['posted_at']

# If none of the expected keys are present or if the values are empty, return the current date and time
return datetime.now().strftime("%Y-%m-%d")
Copy link

Choose a reason for hiding this comment

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

The extended search output confirms that the get_invoice_date method in base_model.py returns the current date in the format '%Y-%m-%d' when none of the expected keys are present or their values are empty. This format does not match the format specified in the method's docstring ('%Y-%m-%dT%H:%M:%S'). Therefore, there is an inconsistency between the docstring and the actual behavior of the method.

The review comment is correct in pointing out that the method should ensure that the date format returned is consistent with the expected format mentioned in the docstring. The current implementation does not do this when it falls back to the current date.

Analysis chain

The get_invoice_date method extracts the invoice date from various fields within the description of an AccountingExport object. It falls back to the current date if none of the expected keys are present. This method should ensure that the date format returned is consistent and matches the expected format '%Y-%m-%dT%H:%M:%S' as mentioned in the docstring.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the date format returned by get_invoice_date is consistent with '%Y-%m-%dT%H:%M:%S'
# This script should be run in a test environment where the method's behavior can be safely evaluated.

Length of output: 0



Script:

#!/bin/bash
# Search for the implementation of the get_invoice_date method in base_model.py
# and display the relevant code block that handles the return of the current date.
rg "def get_invoice_date" -A 20 apps/business_central/exports/base_model.py

Length of output: 1421



Script:

#!/bin/bash
# Extend the search to include the entire get_invoice_date method to see the fallback date format.
rg "def get_invoice_date" -A 30 apps/business_central/exports/base_model.py

Length of output: 1926

Comment on lines +130 to +150
def get_expense_purpose(lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
memo_structure = advance_setting.expense_memo_structure

details = {
'employee_email': lineitem.employee_email,
'merchant': '{0}'.format(lineitem.vendor) if lineitem.vendor else '',
'category': '{0}'.format(category) if lineitem.category else '',
'purpose': '{0}'.format(lineitem.purpose) if lineitem.purpose else '',
'report_number': '{0}'.format(lineitem.claim_number),
'spent_on': '{0}'.format(lineitem.spent_at.date()) if lineitem.spent_at else '',
}

purpose = ''

for id, field in enumerate(memo_structure):
if field in details:
purpose += details[field]
if id + 1 != len(memo_structure):
purpose = '{0} - '.format(purpose)

return purpose
Copy link

Choose a reason for hiding this comment

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

The get_expense_purpose method appears to be a duplicate of the get_expense_comment method. This redundancy should be addressed to follow the DRY principle. The method should be refactored to reuse the common logic, or one of the methods should be removed if they are indeed identical.

-    def get_expense_purpose(lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
-        memo_structure = advance_setting.expense_memo_structure
-        ...
-        return purpose
+    # Removed duplicate method get_expense_purpose, use get_expense_comment instead.

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.

Suggested change
def get_expense_purpose(lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
memo_structure = advance_setting.expense_memo_structure
details = {
'employee_email': lineitem.employee_email,
'merchant': '{0}'.format(lineitem.vendor) if lineitem.vendor else '',
'category': '{0}'.format(category) if lineitem.category else '',
'purpose': '{0}'.format(lineitem.purpose) if lineitem.purpose else '',
'report_number': '{0}'.format(lineitem.claim_number),
'spent_on': '{0}'.format(lineitem.spent_at.date()) if lineitem.spent_at else '',
}
purpose = ''
for id, field in enumerate(memo_structure):
if field in details:
purpose += details[field]
if id + 1 != len(memo_structure):
purpose = '{0} - '.format(purpose)
return purpose
# Removed duplicate method get_expense_purpose, use get_expense_comment instead.

Comment on lines +13 to +16
migrations.RenameField(
model_name='exportsetting',
old_name='employee_mapping',
new_name='employee_field_mapping',
Copy link

Choose a reason for hiding this comment

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

The search results show that the term employee_mapping is still present in the active codebase, specifically within the employees.py module and the helpers.py file in the business_central/exports directory. These references are not part of migration files or comments, which suggests that there are indeed active parts of the code that may still be using the old field name.

This indicates that the renaming of the field employee_mapping to employee_field_mapping in the ExportSetting model may not be fully consistent throughout the codebase, and there could be areas where the old field name is still being used. This could lead to errors if the code attempts to reference a field that has been renamed.

Based on this evidence, it seems that the review comment is correct, and there are indeed references to the old field name that need to be updated to reflect the new field name.

Analysis chain

The migration renames the field employee_mapping to employee_field_mapping in the ExportSetting model. Confirm that this renaming is consistent with the intended usage and that all references to the old field name are updated.


The search results indicate that the term employee_mapping is still present in various files within the codebase. Specifically, it appears in migration files, a helper file, and within comments in the employees.py module.

Given this information, it seems that not all references to the old field name employee_mapping have been updated. This could potentially lead to issues if the code is still referencing the old field name outside of the migration context.

To provide a more accurate response, I will need to differentiate between references that are part of historical migrations (which are expected and acceptable) and references that are part of the active codebase (which would need to be updated).

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the old field name is not used anywhere in the codebase.
rg --type python "employee_mapping" .

Length of output: 68



Script:

#!/bin/bash
# Correct the previous script to search for the old field name "employee_mapping" without specifying the file type.
rg "employee_mapping" .

Length of output: 1371



Script:

#!/bin/bash
# Check the usage of "employee_mapping" in active codebase files, excluding migrations and comments.
rg "employee_mapping" --glob '!**/migrations/*' --glob '!*.pyc' --glob '!*.pyo' --glob '!__pycache__/*' --glob '!*.py~' --glob '!*.py#'

Length of output: 860

Comment on lines +47 to +50
migrations.AddField(
model_name='exportsetting',
name='emoloyee_mapping',
field=ms_business_central_api.models.fields.StringNullField(help_text='Employee Mapping', max_length=255, null=True),
Copy link

Choose a reason for hiding this comment

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

The search results indicate that the correct field name employee_mapping does not appear in apps/workspaces/models.py, which suggests that the latest models file does not contain the corrected field name. Additionally, the incorrect field name emoloyee_mapping still appears in several migration files outside of the specific migrations that were meant to fix the typo.

Given this information, it seems that the typo has not been fully corrected in the codebase, as the incorrect field name still exists in migrations that are not related to the renaming. This suggests that the review comment is correct in pointing out the typo, and it has not been fully addressed in the codebase.

Analysis chain

The migration adds a new field emoloyee_mapping to the ExportSetting model. This seems to be a typo and should be employee_mapping. Confirm that this is a typo and that it is corrected in the codebase.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the typo in the field name is corrected in the codebase.
rg --type python "emoloyee_mapping" apps/workspaces/

Length of output: 83



Script:

#!/bin/bash
# Verify that the typo in the field name is corrected in the codebase.
rg "emoloyee_mapping" apps/workspaces/

Length of output: 785



Script:

#!/bin/bash
# Verify that the typo in the field name has been corrected in the latest state of the codebase.
# Search for the correct field name "employee_mapping" in the models.
rg "employee_mapping" apps/workspaces/models.py

# Search for the incorrect field name "emoloyee_mapping" outside of the specific migrations that fixed it.
rg "emoloyee_mapping" --glob '!apps/workspaces/migrations/0005_rename_emoloyee_mapping_exportsetting_employee_mapping.py' --glob '!apps/workspaces/migrations/0006_rename_employee_mapping_exportsetting_employee_field_mapping.py'

Length of output: 708

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.

3 participants