-
Notifications
You must be signed in to change notification settings - Fork 0
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
Expenses Sync APIs #32
Merged
Merged
Changes from 13 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
ca98522
Import Business Central Attributes API added
ruuushhh e389d66
Business central fields api added
ruuushhh 1e07ae5
Business central models added
ruuushhh 32384e5
flake8 resolved
ruuushhh e39b202
models removed, commentrs resolved
ruuushhh 6eb13ba
bug fixed
ruuushhh 4843ad0
Merge branch 'master' into exportable-expense-api
ruuushhh 9ab47c5
Exportable expense groups api
ruuushhh 0008017
test cases added
ruuushhh f0aebe2
Expenses Sync APIs
ruuushhh c8f1d9e
test cases resolved
ruuushhh 620b36b
Merge branch 'exportable-expense-api' into expense-sync-api
ruuushhh 8d18a8a
Expense sync APIs added
ruuushhh b9f254b
Merge branch 'master' into expense-sync-api
ruuushhh 42a62a4
Fyle orgs api (#33)
ruuushhh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
|
||
from datetime import datetime, timezone | ||
import logging | ||
|
||
from django.utils.module_loading import import_string | ||
|
||
from apps.workspaces.models import Workspace, BusinessCentralCredentials | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
# Import your Workspace and BusinessCentralCredentials models here | ||
# Also, make sure you have 'logger' defined and imported from a logging module | ||
def check_interval_and_sync_dimension(workspace: Workspace, business_central_credential: BusinessCentralCredentials) -> bool: | ||
""" | ||
Check the synchronization interval and trigger dimension synchronization if needed. | ||
|
||
:param workspace: Workspace Instance | ||
:param business_central_credential: BusinessCentralCredentials Instance | ||
|
||
:return: True if synchronization is triggered, False if not | ||
""" | ||
|
||
if workspace.destination_synced_at: | ||
# Calculate the time interval since the last destination sync | ||
time_interval = datetime.now(timezone.utc) - workspace.destination_synced_at | ||
|
||
if workspace.destination_synced_at is None or time_interval.days > 0: | ||
# If destination_synced_at is None or the time interval is greater than 0 days, trigger synchronization | ||
sync_dimensions(business_central_credential, workspace.id) | ||
return True | ||
|
||
return False | ||
|
||
|
||
def sync_dimensions(business_central_credential: BusinessCentralCredentials, workspace_id: int) -> None: | ||
""" | ||
Synchronize various dimensions with Business Central using the provided credentials. | ||
|
||
:param business_central_credential: BusinessCentralCredentials Instance | ||
:param workspace_id: ID of the workspace | ||
|
||
This function syncs dimensions like accounts, vendors, commitments, jobs, categories, and cost codes. | ||
""" | ||
|
||
# Initialize the Business Central connection using the provided credentials and workspace ID | ||
business_central_connection = import_string('apps.business_central.utils.BusinessCentralConnector')(business_central_credential, workspace_id) | ||
|
||
# List of dimensions to sync | ||
dimensions = ['accounts', 'vendors', 'employees', 'locations'] | ||
|
||
for dimension in dimensions: | ||
try: | ||
# Dynamically call the sync method based on the dimension | ||
sync = getattr(business_central_connection, 'sync_{}'.format(dimension)) | ||
sync() | ||
except Exception as exception: | ||
# Log any exceptions that occur during synchronization | ||
logger.info(exception) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import logging | ||
from django.db.models import Q | ||
from datetime import datetime | ||
from rest_framework import serializers | ||
from rest_framework.response import Response | ||
from rest_framework.views import status | ||
|
||
from fyle_accounting_mappings.models import DestinationAttribute | ||
|
||
from apps.workspaces.models import Workspace, BusinessCentralCredentials | ||
from apps.business_central.helpers import sync_dimensions, check_interval_and_sync_dimension | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
class ImportBusinessCentralAttributesSerializer(serializers.Serializer): | ||
""" | ||
Import Business Central Attributes serializer | ||
""" | ||
|
||
def create(self, validated_data): | ||
try: | ||
# Get the workspace ID from the URL kwargs | ||
workspace_id = self.context['request'].parser_context['kwargs']['workspace_id'] | ||
|
||
# Check if the 'refresh' field is provided in the request data | ||
refresh_dimension = self.context['request'].data.get('refresh', False) | ||
|
||
# Retrieve the workspace and Business Central credentials | ||
workspace = Workspace.objects.get(pk=workspace_id) | ||
business_central_credentials = BusinessCentralCredentials.objects.get( | ||
workspace_id=workspace.id | ||
) | ||
|
||
if refresh_dimension: | ||
# If 'refresh' is true, perform a full sync of dimensions | ||
sync_dimensions(business_central_credentials, workspace.id) | ||
else: | ||
# If 'refresh' is false, check the interval and sync dimension accordingly | ||
check_interval_and_sync_dimension(workspace, business_central_credentials) | ||
|
||
# Update the destination_synced_at field and save the workspace | ||
workspace.destination_synced_at = datetime.now() | ||
workspace.save(update_fields=['destination_synced_at']) | ||
|
||
# Return a success response | ||
return Response(status=status.HTTP_200_OK) | ||
|
||
except BusinessCentralCredentials.DoesNotExist: | ||
# Handle the case when business central credentials are not found or invalid | ||
raise serializers.ValidationError( | ||
{'message': 'Business Central credentials not found / invalid in workspace'} | ||
) | ||
|
||
except Exception as exception: | ||
# Handle unexpected exceptions and log the error | ||
logger.error( | ||
'Something unexpected happened workspace_id: %s %s', | ||
workspace_id, | ||
exception, | ||
) | ||
# Raise a custom exception or re-raise the original exception | ||
raise | ||
|
||
|
||
class BusinessCentralFieldSerializer(serializers.Serializer): | ||
""" | ||
Business Central Fields Serializer | ||
""" | ||
|
||
attribute_type = serializers.CharField() | ||
display_name = serializers.CharField() | ||
|
||
def format_business_central_fields(self, workspace_id): | ||
attribute_types = [ | ||
"VENDOR", | ||
"ACCOUNT", | ||
"EMPLOYEE", | ||
"LOCATION", | ||
] | ||
attributes = ( | ||
DestinationAttribute.objects.filter( | ||
~Q(attribute_type__in=attribute_types), | ||
workspace_id=workspace_id, | ||
) | ||
.values("attribute_type", "display_name") | ||
.distinct() | ||
) | ||
|
||
serialized_attributes = BusinessCentralFieldSerializer(attributes, many=True).data | ||
|
||
attributes_list = list(serialized_attributes) | ||
|
||
return attributes_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.urls import path | ||
|
||
from apps.business_central.views import ImportBusinessCentralAttributesView, BusinessCentralFieldsView | ||
|
||
|
||
urlpatterns = [ | ||
path( | ||
"import_attributes/", | ||
ImportBusinessCentralAttributesView.as_view(), | ||
name="import-business-central-attributes", | ||
), | ||
path("fields/", BusinessCentralFieldsView.as_view(), name="business-central-fields"), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from rest_framework import generics | ||
|
||
from apps.business_central.serializers import ImportBusinessCentralAttributesSerializer, BusinessCentralFieldSerializer | ||
|
||
|
||
class ImportBusinessCentralAttributesView(generics.CreateAPIView): | ||
""" | ||
Import Business Central Attributes View | ||
""" | ||
serializer_class = ImportBusinessCentralAttributesSerializer | ||
|
||
|
||
class BusinessCentralFieldsView(generics.ListAPIView): | ||
""" | ||
Business Central Fields View | ||
""" | ||
serializer_class = BusinessCentralFieldSerializer | ||
pagination_class = None | ||
|
||
def get_queryset(self): | ||
return BusinessCentralFieldSerializer().format_business_central_fields(self.kwargs["workspace_id"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import logging | ||
import traceback | ||
from functools import wraps | ||
|
||
from fyle.platform.exceptions import NoPrivilegeError | ||
|
||
from apps.workspaces.models import FyleCredential | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
def handle_exceptions(func): | ||
@wraps(func) | ||
def wrapper(*args, **kwargs): | ||
try: | ||
return func(*args, **kwargs) | ||
except FyleCredential.DoesNotExist: | ||
logger.info('Fyle credentials not found %s', args[0]) # args[1] is workspace_id | ||
args[1].detail = {'message': 'Fyle credentials do not exist in workspace'} | ||
args[1].status = 'FAILED' | ||
args[1].save() | ||
|
||
except NoPrivilegeError: | ||
logger.info('Invalid Fyle Credentials / Admin is disabled') | ||
args[1].detail = {'message': 'Invalid Fyle Credentials / Admin is disabled'} | ||
args[1].status = 'FAILED' | ||
args[1].save() | ||
|
||
except Exception: | ||
error = traceback.format_exc() | ||
args[1].detail = {'error': error} | ||
args[1].status = 'FATAL' | ||
args[1].save() | ||
logger.exception('Something unexpected happened workspace_id: %s %s', args[0], args[1].detail) | ||
|
||
return wrapper | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
handle_exceptions
decorator assumes a specific order and type of arguments, which could lead to issues if the decorated function does not follow this pattern. Consider making the decorator more flexible to handle different argument orders or types, and ensure that exceptions are re-raised after logging and updating the status to notify the calling code of the exception.+ raise
Committable suggestion