예제 #1
0
def auto_create_project_mappings(workspace_id: int):
    """
    Create Project Mappings
    :return: mappings
    """
    try:
        fyle_credentials: FyleCredential = FyleCredential.objects.get(
            workspace_id=workspace_id)
        qbo_credentials: QBOCredential = QBOCredential.objects.get(
            workspace_id=workspace_id)

        fyle_connection = FyleConnector(
            refresh_token=fyle_credentials.refresh_token,
            workspace_id=workspace_id)

        qbo_connection = QBOConnector(credentials_object=qbo_credentials,
                                      workspace_id=workspace_id)

        fyle_connection.sync_projects()
        qbo_connection.sync_customers()

        post_projects_in_batches(fyle_connection, workspace_id)

    except WrongParamsError as exception:
        logger.error(
            'Error while creating projects workspace_id - %s in Fyle %s %s',
            workspace_id, exception.message, {'error': exception.response})

    except Exception:
        error = traceback.format_exc()
        error = {'error': error}
        logger.error(
            'Error while creating projects workspace_id - %s error: %s',
            workspace_id, error)
예제 #2
0
def schedule_export(enterprise_id, user):
    google_sheet_object = GoogleSpreadSheet()

    export = Export.objects.filter(enterprise_id=enterprise_id).first()

    sheet_id = export.google_sheets_link if export else google_sheet_object.create_sheet(
    )

    export, _ = Export.objects.update_or_create(enterprise_id=enterprise_id,
                                                defaults={
                                                    'status':
                                                    DEFAULT_SYNC_STATUS,
                                                    'google_sheets_link':
                                                    sheet_id,
                                                })

    fyle_credentials = AuthToken.objects.get(user__user_id=user)

    fyle_connector = FyleConnector(fyle_credentials.refresh_token)

    created_job = fyle_connector.trigger_job(
        callback_url='{0}{1}'.format(
            settings.API_URL,
            '/enterprises/{0}/exports/'.format(enterprise_id)),
        callback_method='POST',
        object_id=export.id,
        payload={},
        job_description='Running export for Enterprise id: {0}'.format(
            enterprise_id),
        org_user_id=fyle_connector.get_employee_profile()['id'])

    export.job_id = created_job['id']
    export.save()
    return created_job
예제 #3
0
def post_projects_in_batches(fyle_connection: FyleConnector,
                             workspace_id: int):
    existing_project_names = ExpenseAttribute.objects.filter(
        attribute_type='PROJECT',
        workspace_id=workspace_id).values_list('value', flat=True)
    qbo_attributes_count = DestinationAttribute.objects.filter(
        attribute_type='CUSTOMER', workspace_id=workspace_id).count()

    page_size = 200
    for offset in range(0, qbo_attributes_count, page_size):
        limit = offset + page_size
        paginated_qbo_attributes = DestinationAttribute.objects.filter(
            attribute_type='CUSTOMER',
            workspace_id=workspace_id).order_by('value', 'id')[offset:limit]

        paginated_qbo_attributes = remove_duplicates(paginated_qbo_attributes)

        fyle_payload: List[Dict] = create_fyle_projects_payload(
            paginated_qbo_attributes, existing_project_names, workspace_id)
        if fyle_payload:
            fyle_connection.connection.Projects.post(fyle_payload)
            fyle_connection.sync_projects()

        Mapping.bulk_create_mappings(paginated_qbo_attributes, 'PROJECT',
                                     'CUSTOMER', workspace_id)
예제 #4
0
def async_auto_map_employees(workspace_id: int):
    general_settings = WorkspaceGeneralSettings.objects.get(
        workspace_id=workspace_id)
    employee_mapping_preference = general_settings.auto_map_employees

    mapping_setting = MappingSetting.objects.filter(
        ~Q(destination_field='CREDIT_CARD_ACCOUNT'),
        source_field='EMPLOYEE',
        workspace_id=workspace_id).first()
    destination_type = mapping_setting.destination_field

    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
    fyle_connection = FyleConnector(
        refresh_token=fyle_credentials.refresh_token,
        workspace_id=workspace_id)

    qbo_credentials = QBOCredential.objects.get(workspace_id=workspace_id)
    qbo_connection = QBOConnector(credentials_object=qbo_credentials,
                                  workspace_id=workspace_id)

    fyle_connection.sync_employees()
    if destination_type == 'EMPLOYEE':
        qbo_connection.sync_employees()
    else:
        qbo_connection.sync_vendors()

    Mapping.auto_map_employees(destination_type, employee_mapping_preference,
                               workspace_id)
예제 #5
0
def async_auto_map_employees(workspace_id: int):
    try:
        employee_mapping_preference = WorkspaceGeneralSettings.objects.get(
            workspace_id=workspace_id).auto_map_employees

        fyle_credentials = FyleCredential.objects.get(
            workspace_id=workspace_id)
        fyle_connection = FyleConnector(
            refresh_token=fyle_credentials.refresh_token,
            workspace_id=workspace_id)

        xero_credentials = XeroCredentials.objects.get(
            workspace_id=workspace_id)
        xero_connection = XeroConnector(xero_credentials,
                                        workspace_id=workspace_id)

        fyle_connection.sync_employees()
        xero_connection.sync_contacts()

        Mapping.auto_map_employees('CONTACT', employee_mapping_preference,
                                   workspace_id)

    except XeroCredentials.DoesNotExist:
        logger.error(
            'Xero Credentials not found for workspace_id %s',
            workspace_id,
        )
예제 #6
0
    def get(self, request, *args, **kwargs):
        """
        Get User Details
        """
        fyle_credentials = AuthToken.objects.get(user__user_id=request.user)

        fyle_connector = FyleConnector(fyle_credentials.refresh_token)

        employee_profile = fyle_connector.get_employee_profile()

        return Response(data=employee_profile, status=status.HTTP_200_OK)
예제 #7
0
def process_reimbursements(workspace_id):
    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)

    fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                   workspace_id)

    fyle_connector.sync_reimbursements()

    reimbursements = Reimbursement.objects.filter(
        state='PENDING', workspace_id=workspace_id).all()

    reimbursement_ids = []

    if reimbursements:
        for reimbursement in reimbursements:
            expenses = Expense.objects.filter(
                settlement_id=reimbursement.settlement_id,
                fund_source='PERSONAL').all()
            paid_expenses = expenses.filter(paid_on_qbo=True)

            all_expense_paid = False
            if len(expenses):
                all_expense_paid = len(expenses) == len(paid_expenses)

            if all_expense_paid:
                reimbursement_ids.append(reimbursement.reimbursement_id)

    if reimbursement_ids:
        fyle_connector.post_reimbursement(reimbursement_ids)
        fyle_connector.sync_reimbursements()
예제 #8
0
def async_auto_map_ccc_account(workspace_id: str):
    general_mappings = GeneralMapping.objects.get(workspace_id=workspace_id)
    default_ccc_account_id = general_mappings.default_ccc_account_id

    if default_ccc_account_id:
        fyle_credentials = FyleCredential.objects.get(
            workspace_id=workspace_id)
        fyle_connection = FyleConnector(
            refresh_token=fyle_credentials.refresh_token,
            workspace_id=workspace_id)
        fyle_connection.sync_employees()

        Mapping.auto_map_ccc_employees('CREDIT_CARD_ACCOUNT',
                                       default_ccc_account_id, workspace_id)
예제 #9
0
    def get(self, request, *args, **kwargs):
        """
        Get cluster domain from Fyle
        """
        try:
            fyle_credentials = AuthToken.objects.get(
                user__user_id=request.user)
            fyle_connector = FyleConnector(fyle_credentials.refresh_token)
            cluster_domain = fyle_connector.get_cluster_domain(
            )['cluster_domain']

            return Response(data=cluster_domain, status=status.HTTP_200_OK)
        except FyleCredential.DoesNotExist:
            return Response(data={'message': 'Invalid / Expired Token'},
                            status=status.HTTP_400_BAD_REQUEST)
예제 #10
0
def get_expense_purpose(workspace_id, lineitem, category) -> str:
    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
    fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                   workspace_id)

    cluster_domain = fyle_connector.get_cluster_domain()
    org_id = Workspace.objects.get(id=workspace_id).fyle_org_id

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

    expense_purpose = ' purpose - {0}'.format(
        lineitem.purpose) if lineitem.purpose else ''
    spent_at = ' spent on {0} '.format(
        lineitem.spent_at.date()) if lineitem.spent_at else ''
    merchant = ' spent on merchant {0}'.format(
        lineitem.vendor) if lineitem.vendor else ''
    return 'Expense by {0}{1} against category {2}{3} with claim number - {4} -{5} - {6}'.format(
        lineitem.employee_email, merchant, category, spent_at,
        lineitem.claim_number, expense_purpose, expense_link)
예제 #11
0
    def write_data(self, orgs, sheet_id):
        data_to_export, total_orgs = [get_headers()], len(orgs)

        for org in orgs.values():
            fyle_connector = FyleConnector(org['refresh_token'])
            expenses = fyle_connector.get_expenses()
            data_to_export.extend(format_expenses(expenses))

        data = {'values': data_to_export}
        self.service.spreadsheets().values().clear(spreadsheetId=sheet_id,
                                                   range=self.range).execute()
        request = self.service.spreadsheets().values().append(
            spreadsheetId=sheet_id,
            range=self.range,
            valueInputOption='RAW',
            body=data)
        response = request.execute()
        rows = len(data_to_export) - 1
        if response:
            return True, rows, total_orgs
        return False, 0, total_orgs
예제 #12
0
def upload_categories_to_fyle(workspace_id):
    """
    Upload categories to Fyle
    """
    fyle_credentials: FyleCredential = FyleCredential.objects.get(
        workspace_id=workspace_id)
    qbo_credentials: QBOCredential = QBOCredential.objects.get(
        workspace_id=workspace_id)

    fyle_connection = FyleConnector(
        refresh_token=fyle_credentials.refresh_token,
        workspace_id=workspace_id)

    qbo_connection = QBOConnector(credentials_object=qbo_credentials,
                                  workspace_id=workspace_id)
    fyle_connection.sync_categories(False)
    qbo_connection.sync_accounts()
    qbo_attributes: List[
        DestinationAttribute] = DestinationAttribute.objects.filter(
            workspace_id=workspace_id, attribute_type='ACCOUNT').all()
    qbo_attributes = remove_duplicates(qbo_attributes)

    fyle_payload: List[Dict] = create_fyle_categories_payload(
        qbo_attributes, workspace_id)

    if fyle_payload:
        fyle_connection.connection.Categories.post(fyle_payload)
        fyle_connection.sync_categories(False)

    return qbo_attributes
예제 #13
0
def schedule_sync(workspace_id: int, schedule_enabled: bool, hours: int,
                  next_run: str, user: str):
    ws_settings, _ = WorkspaceSettings.objects.get_or_create(
        workspace_id=workspace_id)

    start_datetime = datetime.strptime(next_run, '%Y-%m-%dT%H:%M:00.000Z')

    if not ws_settings.schedule and schedule_enabled:
        schedule = WorkspaceSchedule.objects.create(
            enabled=schedule_enabled,
            interval_hours=hours,
            start_datetime=start_datetime)
        ws_settings.schedule = schedule

        created_job = create_schedule_job(workspace_id=workspace_id,
                                          schedule=schedule,
                                          user=user,
                                          start_datetime=start_datetime,
                                          hours=hours)

        ws_settings.schedule.fyle_job_id = created_job['id']
        ws_settings.schedule.save()

        ws_settings.save(update_fields=['schedule'])
    else:
        ws_settings.schedule.enabled = schedule_enabled
        ws_settings.schedule.start_datetime = start_datetime
        ws_settings.schedule.interval_hours = hours

        fyle_credentials = FyleCredential.objects.get(
            workspace_id=workspace_id)
        fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                       workspace_id)
        fyle_sdk_connection = fyle_connector.connection

        jobs = FyleJobsSDK(settings.FYLE_JOBS_URL, fyle_sdk_connection)
        if ws_settings.schedule.fyle_job_id:
            jobs.delete_job(ws_settings.schedule.fyle_job_id)

        if schedule_enabled:
            created_job = create_schedule_job(workspace_id=workspace_id,
                                              schedule=ws_settings.schedule,
                                              user=user,
                                              start_datetime=start_datetime,
                                              hours=hours)
            ws_settings.schedule.fyle_job_id = created_job['id']
        else:
            ws_settings.schedule.fyle_job_id = None

        ws_settings.schedule.save()

    return ws_settings
예제 #14
0
def load_attachments(qbo_connection: QBOConnector, ref_id: str, ref_type: str,
                     expense_group: ExpenseGroup):
    """
    Get attachments from fyle
    :param qbo_connection: QBO 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=expense_group.workspace_id)
        expense_ids = expense_group.expenses.values_list('expense_id',
                                                         flat=True)
        fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                       expense_group.workspace_id)
        attachments = fyle_connector.get_attachments(expense_ids)
        qbo_connection.post_attachments(ref_id, ref_type, attachments)
    except Exception:
        error = traceback.format_exc()
        logger.error(
            'Attachment failed for expense group id %s / workspace id %s \n Error: %s',
            expense_group.id, expense_group.workspace_id, {'error': error})
예제 #15
0
def load_attachments(sage_intacct_connection: SageIntacctConnector, key: str,
                     expense_group: ExpenseGroup):
    """
    Get attachments from fyle
    :param sage_intacct_connection: Sage Intacct Connection
    :param key: expense report / bills key
    :param expense_group: Expense group
    """
    try:
        fyle_credentials = FyleCredential.objects.get(
            workspace_id=expense_group.workspace_id)
        expense_ids = expense_group.expenses.values_list('expense_id',
                                                         flat=True)
        fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                       expense_group.workspace_id)
        claim_number = expense_group.description.get('claim_number')
        attachments = fyle_connector.get_attachments(expense_ids)
        return sage_intacct_connection.post_attachments(
            attachments, claim_number)
    except Exception:
        error = traceback.format_exc()
        logger.error(
            'Attachment failed for expense group id %s / workspace id %s \n Error: %s',
            expense_group.id, expense_group.workspace_id, error)
예제 #16
0
def schedule_bills_creation(workspace_id: int, expense_group_ids: List[str],
                            user):
    """
    Schedule bills creation
    :param expense_group_ids: List of expense group ids
    :param workspace_id: workspace id
    :param user: user email
    :return: None
    """
    if expense_group_ids:
        expense_groups = ExpenseGroup.objects.filter(
            workspace_id=workspace_id,
            id__in=expense_group_ids,
            bill__id__isnull=True).all()
    else:
        expense_groups = ExpenseGroup.objects.filter(
            workspace_id=workspace_id, bill__id__isnull=True).all()

    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
    fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                   workspace_id)
    fyle_sdk_connection = fyle_connector.connection
    jobs = FyleJobsSDK(settings.FYLE_JOBS_URL, fyle_sdk_connection)

    for expense_group in expense_groups:
        task_log, _ = TaskLog.objects.update_or_create(
            workspace_id=expense_group.workspace_id,
            expense_group=expense_group,
            defaults={
                'status': 'IN_PROGRESS',
                'type': 'CREATING_BILL'
            })
        created_job = jobs.trigger_now(
            callback_url='{0}{1}'.format(
                settings.API_URL,
                '/workspaces/{0}/qbo/bills/'.format(workspace_id)),
            callback_method='POST',
            object_id=task_log.id,
            payload={
                'expense_group_id': expense_group.id,
                'task_log_id': task_log.id
            },
            job_description=
            'Create Bill: Workspace id - {0}, user - {1}, expense group id - {2}'
            .format(workspace_id, user, expense_group.id))
        task_log.task_id = created_job['id']
        task_log.save()
예제 #17
0
def schedule_expense_reports_creation(workspace_id: int,
                                      expense_group_ids: List[str], user):
    """
    Schedule expense reports creation
    :param expense_group_ids: List of expense group ids
    :param workspace_id: workspace id
    :param user: user email
    :return: None
    """
    if expense_group_ids:
        expense_groups = ExpenseGroup.objects.filter(
            workspace_id=workspace_id,
            id__in=expense_group_ids,
            expensereport__id__isnull=True).all()
    else:
        expense_groups = ExpenseGroup.objects.filter(
            workspace_id=workspace_id, expensereport__id__isnull=True).all()

    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
    fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                   workspace_id)
    fyle_sdk_connection = fyle_connector.connection
    jobs = fyle_sdk_connection.Jobs
    user_profile = fyle_sdk_connection.Employees.get_my_profile()['data']

    for expense_group in expense_groups:
        task_log, _ = TaskLog.objects.update_or_create(
            workspace_id=expense_group.workspace_id,
            expense_group=expense_group,
            defaults={
                'status': 'IN_PROGRESS',
                'type': 'CREATING_EXPENSE_REPORTS'
            })
        created_job = jobs.trigger_now(
            callback_url='{0}{1}'.format(settings.API_URL, \
                '/workspaces/{0}/sage_intacct/expense_reports/'.format(workspace_id)),
            callback_method='POST', object_id=task_log.id, payload={
                'expense_group_id': expense_group.id,
                'task_log_id': task_log.id
            }, job_description='Create Expense Report: Workspace id - {0}, user - {1}, expense group id - {2}'.format(
                workspace_id, user, expense_group.id
            ),
            org_user_id=user_profile['id']
        )
        task_log.task_id = created_job['id']
        task_log.save()
예제 #18
0
def create_schedule_job(workspace_id: int, schedule: WorkspaceSchedule,
                        user: str, start_datetime: datetime, hours: int):
    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
    fyle_connector = FyleConnector(fyle_credentials.refresh_token)
    fyle_sdk_connection = fyle_connector.connection

    jobs = FyleJobsSDK(settings.FYLE_JOBS_URL, fyle_sdk_connection)

    created_job = jobs.trigger_interval(
        callback_url='{0}{1}'.format(
            settings.API_URL,
            '/workspaces/{0}/schedule/trigger/'.format(workspace_id)),
        callback_method='POST',
        object_id=schedule.id,
        job_description='Fetch expenses: Workspace id - {0}, user - {1}'.
        format(workspace_id, user),
        start_datetime=start_datetime.strftime('%Y-%m-%d %H:%M:00.00'),
        hours=hours)
    return created_job
예제 #19
0
def upload_categories_to_fyle(workspace_id):
    """
    Upload categories to Fyle
    """
    try:
        fyle_credentials: FyleCredential = FyleCredential.objects.get(
            workspace_id=workspace_id)
        xero_credentials: XeroCredentials = XeroCredentials.objects.get(
            workspace_id=workspace_id)

        fyle_connection = FyleConnector(
            refresh_token=fyle_credentials.refresh_token,
            workspace_id=workspace_id)

        xero_connection = XeroConnector(credentials_object=xero_credentials,
                                        workspace_id=workspace_id)
        fyle_connection.sync_categories(False)
        xero_connection.sync_accounts()

        xero_attributes = DestinationAttribute.objects.filter(
            attribute_type='ACCOUNT', workspace_id=workspace_id)

        xero_attributes = remove_duplicates(xero_attributes)

        fyle_payload: List[Dict] = create_fyle_categories_payload(
            xero_attributes, workspace_id)

        if fyle_payload:
            fyle_connection.connection.Categories.post(fyle_payload)
            fyle_connection.sync_categories(False)

        return xero_attributes

    except XeroCredentials.DoesNotExist:
        logger.error(
            'Xero Credentials not found for workspace_id %s',
            workspace_id,
        )
예제 #20
0
def create_bill_payment(workspace_id):
    fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)

    fyle_connector = FyleConnector(fyle_credentials.refresh_token,
                                   workspace_id)

    fyle_connector.sync_reimbursements()

    bills = Bill.objects.filter(payment_synced=False,
                                expense_group__workspace_id=workspace_id,
                                expense_group__fund_source='PERSONAL').all()

    if bills:
        for bill in bills:
            expense_group_reimbursement_status = check_expenses_reimbursement_status(
                bill.expense_group.expenses.all())
            if expense_group_reimbursement_status:
                task_log, _ = TaskLog.objects.update_or_create(
                    workspace_id=workspace_id,
                    task_id='PAYMENT_{}'.format(bill.expense_group.id),
                    defaults={
                        'status': 'IN_PROGRESS',
                        'type': 'CREATING_BILL_PAYMENT'
                    })
                try:
                    with transaction.atomic():

                        bill_payment_object = BillPayment.create_bill_payment(
                            bill.expense_group)

                        qbo_object_task_log = TaskLog.objects.get(
                            expense_group=bill.expense_group)

                        linked_transaction_id = qbo_object_task_log.detail[
                            'Bill']['Id']

                        bill_payment_lineitems_objects = BillPaymentLineitem.create_bill_payment_lineitems(
                            bill_payment_object.expense_group,
                            linked_transaction_id)

                        qbo_credentials = QBOCredential.objects.get(
                            workspace_id=workspace_id)

                        qbo_connection = QBOConnector(qbo_credentials,
                                                      workspace_id)

                        created_bill_payment = qbo_connection.post_bill_payment(
                            bill_payment_object,
                            bill_payment_lineitems_objects)

                        bill.payment_synced = True
                        bill.paid_on_qbo = True
                        bill.save()

                        task_log.detail = created_bill_payment
                        task_log.bill_payment = bill_payment_object
                        task_log.status = 'COMPLETE'

                        task_log.save()

                except QBOCredential.DoesNotExist:
                    logger.error(
                        'QBO Credentials not found for workspace_id %s / expense group %s',
                        workspace_id, bill.expense_group)
                    detail = {
                        'expense_group_id': bill.expense_group,
                        'message': 'QBO Account not connected'
                    }
                    task_log.status = 'FAILED'
                    task_log.detail = detail

                    task_log.save()

                except BulkError as exception:
                    logger.error(exception.response)
                    detail = exception.response
                    task_log.status = 'FAILED'
                    task_log.detail = detail

                    task_log.save()

                except WrongParamsError as exception:
                    logger.error(exception.response)
                    detail = json.loads(exception.response)
                    task_log.status = 'FAILED'
                    task_log.detail = detail

                    task_log.save()

                except Exception:
                    error = traceback.format_exc()
                    task_log.detail = {'error': error}
                    task_log.status = 'FATAL'
                    task_log.save()
                    logger.error(
                        'Something unexpected happened workspace_id: %s %s',
                        task_log.workspace_id, task_log.detail)