feat: Consider business hours while generating the reports (#4330)
* feat: Consider business hours while generating the reports
This commit is contained in:
parent
57359be37e
commit
d5536d65f7
19 changed files with 241 additions and 48 deletions
3
Gemfile
3
Gemfile
|
@ -125,6 +125,9 @@ gem 'procore-sift'
|
||||||
gem 'email_reply_trimmer'
|
gem 'email_reply_trimmer'
|
||||||
gem 'html2text'
|
gem 'html2text'
|
||||||
|
|
||||||
|
# to calculate working hours
|
||||||
|
gem 'working_hours'
|
||||||
|
|
||||||
group :production, :staging do
|
group :production, :staging do
|
||||||
# we dont want request timing out in development while using byebug
|
# we dont want request timing out in development while using byebug
|
||||||
gem 'rack-timeout'
|
gem 'rack-timeout'
|
||||||
|
|
|
@ -636,6 +636,9 @@ GEM
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
wisper (2.0.0)
|
wisper (2.0.0)
|
||||||
|
working_hours (1.4.1)
|
||||||
|
activesupport (>= 3.2)
|
||||||
|
tzinfo
|
||||||
zeitwerk (2.5.4)
|
zeitwerk (2.5.4)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
@ -746,6 +749,7 @@ DEPENDENCIES
|
||||||
webpacker (~> 5.x)
|
webpacker (~> 5.x)
|
||||||
webpush
|
webpush
|
||||||
wisper (= 2.0.0)
|
wisper (= 2.0.0)
|
||||||
|
working_hours
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 3.0.2p107
|
ruby 3.0.2p107
|
||||||
|
|
|
@ -47,36 +47,36 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||||
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_summary_params
|
def common_params
|
||||||
{
|
{
|
||||||
type: params[:type].to_sym,
|
type: params[:type].to_sym,
|
||||||
id: params[:id],
|
id: params[:id],
|
||||||
since: range[:current][:since],
|
group_by: params[:group_by],
|
||||||
until: range[:current][:until],
|
business_hours: ActiveModel::Type::Boolean.new.cast(params[:business_hours])
|
||||||
group_by: params[:group_by]
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def current_summary_params
|
||||||
|
common_params.merge({
|
||||||
|
since: range[:current][:since],
|
||||||
|
until: range[:current][:until]
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
def previous_summary_params
|
def previous_summary_params
|
||||||
{
|
common_params.merge({
|
||||||
type: params[:type].to_sym,
|
since: range[:previous][:since],
|
||||||
id: params[:id],
|
until: range[:previous][:until]
|
||||||
since: range[:previous][:since],
|
})
|
||||||
until: range[:previous][:until],
|
|
||||||
group_by: params[:group_by]
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_params
|
def report_params
|
||||||
{
|
common_params.merge({
|
||||||
metric: params[:metric],
|
metric: params[:metric],
|
||||||
type: params[:type].to_sym,
|
since: params[:since],
|
||||||
since: params[:since],
|
until: params[:until],
|
||||||
until: params[:until],
|
timezone_offset: params[:timezone_offset]
|
||||||
id: params[:id],
|
})
|
||||||
group_by: params[:group_by],
|
|
||||||
timezone_offset: params[:timezone_offset]
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation_params
|
def conversation_params
|
||||||
|
|
|
@ -33,17 +33,23 @@ module ReportHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def avg_first_response_time
|
def avg_first_response_time
|
||||||
(get_grouped_values scope.reporting_events.where(name: 'first_response')).average(:value)
|
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'first_response'))
|
||||||
|
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
|
||||||
|
|
||||||
|
grouped_reporting_events.average(:value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def avg_resolution_time
|
def avg_resolution_time
|
||||||
(get_grouped_values scope.reporting_events.where(name: 'conversation_resolved')).average(:value)
|
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'conversation_resolved'))
|
||||||
|
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
|
||||||
|
|
||||||
|
grouped_reporting_events.average(:value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def avg_resolution_time_summary
|
def avg_resolution_time_summary
|
||||||
avg_rt = scope.reporting_events
|
reporting_events = scope.reporting_events
|
||||||
.where(name: 'conversation_resolved', created_at: range)
|
.where(name: 'conversation_resolved', created_at: range)
|
||||||
.average(:value)
|
avg_rt = params[:business_hours] ? reporting_events.average(:value_in_business_hours) : reporting_events.average(:value)
|
||||||
|
|
||||||
return 0 if avg_rt.blank?
|
return 0 if avg_rt.blank?
|
||||||
|
|
||||||
|
@ -51,9 +57,9 @@ module ReportHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def avg_first_response_time_summary
|
def avg_first_response_time_summary
|
||||||
avg_frt = scope.reporting_events
|
reporting_events = scope.reporting_events
|
||||||
.where(name: 'first_response', created_at: range)
|
.where(name: 'first_response', created_at: range)
|
||||||
.average(:value)
|
avg_frt = params[:business_hours] ? reporting_events.average(:value_in_business_hours) : reporting_events.average(:value)
|
||||||
|
|
||||||
return 0 if avg_frt.blank?
|
return 0 if avg_frt.blank?
|
||||||
|
|
||||||
|
|
50
app/helpers/reporting_event_helper.rb
Normal file
50
app/helpers/reporting_event_helper.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
module ReportingEventHelper
|
||||||
|
def business_hours(inbox, from, to)
|
||||||
|
return 0 unless inbox.working_hours_enabled?
|
||||||
|
|
||||||
|
inbox_working_hours = configure_working_hours(inbox.working_hours)
|
||||||
|
return 0 if inbox_working_hours.blank?
|
||||||
|
|
||||||
|
# Configure working hours
|
||||||
|
WorkingHours::Config.working_hours = inbox_working_hours
|
||||||
|
|
||||||
|
# Configure timezone
|
||||||
|
WorkingHours::Config.time_zone = inbox.timezone
|
||||||
|
|
||||||
|
# Use inbox timezone to change from & to values.
|
||||||
|
from_in_inbox_timezone = from.in_time_zone(inbox.timezone).to_time
|
||||||
|
to_in_inbox_timezone = to.in_time_zone(inbox.timezone).to_time
|
||||||
|
from_in_inbox_timezone.working_time_until(to_in_inbox_timezone)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def configure_working_hours(working_hours)
|
||||||
|
working_hours.each_with_object({}) do |working_hour, object|
|
||||||
|
object[day(working_hour.day_of_week)] = working_hour_range(working_hour) unless working_hour.closed_all_day?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def day(day_of_week)
|
||||||
|
week_days = {
|
||||||
|
0 => :sun,
|
||||||
|
1 => :mon,
|
||||||
|
2 => :tue,
|
||||||
|
3 => :wed,
|
||||||
|
4 => :thu,
|
||||||
|
5 => :fri,
|
||||||
|
6 => :sat
|
||||||
|
}
|
||||||
|
week_days[day_of_week]
|
||||||
|
end
|
||||||
|
|
||||||
|
def working_hour_range(working_hour)
|
||||||
|
{ format_time(working_hour.open_hour, working_hour.open_minutes) => format_time(working_hour.close_hour, working_hour.close_minutes) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def format_time(hour, minute)
|
||||||
|
hour = hour < 10 ? "0#{hour}" : hour
|
||||||
|
minute = minute < 10 ? "0#{minute}" : minute
|
||||||
|
"#{hour}:#{minute}"
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,7 +8,15 @@ class ReportsAPI extends ApiClient {
|
||||||
super('reports', { accountScoped: true, apiVersion: 'v2' });
|
super('reports', { accountScoped: true, apiVersion: 'v2' });
|
||||||
}
|
}
|
||||||
|
|
||||||
getReports(metric, since, until, type = 'account', id, group_by) {
|
getReports(
|
||||||
|
metric,
|
||||||
|
since,
|
||||||
|
until,
|
||||||
|
type = 'account',
|
||||||
|
id,
|
||||||
|
group_by,
|
||||||
|
business_hours
|
||||||
|
) {
|
||||||
return axios.get(`${this.url}`, {
|
return axios.get(`${this.url}`, {
|
||||||
params: {
|
params: {
|
||||||
metric,
|
metric,
|
||||||
|
@ -17,12 +25,13 @@ class ReportsAPI extends ApiClient {
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
group_by,
|
group_by,
|
||||||
|
business_hours,
|
||||||
timezone_offset: getTimeOffset(),
|
timezone_offset: getTimeOffset(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSummary(since, until, type = 'account', id, group_by) {
|
getSummary(since, until, type = 'account', id, group_by, business_hours) {
|
||||||
return axios.get(`${this.url}/summary`, {
|
return axios.get(`${this.url}/summary`, {
|
||||||
params: {
|
params: {
|
||||||
since,
|
since,
|
||||||
|
@ -30,6 +39,7 @@ class ReportsAPI extends ApiClient {
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
group_by,
|
group_by,
|
||||||
|
business_hours,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,5 +78,10 @@
|
||||||
font-size: $font-size-default;
|
font-size: $font-size-default;
|
||||||
color: $color-gray;
|
color: $color-gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.business-hours {
|
||||||
|
margin: $space-normal;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,3 +25,21 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.business-hours {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
margin-bottom: var(--space-normal);
|
||||||
|
margin-left: auto;
|
||||||
|
padding-right: var(--space-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.business-hours-text {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
margin-bottom: var(--space-zero);
|
||||||
|
margin-left: var(--space-small);
|
||||||
|
}
|
||||||
|
|
|
@ -78,7 +78,8 @@
|
||||||
{ "id": 2, "groupBy": "Week" },
|
{ "id": 2, "groupBy": "Week" },
|
||||||
{ "id": 3, "groupBy": "Month" },
|
{ "id": 3, "groupBy": "Month" },
|
||||||
{ "id": 4, "groupBy": "Year" }
|
{ "id": 4, "groupBy": "Year" }
|
||||||
]
|
],
|
||||||
|
"BUSINESS_HOURS": "Business Hours"
|
||||||
},
|
},
|
||||||
"AGENT_REPORTS": {
|
"AGENT_REPORTS": {
|
||||||
"HEADER": "Agents Overview",
|
"HEADER": "Agents Overview",
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
:filter-items-list="filterItemsList"
|
:filter-items-list="filterItemsList"
|
||||||
@date-range-change="onDateRangeChange"
|
@date-range-change="onDateRangeChange"
|
||||||
@filter-change="onFilterChange"
|
@filter-change="onFilterChange"
|
||||||
|
@business-hours-toggle="onBusinessHoursToggle"
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<woot-report-stats-card
|
<woot-report-stats-card
|
||||||
|
@ -79,6 +80,7 @@ export default {
|
||||||
groupBy: GROUP_BY_FILTER[1],
|
groupBy: GROUP_BY_FILTER[1],
|
||||||
filterItemsList: this.$t('REPORT.GROUP_BY_DAY_OPTIONS'),
|
filterItemsList: this.$t('REPORT.GROUP_BY_DAY_OPTIONS'),
|
||||||
selectedGroupByFilter: {},
|
selectedGroupByFilter: {},
|
||||||
|
businessHours: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -166,21 +168,23 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchAllData() {
|
fetchAllData() {
|
||||||
const { from, to, groupBy } = this;
|
const { from, to, groupBy, businessHours } = this;
|
||||||
this.$store.dispatch('fetchAccountSummary', {
|
this.$store.dispatch('fetchAccountSummary', {
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
groupBy: groupBy.period,
|
groupBy: groupBy.period,
|
||||||
|
businessHours,
|
||||||
});
|
});
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
},
|
},
|
||||||
fetchChartData() {
|
fetchChartData() {
|
||||||
const { from, to, groupBy } = this;
|
const { from, to, groupBy, businessHours } = this;
|
||||||
this.$store.dispatch('fetchAccountReport', {
|
this.$store.dispatch('fetchAccountReport', {
|
||||||
metric: this.metrics[this.currentSelection].KEY,
|
metric: this.metrics[this.currentSelection].KEY,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
groupBy: groupBy.period,
|
groupBy: groupBy.period,
|
||||||
|
businessHours,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadAgentReports() {
|
downloadAgentReports() {
|
||||||
|
@ -226,6 +230,10 @@ export default {
|
||||||
return this.$t('REPORT.GROUP_BY_DAY_OPTIONS');
|
return this.$t('REPORT.GROUP_BY_DAY_OPTIONS');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onBusinessHoursToggle(value) {
|
||||||
|
this.businessHours = value;
|
||||||
|
this.fetchAllData();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -61,6 +61,12 @@
|
||||||
@input="handleAgentsFilterSelection"
|
@input="handleAgentsFilterSelection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="small-12 medium-3 business-hours">
|
||||||
|
<span class="business-hours-text">{{ $t('REPORT.BUSINESS_HOURS') }}</span>
|
||||||
|
<span>
|
||||||
|
<woot-switch v-model="businessHoursSelected" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
@ -105,6 +111,7 @@ export default {
|
||||||
customDateRange: [new Date(), new Date()],
|
customDateRange: [new Date(), new Date()],
|
||||||
currentSelectedFilter: null,
|
currentSelectedFilter: null,
|
||||||
selectedAgents: [],
|
selectedAgents: [],
|
||||||
|
businessHoursSelected: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -153,6 +160,9 @@ export default {
|
||||||
filterItemsList() {
|
filterItemsList() {
|
||||||
this.currentSelectedFilter = this.selectedGroupByFilter;
|
this.currentSelectedFilter = this.selectedGroupByFilter;
|
||||||
},
|
},
|
||||||
|
businessHoursSelected() {
|
||||||
|
this.$emit('business-hours-toggle', this.businessHoursSelected);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.onDateRangeChange();
|
this.onDateRangeChange();
|
||||||
|
|
|
@ -145,6 +145,12 @@
|
||||||
@input="changeGroupByFilterSelection"
|
@input="changeGroupByFilterSelection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="small-12 medium-3 business-hours">
|
||||||
|
<span class="business-hours-text">{{ $t('REPORT.BUSINESS_HOURS') }}</span>
|
||||||
|
<span>
|
||||||
|
<woot-switch v-model="businessHoursSelected" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
@ -188,6 +194,7 @@ export default {
|
||||||
dateRange: this.$t('REPORT.DATE_RANGE'),
|
dateRange: this.$t('REPORT.DATE_RANGE'),
|
||||||
customDateRange: [new Date(), new Date()],
|
customDateRange: [new Date(), new Date()],
|
||||||
currentSelectedGroupByFilter: null,
|
currentSelectedGroupByFilter: null,
|
||||||
|
businessHoursSelected: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -249,6 +256,9 @@ export default {
|
||||||
groupByFilterItemsList() {
|
groupByFilterItemsList() {
|
||||||
this.currentSelectedGroupByFilter = this.selectedGroupByFilter;
|
this.currentSelectedGroupByFilter = this.selectedGroupByFilter;
|
||||||
},
|
},
|
||||||
|
businessHoursSelected() {
|
||||||
|
this.$emit('business-hours-toggle', this.businessHoursSelected);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.onDateRangeChange();
|
this.onDateRangeChange();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
@date-range-change="onDateRangeChange"
|
@date-range-change="onDateRangeChange"
|
||||||
@filter-change="onFilterChange"
|
@filter-change="onFilterChange"
|
||||||
@group-by-filter-change="onGroupByFilterChange"
|
@group-by-filter-change="onGroupByFilterChange"
|
||||||
|
@business-hours-toggle="onBusinessHoursToggle"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="filterItemsList.length" class="row">
|
<div v-if="filterItemsList.length" class="row">
|
||||||
|
@ -100,6 +101,7 @@ export default {
|
||||||
groupBy: GROUP_BY_FILTER[1],
|
groupBy: GROUP_BY_FILTER[1],
|
||||||
groupByfilterItemsList: this.$t('REPORT.GROUP_BY_DAY_OPTIONS'),
|
groupByfilterItemsList: this.$t('REPORT.GROUP_BY_DAY_OPTIONS'),
|
||||||
selectedGroupByFilter: null,
|
selectedGroupByFilter: null,
|
||||||
|
businessHours: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -202,19 +204,20 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
fetchAllData() {
|
fetchAllData() {
|
||||||
if (this.selectedFilter) {
|
if (this.selectedFilter) {
|
||||||
const { from, to, groupBy } = this;
|
const { from, to, groupBy, businessHours } = this;
|
||||||
this.$store.dispatch('fetchAccountSummary', {
|
this.$store.dispatch('fetchAccountSummary', {
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
id: this.selectedFilter.id,
|
id: this.selectedFilter.id,
|
||||||
groupBy: groupBy.period,
|
groupBy: groupBy.period,
|
||||||
|
businessHours,
|
||||||
});
|
});
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchChartData() {
|
fetchChartData() {
|
||||||
const { from, to, groupBy } = this;
|
const { from, to, groupBy, businessHours } = this;
|
||||||
this.$store.dispatch('fetchAccountReport', {
|
this.$store.dispatch('fetchAccountReport', {
|
||||||
metric: this.metrics[this.currentSelection].KEY,
|
metric: this.metrics[this.currentSelection].KEY,
|
||||||
from,
|
from,
|
||||||
|
@ -222,6 +225,7 @@ export default {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
id: this.selectedFilter.id,
|
id: this.selectedFilter.id,
|
||||||
groupBy: groupBy.period,
|
groupBy: groupBy.period,
|
||||||
|
businessHours,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
downloadReports() {
|
downloadReports() {
|
||||||
|
@ -288,6 +292,10 @@ export default {
|
||||||
return this.$t('REPORT.GROUP_BY_DAY_OPTIONS');
|
return this.$t('REPORT.GROUP_BY_DAY_OPTIONS');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onBusinessHoursToggle(value) {
|
||||||
|
this.businessHours = value;
|
||||||
|
this.fetchAllData();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -42,7 +42,8 @@ export const actions = {
|
||||||
reportObj.to,
|
reportObj.to,
|
||||||
reportObj.type,
|
reportObj.type,
|
||||||
reportObj.id,
|
reportObj.id,
|
||||||
reportObj.groupBy
|
reportObj.groupBy,
|
||||||
|
reportObj.businessHours
|
||||||
).then(accountReport => {
|
).then(accountReport => {
|
||||||
let { data } = accountReport;
|
let { data } = accountReport;
|
||||||
data = data.filter(
|
data = data.filter(
|
||||||
|
@ -69,7 +70,8 @@ export const actions = {
|
||||||
reportObj.to,
|
reportObj.to,
|
||||||
reportObj.type,
|
reportObj.type,
|
||||||
reportObj.id,
|
reportObj.id,
|
||||||
reportObj.groupBy
|
reportObj.groupBy,
|
||||||
|
reportObj.businessHours
|
||||||
)
|
)
|
||||||
.then(accountSummary => {
|
.then(accountSummary => {
|
||||||
commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data);
|
commit(types.default.SET_ACCOUNT_SUMMARY, accountSummary.data);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
class ReportingEventListener < BaseListener
|
class ReportingEventListener < BaseListener
|
||||||
|
include ReportingEventHelper
|
||||||
def conversation_resolved(event)
|
def conversation_resolved(event)
|
||||||
conversation = extract_conversation_and_account(event)[0]
|
conversation = extract_conversation_and_account(event)[0]
|
||||||
time_to_resolve = conversation.updated_at.to_i - conversation.created_at.to_i
|
time_to_resolve = conversation.updated_at.to_i - conversation.created_at.to_i
|
||||||
|
@ -6,10 +7,14 @@ class ReportingEventListener < BaseListener
|
||||||
reporting_event = ReportingEvent.new(
|
reporting_event = ReportingEvent.new(
|
||||||
name: 'conversation_resolved',
|
name: 'conversation_resolved',
|
||||||
value: time_to_resolve,
|
value: time_to_resolve,
|
||||||
|
value_in_business_hours: business_hours(conversation.inbox, conversation.created_at,
|
||||||
|
conversation.updated_at),
|
||||||
account_id: conversation.account_id,
|
account_id: conversation.account_id,
|
||||||
inbox_id: conversation.inbox_id,
|
inbox_id: conversation.inbox_id,
|
||||||
user_id: conversation.assignee_id,
|
user_id: conversation.assignee_id,
|
||||||
conversation_id: conversation.id
|
conversation_id: conversation.id,
|
||||||
|
event_start_time: conversation.created_at,
|
||||||
|
event_end_time: conversation.updated_at
|
||||||
)
|
)
|
||||||
reporting_event.save
|
reporting_event.save
|
||||||
end
|
end
|
||||||
|
@ -22,10 +27,14 @@ class ReportingEventListener < BaseListener
|
||||||
reporting_event = ReportingEvent.new(
|
reporting_event = ReportingEvent.new(
|
||||||
name: 'first_response',
|
name: 'first_response',
|
||||||
value: first_response_time,
|
value: first_response_time,
|
||||||
|
value_in_business_hours: business_hours(conversation.inbox, conversation.created_at,
|
||||||
|
message.created_at),
|
||||||
account_id: conversation.account_id,
|
account_id: conversation.account_id,
|
||||||
inbox_id: conversation.inbox_id,
|
inbox_id: conversation.inbox_id,
|
||||||
user_id: conversation.assignee_id,
|
user_id: conversation.assignee_id,
|
||||||
conversation_id: conversation.id
|
conversation_id: conversation.id,
|
||||||
|
event_start_time: conversation.created_at,
|
||||||
|
event_end_time: message.created_at
|
||||||
)
|
)
|
||||||
reporting_event.save
|
reporting_event.save
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,15 +2,18 @@
|
||||||
#
|
#
|
||||||
# Table name: reporting_events
|
# Table name: reporting_events
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# name :string
|
# event_end_time :datetime
|
||||||
# value :float
|
# event_start_time :datetime
|
||||||
# created_at :datetime not null
|
# name :string
|
||||||
# updated_at :datetime not null
|
# value :float
|
||||||
# account_id :integer
|
# value_in_business_hours :float
|
||||||
# conversation_id :integer
|
# created_at :datetime not null
|
||||||
# inbox_id :integer
|
# updated_at :datetime not null
|
||||||
# user_id :integer
|
# account_id :integer
|
||||||
|
# conversation_id :integer
|
||||||
|
# inbox_id :integer
|
||||||
|
# user_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddValueInBusinessHoursToReportingEvent < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
change_table :reporting_events, bulk: true do |t|
|
||||||
|
t.float :value_in_business_hours, default: nil
|
||||||
|
t.datetime :event_start_time, default: nil
|
||||||
|
t.datetime :event_end_time, default: nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -657,6 +657,9 @@ ActiveRecord::Schema.define(version: 2022_04_05_092033) do
|
||||||
t.integer "conversation_id"
|
t.integer "conversation_id"
|
||||||
t.datetime "created_at", precision: 6, null: false
|
t.datetime "created_at", precision: 6, null: false
|
||||||
t.datetime "updated_at", precision: 6, null: false
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.float "value_in_business_hours"
|
||||||
|
t.datetime "event_start_time"
|
||||||
|
t.datetime "event_end_time"
|
||||||
t.index ["account_id"], name: "index_reporting_events_on_account_id"
|
t.index ["account_id"], name: "index_reporting_events_on_account_id"
|
||||||
t.index ["created_at"], name: "index_reporting_events_on_created_at"
|
t.index ["created_at"], name: "index_reporting_events_on_created_at"
|
||||||
t.index ["inbox_id"], name: "index_reporting_events_on_inbox_id"
|
t.index ["inbox_id"], name: "index_reporting_events_on_inbox_id"
|
||||||
|
|
|
@ -17,6 +17,21 @@ describe ReportingEventListener do
|
||||||
listener.conversation_resolved(event)
|
listener.conversation_resolved(event)
|
||||||
expect(account.reporting_events.where(name: 'conversation_resolved').count).to be 1
|
expect(account.reporting_events.where(name: 'conversation_resolved').count).to be 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when business hours enabled for inbox' do
|
||||||
|
let(:created_at) { Time.zone.parse('March 20, 2022 00:00') }
|
||||||
|
let(:updated_at) { Time.zone.parse('March 26, 2022 23:59') }
|
||||||
|
let!(:new_inbox) { create(:inbox, working_hours_enabled: true, account: account) }
|
||||||
|
let!(:new_conversation) do
|
||||||
|
create(:conversation, created_at: created_at, updated_at: updated_at, account: account, inbox: new_inbox, assignee: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates conversation_resolved event with business hour value' do
|
||||||
|
event = Events::Base.new('conversation.resolved', Time.zone.now, conversation: new_conversation)
|
||||||
|
listener.conversation_resolved(event)
|
||||||
|
expect(account.reporting_events.where(name: 'conversation_resolved')[0]['value_in_business_hours']).to be 144_000.0
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#first_reply_created' do
|
describe '#first_reply_created' do
|
||||||
|
@ -26,5 +41,24 @@ describe ReportingEventListener do
|
||||||
listener.first_reply_created(event)
|
listener.first_reply_created(event)
|
||||||
expect(account.reporting_events.where(name: 'first_response').count).to eql previous_count + 1
|
expect(account.reporting_events.where(name: 'first_response').count).to eql previous_count + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when business hours enabled for inbox' do
|
||||||
|
let(:conversation_created_at) { Time.zone.parse('March 20, 2022 00:00') }
|
||||||
|
let(:message_created_at) { Time.zone.parse('March 26, 2022 23:59') }
|
||||||
|
let!(:new_inbox) { create(:inbox, working_hours_enabled: true, account: account) }
|
||||||
|
let!(:new_conversation) do
|
||||||
|
create(:conversation, created_at: conversation_created_at, account: account, inbox: new_inbox, assignee: user)
|
||||||
|
end
|
||||||
|
let!(:new_message) do
|
||||||
|
create(:message, message_type: 'outgoing', created_at: message_created_at,
|
||||||
|
account: account, inbox: new_inbox, conversation: new_conversation)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates first_response event with business hour value' do
|
||||||
|
event = Events::Base.new('first.reply.created', Time.zone.now, message: new_message)
|
||||||
|
listener.first_reply_created(event)
|
||||||
|
expect(account.reporting_events.where(name: 'first_response')[0]['value_in_business_hours']).to be 144_000.0
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue