def post(self, request, pk, *args, **kwargs): """ Parses the income statement and balance sheet sheet request, sent in the form of: {"IncomeStatement": [{ "Period": "2016-01", "Account": "Value"},{..}] } """ try: company = AccountsUtils.get_company(pk) try: entries, error_tags = AccountingUtils.parse_statement(request, company, request.data, FinancialStatementEntry.INCOME_STATEMENT) FinancialStatementEntry.objects.bulk_create(entries) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) serializer = FinancialStatementEntrySerializer(entries, many=True) if len(serializer.data) > 0: return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'NO_DATA_CHANGES') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def refresh(self, pk, request): """ Refresh the token :param pk: Company ID :return: Response """ try: company = AccountsUtils.get_company(pk) credentials = AccountingUtils.get_credentials_by_company(company) refresh_token = credentials.refreshToken if refresh_token is None: return Utils.dispatch_failure(request, 'NO_TOKEN_AUTHENTICATION') bearer = Utils.get_bearer_token_from_refresh_token(refresh_token) if bearer is "failure": return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") if isinstance(bearer, str): return Utils.dispatch_success(request, bearer) else: token_info = AccountingOauth2.objects.filter( company=company).first() AccountingUtils.updateAccountingSession( company, bearer.accessToken, bearer.refreshToken, token_info.realmId) return Utils.dispatch_success(request, "CREDENTIALS_UPDATED") except Exception as e: return Utils.dispatch_failure(request, 'NO_TOKEN_AUTHENTICATION')
def post(self, request, pk, *args, **kwargs): """ Starts a new monthly report for the current reporting period, and moves next period ahead by 1 month to prepare for the next time. Will not create one that already exists, or create another one until the previous one is completed, this is a business rule. """ try: company = AccountsUtils.get_company(pk) response_status = status.HTTP_200_OK response = '' meta = CompanyMeta.objects.filter(company_id=pk).first() current_period = meta.monthly_reporting_current_period next_period = meta.monthly_reporting_next_period if current_period is None: Utils.send_company_meta(request.user,"PERIOD") return Utils.dispatch_failure(request,'MISSING_MONTHLY_REPORTING_CURRENT_PERIOD') try: # does one exist for the current period report = MonthlyReport.objects.filter(company=company, period_ending=current_period).first() print('######## CURR AND NEXT - start as: c: ', current_period, ' n: ', next_period, ' rpt: ', report) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure (request,'DATA_PARSING_ISSUE', error) # if it doesn't exist, or if the previous one has been completed, then it's ok to create a new one if not report or report.status == 'Complete': # todo: Due date is not relevant, remove it. We allow them to report any time after the end of the current # during initial setup, we do not advance the reporting dates. There is no initial setup for Form Entry # aka manual reporting if not meta.is_initial_setup: print('#### moving period forward for new monthly report') current_period = next_period next_period = next_period + relativedelta(months=+1, day=31) meta.monthly_reporting_current_period = current_period meta.monthly_reporting_next_period = next_period meta.monthly_reporting_current_period_status = 'IN_PROGRESS' meta.save() print('######## CURR AND NEXT - not initial setup, after move forward ', current_period, next_period) print('#### creating new monthly report for current period = ', meta.monthly_reporting_current_period) report = MonthlyReport(status='In Progress', company_id=pk, period_ending=current_period) report.save() response = serializers.serialize('json', [report, ]) # return response return Utils.dispatch_success(request,[response]) else: # was using HTTP_304_NOT_MODIFIED, but when this status is returned the JSON message is null which causes # problems for the UI # 409 causes the UI to stop processing because it thinks theres an error, but there isn't one. # so changing to 200 OK instead since it's OK to move forward # response_status = status.HTTP_409_CONFLICT return Utils.dispatch_success(request,'MONTHLY_REPORT_ALREADY_EXISTS_WITH_INPROGRESS') except Exception as e: return Utils.dispatch_failure (request,'INTERNAL_SERVER_ERROR')
def get(self, request, pk, report_identifier): """ Gets a Company's Questionnaire answers for the requested period If called with no period specified, it simply returns a blank set of questions. The functionality supports the UI which dynamically displays the questions returned by the server. """ try: response = None response_status = status.HTTP_200_OK context = {'request': request, 'company': pk, 'period': report_identifier} print('############### BEFORE INT COMPARISON') # todo: logic needs to be cleaned up. questions and has_answers have overlapping functionality that can be # simplified questions = ReportingUtils.get_questionnaire_objects(pk, report_identifier) has_answers = ReportingUtils.has_answers_for_period(pk, report_identifier) if questions and has_answers: # return Q n A for requested period. serializer = QuestionWithAnswerSerializer(questions, many=True, context=context) if len(serializer.data) > 0: return Utils.dispatch_success(request,serializer.data) return Utils.dispatch_success(request,'NO_ANSWER_FOUND') return Utils.dispatch_success (request, 'DATA_NOT_FOUND') except Exception as e: return Utils.dispatch_failure(request,'INTERNAL_SERVER_ERROR')
def get(self, request, pk): """ Gets a Company's Questionnaire answers """ try: response = None response_status = status.HTTP_200_OK context = {'request': request, 'company': pk, } # get an empty set of questions qs1 = Question.objects.filter(Q(common_to_all_companies=True, show_on_ui=True) | Q(common_to_all_companies=False,show_on_ui=True,company=pk))\ .all() qs1 = ReportingUtils.sanitize_next_questions(qs1) serializer = QuestionWithAnswerSerializer(qs1, many=True, context=context) if len (serializer.data) > 0: return Utils.dispatch_success (request,serializer.data) return Utils.dispatch_success (request,'NO_ANSWER_FOUND') except Exception as e: return Utils.dispatch_failure(request,'INTERNAL_SERVER_ERROR')
def get(self, request, pk, *args, **kwargs): """ Creates the CoAMap for a company from the CoA in the database """ try: company = AccountsUtils.get_company(pk) coa = CoA.objects.filter(company=company) remap = request.GET.get('remap', None) if coa: default_mappings = DefaultAccountTagMapping.objects.filter(software__iexact=company.accounting_type) if not default_mappings: return Utils.dispatch_failure(request, "OBJECT_RESOURCE_NOT_FOUND") # todo: make sure abstract financial statement entry tags are not getting processed in the CoA Mapping try: entries = AccountingUtils.create_coa_map(request, company, default_mappings, coa, remap) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) if len(entries) > 0: serializer = CoAMapSerializer(entries, many=True) return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, "NO_DATA_CHANGES") return Utils.dispatch_failure(request, "OBJECT_RESOURCE_NOT_FOUND") except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def post(self, request, pk, *args, **kwargs): """ Creates Trial Balance from Posted json request, follows the format {"Header": {"Time": <timestamp>, "StartPeriod: "YYYY-MM-DD", "EndPeriod": "YYYY-MM-DD", "Currency"...}, "Columns": { "Column": [ {"ColTitle": "Title", "ColType": "AccountType"}, {..}], "Rows": {"Row": [ {"ColData": [{"value": "Account", "id": <int>}, {..}], { "ColData"...}] } More info here: https://developer.intuit.com/docs/api/accounting/trial%20balance """ try: company = AccountsUtils.get_company(pk) if 'file' in request.data: file_type = CSVUtils.file_type(request.FILES['file']) if not file_type: return Utils.dispatch_failure(request, "INVALID_FILE_FORMAT") request.FILES['file'].seek(0) if file_type == 'CSV': print('#--- processing tb csv') try: csv_data = CSVUtils.format_request_csv(request.FILES['file']) tb_data = CSVUtils.process_trial_balance_csv(company, csv_data) if not tb_data: return Utils.dispatch_failure(request, 'INVALID_TB_DATE') if tb_data is "INVALID_CSV": return Utils.dispatch_failure(request, 'INVALID_TB_CSV') serializer = TrialBalanceSerializer(tb_data, many=True) if len(serializer.data) > 0: return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'NO_DATA_CHANGES') except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) elif file_type == 'Excel': try: excel_data = CSVUtils.format_request_excel(request.FILES) # wont be found on heroku with contextlib.suppress(FileNotFoundError): os.remove('tmp.xlsx') except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) # return Response({"message": "Sent a CSV"}) else: try: entries = QuickBooks.save_trial_balance(company, request.data) if entries is None: return Utils.dispatch_failure(request, 'INVALID_TB_DATE') TrialBalance.objects.bulk_create(entries) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) return Utils.dispatch_success(request, 'TRIAL_BALANCE_SAVE_SUCCESS') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def post(self, request, pk, *args, **kwargs): try: company = AccountsUtils.get_company(pk) # print('CoAMap POST - request.data ', request.data) updated_maps = AccountingUtils.set_coa_map(company, request.data) serializer = UpdatedCoAMapSerializer(updated_maps, many=True) if len(serializer.data): return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, "NO_DATA_CHANGES") except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def delete(self, request, pk, *args, **kwargs): try: if self.request.user.is_superuser: company = AccountsUtils.get_company(pk) if FinancialStatementEntry.objects.filter(company=company, statement_type=FinancialStatementEntry.INCOME_STATEMENT, ).count(): FinancialStatementEntry.objects.filter(company=company, statement_type=FinancialStatementEntry.INCOME_STATEMENT, ).delete() return Utils.dispatch_success(request, 'DELETED_SUCCESSFULLY') return Utils.dispatch_success(request, "DATA_NOT_FOUND") return Utils.dispatch_failure(request, 'UNAUTHORIZED_ACCESS') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def trail_balance(self, id, request): """ Get Trail Balance From Database :param id: Company Id :return: Response """ try: entries = TrialBalance.objects.filter(company=id) serializer = TrialBalanceSerializer(entries, many=True) if len(serializer.data) > 0: return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'NO_DATA_CHANGES') except Exception: return Utils.dispatch_failure(request, "INTERNAL_SERVER_ERROR")
def put(self, request, pk, report_identifier, *args, **kwargs): """ Updates a Company's Monthly Report instance Monthly Report Period or Monthly Report ID """ try: company = AccountsUtils.get_company(pk) if '-' in report_identifier: monthly_report = ReportingUtils.get_monthly_report(pk=pk, period=report_identifier) else: monthly_report = ReportingUtils.get_monthly_report(pk=pk, report_id=report_identifier) response_status = status.HTTP_200_OK # default to OK and override if not if monthly_report: data = request.data data['company'] = company.id serializer = MonthlyReportSerializer(monthly_report, data=data, context={'request': request, 'company_id': pk}, partial=True) if serializer.is_valid(): serializer.save() return Utils.dispatch_success(request,serializer.data) else: return Utils.dispatch_failure (request,'VALIDATION_ERROR',serializer.errors) return Utils.dispatch_failure(request,'MONTHLY_REPORT_NOT_FOUND') except Exception as e: return Utils.dispatch_failure (request,'INTERNAL_SERVER_ERROR')
def is_token_valid(self, pk, request): """ Check the validity of Token :param pk: Company ID :return: Response of validation """ try: company = AccountsUtils.get_company(pk) credentials = AccountingOauth2.objects.filter( company=company).first() if credentials: sample_response, status_code = Utils.get_company_info( credentials.accessToken, credentials.realmId) if status_code >= 400: bearer = get(credentials.refreshToken) new_credentials = AccountingUtils.updateAccountingSession( company, bearer.accessToken, bearer.refreshToken, credentials.realmId) sample_response, status_code = Utils.get_company_info( new_credentials.accessToken, new_credentials.realmId) if status_code >= 400: return Utils.dispatch_failure( request, 'NO_TOKEN_AUTHENTICATION') return Utils.dispatch_success(request, "AUTHENTICATED_SUCCESSFULLY") return Utils.dispatch_failure(request, 'NO_TOKEN_AUTHENTICATION') except Exception as e: return Utils.dispatch_failure(request, 'NO_TOKEN_AUTHENTICATION')
def trail_balance(self, pk, request): """ Get the trail balance profile from Quick Books :param pk: company: Company ID :return: Response of trail balance """ try: meta = CompanyMeta.objects.filter(company_id=pk).first() if meta.monthly_reporting_current_period: st = time.time() # this will grab the trial balance for the companymeta.monthly_reporting_current_period # plus 23 more months worth of history. job = group( trial_balance_for_period.s(pk, i) for i in range(0, 23)) result = job.apply_async() else: return Utils.dispatch_failure( request, 'MISSING_MONTHLY_REPORTING_CURRENT_PERIOD') # note: this bit of code basically turns this into a synchronous call, this is intended behaviour for now # todo: phase 2 we need to add sockets for better communication with UI while not result.ready(): continue print('##### CELERY get TB now takes {:.2f}s'.format(time.time() - st)) return Utils.dispatch_success(request, 'TRIAL_BALANCE_RECEIVED_SUCCESS') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def get(self, request, pk, *args, **kwargs): """ Gets the Balance Sheet Financial Statement Entries from the database Range can be specified by adding ?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD No date params will return all entries for the specified company. end_date param only will provide data for that specific period. A start_date without an end_date, it will cause an error. """ try: company = AccountsUtils.get_company(pk) # todo: query variable is never used, and appears to be unncessary query, dates = Utils.validate_query(request, dateformat=True) # todo: figure out why order_by isn't workin on these query_sets, ordering added to model as workaround if not dates: print('########### not dates') queryset = FinancialStatementEntry.objects.filter(company=company, statement_type=FinancialStatementEntry.BALANCE_SHEET) else: if dates[0] == 'ERROR': return Utils.dispatch_failure(request, dates[1]) elif len(dates) == 2: queryset = FinancialStatementEntry.objects.filter(company=company, period_ending__range=(dates[0], dates[1]), statement_type=FinancialStatementEntry.BALANCE_SHEET, ) elif len(dates) == 1: print(dates) queryset = FinancialStatementEntry.objects.filter(company=company, period_ending=dates[0], statement_type=FinancialStatementEntry.BALANCE_SHEET, ) else: print(dates) queryset = FinancialStatementEntry.objects.filter(company=company, statement_type=FinancialStatementEntry.BALANCE_SHEET) if queryset: serializer = FinancialStatementEntrySerializer(queryset, many=True) return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'DATA_NOT_FOUND') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def get(self, request, pk, *args, **kwargs): """ Gets the Income Statement Financial Statement Entries from the database Range can be specified by adding ?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD No date params will return all entries for the specified company. end_date param only will provide data for that specific period. A start_date without an end_date, it will cause an error. """ try: company = AccountsUtils.get_company(pk) query, dates = Utils.validate_query(request, dateformat=True) if not dates: queryset = FinancialStatementEntry.objects.filter(company=company, statement_type=FinancialStatementEntry.INCOME_STATEMENT) else: if dates[0] == 'ERROR': return Utils.dispatch_failure(request, dates[1]) elif len(dates) == 2: queryset = FinancialStatementEntry.objects.filter(company=company, period_ending__range=(dates[0], dates[1]), statement_type=FinancialStatementEntry.INCOME_STATEMENT, ) elif len(dates) == 1: queryset = FinancialStatementEntry.objects.filter(company=company, period_ending=dates[0], statement_type=FinancialStatementEntry.INCOME_STATEMENT, ) else: queryset = FinancialStatementEntry.objects.filter(company=company, statement_type=FinancialStatementEntry.INCOME_STATEMENT) queryset = queryset.order_by('value') if queryset: serializer = FinancialStatementEntrySerializer(queryset, many=True) return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'DATA_NOT_FOUND') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def get(self, request, pk, *args, **kwargs): """ Lists all monthly reports for the specified companies """ try: self.queryset = MonthlyReport.objects.filter(company=pk).order_by('period_ending') if len (self.queryset) == 0: return Utils.dispatch_success(request,'DATA_NOT_FOUND') return super(MonthlyReportList, self).get(request) except Exception: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def delete(self, request, pk, *args, **kwargs): try: if self.request.user.is_superuser: company = AccountsUtils.get_company(pk) if TrialBalance.objects.filter(company=company).count(): TrialBalance.objects.filter(company=company).delete() return Utils.dispatch_success(request, 'DELETED_SUCCESSFULLY') else: return Utils.dispatch_failure(request, "OBJECT_RESOURCE_NOT_FOUND") return Utils.dispatch_failure(request, 'UNAUTHORIZED_ACCESS') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def put(self, request, pk, *args, **kwargs): """ Updates verified by user in CoAMap for a company in the database """ try: company = AccountsUtils.get_company(pk) coamap = CoAMap.objects.filter(company_id=company) for entry in coamap: entry.verified_by_user = False entry.save() return Utils.dispatch_success(request, 'COAMAP_UPDATED_SUCCESSFULLY') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def get(self, request, pk, *args, **kwargs): try: # Check company is exists is_valid_company, message = Utils.check_company_exists(pk) if not is_valid_company: return Utils.dispatch_failure(request, 'RESOURCE_NOT_FOUND') login_status = Utils.get_login_status(pk) if login_status: return Utils.dispatch_success(request, login_status.data) return Utils.dispatch_failure(request, 'UNAUTHORIZED_ACCESS') except Exception as e: print(e) return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def chart_of_accounts(self, company, request): """ Get the chart of account profile from Quick Books :param company: Company ID :return: Response """ try: company = AccountsUtils.get_company(company) cm = CompanyMeta.objects.filter(company_id=company).first() credentials = AccountingUtils.get_credentials_by_company(company) if not credentials: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") chart_of_accounts_response, status_code = Utils.get_chart_of_accounts( credentials.accessToken, credentials.realmId) if status_code >= 400: print("First Failure") bearer = Utils.get_bearer_token_from_refresh_token( credentials.refreshToken) if bearer is "failure": return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") new_credentials = AccountingUtils.updateAccountingSession( company, bearer.accessToken, bearer.refreshToken, credentials.realmId) chart_of_accounts_response, status_code = Utils.get_chart_of_accounts( new_credentials.accessToken, new_credentials.realmId) if status_code >= 400: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") coas = QuickBooks.save_chart_of_accounts( company, chart_of_accounts_response) cm.chartofaccounts_last_refresh_date = datetime.datetime.now() cm.save() serializer = CoASerializer(coas, many=True) return Utils.dispatch_success(request, "COA_FETECHED_SUCCESSFULLY") except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def get(self, request, pk, report_identifier, *args, **kwargs): """ Gets a Company's Monthly Report instance by Monthly Report Period or Monthly Report ID """ try: if '-' in report_identifier: monthly_report = ReportingUtils.get_monthly_report(pk=pk, period=report_identifier) else: monthly_report = ReportingUtils.get_monthly_report(pk=pk, report_id=report_identifier) if monthly_report: serializer = MonthlyReportSerializer(monthly_report, context={'request': request, 'company_id': pk}) return Utils.dispatch_success(request,serializer.data) return Utils.dispatch_failure(request,'MONTHLY_REPORT_NOT_FOUND') except Exception as e: return Utils.dispatch_failure (request,'INTERNAL_SERVER_ERROR')
def delete(self, request,pk, report_identifier, *args, **kwargs): """ Updates a Company's Monthly Report instance Monthly Report Period or Monthly Report ID """ try: # TODO: Can you delete monthly reports? pls confirm if self.request.user.is_superuser: if '-' in report_identifier: monthly_report = ReportingUtils.get_monthly_report(pk=pk, period=report_identifier) else: monthly_report = ReportingUtils.get_monthly_report(pk=pk, report_id=report_identifier) if monthly_report: monthly_report.delete() return Utils.dispatch_success(request,'DELETED_SUCCESSFULLY') return Utils.dispatch_failure(request, 'MONTHLY_REPORT_NOT_FOUND') return Utils.dispatch_failure(request,'UNAUTHORIZED_ACCESS') except Exception as e: return Utils.dispatch_failure (request,'INTERNAL_SERVER_ERROR')
def disconnect(self, pk, request): """ Disconnect the QuickBooks :param pk: Company ID :return: Response """ company = AccountsUtils.get_company(pk) credentials = AccountingUtils.get_credentials_by_company(company) try: revoke_response = Utils.revoke_token(credentials.accessToken) if "Token is incorrect" in revoke_response: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") return Utils.dispatch_success(request, "REVOKE_SUCCESSFULL") except Exception as e: print(e) return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION")
def post(self, request, pk, report_identifier): """ Updates or creates a Company's Questionnaire Answers for a given period """ try: if '-' in report_identifier: monthly_report = ReportingUtils.get_monthly_report(pk=pk, period=report_identifier) else: monthly_report = ReportingUtils.get_monthly_report(pk=pk, report_id=report_identifier) if monthly_report is None: return Utils.dispatch_failure(request, 'MISSING_MONTHLY_REPORTING_PREVIOUS_PERIOD') answers = [] context = {'request': request, 'company': pk, 'period': report_identifier} try: for item in request.data: # None Values not checked becauseof, To update answer If yes, please provide details is changed to No scenario # if item['answer'] is not None: item['company'] = pk item['monthly_report'] = monthly_report.id exists = ReportingUtils.answer_exists(item) if exists: serializer = CreateAnswerSerializer(exists, data=item, context=context) else: serializer = CreateAnswerSerializer(data=item, context=context) if serializer.is_valid(): serializer.save() except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request,'DATA_PARSING_ISSUE', error) questions = ReportingUtils.get_questionnaire_objects(pk, report_identifier) serializer = QuestionWithAnswerSerializer(questions, many=True, context=context) return Utils.dispatch_success(request,serializer.data) except Exception as e: return Utils.dispatch_failure (request,'INTERNAL_SERVER_ERROR')
def post(self, request, pk, *args, **kwargs): """ Creates a chart of accounts from the request data, follows the format {"QueryResponse": { 'Account': [{ 'Name': <string>, 'attr': <type>, 'CurrencyRef': {'value': 'USD', name: '..'}, 'MetaData': {'CreatedTime': <timestamp>, 'LastUpdated:..} },{..} ] } } Sample response found at https://developer.intuit.com/docs/api/accounting/account """ try: print('##### Chart of Accounts RAW Request') print('####### End of CoA Request') company = AccountsUtils.get_company(pk) if 'file' in request.data: file_type = CSVUtils.file_type(request.FILES['file']) if not file_type: return Utils.dispatch_failure(request, "INVALID_FILE_FORMAT") request.FILES['file'].seek(0) if file_type == 'CSV': # print('#--- processing coa csv') csv_data = CSVUtils.format_request_csv(request.FILES['file']) try: coa_data = CSVUtils.process_chart_of_accounts_csv(company, csv_data) if coa_data is "INVALID_CSV": return Utils.dispatch_failure(request, 'INVALID_COA_CSV') serializer = CoASerializer(coa_data, many=True) if len(serializer.data) > 0: return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'NO_DATA_CHANGES') except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) # elif file_type == 'Excel': # excel_data = CSVUtils.format_request_excel(request.FILES) # CSVUtils.silentremove('tmp.xlsx') # # with contextlib.suppress(FileNotFoundError): # os.remove('tmp.xlsx') return Utils.dispatch_failure(request, "OBJECT_RESOURCE_NOT_FOUND") else: try: coas = QuickBooks.save_chart_of_accounts(company, request.data) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) try: serializer = CoASerializer(coas, many=True) if len(serializer.data) > 0: return Utils.dispatch_success(request, serializer.data) return Utils.dispatch_success(request, 'NO_DATA_CHANGES') except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR') # return Response({"message": "todo implement csv parsing"}) except Exception as e: print(e) return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def get(self, request, pk, *args, **kwargs): try: company = AccountsUtils.get_company(pk) # Build Request st = time.time() data = AccountingUtils.build_all_sight_save_request(company) # TODO: FISCAL YEAR CHANGE # Company's Current Fiscal Year End date print('setting fye dict') try: fye_dict = Utils.get_curr_prior_fiscal_year_end(company) except Exception: return Utils.dispatch_failure(request, 'FISCAL_YEAR_MISSING') print('splitting data into chuncks') slitted_data = Utils.spilt_input_to_chunk(data, fye_dict) error_tags = [] # todo: this needs to be updated for production user and pass auth_header = 'Basic ' + base64.b64encode(bytes('user1:Password@1', 'utf-8')).decode() headers = {'Accept': 'application/json', 'content-type': 'application/json', 'Authorization': auth_header} proxy_dict_required = os.environ.get("PROXY_REQUIRED", 0) if os.environ.get('FIXIE_URL', ''): proxydict = {"http": os.environ.get('FIXIE_URL', ''), "https": os.environ.get('FIXIE_URL', '')} else: proxydict = {"http": 'http://*****:*****@velodrome.usefixie.com:80', "https": 'http://*****:*****@velodrome.usefixie.com:80'} url_configured = True endpoint = os.environ.get('ALLSIGHT_URL', '') if endpoint == '' or endpoint is None: url_configured = False # TODO: FISCAL YEAR CHANGE try: # Send to All Sight balance_sheet_data = [] income_statement_data = [] for data in slitted_data: print('^^^^^^^^^^^^^^^ processing tb data ') print('^^^^^^^^^^^^^^^^^ tb data end') st = time.time() if url_configured: if proxy_dict_required: r = requests.post(endpoint, data=json.dumps(data), headers=headers, verify=True, proxies=proxydict) else: r = requests.post(endpoint, data=json.dumps(data), headers=headers, verify=True) response = json.loads(r.text) else: try: json_response = AllSightMock.initiate_allsight(input_data=data) response = json.loads(json_response) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request,'CREDIT_DEBIT_UNEQUALS') print('{:.2f}s AS - SAVE Request'.format(time.time() - st)) # print('########## AS RESPONSE', response) for entry in response["Model"]["Financials"]["BalanceSheet"]: balance_sheet_data.append(entry) for entry in response["Model"]["Financials"]["IncomeStatement"]: income_statement_data.append(entry) response["Model"]["Financials"]["BalanceSheet"] = balance_sheet_data response["Model"]["Financials"]["IncomeStatement"] = income_statement_data except Exception: return Utils.dispatch_failure(request, 'ALL_SIGHT_CONNECTION') # Save to Database st = time.time() try: balance_sheet, bs_error_tags = AccountingUtils.parse_statement(request, company, response["Model"]["Financials"], FinancialStatementEntry.BALANCE_SHEET) income_statement, is_error_tags = AccountingUtils.parse_statement(request, company, response["Model"]["Financials"], FinancialStatementEntry.INCOME_STATEMENT) company = Company.objects.get(id=pk) if len(bs_error_tags) or len(is_error_tags): if len(bs_error_tags): error_tags += bs_error_tags if len(is_error_tags): error_tags += is_error_tags if not company.is_tag_error_notified: company.is_tag_error_notified = True company.save() Utils.send_error_tags(company=pk, user=request.user, error_tags=error_tags, response=response) else: company.is_tag_error_notified = False company.save() # combine them both and create them all at once income_statement.extend(balance_sheet) FinancialStatementEntry.objects.bulk_create(income_statement) credit_debit_equality = AccountingUtils.credit_debit_mismatch(company) if credit_debit_equality is not None: if len(credit_debit_equality) > 0: response['credit_debit_unequals'] = credit_debit_equality # returns the response from All Sight return Utils.dispatch_success(request, response) except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def put(self, request, pk, report_identifier): try: company = AccountsUtils.get_company(pk) if '-' in report_identifier: monthly_report = ReportingUtils.get_monthly_report(pk=pk, period=report_identifier) else: monthly_report = ReportingUtils.get_monthly_report(pk=pk, report_id=report_identifier) changed_items = { "Balancesheet" : [], "Incomestatement" : [], "Answers" : [], } change_available = False data=request.data for name,value in data["Balancesheet"]["data"].items(): if value is "": value = "0" fse_tag = FinancialStatementEntryTag.objects.filter(all_sight_name = name).first() balancesheet = FinancialStatementEntry.objects.filter(company=company, period_ending=monthly_report.period_ending, statement_type=FinancialStatementEntry.BALANCE_SHEET, fse_tag =fse_tag ).first() if balancesheet is None: continue if int(balancesheet.value) != int(value): change_available = True old_value = balancesheet.value balancesheet.value = int(value) balancesheet.save() changed_items["Balancesheet"].append({ "object":balancesheet, "old_value":old_value, "new_value":value }) for name,value in data["Incomestatement"]["data"].items(): if value is "": value = "0" print(value) fse_tag = FinancialStatementEntryTag.objects.filter(all_sight_name = name).first() incomestatement = FinancialStatementEntry.objects.filter(company=company, period_ending=monthly_report.period_ending, statement_type=FinancialStatementEntry.INCOME_STATEMENT, fse_tag =fse_tag ).first() if incomestatement is None: continue if int(incomestatement.value) != int(value): change_available = True old_value = incomestatement.value incomestatement.value = int(value) incomestatement.save() changed_items["Incomestatement"].append({ "object": incomestatement, "old_value": old_value, "new_value": value }) for entry in data["Answers"]: answer_data = entry["answer"] answer_entry = Answer.objects.filter(id=answer_data["id"]).first() if str(answer_entry.answer) != str(answer_data["answer"]): change_available = True old_value = answer_entry.answer answer_entry.answer = answer_data["answer"] answer_entry.save() changed_items["Answers"].append( { "object": answer_entry, "old_value": old_value, "new_value": answer_data["answer"] } ) if change_available: Utils.log_prev_report_edit(company,monthly_report,request.user,changed_items) return Utils.dispatch_success(request,[]) except Exception as e: return Utils.dispatch_failure(request, 'INTERNAL_SERVER_ERROR')
def chart_of_accounts(self,id,request): """ Get Chart of Accounts From online :param company: Company ID :return: Response """ try: # login_status = Utils.get_login_status(company) # if login_status != LoginInfo.IN_PROGRESS: # message = "Login Authentication Failed" # return Utils.dispatch_failure(request,message) company = AccountsUtils.get_company(id) secret_keys = Utils.get_access_keys(id) # Get xero auth access information form xero connection auth_info = AccountingOauth2.objects.filter(company_id=id).values('accessToken', 'accessSecretKey', 'tokenAcitvatedOn', 'tokenExpiryON') if len(auth_info) == 0: return Utils.dispatch_failure(request, 'NO_TOKEN_AUTHENTICATION') for key, value in auth_info[0].items(): OAUTH_PERSISTENT_SERVER_STORAGE.update({key: value}) stored_values = OAUTH_PERSISTENT_SERVER_STORAGE if len(stored_values) == 0: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") auth = Utils.get_xero_auth(id) if AccountingConfiguration.PRIVATE == secret_keys.type: credentials = PrivateCredentials(**auth) else: credentials = PublicCredentials(**auth) if credentials.expired(): return Utils.dispatch_failure(request, 'NO_TOKEN_AUTHENTICATION') # Enable the access for accessing the reports from xero logged in account. xero = Xero(credentials) # Resave our verified credentials # stored_values = bind_auth_info(credentials, pk) except XeroException as e: if AccountingConfiguration.PRIVATE == secret_keys.type: error = ["%s" % e] return Utils.dispatch_failure(request, 'XERO_CONNECTION_ERROR', error) else: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") try: chartofaccounts = xero.accounts.all() XeroAccountings.save_chart_of_accounts(company, chartofaccounts) return Utils.dispatch_success(request,"COA_FETECHED_SUCCESSFULLY") except XeroException as e: if AccountingConfiguration.PRIVATE == secret_keys.type: error = ["%s" % e] return Utils.dispatch_failure(request, 'XERO_CONNECTION_ERROR', error) else: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error)
def get(self, request, pk, *args, **kwargs): try: company = AccountsUtils.get_company (pk) query, dates = Utils.validate_query (request, dateformat=True) end_date = request.GET.get ('end_date', None) split_date = end_date.split ('-') report_identifier = split_date[0]+"-"+split_date[1] report_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date() report_month = report_date.strftime('%b') report_month_fillform = report_date.strftime ('%B') if '-' in report_identifier: monthly_report = ReportingUtils.get_monthly_report (pk=pk, period=report_identifier) else: monthly_report = ReportingUtils.get_monthly_report (pk=pk, report_id=report_identifier) context = {'request': request, 'company': pk, 'period': report_identifier} # todo: figure out why order_by isn't workin on these query_sets, ordering added to model as workaround if not dates: print('########### not dates') bs_queryset = FinancialStatementEntry.objects.filter (company=company, statement_type=FinancialStatementEntry.BALANCE_SHEET) is_queryset = FinancialStatementEntry.objects.filter (company=company, statement_type=FinancialStatementEntry.INCOME_STATEMENT) else: if dates[0] == 'ERROR': return Utils.dispatch_failure (request, dates[1]) elif len (dates) == 2: bs_queryset = FinancialStatementEntry.objects.filter (company=company, period_ending__range=(dates[0], dates[1]), statement_type=FinancialStatementEntry.BALANCE_SHEET, ) is_queryset = FinancialStatementEntry.objects.filter (company=company, period_ending__range=(dates[0], dates[1]), statement_type=FinancialStatementEntry.INCOME_STATEMENT, ) elif len (dates) == 1: bs_queryset = FinancialStatementEntry.objects.filter (company=company, period_ending=dates[0], statement_type=FinancialStatementEntry.BALANCE_SHEET, ) is_queryset = FinancialStatementEntry.objects.filter (company=company, period_ending=dates[0], statement_type=FinancialStatementEntry.INCOME_STATEMENT, ) else: bs_queryset = FinancialStatementEntry.objects.filter (company=company, statement_type=FinancialStatementEntry.BALANCE_SHEET) is_queryset = FinancialStatementEntry.objects.filter (company=company, statement_type=FinancialStatementEntry.INCOME_STATEMENT) orderlist = {} ordered_data = {} bslist = {} islist = {} qalist = {} signofflist = {} reportlist = {} questions = ReportingUtils.get_questionnaire_objects (pk, report_identifier) has_answers = ReportingUtils.has_answers_for_period (pk, report_identifier) if questions and has_answers: # return Q n A for requested period. question_and_answer = QuestionWithAnswerSerializer (questions, many=True, context=context) if len (question_and_answer.data) > 0: for row in range (0, int (len (question_and_answer.data))): question_text = question_and_answer.data[row]['question_text'] answer_data_type = question_and_answer.data[row]['answer_data_type'] short_tag = question_and_answer.data[row]['short_tag'] answer = question_and_answer.data[row]['answer']['answer'] if row is not 0 and qalist[row-1]["answer_data_type"] == "boolean" and answer_data_type == "varchar(255)" or answer_data_type == "varchar(511)": if question_text.split(' ')[1].split(',')[0].lower() != qalist[row - 1]["answer"].lower(): answer = None qalist[row] = {"question": question_text, "answer_data_type": answer_data_type, "short_tag": short_tag, "answer":answer, "count": int(len (question_and_answer.data))} ordered_data["QUESTIONNAIRE"] = qalist else: qalist[0] = {"question": "", "answer_data_type": "","short_tag": "", "answer": "", "count": 0} ordered_data["QUESTIONNAIRE"] = qalist orderlist['QA'] = ordered_data if bs_queryset: balance_sheet = FinancialStatementEntrySerializer (bs_queryset, many=True) for row in range (0, int (len (balance_sheet.data))): key = balance_sheet.data[row]['fse_tag']["sort_order"] description = balance_sheet.data[row]['fse_tag']['description'] value = balance_sheet.data[row]['value'] is_total = balance_sheet.data[row]['fse_tag']['is_total_row'] bslist[key] = {"description": description, "value": Utils.currencyformat(value), "is_total": is_total} ordered_data["BALANCE"] = bslist orderlist['BS'] = ordered_data if is_queryset: income_statement = FinancialStatementEntrySerializer (is_queryset, many=True) ordered_data = {"INCOME":{}} for row in range (0, int (len (income_statement.data))): key = income_statement.data[row]['fse_tag']["sort_order"] description = income_statement.data[row]['fse_tag']['description'] value = float(income_statement.data[row]['value']) is_total = income_statement.data[row]['fse_tag']['is_total_row'] islist[key] = {"description": description,"value": Utils.currencyformat(value), "is_total": is_total} ordered_data["INCOME"] = islist orderlist['IS'] = ordered_data if monthly_report: monthly_report_serializer = MonthlyReportSerializer (monthly_report, context={'request': request, 'company_id': pk}) status = monthly_report_serializer.data['status'] signoff_by_name = monthly_report_serializer.data['signoff_by_name'] signoff_by_title = monthly_report_serializer.data['signoff_by_title'] signoff_date = monthly_report_serializer.data['signoff_date'] signofflist[0] = {"status": status, "signoff_by_name": signoff_by_name, "signoff_by_title": signoff_by_title, 'signoff_date':signoff_date} ordered_data["SIGNOFF"] = signofflist orderlist['REPORT'] = ordered_data reportlist[0] = {"period": report_month+'. '+split_date[0]} ordered_data["PERIOD"] = reportlist orderlist['REPORT'] = ordered_data pdf = AccountingUtils.render_to_pdf('pdf-layout.html', orderlist) response = HttpResponse (pdf, content_type='application/pdf') if pdf: response = HttpResponse (pdf, content_type='application/pdf') filename = PREVIOUS_MONTHLY_REPORT_DOWNLOAD_FILE_PREFIX+"%s.pdf" % (report_month_fillform+' '+split_date[0]) content = "inline; filename='%s'" % (filename) download = request.GET.get("download") if download: content = "attachment; filename='%s'" % (filename) response['Content-Disposition'] = content return response return Utils.dispatch_success (request, 'DATA_NOT_FOUND') except Exception as e: print(e) return Utils.dispatch_failure (request, 'INTERNAL_SERVER_ERROR')
def trail_balance(self, pk, request): """ Get Trail Balance From online :param company: Company Id :return: Response """ try: # Checking Token Authentication available auth_info = AccountingOauth2.objects.filter(company_id=pk).values('accessToken', 'accessSecretKey', 'tokenAcitvatedOn', 'tokenExpiryON') secret_keys = Utils.get_access_keys(pk) if len(auth_info) == 0: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") for key, value in auth_info[0].items(): OAUTH_PERSISTENT_SERVER_STORAGE.update({key: value}) stored_values = OAUTH_PERSISTENT_SERVER_STORAGE if len(stored_values) == 0: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") # Checking Xero Connection Authentication available auth = Utils.get_xero_auth(pk) if AccountingConfiguration.PRIVATE == secret_keys.type: credentials = PrivateCredentials(**auth) else: credentials = PublicCredentials(**auth) if credentials.expired() or credentials is None: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") try: xero = Xero(credentials) xero.reports.get('TrialBalance') except XeroException as e: if AccountingConfiguration.PRIVATE == secret_keys.type: error = ["%s" % e] return Utils.dispatch_failure(request, 'XERO_CONNECTION_ERROR', error) else: return Utils.dispatch_failure(request, "NO_TOKEN_AUTHENTICATION") try: meta = CompanyMeta.objects.filter(company_id=pk).first() if meta.monthly_reporting_current_period: st = time.time() from portalbackend.lendapi.v1.accounting.tasks import trial_balance_for_period job = group(trial_balance_for_period.s(pk, i) for i in range(0, 23)) result = job.apply_async() else: return Utils.dispatch_failure(request, 'MISSING_MONTHLY_REPORTING_CURRENT_PERIOD') while not result.ready(): continue return Utils.dispatch_success(request, 'TRIAL_BALANCE_RECEIVED_SUCCESS') except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error) except Exception as e: return Utils.dispatch_failure(request, "INTERNAL_SERVER_ERROR")