hide total billable amounts from employees when employees_can_see_billable_rates is disabled

This commit is contained in:
Gregor Vostrak
2025-03-09 14:12:42 +01:00
committed by Constantin Graf
parent 73ce5f793d
commit 50cc7053e4
17 changed files with 176 additions and 125 deletions

View File

@@ -12,7 +12,7 @@ type AggregatedGroupedData = GroupedData & {
type GroupedData = {
seconds: number;
cost: number;
cost: number | null;
description: string | null | undefined;
};
@@ -48,7 +48,7 @@ const expanded = ref(false);
{{ formatHumanReadableDuration(entry.seconds) }}
</div>
<div class="justify-end pr-6 flex items-center">
{{ formatCents(entry.cost, getOrganizationCurrencyString()) }}
{{entry.cost ? formatCents(entry.cost, getOrganizationCurrencyString()) : '--' }}
</div>
</div>
<div

View File

@@ -2,9 +2,13 @@
import { router } from '@inertiajs/vue3';
import TabBar from '@/Components/Common/TabBar/TabBar.vue';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue';
import {canViewReport} from "@/utils/permissions";
import {computed} from "vue";
defineProps<{
active: 'reporting' | 'detailed' | 'shared';
}>();
const showSharedReports = computed(() => canViewReport());
</script>
<template>
@@ -20,6 +24,7 @@ defineProps<{
>Detailed</TabBarItem
>
<TabBarItem
v-if="showSharedReports"
:active="active === 'shared'"
@click="router.visit(route('reporting.shared'))"
>Shared</TabBarItem

View File

@@ -43,7 +43,7 @@ const props = defineProps<{
totalWeeklyBillableAmount: {
value: number;
currency: string;
};
} | null;
weeklyHistory: {
date: string;
duration: number;
@@ -199,10 +199,11 @@ const option = ref({
<StatCard
title="Billable Amount"
:value="
props.totalWeeklyBillableAmount ?
formatCents(
props.totalWeeklyBillableAmount.value,
getOrganizationCurrencyString()
)
) : '--'
" />
<ProjectsChartCard
:weekly-project-overview="

View File

@@ -14,7 +14,7 @@ const props = defineProps<{
icon?: Component;
current?: boolean;
href: string;
subItems?: { title: string; route: string }[];
subItems?: { title: string; route: string, show: boolean }[];
}>();
const open = useSessionStorage('nav-collapse-state-' + props.title, true);
@@ -66,6 +66,7 @@ const open = useSessionStorage('nav-collapse-state-' + props.title, true);
:key="subItem.title"
class="w-full relative">
<NavigationSidebarLink
v-if="subItem.show"
:title="subItem.title"
:current="route().current(subItem.route)"
:href="

View File

@@ -27,7 +27,7 @@ import {
canUpdateOrganization,
canViewClients,
canViewMembers,
canViewProjects,
canViewProjects, canViewReport,
canViewTags,
} from '@/utils/permissions';
import { isBillingActivated } from '@/utils/billing';
@@ -118,14 +118,17 @@ const page = usePage<{
{
title: 'Overview',
route: 'reporting',
show: true
},
{
title: 'Detailed',
route: 'reporting.detailed',
show: true
},
{
title: 'Shared',
route: 'reporting.shared',
show: canViewReport()
},
]"
:current="

View File

@@ -464,10 +464,11 @@ const tableData = computed(() => {
<div
class="justify-end pr-6 flex items-center font-medium">
{{
aggregatedTableTimeEntries.cost ?
formatCents(
aggregatedTableTimeEntries.cost,
getOrganizationCurrencyString()
)
) : '--'
}}
</div>
</div>

View File

@@ -58,7 +58,7 @@ import {
PaginationRoot,
} from 'radix-vue';
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { getCurrentOrganizationId } from '@/utils/useUser';
import { getCurrentOrganizationId, getCurrentMembershipId } from '@/utils/useUser';
import { useTimeEntriesStore } from '@/utils/useTimeEntries';
import ReportingTabNavbar from '@/Components/Common/Reporting/ReportingTabNavbar.vue';
import ReportingExportButton from '@/Components/Common/Reporting/ReportingExportButton.vue';
@@ -66,7 +66,7 @@ import type { ExportFormat } from '@/types/reporting';
import { useNotificationsStore } from '@/utils/notification';
import TimeEntryMassActionRow from '@/packages/ui/src/TimeEntry/TimeEntryMassActionRow.vue';
import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { canCreateProjects } from '@/utils/permissions';
import {canCreateProjects, canViewAllTimeEntries} from '@/utils/permissions';
import ReportingExportModal from '@/Components/Common/Reporting/ReportingExportModal.vue';
const startDate = useSessionStorage<string>(
@@ -98,6 +98,7 @@ function getFilterAttributes() {
};
const params = {
...defaultParams,
member_id: !canViewAllTimeEntries() ? getCurrentMembershipId() : undefined,
member_ids:
selectedMembers.value.length > 0
? selectedMembers.value

View File

@@ -94,28 +94,6 @@ const OrganizationUpdateRequest = z
employees_can_see_billable_rates: z.boolean().optional(),
})
.passthrough();
const VersionRequest = z
.object({
version: z.string().max(255),
build: z.string().max(255),
url: z.string().max(255),
})
.passthrough();
const TelemetryRequest = z
.object({
version: z.string().max(255),
build: z.string().max(255),
url: z.string().max(255).url(),
user_count: z.number().int(),
organization_count: z.number().int(),
audit_count: z.number().int(),
project_count: z.number().int(),
project_member_count: z.number().int(),
client_count: z.number().int(),
task_count: z.number().int(),
time_entry_count: z.number().int(),
})
.passthrough();
const ProjectResource = z
.object({
id: z.string(),
@@ -525,8 +503,6 @@ export const schemas = {
MemberMergeIntoRequest,
OrganizationResource,
OrganizationUpdateRequest,
VersionRequest,
TelemetryRequest,
ProjectResource,
ProjectStoreRequest,
ProjectUpdateRequest,
@@ -3149,7 +3125,7 @@ If the group parameters are all set to &#x60;null&#x60; or are all missing, the
.object({
key: z.union([z.string(), z.null()]),
seconds: z.number().int(),
cost: z.number().int(),
cost: z.union([z.number(), z.null()]),
grouped_type: z.union([
z.string(),
z.null(),
@@ -3165,7 +3141,10 @@ If the group parameters are all set to &#x60;null&#x60; or are all missing, the
seconds: z
.number()
.int(),
cost: z.number().int(),
cost: z.union([
z.number(),
z.null(),
]),
grouped_type: z.null(),
grouped_data: z.null(),
})
@@ -3179,7 +3158,7 @@ If the group parameters are all set to &#x60;null&#x60; or are all missing, the
z.null(),
]),
seconds: z.number().int(),
cost: z.number().int(),
cost: z.union([z.number(), z.null()]),
})
.passthrough(),
})
@@ -3498,58 +3477,6 @@ If the group parameters are all set to &#x60;null&#x60; or are all missing, the
},
],
},
{
method: 'post',
path: '/v1/ping/telemetry',
alias: 'v1.ping.telemetry',
requestFormat: 'json',
parameters: [
{
name: 'body',
type: 'Body',
schema: TelemetryRequest,
},
],
response: z.object({ success: z.boolean() }).passthrough(),
errors: [
{
status: 422,
description: `Validation error`,
schema: z
.object({
message: z.string(),
errors: z.record(z.array(z.string())),
})
.passthrough(),
},
],
},
{
method: 'post',
path: '/v1/ping/version',
alias: 'v1.ping.version',
requestFormat: 'json',
parameters: [
{
name: 'body',
type: 'Body',
schema: VersionRequest,
},
],
response: z.object({ version: z.string() }).passthrough(),
errors: [
{
status: 422,
description: `Validation error`,
schema: z
.object({
message: z.string(),
errors: z.record(z.array(z.string())),
})
.passthrough(),
},
],
},
{
method: 'get',
path: '/v1/public/reports',

View File

@@ -105,9 +105,16 @@ export function canManageBilling() {
return currentUserHasPermission('billing');
}
export function canViewReport() {
return currentUserHasPermission('reports:view');
}
export function canUpdateReport() {
return currentUserHasPermission('reports:update');
}
export function canDeleteReport() {
return currentUserHasPermission('reports:delete');
}
export function canViewAllTimeEntries() {
return currentUserHasPermission('time-entries:view:all');
}