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 trial_balance_for_period(pk, period_offset): cm = CompanyMeta.objects.filter(company_id=pk).first() cm.save() company = AccountsUtils.get_company(pk) st = time.time() credentials = AccountingUtils.get_credentials_by_company(company) if not credentials: return if not cm.monthly_reporting_current_period: return if company.accounting_type.lower() == Company.QUICKBOOKS.lower(): period = cm.monthly_reporting_current_period - relativedelta( months=+period_offset, day=31) # print('##### Getting TRIAL BALANCE for period ', period) query = '?start_date=' + period.strftime( '%Y-%m-1') + '&end_date=' + period.strftime('%Y-%m-%d') # print('####### QBO TB QUERY ', query) # for query in query_list: trial_balance_response, status_code = Utils.get_trial_balance( credentials.accessToken, credentials.realmId, query) if status_code >= 400: bearer = Utils.get_bearer_token_from_refresh_token( credentials.refreshToken) new_credentials = AccountingUtils.updateAccountingSession( company, bearer.accessToken, bearer.refreshToken, credentials.realmId) trial_balance_response, status_code = Utils.get_trial_balance( new_credentials.accessToken, new_credentials.realmId, query) if status_code >= 400: return from .third_party.quickbooks import QuickBooks entries = QuickBooks.save_trial_balance(company, trial_balance_response) TrialBalance.objects.bulk_create(entries) if company.accounting_type.lower() == Company.XERO.lower(): period = cm.monthly_reporting_current_period - relativedelta( months=+period_offset, day=31) # params = {'fromDate': str(period.strftime('%Y-%m-01')), 'toDate': str(period.strftime('%Y-%m-%d'))} params = {'date': str(period.strftime('%Y-%m-%d'))} auth = Utils.get_xero_auth(pk) credentials = Utils.get_xero_credentials(pk, **auth) xero = Xero(credentials) trialbalance = xero.reports.get('TrialBalance', params=params) XeroAccountings.save_trial_balance(company, trialbalance[0]) # print('{:.2f}s Elapsed'.format(time.time() - st)) # print('Took {:.2f}s Total'.format(time.time() - st)) cm.trialbalance_last_refresh_date = datetime.datetime.now() cm.trialbalance_dl_complete = True cm.save() return
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 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): """ 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 test_007_get_company_from_utils_failure(self): """ Get the all company list without having company """ with self.assertRaises(Exception) as e: self.assertEquals( e, AccountsUtils.get_company(TestConstants.INVALID_ID))
def test_006_get_company_from_utils_success(self): """ Get the all company list from accounting Utils """ company = TestUtils._create_company(1, CompanyConstant.COMPANY_NAME_001) self.assertEquals(company, AccountsUtils.get_company(company.id))
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 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 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 auth_code_handler(self, request, pk=None): """ Handles the authentication Code from quickbooks redirect :param pk: Company ID :param request: :return: """ try: state = request.GET.get('state', None) error = request.GET.get('error', None) auth_cancel_url = settings.QBO_AUTH_CANCEL_URL print('############ auth code handler state ', state) if error == 'access_denied': print('############ auth code handerl access deined ', error) return redirect(auth_cancel_url) if state is None: return redirect(auth_cancel_url) auth_code = request.GET.get('code', None) print('############ auth code handerl code ', auth_code) if auth_code is None: return redirect(auth_cancel_url) company = AccountsUtils.get_company(state) bearer = Utils.get_bearer_token(auth_code) realmId = request.GET.get('realmId', None) AccountingUtils.updateAccountingSession(company, bearer.accessToken, bearer.refreshToken, realmId) qb_status = LoginInfo.objects.filter( company=company, status=LoginInfo.IN_PROGRESS, created__range=[ timezone.now() - datetime.timedelta(minutes=10), timezone.now() ]).first() qb_status.status = LoginInfo.COMPLETED # todo: change this env variable for production #qbo_auth_redirect_url = os.environ.get('QBO_AUTH_REDIRECT_URL') auth_redirect_url = settings.QBO_AUTH_REDIRECT_URL #auth_redirect_url = os.environ.get ('QBO_AUTH_REDIRECT_URL','http://ec2-52-207-28-114.compute-1.amazonaws.com/IX/coa-match/quickbooks') return redirect(auth_redirect_url) #return Utils.dispatch_success(request,"successfully authenticated") except Exception as e: # todo: need to clarify this scenario occurs or not and handle correct redirect urls auth_cancel_url = settings.QBO_AUTH_CANCEL_URL Utils.send_company_misconfig(pk, e) return redirect(auth_cancel_url + '/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 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 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 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 get_login_status(id): """ Get Login status :return: login status """ company = AccountsUtils.get_company(id) login_status = AccountingUtils.get_status_object(company) if not login_status: return None now = datetime.datetime.utcnow().replace(tzinfo=utc) if login_status.created < (now - timedelta(minutes=5)): login_status.status = LoginInfo.FAILED login_status.save() print(LoginInfoSerializer(login_status).data) return Response(LoginInfoSerializer(login_status).data)
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 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 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 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 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, *args, **kwargs): try: company = AccountsUtils.get_company(pk) meta = CompanyMeta.objects.filter(company_id=pk).first() # get report for current period monthly_report = MonthlyReport.objects.filter(company_id=pk, period_ending=meta.monthly_reporting_current_period).first() if monthly_report is None: return Utils.dispatch_failure(request, 'MONTHLY_REPORT_NOT_FOUND') if monthly_report.status == MonthlyReport.COMPLETE and meta.monthly_reporting_current_period_status == CompanyMeta.COMPLETE: #return Response({"status": "INFO", "message": ""}) return Utils.dispatch_success(request,'MONTHLY_REPORT_ALREADY_EXISTS_WITH_COMPLETED') # if we're signing off as part of the setup process, then we need to set this flag to False # so the system will carry forward into it's normal monthly reporting workflow if meta.is_initial_setup: meta.qb_desktop_installed = True meta.is_initial_setup = False meta.accounting_setup_status = 'COMPLETE' meta.monthly_reporting_current_period_status = 'COMPLETE' meta.save() # todo: signoff_date and submitted_on date are duplicates at the moment, confirm with the business that we can # conslidate and then merge these two fields. Submitting a report and signing off happen via the same # UI activity. try: # checking next reporting period has fye. If not automatically new fye created with label # new "year_end fye" and mark it as active one cur_fye = FiscalYearEnd.objects.filter(company=company, is_active=True).first() if cur_fye is not None and meta.monthly_reporting_next_period > cur_fye.fye_end_date: cur_fye.is_active = False cur_fye.save() new_fye = FiscalYearEnd(company=company, fye_start_date=cur_fye.fye_start_date + relativedelta(years=1), fye_end_date=cur_fye.fye_end_date + relativedelta(years=1), label="{}FY".format((cur_fye.fye_end_date + relativedelta(years=1)).year), is_active=True) new_fye.save() data = { "status": MonthlyReport.COMPLETE, "submitted_on": timezone.now().date(), "signoff_date": timezone.now().date(), "signoff_by_name": request.data["signoff_by_name"], "signoff_by_title": request.data["signoff_by_title"] } except Exception as e: error = ["%s" % e] return Utils.dispatch_failure(request,'DATA_PARSING_ISSUE', error) serializer = MonthlyReportSerializer(monthly_report, data=data, context={'request': request, 'company_id': pk}, partial=True) # once they sign off on the current monthly reporting, the CoA Map entries will be officially verified # so they do not get asked to re-map them again the next time around MonthlyReportSignoff.mark_coamap_verified(self,request, pk) if serializer.is_valid(): serializer.validated_data['company_id'] = pk serializer.save() return Utils.dispatch_success (request,serializer.data) return Utils.dispatch_failure(request,'VALIDATION_ERROR',serializer.errors) 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)