Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Export options for accounts aging #1680

Merged
merged 8 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ def index
authorize :report
render :index, locals: Reports::AccountsAging::FetchOverdueAmount.process(current_company), status: :ok
end

def download
authorize :report
send_data Reports::AccountsAging::DownloadService.new(params, current_company).process
end
end
9 changes: 8 additions & 1 deletion app/javascript/src/apis/reports/accountsAging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const path = "/reports/accounts_aging";

const get = () => axios.get(path);

const accountsAgingApi = { get };
const download = (type, queryParams) =>
axios({
method: "GET",
url: `${path}/download.${type}${queryParams}`,
responseType: "blob",
});

const accountsAgingApi = { get, download };

export default accountsAgingApi;
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import React, { useState, useEffect } from "react";
import Logger from "js-logger";
import { useNavigate } from "react-router-dom";

import accountsAgingApi from "apis/reports/accountsAging";
import Loader from "common/Loader/index";

import Container from "./Container";
import FilterSideBar from "./Filters";

import getReportData from "../api/accountsAging";
import { getQueryParams } from "../api/applyFilter";
import EntryContext from "../context/EntryContext";
import OutstandingOverdueInvoiceContext from "../context/outstandingOverdueInvoiceContext";
import RevenueByClientReportContext from "../context/RevenueByClientContext";
Expand Down Expand Up @@ -75,19 +77,31 @@ const AccountsAgingReport = () => {
},
};

const handleDownload = async type => {
const queryParams = getQueryParams(selectedFilter).substring(1);
const response = await accountsAgingApi.download(type, `?${queryParams}`);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
// const filename = `${selectedFilter.dateRange.label}.${type}`;
const filename = `report.${type}`;
link.href = url;
link.setAttribute("download", filename);
link.click();
};

if (loading) {
return <Loader />;
}

return (
<EntryContext.Provider value={{ ...contextValues }}>
<Header
handleDownload
showExportButon
handleDownload={handleDownload}
isFilterVisible={isFilterVisible}
resetFilter={resetFilter}
revenueFilterCounter={() => {}} // eslint-disable-line @typescript-eslint/no-empty-function
setIsFilterVisible={setIsFilterVisible}
showExportButon={false}
showNavFilters={showNavFilters}
type="Accounts Aging Report"
/>
Expand Down
52 changes: 52 additions & 0 deletions app/services/reports/accounts_aging/download_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module Reports::AccountsAging
class DownloadService < Reports::DownloadService
attr_reader :current_company, :reports

def initialize(params, current_company)
super
@reports = []
end

private

def fetch_complete_report
@reports = FetchOverdueAmount.new(current_company).process
end

def generate_pdf
Reports::GeneratePdf.new(:accounts_aging, reports, current_company).process
end

def generate_csv
csv_data = []
headers = ["Client Name", "0-30 Days", "31-60 Days", "61-90 Days", "90+ Days", "Total"]
reports[:clients].each do |client|
csv_data << [
client[:name],
format_amount(client[:amount_overdue][:zero_to_thirty_days]),
format_amount(client[:amount_overdue][:thirty_one_to_sixty_days]),
format_amount(client[:amount_overdue][:sixty_one_to_ninety_days]),
format_amount(client[:amount_overdue][:ninety_plus_days]),
format_amount(client[:amount_overdue][:total])
]
end

csv_data << [
"Total Amounts",
reports[:total_amount_overdue_by_date_range][:zero_to_thirty_days],
reports[:total_amount_overdue_by_date_range][:thirty_one_to_sixty_days],
reports[:total_amount_overdue_by_date_range][:sixty_one_to_ninety_days],
reports[:total_amount_overdue_by_date_range][:ninety_plus_days],
reports[:total_amount_overdue_by_date_range][:total]
]

Reports::GenerateCsv.new(csv_data, headers).process
end

def format_amount(amount)
FormatAmountService.new(reports[:base_currency], amount).process
end
end
end
37 changes: 37 additions & 0 deletions app/services/reports/download_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

class Reports::DownloadService
attr_reader :params, :current_company

def initialize(params, current_company)
@params = params
@current_company = current_company
end

def process
fetch_complete_report
format_report
end

private

def fetch_complete_report
raise NotImplementedError, "Subclasses must implement a 'fetch_complete_report' method."
end

def format_report
if params[:format] == "pdf"
generate_pdf
else
generate_csv
end
end

def generate_pdf
raise NotImplementedError, "Implement generate_pdf in the inheriting class"
end

def generate_csv
raise NotImplementedError, "Implement generate_csv in the inheriting class"
end
end
21 changes: 21 additions & 0 deletions app/services/reports/generate_csv.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require "csv"

class Reports::GenerateCsv
attr_reader :data, :headers

def initialize(data, headers)
@data = data
@headers = headers
end

def process
CSV.generate do |csv|
csv << headers
data.each do |row|
csv << row
end
end
end
end
29 changes: 29 additions & 0 deletions app/services/reports/generate_pdf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

class Reports::GeneratePdf
attr_reader :report_data, :current_company, :report_type

def initialize(report_type, report_data, current_company)
@report_type = report_type
@report_data = report_data
@current_company = current_company
end

def process
case report_type
when :time_entries, :accounts_aging
generate_pdf(report_type)
else
raise ArgumentError, "Unsupported report type: #{report_type}"
end
end

private

def generate_pdf(report_type)
Pdf::HtmlGenerator.new(
report_type,
locals: { report_data:, current_company: }
).make
end
end
43 changes: 26 additions & 17 deletions app/services/reports/time_entries/download_service.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
# frozen_string_literal: true

class Reports::TimeEntries::DownloadService
attr_reader :params, :current_company, :reports
class Reports::TimeEntries::DownloadService < Reports::DownloadService
attr_reader :reports

def initialize(params, current_company)
@params = params
@current_company = current_company

super
@reports = []
end

def process
fetch_complete_report
format_report
end

private

def fetch_complete_report
Expand All @@ -31,12 +24,28 @@ def fetch_complete_report
end
end

def format_report
if params[:format] == "pdf"
Reports::TimeEntries::GeneratePdf.new(reports, current_company).process
else
flatten_reports = reports.map { |e| e[:entries] }.flatten
Reports::TimeEntries::GenerateCsv.new(flatten_reports, current_company).process
end
def generate_pdf
Reports::GeneratePdf.new(:time_entries, reports, current_company).process
end

def generate_csv
data = []
headers = ["Project", "Client", "Note", "Team Member", "Date", "Hours Logged"]
flatten_reports = reports.map { |e| e[:entries] }.flatten
flatten_reports.each do |entry|
data << [
"#{entry.project_name}",
"#{entry.client_name}",
"#{entry.note}",
"#{entry.user_name}",
"#{format_date(entry.work_date)}",
"#{DurationFormatter.new(entry.duration).process}"
]
end
Reports::GenerateCsv.new(data, headers).process
end

def format_date(date)
CompanyDateFormattingService.new(date, company: current_company, es_date_presence: true).process
end
end
36 changes: 0 additions & 36 deletions app/services/reports/time_entries/generate_csv.rb

This file was deleted.

19 changes: 0 additions & 19 deletions app/services/reports/time_entries/generate_pdf.rb

This file was deleted.

Loading
Loading